@wordpress/block-editor
Version:
270 lines (269 loc) • 7.66 kB
JavaScript
// packages/block-editor/src/components/use-on-block-drop/index.js
import { useCallback } from "@wordpress/element";
import {
cloneBlock,
createBlock,
findTransform,
getBlockTransforms,
pasteHandler,
store as blocksStore
} from "@wordpress/blocks";
import { useDispatch, useSelect, useRegistry } from "@wordpress/data";
import { getFilesFromDataTransfer } from "@wordpress/dom";
import { store as blockEditorStore } from "../../store";
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;
}
function onBlockDrop(targetRootClientId, targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, moveBlocks, insertOrReplaceBlocks, clearSelectedBlock, operation, getBlock) {
return (event) => {
const {
srcRootClientId: sourceRootClientId,
srcClientIds: sourceClientIds,
type: dropType,
blocks
} = parseDropEvent(event);
if (dropType === "inserter") {
clearSelectedBlock();
const blocksToInsert = blocks.map(
(block) => cloneBlock(block)
);
insertOrReplaceBlocks(blocksToInsert, true, null);
}
if (dropType === "block") {
const sourceBlockIndex = getBlockIndex(sourceClientIds[0]);
if (sourceRootClientId === targetRootClientId && sourceBlockIndex === targetBlockIndex) {
return;
}
if (sourceClientIds.includes(targetRootClientId) || getClientIdsOfDescendants(sourceClientIds).some(
(id) => id === targetRootClientId
)) {
return;
}
if (operation === "group") {
const blocksToInsert = sourceClientIds.map(
(clientId) => getBlock(clientId)
);
insertOrReplaceBlocks(
blocksToInsert,
true,
null,
sourceClientIds
);
return;
}
const isAtSameLevel = sourceRootClientId === targetRootClientId;
const draggedBlockCount = sourceClientIds.length;
const insertIndex = isAtSameLevel && sourceBlockIndex < targetBlockIndex ? targetBlockIndex - draggedBlockCount : targetBlockIndex;
moveBlocks(sourceClientIds, sourceRootClientId, insertIndex);
}
};
}
function onFilesDrop(targetRootClientId, getSettings, updateBlockAttributes, canInsertBlockType, insertOrReplaceBlocks) {
return (files) => {
if (!getSettings().mediaUpload) {
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
);
insertOrReplaceBlocks(blocks);
}
};
}
function onHTMLDrop(insertOrReplaceBlocks) {
return (HTML) => {
const blocks = pasteHandler({ HTML, mode: "BLOCKS" });
if (blocks.length) {
insertOrReplaceBlocks(blocks);
}
};
}
function useOnBlockDrop(targetRootClientId, targetBlockIndex, options = {}) {
const { operation = "insert", nearestSide = "right" } = options;
const {
canInsertBlockType,
getBlockIndex,
getClientIdsOfDescendants,
getBlockOrder,
getBlocksByClientId,
getSettings,
getBlock
} = useSelect(blockEditorStore);
const { getGroupingBlockName } = useSelect(blocksStore);
const {
insertBlocks,
moveBlocksToPosition,
updateBlockAttributes,
clearSelectedBlock,
replaceBlocks,
removeBlocks
} = useDispatch(blockEditorStore);
const registry = useRegistry();
const insertOrReplaceBlocks = useCallback(
(blocks, updateSelection = true, initialPosition = 0, clientIdsToReplace = []) => {
if (!Array.isArray(blocks)) {
blocks = [blocks];
}
const clientIds = getBlockOrder(targetRootClientId);
const clientId = clientIds[targetBlockIndex];
if (operation === "replace") {
replaceBlocks(clientId, blocks, void 0, initialPosition);
} else if (operation === "group") {
const targetBlock = getBlock(clientId);
if (nearestSide === "left") {
blocks.push(targetBlock);
} else {
blocks.unshift(targetBlock);
}
const groupInnerBlocks = blocks.map((block) => {
return createBlock(
block.name,
block.attributes,
block.innerBlocks
);
});
const areAllImages = blocks.every((block) => {
return block.name === "core/image";
});
const galleryBlock = canInsertBlockType(
"core/gallery",
targetRootClientId
);
const wrappedBlocks = createBlock(
areAllImages && galleryBlock ? "core/gallery" : getGroupingBlockName(),
{
layout: {
type: "flex",
flexWrap: areAllImages && galleryBlock ? null : "nowrap"
}
},
groupInnerBlocks
);
replaceBlocks(
[clientId, ...clientIdsToReplace],
wrappedBlocks,
void 0,
initialPosition
);
} else {
insertBlocks(
blocks,
targetBlockIndex,
targetRootClientId,
updateSelection,
initialPosition
);
}
},
[
getBlockOrder,
targetRootClientId,
targetBlockIndex,
operation,
replaceBlocks,
getBlock,
nearestSide,
canInsertBlockType,
getGroupingBlockName,
insertBlocks
]
);
const moveBlocks = useCallback(
(sourceClientIds, sourceRootClientId, insertIndex) => {
if (operation === "replace") {
const sourceBlocks = getBlocksByClientId(sourceClientIds);
const targetBlockClientIds = getBlockOrder(targetRootClientId);
const targetBlockClientId = targetBlockClientIds[targetBlockIndex];
registry.batch(() => {
removeBlocks(sourceClientIds, false);
replaceBlocks(
targetBlockClientId,
sourceBlocks,
void 0,
0
);
});
} else {
moveBlocksToPosition(
sourceClientIds,
sourceRootClientId,
targetRootClientId,
insertIndex
);
}
},
[
operation,
getBlockOrder,
getBlocksByClientId,
moveBlocksToPosition,
registry,
removeBlocks,
replaceBlocks,
targetBlockIndex,
targetRootClientId
]
);
const _onDrop = onBlockDrop(
targetRootClientId,
targetBlockIndex,
getBlockIndex,
getClientIdsOfDescendants,
moveBlocks,
insertOrReplaceBlocks,
clearSelectedBlock,
operation,
getBlock
);
const _onFilesDrop = onFilesDrop(
targetRootClientId,
getSettings,
updateBlockAttributes,
canInsertBlockType,
insertOrReplaceBlocks
);
const _onHTMLDrop = onHTMLDrop(insertOrReplaceBlocks);
return (event) => {
const files = getFilesFromDataTransfer(event.dataTransfer);
const html = event.dataTransfer.getData("text/html");
if (html) {
_onHTMLDrop(html);
} else if (files.length) {
_onFilesDrop(files);
} else {
_onDrop(event);
}
};
}
export {
useOnBlockDrop as default,
onBlockDrop,
onFilesDrop,
onHTMLDrop,
parseDropEvent
};
//# sourceMappingURL=index.js.map