diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index fa814630b00bb8..69fe3161126185 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -524,7 +524,7 @@ function BlockListBlockProvider( props ) { __unstableIsFullySelected, __unstableSelectionHasUnmergeableBlock, isBlockBeingDragged, - isDraggingBlocks, + isDragging, hasBlockMovingClientId, canInsertBlockType, __unstableHasActiveBlockOverlayActive, @@ -602,7 +602,7 @@ function BlockListBlockProvider( props ) { isOutlineEnabled: outlineMode, hasOverlay: __unstableHasActiveBlockOverlayActive( clientId ) && - ! isDraggingBlocks(), + ! isDragging(), initialPosition: _isSelected && __unstableGetEditorMode() === 'edit' ? getSelectedBlocksInitialCaretPosition() diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 76f3d70041464f..deb4328212b105 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -203,7 +203,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { getBlockRootClientId, getBlockEditingMode, getBlockSettings, - isDraggingBlocks, + isDragging, } = unlock( select( blockEditorStore ) ); const { hasBlockSupport, getBlockType } = select( blocksStore ); const blockName = getBlockName( clientId ); @@ -223,7 +223,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { ! isBlockSelected( clientId ) && ! hasSelectedInnerBlock( clientId, true ) && enableClickThrough && - ! isDraggingBlocks(), + ! isDragging(), name: blockName, blockType: getBlockType( blockName ), parentLock: getTemplateLock( parentClientId ), diff --git a/packages/block-editor/src/components/inserter-draggable-blocks/index.js b/packages/block-editor/src/components/inserter-draggable-blocks/index.js index cdaadcb0f36eb5..7d20b5e53650bf 100644 --- a/packages/block-editor/src/components/inserter-draggable-blocks/index.js +++ b/packages/block-editor/src/components/inserter-draggable-blocks/index.js @@ -7,12 +7,15 @@ import { serialize, store as blocksStore, } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; + /** * Internal dependencies */ import BlockDraggableChip from '../block-draggable/draggable-chip'; import { INSERTER_PATTERN_TYPES } from '../inserter/block-patterns-tab/utils'; +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; const InserterDraggableBlocks = ( { isEnabled, @@ -36,11 +39,16 @@ const InserterDraggableBlocks = ( { [ blocks ] ); + const { startDragging, stopDragging } = unlock( + useDispatch( blockEditorStore ) + ); + return ( { + startDragging(); const parsedBlocks = pattern?.type === INSERTER_PATTERN_TYPES.user && pattern?.syncStatus !== 'unsynced' @@ -51,6 +59,9 @@ const InserterDraggableBlocks = ( { serialize( parsedBlocks ) ); } } + onDragEnd={ () => { + stopDragging(); + } } __experimentalDragComponent={ { + if ( ! isDragging() ) { + // When dragging from the desktop, no drag start event is fired. + // So, ensure that the drag state is set when the user drags over a drop zone. + startDragging(); + } const allowedBlocks = getAllowedBlocks( targetRootClientId ); const targetBlockName = getBlockNamesByClientId( [ targetRootClientId, @@ -423,6 +434,8 @@ export default function useBlockDropZone( { getBlockIndex, registry, showInsertionPoint, + isDragging, + startDragging, ] ), 200 @@ -444,6 +457,7 @@ export default function useBlockDropZone( { }, onDragEnd() { throttled.cancel(); + stopDragging(); hideInsertionPoint(); }, } ); diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index aea3613884bb69..1d0d41197bc1ee 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -370,3 +370,25 @@ export function registerBlockBindingsSource( source ) { lockAttributesEditing: source.lockAttributesEditing, }; } + +/** + * Returns an action object used in signalling that the user has begun to drag. + * + * @return {Object} Action object. + */ +export function startDragging() { + return { + type: 'START_DRAGGING', + }; +} + +/** + * Returns an action object used in signalling that the user has stopped dragging. + * + * @return {Object} Action object. + */ +export function stopDragging() { + return { + type: 'STOP_DRAGGING', + }; +} diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index ee8faeab155b8c..4700e50f739f45 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -351,3 +351,16 @@ export function getAllBlockBindingsSources( state ) { export function getBlockBindingsSource( state, sourceName ) { return state.blockBindingsSources[ sourceName ]; } + +/** + * Returns true if the user is dragging anything, or false otherwise. It is possible for a + * user to be dragging data from outside of the editor, so this selector is separate from + * the `isDraggingBlocks` selector which only returns true if the user is dragging blocks. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether user is dragging. + */ +export function isDragging( state ) { + return state.isDragging; +} diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index dc69a4da609a4d..c465e390213036 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1246,6 +1246,27 @@ export function isTyping( state = false, action ) { return state; } +/** + * Reducer returning dragging state. It is possible for a user to be dragging + * data from outside of the editor, so this state is separate from `draggedBlocks`. + * + * @param {boolean} state Current state. + * @param {Object} action Dispatched action. + * + * @return {boolean} Updated state. + */ +export function isDragging( state = false, action ) { + switch ( action.type ) { + case 'START_DRAGGING': + return true; + + case 'STOP_DRAGGING': + return false; + } + + return state; +} + /** * Reducer returning dragged block client id. * @@ -2048,6 +2069,7 @@ function blockPatterns( state = [], action ) { const combinedReducers = combineReducers( { blocks, + isDragging, isTyping, isBlockInterfaceHidden, draggedBlocks, diff --git a/packages/block-editor/src/store/test/private-actions.js b/packages/block-editor/src/store/test/private-actions.js index 5763cb382937b2..08370f731902d2 100644 --- a/packages/block-editor/src/store/test/private-actions.js +++ b/packages/block-editor/src/store/test/private-actions.js @@ -6,6 +6,8 @@ import { showBlockInterface, __experimentalUpdateSettings, setOpenedBlockSettingsMenu, + startDragging, + stopDragging, } from '../private-actions'; describe( 'private actions', () => { @@ -95,4 +97,20 @@ describe( 'private actions', () => { } ); } ); } ); + + describe( 'startDragging', () => { + it( 'should return the START_DRAGGING action', () => { + expect( startDragging() ).toEqual( { + type: 'START_DRAGGING', + } ); + } ); + } ); + + describe( 'stopDragging', () => { + it( 'should return the STOP_DRAGGING action', () => { + expect( stopDragging() ).toEqual( { + type: 'STOP_DRAGGING', + } ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index 746a51b6031101..f661271b570b4b 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -7,6 +7,7 @@ import { isBlockSubtreeDisabled, getEnabledClientIdsTree, getEnabledBlockParents, + isDragging, } from '../private-selectors'; import { getBlockEditingMode } from '../selectors'; @@ -477,4 +478,22 @@ describe( 'private selectors', () => { ] ); } ); } ); + + describe( 'isDragging', () => { + it( 'should return true if the dragging state is true', () => { + const state = { + isDragging: true, + }; + + expect( isDragging( state ) ).toBe( true ); + } ); + + it( 'should return false if the dragging state is false', () => { + const state = { + isDragging: false, + }; + + expect( isDragging( state ) ).toBe( false ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 8c82d1c3092b16..c99d914ba21755 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -21,6 +21,7 @@ import { blocks, isBlockInterfaceHidden, isTyping, + isDragging, draggedBlocks, selection, initialPosition, @@ -2445,6 +2446,24 @@ describe( 'state', () => { } ); } ); + describe( 'isDragging', () => { + it( 'should set the dragging flag to true', () => { + const state = isDragging( false, { + type: 'START_DRAGGING', + } ); + + expect( state ).toBe( true ); + } ); + + it( 'should set the dragging flag to false', () => { + const state = isDragging( true, { + type: 'STOP_DRAGGING', + } ); + + expect( state ).toBe( false ); + } ); + } ); + describe( 'draggedBlocks', () => { it( 'should store the dragged client ids when a user starts dragging blocks', () => { const clientIds = [ 'block-1', 'block-2', 'block-3' ];