@gechiui/block-editor
Version:
187 lines (161 loc) • 7.14 kB
JavaScript
/**
* GeChiUI dependencies
*/
import { cloneBlock, findTransform, getBlockTransforms, pasteHandler } from '@gechiui/blocks';
import { useDispatch, useSelect } from '@gechiui/data';
import { getFilesFromDataTransfer } from '@gechiui/dom';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
/** @typedef {import('@gechiui/element').GCSyntheticEvent} GCSyntheticEvent */
/**
* Retrieve the data for a block drop event.
*
* @param {GCSyntheticEvent} event The drop event.
*
* @return {Object} An object with block drag and drop data.
*/
export 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('gc-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} moveBlocksToPosition A function that moves blocks.
* @param {Function} insertBlocks A function that inserts blocks.
* @param {Function} clearSelectedBlock A function that clears block selection.
* @return {Function} The event handler for a block drop event.
*/
export function onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocksToPosition, insertBlocks, 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 => cloneBlock(block));
insertBlocks(blocksToInsert, targetBlockIndex, targetRootClientId, 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;
moveBlocksToPosition(sourceClientIds, sourceRootClientId, targetRootClientId, 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} insertBlocks A function that inserts blocks.
*
* @return {Function} The event handler for a block-related file drop event.
*/
export function onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertBlocks) {
return files => {
if (!hasUploadPermissions) {
return;
}
const transformation = findTransform(getBlockTransforms('from'), transform => transform.type === 'files' && canInsertBlockType(transform.blockName, targetRootClientId) && transform.isMatch(files));
if (transformation) {
const blocks = transformation.transform(files, updateBlockAttributes);
insertBlocks(blocks, targetBlockIndex, targetRootClientId);
}
};
}
/**
* 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} insertBlocks A function that inserts blocks.
*
* @return {Function} The event handler for a block-related HTML drop event.
*/
export function onHTMLDrop(targetRootClientId, targetBlockIndex, insertBlocks) {
return HTML => {
const blocks = pasteHandler({
HTML,
mode: 'BLOCKS'
});
if (blocks.length) {
insertBlocks(blocks, targetBlockIndex, targetRootClientId);
}
};
}
/**
* 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.
*
* @return {Object} An object that contains the event handlers `onDrop`, `onFilesDrop` and `onHTMLDrop`.
*/
export default function useOnBlockDrop(targetRootClientId, targetBlockIndex) {
const hasUploadPermissions = useSelect(select => select(blockEditorStore).getSettings().mediaUpload, []);
const {
canInsertBlockType,
getBlockIndex,
getClientIdsOfDescendants
} = useSelect(blockEditorStore);
const {
insertBlocks,
moveBlocksToPosition,
updateBlockAttributes,
clearSelectedBlock
} = useDispatch(blockEditorStore);
const _onDrop = onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocksToPosition, insertBlocks, clearSelectedBlock);
const _onFilesDrop = onFilesDrop(targetRootClientId, targetBlockIndex, hasUploadPermissions, updateBlockAttributes, canInsertBlockType, insertBlocks);
const _onHTMLDrop = onHTMLDrop(targetRootClientId, targetBlockIndex, insertBlocks);
return event => {
const files = getFilesFromDataTransfer(event.dataTransfer);
const html = event.dataTransfer.getData('text/html');
if (files.length) {
_onFilesDrop(files);
} else if (html) {
_onHTMLDrop(html);
} else {
_onDrop(event);
}
};
}
//# sourceMappingURL=index.js.map