UNPKG

@wordpress/block-editor

Version:
244 lines (243 loc) 7.34 kB
// packages/block-editor/src/components/block-tools/insertion-point.js import clsx from "clsx"; import { useSelect, useDispatch } from "@wordpress/data"; import { useRef, createContext, useContext, useCallback } from "@wordpress/element"; import { __unstableMotion as motion } from "@wordpress/components"; import { useReducedMotion } from "@wordpress/compose"; import Inserter from "../inserter"; import { store as blockEditorStore } from "../../store"; import BlockPopoverInbetween from "../block-popover/inbetween"; import BlockDropZonePopover from "../block-popover/drop-zone"; import { unlock } from "../../lock-unlock"; import { jsx, jsxs } from "react/jsx-runtime"; var InsertionPointOpenRef = createContext(); InsertionPointOpenRef.displayName = "InsertionPointOpenRefContext"; function InbetweenInsertionPointPopover({ __unstablePopoverSlot, __unstableContentRef, operation = "insert", nearestSide = "right" }) { const { selectBlock, hideInsertionPoint } = useDispatch(blockEditorStore); const openRef = useContext(InsertionPointOpenRef); const ref = useRef(); const { orientation, previousClientId, nextClientId, rootClientId, isInserterShown, isDistractionFree, isZoomOutMode } = useSelect((select) => { const { getBlockOrder, getBlockListSettings, getBlockInsertionPoint, isBlockBeingDragged, getPreviousBlockClientId, getNextBlockClientId, getSettings, isZoomOut } = unlock(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); } const settings = getSettings(); return { previousClientId: _previousClientId, nextClientId: _nextClientId, orientation: getBlockListSettings(insertionPoint.rootClientId)?.orientation || "vertical", rootClientId: insertionPoint.rootClientId, isDistractionFree: settings.isDistractionFree, isInserterShown: insertionPoint?.__unstableWithInserter, isZoomOutMode: isZoomOut() }; }, []); const { getBlockEditingMode } = useSelect(blockEditorStore); const disableMotion = useReducedMotion(); function onClick(event) { if (event.target === ref.current && nextClientId && getBlockEditingMode(nextClientId) !== "disabled") { selectBlock(nextClientId, -1); } } function maybeHideInserterPoint(event) { if (event.target === ref.current && !openRef.current) { hideInsertionPoint(); } } function onFocus(event) { if (event.target !== ref.current) { openRef.current = true; } } const maybeResetOpenRef = useCallback( (node) => { if (!node && openRef.current) { openRef.current = false; } }, [openRef] ); const lineVariants = { // Initial position starts from the center and invisible. start: { opacity: 0, scale: 0 }, // The line expands to fill the container. If the inserter is visible it // is delayed so it appears orchestrated. rest: { opacity: 1, scale: 1, transition: { delay: isInserterShown ? 0.5 : 0, type: "tween" } }, hover: { opacity: 1, scale: 1, transition: { delay: 0.5, type: "tween" } } }; const inserterVariants = { start: { scale: disableMotion ? 1 : 0 }, rest: { scale: 1, transition: { delay: 0.4, type: "tween" } } }; if (isDistractionFree) { return null; } if (isZoomOutMode && operation !== "insert") { return null; } const orientationClassname = orientation === "horizontal" || operation === "group" ? "is-horizontal" : "is-vertical"; const className = clsx( "block-editor-block-list__insertion-point", orientationClassname ); return /* @__PURE__ */ jsx( BlockPopoverInbetween, { previousClientId, nextClientId, __unstablePopoverSlot, __unstableContentRef, operation, nearestSide, children: /* @__PURE__ */ jsxs( motion.div, { layout: !disableMotion, initial: disableMotion ? "rest" : "start", animate: "rest", whileHover: "hover", whileTap: "pressed", exit: "start", ref, tabIndex: -1, onClick, onFocus, className: clsx(className, { "is-with-inserter": isInserterShown }), onHoverEnd: maybeHideInserterPoint, children: [ /* @__PURE__ */ jsx( motion.div, { variants: lineVariants, className: "block-editor-block-list__insertion-point-indicator", "data-testid": "block-list-insertion-point-indicator" } ), isInserterShown && /* @__PURE__ */ jsx( motion.div, { variants: inserterVariants, className: clsx( "block-editor-block-list__insertion-point-inserter" ), children: /* @__PURE__ */ jsx( Inserter, { ref: maybeResetOpenRef, position: "bottom center", clientId: nextClientId, rootClientId, __experimentalIsQuick: true, onToggle: (isOpen) => { openRef.current = isOpen; }, onSelectOrClose: () => { openRef.current = false; } } ) } ) ] } ) } ); } function InsertionPoint(props) { const { insertionPoint, isVisible, isBlockListEmpty } = useSelect( (select) => { const { getBlockInsertionPoint, isBlockInsertionPointVisible, getBlockCount } = select(blockEditorStore); const blockInsertionPoint = getBlockInsertionPoint(); return { insertionPoint: blockInsertionPoint, isVisible: isBlockInsertionPointVisible(), isBlockListEmpty: getBlockCount(blockInsertionPoint?.rootClientId) === 0 }; }, [] ); if (!isVisible || // Don't render the insertion point if the block list is empty. // The insertion point will be represented by the appender instead. isBlockListEmpty) { return null; } return insertionPoint.operation === "replace" ? /* @__PURE__ */ jsx( BlockDropZonePopover, { ...props }, `${insertionPoint.rootClientId}-${insertionPoint.index}` ) : /* @__PURE__ */ jsx( InbetweenInsertionPointPopover, { operation: insertionPoint.operation, nearestSide: insertionPoint.nearestSide, ...props } ); } export { InsertionPointOpenRef, InsertionPoint as default }; //# sourceMappingURL=insertion-point.js.map