UNPKG

@wordpress/block-editor

Version:
125 lines (105 loc) 3.52 kB
/** * External dependencies */ import { first, last } from 'lodash'; /** * WordPress dependencies */ import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies */ import { store as blockEditorStore } from '../../store'; import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs'; /** * Returns for the deepest node at the start or end of a container node. Ignores * any text nodes that only contain HTML formatting whitespace. * * @param {Element} node Container to search. * @param {string} type 'start' or 'end'. */ function getDeepestNode(node, type) { const child = type === 'start' ? 'firstChild' : 'lastChild'; const sibling = type === 'start' ? 'nextSibling' : 'previousSibling'; while (node[child]) { node = node[child]; while (node.nodeType === node.TEXT_NODE && /^[ \t\n]*$/.test(node.data) && node[sibling]) { node = node[sibling]; } } return node; } function selector(select) { const { isMultiSelecting, getMultiSelectedBlockClientIds, hasMultiSelection, getSelectedBlockClientId } = select(blockEditorStore); return { isMultiSelecting: isMultiSelecting(), multiSelectedBlockClientIds: getMultiSelectedBlockClientIds(), hasMultiSelection: hasMultiSelection(), selectedBlockClientId: getSelectedBlockClientId() }; } export default function useMultiSelection(ref) { const { isMultiSelecting, multiSelectedBlockClientIds, hasMultiSelection, selectedBlockClientId } = useSelect(selector, []); const { selectBlock } = useDispatch(blockEditorStore); const selectedRef = useBlockRef(selectedBlockClientId); // These must be in the right DOM order. const startRef = useBlockRef(first(multiSelectedBlockClientIds)); const endRef = useBlockRef(last(multiSelectedBlockClientIds)); /** * When the component updates, and there is multi selection, we need to * select the entire block contents. */ useEffect(() => { const { ownerDocument } = ref.current; const { defaultView } = ownerDocument; if (!hasMultiSelection || isMultiSelecting) { if (!selectedBlockClientId || isMultiSelecting) { return; } const selection = defaultView.getSelection(); if (selection.rangeCount && !selection.isCollapsed) { const blockNode = selectedRef.current; const { startContainer, endContainer } = selection.getRangeAt(0); if (!!blockNode && (!blockNode.contains(startContainer) || !blockNode.contains(endContainer))) { selection.removeAllRanges(); } } return; } const { length } = multiSelectedBlockClientIds; if (length < 2) { return; } // These must be in the right DOM order. const selection = defaultView.getSelection(); const range = ownerDocument.createRange(); // The most stable way to select the whole block contents is to start // and end at the deepest points. const startNode = getDeepestNode(startRef.current, 'start'); const endNode = getDeepestNode(endRef.current, 'end'); range.setStartBefore(startNode); range.setEndAfter(endNode); selection.removeAllRanges(); selection.addRange(range); }, [hasMultiSelection, isMultiSelecting, multiSelectedBlockClientIds, selectBlock, selectedBlockClientId]); } //# sourceMappingURL=use-multi-selection.js.map