@wordpress/block-editor
Version:
248 lines (209 loc) • 9.38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useOnBlockDrop;
exports.onBlockDrop = onBlockDrop;
exports.onFilesDrop = onFilesDrop;
exports.onHTMLDrop = onHTMLDrop;
exports.parseDropEvent = parseDropEvent;
var _element = require("@wordpress/element");
var _blocks = require("@wordpress/blocks");
var _data = require("@wordpress/data");
var _dom = require("@wordpress/dom");
var _store = require("../../store");
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/** @typedef {import('@wordpress/element').WPSyntheticEvent} WPSyntheticEvent */
/** @typedef {import('./types').WPDropOperation} WPDropOperation */
/**
* 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,
srcIndex: null,
type: null,
blocks: null
};
if (!event.dataTransfer) {
return result;
}
try {
result = Object.assign(result, JSON.parse(event.dataTransfer.getData('wp-blocks')));
} catch (err) {
return result;
}
return result;
}
/**
* A function that returns an event handler function for block drop events.
*
* @param {string} targetRootClientId The root client id where the block(s) will be inserted.
* @param {number} targetBlockIndex The index where the block(s) will be inserted.
* @param {Function} getBlockIndex A function that gets the index of a block.
* @param {Function} getClientIdsOfDescendants A function that gets the client ids of descendant blocks.
* @param {Function} moveBlocks A function that moves blocks.
* @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
* @param {Function} clearSelectedBlock A function that clears block selection.
* @return {Function} The event handler for a block drop event.
*/
function onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, clearSelectedBlock) {
return event => {
const {
srcRootClientId: sourceRootClientId,
srcClientIds: sourceClientIds,
type: dropType,
blocks
} = parseDropEvent(event); // If the user is inserting a block.
if (dropType === 'inserter') {
clearSelectedBlock();
const blocksToInsert = blocks.map(block => (0, _blocks.cloneBlock)(block));
insertOrReplaceBlocks(blocksToInsert, true, null);
} // If the user is moving a block.
if (dropType === 'block') {
const sourceBlockIndex = getBlockIndex(sourceClientIds[0]); // 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;
}
const isAtSameLevel = sourceRootClientId === targetRootClientId;
const draggedBlockCount = sourceClientIds.length; // 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 above the insertion point.
const insertIndex = isAtSameLevel && sourceBlockIndex < targetBlockIndex ? targetBlockIndex - draggedBlockCount : targetBlockIndex;
moveBlocks(sourceClientIds, sourceRootClientId, insertIndex);
}
};
}
/**
* A function that returns an event handler function for block-related file drop events.
*
* @param {string} targetRootClientId The root client id where the block(s) will be inserted.
* @param {number} targetBlockIndex The index where the block(s) will be inserted.
* @param {boolean} hasUploadPermissions Whether the user has upload permissions.
* @param {Function} updateBlockAttributes A function that updates a block's attributes.
* @param {Function} canInsertBlockType A function that returns checks whether a block type can be inserted.
* @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
*
* @return {Function} The event handler for a block-related file drop event.
*/
function onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertOrReplaceBlocks) {
return files => {
if (!hasUploadPermissions) {
return;
}
const transformation = (0, _blocks.findTransform)((0, _blocks.getBlockTransforms)('from'), transform => transform.type === 'files' && canInsertBlockType(transform.blockName, targetRootClientId) && transform.isMatch(files));
if (transformation) {
const blocks = transformation.transform(files, updateBlockAttributes);
insertOrReplaceBlocks(blocks);
}
};
}
/**
* A function that returns an event handler function for block-related HTML drop events.
*
* @param {string} targetRootClientId The root client id where the block(s) will be inserted.
* @param {number} targetBlockIndex The index where the block(s) will be inserted.
* @param {Function} insertOrReplaceBlocks A function that inserts or replaces blocks.
*
* @return {Function} The event handler for a block-related HTML drop event.
*/
function onHTMLDrop(targetRootClientId, targetBlockIndex, insertOrReplaceBlocks) {
return HTML => {
const blocks = (0, _blocks.pasteHandler)({
HTML,
mode: 'BLOCKS'
});
if (blocks.length) {
insertOrReplaceBlocks(blocks);
}
};
}
/**
* A React hook for handling block drop events.
*
* @param {string} targetRootClientId The root client id where the block(s) will be inserted.
* @param {number} targetBlockIndex The index where the block(s) will be inserted.
* @param {Object} options The optional options.
* @param {WPDropOperation} [options.operation] The type of operation to perform on drop. Could be `insert` or `replace` for now.
*
* @return {Function} A function to be passed to the onDrop handler.
*/
function useOnBlockDrop(targetRootClientId, targetBlockIndex, options = {}) {
const {
operation = 'insert'
} = options;
const hasUploadPermissions = (0, _data.useSelect)(select => select(_store.store).getSettings().mediaUpload, []);
const {
canInsertBlockType,
getBlockIndex,
getClientIdsOfDescendants,
getBlockOrder,
getBlocksByClientId
} = (0, _data.useSelect)(_store.store);
const {
insertBlocks,
moveBlocksToPosition,
updateBlockAttributes,
clearSelectedBlock,
replaceBlocks,
removeBlocks
} = (0, _data.useDispatch)(_store.store);
const registry = (0, _data.useRegistry)();
const insertOrReplaceBlocks = (0, _element.useCallback)((blocks, updateSelection = true, initialPosition = 0) => {
if (operation === 'replace') {
const clientIds = getBlockOrder(targetRootClientId);
const clientId = clientIds[targetBlockIndex];
replaceBlocks(clientId, blocks, undefined, initialPosition);
} else {
insertBlocks(blocks, targetBlockIndex, targetRootClientId, updateSelection, initialPosition);
}
}, [operation, getBlockOrder, insertBlocks, replaceBlocks, targetBlockIndex, targetRootClientId]);
const moveBlocks = (0, _element.useCallback)((sourceClientIds, sourceRootClientId, insertIndex) => {
if (operation === 'replace') {
const sourceBlocks = getBlocksByClientId(sourceClientIds);
const targetBlockClientIds = getBlockOrder(targetRootClientId);
const targetBlockClientId = targetBlockClientIds[targetBlockIndex];
registry.batch(() => {
// Remove the source blocks.
removeBlocks(sourceClientIds, false); // Replace the target block with the source blocks.
replaceBlocks(targetBlockClientId, sourceBlocks, undefined, 0);
});
} else {
moveBlocksToPosition(sourceClientIds, sourceRootClientId, targetRootClientId, insertIndex);
}
}, [operation, getBlockOrder, getBlocksByClientId, insertBlocks, moveBlocksToPosition, removeBlocks, targetBlockIndex, targetRootClientId]);
const _onDrop = onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, clearSelectedBlock);
const _onFilesDrop = onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertOrReplaceBlocks);
const _onHTMLDrop = onHTMLDrop(targetRootClientId, targetBlockIndex, insertOrReplaceBlocks);
return event => {
const files = (0, _dom.getFilesFromDataTransfer)(event.dataTransfer);
const html = event.dataTransfer.getData('text/html');
/**
* From Windows Chrome 96, the `event.dataTransfer` returns both file object and HTML.
* The order of the checks is important to recognise the HTML drop.
*/
if (html) {
_onHTMLDrop(html);
} else if (files.length) {
_onFilesDrop(files);
} else {
_onDrop(event);
}
};
}
//# sourceMappingURL=index.js.map