UNPKG

@gechiui/block-editor

Version:
325 lines (298 loc) 10 kB
import { createElement } from "@gechiui/element"; /** * External dependencies */ import classnames from 'classnames'; /** * GeChiUI dependencies */ import { useSelect, useDispatch } from '@gechiui/data'; import { useCallback, useRef, useMemo, createContext, useContext } from '@gechiui/element'; import { Popover, __unstableMotion as motion } from '@gechiui/components'; import { useReducedMotion } from '@gechiui/compose'; import { isRTL } from '@gechiui/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'; import { usePopoverScroll } from './use-popover-scroll'; export const InsertionPointOpenRef = createContext(); function InsertionPointPopover(_ref) { let { __unstablePopoverSlot, __unstableContentRef } = _ref; const { selectBlock } = useDispatch(blockEditorStore); const openRef = useContext(InsertionPointOpenRef); const ref = useRef(); const { orientation, previousClientId, nextClientId, rootClientId, isInserterShown } = useSelect(select => { var _getBlockListSettings; const { getBlockOrder, getBlockListSettings, getBlockInsertionPoint, isBlockBeingDragged, getPreviousBlockClientId, getNextBlockClientId } = select(blockEditorStore); const insertionPoint = getBlockInsertionPoint(); const order = getBlockOrder(insertionPoint.rootClientId); if (!order.length) { return {}; } let _previousClientId = order[insertionPoint.index - 1]; let _nextClientId = order[insertionPoint.index]; while (isBlockBeingDragged(_previousClientId)) { _previousClientId = getPreviousBlockClientId(_previousClientId); } while (isBlockBeingDragged(_nextClientId)) { _nextClientId = getNextBlockClientId(_nextClientId); } return { previousClientId: _previousClientId, nextClientId: _nextClientId, orientation: ((_getBlockListSettings = getBlockListSettings(insertionPoint.rootClientId)) === null || _getBlockListSettings === void 0 ? void 0 : _getBlockListSettings.orientation) || 'vertical', rootClientId: insertionPoint.rootClientId, isInserterShown: insertionPoint === null || insertionPoint === void 0 ? void 0 : insertionPoint.__unstableWithInserter }; }, []); const previousElement = useBlockElement(previousClientId); const nextElement = useBlockElement(nextClientId); const isVertical = orientation === 'vertical'; const style = useMemo(() => { if (!previousElement && !nextElement) { return {}; } const previousRect = previousElement ? previousElement.getBoundingClientRect() : null; const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; if (isVertical) { return { width: previousElement ? previousElement.offsetWidth : nextElement.offsetWidth, height: nextRect && previousRect ? nextRect.top - previousRect.bottom : 0 }; } let width = 0; if (previousRect && nextRect) { width = isRTL() ? previousRect.left - nextRect.right : nextRect.left - previousRect.right; } return { width, height: previousElement ? previousElement.offsetHeight : nextElement.offsetHeight }; }, [previousElement, nextElement]); const getAnchorRect = useCallback(() => { if (!previousElement && !nextElement) { return {}; } const { ownerDocument } = previousElement || nextElement; const previousRect = previousElement ? previousElement.getBoundingClientRect() : null; const nextRect = nextElement ? nextElement.getBoundingClientRect() : null; if (isVertical) { if (isRTL()) { return { top: previousRect ? previousRect.bottom : nextRect.top, left: previousRect ? previousRect.right : nextRect.right, right: previousRect ? previousRect.left : nextRect.left, bottom: nextRect ? nextRect.top : previousRect.bottom, ownerDocument }; } return { top: previousRect ? previousRect.bottom : nextRect.top, left: previousRect ? previousRect.left : nextRect.left, right: previousRect ? previousRect.right : nextRect.right, bottom: nextRect ? nextRect.top : previousRect.bottom, ownerDocument }; } if (isRTL()) { return { top: previousRect ? previousRect.top : nextRect.top, left: previousRect ? previousRect.left : nextRect.right, right: nextRect ? nextRect.right : previousRect.left, bottom: previousRect ? previousRect.bottom : nextRect.bottom, ownerDocument }; } return { top: previousRect ? previousRect.top : nextRect.top, left: previousRect ? previousRect.right : nextRect.left, right: nextRect ? nextRect.left : previousRect.right, bottom: previousRect ? previousRect.bottom : nextRect.bottom, ownerDocument }; }, [previousElement, nextElement]); const popoverScrollRef = usePopoverScroll(__unstableContentRef); const disableMotion = useReducedMotion(); 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 in-between inserter between blocks, so when there's a // previous and a next element. const showInsertionPointInserter = previousElement && nextElement && isInserterShown; // Define animation variants for the line element. const horizontalLine = { start: { width: 0, top: '50%', bottom: '50%', x: 0 }, rest: { width: 4, top: 0, bottom: 0, x: -2 }, hover: { width: 4, top: 0, bottom: 0, x: -2 } }; const verticalLine = { start: { height: 0, left: '50%', right: '50%', y: 0 }, rest: { height: 4, left: 0, right: 0, y: -2 }, hover: { height: 4, left: 0, right: 0, y: -2 } }; const lineVariants = { // Initial position starts from the center and invisible. start: { ...(!isVertical ? horizontalLine.start : verticalLine.start), opacity: 0 }, // The line expands to fill the container. If the inserter is visible it // is delayed so it appears orchestrated. rest: { ...(!isVertical ? horizontalLine.rest : verticalLine.rest), opacity: 1, borderRadius: '2px', transition: { delay: showInsertionPointInserter ? 0.4 : 0 } }, hover: { ...(!isVertical ? horizontalLine.hover : verticalLine.hover), opacity: 1, borderRadius: '2px', transition: { delay: 0.4 } } }; const inserterVariants = { start: { scale: disableMotion ? 1 : 0 }, rest: { scale: 1, transition: { delay: 0.2 } } }; /* 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, { ref: popoverScrollRef, noArrow: true, animate: false, getAnchorRect: getAnchorRect, focusOnMount: false, className: "block-editor-block-list__insertion-point-popover" // Render in the old slot if needed for backward compatibility, // otherwise render in place (not in the the default popover slot). , __unstableSlotName: __unstablePopoverSlot || null // Forces a remount of the popover when its position changes // This makes sure the popover doesn't animate from its previous position. , key: nextClientId + '--' + rootClientId }, createElement(motion.div, { layout: !disableMotion, initial: disableMotion ? 'rest' : 'start', animate: "rest", whileHover: "hover", whileTap: "pressed", exit: "start", ref: ref, tabIndex: -1, onClick: onClick, onFocus: onFocus, className: classnames(className, { 'is-with-inserter': showInsertionPointInserter }), style: style }, createElement(motion.div, { variants: lineVariants, className: "block-editor-block-list__insertion-point-indicator" }), showInsertionPointInserter && createElement(motion.div, { variants: inserterVariants, 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(_ref2) { let { children, __unstablePopoverSlot, __unstableContentRef } = _ref2; const isVisible = useSelect(select => { return select(blockEditorStore).isBlockInsertionPointVisible(); }, []); return createElement(InsertionPointOpenRef.Provider, { value: useRef(false) }, isVisible && createElement(InsertionPointPopover, { __unstablePopoverSlot: __unstablePopoverSlot, __unstableContentRef: __unstableContentRef }), children); } //# sourceMappingURL=insertion-point.js.map