@wordpress/block-editor
Version:
363 lines (362 loc) • 10.3 kB
JavaScript
// packages/block-editor/src/components/grid/grid-visualizer.js
import clsx from "clsx";
import { useState, useEffect, forwardRef, useMemo } from "@wordpress/element";
import { useSelect, useDispatch } from "@wordpress/data";
import { __experimentalUseDropZone as useDropZone } from "@wordpress/compose";
import { useBlockElement } from "../block-list/use-block-props/use-block-refs";
import BlockPopoverCover from "../block-popover/cover";
import { range, GridRect, getGridInfo } from "./utils";
import { store as blockEditorStore } from "../../store";
import { useGetNumberOfBlocksBeforeCell } from "./use-get-number-of-blocks-before-cell";
import ButtonBlockAppender from "../button-block-appender";
import { unlock } from "../../lock-unlock";
import { jsx } from "react/jsx-runtime";
function GridVisualizer({ clientId, contentRef, parentLayout }) {
const isDistractionFree = useSelect(
(select) => select(blockEditorStore).getSettings().isDistractionFree,
[]
);
const gridElement = useBlockElement(clientId);
if (isDistractionFree || !gridElement) {
return null;
}
const isManualGrid = parentLayout?.isManualPlacement && window.__experimentalEnableGridInteractivity;
return /* @__PURE__ */ jsx(
GridVisualizerGrid,
{
gridClientId: clientId,
gridElement,
isManualGrid,
ref: contentRef
}
);
}
var GridVisualizerGrid = forwardRef(
({ gridClientId, gridElement, isManualGrid }, ref) => {
const [gridInfo, setGridInfo] = useState(
() => getGridInfo(gridElement)
);
const [isDroppingAllowed, setIsDroppingAllowed] = useState(false);
useEffect(() => {
const resizeCallback = () => setGridInfo(getGridInfo(gridElement));
const borderBoxSpy = new window.ResizeObserver(resizeCallback);
borderBoxSpy.observe(gridElement, { box: "border-box" });
const contentBoxSpy = new window.ResizeObserver(resizeCallback);
contentBoxSpy.observe(gridElement);
return () => {
borderBoxSpy.disconnect();
contentBoxSpy.disconnect();
};
}, [gridElement]);
useEffect(() => {
function onGlobalDrag() {
setIsDroppingAllowed(true);
}
function onGlobalDragEnd() {
setIsDroppingAllowed(false);
}
document.addEventListener("drag", onGlobalDrag);
document.addEventListener("dragend", onGlobalDragEnd);
return () => {
document.removeEventListener("drag", onGlobalDrag);
document.removeEventListener("dragend", onGlobalDragEnd);
};
}, []);
return /* @__PURE__ */ jsx(
BlockPopoverCover,
{
className: clsx("block-editor-grid-visualizer", {
"is-dropping-allowed": isDroppingAllowed
}),
clientId: gridClientId,
__unstablePopoverSlot: "__unstable-block-tools-after",
children: /* @__PURE__ */ jsx(
"div",
{
ref,
className: "block-editor-grid-visualizer__grid",
style: gridInfo.style,
children: isManualGrid ? /* @__PURE__ */ jsx(
ManualGridVisualizer,
{
gridClientId,
gridInfo
}
) : Array.from({ length: gridInfo.numItems }, (_, i) => /* @__PURE__ */ jsx(
GridVisualizerCell,
{
color: gridInfo.currentColor
},
i
))
}
)
}
);
}
);
function ManualGridVisualizer({ gridClientId, gridInfo }) {
const [highlightedRect, setHighlightedRect] = useState(null);
const gridItemStyles = useSelect(
(select) => {
const { getBlockOrder, getBlockStyles } = unlock(
select(blockEditorStore)
);
const blockOrder = getBlockOrder(gridClientId);
return getBlockStyles(blockOrder);
},
[gridClientId]
);
const occupiedRects = useMemo(() => {
const rects = [];
for (const style of Object.values(gridItemStyles)) {
const {
columnStart,
rowStart,
columnSpan = 1,
rowSpan = 1
} = style?.layout ?? {};
if (!columnStart || !rowStart) {
continue;
}
rects.push(
new GridRect({
columnStart,
rowStart,
columnSpan,
rowSpan
})
);
}
return rects;
}, [gridItemStyles]);
return range(1, gridInfo.numRows).map(
(row) => range(1, gridInfo.numColumns).map((column) => {
const isCellOccupied = occupiedRects.some(
(rect) => rect.contains(column, row)
);
const isHighlighted = highlightedRect?.contains(column, row) ?? false;
return /* @__PURE__ */ jsx(
GridVisualizerCell,
{
color: gridInfo.currentColor,
className: isHighlighted && "is-highlighted",
children: isCellOccupied ? /* @__PURE__ */ jsx(
GridVisualizerDropZone,
{
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
}
) : /* @__PURE__ */ jsx(
GridVisualizerAppender,
{
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
}
)
},
`${row}-${column}`
);
})
);
}
function GridVisualizerCell({ color, children, className }) {
return /* @__PURE__ */ jsx(
"div",
{
className: clsx(
"block-editor-grid-visualizer__cell",
className
),
style: {
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${color} 20%, #0000)`,
color
},
children
}
);
}
function useGridVisualizerDropZone(column, row, gridClientId, gridInfo, setHighlightedRect) {
const {
getBlockAttributes,
getBlockRootClientId,
canInsertBlockType,
getBlockName
} = useSelect(blockEditorStore);
const {
updateBlockAttributes,
moveBlocksToPosition,
__unstableMarkNextChangeAsNotPersistent
} = useDispatch(blockEditorStore);
const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell(
gridClientId,
gridInfo.numColumns
);
return useDropZoneWithValidation({
validateDrag(srcClientId) {
const blockName = getBlockName(srcClientId);
if (!canInsertBlockType(blockName, gridClientId)) {
return false;
}
const attributes = getBlockAttributes(srcClientId);
const rect = new GridRect({
columnStart: column,
rowStart: row,
columnSpan: attributes.style?.layout?.columnSpan,
rowSpan: attributes.style?.layout?.rowSpan
});
const isInBounds = new GridRect({
columnSpan: gridInfo.numColumns,
rowSpan: gridInfo.numRows
}).containsRect(rect);
return isInBounds;
},
onDragEnter(srcClientId) {
const attributes = getBlockAttributes(srcClientId);
setHighlightedRect(
new GridRect({
columnStart: column,
rowStart: row,
columnSpan: attributes.style?.layout?.columnSpan,
rowSpan: attributes.style?.layout?.rowSpan
})
);
},
onDragLeave() {
setHighlightedRect(
(prevHighlightedRect) => prevHighlightedRect?.columnStart === column && prevHighlightedRect?.rowStart === row ? null : prevHighlightedRect
);
},
onDrop(srcClientId) {
setHighlightedRect(null);
const attributes = getBlockAttributes(srcClientId);
updateBlockAttributes(srcClientId, {
style: {
...attributes.style,
layout: {
...attributes.style?.layout,
columnStart: column,
rowStart: row
}
}
});
__unstableMarkNextChangeAsNotPersistent();
moveBlocksToPosition(
[srcClientId],
getBlockRootClientId(srcClientId),
gridClientId,
getNumberOfBlocksBeforeCell(column, row)
);
}
});
}
function GridVisualizerDropZone({
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
}) {
return /* @__PURE__ */ jsx(
"div",
{
className: "block-editor-grid-visualizer__drop-zone",
ref: useGridVisualizerDropZone(
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
)
}
);
}
function GridVisualizerAppender({
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
}) {
const {
updateBlockAttributes,
moveBlocksToPosition,
__unstableMarkNextChangeAsNotPersistent
} = useDispatch(blockEditorStore);
const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell(
gridClientId,
gridInfo.numColumns
);
return /* @__PURE__ */ jsx(
ButtonBlockAppender,
{
rootClientId: gridClientId,
className: "block-editor-grid-visualizer__appender",
ref: useGridVisualizerDropZone(
column,
row,
gridClientId,
gridInfo,
setHighlightedRect
),
style: {
color: gridInfo.currentColor
},
onSelect: (block) => {
if (!block) {
return;
}
updateBlockAttributes(block.clientId, {
style: {
layout: {
columnStart: column,
rowStart: row
}
}
});
__unstableMarkNextChangeAsNotPersistent();
moveBlocksToPosition(
[block.clientId],
gridClientId,
gridClientId,
getNumberOfBlocksBeforeCell(column, row)
);
}
}
);
}
function useDropZoneWithValidation({
validateDrag,
onDragEnter,
onDragLeave,
onDrop
}) {
const { getDraggedBlockClientIds } = useSelect(blockEditorStore);
return useDropZone({
onDragEnter() {
const [srcClientId] = getDraggedBlockClientIds();
if (srcClientId && validateDrag(srcClientId)) {
onDragEnter(srcClientId);
}
},
onDragLeave() {
onDragLeave();
},
onDrop() {
const [srcClientId] = getDraggedBlockClientIds();
if (srcClientId && validateDrag(srcClientId)) {
onDrop(srcClientId);
}
}
});
}
export {
GridVisualizer
};
//# sourceMappingURL=grid-visualizer.js.map