@wordpress/block-editor
Version:
204 lines (197 loc) • 7.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useClipboardHandler;
var _blocks = require("@wordpress/blocks");
var _dom = require("@wordpress/dom");
var _data = require("@wordpress/data");
var _compose = require("@wordpress/compose");
var _store = require("../../store");
var _useNotifyCopy = require("../../utils/use-notify-copy");
var _utils = require("./utils");
var _pasting = require("../../utils/pasting");
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
function useClipboardHandler() {
const registry = (0, _data.useRegistry)();
const {
getBlocksByClientId,
getSelectedBlockClientIds,
hasMultiSelection,
getSettings,
getBlockName,
__unstableIsFullySelected,
__unstableIsSelectionCollapsed,
__unstableIsSelectionMergeable,
__unstableGetSelectedBlocksWithPartialSelection,
canInsertBlockType,
getBlockRootClientId
} = (0, _data.useSelect)(_store.store);
const {
flashBlock,
removeBlocks,
replaceBlocks,
__unstableDeleteSelection,
__unstableExpandSelection,
__unstableSplitSelection
} = (0, _data.useDispatch)(_store.store);
const notifyCopy = (0, _useNotifyCopy.useNotifyCopy)();
return (0, _compose.useRefEffect)(node => {
function handler(event) {
if (event.defaultPrevented) {
// This was likely already handled in rich-text/use-paste-handler.js.
return;
}
const selectedBlockClientIds = getSelectedBlockClientIds();
if (selectedBlockClientIds.length === 0) {
return;
}
// Let native copy/paste behaviour take over in input fields.
// But always handle multiple selected blocks.
if (!hasMultiSelection()) {
const {
target
} = event;
const {
ownerDocument
} = target;
// If copying, only consider actual text selection as selection.
// Otherwise, any focus on an input field is considered.
const hasSelection = event.type === 'copy' || event.type === 'cut' ? (0, _dom.documentHasUncollapsedSelection)(ownerDocument) : (0, _dom.documentHasSelection)(ownerDocument) && !ownerDocument.activeElement.isContentEditable;
// Let native copy behaviour take over in input fields.
if (hasSelection) {
return;
}
}
const {
activeElement
} = event.target.ownerDocument;
if (!node.contains(activeElement)) {
return;
}
const isSelectionMergeable = __unstableIsSelectionMergeable();
const shouldHandleWholeBlocks = __unstableIsSelectionCollapsed() || __unstableIsFullySelected();
const expandSelectionIsNeeded = !shouldHandleWholeBlocks && !isSelectionMergeable;
if (event.type === 'copy' || event.type === 'cut') {
event.preventDefault();
if (selectedBlockClientIds.length === 1) {
flashBlock(selectedBlockClientIds[0]);
}
// If we have a partial selection that is not mergeable, just
// expand the selection to the whole blocks.
if (expandSelectionIsNeeded) {
__unstableExpandSelection();
} else {
notifyCopy(event.type, selectedBlockClientIds);
let blocks;
// Check if we have partial selection.
if (shouldHandleWholeBlocks) {
blocks = getBlocksByClientId(selectedBlockClientIds);
} else {
const [head, tail] = __unstableGetSelectedBlocksWithPartialSelection();
const inBetweenBlocks = getBlocksByClientId(selectedBlockClientIds.slice(1, selectedBlockClientIds.length - 1));
blocks = [head, ...inBetweenBlocks, tail];
}
(0, _utils.setClipboardBlocks)(event, blocks, registry);
}
}
if (event.type === 'cut') {
// We need to also check if at the start we needed to
// expand the selection, as in this point we might have
// programmatically fully selected the blocks above.
if (shouldHandleWholeBlocks && !expandSelectionIsNeeded) {
removeBlocks(selectedBlockClientIds);
} else {
event.target.ownerDocument.activeElement.contentEditable = false;
__unstableDeleteSelection();
}
} else if (event.type === 'paste') {
const {
__experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML
} = getSettings();
const isInternal = event.clipboardData.getData('rich-text') === 'true';
if (isInternal) {
return;
}
const {
plainText,
html,
files
} = (0, _pasting.getPasteEventData)(event);
const isFullySelected = __unstableIsFullySelected();
let blocks = [];
if (files.length) {
const fromTransforms = (0, _blocks.getBlockTransforms)('from');
blocks = files.reduce((accumulator, file) => {
const transformation = (0, _blocks.findTransform)(fromTransforms, transform => transform.type === 'files' && transform.isMatch([file]));
if (transformation) {
accumulator.push(transformation.transform([file]));
}
return accumulator;
}, []).flat();
} else {
blocks = (0, _blocks.pasteHandler)({
HTML: html,
plainText,
mode: isFullySelected ? 'BLOCKS' : 'AUTO',
canUserUseUnfilteredHTML
});
}
// Inline paste: let rich text handle it.
if (typeof blocks === 'string') {
return;
}
if (isFullySelected) {
replaceBlocks(selectedBlockClientIds, blocks, blocks.length - 1, -1);
event.preventDefault();
return;
}
// If a block doesn't support splitting, let rich text paste
// inline.
if (!hasMultiSelection() && !(0, _blocks.hasBlockSupport)(getBlockName(selectedBlockClientIds[0]), 'splitting', false) && !event.__deprecatedOnSplit) {
return;
}
const [firstSelectedClientId] = selectedBlockClientIds;
const rootClientId = getBlockRootClientId(firstSelectedClientId);
const newBlocks = [];
for (const block of blocks) {
if (canInsertBlockType(block.name, rootClientId)) {
newBlocks.push(block);
} else {
// If a block cannot be inserted in a root block, try
// converting it to that root block type and insert the
// inner blocks.
// Example: paragraphs cannot be inserted into a list,
// so convert the paragraphs to a list for list items.
const rootBlockName = getBlockName(rootClientId);
const switchedBlocks = block.name !== rootBlockName ? (0, _blocks.switchToBlockType)(block, rootBlockName) : [block];
if (!switchedBlocks) {
return;
}
for (const switchedBlock of switchedBlocks) {
for (const innerBlock of switchedBlock.innerBlocks) {
newBlocks.push(innerBlock);
}
}
}
}
__unstableSplitSelection(newBlocks);
event.preventDefault();
}
}
node.ownerDocument.addEventListener('copy', handler);
node.ownerDocument.addEventListener('cut', handler);
node.ownerDocument.addEventListener('paste', handler);
return () => {
node.ownerDocument.removeEventListener('copy', handler);
node.ownerDocument.removeEventListener('cut', handler);
node.ownerDocument.removeEventListener('paste', handler);
};
}, []);
}
//# sourceMappingURL=use-clipboard-handler.js.map