From a2fd12cb70d5c89cae0aec919dbdb525890a47f2 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 7 Dec 2023 11:54:51 +1100 Subject: [PATCH 01/14] Hide drop indicator where block isn't allowed to drop. --- .../components/use-block-drop-zone/index.js | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index cb3c3ae6a28a3d..dcfa8c342741bb 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -216,7 +216,13 @@ export default function useBlockDropZone( { operation: 'insert', } ); - const { isDisabled, parentBlockClientId, rootBlockIndex } = useSelect( + const { + parentBlockClientId, + rootBlockIndex, + isDisabled, + allowedBlocks, + draggedBlockNames, + } = useSelect( ( select ) => { const { __unstableIsWithinBlockOverlay, @@ -224,7 +230,11 @@ export default function useBlockDropZone( { getBlockIndex, getBlockParents, getBlockEditingMode, + getDraggedBlockClientIds, + getBlockNamesByClientId, + getAllowedBlocks, } = select( blockEditorStore ); + const blockEditingMode = getBlockEditingMode( targetRootClientId ); return { parentBlockClientId: @@ -236,11 +246,26 @@ export default function useBlockDropZone( { targetRootClientId ) || __unstableIsWithinBlockOverlay( targetRootClientId ), + allowedBlocks: getAllowedBlocks( targetRootClientId ), + draggedBlockNames: getBlockNamesByClientId( + getDraggedBlockClientIds() + ), }; }, [ targetRootClientId ] ); + // At root level allowedBlocks is undefined and all blocks are allowed. + // Otherwise, check if all dragged blocks are allowed. + let areBlocksAllowed = true; + if ( allowedBlocks ) { + const allowedBlockNames = allowedBlocks?.map( ( { name } ) => name ); + + areBlocksAllowed = draggedBlockNames.every( ( name ) => + allowedBlockNames?.includes( name ) + ); + } + const { getBlockListSettings, getBlocks, getBlockIndex } = useSelect( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = @@ -338,7 +363,7 @@ export default function useBlockDropZone( { return useDropZone( { dropZoneElement, - isDisabled, + isDisabled: isDisabled || ! areBlocksAllowed, onDrop: onBlockDrop, onDragOver( event ) { // `currentTarget` is only available while the event is being From 761fb6178ca0ecc4eeedc7db040d6bb2fe795bca Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 7 Dec 2023 14:16:03 +1100 Subject: [PATCH 02/14] Don't show indicator when drop outside of allowed parent --- .../components/use-block-drop-zone/index.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index dcfa8c342741bb..681a7a03501063 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -8,7 +8,10 @@ import { __experimentalUseDropZone as useDropZone, } from '@wordpress/compose'; import { isRTL } from '@wordpress/i18n'; -import { isUnmodifiedDefaultBlock as getIsUnmodifiedDefaultBlock } from '@wordpress/blocks'; +import { + isUnmodifiedDefaultBlock as getIsUnmodifiedDefaultBlock, + store as blocksStore, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -222,6 +225,8 @@ export default function useBlockDropZone( { isDisabled, allowedBlocks, draggedBlockNames, + getBlockType, + targetBlockName, } = useSelect( ( select ) => { const { @@ -234,7 +239,7 @@ export default function useBlockDropZone( { getBlockNamesByClientId, getAllowedBlocks, } = select( blockEditorStore ); - + const { getBlockType: _getBlockType } = select( blocksStore ); const blockEditingMode = getBlockEditingMode( targetRootClientId ); return { parentBlockClientId: @@ -250,6 +255,10 @@ export default function useBlockDropZone( { draggedBlockNames: getBlockNamesByClientId( getDraggedBlockClientIds() ), + getBlockType: _getBlockType, + targetBlockName: getBlockNamesByClientId( [ + targetRootClientId, + ] )[ 0 ], }; }, [ targetRootClientId ] @@ -266,6 +275,20 @@ export default function useBlockDropZone( { ); } + // Work out if dragged blocks have an allowed parent and if so + // check target block matches the allowed parent. + const draggedBlockTypes = draggedBlockNames.map( ( name ) => + getBlockType( name ) + ); + const targetMatchesDraggedBlockParents = draggedBlockTypes.every( + ( block ) => { + const [ allowedParentName ] = block?.parent || []; + if ( ! allowedParentName ) { + return true; + } + return allowedParentName === targetBlockName; + } + ); const { getBlockListSettings, getBlocks, getBlockIndex } = useSelect( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = @@ -363,7 +386,10 @@ export default function useBlockDropZone( { return useDropZone( { dropZoneElement, - isDisabled: isDisabled || ! areBlocksAllowed, + isDisabled: + isDisabled || + ! areBlocksAllowed || + ! targetMatchesDraggedBlockParents, onDrop: onBlockDrop, onDragOver( event ) { // `currentTarget` is only available while the event is being From 4f0513f5172fd860173b8fd27ab28664a8328cf3 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Mon, 11 Dec 2023 18:01:19 +1100 Subject: [PATCH 03/14] logic to change the draggable icon --- .../src/components/block-draggable/index.js | 104 ++++++++++++----- .../components/use-block-drop-zone/index.js | 110 +++++++++++------- 2 files changed, 144 insertions(+), 70 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 0b8f3c2d87f520..8cef5917389f68 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -4,7 +4,7 @@ import { store as blocksStore } from '@wordpress/blocks'; import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, useRef } from '@wordpress/element'; +import { useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -12,6 +12,8 @@ import { useEffect, useRef } from '@wordpress/element'; import BlockDraggableChip from './draggable-chip'; import useScrollWhenDragging from './use-scroll-when-dragging'; import { store as blockEditorStore } from '../../store'; +import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs'; +import { useIsDropTargetValid } from '../use-block-drop-zone'; const BlockDraggable = ( { children, @@ -20,31 +22,39 @@ const BlockDraggable = ( { onDragStart, onDragEnd, } ) => { - const { srcRootClientId, isDraggable, icon } = useSelect( - ( select ) => { - const { - canMoveBlocks, - getBlockRootClientId, - getBlockName, - getBlockAttributes, - } = select( blockEditorStore ); - const { getBlockType, getActiveBlockVariation } = - select( blocksStore ); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - const blockName = getBlockName( clientIds[ 0 ] ); - const variation = getActiveBlockVariation( - blockName, - getBlockAttributes( clientIds[ 0 ] ) - ); - - return { - srcRootClientId: rootClientId, - isDraggable: canMoveBlocks( clientIds, rootClientId ), - icon: variation?.icon || getBlockType( blockName )?.icon, - }; - }, - [ clientIds ] - ); + const { srcRootClientId, isDraggable, icon, getBlockListSettings } = + useSelect( + ( select ) => { + const { + canMoveBlocks, + getBlockRootClientId, + getBlockName, + getBlockAttributes, + getBlockListSettings: _getBlockListSettings, + } = select( blockEditorStore ); + const { getBlockType, getActiveBlockVariation } = + select( blocksStore ); + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + const blockName = getBlockName( clientIds[ 0 ] ); + const variation = getActiveBlockVariation( + blockName, + getBlockAttributes( clientIds[ 0 ] ) + ); + + return { + srcRootClientId: rootClientId, + isDraggable: canMoveBlocks( clientIds, rootClientId ), + icon: variation?.icon || getBlockType( blockName )?.icon, + getBlockListSettings: _getBlockListSettings, + }; + }, + [ clientIds ] + ); + + const [ targetClientId, setTargetClientId ] = useState( null ); + + const isDropTargetValid = useIsDropTargetValid( clientIds, targetClientId ); + const isDragging = useRef( false ); const [ startScrolling, scrollOnDragOver, stopScrolling ] = useScrollWhenDragging(); @@ -61,6 +71,42 @@ const BlockDraggable = ( { }; }, [] ); + const blockRef = useBlockRef( clientIds[ 0 ] ); + const editorRoot = blockRef.current?.closest( '.is-root-container' ); + + // Add a dragover event listener to the editor root to track the blocks being dragged over. + // The listener has to be inside the editor iframe otherwise the target isn't accessible. + // Check if the dragged blocks are allowed inside the target. If not, grey out the draggable. + useEffect( () => { + if ( ! editorRoot ) { + return; + } + + const onDragOver = ( event ) => { + if ( ! event.target.closest( '[data-block]' ) ) { + return; + } + + const newTargetClientId = event.target + .closest( '[data-block]' ) + .getAttribute( 'data-block' ); + //Only update targetClientId if it has changed + // and if it's a container block + if ( + targetClientId !== newTargetClientId && + getBlockListSettings( newTargetClientId ) + ) { + setTargetClientId( newTargetClientId ); + } + }; + + editorRoot.addEventListener( 'dragover', onDragOver ); + + return () => { + editorRoot.removeEventListener( 'dragover', onDragOver ); + }; + } ); + if ( ! isDraggable ) { return children( { draggable: false } ); } @@ -102,7 +148,11 @@ const BlockDraggable = ( { } } } __experimentalDragComponent={ - + } > { ( { onDraggableStart, onDraggableEnd } ) => { diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index 681a7a03501063..a3d93d58753bbe 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -194,6 +194,65 @@ export function getDropTargetPosition( ]; } +/** + * A Rect hook that takes an array of dragged block client ids and a target client id + * and determines if the dragged blocks can be dropped on the target. + * + * @param {string[]} draggedBlockClientIds The client ids of the dragged blocks. + * @param {string} targetClientId The client id of the target. + * @return {boolean} Whether the dragged blocks can be dropped on the target. + */ +export function useIsDropTargetValid( draggedBlockClientIds, targetClientId ) { + const { getBlockType, allowedBlocks, draggedBlockNames, targetBlockName } = + useSelect( + ( select ) => { + const { getBlockType: _getBlockType } = select( blocksStore ); + const { getBlockNamesByClientId, getAllowedBlocks } = + select( blockEditorStore ); + + return { + getBlockType: _getBlockType, + allowedBlocks: getAllowedBlocks( targetClientId ), + draggedBlockNames: getBlockNamesByClientId( + draggedBlockClientIds + ), + targetBlockName: getBlockNamesByClientId( [ + targetClientId, + ] )[ 0 ], + }; + }, + [ draggedBlockClientIds, targetClientId ] + ); + + // At root level allowedBlocks is undefined and all blocks are allowed. + // Otherwise, check if all dragged blocks are allowed. + let areBlocksAllowed = true; + if ( allowedBlocks ) { + const allowedBlockNames = allowedBlocks?.map( ( { name } ) => name ); + + areBlocksAllowed = draggedBlockNames.every( ( name ) => + allowedBlockNames?.includes( name ) + ); + } + + // Work out if dragged blocks have an allowed parent and if so + // check target block matches the allowed parent. + const draggedBlockTypes = draggedBlockNames.map( ( name ) => + getBlockType( name ) + ); + const targetMatchesDraggedBlockParents = draggedBlockTypes.every( + ( block ) => { + const [ allowedParentName ] = block?.parent || []; + if ( ! allowedParentName ) { + return true; + } + + return allowedParentName === targetBlockName; + } + ); + return areBlocksAllowed && targetMatchesDraggedBlockParents; +} + /** * @typedef {Object} WPBlockDropZoneConfig * @property {?HTMLElement} dropZoneElement Optional element to be used as the drop zone. @@ -223,10 +282,7 @@ export default function useBlockDropZone( { parentBlockClientId, rootBlockIndex, isDisabled, - allowedBlocks, - draggedBlockNames, - getBlockType, - targetBlockName, + draggedBlockClientIds, } = useSelect( ( select ) => { const { @@ -236,10 +292,8 @@ export default function useBlockDropZone( { getBlockParents, getBlockEditingMode, getDraggedBlockClientIds, - getBlockNamesByClientId, - getAllowedBlocks, } = select( blockEditorStore ); - const { getBlockType: _getBlockType } = select( blocksStore ); + const blockEditingMode = getBlockEditingMode( targetRootClientId ); return { parentBlockClientId: @@ -251,44 +305,17 @@ export default function useBlockDropZone( { targetRootClientId ) || __unstableIsWithinBlockOverlay( targetRootClientId ), - allowedBlocks: getAllowedBlocks( targetRootClientId ), - draggedBlockNames: getBlockNamesByClientId( - getDraggedBlockClientIds() - ), - getBlockType: _getBlockType, - targetBlockName: getBlockNamesByClientId( [ - targetRootClientId, - ] )[ 0 ], + draggedBlockClientIds: getDraggedBlockClientIds(), }; }, [ targetRootClientId ] ); - // At root level allowedBlocks is undefined and all blocks are allowed. - // Otherwise, check if all dragged blocks are allowed. - let areBlocksAllowed = true; - if ( allowedBlocks ) { - const allowedBlockNames = allowedBlocks?.map( ( { name } ) => name ); - - areBlocksAllowed = draggedBlockNames.every( ( name ) => - allowedBlockNames?.includes( name ) - ); - } - - // Work out if dragged blocks have an allowed parent and if so - // check target block matches the allowed parent. - const draggedBlockTypes = draggedBlockNames.map( ( name ) => - getBlockType( name ) - ); - const targetMatchesDraggedBlockParents = draggedBlockTypes.every( - ( block ) => { - const [ allowedParentName ] = block?.parent || []; - if ( ! allowedParentName ) { - return true; - } - return allowedParentName === targetBlockName; - } + const isBlockDroppingAllowed = useIsDropTargetValid( + draggedBlockClientIds, + targetRootClientId ); + const { getBlockListSettings, getBlocks, getBlockIndex } = useSelect( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = @@ -386,10 +413,7 @@ export default function useBlockDropZone( { return useDropZone( { dropZoneElement, - isDisabled: - isDisabled || - ! areBlocksAllowed || - ! targetMatchesDraggedBlockParents, + isDisabled: isDisabled || ! isBlockDroppingAllowed, onDrop: onBlockDrop, onDragOver( event ) { // `currentTarget` is only available while the event is being From e0a975341200c68f34e4b2ea676eadbb44a1a7cf Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 12 Dec 2023 16:28:08 +1100 Subject: [PATCH 04/14] Make chip transparent when dragging over undroppable area. --- .../block-draggable/draggable-chip.js | 5 ++++- .../src/components/block-draggable/index.js | 18 ++++++++++++++++-- .../src/components/block-draggable/style.scss | 8 ++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.js b/packages/block-editor/src/components/block-draggable/draggable-chip.js index d7d053be179fa6..3035346864552c 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.js @@ -13,7 +13,10 @@ import BlockIcon from '../block-icon'; export default function BlockDraggableChip( { count, icon, isPattern } ) { const patternLabel = isPattern && __( 'Pattern' ); return ( -
+
} + isValid={ isDropTargetValid } > { ( { onDraggableStart, onDraggableEnd } ) => { return children( { diff --git a/packages/block-editor/src/components/block-draggable/style.scss b/packages/block-editor/src/components/block-draggable/style.scss index a27d4c4caf2f29..210e6c9dd9d671 100644 --- a/packages/block-editor/src/components/block-draggable/style.scss +++ b/packages/block-editor/src/components/block-draggable/style.scss @@ -45,3 +45,11 @@ font-size: $default-font-size; } } + +:root { + --wp--block-draggable-valid-opacity: 1; +} + +.block-draggable-invalid-drag-token { + --wp--block-draggable-valid-opacity: 0.6; +} From 43a1bc0df5fd64c7e761e3ba412cc8a8291891ec Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Tue, 12 Dec 2023 16:44:43 +1100 Subject: [PATCH 05/14] Remove unused prop --- packages/block-editor/src/components/block-draggable/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index e564c793c23523..838a95e6c9645c 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -71,6 +71,7 @@ const BlockDraggable = ( { }; }, [] ); + // Find the root of the editor iframe. const blockRef = useBlockRef( clientIds[ 0 ] ); const editorRoot = blockRef.current?.closest( 'body' ); @@ -167,7 +168,6 @@ const BlockDraggable = ( { targetClientId={ targetClientId } /> } - isValid={ isDropTargetValid } > { ( { onDraggableStart, onDraggableEnd } ) => { return children( { From 359b5b88fd6b6a23f3462f89d875399f5cb59ed1 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 13 Dec 2023 14:50:55 +1100 Subject: [PATCH 06/14] Check insertion point visibility --- .../src/components/block-draggable/index.js | 69 ++++++++++--------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 838a95e6c9645c..7b3dac971c482d 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -22,34 +22,41 @@ const BlockDraggable = ( { onDragStart, onDragEnd, } ) => { - const { srcRootClientId, isDraggable, icon, getBlockListSettings } = - useSelect( - ( select ) => { - const { - canMoveBlocks, - getBlockRootClientId, - getBlockName, - getBlockAttributes, - getBlockListSettings: _getBlockListSettings, - } = select( blockEditorStore ); - const { getBlockType, getActiveBlockVariation } = - select( blocksStore ); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - const blockName = getBlockName( clientIds[ 0 ] ); - const variation = getActiveBlockVariation( - blockName, - getBlockAttributes( clientIds[ 0 ] ) - ); - - return { - srcRootClientId: rootClientId, - isDraggable: canMoveBlocks( clientIds, rootClientId ), - icon: variation?.icon || getBlockType( blockName )?.icon, - getBlockListSettings: _getBlockListSettings, - }; - }, - [ clientIds ] - ); + const { + srcRootClientId, + isDraggable, + icon, + getBlockListSettings, + visibleInserter, + } = useSelect( + ( select ) => { + const { + canMoveBlocks, + getBlockRootClientId, + getBlockName, + getBlockAttributes, + getBlockListSettings: _getBlockListSettings, + isBlockInsertionPointVisible, + } = select( blockEditorStore ); + const { getBlockType, getActiveBlockVariation } = + select( blocksStore ); + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + const blockName = getBlockName( clientIds[ 0 ] ); + const variation = getActiveBlockVariation( + blockName, + getBlockAttributes( clientIds[ 0 ] ) + ); + + return { + srcRootClientId: rootClientId, + isDraggable: canMoveBlocks( clientIds, rootClientId ), + icon: variation?.icon || getBlockType( blockName )?.icon, + getBlockListSettings: _getBlockListSettings, + visibleInserter: isBlockInsertionPointVisible(), + }; + }, + [ clientIds ] + ); const [ targetClientId, setTargetClientId ] = useState( null ); @@ -102,12 +109,12 @@ const BlockDraggable = ( { // Update the body class to reflect if drop target is valid. // This has to be done on the document body because the draggable // chip is rendered outside of the editor iframe. - if ( isDropTargetValid ) { - window?.document?.body?.classList?.remove( + if ( ! isDropTargetValid && ! visibleInserter ) { + window?.document?.body?.classList?.add( 'block-draggable-invalid-drag-token' ); } else { - window?.document?.body?.classList?.add( + window?.document?.body?.classList?.remove( 'block-draggable-invalid-drag-token' ); } From 59607c967e1512c461d472282824803b38d67b14 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 13 Dec 2023 16:32:25 +1100 Subject: [PATCH 07/14] Move some code around --- .../src/components/block-draggable/index.js | 50 +++++++----- .../components/use-block-drop-zone/index.js | 79 ++++++++++--------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index 7b3dac971c482d..de959a40d6f379 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -13,7 +13,7 @@ import BlockDraggableChip from './draggable-chip'; import useScrollWhenDragging from './use-scroll-when-dragging'; import { store as blockEditorStore } from '../../store'; import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs'; -import { useIsDropTargetValid } from '../use-block-drop-zone'; +import { isDropTargetValid } from '../use-block-drop-zone'; const BlockDraggable = ( { children, @@ -26,8 +26,11 @@ const BlockDraggable = ( { srcRootClientId, isDraggable, icon, - getBlockListSettings, visibleInserter, + getBlockType, + getAllowedBlocks, + draggedBlockNames, + getBlockNamesByClientId, } = useSelect( ( select ) => { const { @@ -35,10 +38,12 @@ const BlockDraggable = ( { getBlockRootClientId, getBlockName, getBlockAttributes, - getBlockListSettings: _getBlockListSettings, isBlockInsertionPointVisible, + getDraggedBlockClientIds, + getBlockNamesByClientId: _getBlockNamesByClientId, + getAllowedBlocks: _getAllowedBlocks, } = select( blockEditorStore ); - const { getBlockType, getActiveBlockVariation } = + const { getBlockType: _getBlockType, getActiveBlockVariation } = select( blocksStore ); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); const blockName = getBlockName( clientIds[ 0 ] ); @@ -50,9 +55,14 @@ const BlockDraggable = ( { return { srcRootClientId: rootClientId, isDraggable: canMoveBlocks( clientIds, rootClientId ), - icon: variation?.icon || getBlockType( blockName )?.icon, - getBlockListSettings: _getBlockListSettings, + icon: variation?.icon || _getBlockType( blockName )?.icon, visibleInserter: isBlockInsertionPointVisible(), + getBlockType: _getBlockType, + getAllowedBlocks: _getAllowedBlocks, + draggedBlockNames: _getBlockNamesByClientId( + getDraggedBlockClientIds() + ), + getBlockNamesByClientId: _getBlockNamesByClientId, }; }, [ clientIds ] @@ -60,8 +70,6 @@ const BlockDraggable = ( { const [ targetClientId, setTargetClientId ] = useState( null ); - const isDropTargetValid = useIsDropTargetValid( clientIds, targetClientId ); - const isDragging = useRef( false ); const [ startScrolling, scrollOnDragOver, stopScrolling ] = useScrollWhenDragging(); @@ -100,16 +108,25 @@ const BlockDraggable = ( { .getAttribute( 'data-block' ); //Only update targetClientId if it has changed // and if it's a container block - if ( - targetClientId !== newTargetClientId && - getBlockListSettings( newTargetClientId ) - ) { + if ( targetClientId !== newTargetClientId ) { setTargetClientId( newTargetClientId ); } + + const allowedBlocks = getAllowedBlocks( targetClientId ); + const targetBlockName = getBlockNamesByClientId( [ + targetClientId, + ] )[ 0 ]; + const dropTargetValid = isDropTargetValid( + getBlockType, + allowedBlocks, + draggedBlockNames, + targetBlockName + ); + // Update the body class to reflect if drop target is valid. // This has to be done on the document body because the draggable // chip is rendered outside of the editor iframe. - if ( ! isDropTargetValid && ! visibleInserter ) { + if ( ! dropTargetValid && ! visibleInserter ) { window?.document?.body?.classList?.add( 'block-draggable-invalid-drag-token' ); @@ -168,12 +185,7 @@ const BlockDraggable = ( { } } } __experimentalDragComponent={ - + } > { ( { onDraggableStart, onDraggableEnd } ) => { diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index a3d93d58753bbe..2f895e528ecc62 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -195,35 +195,19 @@ export function getDropTargetPosition( } /** - * A Rect hook that takes an array of dragged block client ids and a target client id - * and determines if the dragged blocks can be dropped on the target. - * - * @param {string[]} draggedBlockClientIds The client ids of the dragged blocks. - * @param {string} targetClientId The client id of the target. + * Check if the dragged blocks can be dropped on the target. + * @param {Function} getBlockType + * @param {Object[]} allowedBlocks + * @param {string[]} draggedBlockNames + * @param {string} targetBlockName * @return {boolean} Whether the dragged blocks can be dropped on the target. */ -export function useIsDropTargetValid( draggedBlockClientIds, targetClientId ) { - const { getBlockType, allowedBlocks, draggedBlockNames, targetBlockName } = - useSelect( - ( select ) => { - const { getBlockType: _getBlockType } = select( blocksStore ); - const { getBlockNamesByClientId, getAllowedBlocks } = - select( blockEditorStore ); - - return { - getBlockType: _getBlockType, - allowedBlocks: getAllowedBlocks( targetClientId ), - draggedBlockNames: getBlockNamesByClientId( - draggedBlockClientIds - ), - targetBlockName: getBlockNamesByClientId( [ - targetClientId, - ] )[ 0 ], - }; - }, - [ draggedBlockClientIds, targetClientId ] - ); - +export function isDropTargetValid( + getBlockType, + allowedBlocks, + draggedBlockNames, + targetBlockName +) { // At root level allowedBlocks is undefined and all blocks are allowed. // Otherwise, check if all dragged blocks are allowed. let areBlocksAllowed = true; @@ -250,6 +234,7 @@ export function useIsDropTargetValid( draggedBlockClientIds, targetClientId ) { return allowedParentName === targetBlockName; } ); + return areBlocksAllowed && targetMatchesDraggedBlockParents; } @@ -282,7 +267,10 @@ export default function useBlockDropZone( { parentBlockClientId, rootBlockIndex, isDisabled, - draggedBlockClientIds, + getBlockType, + allowedBlocks, + draggedBlockNames, + targetBlockName, } = useSelect( ( select ) => { const { @@ -292,8 +280,10 @@ export default function useBlockDropZone( { getBlockParents, getBlockEditingMode, getDraggedBlockClientIds, + getBlockNamesByClientId, + getAllowedBlocks, } = select( blockEditorStore ); - + const { getBlockType: _getBlockType } = select( blocksStore ); const blockEditingMode = getBlockEditingMode( targetRootClientId ); return { parentBlockClientId: @@ -305,17 +295,20 @@ export default function useBlockDropZone( { targetRootClientId ) || __unstableIsWithinBlockOverlay( targetRootClientId ), - draggedBlockClientIds: getDraggedBlockClientIds(), + + getBlockType: _getBlockType, + allowedBlocks: getAllowedBlocks( targetRootClientId ), + draggedBlockNames: getBlockNamesByClientId( + getDraggedBlockClientIds() + ), + targetBlockName: getBlockNamesByClientId( [ + targetRootClientId, + ] )[ 0 ], }; }, [ targetRootClientId ] ); - const isBlockDroppingAllowed = useIsDropTargetValid( - draggedBlockClientIds, - targetRootClientId - ); - const { getBlockListSettings, getBlocks, getBlockIndex } = useSelect( blockEditorStore ); const { showInsertionPoint, hideInsertionPoint } = @@ -333,6 +326,16 @@ export default function useBlockDropZone( { const throttled = useThrottle( useCallback( ( event, ownerDocument ) => { + const isBlockDroppingAllowed = isDropTargetValid( + getBlockType, + allowedBlocks, + draggedBlockNames, + targetBlockName + ); + if ( ! isBlockDroppingAllowed ) { + return; + } + const blocks = getBlocks( targetRootClientId ); // The block list is empty, don't show the insertion point but still allow dropping. @@ -406,6 +409,10 @@ export default function useBlockDropZone( { getBlockIndex, parentBlockClientId, rootBlockIndex, + getBlockType, + allowedBlocks, + draggedBlockNames, + targetBlockName, ] ), 200 @@ -413,7 +420,7 @@ export default function useBlockDropZone( { return useDropZone( { dropZoneElement, - isDisabled: isDisabled || ! isBlockDroppingAllowed, + isDisabled, onDrop: onBlockDrop, onDragOver( event ) { // `currentTarget` is only available while the event is being From 9e40e4e8164a8268174a40d97ff0ed5e3659330d Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 14 Dec 2023 11:48:50 +1100 Subject: [PATCH 08/14] Throttle the event listener --- .../block-editor/src/components/block-draggable/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index de959a40d6f379..ba1aed60040a59 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -5,6 +5,7 @@ import { store as blocksStore } from '@wordpress/blocks'; import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useRef, useState } from '@wordpress/element'; +import { throttle } from '@wordpress/compose'; /** * Internal dependencies @@ -137,10 +138,12 @@ const BlockDraggable = ( { } }; - editorRoot.addEventListener( 'dragover', onDragOver ); + const throttledOnDragOver = throttle( onDragOver, 16 ); + + editorRoot.addEventListener( 'dragover', throttledOnDragOver ); return () => { - editorRoot.removeEventListener( 'dragover', onDragOver ); + editorRoot.removeEventListener( 'dragover', throttledOnDragOver ); }; } ); From 3bf8a0fd658d8b6bd2130e8e5c0330e5cbc30c21 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 14 Dec 2023 14:50:22 +1100 Subject: [PATCH 09/14] Add dependencies to effect --- .../src/components/block-draggable/index.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index ba1aed60040a59..e55fd1814dbdd3 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -4,7 +4,7 @@ import { store as blocksStore } from '@wordpress/blocks'; import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useEffect, useRef, useState } from '@wordpress/element'; +import { useEffect, useRef } from '@wordpress/element'; import { throttle } from '@wordpress/compose'; /** @@ -69,8 +69,6 @@ const BlockDraggable = ( { [ clientIds ] ); - const [ targetClientId, setTargetClientId ] = useState( null ); - const isDragging = useRef( false ); const [ startScrolling, scrollOnDragOver, stopScrolling ] = useScrollWhenDragging(); @@ -104,14 +102,9 @@ const BlockDraggable = ( { return; } - const newTargetClientId = event.target + const targetClientId = event.target .closest( '[data-block]' ) .getAttribute( 'data-block' ); - //Only update targetClientId if it has changed - // and if it's a container block - if ( targetClientId !== newTargetClientId ) { - setTargetClientId( newTargetClientId ); - } const allowedBlocks = getAllowedBlocks( targetClientId ); const targetBlockName = getBlockNamesByClientId( [ @@ -138,14 +131,21 @@ const BlockDraggable = ( { } }; - const throttledOnDragOver = throttle( onDragOver, 16 ); + const throttledOnDragOver = throttle( onDragOver, 200 ); editorRoot.addEventListener( 'dragover', throttledOnDragOver ); return () => { editorRoot.removeEventListener( 'dragover', throttledOnDragOver ); }; - } ); + }, [ + draggedBlockNames, + editorRoot, + getAllowedBlocks, + getBlockNamesByClientId, + getBlockType, + visibleInserter, + ] ); if ( ! isDraggable ) { return children( { draggable: false } ); From 5270a6438dfc8380f8f0e2e08764a2ec7b113b63 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 14 Dec 2023 15:34:46 +1100 Subject: [PATCH 10/14] Update CSS --- .../src/components/block-draggable/draggable-chip.js | 5 +---- .../src/components/block-draggable/style.scss | 9 ++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.js b/packages/block-editor/src/components/block-draggable/draggable-chip.js index 3035346864552c..d7d053be179fa6 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.js @@ -13,10 +13,7 @@ import BlockIcon from '../block-icon'; export default function BlockDraggableChip( { count, icon, isPattern } ) { const patternLabel = isPattern && __( 'Pattern' ); return ( -
+
Date: Thu, 14 Dec 2023 16:21:25 +1100 Subject: [PATCH 11/14] Fade chip only when dragging in editor canvas --- .../block-draggable/draggable-chip.js | 19 +++++++++++++++++-- .../src/components/block-draggable/index.js | 10 +++++++++- .../src/components/block-draggable/style.scss | 5 ++++- .../src/components/block-mover/index.js | 2 +- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.js b/packages/block-editor/src/components/block-draggable/draggable-chip.js index d7d053be179fa6..8de88cd8e81ab4 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -10,10 +15,20 @@ import { dragHandle } from '@wordpress/icons'; */ import BlockIcon from '../block-icon'; -export default function BlockDraggableChip( { count, icon, isPattern } ) { +export default function BlockDraggableChip( { + count, + icon, + isPattern, + className, +} ) { const patternLabel = isPattern && __( 'Pattern' ); return ( -
+
{ const { srcRootClientId, @@ -188,7 +189,14 @@ const BlockDraggable = ( { } } } __experimentalDragComponent={ - + } > { ( { onDraggableStart, onDraggableEnd } ) => { diff --git a/packages/block-editor/src/components/block-draggable/style.scss b/packages/block-editor/src/components/block-draggable/style.scss index e1ddbb3132f06a..c6bc8c06990409 100644 --- a/packages/block-editor/src/components/block-draggable/style.scss +++ b/packages/block-editor/src/components/block-draggable/style.scss @@ -16,6 +16,9 @@ padding: 0 ( $grid-unit-15 + $border-width ); user-select: none; width: max-content; + transition: + background-color 0.5s linear, + opacity 0.5s linear; svg { fill: currentColor; @@ -47,7 +50,7 @@ } .block-draggable-invalid-drag-token { - .block-editor-block-draggable-chip { + .block-editor-block-draggable-chip-fade .block-editor-block-draggable-chip { background-color: $gray-600; opacity: 0.8; } diff --git a/packages/block-editor/src/components/block-mover/index.js b/packages/block-editor/src/components/block-mover/index.js index 9b9f32457561e6..328160e944e954 100644 --- a/packages/block-editor/src/components/block-mover/index.js +++ b/packages/block-editor/src/components/block-mover/index.js @@ -64,7 +64,7 @@ function BlockMover( { clientIds, hideDragHandle } ) { } ) } > { ! hideDragHandle && ( - + { ( draggableProps ) => (