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

Refactor block drop event handlers into a single hook to support drag and drop in List View #24649

Merged
merged 6 commits into from
Aug 21, 2020
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
183 changes: 18 additions & 165 deletions packages/block-editor/src/components/use-block-drop-zone/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { difference } from 'lodash';
* WordPress dependencies
*/
import { __unstableUseDropZone as useDropZone } from '@wordpress/components';
import {
pasteHandler,
getBlockTransforms,
findTransform,
} from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';
import { useEffect, useCallback, useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { useEffect, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import useOnBlockDrop from '../use-on-block-drop';

/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */

Expand All @@ -23,6 +23,8 @@ import { useEffect, useCallback, useState } from '@wordpress/element';
* @property {number} y The vertical position of the block being dragged.
*/

/** @typedef {import('@wordpress/dom').WPPoint} WPPoint */

/**
* The orientation of a block list.
*
Expand Down Expand Up @@ -126,36 +128,6 @@ export function getNearestBlockIndex( elements, position, orientation ) {
return candidateIndex;
}

/**
* Retrieve the data for a block drop event.
*
* @param {WPSyntheticEvent} event The drop event.
*
* @return {Object} An object with block drag and drop data.
*/
function parseDropEvent( event ) {
let result = {
srcRootClientId: null,
srcClientIds: null,
type: null,
};

if ( ! event.dataTransfer ) {
return result;
}

try {
result = Object.assign(
result,
JSON.parse( event.dataTransfer.getData( 'text' ) )
);
} catch ( err ) {
return result;
}

return result;
}

/**
* @typedef {Object} WPBlockDropZoneConfig
* @property {Object} element A React ref object pointing to the block list's DOM element.
Expand All @@ -179,149 +151,30 @@ export default function useBlockDropZone( {
} ) {
const [ targetBlockIndex, setTargetBlockIndex ] = useState( null );

const {
getClientIdsOfDescendants,
getBlockIndex,
hasUploadPermissions,
isLockedAll,
orientation,
} = useSelect(
const { isLockedAll, orientation } = useSelect(
( select ) => {
const {
getBlockListSettings,
getClientIdsOfDescendants: _getClientIdsOfDescendants,
getBlockIndex: _getBlockIndex,
getSettings,
getTemplateLock,
} = select( 'core/block-editor' );
const { getBlockListSettings, getTemplateLock } = select(
'core/block-editor'
);
return {
isLockedAll: getTemplateLock( targetRootClientId ) === 'all',
orientation: getBlockListSettings( targetRootClientId )
?.orientation,
getClientIdsOfDescendants: _getClientIdsOfDescendants,
getBlockIndex: _getBlockIndex,
hasUploadPermissions: !! getSettings().mediaUpload,
isLockedAll: getTemplateLock( targetRootClientId ) === 'all',
};
},
[ targetRootClientId ]
);
const {
insertBlocks,
updateBlockAttributes,
moveBlocksToPosition,
} = useDispatch( 'core/block-editor' );

const onFilesDrop = useCallback(
( files ) => {
if ( ! hasUploadPermissions ) {
return;
}

const transformation = findTransform(
getBlockTransforms( 'from' ),
( transform ) =>
transform.type === 'files' && transform.isMatch( files )
);

if ( transformation ) {
const blocks = transformation.transform(
files,
updateBlockAttributes
);
insertBlocks( blocks, targetBlockIndex, targetRootClientId );
}
},
[
hasUploadPermissions,
updateBlockAttributes,
insertBlocks,
targetBlockIndex,
targetRootClientId,
]
);

const onHTMLDrop = useCallback(
( HTML ) => {
const blocks = pasteHandler( { HTML, mode: 'BLOCKS' } );

if ( blocks.length ) {
insertBlocks( blocks, targetBlockIndex, targetRootClientId );
}
},
[ insertBlocks, targetBlockIndex, targetRootClientId ]
);

const onDrop = useCallback(
( event ) => {
const {
srcRootClientId: sourceRootClientId,
srcClientIds: sourceClientIds,
type: dropType,
} = parseDropEvent( event );

// If the user isn't dropping a block, return early.
if ( dropType !== 'block' ) {
return;
}

const sourceBlockIndex = getBlockIndex(
sourceClientIds[ 0 ],
sourceRootClientId
);

// If the user is dropping to the same position, return early.
if (
sourceRootClientId === targetRootClientId &&
sourceBlockIndex === targetBlockIndex
) {
return;
}

// If the user is attempting to drop a block within its own
// nested blocks, return early as this would create infinite
// recursion.
if (
sourceClientIds.includes( targetRootClientId ) ||
getClientIdsOfDescendants( sourceClientIds ).some(
( id ) => id === targetRootClientId
)
) {
return;
}

// If the block is kept at the same level and moved downwards,
// subtract to take into account that the blocks being dragged
// were removed from the block list.
const isAtSameLevel = sourceRootClientId === targetRootClientId;
const draggedBlockCount = sourceClientIds.length;
const insertIndex =
isAtSameLevel && sourceBlockIndex < targetBlockIndex
? targetBlockIndex - draggedBlockCount
: targetBlockIndex;

moveBlocksToPosition(
sourceClientIds,
sourceRootClientId,
targetRootClientId,
insertIndex
);
},
[
getClientIdsOfDescendants,
getBlockIndex,
targetBlockIndex,
moveBlocksToPosition,
targetRootClientId,
]
const dropEventHandlers = useOnBlockDrop(
targetRootClientId,
targetBlockIndex
);

const { position } = useDropZone( {
element,
onFilesDrop,
onHTMLDrop,
onDrop,
isDisabled: isLockedAll,
withPosition: true,
...dropEventHandlers,
} );

useEffect( () => {
Expand Down
Loading