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

Drag and drop: Allow dragging from inserter or desktop to template parts #58589

Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ function BlockListBlockProvider( props ) {
__unstableIsFullySelected,
__unstableSelectionHasUnmergeableBlock,
isBlockBeingDragged,
isDraggingBlocks,
isDragging,
hasBlockMovingClientId,
canInsertBlockType,
__unstableHasActiveBlockOverlayActive,
Expand Down Expand Up @@ -602,7 +602,7 @@ function BlockListBlockProvider( props ) {
isOutlineEnabled: outlineMode,
hasOverlay:
__unstableHasActiveBlockOverlayActive( clientId ) &&
! isDraggingBlocks(),
! isDragging(),
initialPosition:
_isSelected && __unstableGetEditorMode() === 'edit'
? getSelectedBlocksInitialCaretPosition()
Expand Down
4 changes: 2 additions & 2 deletions packages/block-editor/src/components/inner-blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
getBlockRootClientId,
getBlockEditingMode,
getBlockSettings,
isDraggingBlocks,
isDragging,
} = unlock( select( blockEditorStore ) );
const { hasBlockSupport, getBlockType } = select( blocksStore );
const blockName = getBlockName( clientId );
Expand All @@ -219,7 +219,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) {
! isBlockSelected( clientId ) &&
! hasSelectedInnerBlock( clientId, true ) &&
enableClickThrough &&
! isDraggingBlocks(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it still necessary to have isDraggingBlocks given the addition of isDragging? When would one use one and not the other? Where do we still use isDraggingBlocks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to be used much but it's a public API so we can't really get rid of it altogether 😅
We could maybe refactor existing uses of it but that's better done as a separate PR I reckon. Not sure if there is a case for knowing that the thing being dragged is specifically a block 🤔

! isDragging(),
name: blockName,
blockType: getBlockType( blockName ),
parentLock: getTemplateLock( parentClientId ),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -36,11 +39,16 @@ const InserterDraggableBlocks = ( {
[ blocks ]
);

const { startDragging, stopDragging } = unlock(
useDispatch( blockEditorStore )
);

return (
<Draggable
__experimentalTransferDataType="wp-blocks"
transferData={ transferData }
onDragStart={ ( event ) => {
startDragging();
const parsedBlocks =
pattern?.type === INSERTER_PATTERN_TYPES.user &&
pattern?.syncStatus !== 'unsynced'
Expand All @@ -51,6 +59,9 @@ const InserterDraggableBlocks = ( {
serialize( parsedBlocks )
);
} }
onDragEnd={ () => {
stopDragging();
} }
__experimentalDragComponent={
<BlockDraggableChip
count={ blocks.length }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
isPointWithinTopAndBottomBoundariesOfRect,
} from '../../utils/math';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';

const THRESHOLD_DISTANCE = 30;
const MINIMUM_HEIGHT_FOR_THRESHOLD = 120;
Expand Down Expand Up @@ -308,9 +309,14 @@ export default function useBlockDropZone( {
getDraggedBlockClientIds,
getBlockNamesByClientId,
getAllowedBlocks,
} = useSelect( blockEditorStore );
const { showInsertionPoint, hideInsertionPoint } =
useDispatch( blockEditorStore );
isDragging,
} = unlock( useSelect( blockEditorStore ) );
const {
showInsertionPoint,
hideInsertionPoint,
startDragging,
stopDragging,
} = unlock( useDispatch( blockEditorStore ) );

const onBlockDrop = useOnBlockDrop(
dropTarget.operation === 'before' || dropTarget.operation === 'after'
Expand All @@ -325,6 +331,11 @@ export default function useBlockDropZone( {
const throttled = useThrottle(
useCallback(
( event, ownerDocument ) => {
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,
Expand Down Expand Up @@ -423,6 +434,8 @@ export default function useBlockDropZone( {
getBlockIndex,
registry,
showInsertionPoint,
isDragging,
startDragging,
]
),
200
Expand All @@ -444,6 +457,7 @@ export default function useBlockDropZone( {
},
onDragEnd() {
throttled.cancel();
stopDragging();
hideInsertionPoint();
},
} );
Expand Down
22 changes: 22 additions & 0 deletions packages/block-editor/src/store/private-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};
}
11 changes: 11 additions & 0 deletions packages/block-editor/src/store/private-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,14 @@ export function getAllBlockBindingsSources( state ) {
export function getBlockBindingsSource( state, sourceName ) {
return state.blockBindingsSources[ sourceName ];
}

/**
* Returns true if the user is dragging, or false otherwise.
*
* @param {Object} state Global application state.
*
* @return {boolean} Whether user is dragging.
*/
export function isDragging( state ) {
return !! state.isDragging;
andrewserong marked this conversation as resolved.
Show resolved Hide resolved
}
22 changes: 22 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think our unit test coverage for selectors, actions, and reducers is very high so should probably maintain that by adding unit tests for these new ones. Even though they're painfully simple... maybe ChatGPT can write tests for you.


/**
* Reducer returning dragged block client id.
*
Expand Down Expand Up @@ -2048,6 +2069,7 @@ function blockPatterns( state = [], action ) {

const combinedReducers = combineReducers( {
blocks,
isDragging,
isTyping,
isBlockInterfaceHidden,
draggedBlocks,
Expand Down
Loading