UNPKG

@wordpress/block-editor

Version:
230 lines (205 loc) 7.72 kB
import { createElement } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; import { useCallback, useRef, useMemo, createContext, useContext } from '@wordpress/element'; import { Popover } from '@wordpress/components'; import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies */ import Inserter from '../inserter'; import { store as blockEditorStore } from '../../store'; import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; export const InsertionPointOpenRef = createContext(); function InsertionPointPopover() { const { selectBlock } = useDispatch(blockEditorStore); const openRef = useContext(InsertionPointOpenRef); const ref = useRef(); const { orientation, isHidden, previousClientId, nextClientId, rootClientId, isInserterShown } = useSelect(select => { var _getBlockListSettings; const { getBlockOrder, getBlockListSettings, getMultiSelectedBlockClientIds, getSelectedBlockClientId, hasMultiSelection, getSettings, getBlockInsertionPoint } = select(blockEditorStore); const insertionPoint = getBlockInsertionPoint(); const order = getBlockOrder(insertionPoint.rootClientId); const targetClientId = order[insertionPoint.index - 1]; const targetRootClientId = insertionPoint.rootClientId; const blockOrder = getBlockOrder(targetRootClientId); if (!blockOrder.length) { return {}; } const previous = targetClientId ? targetClientId : blockOrder[blockOrder.length - 1]; const isLast = previous === blockOrder[blockOrder.length - 1]; const next = isLast ? null : blockOrder[blockOrder.indexOf(previous) + 1]; const { hasReducedUI } = getSettings(); const multiSelectedBlockClientIds = getMultiSelectedBlockClientIds(); const selectedBlockClientId = getSelectedBlockClientId(); const blockOrientation = ((_getBlockListSettings = getBlockListSettings(targetRootClientId)) === null || _getBlockListSettings === void 0 ? void 0 : _getBlockListSettings.orientation) || 'vertical'; return { previousClientId: previous, nextClientId: next, isHidden: hasReducedUI || (hasMultiSelection() ? next && multiSelectedBlockClientIds.includes(next) : next && blockOrientation === 'vertical' && next === selectedBlockClientId), orientation: blockOrientation, clientId: targetClientId, rootClientId: targetRootClientId, isInserterShown: insertionPoint === null || insertionPoint === void 0 ? void 0 : insertionPoint.__unstableWithInserter }; }, []); const previousElement = useBlockElement(previousClientId); const nextElement = useBlockElement(nextClientId); const style = useMemo(() => { if (!previousElement) { return {}; } const previousRect = previousElement.getBoundingClientRect(); const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; if (orientation === 'vertical') { return { width: previousElement.offsetWidth, height: nextRect ? nextRect.top - previousRect.bottom : 0 }; } let width = 0; if (nextElement) { width = isRTL() ? previousRect.left - nextRect.right : nextRect.left - previousRect.right; } return { width, height: previousElement.offsetHeight }; }, [previousElement, nextElement]); const getAnchorRect = useCallback(() => { const previousRect = previousElement.getBoundingClientRect(); const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; if (orientation === 'vertical') { if (isRTL()) { return { top: previousRect.bottom, left: previousRect.right, right: previousRect.left, bottom: nextRect ? nextRect.top : previousRect.bottom }; } return { top: previousRect.bottom, left: previousRect.left, right: previousRect.right, bottom: nextRect ? nextRect.top : previousRect.bottom }; } if (isRTL()) { return { top: previousRect.top, left: nextRect ? nextRect.right : previousRect.left, right: previousRect.left, bottom: previousRect.bottom }; } return { top: previousRect.top, left: previousRect.right, right: nextRect ? nextRect.left : previousRect.right, bottom: previousRect.bottom }; }, [previousElement, nextElement]); if (!previousElement) { return null; } const className = classnames('block-editor-block-list__insertion-point', 'is-' + orientation); function onClick(event) { if (event.target === ref.current && nextClientId) { selectBlock(nextClientId, -1); } } function onFocus(event) { // Only handle click on the wrapper specifically, and not an event // bubbled from the inserter itself. if (event.target !== ref.current) { openRef.current = true; } } // Only show the inserter when there's a `nextElement` (a block after the // insertion point). At the end of the block list the trailing appender // should serve the purpose of inserting blocks. const showInsertionPointInserter = !isHidden && nextElement && isInserterShown; // Show the indicator if the insertion point inserter is visible, or if // the `showInsertionPoint` state is `true`. The latter is generally true // when hovering blocks for insertion in the block library. const showInsertionPointIndicator = showInsertionPointInserter || !isHidden; /* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ // While ideally it would be enough to capture the // bubbling focus event from the Inserter, due to the // characteristics of click focusing of `button`s in // Firefox and Safari, it is not reliable. // // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus return createElement(Popover, { noArrow: true, animate: false, getAnchorRect: getAnchorRect, focusOnMount: false, className: "block-editor-block-list__insertion-point-popover", __unstableSlotName: "block-toolbar" }, createElement("div", { ref: ref, tabIndex: -1, onClick: onClick, onFocus: onFocus, className: classnames(className, { 'is-with-inserter': showInsertionPointInserter }), style: style }, showInsertionPointIndicator && createElement("div", { className: "block-editor-block-list__insertion-point-indicator" }), showInsertionPointInserter && createElement("div", { className: classnames('block-editor-block-list__insertion-point-inserter') }, createElement(Inserter, { position: "bottom center", clientId: nextClientId, rootClientId: rootClientId, __experimentalIsQuick: true, onToggle: isOpen => { openRef.current = isOpen; }, onSelectOrClose: () => { openRef.current = false; } })))); /* eslint-enable jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */ } export default function InsertionPoint({ children }) { const isVisible = useSelect(select => { const { isMultiSelecting, isBlockInsertionPointVisible } = select(blockEditorStore); return isBlockInsertionPointVisible() && !isMultiSelecting(); }, []); return createElement(InsertionPointOpenRef.Provider, { value: useRef(false) }, isVisible && createElement(InsertionPointPopover, null), children); } //# sourceMappingURL=insertion-point.js.map