UNPKG

@wordpress/block-editor

Version:
248 lines (209 loc) 9.38 kB
"use strict"; 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