@wordpress/block-editor
Version:
244 lines (243 loc) • 7.34 kB
JavaScript
// 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