From e6ebdd080876cf5bed745587014193a18183a476 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Fri, 1 Dec 2023 15:30:08 +1100 Subject: [PATCH] Drop to either side of target block --- .../src/components/block-popover/inbetween.js | 24 +++++++---- .../components/block-tools/insertion-point.js | 3 ++ .../components/use-block-drop-zone/index.js | 42 ++++++++++++------- .../src/components/use-on-block-drop/index.js | 10 +++-- packages/block-editor/src/store/actions.js | 4 +- packages/block-editor/src/store/reducer.js | 10 ++++- 6 files changed, 65 insertions(+), 28 deletions(-) diff --git a/packages/block-editor/src/components/block-popover/inbetween.js b/packages/block-editor/src/components/block-popover/inbetween.js index f82b4a56ce74ee..a08df1600e03c5 100644 --- a/packages/block-editor/src/components/block-popover/inbetween.js +++ b/packages/block-editor/src/components/block-popover/inbetween.js @@ -34,6 +34,7 @@ function BlockPopoverInbetween( { __unstablePopoverSlot, __unstableContentRef, operation = 'insert', + nearestSide = 'right', ...props } ) { // This is a temporary hack to get the inbetween inserter to recompute properly. @@ -82,7 +83,10 @@ function BlockPopoverInbetween( { return undefined; } - const contextElement = previousElement || nextElement; + const contextElement = + operation === 'group' + ? nextElement || previousElement + : previousElement || nextElement; return { contextElement, @@ -100,12 +104,16 @@ function BlockPopoverInbetween( { let height = 0; if ( operation === 'group' ) { - // If the operation is group, nextRect is the target to be grouped. - // Not sure if previousRect is needed here. - top = nextRect ? nextRect.top : previousRect.top; - width = nextRect ? nextRect.width : previousRect.width; - height = nextRect ? nextRect.bottom - nextRect.top : 0; - left = nextRect ? nextRect.left : previousRect.left; + const targetRect = nextRect || previousRect; + top = targetRect.top; + width = targetRect.width; + height = targetRect.bottom - targetRect.top; + // Popover calculates its distance from mid-block so some + // adjustments are needed to make it appear in the right place. + left = + nearestSide === 'left' + ? -targetRect.width / 2 + targetRect.left + : targetRect.width / 2 + targetRect.left; } else if ( isVertical ) { // vertical top = previousRect ? previousRect.bottom : nextRect.top; @@ -149,6 +157,8 @@ function BlockPopoverInbetween( { popoverRecomputeCounter, isVertical, isVisible, + operation, + nearestSide, ] ); const popoverScrollRef = usePopoverScroll( __unstableContentRef ); diff --git a/packages/block-editor/src/components/block-tools/insertion-point.js b/packages/block-editor/src/components/block-tools/insertion-point.js index 0a6a19ea126ac5..19ad39caca336a 100644 --- a/packages/block-editor/src/components/block-tools/insertion-point.js +++ b/packages/block-editor/src/components/block-tools/insertion-point.js @@ -25,6 +25,7 @@ function InbetweenInsertionPointPopover( { __unstablePopoverSlot, __unstableContentRef, operation = 'insert', + nearestSide = 'right', } ) { const { selectBlock, hideInsertionPoint } = useDispatch( blockEditorStore ); const openRef = useContext( InsertionPointOpenRef ); @@ -156,6 +157,7 @@ function InbetweenInsertionPointPopover( { __unstablePopoverSlot={ __unstablePopoverSlot } __unstableContentRef={ __unstableContentRef } operation={ operation } + nearestSide={ nearestSide } > ); diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js index 2aafeaf37855d6..6c83dd475932c7 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/index.js @@ -73,6 +73,7 @@ export function getDropTargetPosition( let insertPosition = 'before'; let minDistance = Infinity; let targetBlockIndex = null; + let nearestSide = 'right'; const { dropZoneElement, @@ -155,6 +156,13 @@ export function getDropTargetPosition( // Set target block index if the point is inside of the block // and the block is modified. targetBlockIndex = blockIndex; + // If the point is inside of the block, find nearest side. + const [ , sideEdge ] = getDistanceToNearestEdge( + position, + rect, + [ 'left', 'right' ] + ); + nearestSide = sideEdge; } if ( distance < minDistance ) { @@ -182,7 +190,7 @@ export function getDropTargetPosition( // If the target index is set then group with the block at that index. if ( targetBlockIndex !== null ) { - return [ targetBlockIndex, 'group' ]; + return [ targetBlockIndex, 'group', nearestSide ]; } // If both blocks are not unmodified default blocks then just insert between them. if ( @@ -293,6 +301,7 @@ export default function useBlockDropZone( { dropTarget.index, { operation: dropTarget.operation, + nearestSide: dropTarget.nearestSide, } ); const throttled = useThrottle( @@ -345,25 +354,27 @@ export default function useBlockDropZone( { }; } ); - const [ targetIndex, operation ] = getDropTargetPosition( - blocksData, - { x: event.clientX, y: event.clientY }, - getBlockListSettings( targetRootClientId )?.orientation, - { - dropZoneElement, - parentBlockClientId, - parentBlockOrientation: parentBlockClientId - ? getBlockListSettings( parentBlockClientId ) - ?.orientation - : undefined, - rootBlockIndex: getBlockIndex( targetRootClientId ), - } - ); + const [ targetIndex, operation, nearestSide ] = + getDropTargetPosition( + blocksData, + { x: event.clientX, y: event.clientY }, + getBlockListSettings( targetRootClientId )?.orientation, + { + dropZoneElement, + parentBlockClientId, + parentBlockOrientation: parentBlockClientId + ? getBlockListSettings( parentBlockClientId ) + ?.orientation + : undefined, + rootBlockIndex: getBlockIndex( targetRootClientId ), + } + ); registry.batch( () => { setDropTarget( { index: targetIndex, operation, + nearestSide, } ); const insertionPointClientId = [ @@ -375,6 +386,7 @@ export default function useBlockDropZone( { showInsertionPoint( insertionPointClientId, targetIndex, { operation, + nearestSide, } ); } ); }, diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index 661532d319a06f..bbaa05f25ca2f2 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -222,7 +222,7 @@ export default function useOnBlockDrop( targetBlockIndex, options = {} ) { - const { operation = 'insert' } = options; + const { operation = 'insert', nearestSide = 'right' } = options; const { canInsertBlockType, getBlockIndex, @@ -255,7 +255,11 @@ export default function useOnBlockDrop( replaceBlocks( clientId, blocks, undefined, initialPosition ); } else if ( operation === 'group' ) { const targetBlock = getBlock( clientId ); - blocks.unshift( targetBlock ); + if ( nearestSide === 'left' ) { + blocks.push( targetBlock ); + } else { + blocks.unshift( targetBlock ); + } const groupInnerBlocks = blocks.map( ( block ) => { return createBlock( @@ -268,7 +272,7 @@ export default function useOnBlockDrop( const wrappedBlocks = createBlock( 'core/group', { - layout: { type: 'flex' }, + layout: { type: 'flex', flexWrap: 'nowrap' }, }, groupInnerBlocks ); diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index da9beb0ba73a95..bc5fb91fbff6b2 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -640,13 +640,15 @@ export function showInsertionPoint( index, __unstableOptions = {} ) { - const { __unstableWithInserter, operation } = __unstableOptions; + const { __unstableWithInserter, operation, nearestSide } = + __unstableOptions; return { type: 'SHOW_INSERTION_POINT', rootClientId, index, __unstableWithInserter, operation, + nearestSide, }; } /** diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 11811afd83f6fe..cee1f080626e5e 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1599,13 +1599,19 @@ export function blocksMode( state = {}, action ) { export function insertionPoint( state = null, action ) { switch ( action.type ) { case 'SHOW_INSERTION_POINT': { - const { rootClientId, index, __unstableWithInserter, operation } = - action; + const { + rootClientId, + index, + __unstableWithInserter, + operation, + nearestSide, + } = action; const nextState = { rootClientId, index, __unstableWithInserter, operation, + nearestSide, }; // Bail out updates if the states are the same.