From 8a21e07ff99bc88ccfb47fbcdbd83b49c87e5771 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 9 Jul 2020 15:17:14 +0800 Subject: [PATCH 01/45] Split useBlockDropZone into multiple hooks --- .../use-block-drop-zone/use-on-block-drop.js | 112 ++++++++++++++++++ .../use-block-drop-zone/use-on-file-drop.js | 37 ++++++ .../use-block-drop-zone/use-on-html-drop.js | 17 +++ 3 files changed, 166 insertions(+) create mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js create mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js create mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js new file mode 100644 index 0000000000000..e4dea53746579 --- /dev/null +++ b/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js @@ -0,0 +1,112 @@ +/** + * WordPress dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; + +/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */ + +/** + * Retrieve the data for a block drop event. + * + * @param {WPSyntheticEvent} event The drop event. + * + * @return {Object} An object with block drag and drop data. + */ +function parseDropEvent( event ) { + let result = { + srcRootClientId: null, + srcClientIds: null, + srcIndex: null, + type: null, + }; + + if ( ! event.dataTransfer ) { + return result; + } + + try { + result = Object.assign( + result, + JSON.parse( event.dataTransfer.getData( 'text' ) ) + ); + } catch ( err ) { + return result; + } + + return result; +} + +export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { + const { getBlockIndex, getClientIdsOfDescendants } = useSelect( + ( select ) => { + const { + getBlockIndex: _getBlockIndex, + getClientIdsOfDescendants: _getClientIdsOfDescendants, + } = select( 'core/block-editor' ); + + return { + _getBlockIndex, + _getClientIdsOfDescendants, + }; + }, + [] + ); + + const { moveBlocksToPosition } = useDispatch( 'core/block-editor' ); + + return ( event ) => { + const { + srcRootClientId: sourceRootClientId, + srcClientIds: sourceClientIds, + type: dropType, + } = parseDropEvent( event ); + + // If the user isn't dropping a block, return early. + if ( dropType !== 'block' ) { + return; + } + + const sourceBlockIndex = getBlockIndex( + sourceClientIds[ 0 ], + sourceRootClientId + ); + + // If the user is dropping to the same position, return early. + if ( + sourceRootClientId === targetRootClientId && + sourceBlockIndex === targetBlockIndex + ) { + return; + } + + // If the user is attempting to drop a block within its own + // nested blocks, return early as this would create infinite + // recursion. + if ( + sourceClientIds.includes( targetRootClientId ) || + getClientIdsOfDescendants( sourceClientIds ).some( + ( id ) => id === targetRootClientId + ) + ) { + return; + } + + const isAtSameLevel = sourceRootClientId === targetRootClientId; + const draggedBlockCount = sourceClientIds.length; + + // If the block is kept at the same level and moved downwards, + // subtract to take into account that the blocks being dragged + // were removed from the block list above the insertion point. + const insertIndex = + isAtSameLevel && sourceBlockIndex < targetBlockIndex + ? targetBlockIndex - draggedBlockCount + : targetBlockIndex; + + moveBlocksToPosition( + sourceClientIds, + sourceRootClientId, + targetRootClientId, + insertIndex + ); + }; +} diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js new file mode 100644 index 0000000000000..78f7fe3d9e1fb --- /dev/null +++ b/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { getBlockTransforms, findTransform } from '@wordpress/blocks'; +import { useDispatch, useSelect } from '@wordpress/data'; + +export default function useOnFileDrop( rootClientId, blockIndex ) { + const hasUploadPermissions = useSelect( + ( select ) => + !! select( 'core/block-editor' ).getSettings().mediaUpload, + [] + ); + + const { insertBlocks, updateBlockAttributes } = useDispatch( + 'core/block-editor' + ); + + return ( files ) => { + if ( ! hasUploadPermissions ) { + return; + } + + const transformation = findTransform( + getBlockTransforms( 'from' ), + ( transform ) => + transform.type === 'files' && transform.isMatch( files ) + ); + + if ( transformation ) { + const blocks = transformation.transform( + files, + updateBlockAttributes + ); + insertBlocks( blocks, blockIndex, rootClientId ); + } + }; +} diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js new file mode 100644 index 0000000000000..62c10eb0be350 --- /dev/null +++ b/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js @@ -0,0 +1,17 @@ +/** + * WordPress dependencies + */ +import { pasteHandler } from '@wordpress/blocks'; +import { useDispatch } from '@wordpress/data'; + +export default function useOnHTMLDrop( rootClientId, blockIndex ) { + const { insertBlocks } = useDispatch( 'core/block-editor' ); + + return ( HTML ) => { + const blocks = pasteHandler( { HTML, mode: 'BLOCKS' } ); + + if ( blocks.length ) { + insertBlocks( blocks, blockIndex, rootClientId ); + } + }; +} From 38d35f3cb3768956ce50dfdf2c5f7449f3188e28 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 9 Jul 2020 15:46:13 +0800 Subject: [PATCH 02/45] Add Draggable to block navigation block component --- .../src/components/block-navigation/block.js | 201 ++++++++++-------- 1 file changed, 108 insertions(+), 93 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index 62ad355da94fd..a2fae4809a7d5 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -29,6 +29,7 @@ import DescenderLines from './descender-lines'; import BlockNavigationBlockContents from './block-contents'; import BlockSettingsDropdown from '../block-settings-menu/block-settings-dropdown'; import { useBlockNavigationContext } from './context'; +import BlockDraggable from '../block-draggable'; export default function BlockNavigationBlock( { block, @@ -71,112 +72,126 @@ export default function BlockNavigationBlock( { }, [ withExperimentalFeatures, isSelected ] ); return ( - setIsHovered( true ) } - onMouseLeave={ () => setIsHovered( false ) } - onFocus={ () => setIsFocused( true ) } - onBlur={ () => setIsFocused( false ) } - level={ level } - position={ position } - rowCount={ rowCount } - path={ path } + - - { ( { ref, tabIndex, onFocus } ) => ( -
- - onClick( block.clientId ) } - isSelected={ isSelected } - position={ position } - siblingBlockCount={ siblingBlockCount } - level={ level } - ref={ ref } - tabIndex={ tabIndex } - onFocus={ onFocus } - /> -
- ) } -
- { hasRenderedMovers && ( - <> + { ( { isDraggable, onDraggableStart, onDraggableEnd } ) => ( + setIsHovered( true ) } + onMouseLeave={ () => setIsHovered( false ) } + onFocus={ () => setIsFocused( true ) } + onBlur={ () => setIsFocused( false ) } + level={ level } + position={ position } + rowCount={ rowCount } + path={ path } + draggable={ isDraggable } + onDragStart={ onDraggableStart } + onDragEnd={ onDraggableEnd } + > - - { ( { ref, tabIndex, onFocus } ) => ( - ( +
+ - ) } - - - { ( { ref, tabIndex, onFocus } ) => ( - onClick( block.clientId ) } + isSelected={ isSelected } + position={ position } + siblingBlockCount={ siblingBlockCount } + level={ level } ref={ ref } tabIndex={ tabIndex } onFocus={ onFocus } /> - ) } - +
+ ) }
- - ) } + { hasRenderedMovers && ( + <> + + + { ( { ref, tabIndex, onFocus } ) => ( + + ) } + + + { ( { ref, tabIndex, onFocus } ) => ( + + ) } + + + + ) } - { withExperimentalFeatures && ( - - { ( { ref, tabIndex, onFocus } ) => ( - - { ( { onClose } ) => ( - - { - // If clientId is already selected, it won't be focused (see block-wrapper.js) - // This removes the selection first to ensure the focus will always switch. - await selectEditorBlock( null ); - await selectEditorBlock( clientId ); - onClose(); - } } - > - { __( 'Go to block' ) } - - + { ( { ref, tabIndex, onFocus } ) => ( + + { ( { onClose } ) => ( + + { + // If clientId is already selected, it won't be focused (see block-wrapper.js) + // This removes the selection first to ensure the focus will always switch. + await selectEditorBlock( + null + ); + await selectEditorBlock( + clientId + ); + onClose(); + } } + > + { __( 'Go to block' ) } + + + ) } + ) } - + ) } - +
) } -
+ ); } From a04561a72176a0ec69680aa2cc4d5a673c6753f8 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 10 Jul 2020 17:07:51 +0800 Subject: [PATCH 03/45] Add block navigation drop zone hook --- .../use-block-navigation-drop-zone.js | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js new file mode 100644 index 0000000000000..b6fcee4a4a1e2 --- /dev/null +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -0,0 +1,133 @@ +/** + * WordPress dependencies + */ +import { __unstableUseDropZone as useDropZone } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { getDistanceToNearestEdge } from '@wordpress/dom'; +import { useEffect, useRef, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import useOnHTMLDrop from '../use-block-drop-zone/use-on-html-drop'; +import useOnFileDrop from '../use-block-drop-zone/use-on-file-drop'; +import useOnBlockDrop from '../use-block-drop-zone/use-on-block-drop'; + +function getDropTargetBlocksData( ref, getRootClientId, getBlockIndex ) { + if ( ! ref.current ) { + return; + } + + const blockElements = Array.from( + ref.current.querySelectorAll( '[data-block]' ) + ); + + return blockElements.map( ( blockElement ) => { + const clientId = blockElement.dataset.block; + const rootClientId = getRootClientId( clientId ); + + return { + clientId, + rootClientId, + blockIndex: getBlockIndex( clientId, rootClientId ), + element: blockElement, + orientation: 'vertical', + }; + } ); +} + +// Block navigation is always a vertical list, so only allow dropping +// to the above or below a block. +const ALLOWED_DROP_EDGES = [ 'top', 'bottom' ]; + +function getBlockNavigationDropTarget( blocksData, position ) { + let offset; + let candidateBlockData; + let candidateDistance; + + blocksData.forEach( ( blockData ) => { + const rect = blockData.element.getBoundingClientRect(); + const [ distance, edge ] = getDistanceToNearestEdge( + position, + rect, + ALLOWED_DROP_EDGES + ); + + if ( candidateDistance === undefined || distance < candidateDistance ) { + candidateDistance = distance; + candidateBlockData = blockData; + offset = edge === 'bottom' ? 1 : 0; + } + } ); + + if ( ! candidateBlockData ) { + return; + } + + return { + rootClientId: candidateBlockData.rootClientId, + blockIndex: candidateBlockData.blockIndex + offset, + }; +} + +export default function useBlockNavigationDropZone( ref ) { + const { getBlockRootClientId, getBlockIndex } = useSelect( ( select ) => { + const { + getBlockRootClientId: _getBlockRootClientId, + getBlockIndex: _getBlockIndex, + } = select( 'core/block-editor' ); + return { + getBlockRootClientId: _getBlockRootClientId, + getBlockIndex: _getBlockIndex, + }; + }, [] ); + + const [ target = {}, setTarget ] = useState(); + const { + rootClientId: targetRootClientId, + blockIndex: targetBlockIndex, + } = target; + + const onHTMLDrop = useOnHTMLDrop( targetRootClientId, targetBlockIndex ); + const onFilesDrop = useOnFileDrop( targetRootClientId, targetBlockIndex ); + const onDrop = useOnBlockDrop( targetRootClientId, targetBlockIndex ); + + const { position } = useDropZone( { + element: ref, + onFilesDrop, + onHTMLDrop, + onDrop, + withPosition: true, + } ); + + const hasPosition = !! position; + const blocksData = useRef(); + + // When the user starts dragging, build a list of block elements. + useEffect( () => { + if ( hasPosition ) { + blocksData.current = getDropTargetBlocksData( + ref, + getBlockRootClientId, + getBlockIndex + ); + } + }, [ hasPosition ] ); + + // Calculate the drop target based on the drag position. + useEffect( () => { + if ( position ) { + const newTarget = getBlockNavigationDropTarget( + blocksData.current, + position + ); + if ( target ) { + setTarget( newTarget ); + } + } + }, [ position ] ); + + if ( position ) { + return target; + } +} From a99b86b6cb92a79cd7bc74a9919a193691c39c2a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 10 Jul 2020 17:11:01 +0800 Subject: [PATCH 04/45] Allow tree grid component to take a ref --- packages/components/src/tree-grid/index.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/components/src/tree-grid/index.js b/packages/components/src/tree-grid/index.js index 8221efaac3366..cd5d6bb2a3cd3 100644 --- a/packages/components/src/tree-grid/index.js +++ b/packages/components/src/tree-grid/index.js @@ -7,7 +7,7 @@ import { includes } from 'lodash'; * WordPress dependencies */ import { focus } from '@wordpress/dom'; -import { useCallback } from '@wordpress/element'; +import { forwardRef, useCallback } from '@wordpress/element'; import { UP, DOWN, LEFT, RIGHT } from '@wordpress/keycodes'; /** @@ -39,11 +39,11 @@ function getRowFocusables( rowElement ) { * Renders both a table and tbody element, used to create a tree hierarchy. * * @see https://github.com/WordPress/gutenberg/blob/master/packages/components/src/tree-grid/README.md - * * @param {Object} props Component props. - * @param {WPElement} props.children Children to be rendered + * @param {WPElement} props.children Children to be rendered. + * @param {Object} ref A ref to the underlying DOM table element. */ -export default function TreeGrid( { children, ...props } ) { +function TreeGrid( { children, ...props }, ref ) { const onKeyDown = useCallback( ( event ) => { const { keyCode, metaKey, ctrlKey, altKey, shiftKey } = event; @@ -147,17 +147,24 @@ export default function TreeGrid( { children, ...props } ) { } }, [] ); + /* Disable reason: A treegrid is implemented using a table element. */ + /* eslint-disable jsx-a11y/no-noninteractive-element-to-interactive-role */ return ( - { /* Disable reason: A treegrid is implemented using a table element. */ } - { /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role */ } - +
{ children }
); + /* eslint-enable jsx-a11y/no-noninteractive-element-to-interactive-role */ } +export default forwardRef( TreeGrid ); export { default as TreeGridRow } from './row'; export { default as TreeGridCell } from './cell'; export { default as TreeGridItem } from './item'; From c3aadf6a310340b72bfe47a0f1bafc760a324896 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 10 Jul 2020 17:12:41 +0800 Subject: [PATCH 05/45] Add drop zone to block navigation tree --- .../block-editor/src/components/block-navigation/tree.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/tree.js b/packages/block-editor/src/components/block-navigation/tree.js index b289df051273f..ba7687923fecf 100644 --- a/packages/block-editor/src/components/block-navigation/tree.js +++ b/packages/block-editor/src/components/block-navigation/tree.js @@ -3,7 +3,7 @@ */ import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components'; -import { useMemo } from '@wordpress/element'; +import { useMemo, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; */ import BlockNavigationBranch from './branch'; import { BlockNavigationContext } from './context'; +import useBlockNavigationDropZone from './use-block-navigation-drop-zone'; /** * Wrap `BlockNavigationRows` with `TreeGrid`. BlockNavigationRows is a @@ -24,17 +25,21 @@ export default function BlockNavigationTree( { __experimentalFeatures, ...props } ) { + const treeGridRef = useRef(); + const blockDropTarget = useBlockNavigationDropZone( treeGridRef ); const contextValue = useMemo( () => ( { __experimentalFeatures, + blockDropTarget, } ), - [ __experimentalFeatures ] + [ __experimentalFeatures, blockDropTarget ] ); return ( From 4004f3a3981b23ee45ffa75f58dca2198c14eea5 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 10 Jul 2020 17:13:46 +0800 Subject: [PATCH 06/45] Allow custom elementId in BlockDraggable component --- packages/block-editor/src/components/block-draggable/index.js | 3 ++- 1 file changed, 2 insertions(+), 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 a947f4ea858f1..238a86c8937ea 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -17,6 +17,7 @@ const BlockDraggable = ( { cloneClassname, onDragStart, onDragEnd, + elementId, } ) => { const { srcRootClientId, isDraggable } = useSelect( ( select ) => { @@ -68,7 +69,7 @@ const BlockDraggable = ( { return ( { startDraggingBlocks( clientIds ); From 73f720c50ef5671bc6dbf8bbd098068f5a32b25a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 14 Jul 2020 11:14:10 +0800 Subject: [PATCH 07/45] Move block draggable to block contents and add simple initial drop indicator --- .../block-navigation/block-contents.js | 95 +++++--- .../block-navigation/block-select-button.js | 6 + .../src/components/block-navigation/block.js | 203 ++++++++---------- .../components/block-navigation/style.scss | 4 + 4 files changed, 176 insertions(+), 132 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index e83e5bb42aa81..3b9dc0fd888c1 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -1,6 +1,12 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ +import { useSelect } from '@wordpress/data'; import { forwardRef } from '@wordpress/element'; /** @@ -9,6 +15,7 @@ import { forwardRef } from '@wordpress/element'; import { useBlockNavigationContext } from './context'; import BlockNavigationBlockSlot from './block-slot'; import BlockNavigationBlockSelectButton from './block-select-button'; +import BlockDraggable from '../block-draggable'; const BlockNavigationBlockContents = forwardRef( ( @@ -25,32 +32,72 @@ const BlockNavigationBlockContents = forwardRef( ) => { const { __experimentalFeatures: withBlockNavigationSlots, + blockDropTarget = {}, } = useBlockNavigationContext(); - return withBlockNavigationSlots ? ( - - ) : ( - + const rootClientId = useSelect( + ( select ) => + select( 'core/block-editor' ).getBlockRootClientId( + block.clientId + ), + [ block.rootClientId ] + ); + + const { + rootClientId: dropTargetRootClientId, + blockIndex: dropTargetBlockIndex, + } = blockDropTarget; + + const isDropTarget = + ( dropTargetRootClientId === rootClientId || + ( dropTargetRootClientId === '' && + rootClientId === undefined ) ) && + position === dropTargetBlockIndex + 1; + + const className = classnames( + 'block-editor-block-navigation-block-contents', + { 'is-drop-target': isDropTarget } + ); + + return ( + + { ( { isDraggable, onDraggableStart, onDraggableEnd } ) => + withBlockNavigationSlots ? ( + + ) : ( + + ) + } + ); } ); diff --git a/packages/block-editor/src/components/block-navigation/block-select-button.js b/packages/block-editor/src/components/block-navigation/block-select-button.js index 17042e3951d57..2e9956d5387e1 100644 --- a/packages/block-editor/src/components/block-navigation/block-select-button.js +++ b/packages/block-editor/src/components/block-navigation/block-select-button.js @@ -32,6 +32,9 @@ function BlockNavigationBlockSelectButton( level, tabIndex, onFocus, + onDragStart, + onDragEnd, + draggable, }, ref ) { @@ -59,6 +62,9 @@ function BlockNavigationBlockSelectButton( ref={ ref } tabIndex={ tabIndex } onFocus={ onFocus } + onDragStart={ onDragStart } + onDragEnd={ onDragEnd } + draggable={ draggable } > { blockDisplayName } diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index a2fae4809a7d5..bfd7ed9d7f12e 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -29,7 +29,6 @@ import DescenderLines from './descender-lines'; import BlockNavigationBlockContents from './block-contents'; import BlockSettingsDropdown from '../block-settings-menu/block-settings-dropdown'; import { useBlockNavigationContext } from './context'; -import BlockDraggable from '../block-draggable'; export default function BlockNavigationBlock( { block, @@ -72,126 +71,114 @@ export default function BlockNavigationBlock( { }, [ withExperimentalFeatures, isSelected ] ); return ( - setIsHovered( true ) } + onMouseLeave={ () => setIsHovered( false ) } + onFocus={ () => setIsFocused( true ) } + onBlur={ () => setIsFocused( false ) } + level={ level } + position={ position } + rowCount={ rowCount } + path={ path } + id={ `block-navigation-block-${ block.clientId }` } + data-block={ clientId } > - { ( { isDraggable, onDraggableStart, onDraggableEnd } ) => ( - setIsHovered( true ) } - onMouseLeave={ () => setIsHovered( false ) } - onFocus={ () => setIsFocused( true ) } - onBlur={ () => setIsFocused( false ) } - level={ level } - position={ position } - rowCount={ rowCount } - path={ path } - draggable={ isDraggable } - onDragStart={ onDraggableStart } - onDragEnd={ onDraggableEnd } - > + + { ( { ref, tabIndex, onFocus } ) => ( +
+ + onClick( block.clientId ) } + isSelected={ isSelected } + position={ position } + siblingBlockCount={ siblingBlockCount } + level={ level } + ref={ ref } + tabIndex={ tabIndex } + onFocus={ onFocus } + /> +
+ ) } +
+ { hasRenderedMovers && ( + <> - { ( { ref, tabIndex, onFocus } ) => ( -
- + { ( { ref, tabIndex, onFocus } ) => ( + - onClick( block.clientId ) } - isSelected={ isSelected } - position={ position } - siblingBlockCount={ siblingBlockCount } - level={ level } + ) } + + + { ( { ref, tabIndex, onFocus } ) => ( + -
- ) } + ) } +
- { hasRenderedMovers && ( - <> - - - { ( { ref, tabIndex, onFocus } ) => ( - - ) } - - - { ( { ref, tabIndex, onFocus } ) => ( - - ) } - - - - ) } + + ) } - { withExperimentalFeatures && ( - + { ( { ref, tabIndex, onFocus } ) => ( + - { ( { ref, tabIndex, onFocus } ) => ( - - { ( { onClose } ) => ( - - { - // If clientId is already selected, it won't be focused (see block-wrapper.js) - // This removes the selection first to ensure the focus will always switch. - await selectEditorBlock( - null - ); - await selectEditorBlock( - clientId - ); - onClose(); - } } - > - { __( 'Go to block' ) } - - - ) } - + { ( { onClose } ) => ( + + { + // If clientId is already selected, it won't be focused (see block-wrapper.js) + // This removes the selection first to ensure the focus will always switch. + await selectEditorBlock( null ); + await selectEditorBlock( clientId ); + onClose(); + } } + > + { __( 'Go to block' ) } + + ) } - + ) } -
+ ) } -
+ ); } diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index 4aa404d588856..ebb3c9121c468 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -40,6 +40,10 @@ $tree-item-height: 36px; color: $dark-gray-600; border-radius: 2px; + &.is-drop-target { + border-top: 2px solid blue; + } + .components-modal__content & { padding-left: 0; padding-right: 0; From 46b37814cc157156b2f435f832a1824e5a3cc7bc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 15 Jul 2020 13:28:36 +0800 Subject: [PATCH 08/45] Fix block drop index --- .../src/components/use-block-drop-zone/use-on-block-drop.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js index e4dea53746579..de95c4e32dbf7 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js +++ b/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js @@ -45,8 +45,8 @@ export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { } = select( 'core/block-editor' ); return { - _getBlockIndex, - _getClientIdsOfDescendants, + getBlockIndex: _getBlockIndex, + getClientIdsOfDescendants: _getClientIdsOfDescendants, }; }, [] From 4c60abe223be8e6626d8f5205116aac3afe4eeee Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 16 Jul 2020 17:35:11 +0800 Subject: [PATCH 09/45] Try allowing blocks to be nested when dragging below, first attempt --- .../use-block-navigation-drop-zone.js | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index b6fcee4a4a1e2..4bd4ea5abb6bf 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -13,11 +13,24 @@ import useOnHTMLDrop from '../use-block-drop-zone/use-on-html-drop'; import useOnFileDrop from '../use-block-drop-zone/use-on-file-drop'; import useOnBlockDrop from '../use-block-drop-zone/use-on-block-drop'; -function getDropTargetBlocksData( ref, getRootClientId, getBlockIndex ) { +function getDropTargetBlocksData( + ref, + dragEventType, + getRootClientId, + getBlockIndex, + getDraggedBlockClientIds, + canInsertBlocks +) { if ( ! ref.current ) { return; } + const isBlockDrag = dragEventType === 'default'; + + const draggedBlockClientIds = isBlockDrag + ? getDraggedBlockClientIds() + : undefined; + const blockElements = Array.from( ref.current.querySelectorAll( '[data-block]' ) ); @@ -32,6 +45,12 @@ function getDropTargetBlocksData( ref, getRootClientId, getBlockIndex ) { blockIndex: getBlockIndex( clientId, rootClientId ), element: blockElement, orientation: 'vertical', + canInsertDraggedBlocksAsSibling: isBlockDrag + ? canInsertBlocks( draggedBlockClientIds, rootClientId ) + : true, + canInsertDraggedBlocksAsChild: isBlockDrag + ? canInsertBlocks( draggedBlockClientIds, clientId ) + : true, }; } ); } @@ -41,7 +60,7 @@ function getDropTargetBlocksData( ref, getRootClientId, getBlockIndex ) { const ALLOWED_DROP_EDGES = [ 'top', 'bottom' ]; function getBlockNavigationDropTarget( blocksData, position ) { - let offset; + let candidateEdge; let candidateBlockData; let candidateDistance; @@ -56,7 +75,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { if ( candidateDistance === undefined || distance < candidateDistance ) { candidateDistance = distance; candidateBlockData = blockData; - offset = edge === 'bottom' ? 1 : 0; + candidateEdge = edge; } } ); @@ -64,6 +83,19 @@ function getBlockNavigationDropTarget( blocksData, position ) { return; } + const isDraggingBelow = candidateEdge === 'bottom'; + + // If the user is dragging towards the bottom of the block interpret that + // they're trying to next the dragged block. + if ( isDraggingBelow && candidateBlockData.canInsertDraggedBlocksAsChild ) { + return { + rootClientId: candidateBlockData.clientId, + blockIndex: 0, + }; + } + + const offset = isDraggingBelow ? 1 : 0; + return { rootClientId: candidateBlockData.rootClientId, blockIndex: candidateBlockData.blockIndex + offset, @@ -71,14 +103,23 @@ function getBlockNavigationDropTarget( blocksData, position ) { } export default function useBlockNavigationDropZone( ref ) { - const { getBlockRootClientId, getBlockIndex } = useSelect( ( select ) => { + const { + canInsertBlocks, + getBlockRootClientId, + getBlockIndex, + getDraggedBlockClientIds, + } = useSelect( ( select ) => { const { + canInsertBlocks: _canInsertBlocks, getBlockRootClientId: _getBlockRootClientId, getBlockIndex: _getBlockIndex, + getDraggedBlockClientIds: _getDraggedBlockClientIds, } = select( 'core/block-editor' ); return { + canInsertBlocks: _canInsertBlocks, getBlockRootClientId: _getBlockRootClientId, getBlockIndex: _getBlockIndex, + getDraggedBlockClientIds: _getDraggedBlockClientIds, }; }, [] ); @@ -92,7 +133,7 @@ export default function useBlockNavigationDropZone( ref ) { const onFilesDrop = useOnFileDrop( targetRootClientId, targetBlockIndex ); const onDrop = useOnBlockDrop( targetRootClientId, targetBlockIndex ); - const { position } = useDropZone( { + const { position, type: dragEventType } = useDropZone( { element: ref, onFilesDrop, onHTMLDrop, @@ -108,8 +149,11 @@ export default function useBlockNavigationDropZone( ref ) { if ( hasPosition ) { blocksData.current = getDropTargetBlocksData( ref, + dragEventType, getBlockRootClientId, - getBlockIndex + getBlockIndex, + getDraggedBlockClientIds, + canInsertBlocks ); } }, [ hasPosition ] ); From 7ec4e6ed6adbceee561d425c2061941427a53d74 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 7 Aug 2020 12:06:40 +0800 Subject: [PATCH 10/45] Break early when cursor is contained by block --- .../use-block-navigation-drop-zone.js | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 4bd4ea5abb6bf..350744cd077d6 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -55,6 +55,15 @@ function getDropTargetBlocksData( } ); } +function isPointContainedByRect( point, rect ) { + return ( + rect.left <= point.x && + rect.right >= point.x && + rect.top <= point.y && + rect.bottom >= point.y + ); +} + // Block navigation is always a vertical list, so only allow dropping // to the above or below a block. const ALLOWED_DROP_EDGES = [ 'top', 'bottom' ]; @@ -64,7 +73,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { let candidateBlockData; let candidateDistance; - blocksData.forEach( ( blockData ) => { + for ( const blockData of blocksData ) { const rect = blockData.element.getBoundingClientRect(); const [ distance, edge ] = getDistanceToNearestEdge( position, @@ -77,7 +86,17 @@ function getBlockNavigationDropTarget( blocksData, position ) { candidateBlockData = blockData; candidateEdge = edge; } - } ); + + // If the mouse position is within the block, break early + // as the user would intend to drop either before or after + // this block. + // + // This solves an issue where some rows in the block navigation + // tree overlap slightly due to sub-pixel rendering. + if ( isPointContainedByRect( position, rect ) ) { + break; + } + } if ( ! candidateBlockData ) { return; From 50cef8790ee084db8301e4e69306ec2236e29426 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 7 Aug 2020 13:24:11 +0800 Subject: [PATCH 11/45] Improve drop zone detection --- .../block-navigation/block-contents.js | 26 ++++++++++++++----- .../components/block-navigation/style.scss | 10 ++++++- .../use-block-navigation-drop-zone.js | 18 ++++++++++--- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index 3b9dc0fd888c1..3f8c456dfd3b6 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -39,7 +39,7 @@ const BlockNavigationBlockContents = forwardRef( ( select ) => select( 'core/block-editor' ).getBlockRootClientId( block.clientId - ), + ) || '', [ block.rootClientId ] ); @@ -48,15 +48,27 @@ const BlockNavigationBlockContents = forwardRef( blockIndex: dropTargetBlockIndex, } = blockDropTarget; - const isDropTarget = - ( dropTargetRootClientId === rootClientId || - ( dropTargetRootClientId === '' && - rootClientId === undefined ) ) && - position === dropTargetBlockIndex + 1; + const blockIndex = position - 1; + + const isDroppingBefore = + dropTargetRootClientId === rootClientId && + blockIndex === dropTargetBlockIndex; + const isDroppingAfter = + dropTargetRootClientId === rootClientId && + position === siblingBlockCount && + dropTargetBlockIndex === siblingBlockCount; + const isDroppingToInnerBlocks = + block.clientId === dropTargetRootClientId && + ! block.innerBlocks?.length && + dropTargetBlockIndex === 0; const className = classnames( 'block-editor-block-navigation-block-contents', - { 'is-drop-target': isDropTarget } + { + 'is-dropping-before': isDroppingBefore, + 'is-dropping-after': isDroppingAfter, + 'is-dropping-to-inner-blocks': isDroppingToInnerBlocks, + } ); return ( diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index ebb3c9121c468..5ba5128dd7c57 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -40,10 +40,18 @@ $tree-item-height: 36px; color: $dark-gray-600; border-radius: 2px; - &.is-drop-target { + &.is-dropping-before { border-top: 2px solid blue; } + &.is-dropping-after { + border-bottom: 2px solid green; + } + + &.is-dropping-to-inner-blocks { + border-bottom: 2px solid red; + } + .components-modal__content & { padding-left: 0; padding-right: 0; diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 350744cd077d6..a8bb56f3c75fe 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -64,6 +64,11 @@ function isPointContainedByRect( point, rect ) { ); } +function isDroppingToInnerBlocks( point, rect ) { + const blockCenterX = rect.left + rect.width / 2; + return point.x > blockCenterX; +} + // Block navigation is always a vertical list, so only allow dropping // to the above or below a block. const ALLOWED_DROP_EDGES = [ 'top', 'bottom' ]; @@ -72,6 +77,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { let candidateEdge; let candidateBlockData; let candidateDistance; + let candidateRect; for ( const blockData of blocksData ) { const rect = blockData.element.getBoundingClientRect(); @@ -85,6 +91,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { candidateDistance = distance; candidateBlockData = blockData; candidateEdge = edge; + candidateRect = rect; } // If the mouse position is within the block, break early @@ -104,9 +111,13 @@ function getBlockNavigationDropTarget( blocksData, position ) { const isDraggingBelow = candidateEdge === 'bottom'; - // If the user is dragging towards the bottom of the block interpret that - // they're trying to next the dragged block. - if ( isDraggingBelow && candidateBlockData.canInsertDraggedBlocksAsChild ) { + // If the user is dragging towards the bottom of the block check whether + // they might be inserting as a child. + if ( + isDraggingBelow && + candidateBlockData.canInsertDraggedBlocksAsChild && + isDroppingToInnerBlocks( position, candidateRect ) + ) { return { rootClientId: candidateBlockData.clientId, blockIndex: 0, @@ -114,7 +125,6 @@ function getBlockNavigationDropTarget( blocksData, position ) { } const offset = isDraggingBelow ? 1 : 0; - return { rootClientId: candidateBlockData.rootClientId, blockIndex: candidateBlockData.blockIndex + offset, From 9f4a58e5712b7f173e4d493e3cef5c506c492207 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 7 Aug 2020 13:50:49 +0800 Subject: [PATCH 12/45] Fix issue where incorrect drop position selected when blocks overlap --- .../use-block-navigation-drop-zone.js | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index a8bb56f3c75fe..4c48a89130385 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -18,6 +18,7 @@ function getDropTargetBlocksData( dragEventType, getRootClientId, getBlockIndex, + getBlockCount, getDraggedBlockClientIds, canInsertBlocks ) { @@ -45,6 +46,7 @@ function getDropTargetBlocksData( blockIndex: getBlockIndex( clientId, rootClientId ), element: blockElement, orientation: 'vertical', + innerBlockCount: getBlockCount( clientId ), canInsertDraggedBlocksAsSibling: isBlockDrag ? canInsertBlocks( draggedBlockClientIds, rootClientId ) : true, @@ -64,7 +66,10 @@ function isPointContainedByRect( point, rect ) { ); } -function isDroppingToInnerBlocks( point, rect ) { +function isDroppingToInnerBlocks( point, rect, innerBlockCount ) { + if ( innerBlockCount > 0 ) { + return true; + } const blockCenterX = rect.left + rect.width / 2; return point.x > blockCenterX; } @@ -87,21 +92,26 @@ function getBlockNavigationDropTarget( blocksData, position ) { ALLOWED_DROP_EDGES ); - if ( candidateDistance === undefined || distance < candidateDistance ) { + const isCursorWithinBlock = isPointContainedByRect( position, rect ); + if ( + candidateDistance === undefined || + distance < candidateDistance || + isCursorWithinBlock + ) { candidateDistance = distance; candidateBlockData = blockData; candidateEdge = edge; candidateRect = rect; - } - // If the mouse position is within the block, break early - // as the user would intend to drop either before or after - // this block. - // - // This solves an issue where some rows in the block navigation - // tree overlap slightly due to sub-pixel rendering. - if ( isPointContainedByRect( position, rect ) ) { - break; + // If the mouse position is within the block, break early + // as the user would intend to drop either before or after + // this block. + // + // This solves an issue where some rows in the block navigation + // tree overlap slightly due to sub-pixel rendering. + if ( isCursorWithinBlock ) { + break; + } } } @@ -116,7 +126,11 @@ function getBlockNavigationDropTarget( blocksData, position ) { if ( isDraggingBelow && candidateBlockData.canInsertDraggedBlocksAsChild && - isDroppingToInnerBlocks( position, candidateRect ) + isDroppingToInnerBlocks( + position, + candidateRect, + candidateBlockData.innerBlockCount + ) ) { return { rootClientId: candidateBlockData.clientId, @@ -136,18 +150,21 @@ export default function useBlockNavigationDropZone( ref ) { canInsertBlocks, getBlockRootClientId, getBlockIndex, + getBlockCount, getDraggedBlockClientIds, } = useSelect( ( select ) => { const { canInsertBlocks: _canInsertBlocks, getBlockRootClientId: _getBlockRootClientId, getBlockIndex: _getBlockIndex, + getBlockCount: _getBlockCount, getDraggedBlockClientIds: _getDraggedBlockClientIds, } = select( 'core/block-editor' ); return { canInsertBlocks: _canInsertBlocks, getBlockRootClientId: _getBlockRootClientId, getBlockIndex: _getBlockIndex, + getBlockCount: _getBlockCount, getDraggedBlockClientIds: _getDraggedBlockClientIds, }; }, [] ); @@ -181,6 +198,7 @@ export default function useBlockNavigationDropZone( ref ) { dragEventType, getBlockRootClientId, getBlockIndex, + getBlockCount, getDraggedBlockClientIds, canInsertBlocks ); From b7bb995cacb76b81b9bcb2c0ebb4eec7351ec019 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 7 Aug 2020 14:18:05 +0800 Subject: [PATCH 13/45] Improve drop zone indicators --- .../components/block-navigation/style.scss | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index 5ba5128dd7c57..2bd66e12340b6 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -39,17 +39,39 @@ $tree-item-height: 36px; text-align: left; color: $dark-gray-600; border-radius: 2px; + position: relative; - &.is-dropping-before { - border-top: 2px solid blue; + &.is-dropping-before::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + top: -6px; + right: 0; + left: 0; + border-top: 4px solid var(--wp-admin-theme-color); } - &.is-dropping-after { - border-bottom: 2px solid green; + &.is-dropping-after::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + bottom: -6px; + right: 0; + left: 0; + border-bottom: 4px solid var(--wp-admin-theme-color); } - &.is-dropping-to-inner-blocks { - border-bottom: 2px solid red; + &.is-dropping-to-inner-blocks::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + bottom: -6px; + right: 0; + left: $icon-size + 6px; + border-bottom: 4px solid var(--wp-admin-theme-color); } .components-modal__content & { From 002f2afda496f380d5fa9483514520e68d693ab9 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 11 Aug 2020 17:03:57 +0800 Subject: [PATCH 14/45] Hide dragged block in list view --- .../src/components/block-navigation/block.js | 22 ++++++++++++++++++- .../components/block-navigation/style.scss | 4 ++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index bfd7ed9d7f12e..cb59ad55541ca 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -15,7 +15,7 @@ import { import { __ } from '@wordpress/i18n'; import { moreVertical } from '@wordpress/icons'; import { useState, useRef, useEffect } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -45,6 +45,19 @@ export default function BlockNavigationBlock( { const cellRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); const [ isFocused, setIsFocused ] = useState( false ); + const { isDraggingBlocks, getDraggedBlockClientIds } = useSelect( + ( select ) => { + const { + isDraggingBlocks: _isDraggingBlocks, + getDraggedBlockClientIds: _getDraggedBlockClientIds, + } = select( 'core/block-editor' ); + return { + isDraggingBlocks: _isDraggingBlocks(), + getDraggedBlockClientIds: _getDraggedBlockClientIds, + }; + } + ); + const { selectBlock: selectEditorBlock } = useDispatch( 'core/block-editor' ); @@ -70,10 +83,17 @@ export default function BlockNavigationBlock( { } }, [ withExperimentalFeatures, isSelected ] ); + let isDragging = false; + if ( isDraggingBlocks ) { + const draggedBlockClientIds = getDraggedBlockClientIds(); + isDragging = !! draggedBlockClientIds.includes( block.clientId ); + } + return ( setIsHovered( true ) } onMouseLeave={ () => setIsHovered( false ) } diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index 2bd66e12340b6..e7d9abf88edbc 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -28,6 +28,10 @@ $tree-item-height: 36px; // Use position relative for row animation. position: relative; + &.is-dragging { + display: none; + } + .block-editor-block-navigation-block-contents { display: flex; align-items: center; From b68659167082d39bdc8dafde9134959fb14b2e59 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 11 Aug 2020 17:27:26 +0800 Subject: [PATCH 15/45] Refactor selector --- .../src/components/block-navigation/block.js | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index cb59ad55541ca..30cb8a470262f 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -45,17 +45,10 @@ export default function BlockNavigationBlock( { const cellRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); const [ isFocused, setIsFocused ] = useState( false ); - const { isDraggingBlocks, getDraggedBlockClientIds } = useSelect( - ( select ) => { - const { - isDraggingBlocks: _isDraggingBlocks, - getDraggedBlockClientIds: _getDraggedBlockClientIds, - } = select( 'core/block-editor' ); - return { - isDraggingBlocks: _isDraggingBlocks(), - getDraggedBlockClientIds: _getDraggedBlockClientIds, - }; - } + const isDragging = useSelect( + ( select ) => + select( 'core/block-editor' ).isBlockBeingDragged( block.clientId ), + [ block.clientId ] ); const { selectBlock: selectEditorBlock } = useDispatch( @@ -83,12 +76,6 @@ export default function BlockNavigationBlock( { } }, [ withExperimentalFeatures, isSelected ] ); - let isDragging = false; - if ( isDraggingBlocks ) { - const draggedBlockClientIds = getDraggedBlockClientIds(); - isDragging = !! draggedBlockClientIds.includes( block.clientId ); - } - return ( Date: Tue, 11 Aug 2020 17:37:10 +0800 Subject: [PATCH 16/45] Hide dragged block children in list view --- .../src/components/block-navigation/block.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block.js b/packages/block-editor/src/components/block-navigation/block.js index 30cb8a470262f..da24893d49d0d 100644 --- a/packages/block-editor/src/components/block-navigation/block.js +++ b/packages/block-editor/src/components/block-navigation/block.js @@ -45,16 +45,24 @@ export default function BlockNavigationBlock( { const cellRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); const [ isFocused, setIsFocused ] = useState( false ); + const { clientId } = block; const isDragging = useSelect( - ( select ) => - select( 'core/block-editor' ).isBlockBeingDragged( block.clientId ), - [ block.clientId ] + ( select ) => { + const { isBlockBeingDragged, isAncestorBeingDragged } = select( + 'core/block-editor' + ); + + return ( + isBlockBeingDragged( clientId ) || + isAncestorBeingDragged( clientId ) + ); + }, + [ clientId ] ); const { selectBlock: selectEditorBlock } = useDispatch( 'core/block-editor' ); - const { clientId } = block; const hasSiblings = siblingBlockCount > 0; const hasRenderedMovers = showBlockMovers && hasSiblings; @@ -90,7 +98,7 @@ export default function BlockNavigationBlock( { position={ position } rowCount={ rowCount } path={ path } - id={ `block-navigation-block-${ block.clientId }` } + id={ `block-navigation-block-${ clientId }` } data-block={ clientId } > Date: Tue, 11 Aug 2020 17:41:52 +0800 Subject: [PATCH 17/45] Hide appender when dragging parent --- .../components/block-navigation/appender.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/block-editor/src/components/block-navigation/appender.js b/packages/block-editor/src/components/block-navigation/appender.js index 2d4db4e5f6b88..58bdb381e10e6 100644 --- a/packages/block-editor/src/components/block-navigation/appender.js +++ b/packages/block-editor/src/components/block-navigation/appender.js @@ -1,9 +1,15 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ import { __experimentalTreeGridCell as TreeGridCell } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -20,6 +26,19 @@ export default function BlockNavigationAppender( { terminatedLevels, path, } ) { + const isDragging = useSelect( + ( select ) => { + const { isBlockBeingDragged, isAncestorBeingDragged } = select( + 'core/block-editor' + ); + + return ( + isBlockBeingDragged( parentBlockClientId ) || + isAncestorBeingDragged( parentBlockClientId ) + ); + }, + [ parentBlockClientId ] + ); const instanceId = useInstanceId( BlockNavigationAppender ); const descriptionId = `block-navigation-appender-row__description_${ instanceId }`; @@ -32,6 +51,7 @@ export default function BlockNavigationAppender( { return ( Date: Tue, 11 Aug 2020 17:43:21 +0800 Subject: [PATCH 18/45] Fix block draggable chip error --- .../src/components/block-draggable/draggable-chip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4deed6aaef7d0..4ab64ad57dc68 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.js @@ -23,7 +23,7 @@ export default function BlockDraggableChip( { clientIds } ) { const [ firstId ] = clientIds; const blockName = getBlockName( firstId ); - return getBlockType( blockName ).icon; + return getBlockType( blockName )?.icon; }, [ clientIds ] ); From 45947ba911275eeceea665401ddc95d37a8079c7 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 19 Aug 2020 12:28:17 +0800 Subject: [PATCH 19/45] Refactor block drop event handlers into a single hook --- .../use-block-navigation-drop-zone.js | 15 +-- .../use-block-drop-zone/use-on-block-drop.js | 112 ------------------ .../use-block-drop-zone/use-on-file-drop.js | 37 ------ .../use-block-drop-zone/use-on-html-drop.js | 17 --- 4 files changed, 6 insertions(+), 175 deletions(-) delete mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js delete mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js delete mode 100644 packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 4c48a89130385..0f5e4e80a5312 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -9,9 +9,7 @@ import { useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies */ -import useOnHTMLDrop from '../use-block-drop-zone/use-on-html-drop'; -import useOnFileDrop from '../use-block-drop-zone/use-on-file-drop'; -import useOnBlockDrop from '../use-block-drop-zone/use-on-block-drop'; +import useOnBlockDrop from '../use-on-block-drop'; function getDropTargetBlocksData( ref, @@ -175,16 +173,15 @@ export default function useBlockNavigationDropZone( ref ) { blockIndex: targetBlockIndex, } = target; - const onHTMLDrop = useOnHTMLDrop( targetRootClientId, targetBlockIndex ); - const onFilesDrop = useOnFileDrop( targetRootClientId, targetBlockIndex ); - const onDrop = useOnBlockDrop( targetRootClientId, targetBlockIndex ); + const dropEventHandlers = useOnBlockDrop( + targetRootClientId, + targetBlockIndex + ); const { position, type: dragEventType } = useDropZone( { element: ref, - onFilesDrop, - onHTMLDrop, - onDrop, withPosition: true, + ...dropEventHandlers, } ); const hasPosition = !! position; diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js deleted file mode 100644 index de95c4e32dbf7..0000000000000 --- a/packages/block-editor/src/components/use-block-drop-zone/use-on-block-drop.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * WordPress dependencies - */ -import { useDispatch, useSelect } from '@wordpress/data'; - -/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */ - -/** - * Retrieve the data for a block drop event. - * - * @param {WPSyntheticEvent} event The drop event. - * - * @return {Object} An object with block drag and drop data. - */ -function parseDropEvent( event ) { - let result = { - srcRootClientId: null, - srcClientIds: null, - srcIndex: null, - type: null, - }; - - if ( ! event.dataTransfer ) { - return result; - } - - try { - result = Object.assign( - result, - JSON.parse( event.dataTransfer.getData( 'text' ) ) - ); - } catch ( err ) { - return result; - } - - return result; -} - -export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { - const { getBlockIndex, getClientIdsOfDescendants } = useSelect( - ( select ) => { - const { - getBlockIndex: _getBlockIndex, - getClientIdsOfDescendants: _getClientIdsOfDescendants, - } = select( 'core/block-editor' ); - - return { - getBlockIndex: _getBlockIndex, - getClientIdsOfDescendants: _getClientIdsOfDescendants, - }; - }, - [] - ); - - const { moveBlocksToPosition } = useDispatch( 'core/block-editor' ); - - return ( event ) => { - const { - srcRootClientId: sourceRootClientId, - srcClientIds: sourceClientIds, - type: dropType, - } = parseDropEvent( event ); - - // If the user isn't dropping a block, return early. - if ( dropType !== 'block' ) { - return; - } - - const sourceBlockIndex = getBlockIndex( - sourceClientIds[ 0 ], - sourceRootClientId - ); - - // If the user is dropping to the same position, return early. - if ( - sourceRootClientId === targetRootClientId && - sourceBlockIndex === targetBlockIndex - ) { - return; - } - - // If the user is attempting to drop a block within its own - // nested blocks, return early as this would create infinite - // recursion. - if ( - sourceClientIds.includes( targetRootClientId ) || - getClientIdsOfDescendants( sourceClientIds ).some( - ( id ) => id === targetRootClientId - ) - ) { - return; - } - - const isAtSameLevel = sourceRootClientId === targetRootClientId; - const draggedBlockCount = sourceClientIds.length; - - // If the block is kept at the same level and moved downwards, - // subtract to take into account that the blocks being dragged - // were removed from the block list above the insertion point. - const insertIndex = - isAtSameLevel && sourceBlockIndex < targetBlockIndex - ? targetBlockIndex - draggedBlockCount - : targetBlockIndex; - - moveBlocksToPosition( - sourceClientIds, - sourceRootClientId, - targetRootClientId, - insertIndex - ); - }; -} diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js deleted file mode 100644 index 78f7fe3d9e1fb..0000000000000 --- a/packages/block-editor/src/components/use-block-drop-zone/use-on-file-drop.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * WordPress dependencies - */ -import { getBlockTransforms, findTransform } from '@wordpress/blocks'; -import { useDispatch, useSelect } from '@wordpress/data'; - -export default function useOnFileDrop( rootClientId, blockIndex ) { - const hasUploadPermissions = useSelect( - ( select ) => - !! select( 'core/block-editor' ).getSettings().mediaUpload, - [] - ); - - const { insertBlocks, updateBlockAttributes } = useDispatch( - 'core/block-editor' - ); - - return ( files ) => { - if ( ! hasUploadPermissions ) { - return; - } - - const transformation = findTransform( - getBlockTransforms( 'from' ), - ( transform ) => - transform.type === 'files' && transform.isMatch( files ) - ); - - if ( transformation ) { - const blocks = transformation.transform( - files, - updateBlockAttributes - ); - insertBlocks( blocks, blockIndex, rootClientId ); - } - }; -} diff --git a/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js b/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js deleted file mode 100644 index 62c10eb0be350..0000000000000 --- a/packages/block-editor/src/components/use-block-drop-zone/use-on-html-drop.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * WordPress dependencies - */ -import { pasteHandler } from '@wordpress/blocks'; -import { useDispatch } from '@wordpress/data'; - -export default function useOnHTMLDrop( rootClientId, blockIndex ) { - const { insertBlocks } = useDispatch( 'core/block-editor' ); - - return ( HTML ) => { - const blocks = pasteHandler( { HTML, mode: 'BLOCKS' } ); - - if ( blocks.length ) { - insertBlocks( blocks, blockIndex, rootClientId ); - } - }; -} From 98f438f26031d74773b02471f32d62d20b0688e2 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 19 Aug 2020 16:40:55 +0800 Subject: [PATCH 20/45] Disallow dropping in invalid block context --- .../block-navigation/use-block-navigation-drop-zone.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 0f5e4e80a5312..e6380322c5903 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -136,6 +136,12 @@ function getBlockNavigationDropTarget( blocksData, position ) { }; } + // If dropping as a sibling, but block cannot be inserted in + // this context, return early. + if ( ! candidateBlockData.canInsertDraggedBlocksAsSibling ) { + return; + } + const offset = isDraggingBelow ? 1 : 0; return { rootClientId: candidateBlockData.rootClientId, From d6b1d3fdd9d02cc4c60b703abb3edf5a2d43aa1c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 19 Aug 2020 17:15:41 +0800 Subject: [PATCH 21/45] Allow nesting from both above and below the insertion point --- .../use-block-navigation-drop-zone.js | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index e6380322c5903..9b915c452532b 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -97,9 +97,26 @@ function getBlockNavigationDropTarget( blocksData, position ) { isCursorWithinBlock ) { candidateDistance = distance; - candidateBlockData = blockData; - candidateEdge = edge; - candidateRect = rect; + + const index = blocksData.indexOf( blockData ); + const previousBlockData = blocksData[ index - 1 ]; + + // If dragging near the top of a block and then preceding block + // is at the same level, use the preceding block as the candidate + // instead, as later it makes determining a nesting drop easier. + if ( + edge === 'top' && + previousBlockData && + previousBlockData.rootClientId === blockData.rootClientId + ) { + candidateBlockData = previousBlockData; + candidateEdge = 'bottom'; + candidateRect = previousBlockData.element.getBoundingClientRect(); + } else { + candidateBlockData = blockData; + candidateEdge = edge; + candidateRect = rect; + } // If the mouse position is within the block, break early // as the user would intend to drop either before or after @@ -120,7 +137,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { const isDraggingBelow = candidateEdge === 'bottom'; // If the user is dragging towards the bottom of the block check whether - // they might be inserting as a child. + // they might be trying to insert the block as a child. if ( isDraggingBelow && candidateBlockData.canInsertDraggedBlocksAsChild && From 2d7f5893ea065cf394a95fbb109f1c5db71ecfdc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 19 Aug 2020 17:25:15 +0800 Subject: [PATCH 22/45] remove unused data --- .../block-navigation/use-block-navigation-drop-zone.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 9b915c452532b..9034d70e20552 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -43,7 +43,6 @@ function getDropTargetBlocksData( rootClientId, blockIndex: getBlockIndex( clientId, rootClientId ), element: blockElement, - orientation: 'vertical', innerBlockCount: getBlockCount( clientId ), canInsertDraggedBlocksAsSibling: isBlockDrag ? canInsertBlocks( draggedBlockClientIds, rootClientId ) From 0c6dbfe7e6e7d07024d20ca8e0e5ae3eb90dfa70 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 19 Aug 2020 17:27:13 +0800 Subject: [PATCH 23/45] Fix comment --- .../block-navigation/use-block-navigation-drop-zone.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 9034d70e20552..41112433d3c1f 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -100,7 +100,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { const index = blocksData.indexOf( blockData ); const previousBlockData = blocksData[ index - 1 ]; - // If dragging near the top of a block and then preceding block + // If dragging near the top of a block and the preceding block // is at the same level, use the preceding block as the candidate // instead, as later it makes determining a nesting drop easier. if ( From 7db062302f50f38adf9bc49875a4d21543fb4b7c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 20 Aug 2020 11:07:31 +0800 Subject: [PATCH 24/45] Try a separate fixed position element for the drop indicator --- .../block-navigation/block-contents.js | 47 +--------------- .../block-navigation/drop-indicator.js | 55 +++++++++++++++++++ .../components/block-navigation/style.scss | 55 ++++++++----------- .../src/components/block-navigation/tree.js | 22 +++++--- .../use-block-navigation-drop-zone.js | 17 ++++-- 5 files changed, 103 insertions(+), 93 deletions(-) create mode 100644 packages/block-editor/src/components/block-navigation/drop-indicator.js diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index 3f8c456dfd3b6..d57b03b7acebe 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -1,12 +1,6 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; import { forwardRef } from '@wordpress/element'; /** @@ -32,45 +26,8 @@ const BlockNavigationBlockContents = forwardRef( ) => { const { __experimentalFeatures: withBlockNavigationSlots, - blockDropTarget = {}, } = useBlockNavigationContext(); - const rootClientId = useSelect( - ( select ) => - select( 'core/block-editor' ).getBlockRootClientId( - block.clientId - ) || '', - [ block.rootClientId ] - ); - - const { - rootClientId: dropTargetRootClientId, - blockIndex: dropTargetBlockIndex, - } = blockDropTarget; - - const blockIndex = position - 1; - - const isDroppingBefore = - dropTargetRootClientId === rootClientId && - blockIndex === dropTargetBlockIndex; - const isDroppingAfter = - dropTargetRootClientId === rootClientId && - position === siblingBlockCount && - dropTargetBlockIndex === siblingBlockCount; - const isDroppingToInnerBlocks = - block.clientId === dropTargetRootClientId && - ! block.innerBlocks?.length && - dropTargetBlockIndex === 0; - - const className = classnames( - 'block-editor-block-navigation-block-contents', - { - 'is-dropping-before': isDroppingBefore, - 'is-dropping-after': isDroppingAfter, - 'is-dropping-to-inner-blocks': isDroppingToInnerBlocks, - } - ); - return ( { + if ( ! dropTarget ) { + return; + } + const { clientId, position } = dropTarget; + const blockRect = getBlockRect( clientId ); + + if ( position === 'top' ) { + return { + top: blockRect.top, + height: 0, + left: blockRect.left, + width: blockRect.width, + }; + } else if ( position === 'inside' || position === 'bottom' ) { + return { + top: blockRect.bottom, + height: 0, + left: blockRect.left, + width: blockRect.width, + }; + } + }, [ dropTarget ] ); + + const className = useMemo( + () => + classnames( 'block-navigation-drop-indicator', { + 'is-hidden': ! dropTarget, + 'is-dropping-inside': dropTarget?.position === 'inside', + } ), + [ dropTarget ] + ); + + return
; +} diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index e7d9abf88edbc..f9f335218fb4f 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -45,39 +45,6 @@ $tree-item-height: 36px; border-radius: 2px; position: relative; - &.is-dropping-before::before { - content: ""; - position: absolute; - pointer-events: none; - transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - top: -6px; - right: 0; - left: 0; - border-top: 4px solid var(--wp-admin-theme-color); - } - - &.is-dropping-after::before { - content: ""; - position: absolute; - pointer-events: none; - transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - bottom: -6px; - right: 0; - left: 0; - border-bottom: 4px solid var(--wp-admin-theme-color); - } - - &.is-dropping-to-inner-blocks::before { - content: ""; - position: absolute; - pointer-events: none; - transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - bottom: -6px; - right: 0; - left: $icon-size + 6px; - border-bottom: 4px solid var(--wp-admin-theme-color); - } - .components-modal__content & { padding-left: 0; padding-right: 0; @@ -288,3 +255,25 @@ $tree-item-height: 36px; } } } + +.block-navigation-drop-indicator { + display: block; + position: fixed; + pointer-events: none; + + &::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + top: -2px; + right: 0; + left: 0; + bottom: -2px; + border-top: 4px solid var(--wp-admin-theme-color); + } + + &.is-dropping-inside::before { + left: $icon-size; + } +} diff --git a/packages/block-editor/src/components/block-navigation/tree.js b/packages/block-editor/src/components/block-navigation/tree.js index ba7687923fecf..35cb1f8b551f1 100644 --- a/packages/block-editor/src/components/block-navigation/tree.js +++ b/packages/block-editor/src/components/block-navigation/tree.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; */ import BlockNavigationBranch from './branch'; import { BlockNavigationContext } from './context'; +import BlockNavigationDropIndicator from './drop-indicator'; import useBlockNavigationDropZone from './use-block-navigation-drop-zone'; /** @@ -36,14 +37,17 @@ export default function BlockNavigationTree( { ); return ( - - - - - + <> + + + + + + + ); } diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 41112433d3c1f..956ecf23acb59 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -43,6 +43,7 @@ function getDropTargetBlocksData( rootClientId, blockIndex: getBlockIndex( clientId, rootClientId ), element: blockElement, + isDraggedBlock: draggedBlockClientIds.includes( clientId ), innerBlockCount: getBlockCount( clientId ), canInsertDraggedBlocksAsSibling: isBlockDrag ? canInsertBlocks( draggedBlockClientIds, rootClientId ) @@ -82,6 +83,10 @@ function getBlockNavigationDropTarget( blocksData, position ) { let candidateRect; for ( const blockData of blocksData ) { + if ( blockData.isDraggedBlock ) { + continue; + } + const rect = blockData.element.getBoundingClientRect(); const [ distance, edge ] = getDistanceToNearestEdge( position, @@ -106,7 +111,8 @@ function getBlockNavigationDropTarget( blocksData, position ) { if ( edge === 'top' && previousBlockData && - previousBlockData.rootClientId === blockData.rootClientId + previousBlockData.rootClientId === blockData.rootClientId && + ! previousBlockData.isDraggedBlock ) { candidateBlockData = previousBlockData; candidateEdge = 'bottom'; @@ -147,8 +153,8 @@ function getBlockNavigationDropTarget( blocksData, position ) { ) ) { return { - rootClientId: candidateBlockData.clientId, - blockIndex: 0, + clientId: candidateBlockData.clientId, + position: 'inside', }; } @@ -158,10 +164,9 @@ function getBlockNavigationDropTarget( blocksData, position ) { return; } - const offset = isDraggingBelow ? 1 : 0; return { - rootClientId: candidateBlockData.rootClientId, - blockIndex: candidateBlockData.blockIndex + offset, + clientId: candidateBlockData.clientId, + position: candidateEdge, }; } From 753008a5a455dd6045edd7b81fdb69119ab2a85f Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 20 Aug 2020 11:40:08 +0800 Subject: [PATCH 25/45] Update styles to improve drop zone indicator --- .../block-editor/src/components/block-navigation/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index f9f335218fb4f..9edce8ae1447a 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -35,9 +35,9 @@ $tree-item-height: 36px; .block-editor-block-navigation-block-contents { display: flex; align-items: center; - width: calc(100% - 0.8em); + width: 100%; height: auto; - padding: $grid-unit-15 6px; + padding: $grid-unit-20 6px; margin-top: auto; margin-bottom: auto; text-align: left; From 43fcff6478fc1705999bf590d6d4e892131a70ee Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 20 Aug 2020 14:24:54 +0800 Subject: [PATCH 26/45] Fix media dragging and dropping --- .../block-navigation/use-block-navigation-drop-zone.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 956ecf23acb59..1248b17a11a45 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -43,7 +43,9 @@ function getDropTargetBlocksData( rootClientId, blockIndex: getBlockIndex( clientId, rootClientId ), element: blockElement, - isDraggedBlock: draggedBlockClientIds.includes( clientId ), + isDraggedBlock: isBlockDrag + ? draggedBlockClientIds.includes( clientId ) + : false, innerBlockCount: getBlockCount( clientId ), canInsertDraggedBlocksAsSibling: isBlockDrag ? canInsertBlocks( draggedBlockClientIds, rootClientId ) From 6c483efc40b1347434006910b5d7e45fed1db3f1 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 11:01:04 +0800 Subject: [PATCH 27/45] Fix incorrect import --- .../block-navigation/use-block-navigation-drop-zone.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 1248b17a11a45..d5eac6b39699c 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -3,12 +3,12 @@ */ import { __unstableUseDropZone as useDropZone } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import { getDistanceToNearestEdge } from '@wordpress/dom'; import { useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies */ +import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; function getDropTargetBlocksData( From f550d91ae3db263e67de93300d90f94feb7d454b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 12:00:47 +0800 Subject: [PATCH 28/45] Fix dropping blocks with no drop indicators --- .../components/block-navigation/drop-indicator.js | 13 +++++++++++-- .../use-block-navigation-drop-zone.js | 6 +++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/drop-indicator.js b/packages/block-editor/src/components/block-navigation/drop-indicator.js index 1120337ad9960..c03debd108113 100644 --- a/packages/block-editor/src/components/block-navigation/drop-indicator.js +++ b/packages/block-editor/src/components/block-navigation/drop-indicator.js @@ -22,8 +22,17 @@ export default function BlockNavigationDropIndicator( { dropTarget } ) { if ( ! dropTarget ) { return; } - const { clientId, position } = dropTarget; - const blockRect = getBlockRect( clientId ); + + const { rootClientId, clientId, position } = dropTarget; + + const dropTargetClientId = + position === 'inside' ? rootClientId : clientId; + + const blockRect = getBlockRect( dropTargetClientId ); + + if ( ! blockRect ) { + return; + } if ( position === 'top' ) { return { diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index d5eac6b39699c..e377714844712 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -155,7 +155,8 @@ function getBlockNavigationDropTarget( blocksData, position ) { ) ) { return { - clientId: candidateBlockData.clientId, + rootClientId: candidateBlockData.clientId, + blockIndex: 0, position: 'inside', }; } @@ -166,8 +167,11 @@ function getBlockNavigationDropTarget( blocksData, position ) { return; } + const offset = isDraggingBelow ? 1 : 0; return { + rootClientId: candidateBlockData.rootClientId, clientId: candidateBlockData.clientId, + blockIndex: candidateBlockData.blockIndex + offset, position: candidateEdge, }; } From 26160e6a783127cab8ee82b99d25514f6b19000a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 12:15:40 +0800 Subject: [PATCH 29/45] Tidy up nesting code --- .../use-block-navigation-drop-zone.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index e377714844712..845dc674f6a3b 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -66,10 +66,7 @@ function isPointContainedByRect( point, rect ) { ); } -function isDroppingToInnerBlocks( point, rect, innerBlockCount ) { - if ( innerBlockCount > 0 ) { - return true; - } +function isNestingGesture( point, rect ) { const blockCenterX = rect.left + rect.width / 2; return point.x > blockCenterX; } @@ -144,15 +141,14 @@ function getBlockNavigationDropTarget( blocksData, position ) { const isDraggingBelow = candidateEdge === 'bottom'; // If the user is dragging towards the bottom of the block check whether - // they might be trying to insert the block as a child. + // they might be trying to nest the block as a child. + // If the block already has inner blocks, this should always be treated + // as nesting since the next block in the tree will be the first child. if ( isDraggingBelow && candidateBlockData.canInsertDraggedBlocksAsChild && - isDroppingToInnerBlocks( - position, - candidateRect, - candidateBlockData.innerBlockCount - ) + ( candidateBlockData.innerBlockCount > 0 || + isNestingGesture( position, candidateRect ) ) ) { return { rootClientId: candidateBlockData.clientId, From abf316d2b356ab6d713d40ca554d7d806341d1bd Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 12:22:26 +0800 Subject: [PATCH 30/45] Fix useRef/useEffect anti-pattern, replace with useMemo --- .../block-navigation/use-block-navigation-drop-zone.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 845dc674f6a3b..4c89f48b6d3dc 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -3,7 +3,7 @@ */ import { __unstableUseDropZone as useDropZone } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import { useEffect, useRef, useState } from '@wordpress/element'; +import { useEffect, useMemo, useState } from '@wordpress/element'; /** * Internal dependencies @@ -214,12 +214,11 @@ export default function useBlockNavigationDropZone( ref ) { } ); const hasPosition = !! position; - const blocksData = useRef(); // When the user starts dragging, build a list of block elements. - useEffect( () => { + const blocksData = useMemo( () => { if ( hasPosition ) { - blocksData.current = getDropTargetBlocksData( + return getDropTargetBlocksData( ref, dragEventType, getBlockRootClientId, @@ -235,9 +234,10 @@ export default function useBlockNavigationDropZone( ref ) { useEffect( () => { if ( position ) { const newTarget = getBlockNavigationDropTarget( - blocksData.current, + blocksData, position ); + if ( target ) { setTarget( newTarget ); } From f24cc0b6d36f3e968f5549f16822afe3a6cac4c3 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 12:28:28 +0800 Subject: [PATCH 31/45] Wrap up selectors in an object --- .../use-block-navigation-drop-zone.js | 53 ++++++++----------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 4c89f48b6d3dc..3dc7ba41334a6 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -11,18 +11,17 @@ import { useEffect, useMemo, useState } from '@wordpress/element'; import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; -function getDropTargetBlocksData( - ref, - dragEventType, - getRootClientId, - getBlockIndex, - getBlockCount, - getDraggedBlockClientIds, - canInsertBlocks -) { +function getDropTargetBlocksData( ref, dragEventType, selectors ) { if ( ! ref.current ) { return; } + const { + getBlockRootClientId, + getBlockIndex, + getBlockCount, + getDraggedBlockClientIds, + canInsertBlocks, + } = selectors; const isBlockDrag = dragEventType === 'default'; @@ -36,7 +35,7 @@ function getDropTargetBlocksData( return blockElements.map( ( blockElement ) => { const clientId = blockElement.dataset.block; - const rootClientId = getRootClientId( clientId ); + const rootClientId = getBlockRootClientId( clientId ); return { clientId, @@ -173,26 +172,20 @@ function getBlockNavigationDropTarget( blocksData, position ) { } export default function useBlockNavigationDropZone( ref ) { - const { - canInsertBlocks, - getBlockRootClientId, - getBlockIndex, - getBlockCount, - getDraggedBlockClientIds, - } = useSelect( ( select ) => { + const blocksDataSelectors = useSelect( ( select ) => { const { - canInsertBlocks: _canInsertBlocks, - getBlockRootClientId: _getBlockRootClientId, - getBlockIndex: _getBlockIndex, - getBlockCount: _getBlockCount, - getDraggedBlockClientIds: _getDraggedBlockClientIds, + canInsertBlocks, + getBlockRootClientId, + getBlockIndex, + getBlockCount, + getDraggedBlockClientIds, } = select( 'core/block-editor' ); return { - canInsertBlocks: _canInsertBlocks, - getBlockRootClientId: _getBlockRootClientId, - getBlockIndex: _getBlockIndex, - getBlockCount: _getBlockCount, - getDraggedBlockClientIds: _getDraggedBlockClientIds, + canInsertBlocks, + getBlockRootClientId, + getBlockIndex, + getBlockCount, + getDraggedBlockClientIds, }; }, [] ); @@ -221,11 +214,7 @@ export default function useBlockNavigationDropZone( ref ) { return getDropTargetBlocksData( ref, dragEventType, - getBlockRootClientId, - getBlockIndex, - getBlockCount, - getDraggedBlockClientIds, - canInsertBlocks + blocksDataSelectors ); } }, [ hasPosition ] ); From 9c715da21ac3060fdf5694f24531f376b3a85362 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 12:37:56 +0800 Subject: [PATCH 32/45] Convert getDropTargetBlocksData into a hook so that selectors can be colocated --- .../use-block-navigation-drop-zone.js | 115 ++++++++---------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 3dc7ba41334a6..9daee62da42e2 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -11,49 +11,70 @@ import { useEffect, useMemo, useState } from '@wordpress/element'; import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; -function getDropTargetBlocksData( ref, dragEventType, selectors ) { - if ( ! ref.current ) { - return; - } +function useDropTargetBlocksData( ref, position, dragEventType ) { const { getBlockRootClientId, getBlockIndex, getBlockCount, getDraggedBlockClientIds, canInsertBlocks, - } = selectors; + } = useSelect( ( select ) => { + const { + canInsertBlocks: _canInsertBlocks, + getBlockRootClientId: _getBlockRootClientId, + getBlockIndex: _getBlockIndex, + getBlockCount: _getBlockCount, + getDraggedBlockClientIds: _getDraggedBlockClientIds, + } = select( 'core/block-editor' ); + return { + canInsertBlocks: _canInsertBlocks, + getBlockRootClientId: _getBlockRootClientId, + getBlockIndex: _getBlockIndex, + getBlockCount: _getBlockCount, + getDraggedBlockClientIds: _getDraggedBlockClientIds, + }; + }, [] ); - const isBlockDrag = dragEventType === 'default'; + // Compute data about blocks only when the user + // starts dragging, as determined by `hasPosition`. + const hasPosition = !! position; + return useMemo( () => { + if ( ! ref.current || ! hasPosition ) { + return; + } - const draggedBlockClientIds = isBlockDrag - ? getDraggedBlockClientIds() - : undefined; + const isBlockDrag = dragEventType === 'default'; - const blockElements = Array.from( - ref.current.querySelectorAll( '[data-block]' ) - ); + const draggedBlockClientIds = isBlockDrag + ? getDraggedBlockClientIds() + : undefined; - return blockElements.map( ( blockElement ) => { - const clientId = blockElement.dataset.block; - const rootClientId = getBlockRootClientId( clientId ); + const blockElements = Array.from( + ref.current.querySelectorAll( '[data-block]' ) + ); - return { - clientId, - rootClientId, - blockIndex: getBlockIndex( clientId, rootClientId ), - element: blockElement, - isDraggedBlock: isBlockDrag - ? draggedBlockClientIds.includes( clientId ) - : false, - innerBlockCount: getBlockCount( clientId ), - canInsertDraggedBlocksAsSibling: isBlockDrag - ? canInsertBlocks( draggedBlockClientIds, rootClientId ) - : true, - canInsertDraggedBlocksAsChild: isBlockDrag - ? canInsertBlocks( draggedBlockClientIds, clientId ) - : true, - }; - } ); + return blockElements.map( ( blockElement ) => { + const clientId = blockElement.dataset.block; + const rootClientId = getBlockRootClientId( clientId ); + + return { + clientId, + rootClientId, + blockIndex: getBlockIndex( clientId, rootClientId ), + element: blockElement, + isDraggedBlock: isBlockDrag + ? draggedBlockClientIds.includes( clientId ) + : false, + innerBlockCount: getBlockCount( clientId ), + canInsertDraggedBlocksAsSibling: isBlockDrag + ? canInsertBlocks( draggedBlockClientIds, rootClientId ) + : true, + canInsertDraggedBlocksAsChild: isBlockDrag + ? canInsertBlocks( draggedBlockClientIds, clientId ) + : true, + }; + } ); + }, [ hasPosition ] ); } function isPointContainedByRect( point, rect ) { @@ -172,23 +193,6 @@ function getBlockNavigationDropTarget( blocksData, position ) { } export default function useBlockNavigationDropZone( ref ) { - const blocksDataSelectors = useSelect( ( select ) => { - const { - canInsertBlocks, - getBlockRootClientId, - getBlockIndex, - getBlockCount, - getDraggedBlockClientIds, - } = select( 'core/block-editor' ); - return { - canInsertBlocks, - getBlockRootClientId, - getBlockIndex, - getBlockCount, - getDraggedBlockClientIds, - }; - }, [] ); - const [ target = {}, setTarget ] = useState(); const { rootClientId: targetRootClientId, @@ -206,18 +210,7 @@ export default function useBlockNavigationDropZone( ref ) { ...dropEventHandlers, } ); - const hasPosition = !! position; - - // When the user starts dragging, build a list of block elements. - const blocksData = useMemo( () => { - if ( hasPosition ) { - return getDropTargetBlocksData( - ref, - dragEventType, - blocksDataSelectors - ); - } - }, [ hasPosition ] ); + const blocksData = useDropTargetBlocksData( ref, position, dragEventType ); // Calculate the drop target based on the drag position. useEffect( () => { From 7ad776d69cc28979987245ead543b3d7aceb609b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 14:02:54 +0800 Subject: [PATCH 33/45] Add doc blocks --- .../block-navigation/drop-indicator.js | 10 +-- .../use-block-navigation-drop-zone.js | 80 ++++++++++++++++++- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/drop-indicator.js b/packages/block-editor/src/components/block-navigation/drop-indicator.js index c03debd108113..3ed5765046154 100644 --- a/packages/block-editor/src/components/block-navigation/drop-indicator.js +++ b/packages/block-editor/src/components/block-navigation/drop-indicator.js @@ -23,10 +23,10 @@ export default function BlockNavigationDropIndicator( { dropTarget } ) { return; } - const { rootClientId, clientId, position } = dropTarget; + const { rootClientId, clientId, dropPosition } = dropTarget; const dropTargetClientId = - position === 'inside' ? rootClientId : clientId; + dropPosition === 'inside' ? rootClientId : clientId; const blockRect = getBlockRect( dropTargetClientId ); @@ -34,14 +34,14 @@ export default function BlockNavigationDropIndicator( { dropTarget } ) { return; } - if ( position === 'top' ) { + if ( dropPosition === 'top' ) { return { top: blockRect.top, height: 0, left: blockRect.left, width: blockRect.width, }; - } else if ( position === 'inside' || position === 'bottom' ) { + } else if ( dropPosition === 'inside' || dropPosition === 'bottom' ) { return { top: blockRect.bottom, height: 0, @@ -55,7 +55,7 @@ export default function BlockNavigationDropIndicator( { dropTarget } ) { () => classnames( 'block-navigation-drop-indicator', { 'is-hidden': ! dropTarget, - 'is-dropping-inside': dropTarget?.position === 'inside', + 'is-dropping-inside': dropTarget?.dropPosition === 'inside', } ), [ dropTarget ] ); diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 9daee62da42e2..ee06484ef73de 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -11,6 +11,49 @@ import { useEffect, useMemo, useState } from '@wordpress/element'; import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; +/** @typedef {import('../../utils/math').WPPoint} WPPoint */ + +/** + * The type of a drag event. + * + * @typedef {'default'|'file'|'html'} WPDragEventType + */ + +/** + * An array representing data for blocks in the DOM used by drag and drop. + * + * @typedef {Object} WPBlockNavigationDropZoneBlocks + * @property {string} clientId The client id for the block. + * @property {string} rootClientId The root client id for the block. + * @property {number} blockIndex The block's index. + * @property {Element} element The DOM element representing the block. + * @property {number} innerBlockCount The number of inner blocks the block has. + * @property {boolean} isDraggedBlock Whether the block is currently being dragged. + * @property {boolean} canInsertDraggedBlocksAsSibling Whether the dragged block can be a sibling of this block. + * @property {boolean} canInsertDraggedBlocksAsChild Whether the dragged block can be a child of this block. + */ + +/** + * An object containing details of a drop target. + * + * @typedef {Object} WPBlockNavigationDropZoneTarget + * @property {string} blockIndex The insertion index. + * @property {string} rootClientId The root client id for the block. + * @property {string|undefined} clientId The client id for the block. + * @property {'top'|'bottom'|'inside'} dropPosition The position relative to the block that the user is dropping to. + * 'inside' refers to nesting as an inner block. + */ + +/** + * A react hook that returns data about blocks used for computing where a user + * can drop to when dragging and dropping blocks. + * + * @param {Object} ref A React ref of a containing element for block navigation. + * @param {WPPoint} position The current drag position. + * @param {WPDragEventType} dragEventType The drag event type. + * + * @return {WPBlockNavigationDropZoneBlocks} An array representing data for each block in the block navigation DOM. + */ function useDropTargetBlocksData( ref, position, dragEventType ) { const { getBlockRootClientId, @@ -77,6 +120,14 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { }, [ hasPosition ] ); } +/** + * Is the point contained by the rectangle. + * + * @param {WPPoint} point The point. + * @param {DOMRect} rect The rectangle. + * + * @return {boolean} True if the point is contained by the rectangle, false otherwise. + */ function isPointContainedByRect( point, rect ) { return ( rect.left <= point.x && @@ -86,6 +137,16 @@ function isPointContainedByRect( point, rect ) { ); } +/** + * Determines whether the user positioning the dragged block to nest as an + * inner block. + * + * Presently this is determined by whether the cursor is on the right hand side + * of the block. + * + * @param {WPPoint} point The point representing the cursor position when dragging. + * @param {DOMRect} rect The rectangle. + */ function isNestingGesture( point, rect ) { const blockCenterX = rect.left + rect.width / 2; return point.x > blockCenterX; @@ -95,6 +156,14 @@ function isNestingGesture( point, rect ) { // to the above or below a block. const ALLOWED_DROP_EDGES = [ 'top', 'bottom' ]; +/** + * Given blocks data and the cursor position, compute the drop target. + * + * @param {WPBlockNavigationDropZoneBlocks} blocksData Data about the blocks in block navigation. + * @param {WPPoint} position The point representing the cursor position when dragging. + * + * @return {WPBlockNavigationDropZoneTarget} An object containing data about the drop target. + */ function getBlockNavigationDropTarget( blocksData, position ) { let candidateEdge; let candidateBlockData; @@ -173,7 +242,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { return { rootClientId: candidateBlockData.clientId, blockIndex: 0, - position: 'inside', + dropPosition: 'inside', }; } @@ -188,10 +257,17 @@ function getBlockNavigationDropTarget( blocksData, position ) { rootClientId: candidateBlockData.rootClientId, clientId: candidateBlockData.clientId, blockIndex: candidateBlockData.blockIndex + offset, - position: candidateEdge, + dropPosition: candidateEdge, }; } +/** + * A react hook for implementing a drop zone in block navigation. + * + * @param {Object} ref A React ref of a containing element for block navigation. + * + * @return {WPBlockNavigationDropZoneTarget} The drop target. + */ export default function useBlockNavigationDropZone( ref ) { const [ target = {}, setTarget ] = useState(); const { From f77e540182cce329d2760035482ece69e879226a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 15:05:41 +0800 Subject: [PATCH 34/45] Revert "Update styles to improve drop zone indicator" This reverts commit 753008a5a455dd6045edd7b81fdb69119ab2a85f. --- .../block-navigation/block-contents.js | 47 +++++++++++++- .../block-navigation/drop-indicator.js | 64 ------------------- .../components/block-navigation/style.scss | 59 ++++++++++------- .../src/components/block-navigation/tree.js | 22 +++---- .../use-block-navigation-drop-zone.js | 7 +- 5 files changed, 90 insertions(+), 109 deletions(-) delete mode 100644 packages/block-editor/src/components/block-navigation/drop-indicator.js diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index d57b03b7acebe..3f8c456dfd3b6 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -1,6 +1,12 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ +import { useSelect } from '@wordpress/data'; import { forwardRef } from '@wordpress/element'; /** @@ -26,8 +32,45 @@ const BlockNavigationBlockContents = forwardRef( ) => { const { __experimentalFeatures: withBlockNavigationSlots, + blockDropTarget = {}, } = useBlockNavigationContext(); + const rootClientId = useSelect( + ( select ) => + select( 'core/block-editor' ).getBlockRootClientId( + block.clientId + ) || '', + [ block.rootClientId ] + ); + + const { + rootClientId: dropTargetRootClientId, + blockIndex: dropTargetBlockIndex, + } = blockDropTarget; + + const blockIndex = position - 1; + + const isDroppingBefore = + dropTargetRootClientId === rootClientId && + blockIndex === dropTargetBlockIndex; + const isDroppingAfter = + dropTargetRootClientId === rootClientId && + position === siblingBlockCount && + dropTargetBlockIndex === siblingBlockCount; + const isDroppingToInnerBlocks = + block.clientId === dropTargetRootClientId && + ! block.innerBlocks?.length && + dropTargetBlockIndex === 0; + + const className = classnames( + 'block-editor-block-navigation-block-contents', + { + 'is-dropping-before': isDroppingBefore, + 'is-dropping-after': isDroppingAfter, + 'is-dropping-to-inner-blocks': isDroppingToInnerBlocks, + } + ); + return ( { - if ( ! dropTarget ) { - return; - } - - const { rootClientId, clientId, dropPosition } = dropTarget; - - const dropTargetClientId = - dropPosition === 'inside' ? rootClientId : clientId; - - const blockRect = getBlockRect( dropTargetClientId ); - - if ( ! blockRect ) { - return; - } - - if ( dropPosition === 'top' ) { - return { - top: blockRect.top, - height: 0, - left: blockRect.left, - width: blockRect.width, - }; - } else if ( dropPosition === 'inside' || dropPosition === 'bottom' ) { - return { - top: blockRect.bottom, - height: 0, - left: blockRect.left, - width: blockRect.width, - }; - } - }, [ dropTarget ] ); - - const className = useMemo( - () => - classnames( 'block-navigation-drop-indicator', { - 'is-hidden': ! dropTarget, - 'is-dropping-inside': dropTarget?.dropPosition === 'inside', - } ), - [ dropTarget ] - ); - - return
; -} diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index 9edce8ae1447a..e7d9abf88edbc 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -35,9 +35,9 @@ $tree-item-height: 36px; .block-editor-block-navigation-block-contents { display: flex; align-items: center; - width: 100%; + width: calc(100% - 0.8em); height: auto; - padding: $grid-unit-20 6px; + padding: $grid-unit-15 6px; margin-top: auto; margin-bottom: auto; text-align: left; @@ -45,6 +45,39 @@ $tree-item-height: 36px; border-radius: 2px; position: relative; + &.is-dropping-before::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + top: -6px; + right: 0; + left: 0; + border-top: 4px solid var(--wp-admin-theme-color); + } + + &.is-dropping-after::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + bottom: -6px; + right: 0; + left: 0; + border-bottom: 4px solid var(--wp-admin-theme-color); + } + + &.is-dropping-to-inner-blocks::before { + content: ""; + position: absolute; + pointer-events: none; + transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; + bottom: -6px; + right: 0; + left: $icon-size + 6px; + border-bottom: 4px solid var(--wp-admin-theme-color); + } + .components-modal__content & { padding-left: 0; padding-right: 0; @@ -255,25 +288,3 @@ $tree-item-height: 36px; } } } - -.block-navigation-drop-indicator { - display: block; - position: fixed; - pointer-events: none; - - &::before { - content: ""; - position: absolute; - pointer-events: none; - transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - top: -2px; - right: 0; - left: 0; - bottom: -2px; - border-top: 4px solid var(--wp-admin-theme-color); - } - - &.is-dropping-inside::before { - left: $icon-size; - } -} diff --git a/packages/block-editor/src/components/block-navigation/tree.js b/packages/block-editor/src/components/block-navigation/tree.js index 35cb1f8b551f1..ba7687923fecf 100644 --- a/packages/block-editor/src/components/block-navigation/tree.js +++ b/packages/block-editor/src/components/block-navigation/tree.js @@ -11,7 +11,6 @@ import { __ } from '@wordpress/i18n'; */ import BlockNavigationBranch from './branch'; import { BlockNavigationContext } from './context'; -import BlockNavigationDropIndicator from './drop-indicator'; import useBlockNavigationDropZone from './use-block-navigation-drop-zone'; /** @@ -37,17 +36,14 @@ export default function BlockNavigationTree( { ); return ( - <> - - - - - - - + + + + + ); } diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index ee06484ef73de..95f6143bea49a 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -171,10 +171,6 @@ function getBlockNavigationDropTarget( blocksData, position ) { let candidateRect; for ( const blockData of blocksData ) { - if ( blockData.isDraggedBlock ) { - continue; - } - const rect = blockData.element.getBoundingClientRect(); const [ distance, edge ] = getDistanceToNearestEdge( position, @@ -199,8 +195,7 @@ function getBlockNavigationDropTarget( blocksData, position ) { if ( edge === 'top' && previousBlockData && - previousBlockData.rootClientId === blockData.rootClientId && - ! previousBlockData.isDraggedBlock + previousBlockData.rootClientId === blockData.rootClientId ) { candidateBlockData = previousBlockData; candidateEdge = 'bottom'; From 3d72f3f4b8e4558fe2ece278f0606cc676a92094 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 15:25:37 +0800 Subject: [PATCH 35/45] Avoid showing drop zone on dragged block --- .../block-navigation/block-contents.js | 22 +++++++++---------- .../use-block-navigation-drop-zone.js | 7 +++++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index 3f8c456dfd3b6..3749b69fe739f 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -35,32 +35,32 @@ const BlockNavigationBlockContents = forwardRef( blockDropTarget = {}, } = useBlockNavigationContext(); + const { clientId } = block; + const rootClientId = useSelect( ( select ) => select( 'core/block-editor' ).getBlockRootClientId( - block.clientId + clientId ) || '', - [ block.rootClientId ] + [ clientId ] ); const { rootClientId: dropTargetRootClientId, - blockIndex: dropTargetBlockIndex, + clientId: dropTargetClientId, + dropPosition, } = blockDropTarget; - const blockIndex = position - 1; - const isDroppingBefore = dropTargetRootClientId === rootClientId && - blockIndex === dropTargetBlockIndex; + dropTargetClientId === clientId && + dropPosition === 'top'; const isDroppingAfter = dropTargetRootClientId === rootClientId && - position === siblingBlockCount && - dropTargetBlockIndex === siblingBlockCount; + dropTargetClientId === clientId && + dropPosition === 'bottom'; const isDroppingToInnerBlocks = - block.clientId === dropTargetRootClientId && - ! block.innerBlocks?.length && - dropTargetBlockIndex === 0; + dropTargetRootClientId === clientId && dropPosition === 'inside'; const className = classnames( 'block-editor-block-navigation-block-contents', diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 95f6143bea49a..ee06484ef73de 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -171,6 +171,10 @@ function getBlockNavigationDropTarget( blocksData, position ) { let candidateRect; for ( const blockData of blocksData ) { + if ( blockData.isDraggedBlock ) { + continue; + } + const rect = blockData.element.getBoundingClientRect(); const [ distance, edge ] = getDistanceToNearestEdge( position, @@ -195,7 +199,8 @@ function getBlockNavigationDropTarget( blocksData, position ) { if ( edge === 'top' && previousBlockData && - previousBlockData.rootClientId === blockData.rootClientId + previousBlockData.rootClientId === blockData.rootClientId && + ! previousBlockData.isDraggedBlock ) { candidateBlockData = previousBlockData; candidateEdge = 'bottom'; From 9d774572d2df0106b73f4cb55ac8a75173062ce2 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 15:29:09 +0800 Subject: [PATCH 36/45] Improve styles --- .../block-editor/src/components/block-navigation/style.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index e7d9abf88edbc..d8b8a35f354c4 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -35,9 +35,9 @@ $tree-item-height: 36px; .block-editor-block-navigation-block-contents { display: flex; align-items: center; - width: calc(100% - 0.8em); + width: 100%; height: auto; - padding: $grid-unit-15 6px; + padding: $grid-unit-20 6px; margin-top: auto; margin-bottom: auto; text-align: left; From 2bc3dbb7943492f56ca1644456fda52eb34c189a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 16:36:57 +0800 Subject: [PATCH 37/45] Try making the list view popover a modal drop zone --- .../use-block-navigation-drop-zone.js | 1 + packages/components/src/drop-zone/index.js | 4 ++++ packages/components/src/drop-zone/provider.js | 14 ++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index ee06484ef73de..65a30cac98a4f 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -283,6 +283,7 @@ export default function useBlockNavigationDropZone( ref ) { const { position, type: dragEventType } = useDropZone( { element: ref, withPosition: true, + __experimentalIsModal: true, ...dropEventHandlers, } ); diff --git a/packages/components/src/drop-zone/index.js b/packages/components/src/drop-zone/index.js index 8f247b7cefc10..7e368e7e5abff 100644 --- a/packages/components/src/drop-zone/index.js +++ b/packages/components/src/drop-zone/index.js @@ -22,6 +22,7 @@ export function useDropZone( { onDrop, isDisabled, withPosition, + __experimentalIsModal = false, __unstableIsRelative = false, } ) { const { addDropZone, removeDropZone } = useContext( Context ); @@ -40,6 +41,7 @@ export function useDropZone( { onHTMLDrop, setState, withPosition, + __experimentalIsModal, isRelative: __unstableIsRelative, }; addDropZone( dropZone ); @@ -70,6 +72,7 @@ function DropZoneComponent( { onFilesDrop, onHTMLDrop, onDrop, + __experimentalIsModal = false, } ) { const element = useRef(); const { isDraggingOverDocument, isDraggingOverElement, type } = useDropZone( @@ -78,6 +81,7 @@ function DropZoneComponent( { onFilesDrop, onHTMLDrop, onDrop, + __experimentalIsModal, __unstableIsRelative: true, } ); diff --git a/packages/components/src/drop-zone/provider.js b/packages/components/src/drop-zone/provider.js index 43d9d859fd484..98792b38750d7 100644 --- a/packages/components/src/drop-zone/provider.js +++ b/packages/components/src/drop-zone/provider.js @@ -149,12 +149,23 @@ class DropZoneProvider extends Component { ) ); + const hasActiveModalDropZone = some( hoveredDropZones, ( zone ) => { + return ( + zone.__experimentalIsModal && + zone.element.current.contains( event.target ) + ); + } ); + // Find the leaf dropzone not containing another dropzone const hoveredDropZone = find( hoveredDropZones, ( zone ) => { const container = zone.isRelative ? zone.element.current.parentElement : zone.element.current; + if ( hasActiveModalDropZone && ! zone.__experimentalIsModal ) { + return false; + } + return ! some( hoveredDropZones, ( subZone ) => @@ -193,6 +204,9 @@ class DropZoneProvider extends Component { // Notifying the dropzones toUpdate.forEach( ( dropZone ) => { + if ( ! dropZone ) { + return; + } const index = this.dropZones.indexOf( dropZone ); const isDraggingOverDropZone = index === hoveredDropZoneIndex; dropZone.setState( { From 19c11206ce11ba155e6bc3f5bd0451da15094a63 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 27 Aug 2020 17:26:23 +0800 Subject: [PATCH 38/45] Adjust drop indicator styles --- .../src/components/block-navigation/style.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/style.scss b/packages/block-editor/src/components/block-navigation/style.scss index d8b8a35f354c4..c3a80f54bbeea 100644 --- a/packages/block-editor/src/components/block-navigation/style.scss +++ b/packages/block-editor/src/components/block-navigation/style.scss @@ -50,7 +50,7 @@ $tree-item-height: 36px; position: absolute; pointer-events: none; transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - top: -6px; + top: -2px; right: 0; left: 0; border-top: 4px solid var(--wp-admin-theme-color); @@ -61,7 +61,7 @@ $tree-item-height: 36px; position: absolute; pointer-events: none; transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - bottom: -6px; + bottom: -2px; right: 0; left: 0; border-bottom: 4px solid var(--wp-admin-theme-color); @@ -72,9 +72,9 @@ $tree-item-height: 36px; position: absolute; pointer-events: none; transition: border-color 0.1s linear, border-style 0.1s linear, box-shadow 0.1s linear; - bottom: -6px; + bottom: -2px; right: 0; - left: $icon-size + 6px; + left: $icon-size; border-bottom: 4px solid var(--wp-admin-theme-color); } From 28f313178d81c1f4c436f3c7e320767e00e6da7e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 28 Aug 2020 13:51:14 +0800 Subject: [PATCH 39/45] Use __experimentalFeatures to disable drag and drop outside of navigation editor --- .../src/components/block-navigation/block-contents.js | 8 ++++---- .../block-editor/src/components/block-navigation/tree.js | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/block-contents.js b/packages/block-editor/src/components/block-navigation/block-contents.js index 3749b69fe739f..8bf5baea7985b 100644 --- a/packages/block-editor/src/components/block-navigation/block-contents.js +++ b/packages/block-editor/src/components/block-navigation/block-contents.js @@ -31,7 +31,7 @@ const BlockNavigationBlockContents = forwardRef( ref ) => { const { - __experimentalFeatures: withBlockNavigationSlots, + __experimentalFeatures, blockDropTarget = {}, } = useBlockNavigationContext(); @@ -77,7 +77,7 @@ const BlockNavigationBlockContents = forwardRef( elementId={ `block-navigation-block-${ block.clientId }` } > { ( { isDraggable, onDraggableStart, onDraggableEnd } ) => - withBlockNavigationSlots ? ( + __experimentalFeatures ? ( ( { __experimentalFeatures, From 538b1122016046758adbc6d7f88822cb92809867 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 28 Aug 2020 13:51:46 +0800 Subject: [PATCH 40/45] Revert "Try making the list view popover a modal drop zone" This reverts commit 2bc3dbb7943492f56ca1644456fda52eb34c189a. --- .../use-block-navigation-drop-zone.js | 1 - packages/components/src/drop-zone/index.js | 4 ---- packages/components/src/drop-zone/provider.js | 14 -------------- 3 files changed, 19 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index 65a30cac98a4f..ee06484ef73de 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -283,7 +283,6 @@ export default function useBlockNavigationDropZone( ref ) { const { position, type: dragEventType } = useDropZone( { element: ref, withPosition: true, - __experimentalIsModal: true, ...dropEventHandlers, } ); diff --git a/packages/components/src/drop-zone/index.js b/packages/components/src/drop-zone/index.js index 7e368e7e5abff..8f247b7cefc10 100644 --- a/packages/components/src/drop-zone/index.js +++ b/packages/components/src/drop-zone/index.js @@ -22,7 +22,6 @@ export function useDropZone( { onDrop, isDisabled, withPosition, - __experimentalIsModal = false, __unstableIsRelative = false, } ) { const { addDropZone, removeDropZone } = useContext( Context ); @@ -41,7 +40,6 @@ export function useDropZone( { onHTMLDrop, setState, withPosition, - __experimentalIsModal, isRelative: __unstableIsRelative, }; addDropZone( dropZone ); @@ -72,7 +70,6 @@ function DropZoneComponent( { onFilesDrop, onHTMLDrop, onDrop, - __experimentalIsModal = false, } ) { const element = useRef(); const { isDraggingOverDocument, isDraggingOverElement, type } = useDropZone( @@ -81,7 +78,6 @@ function DropZoneComponent( { onFilesDrop, onHTMLDrop, onDrop, - __experimentalIsModal, __unstableIsRelative: true, } ); diff --git a/packages/components/src/drop-zone/provider.js b/packages/components/src/drop-zone/provider.js index 98792b38750d7..43d9d859fd484 100644 --- a/packages/components/src/drop-zone/provider.js +++ b/packages/components/src/drop-zone/provider.js @@ -149,23 +149,12 @@ class DropZoneProvider extends Component { ) ); - const hasActiveModalDropZone = some( hoveredDropZones, ( zone ) => { - return ( - zone.__experimentalIsModal && - zone.element.current.contains( event.target ) - ); - } ); - // Find the leaf dropzone not containing another dropzone const hoveredDropZone = find( hoveredDropZones, ( zone ) => { const container = zone.isRelative ? zone.element.current.parentElement : zone.element.current; - if ( hasActiveModalDropZone && ! zone.__experimentalIsModal ) { - return false; - } - return ! some( hoveredDropZones, ( subZone ) => @@ -204,9 +193,6 @@ class DropZoneProvider extends Component { // Notifying the dropzones toUpdate.forEach( ( dropZone ) => { - if ( ! dropZone ) { - return; - } const index = this.dropZones.indexOf( dropZone ); const isDraggingOverDropZone = index === hoveredDropZoneIndex; dropZone.setState( { From 9d9e228b00c2996aeb8370801725c6343e1fce4e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 28 Aug 2020 14:17:20 +0800 Subject: [PATCH 41/45] Make selectors more succinct --- .../use-block-navigation-drop-zone.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index ee06484ef73de..a0444add1a89e 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -62,19 +62,13 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { getDraggedBlockClientIds, canInsertBlocks, } = useSelect( ( select ) => { - const { - canInsertBlocks: _canInsertBlocks, - getBlockRootClientId: _getBlockRootClientId, - getBlockIndex: _getBlockIndex, - getBlockCount: _getBlockCount, - getDraggedBlockClientIds: _getDraggedBlockClientIds, - } = select( 'core/block-editor' ); + const selectors = select( 'core/block-editor' ); return { - canInsertBlocks: _canInsertBlocks, - getBlockRootClientId: _getBlockRootClientId, - getBlockIndex: _getBlockIndex, - getBlockCount: _getBlockCount, - getDraggedBlockClientIds: _getDraggedBlockClientIds, + canInsertBlocks: selectors.canInsertBlocks, + getBlockRootClientId: selectors.getBlockRootClientId, + getBlockIndex: selectors.getBlockIndex, + getBlockCount: selectors.getBlockCount, + getDraggedBlockClientIds: selectors.getDraggedBlockClientIds, }; }, [] ); From 575aa75c61e835d9f81c114aa3974914fdf5a7f4 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 28 Aug 2020 14:32:18 +0800 Subject: [PATCH 42/45] Change useMemo back to useEffect to ensure blocksData is recomputed correctly --- .../use-block-navigation-drop-zone.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index a0444add1a89e..ea27684c16e6c 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -3,7 +3,7 @@ */ import { __unstableUseDropZone as useDropZone } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import { useEffect, useMemo, useState } from '@wordpress/element'; +import { useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -12,6 +12,7 @@ import { getDistanceToNearestEdge } from '../../utils/math'; import useOnBlockDrop from '../use-on-block-drop'; /** @typedef {import('../../utils/math').WPPoint} WPPoint */ +/** @typedef {import('@wordpress/element').RefObject} RefObject */ /** * The type of a drag event. @@ -52,7 +53,7 @@ import useOnBlockDrop from '../use-on-block-drop'; * @param {WPPoint} position The current drag position. * @param {WPDragEventType} dragEventType The drag event type. * - * @return {WPBlockNavigationDropZoneBlocks} An array representing data for each block in the block navigation DOM. + * @return {RefObject} A React ref containing the blocks data. */ function useDropTargetBlocksData( ref, position, dragEventType ) { const { @@ -71,11 +72,13 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { getDraggedBlockClientIds: selectors.getDraggedBlockClientIds, }; }, [] ); + const blocksData = useRef(); // Compute data about blocks only when the user // starts dragging, as determined by `hasPosition`. const hasPosition = !! position; - return useMemo( () => { + + useEffect( () => { if ( ! ref.current || ! hasPosition ) { return; } @@ -90,7 +93,7 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { ref.current.querySelectorAll( '[data-block]' ) ); - return blockElements.map( ( blockElement ) => { + blocksData.current = blockElements.map( ( blockElement ) => { const clientId = blockElement.dataset.block; const rootClientId = getBlockRootClientId( clientId ); @@ -112,6 +115,8 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { }; } ); }, [ hasPosition ] ); + + return blocksData; } /** @@ -286,7 +291,7 @@ export default function useBlockNavigationDropZone( ref ) { useEffect( () => { if ( position ) { const newTarget = getBlockNavigationDropTarget( - blocksData, + blocksData.current, position ); From 0cf78690cbea2eb94bfe4c95e9d864846f1cdccf Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 2 Sep 2020 12:46:36 +0800 Subject: [PATCH 43/45] Fix incorrect conditional setting of target --- .../block-navigation/use-block-navigation-drop-zone.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index ea27684c16e6c..a288907e6b20e 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -295,7 +295,7 @@ export default function useBlockNavigationDropZone( ref ) { position ); - if ( target ) { + if ( newTarget ) { setTarget( newTarget ); } } From f3eedfcaa217db80715f51856d7d276857e6b27f Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 2 Sep 2020 12:59:48 +0800 Subject: [PATCH 44/45] Specify all the deps --- .../use-block-navigation-drop-zone.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index a288907e6b20e..a3152e5418335 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -114,7 +114,16 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { : true, }; } ); - }, [ hasPosition ] ); + }, [ + hasPosition, + ref, + dragEventType, + canInsertBlocks, + getBlockCount, + getBlockIndex, + getBlockRootClientId, + getDraggedBlockClientIds, + ] ); return blocksData; } @@ -299,7 +308,7 @@ export default function useBlockNavigationDropZone( ref ) { setTarget( newTarget ); } } - }, [ position ] ); + }, [ blocksData, position ] ); if ( position ) { return target; From 94eb3b39df6bfd649a8116b6a1a296bd4a616ea3 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 2 Sep 2020 13:08:09 +0800 Subject: [PATCH 45/45] Add comment --- .../block-navigation/use-block-navigation-drop-zone.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js index a3152e5418335..02f541ab63917 100644 --- a/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js +++ b/packages/block-editor/src/components/block-navigation/use-block-navigation-drop-zone.js @@ -115,8 +115,10 @@ function useDropTargetBlocksData( ref, position, dragEventType ) { }; } ); }, [ - hasPosition, + // `ref` shouldn't actually change during a drag operation, but + // is specified for completeness as it's used within the hook. ref, + hasPosition, dragEventType, canInsertBlocks, getBlockCount,