diff --git a/packages/block-editor/src/components/block-list/use-in-between-inserter.js b/packages/block-editor/src/components/block-list/use-in-between-inserter.js
index 0dfdab359d7a77..618811ec91ba64 100644
--- a/packages/block-editor/src/components/block-list/use-in-between-inserter.js
+++ b/packages/block-editor/src/components/block-list/use-in-between-inserter.js
@@ -14,12 +14,17 @@ import { InsertionPointOpenRef } from '../block-tools/insertion-point';
export function useInBetweenInserter() {
const openRef = useContext( InsertionPointOpenRef );
+ const hasReducedUI = useSelect(
+ ( select ) => select( blockEditorStore ).getSettings().hasReducedUI,
+ []
+ );
const {
getBlockListSettings,
getBlockRootClientId,
getBlockIndex,
isBlockInsertionPointVisible,
isMultiSelecting,
+ getSelectedBlockClientIds,
} = useSelect( blockEditorStore );
const { showInsertionPoint, hideInsertionPoint } = useDispatch(
blockEditorStore
@@ -27,6 +32,10 @@ export function useInBetweenInserter() {
return useRefEffect(
( node ) => {
+ if ( hasReducedUI ) {
+ return;
+ }
+
function onMouseMove( event ) {
if ( openRef.current ) {
return;
@@ -98,6 +107,12 @@ export function useInBetweenInserter() {
return;
}
+ // Don't show the inserter when hovering above (conflicts with
+ // block toolbar) or inside selected block(s).
+ if ( getSelectedBlockClientIds().includes( clientId ) ) {
+ return;
+ }
+
const elementRect = element.getBoundingClientRect();
if (
@@ -145,6 +160,7 @@ export function useInBetweenInserter() {
isMultiSelecting,
showInsertionPoint,
hideInsertionPoint,
+ getSelectedBlockClientIds,
]
);
}
diff --git a/packages/block-editor/src/components/block-tools/block-popover.js b/packages/block-editor/src/components/block-tools/block-popover.js
index dc803f7670aba6..8371cfff9bbba8 100644
--- a/packages/block-editor/src/components/block-tools/block-popover.js
+++ b/packages/block-editor/src/components/block-tools/block-popover.js
@@ -64,6 +64,24 @@ function BlockPopover( {
hasFixedToolbar,
lastClientId,
} = useSelect( selector, [] );
+ const isInsertionPointVisible = useSelect(
+ ( select ) => {
+ const {
+ isBlockInsertionPointVisible,
+ getBlockInsertionPoint,
+ getBlockOrder,
+ } = select( blockEditorStore );
+
+ if ( ! isBlockInsertionPointVisible() ) {
+ return false;
+ }
+
+ const insertionPoint = getBlockInsertionPoint();
+ const order = getBlockOrder( insertionPoint.rootClientId );
+ return order[ insertionPoint.index ] === clientId;
+ },
+ [ clientId ]
+ );
const isLargeViewport = useViewportMatch( 'medium' );
const [ isToolbarForced, setIsToolbarForced ] = useState( false );
const [ isInserterShown, setIsInserterShown ] = useState( false );
@@ -184,7 +202,9 @@ function BlockPopover( {
position={ popoverPosition }
focusOnMount={ false }
anchorRef={ anchorRef }
- className="block-editor-block-list__block-popover"
+ className={ classnames( 'block-editor-block-list__block-popover', {
+ 'is-insertion-point-visible': isInsertionPointVisible,
+ } ) }
__unstableStickyBoundaryElement={ stickyBoundaryElement }
// Render in the old slot if needed for backward compatibility,
// otherwise render in place (not in the the default popover slot).
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 134430c301a61e..b836beba7d3ae0 100644
--- a/packages/block-editor/src/components/block-tools/insertion-point.js
+++ b/packages/block-editor/src/components/block-tools/insertion-point.js
@@ -36,7 +36,6 @@ function InsertionPointPopover( {
const ref = useRef();
const {
orientation,
- isHidden,
previousClientId,
nextClientId,
rootClientId,
@@ -45,47 +44,36 @@ function InsertionPointPopover( {
const {
getBlockOrder,
getBlockListSettings,
- getMultiSelectedBlockClientIds,
- getSelectedBlockClientId,
- hasMultiSelection,
- getSettings,
getBlockInsertionPoint,
+ isBlockBeingDragged,
+ getPreviousBlockClientId,
+ getNextBlockClientId,
} = select( blockEditorStore );
const insertionPoint = getBlockInsertionPoint();
const order = getBlockOrder( insertionPoint.rootClientId );
- const targetClientId = order[ insertionPoint.index - 1 ];
- const targetRootClientId = insertionPoint.rootClientId;
- const blockOrder = getBlockOrder( targetRootClientId );
- if ( ! blockOrder.length ) {
+
+ if ( ! order.length ) {
return {};
}
- const previous = targetClientId
- ? targetClientId
- : blockOrder[ blockOrder.length - 1 ];
- const isLast = previous === blockOrder[ blockOrder.length - 1 ];
- const next = isLast
- ? null
- : blockOrder[ blockOrder.indexOf( previous ) + 1 ];
- const { hasReducedUI } = getSettings();
- const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds();
- const selectedBlockClientId = getSelectedBlockClientId();
- const blockOrientation =
- getBlockListSettings( targetRootClientId )?.orientation ||
- 'vertical';
+
+ let _previousClientId = order[ insertionPoint.index - 1 ];
+ let _nextClientId = order[ insertionPoint.index ];
+
+ while ( isBlockBeingDragged( _previousClientId ) ) {
+ _previousClientId = getPreviousBlockClientId( _previousClientId );
+ }
+
+ while ( isBlockBeingDragged( _nextClientId ) ) {
+ _nextClientId = getNextBlockClientId( _nextClientId );
+ }
return {
- previousClientId: previous,
- nextClientId: next,
- isHidden:
- hasReducedUI ||
- ( hasMultiSelection()
- ? next && multiSelectedBlockClientIds.includes( next )
- : next &&
- blockOrientation === 'vertical' &&
- next === selectedBlockClientId ),
- orientation: blockOrientation,
- clientId: targetClientId,
- rootClientId: targetRootClientId,
+ previousClientId: _previousClientId,
+ nextClientId: _nextClientId,
+ orientation:
+ getBlockListSettings( insertionPoint.rootClientId )
+ ?.orientation || 'vertical',
+ rootClientId: insertionPoint.rootClientId,
isInserterShown: insertionPoint?.__unstableWithInserter,
};
}, [] );
@@ -193,14 +181,7 @@ function InsertionPointPopover( {
// Only show the inserter when there's a `nextElement` (a block after the
// insertion point). At the end of the block list the trailing appender
// should serve the purpose of inserting blocks.
- const showInsertionPointInserter =
- ! isHidden && nextElement && isInserterShown;
-
- // Show the indicator if the insertion point inserter is visible, or if
- // the `showInsertionPoint` state is `true`. The latter is generally true
- // when hovering blocks for insertion in the block library.
- const showInsertionPointIndicator =
- showInsertionPointInserter || ! isHidden;
+ const showInsertionPointInserter = nextElement && isInserterShown;
/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */
// While ideally it would be enough to capture the
@@ -231,9 +212,7 @@ function InsertionPointPopover( {
} ) }
style={ style }
>
- { showInsertionPointIndicator && (
-
- ) }
+
{ showInsertionPointInserter && (
{
- const { isMultiSelecting, isBlockInsertionPointVisible } = select(
- blockEditorStore
- );
-
- return isBlockInsertionPointVisible() && ! isMultiSelecting();
+ return select( blockEditorStore ).isBlockInsertionPointVisible();
}, [] );
return (
diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss
index e511a41fabe92c..072b085f387bc1 100644
--- a/packages/block-editor/src/components/block-tools/style.scss
+++ b/packages/block-editor/src/components/block-tools/style.scss
@@ -321,6 +321,11 @@
}
}
+ // Hide the block toolbar if the insertion point is shown.
+ &.is-insertion-point-visible {
+ visibility: hidden;
+ }
+
.is-dragging-components-draggable & {
opacity: 0;
// Use a minimal duration to delay hiding the element, see hide-during-dragging animation for more details.
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 391f78ac97aa3c..019f80af6d59da 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
@@ -59,16 +59,7 @@ export function getNearestBlockIndex( elements, position, orientation ) {
// If the user is dropping to the trailing edge of the block
// add 1 to the index to represent dragging after.
const isTrailingEdge = edge === 'bottom' || edge === 'right';
- let offset = isTrailingEdge ? 1 : 0;
-
- // If the target is the dragged block itself and another 1 to
- // index as the dragged block is set to `display: none` and
- // should be skipped in the calculation.
- const isTargetDraggedBlock =
- isTrailingEdge &&
- elements[ index + 1 ] &&
- elements[ index + 1 ].classList.contains( 'is-dragging' );
- offset += isTargetDraggedBlock ? 1 : 0;
+ const offset = isTrailingEdge ? 1 : 0;
// Update the currently known best candidate.
candidateDistance = distance;
@@ -144,6 +135,11 @@ export default function useBlockDropZone( {
// https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
throttled( event, event.currentTarget );
},
+ onDragLeave() {
+ throttled.cancel();
+ hideInsertionPoint();
+ setTargetBlockIndex( null );
+ },
onDragEnd() {
throttled.cancel();
hideInsertionPoint();
diff --git a/packages/block-editor/src/components/use-block-drop-zone/test/index.js b/packages/block-editor/src/components/use-block-drop-zone/test/index.js
index c0aac7b5827d92..42a9476be0cd41 100644
--- a/packages/block-editor/src/components/use-block-drop-zone/test/index.js
+++ b/packages/block-editor/src/components/use-block-drop-zone/test/index.js
@@ -209,27 +209,6 @@ describe( 'getNearestBlockIndex', () => {
expect( result ).toBe( 4 );
} );
-
- it( 'skips the block being dragged by checking for the `is-dragging` classname', () => {
- const position = { x: 0, y: 450 };
-
- const verticalElementsWithDraggedBlock = [
- ...verticalElements.slice( 0, 2 ),
- {
- ...verticalElements[ 2 ],
- classList: createMockClassList( 'wp-block is-dragging' ),
- },
- ...verticalElements.slice( 3, 4 ),
- ];
-
- const result = getNearestBlockIndex(
- verticalElementsWithDraggedBlock,
- position,
- orientation
- );
-
- expect( result ).toBe( 3 );
- } );
} );
describe( 'Horizontal block lists', () => {
@@ -342,26 +321,5 @@ describe( 'getNearestBlockIndex', () => {
expect( result ).toBe( 4 );
} );
-
- it( 'skips the block being dragged by checking for the `is-dragging` classname', () => {
- const position = { x: 450, y: 0 };
-
- const horizontalElementsWithDraggedBlock = [
- ...horizontalElements.slice( 0, 2 ),
- {
- ...horizontalElements[ 2 ],
- classList: createMockClassList( 'wp-block is-dragging' ),
- },
- ...horizontalElements.slice( 3, 4 ),
- ];
-
- const result = getNearestBlockIndex(
- horizontalElementsWithDraggedBlock,
- position,
- orientation
- );
-
- expect( result ).toBe( 3 );
- } );
} );
} );
diff --git a/packages/compose/src/hooks/use-drop-zone/index.js b/packages/compose/src/hooks/use-drop-zone/index.js
index d2052d1e8c6efe..c2a6a4224f55fb 100644
--- a/packages/compose/src/hooks/use-drop-zone/index.js
+++ b/packages/compose/src/hooks/use-drop-zone/index.js
@@ -70,6 +70,30 @@ export default function useDropZone( {
const { ownerDocument } = element;
+ /**
+ * Checks if an element is in the drop zone.
+ *
+ * @param {HTMLElement|null} elementToCheck
+ *
+ * @return {boolean} True if in drop zone, false if not.
+ */
+ function isElementInZone( elementToCheck ) {
+ if (
+ ! elementToCheck ||
+ ! element.contains( elementToCheck )
+ ) {
+ return false;
+ }
+
+ do {
+ if ( elementToCheck.dataset.isDropZone ) {
+ return elementToCheck === element;
+ }
+ } while ( ( elementToCheck = elementToCheck.parentElement ) );
+
+ return false;
+ }
+
function maybeDragStart( /** @type {DragEvent} */ event ) {
if ( isDragging ) {
return;
@@ -82,6 +106,13 @@ export default function useDropZone( {
maybeDragStart
);
+ // Note that `dragend` doesn't fire consistently for file and
+ // HTML drag events where the drag origin is outside the browser
+ // window. In Firefox it may also not fire if the originating
+ // node is removed.
+ ownerDocument.addEventListener( 'dragend', maybeDragEnd );
+ ownerDocument.addEventListener( 'mousemove', maybeDragEnd );
+
if ( onDragStartRef.current ) {
onDragStartRef.current( event );
}
@@ -125,8 +156,8 @@ export default function useDropZone( {
// (element that has been entered) should be outside the drop
// zone.
if (
- element.contains(
- /** @type {Node} */ ( event.relatedTarget )
+ isElementInZone(
+ /** @type {HTMLElement|null} */ ( event.relatedTarget )
)
) {
return;
@@ -168,33 +199,31 @@ export default function useDropZone( {
isDragging = false;
ownerDocument.addEventListener( 'dragenter', maybeDragStart );
+ ownerDocument.removeEventListener( 'dragend', maybeDragEnd );
+ ownerDocument.removeEventListener( 'mousemove', maybeDragEnd );
if ( onDragEndRef.current ) {
onDragEndRef.current( event );
}
}
+ element.dataset.isDropZone = 'true';
element.addEventListener( 'drop', onDrop );
element.addEventListener( 'dragenter', onDragEnter );
element.addEventListener( 'dragover', onDragOver );
element.addEventListener( 'dragleave', onDragLeave );
- // Note that `dragend` doesn't fire consistently for file and HTML
- // drag events where the drag origin is outside the browser window.
- // In Firefox it may also not fire if the originating node is
- // removed.
- ownerDocument.addEventListener( 'dragend', maybeDragEnd );
- ownerDocument.addEventListener( 'mouseup', maybeDragEnd );
// The `dragstart` event doesn't fire if the drag started outside
// the document.
ownerDocument.addEventListener( 'dragenter', maybeDragStart );
return () => {
+ delete element.dataset.isDropZone;
element.removeEventListener( 'drop', onDrop );
element.removeEventListener( 'dragenter', onDragEnter );
element.removeEventListener( 'dragover', onDragOver );
element.removeEventListener( 'dragleave', onDragLeave );
ownerDocument.removeEventListener( 'dragend', maybeDragEnd );
- ownerDocument.removeEventListener( 'mouseup', maybeDragEnd );
+ ownerDocument.removeEventListener( 'mousemove', maybeDragEnd );
ownerDocument.addEventListener( 'dragenter', maybeDragStart );
};
},