@standhigher/puck-page-builder 0.12.6 → 0.12.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +99 -23
- package/package.json +1 -1
- package/src/styles.css +3 -0
package/dist/index.d.ts
CHANGED
|
@@ -195,7 +195,7 @@ type EditorContextValue = {
|
|
|
195
195
|
confirmDeleteBlock(): void;
|
|
196
196
|
deleteBlock(id: string): void;
|
|
197
197
|
moveBlock(id: string, direction: -1 | 1): void;
|
|
198
|
-
reorderBlock(id: string, beforeId
|
|
198
|
+
reorderBlock(id: string, beforeId?: string): void;
|
|
199
199
|
updateBlockProps(id: string, props: Record<string, JsonValue>): void;
|
|
200
200
|
updateBlockPresentation(id: string, presentation: Pick<BlockNode, "variant" | "style">): void;
|
|
201
201
|
undo(): void;
|
package/dist/index.js
CHANGED
|
@@ -759,11 +759,17 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
|
|
|
759
759
|
reorderBlock: (id, beforeId) => {
|
|
760
760
|
if (!editable || id === beforeId) return;
|
|
761
761
|
const source = history.document.blocks.find((block) => block.id === id);
|
|
762
|
+
if (!source || !canDragBlock(source, registry?.getBlock(source.type), policy)) return;
|
|
762
763
|
const withoutSource = history.document.blocks.filter((block) => block.id !== id);
|
|
763
|
-
const targetIndex = withoutSource.findIndex((block) => block.id === beforeId);
|
|
764
|
-
if (!source || targetIndex < 0 || !canDragBlock(source, registry?.getBlock(source.type), policy)) return;
|
|
765
764
|
const blocks = [...withoutSource];
|
|
766
|
-
|
|
765
|
+
if (beforeId) {
|
|
766
|
+
const targetIndex = withoutSource.findIndex((block) => block.id === beforeId);
|
|
767
|
+
if (targetIndex < 0) return;
|
|
768
|
+
blocks.splice(targetIndex, 0, source);
|
|
769
|
+
} else {
|
|
770
|
+
blocks.push(source);
|
|
771
|
+
}
|
|
772
|
+
if (blocks.every((block, index) => block.id === history.document.blocks[index]?.id)) return;
|
|
767
773
|
replace({ ...history.document, blocks }, id);
|
|
768
774
|
},
|
|
769
775
|
updateBlockProps,
|
|
@@ -966,6 +972,29 @@ import { useEffect as useEffect3, useRef as useRef4, useState as useState3 } fro
|
|
|
966
972
|
function nearestBlockIdAtY(blocks, y) {
|
|
967
973
|
return blocks.find((block) => y < block.top + block.height / 2)?.id;
|
|
968
974
|
}
|
|
975
|
+
function libraryTypesInDocumentOrder(blockTypes, blocks) {
|
|
976
|
+
const allowed = new Set(blockTypes);
|
|
977
|
+
const seen = /* @__PURE__ */ new Set();
|
|
978
|
+
const ordered = [];
|
|
979
|
+
for (const block of blocks) {
|
|
980
|
+
if (!allowed.has(block.type) || seen.has(block.type)) continue;
|
|
981
|
+
ordered.push(block.type);
|
|
982
|
+
seen.add(block.type);
|
|
983
|
+
}
|
|
984
|
+
for (const type of blockTypes) {
|
|
985
|
+
if (!seen.has(type)) ordered.push(type);
|
|
986
|
+
}
|
|
987
|
+
return ordered;
|
|
988
|
+
}
|
|
989
|
+
function dropPlacement(clientY, top, height) {
|
|
990
|
+
if (height <= 0) return "before";
|
|
991
|
+
return clientY < top + height / 2 ? "before" : "after";
|
|
992
|
+
}
|
|
993
|
+
function beforeIdAfterPlacement(blockIds, targetId, placement) {
|
|
994
|
+
if (placement === "before") return targetId;
|
|
995
|
+
const index = blockIds.indexOf(targetId);
|
|
996
|
+
return index < 0 ? targetId : blockIds[index + 1];
|
|
997
|
+
}
|
|
969
998
|
|
|
970
999
|
// src/editor/shell/CanvasLibraryDropTarget.tsx
|
|
971
1000
|
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
@@ -1221,6 +1250,9 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1221
1250
|
const [leftRailOpen, setLeftRailOpen] = useState4(true);
|
|
1222
1251
|
const [rightPanelOpen, setRightPanelOpen] = useState4(true);
|
|
1223
1252
|
const [draggingLibraryType, setDraggingLibraryType] = useState4(null);
|
|
1253
|
+
const [draggingBlockId, setDraggingBlockId] = useState4(null);
|
|
1254
|
+
const draggingBlockIdRef = useRef5(null);
|
|
1255
|
+
const [dropTarget, setDropTarget] = useState4(null);
|
|
1224
1256
|
const canvasFrameRef = useRef5(null);
|
|
1225
1257
|
const [canvasMutationVersion, setCanvasMutationVersion] = useState4(0);
|
|
1226
1258
|
const [canvasResetVersion, setCanvasResetVersion] = useState4(0);
|
|
@@ -1313,14 +1345,27 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1313
1345
|
if (editor.sessionState !== "active") return /* @__PURE__ */ jsx9(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
|
|
1314
1346
|
const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
|
|
1315
1347
|
const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
|
|
1348
|
+
const orderedBlockTypes = libraryTypesInDocumentOrder(blockTypes, editor.document.blocks);
|
|
1316
1349
|
const addFromLibrary = (type, beforeId) => {
|
|
1317
1350
|
const id = editor.addBlock(type, beforeId);
|
|
1318
1351
|
if (id) editor.requestCanvasSelection(id);
|
|
1319
1352
|
};
|
|
1353
|
+
const clearLibraryDrag = () => {
|
|
1354
|
+
draggingBlockIdRef.current = null;
|
|
1355
|
+
setDraggingLibraryType(null);
|
|
1356
|
+
setDraggingBlockId(null);
|
|
1357
|
+
setDropTarget(null);
|
|
1358
|
+
};
|
|
1320
1359
|
const cancelLibraryDrop = (event) => {
|
|
1321
1360
|
event.preventDefault();
|
|
1322
1361
|
event.stopPropagation();
|
|
1323
|
-
|
|
1362
|
+
clearLibraryDrag();
|
|
1363
|
+
};
|
|
1364
|
+
const applyListReorder = (sourceId, targetId, edge) => {
|
|
1365
|
+
const beforeId = beforeIdAfterPlacement(editorRef.current.document.blocks.map((block) => block.id), targetId, edge);
|
|
1366
|
+
editorRef.current.reorderBlock(sourceId, beforeId);
|
|
1367
|
+
editorRef.current.requestCanvasSelection(sourceId);
|
|
1368
|
+
scrollCanvasBlockIntoView(sourceId, canvasFrameRef.current);
|
|
1324
1369
|
};
|
|
1325
1370
|
const saveBadgeTone = resolvedSaveState === "failed" ? "critical" : resolvedSaveState === "offline" || resolvedSaveState === "dirty" ? "attention" : "success";
|
|
1326
1371
|
const saveBadgeLabel = resolvedSaveState === "saving" ? i18n.t("saving") : resolvedSaveState === "failed" ? i18n.t("saveFailed") : resolvedSaveState === "offline" ? i18n.t("offline") : resolvedSaveState === "dirty" ? i18n.t("unsaved") : i18n.t("saved");
|
|
@@ -1381,26 +1426,57 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1381
1426
|
/* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }),
|
|
1382
1427
|
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("collapseLeft"), icon: XIcon2, variant: "tertiary", onClick: () => setLeftRailOpen(false) })
|
|
1383
1428
|
] }),
|
|
1384
|
-
blockView === "blocks" ? /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children:
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
editor.
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1429
|
+
blockView === "blocks" ? /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: orderedBlockTypes.map((type) => {
|
|
1430
|
+
const instance = editor.document.blocks.find((item) => item.type === type);
|
|
1431
|
+
const canAdd = editor.canAddBlock(type);
|
|
1432
|
+
const canReorder = Boolean(instance && editor.canDragBlock(instance.id));
|
|
1433
|
+
const draggable = canAdd || canReorder;
|
|
1434
|
+
const selected = editor.selectedBlock?.type === type;
|
|
1435
|
+
const dropEdge = draggingBlockId && instance && dropTarget?.id === instance.id ? dropTarget.edge : void 0;
|
|
1436
|
+
const actionHint = canReorder && canAdd ? "\uFF0C\u62D6\u62FD\u6392\u5E8F\uFF0C\u6216\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0" : canReorder ? "\uFF0C\u62D6\u62FD\u6392\u5E8F" : canAdd ? "\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0" : "";
|
|
1437
|
+
return /* @__PURE__ */ jsxs7("div", { className: `pb-document-block-row pb-document-block-row--library${selected ? " pb-document-block-row--selected" : ""}${draggingBlockId === instance?.id ? " pb-document-block-row--dragging" : ""}${dropEdge === "before" ? " pb-document-block-row--drop-before" : ""}${dropEdge === "after" ? " pb-document-block-row--drop-after" : ""}`, "data-block-type": type, "data-selected": selected, "data-drop-edge": dropEdge, role: "listitem", draggable, "aria-disabled": !draggable, "aria-label": `${blockTypeLabel(type, registry)}${actionHint}${selected ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onClick: () => {
|
|
1438
|
+
if (!instance) return;
|
|
1439
|
+
editor.requestCanvasSelection(instance.id);
|
|
1440
|
+
scrollCanvasBlockIntoView(instance.id, canvasFrameRef.current);
|
|
1441
|
+
}, onDragStart: (event) => {
|
|
1442
|
+
if (!draggable) {
|
|
1443
|
+
event.preventDefault();
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
event.dataTransfer.setData("application/x-page-document-block", type);
|
|
1447
|
+
if (instance && canReorder) {
|
|
1448
|
+
event.dataTransfer.setData("application/x-page-document-block-id", instance.id);
|
|
1449
|
+
event.dataTransfer.effectAllowed = canAdd ? "copyMove" : "move";
|
|
1450
|
+
draggingBlockIdRef.current = instance.id;
|
|
1451
|
+
setDraggingBlockId(instance.id);
|
|
1452
|
+
} else event.dataTransfer.effectAllowed = "copy";
|
|
1453
|
+
if (canAdd) setDraggingLibraryType(type);
|
|
1454
|
+
}, onDragOver: (event) => {
|
|
1455
|
+
const sourceId = draggingBlockIdRef.current;
|
|
1456
|
+
if (!sourceId || !instance || sourceId === instance.id) return;
|
|
1457
|
+
event.preventDefault();
|
|
1458
|
+
event.stopPropagation();
|
|
1459
|
+
event.dataTransfer.dropEffect = "move";
|
|
1460
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1461
|
+
setDropTarget({ id: instance.id, edge: dropPlacement(event.clientY, rect.top, rect.height) });
|
|
1462
|
+
}, onDragLeave: (event) => {
|
|
1463
|
+
if (instance && !event.currentTarget.contains(event.relatedTarget)) setDropTarget((current) => current?.id === instance.id ? null : current);
|
|
1464
|
+
}, onDrop: (event) => {
|
|
1465
|
+
const sourceId = draggingBlockIdRef.current;
|
|
1466
|
+
if (!sourceId || !instance || sourceId === instance.id) return;
|
|
1391
1467
|
event.preventDefault();
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1468
|
+
event.stopPropagation();
|
|
1469
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
1470
|
+
applyListReorder(sourceId, instance.id, dropPlacement(event.clientY, rect.top, rect.height));
|
|
1471
|
+
clearLibraryDrag();
|
|
1472
|
+
}, onDragEnd: clearLibraryDrag, children: [
|
|
1473
|
+
/* @__PURE__ */ jsxs7("span", { className: "pb-library-block-title", children: [
|
|
1474
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
|
|
1475
|
+
/* @__PURE__ */ jsx9("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx9(DragHandleIcon2, {}) })
|
|
1476
|
+
] }),
|
|
1477
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
|
|
1478
|
+
] }, type);
|
|
1479
|
+
}) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx9(Text3, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs7("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => {
|
|
1404
1480
|
editor.requestCanvasSelection(block.id);
|
|
1405
1481
|
scrollCanvasBlockIntoView(block.id, canvasFrameRef.current);
|
|
1406
1482
|
}, children: [
|
package/package.json
CHANGED
package/src/styles.css
CHANGED
|
@@ -127,6 +127,9 @@ button:focus-visible, [role="button"]:focus-visible { outline: 2px solid var(--b
|
|
|
127
127
|
.pb-document-block-row--library { width: 100%; flex-direction: column; align-items: flex-start; text-align: left; color: var(--builder-text); font: inherit; cursor: grab; }
|
|
128
128
|
.pb-document-block-row--library:active { cursor: grabbing; }
|
|
129
129
|
.pb-document-block-row--library[aria-disabled="true"] { cursor: default; }
|
|
130
|
+
.pb-document-block-row--dragging { opacity: 0.55; }
|
|
131
|
+
.pb-document-block-row--drop-before { box-shadow: inset 0 3px 0 0 var(--builder-accent); }
|
|
132
|
+
.pb-document-block-row--drop-after { box-shadow: inset 0 -3px 0 0 var(--builder-accent); }
|
|
130
133
|
.pb-document-block-row--library[type="button"] { cursor: pointer; }
|
|
131
134
|
.pb-library-block-title { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: var(--builder-space-2); }
|
|
132
135
|
.pb-library-block-drag-hint { display: inline-flex; color: var(--builder-text-muted); pointer-events: none; }
|