Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow drag and drop to create Rows and Galleries #56186

Merged
merged 15 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions packages/block-editor/src/components/block-popover/inbetween.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ function BlockPopoverInbetween( {
children,
__unstablePopoverSlot,
__unstableContentRef,
operation = 'insert',
nearestSide = 'right',
...props
} ) {
// This is a temporary hack to get the inbetween inserter to recompute properly.
Expand Down Expand Up @@ -81,7 +83,10 @@ function BlockPopoverInbetween( {
return undefined;
}

const contextElement = previousElement || nextElement;
const contextElement =
operation === 'group'
? nextElement || previousElement
: previousElement || nextElement;

return {
contextElement,
Expand All @@ -98,7 +103,20 @@ function BlockPopoverInbetween( {
let width = 0;
let height = 0;

if ( isVertical ) {
if ( operation === 'group' ) {
const targetRect = nextRect || previousRect;
top = targetRect.top;
// No spacing is likely around blocks in this operation.
// So width of the inserter containing rect is set to 0.
width = 0;
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.left - 2
: targetRect.right - 2;
} else if ( isVertical ) {
// vertical
top = previousRect ? previousRect.bottom : nextRect.top;
width = previousRect ? previousRect.width : nextRect.width;
Expand Down Expand Up @@ -141,6 +159,8 @@ function BlockPopoverInbetween( {
popoverRecomputeCounter,
isVertical,
isVisible,
operation,
nearestSide,
] );

const popoverScrollRef = usePopoverScroll( __unstableContentRef );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const InsertionPointOpenRef = createContext();
function InbetweenInsertionPointPopover( {
__unstablePopoverSlot,
__unstableContentRef,
operation = 'insert',
nearestSide = 'right',
} ) {
const { selectBlock, hideInsertionPoint } = useDispatch( blockEditorStore );
const openRef = useContext( InsertionPointOpenRef );
Expand Down Expand Up @@ -138,9 +140,14 @@ function InbetweenInsertionPointPopover( {
return null;
}

const orientationClassname =
orientation === 'horizontal' || operation === 'group'
? 'is-horizontal'
: 'is-vertical';

const className = classnames(
'block-editor-block-list__insertion-point',
'is-' + orientation
orientationClassname
);

return (
Expand All @@ -149,6 +156,8 @@ function InbetweenInsertionPointPopover( {
nextClientId={ nextClientId }
__unstablePopoverSlot={ __unstablePopoverSlot }
__unstableContentRef={ __unstableContentRef }
operation={ operation }
nearestSide={ nearestSide }
>
<motion.div
layout={ ! disableMotion }
Expand Down Expand Up @@ -236,6 +245,10 @@ export default function InsertionPoint( props ) {
{ ...props }
/>
) : (
<InbetweenInsertionPointPopover { ...props } />
<InbetweenInsertionPointPopover
operation={ insertionPoint.operation }
nearestSide={ insertionPoint.nearestSide }
{ ...props }
/>
);
}
79 changes: 60 additions & 19 deletions packages/block-editor/src/components/use-block-drop-zone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import useOnBlockDrop from '../use-on-block-drop';
import {
getDistanceToNearestEdge,
isPointContainedByRect,
isPointWithinTopAndBottomBoundariesOfRect,
} from '../../utils/math';
import { store as blockEditorStore } from '../../store';

Expand Down Expand Up @@ -72,6 +73,8 @@ export function getDropTargetPosition(
let nearestIndex = 0;
let insertPosition = 'before';
let minDistance = Infinity;
let targetBlockIndex = null;
let nearestSide = 'right';

const {
dropZoneElement,
Expand Down Expand Up @@ -136,20 +139,48 @@ export function getDropTargetPosition(
}

blocksData.forEach(
( { isUnmodifiedDefaultBlock, getBoundingClientRect, blockIndex } ) => {
( {
isUnmodifiedDefaultBlock,
getBoundingClientRect,
blockIndex,
blockOrientation,
} ) => {
const rect = getBoundingClientRect();

let [ distance, edge ] = getDistanceToNearestEdge(
position,
rect,
allowedEdges
);
// If the the point is close to a side, prioritize that side.
const [ sideDistance, sideEdge ] = getDistanceToNearestEdge(
position,
rect,
[ 'left', 'right' ]
);

const isPointInsideRect = isPointContainedByRect( position, rect );

// Prioritize the element if the point is inside of an unmodified default block.
if (
isUnmodifiedDefaultBlock &&
isPointContainedByRect( position, rect )
) {
if ( isUnmodifiedDefaultBlock && isPointInsideRect ) {
distance = 0;
} else if (
orientation === 'vertical' &&
blockOrientation !== 'horizontal' &&
( ( isPointInsideRect && sideDistance < THRESHOLD_DISTANCE ) ||
( ! isPointInsideRect &&
isPointWithinTopAndBottomBoundariesOfRect(
position,
rect
) ) )
) {
/**
* This condition should only apply when the layout is vertical (otherwise there's
* no need to create a Row) and dropzones should only activate when the block is
* either within and close to the sides of the target block or on its outer sides.
*/
targetBlockIndex = blockIndex;
nearestSide = sideEdge;
}

if ( distance < minDistance ) {
Expand All @@ -175,6 +206,10 @@ export function getDropTargetPosition(
const isAdjacentBlockUnmodifiedDefaultBlock =
!! blocksData[ adjacentIndex ]?.isUnmodifiedDefaultBlock;

// If the target index is set then group with the block at that index.
if ( targetBlockIndex !== null ) {
return [ targetBlockIndex, 'group', nearestSide ];
}
// If both blocks are not unmodified default blocks then just insert between them.
if (
! isNearestBlockUnmodifiedDefaultBlock &&
Expand Down Expand Up @@ -284,6 +319,7 @@ export default function useBlockDropZone( {
dropTarget.index,
{
operation: dropTarget.operation,
nearestSide: dropTarget.nearestSide,
}
);
const throttled = useThrottle(
Expand Down Expand Up @@ -333,28 +369,32 @@ export default function useBlockDropZone( {
.getElementById( `block-${ clientId }` )
.getBoundingClientRect(),
blockIndex: getBlockIndex( clientId ),
blockOrientation:
getBlockListSettings( clientId )?.orientation,
};
} );

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 = [
Expand All @@ -366,6 +406,7 @@ export default function useBlockDropZone( {

showInsertionPoint( insertionPointClientId, targetIndex, {
operation,
nearestSide,
} );
} );
},
Expand Down
Loading
Loading