@wordpress/block-editor
Version:
181 lines (175 loc) • 6.13 kB
JavaScript
/**
* WordPress dependencies
*/
import { isTextField } from '@wordpress/dom';
import { ENTER, BACKSPACE, DELETE } from '@wordpress/keycodes';
import { useSelect, useDispatch } from '@wordpress/data';
import { useRefEffect } from '@wordpress/compose';
import { createRoot } from '@wordpress/element';
import { store as blocksStore } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
import BlockDraggableChip from '../../../components/block-draggable/draggable-chip';
/**
* Adds block behaviour:
* - Removes the block on BACKSPACE.
* - Inserts a default block on ENTER.
* - Disables dragging of block contents.
*
* @param {string} clientId Block client ID.
*/
import { jsx as _jsx } from "react/jsx-runtime";
export function useEventHandlers({
clientId,
isSelected
}) {
const {
getBlockType
} = useSelect(blocksStore);
const {
getBlockRootClientId,
isZoomOut,
hasMultiSelection,
getBlockName
} = unlock(useSelect(blockEditorStore));
const {
insertAfterBlock,
removeBlock,
resetZoomLevel,
startDraggingBlocks,
stopDraggingBlocks
} = unlock(useDispatch(blockEditorStore));
return useRefEffect(node => {
if (!isSelected) {
return;
}
/**
* Interprets keydown event intent to remove or insert after block if
* key event occurs on wrapper node. This can occur when the block has
* no text fields of its own, particularly after initial insertion, to
* allow for easy deletion and continuous writing flow to add additional
* content.
*
* @param {KeyboardEvent} event Keydown event.
*/
function onKeyDown(event) {
const {
keyCode,
target
} = event;
if (keyCode !== ENTER && keyCode !== BACKSPACE && keyCode !== DELETE) {
return;
}
if (target !== node || isTextField(target)) {
return;
}
event.preventDefault();
if (keyCode === ENTER && isZoomOut()) {
resetZoomLevel();
} else if (keyCode === ENTER) {
insertAfterBlock(clientId);
} else {
removeBlock(clientId);
}
}
/**
* Prevents default dragging behavior within a block. To do: we must
* handle this in the future and clean up the drag target.
*
* @param {DragEvent} event Drag event.
*/
function onDragStart(event) {
if (node !== event.target || node.isContentEditable || node.ownerDocument.activeElement !== node || hasMultiSelection()) {
event.preventDefault();
return;
}
const data = JSON.stringify({
type: 'block',
srcClientIds: [clientId],
srcRootClientId: getBlockRootClientId(clientId)
});
event.dataTransfer.effectAllowed = 'move'; // remove "+" cursor
event.dataTransfer.clearData();
event.dataTransfer.setData('wp-blocks', data);
const {
ownerDocument
} = node;
const {
defaultView
} = ownerDocument;
const selection = defaultView.getSelection();
selection.removeAllRanges();
const domNode = document.createElement('div');
const root = createRoot(domNode);
root.render(/*#__PURE__*/_jsx(BlockDraggableChip, {
icon: getBlockType(getBlockName(clientId)).icon
}));
document.body.appendChild(domNode);
domNode.style.position = 'absolute';
domNode.style.top = '0';
domNode.style.left = '0';
domNode.style.zIndex = '1000';
domNode.style.pointerEvents = 'none';
// Setting the drag chip as the drag image actually works, but
// the behaviour is slightly different in every browser. In
// Safari, it animates, in Firefox it's slightly transparent...
// So we set a fake drag image and have to reposition it
// ourselves.
const dragElement = ownerDocument.createElement('div');
// Chrome will show a globe icon if the drag element does not
// have dimensions.
dragElement.style.width = '1px';
dragElement.style.height = '1px';
dragElement.style.position = 'fixed';
dragElement.style.visibility = 'hidden';
ownerDocument.body.appendChild(dragElement);
event.dataTransfer.setDragImage(dragElement, 0, 0);
let offset = {
x: 0,
y: 0
};
if (document !== ownerDocument) {
const frame = defaultView.frameElement;
if (frame) {
const rect = frame.getBoundingClientRect();
offset = {
x: rect.left,
y: rect.top
};
}
}
// chip handle offset
offset.x -= 58;
function over(e) {
domNode.style.transform = `translate( ${e.clientX + offset.x}px, ${e.clientY + offset.y}px )`;
}
over(event);
function end() {
ownerDocument.removeEventListener('dragover', over);
ownerDocument.removeEventListener('dragend', end);
domNode.remove();
dragElement.remove();
stopDraggingBlocks();
document.body.classList.remove('is-dragging-components-draggable');
ownerDocument.documentElement.classList.remove('is-dragging');
}
ownerDocument.addEventListener('dragover', over);
ownerDocument.addEventListener('dragend', end);
ownerDocument.addEventListener('drop', end);
startDraggingBlocks([clientId]);
// Important because it hides the block toolbar.
document.body.classList.add('is-dragging-components-draggable');
ownerDocument.documentElement.classList.add('is-dragging');
}
node.addEventListener('keydown', onKeyDown);
node.addEventListener('dragstart', onDragStart);
return () => {
node.removeEventListener('keydown', onKeyDown);
node.removeEventListener('dragstart', onDragStart);
};
}, [clientId, isSelected, getBlockRootClientId, insertAfterBlock, removeBlock, isZoomOut, resetZoomLevel, hasMultiSelection, startDraggingBlocks, stopDraggingBlocks]);
}
//# sourceMappingURL=use-selected-block-event-handlers.js.map