@standhigher/puck-page-builder 0.9.0 → 0.11.0
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/README.md +25 -0
- package/dist/{chunk-PUTYZI7B.js → chunk-PFCWXLWN.js} +117 -2
- package/dist/extensions.d.ts +18 -183
- package/dist/extensions.js +15 -3
- package/dist/index.d.ts +213 -9
- package/dist/index.js +519 -142
- package/dist/registry-eNtkEC6o.d.ts +221 -0
- package/dist/renderer.d.ts +1 -1
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.js +15 -3
- package/package.json +1 -1
- package/src/styles.css +32 -11
package/dist/index.js
CHANGED
|
@@ -3,8 +3,14 @@ import {
|
|
|
3
3
|
ExtensionRegistryError,
|
|
4
4
|
TemplateRegistry,
|
|
5
5
|
createExtensionRegistry,
|
|
6
|
-
createTemplateRegistry
|
|
7
|
-
|
|
6
|
+
createTemplateRegistry,
|
|
7
|
+
maximumBlockInstances,
|
|
8
|
+
mergeBlockPolicies,
|
|
9
|
+
minimumBlockInstances,
|
|
10
|
+
validateBlockPolicy,
|
|
11
|
+
validateFieldValue,
|
|
12
|
+
validatePageDocumentWithRegistry
|
|
13
|
+
} from "./chunk-PFCWXLWN.js";
|
|
8
14
|
import {
|
|
9
15
|
WebRenderer
|
|
10
16
|
} from "./chunk-KYXIXIWK.js";
|
|
@@ -341,9 +347,9 @@ function PropertyPanel({
|
|
|
341
347
|
}
|
|
342
348
|
|
|
343
349
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
344
|
-
import { Puck as Puck2, usePuck } from "@puckeditor/core";
|
|
345
|
-
import { Badge as
|
|
346
|
-
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2 } from "@shopify/polaris-icons";
|
|
350
|
+
import { IconButton as IconButton2, Puck as Puck2, usePuck } from "@puckeditor/core";
|
|
351
|
+
import { Badge as Badge3, Banner, Button as Button2, ButtonGroup as ButtonGroup2, InlineStack as InlineStack3, Select as Select2, Text as Text3, TextField } from "@shopify/polaris";
|
|
352
|
+
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2, XIcon as XIcon2 } from "@shopify/polaris-icons";
|
|
347
353
|
import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
|
|
348
354
|
|
|
349
355
|
// src/adapters/puck/canvas-extension-block.tsx
|
|
@@ -462,6 +468,65 @@ function fromEngineData(data, base, registry) {
|
|
|
462
468
|
|
|
463
469
|
// src/editor/context/EditorContext.tsx
|
|
464
470
|
import { createContext, useCallback, useContext, useEffect as useEffect2, useMemo, useReducer, useRef as useRef3, useState as useState2 } from "react";
|
|
471
|
+
|
|
472
|
+
// src/editor/policy.ts
|
|
473
|
+
function operationEnabled(operation, policy, blockPolicy) {
|
|
474
|
+
const key = `allow${operation[0].toUpperCase()}${operation.slice(1)}`;
|
|
475
|
+
return policy?.operations?.[key] !== false && blockPolicy[key] !== false;
|
|
476
|
+
}
|
|
477
|
+
function resolvedBlockPolicy(type, definition, policy) {
|
|
478
|
+
return mergeBlockPolicies(definition?.policy, policy?.blocks?.[type]);
|
|
479
|
+
}
|
|
480
|
+
function blockCount(blocks, type) {
|
|
481
|
+
return blocks.filter((block) => block.type === type).length;
|
|
482
|
+
}
|
|
483
|
+
function canAddBlock(type, blocks, definition, policy) {
|
|
484
|
+
const blockPolicy = resolvedBlockPolicy(type, definition, policy);
|
|
485
|
+
return operationEnabled("add", policy, blockPolicy) && blockCount(blocks, type) < maximumBlockInstances(blockPolicy);
|
|
486
|
+
}
|
|
487
|
+
function canDeleteBlock(block, blocks, definition, policy) {
|
|
488
|
+
if (!block) return false;
|
|
489
|
+
const blockPolicy = resolvedBlockPolicy(block.type, definition, policy);
|
|
490
|
+
return operationEnabled("delete", policy, blockPolicy) && blockCount(blocks, block.type) > minimumBlockInstances(blockPolicy);
|
|
491
|
+
}
|
|
492
|
+
function canDuplicateBlock(block, blocks, definition, policy) {
|
|
493
|
+
if (!block) return false;
|
|
494
|
+
const blockPolicy = resolvedBlockPolicy(block.type, definition, policy);
|
|
495
|
+
return operationEnabled("duplicate", policy, blockPolicy) && canAddBlock(block.type, blocks, definition, policy);
|
|
496
|
+
}
|
|
497
|
+
function canDragBlock(block, definition, policy) {
|
|
498
|
+
if (!block) return false;
|
|
499
|
+
return operationEnabled("drag", policy, resolvedBlockPolicy(block.type, definition, policy));
|
|
500
|
+
}
|
|
501
|
+
function canApplyCanvasDocument(previous, next, getDefinition, policy) {
|
|
502
|
+
const previousById = new Map(previous.blocks.map((block) => [block.id, block]));
|
|
503
|
+
const nextById = new Map(next.blocks.map((block) => [block.id, block]));
|
|
504
|
+
for (const block of next.blocks) {
|
|
505
|
+
const previousBlock = previousById.get(block.id);
|
|
506
|
+
if (previousBlock && previousBlock.type !== block.type) return false;
|
|
507
|
+
}
|
|
508
|
+
for (const block of next.blocks) {
|
|
509
|
+
if (!previousById.has(block.id) && !canAddBlock(block.type, previous.blocks, getDefinition(block.type), policy)) return false;
|
|
510
|
+
}
|
|
511
|
+
for (const block of previous.blocks) {
|
|
512
|
+
if (!nextById.has(block.id) && !canDeleteBlock(block, previous.blocks, getDefinition(block.type), policy)) return false;
|
|
513
|
+
}
|
|
514
|
+
const types = new Set([...previous.blocks, ...next.blocks].map((block) => block.type));
|
|
515
|
+
for (const type of types) {
|
|
516
|
+
const definition = getDefinition(type);
|
|
517
|
+
const blockPolicy = resolvedBlockPolicy(type, definition, policy);
|
|
518
|
+
const nextCount = blockCount(next.blocks, type);
|
|
519
|
+
if (nextCount > maximumBlockInstances(blockPolicy) || nextCount < minimumBlockInstances(blockPolicy)) return false;
|
|
520
|
+
}
|
|
521
|
+
if (previous.blocks.length === next.blocks.length && previous.blocks.every((block) => nextById.has(block.id))) {
|
|
522
|
+
for (let index = 0; index < previous.blocks.length; index += 1) {
|
|
523
|
+
if (previous.blocks[index]?.id !== next.blocks[index]?.id && !canDragBlock(previous.blocks[index], getDefinition(previous.blocks[index].type), policy)) return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/editor/context/EditorContext.tsx
|
|
465
530
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
466
531
|
function historyReducer(state, action) {
|
|
467
532
|
if (action.type === "select") return { ...state, selectedBlockId: action.id };
|
|
@@ -476,7 +541,7 @@ function historyReducer(state, action) {
|
|
|
476
541
|
if (!next) return state;
|
|
477
542
|
return { ...state, ...next, past: [...state.past, { document: state.document, selectedBlockId: state.selectedBlockId }], future: state.future.slice(1) };
|
|
478
543
|
}
|
|
479
|
-
if (action.type === "saved") return { ...state, savedDocument:
|
|
544
|
+
if (action.type === "saved") return { ...state, savedDocument: action.document };
|
|
480
545
|
const current = { document: state.document, selectedBlockId: state.selectedBlockId };
|
|
481
546
|
return { ...state, document: action.document, selectedBlockId: action.selectedBlockId, past: [...state.past, current], future: [] };
|
|
482
547
|
}
|
|
@@ -494,15 +559,17 @@ function defaultBlock(type, blocks, registry) {
|
|
|
494
559
|
if (!definition) throw new Error(`Unknown PageDocument block type: ${type}`);
|
|
495
560
|
return { id: uniqueBlockId(type, blocks), type, version: definition.version, props: definition.defaultProps, variant: definition.defaultVariant ?? "default", style: {} };
|
|
496
561
|
}
|
|
497
|
-
function EditorProvider({ initialDocument, registry, loadState = "ready", leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
|
|
562
|
+
function EditorProvider({ initialDocument, registry, policy, loadState = "ready", sessionState = "active", session, sessionMessage, leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
|
|
498
563
|
const [history, dispatch] = useReducer(historyReducer, initialDocument, (document) => ({ document, selectedBlockId: document.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document }));
|
|
499
564
|
const historyRef = useRef3(history);
|
|
500
565
|
useEffect2(() => {
|
|
501
566
|
historyRef.current = history;
|
|
502
567
|
}, [history]);
|
|
503
568
|
const [canvasSelectionRequest, setCanvasSelectionRequest] = useState2(null);
|
|
504
|
-
const
|
|
569
|
+
const [pendingDeleteBlockId, setPendingDeleteBlockId] = useState2(null);
|
|
570
|
+
const editable = (loadState === "ready" || loadState === "success") && sessionState === "active";
|
|
505
571
|
const selectedBlock = history.document.blocks.find((block) => block.id === history.selectedBlockId) ?? null;
|
|
572
|
+
const pendingDeleteBlock = history.document.blocks.find((block) => block.id === pendingDeleteBlockId) ?? null;
|
|
506
573
|
const replace = useCallback((document, selectedBlockId) => dispatch({ type: "replace", document, selectedBlockId }), []);
|
|
507
574
|
const requestCanvasSelection = useCallback((id) => {
|
|
508
575
|
if (editable) setCanvasSelectionRequest(id);
|
|
@@ -513,15 +580,23 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
513
580
|
}, []);
|
|
514
581
|
const updateFromCanvas = useCallback((document) => {
|
|
515
582
|
const current = historyRef.current;
|
|
516
|
-
if (!editable || JSON.stringify(document) === JSON.stringify(current.document)) return;
|
|
583
|
+
if (!editable || JSON.stringify(document) === JSON.stringify(current.document)) return false;
|
|
584
|
+
if (!canApplyCanvasDocument(current.document, document, (type) => registry?.getBlock(type), policy)) return false;
|
|
517
585
|
replace(document, current.selectedBlockId);
|
|
518
|
-
|
|
586
|
+
return true;
|
|
587
|
+
}, [editable, policy, registry, replace]);
|
|
519
588
|
const updateBlockProps = useCallback((id, props) => {
|
|
520
589
|
if (!editable) return;
|
|
521
590
|
const current = historyRef.current;
|
|
522
591
|
const blocks = current.document.blocks.map((block) => block.id === id ? { ...block, props: { ...block.props, ...props } } : block);
|
|
523
592
|
replace({ ...current.document, blocks }, id);
|
|
524
593
|
}, [editable, replace]);
|
|
594
|
+
const updateBlockPresentation = useCallback((id, presentation) => {
|
|
595
|
+
if (!editable) return;
|
|
596
|
+
const current = historyRef.current;
|
|
597
|
+
const blocks = current.document.blocks.map((block) => block.id === id ? { ...block, ...presentation } : block);
|
|
598
|
+
replace({ ...current.document, blocks }, id);
|
|
599
|
+
}, [editable, replace]);
|
|
525
600
|
useEffect2(() => {
|
|
526
601
|
onDocumentChange?.(history.document);
|
|
527
602
|
}, [history.document, onDocumentChange]);
|
|
@@ -555,27 +630,37 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
555
630
|
const actionState = {
|
|
556
631
|
canUndo: editable && history.past.length > 0,
|
|
557
632
|
canRedo: editable && history.future.length > 0,
|
|
558
|
-
canAdd: editable,
|
|
633
|
+
canAdd: editable && ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []].some((type) => canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy)),
|
|
559
634
|
canEdit: editable && selectedBlock !== null,
|
|
560
|
-
canDelete: editable && selectedBlock
|
|
561
|
-
canDuplicate: editable && selectedBlock
|
|
562
|
-
canReorder: editable && history.document.blocks.length > 1
|
|
635
|
+
canDelete: editable && canDeleteBlock(selectedBlock, history.document.blocks, registry?.getBlock(selectedBlock?.type ?? ""), policy),
|
|
636
|
+
canDuplicate: editable && canDuplicateBlock(selectedBlock, history.document.blocks, registry?.getBlock(selectedBlock?.type ?? ""), policy),
|
|
637
|
+
canReorder: editable && history.document.blocks.length > 1 && canDragBlock(selectedBlock, registry?.getBlock(selectedBlock?.type ?? ""), policy)
|
|
563
638
|
};
|
|
564
639
|
return {
|
|
565
640
|
document: history.document,
|
|
566
641
|
selectedBlockId: history.selectedBlockId,
|
|
567
642
|
selectedBlock,
|
|
643
|
+
pendingDeleteBlock,
|
|
568
644
|
canvasSelectionRequest,
|
|
569
645
|
device: history.device,
|
|
570
646
|
loadState,
|
|
647
|
+
sessionState,
|
|
648
|
+
session,
|
|
649
|
+
sessionMessage,
|
|
650
|
+
isEditable: editable,
|
|
571
651
|
isDirty,
|
|
572
652
|
actionState,
|
|
653
|
+
canAddBlock: (type) => editable && canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy),
|
|
654
|
+
canDragBlock: (id) => {
|
|
655
|
+
const block = history.document.blocks.find((item) => item.id === id);
|
|
656
|
+
return editable && canDragBlock(block, registry?.getBlock(block?.type ?? ""), policy);
|
|
657
|
+
},
|
|
573
658
|
requestCanvasSelection,
|
|
574
659
|
confirmCanvasSelection,
|
|
575
660
|
updateFromCanvas,
|
|
576
661
|
setDevice: (device) => dispatch({ type: "device", device }),
|
|
577
662
|
addBlock: (type, beforeId) => {
|
|
578
|
-
if (!editable) return null;
|
|
663
|
+
if (!editable || !canAddBlock(type, history.document.blocks, registry?.getBlock(type), policy)) return null;
|
|
579
664
|
const block = defaultBlock(type, history.document.blocks, registry);
|
|
580
665
|
const blocks = [...history.document.blocks];
|
|
581
666
|
const targetIndex = beforeId ? blocks.findIndex((item) => item.id === beforeId) : -1;
|
|
@@ -588,7 +673,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
588
673
|
if (!editable) return;
|
|
589
674
|
const index = history.document.blocks.findIndex((block2) => block2.id === id);
|
|
590
675
|
const source = history.document.blocks[index];
|
|
591
|
-
if (!source) return;
|
|
676
|
+
if (!canDuplicateBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
|
|
592
677
|
const block = { ...source, id: uniqueBlockId(source.type, history.document.blocks), props: { ...source.props } };
|
|
593
678
|
const blocks = [...history.document.blocks];
|
|
594
679
|
blocks.splice(index + 1, 0, block);
|
|
@@ -597,7 +682,8 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
597
682
|
deleteBlock: (id) => {
|
|
598
683
|
if (!editable) return;
|
|
599
684
|
const index = history.document.blocks.findIndex((block) => block.id === id);
|
|
600
|
-
|
|
685
|
+
const source = history.document.blocks[index];
|
|
686
|
+
if (!canDeleteBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
|
|
601
687
|
const blocks = history.document.blocks.filter((block) => block.id !== id);
|
|
602
688
|
replace({ ...history.document, blocks }, blocks[index]?.id ?? blocks[index - 1]?.id ?? null);
|
|
603
689
|
},
|
|
@@ -605,7 +691,7 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
605
691
|
if (!editable) return;
|
|
606
692
|
const from = history.document.blocks.findIndex((block) => block.id === id);
|
|
607
693
|
const to = from + direction;
|
|
608
|
-
if (from < 0 || to < 0 || to >= history.document.blocks.length) return;
|
|
694
|
+
if (from < 0 || to < 0 || to >= history.document.blocks.length || !canDragBlock(history.document.blocks[from], registry?.getBlock(history.document.blocks[from]?.type ?? ""), policy)) return;
|
|
609
695
|
const blocks = [...history.document.blocks];
|
|
610
696
|
[blocks[from], blocks[to]] = [blocks[to], blocks[from]];
|
|
611
697
|
replace({ ...history.document, blocks }, id);
|
|
@@ -615,21 +701,38 @@ function EditorProvider({ initialDocument, registry, loadState = "ready", leaveW
|
|
|
615
701
|
const source = history.document.blocks.find((block) => block.id === id);
|
|
616
702
|
const withoutSource = history.document.blocks.filter((block) => block.id !== id);
|
|
617
703
|
const targetIndex = withoutSource.findIndex((block) => block.id === beforeId);
|
|
618
|
-
if (!source || targetIndex < 0) return;
|
|
704
|
+
if (!source || targetIndex < 0 || !canDragBlock(source, registry?.getBlock(source.type), policy)) return;
|
|
619
705
|
const blocks = [...withoutSource];
|
|
620
706
|
blocks.splice(targetIndex, 0, source);
|
|
621
707
|
replace({ ...history.document, blocks }, id);
|
|
622
708
|
},
|
|
623
709
|
updateBlockProps,
|
|
710
|
+
updateBlockPresentation,
|
|
711
|
+
requestDeleteBlock: (id) => {
|
|
712
|
+
const block = history.document.blocks.find((item) => item.id === id);
|
|
713
|
+
if (editable && canDeleteBlock(block, history.document.blocks, registry?.getBlock(block?.type ?? ""), policy)) setPendingDeleteBlockId(id);
|
|
714
|
+
},
|
|
715
|
+
cancelDeleteBlock: () => setPendingDeleteBlockId(null),
|
|
716
|
+
confirmDeleteBlock: () => {
|
|
717
|
+
if (pendingDeleteBlockId) {
|
|
718
|
+
const id = pendingDeleteBlockId;
|
|
719
|
+
setPendingDeleteBlockId(null);
|
|
720
|
+
const index = history.document.blocks.findIndex((block) => block.id === id);
|
|
721
|
+
const source = history.document.blocks[index];
|
|
722
|
+
if (!canDeleteBlock(source, history.document.blocks, registry?.getBlock(source?.type ?? ""), policy)) return;
|
|
723
|
+
const blocks = history.document.blocks.filter((block) => block.id !== id);
|
|
724
|
+
replace({ ...history.document, blocks }, blocks[index]?.id ?? blocks[index - 1]?.id ?? null);
|
|
725
|
+
}
|
|
726
|
+
},
|
|
624
727
|
undo: () => {
|
|
625
728
|
if (editable) dispatch({ type: "undo" });
|
|
626
729
|
},
|
|
627
730
|
redo: () => {
|
|
628
731
|
if (editable) dispatch({ type: "redo" });
|
|
629
732
|
},
|
|
630
|
-
markSaved: () => dispatch({ type: "saved" })
|
|
733
|
+
markSaved: (document = historyRef.current.document) => dispatch({ type: "saved", document })
|
|
631
734
|
};
|
|
632
|
-
}, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, registry, replace, requestCanvasSelection, selectedBlock, updateBlockProps, updateFromCanvas]);
|
|
735
|
+
}, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, pendingDeleteBlock, pendingDeleteBlockId, policy, registry, replace, requestCanvasSelection, selectedBlock, session, sessionMessage, sessionState, updateBlockPresentation, updateBlockProps, updateFromCanvas]);
|
|
633
736
|
return /* @__PURE__ */ jsx5(EditorContext.Provider, { value, children });
|
|
634
737
|
}
|
|
635
738
|
function useEditorContext() {
|
|
@@ -638,6 +741,26 @@ function useEditorContext() {
|
|
|
638
741
|
return context;
|
|
639
742
|
}
|
|
640
743
|
|
|
744
|
+
// src/editor/components/PageStatusCard.tsx
|
|
745
|
+
import { Badge as Badge2, InlineStack as InlineStack2, Text as Text2 } from "@shopify/polaris";
|
|
746
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
747
|
+
function PageStatusCard({ status, sessionState }) {
|
|
748
|
+
const publicationTone = status.publicationStatus === "published" ? "success" : "attention";
|
|
749
|
+
const sessionLabel = sessionState === "locked" ? `${status.editingBy ?? "\u5176\u4ED6\u4EBA"}\u6B63\u5728\u7F16\u8F91` : sessionState === "lost" ? "\u7F16\u8F91\u9501\u5DF2\u5931\u6548" : status.editingBy ? `${status.editingBy}\u6B63\u5728\u7F16\u8F91` : void 0;
|
|
750
|
+
return /* @__PURE__ */ jsxs4("div", { className: "pb-page-status-card", "data-testid": "page-status-card", "data-publication-status": status.publicationStatus, "data-session-state": sessionState, children: [
|
|
751
|
+
/* @__PURE__ */ jsxs4(InlineStack2, { gap: "150", blockAlign: "center", wrap: true, children: [
|
|
752
|
+
/* @__PURE__ */ jsx6(Badge2, { tone: publicationTone, children: status.publicationStatus === "published" ? "\u7EBF\u4E0A" : "\u672A\u53D1\u5E03" }),
|
|
753
|
+
status.draftLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.draftLabel }) : null,
|
|
754
|
+
status.publishedVersionLabel ? /* @__PURE__ */ jsx6(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.publishedVersionLabel }) : null,
|
|
755
|
+
sessionLabel ? /* @__PURE__ */ jsx6(Badge2, { tone: sessionState === "lost" ? "critical" : "attention", children: sessionLabel }) : null
|
|
756
|
+
] }),
|
|
757
|
+
status.lastSavedAt ? /* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
|
|
758
|
+
"\u6700\u540E\u4FDD\u5B58\uFF1A",
|
|
759
|
+
status.lastSavedAt
|
|
760
|
+
] }) : null
|
|
761
|
+
] });
|
|
762
|
+
}
|
|
763
|
+
|
|
641
764
|
// src/editor/i18n/admin.ts
|
|
642
765
|
var messages = {
|
|
643
766
|
"zh-CN": {
|
|
@@ -649,6 +772,10 @@ var messages = {
|
|
|
649
772
|
redo: "\u6062\u590D",
|
|
650
773
|
duplicate: "\u590D\u5236",
|
|
651
774
|
remove: "\u5220\u9664",
|
|
775
|
+
confirmDeleteTitle: "\u786E\u8BA4\u5220\u9664\u533A\u5757\uFF1F",
|
|
776
|
+
confirmDeleteMessage: "\u5220\u9664\u540E\u53EF\u4F7F\u7528\u64A4\u9500\u6062\u590D\u3002",
|
|
777
|
+
cancel: "\u53D6\u6D88",
|
|
778
|
+
confirm: "\u786E\u8BA4\u5220\u9664",
|
|
652
779
|
moveUp: "\u4E0A\u79FB",
|
|
653
780
|
moveDown: "\u4E0B\u79FB",
|
|
654
781
|
loading: "\u6B63\u5728\u52A0\u8F7D\u7F16\u8F91\u5668\u2026",
|
|
@@ -663,13 +790,33 @@ var messages = {
|
|
|
663
790
|
publish: "\u53D1\u5E03",
|
|
664
791
|
publishing: "\u6B63\u5728\u53D1\u5E03\u2026",
|
|
665
792
|
saveFailed: "\u4FDD\u5B58\u8349\u7A3F\u5931\u8D25",
|
|
793
|
+
retrySave: "\u91CD\u8BD5\u4FDD\u5B58",
|
|
794
|
+
offline: "\u5F53\u524D\u79BB\u7EBF\uFF0C\u6062\u590D\u7F51\u7EDC\u540E\u5C06\u81EA\u52A8\u4FDD\u5B58",
|
|
666
795
|
publishFailed: "\u53D1\u5E03\u5931\u8D25",
|
|
667
796
|
published: "\u5DF2\u53D1\u5E03",
|
|
797
|
+
back: "\u8FD4\u56DE",
|
|
798
|
+
confirmLeaveTitle: "\u79BB\u5F00\u7F16\u8F91\u5668\uFF1F",
|
|
799
|
+
leave: "\u79BB\u5F00",
|
|
800
|
+
zoom: "\u7F29\u653E",
|
|
801
|
+
zoomAuto: "\u81EA\u52A8\u7F29\u653E",
|
|
802
|
+
preview: "\u6B63\u5F0F\u9884\u89C8",
|
|
803
|
+
addToStore: "\u6DFB\u52A0\u5230\u5E97\u94FA",
|
|
804
|
+
acquiringLock: "\u6B63\u5728\u83B7\u53D6\u7F16\u8F91\u9501\u2026",
|
|
805
|
+
locked: "\u8BE5\u9875\u9762\u6B63\u5728\u88AB\u7F16\u8F91",
|
|
806
|
+
lockLost: "\u7F16\u8F91\u9501\u5DF2\u5931\u6548",
|
|
807
|
+
readonly: "\u9875\u9762\u4E3A\u53EA\u8BFB\u72B6\u6001",
|
|
668
808
|
leaveWarning: "\u4F60\u6709\u672A\u4FDD\u5B58\u7684\u66F4\u6539\u3002\u786E\u5B9A\u8981\u79BB\u5F00\u5417\uFF1F",
|
|
669
809
|
selectBlock: "\u9009\u62E9\u4E00\u4E2A\u533A\u5757\u4EE5\u7F16\u8F91\u3002",
|
|
670
810
|
desktop: "\u684C\u9762",
|
|
671
811
|
tablet: "\u5E73\u677F",
|
|
672
|
-
mobile: "\u624B\u673A"
|
|
812
|
+
mobile: "\u624B\u673A",
|
|
813
|
+
collapseLeft: "\u6536\u8D77\u5DE6\u4FA7\u9762\u677F",
|
|
814
|
+
collapseRight: "\u6536\u8D77\u5C5E\u6027\u9762\u677F",
|
|
815
|
+
expandLeft: "\u6253\u5F00\u533A\u5757\u9762\u677F",
|
|
816
|
+
expandRight: "\u6253\u5F00\u5C5E\u6027\u9762\u677F",
|
|
817
|
+
toggleSidebars: "\u4FA7\u680F\u663E\u793A",
|
|
818
|
+
toggleLeftSidebar: "\u5207\u6362\u5DE6\u4FA7\u9762\u677F",
|
|
819
|
+
toggleRightSidebar: "\u5207\u6362\u5C5E\u6027\u9762\u677F"
|
|
673
820
|
},
|
|
674
821
|
en: {
|
|
675
822
|
blocks: "Blocks",
|
|
@@ -680,6 +827,10 @@ var messages = {
|
|
|
680
827
|
redo: "Redo",
|
|
681
828
|
duplicate: "Duplicate",
|
|
682
829
|
remove: "Delete",
|
|
830
|
+
confirmDeleteTitle: "Delete this block?",
|
|
831
|
+
confirmDeleteMessage: "You can restore it with Undo after deleting.",
|
|
832
|
+
cancel: "Cancel",
|
|
833
|
+
confirm: "Delete block",
|
|
683
834
|
moveUp: "Move up",
|
|
684
835
|
moveDown: "Move down",
|
|
685
836
|
loading: "Loading editor\u2026",
|
|
@@ -694,13 +845,33 @@ var messages = {
|
|
|
694
845
|
publish: "Publish",
|
|
695
846
|
publishing: "Publishing\u2026",
|
|
696
847
|
saveFailed: "Could not save draft",
|
|
848
|
+
retrySave: "Retry save",
|
|
849
|
+
offline: "Offline \u2014 changes will save when connection returns",
|
|
697
850
|
publishFailed: "Could not publish",
|
|
698
851
|
published: "Published",
|
|
852
|
+
back: "Back",
|
|
853
|
+
confirmLeaveTitle: "Leave the editor?",
|
|
854
|
+
leave: "Leave",
|
|
855
|
+
zoom: "Zoom",
|
|
856
|
+
zoomAuto: "Auto zoom",
|
|
857
|
+
preview: "Preview",
|
|
858
|
+
addToStore: "Add to store",
|
|
859
|
+
acquiringLock: "Acquiring edit lock\u2026",
|
|
860
|
+
locked: "This page is being edited",
|
|
861
|
+
lockLost: "The edit lock has expired",
|
|
862
|
+
readonly: "This page is read-only",
|
|
699
863
|
leaveWarning: "You have unsaved changes. Are you sure you want to leave?",
|
|
700
864
|
selectBlock: "Select a block to edit it.",
|
|
701
865
|
desktop: "Desktop",
|
|
702
866
|
tablet: "Tablet",
|
|
703
|
-
mobile: "Mobile"
|
|
867
|
+
mobile: "Mobile",
|
|
868
|
+
collapseLeft: "Collapse blocks panel",
|
|
869
|
+
collapseRight: "Collapse properties panel",
|
|
870
|
+
expandLeft: "Open blocks panel",
|
|
871
|
+
expandRight: "Open properties panel",
|
|
872
|
+
toggleSidebars: "Sidebar visibility",
|
|
873
|
+
toggleLeftSidebar: "Toggle left sidebar",
|
|
874
|
+
toggleRightSidebar: "Toggle right sidebar"
|
|
704
875
|
}
|
|
705
876
|
};
|
|
706
877
|
function createAdminI18n(locale) {
|
|
@@ -718,7 +889,7 @@ function blockIdAtRelativeY(ids, relativeY) {
|
|
|
718
889
|
}
|
|
719
890
|
|
|
720
891
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
721
|
-
import { jsx as
|
|
892
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
722
893
|
var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
|
|
723
894
|
function blockLabel(block, registry) {
|
|
724
895
|
return blockTypeLabel(block.type, registry);
|
|
@@ -728,40 +899,151 @@ function blockTypeLabel(type, registry) {
|
|
|
728
899
|
if (type === "core.image") return "\u56FE\u7247";
|
|
729
900
|
return registry?.getBlock(type)?.label ?? type;
|
|
730
901
|
}
|
|
902
|
+
function PuckPanelIcon({ side }) {
|
|
903
|
+
return /* @__PURE__ */ jsxs5("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", children: [
|
|
904
|
+
/* @__PURE__ */ jsx7("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
905
|
+
/* @__PURE__ */ jsx7("path", { d: side === "left" ? "M9 3v18" : "M15 3v18" })
|
|
906
|
+
] });
|
|
907
|
+
}
|
|
731
908
|
function validateDocumentBlocks(document, registry) {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
909
|
+
return registry ? validatePageDocumentWithRegistry(document, registry) : [];
|
|
910
|
+
}
|
|
911
|
+
function useEditorSession(adapter, pageId) {
|
|
912
|
+
const [managed, setManaged] = useState3(() => adapter ? { state: "acquiring" } : { state: "active" });
|
|
913
|
+
useEffect3(() => {
|
|
914
|
+
let cancelled = false;
|
|
915
|
+
let session;
|
|
916
|
+
let heartbeatTimer;
|
|
917
|
+
const stopHeartbeat = () => {
|
|
918
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
919
|
+
heartbeatTimer = void 0;
|
|
920
|
+
};
|
|
921
|
+
if (!adapter) return;
|
|
922
|
+
void adapter.acquire({ pageId }).then((result) => {
|
|
923
|
+
if (cancelled) return;
|
|
924
|
+
if (result.state !== "active") {
|
|
925
|
+
setManaged({ state: result.state, message: result.message ?? (result.state === "locked" && result.editorName ? `${result.editorName} \u6B63\u5728\u7F16\u8F91` : void 0) });
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
session = result.session;
|
|
929
|
+
setManaged({ state: "active", session });
|
|
930
|
+
if (!adapter.heartbeat) return;
|
|
931
|
+
const heartbeat = () => {
|
|
932
|
+
if (typeof navigator !== "undefined" && !navigator.onLine) return;
|
|
933
|
+
void adapter.heartbeat?.({ pageId, session: result.session }).catch(() => {
|
|
934
|
+
if (!cancelled) {
|
|
935
|
+
stopHeartbeat();
|
|
936
|
+
setManaged({ state: "lost", message: "\u7F16\u8F91\u9501\u5DF2\u5931\u6548\uFF0C\u8BF7\u6062\u590D\u7F51\u7EDC\u540E\u91CD\u65B0\u8FDB\u5165\u7F16\u8F91\u5668\u3002" });
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
};
|
|
940
|
+
heartbeatTimer = setInterval(heartbeat, adapter.heartbeatIntervalMs ?? 3e4);
|
|
941
|
+
}).catch(() => {
|
|
942
|
+
if (!cancelled) setManaged({ state: "lost", message: "\u65E0\u6CD5\u83B7\u53D6\u7F16\u8F91\u9501\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
|
|
943
|
+
});
|
|
944
|
+
return () => {
|
|
945
|
+
cancelled = true;
|
|
946
|
+
stopHeartbeat();
|
|
947
|
+
if (session) void adapter.release?.({ pageId, session });
|
|
948
|
+
};
|
|
949
|
+
}, [adapter, pageId]);
|
|
950
|
+
return managed;
|
|
742
951
|
}
|
|
743
952
|
function PageDocumentEditorShell(props) {
|
|
744
|
-
|
|
953
|
+
const { sessionAdapter, initialDocument, onSessionStateChange } = props;
|
|
954
|
+
const managedSession = useEditorSession(sessionAdapter, initialDocument.pageId);
|
|
955
|
+
useEffect3(() => {
|
|
956
|
+
onSessionStateChange?.(managedSession.state);
|
|
957
|
+
}, [managedSession.state, onSessionStateChange]);
|
|
958
|
+
return /* @__PURE__ */ jsx7(EditorProvider, { initialDocument: props.initialDocument, registry: props.registry, policy: props.policy, loadState: props.loadState, sessionState: managedSession.state, session: managedSession.session, sessionMessage: managedSession.message, leaveWarning: createAdminI18n(props.adminLocale).t("leaveWarning"), onDocumentChange: props.onDocumentChange, children: /* @__PURE__ */ jsx7(PageDocumentEditor, { ...props }) });
|
|
745
959
|
}
|
|
746
|
-
function PageDocumentEditor({ iframe =
|
|
960
|
+
function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, pageStatus, onBack, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
747
961
|
const editor = useEditorContext();
|
|
748
962
|
const [blockView, setBlockView] = useState3("blocks");
|
|
963
|
+
const [leftRailOpen, setLeftRailOpen] = useState3(true);
|
|
964
|
+
const [rightPanelOpen, setRightPanelOpen] = useState3(true);
|
|
749
965
|
const [draggingLibraryType, setDraggingLibraryType] = useState3(null);
|
|
750
966
|
const canvasFrameRef = useRef4(null);
|
|
751
967
|
const [canvasMutationVersion, setCanvasMutationVersion] = useState3(0);
|
|
968
|
+
const [canvasResetVersion, setCanvasResetVersion] = useState3(0);
|
|
752
969
|
const [request, setRequest] = useState3("idle");
|
|
753
970
|
const [notice, setNotice] = useState3(null);
|
|
971
|
+
const [draftRevision, setDraftRevision] = useState3(initialDraftRevision);
|
|
972
|
+
const [isOnline, setIsOnline] = useState3(() => typeof navigator === "undefined" || navigator.onLine);
|
|
973
|
+
const [saveState, setSaveState] = useState3("saved");
|
|
974
|
+
const [zoom, setZoom] = useState3("auto");
|
|
975
|
+
const [confirmBack, setConfirmBack] = useState3(false);
|
|
754
976
|
const i18n = createAdminI18n(adminLocale);
|
|
755
977
|
const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
|
|
756
978
|
const validationIssues = useMemo2(() => validateDocumentBlocks(editor.document, registry), [editor.document, registry]);
|
|
757
|
-
const { confirmCanvasSelection, selectedBlockId, updateBlockProps } = editor;
|
|
979
|
+
const { confirmCanvasSelection, selectedBlockId, updateBlockProps, updateBlockPresentation } = editor;
|
|
758
980
|
const updateFromCanvasInput = useCallback2((id, props, preserveCanvasValue = false) => {
|
|
759
981
|
if (preserveCanvasValue) setCanvasMutationVersion((version) => version + 1);
|
|
760
982
|
updateBlockProps(id, props);
|
|
761
983
|
}, [updateBlockProps]);
|
|
762
984
|
const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry), [confirmCanvasSelection, registry, selectedBlockId, updateFromCanvasInput]);
|
|
763
|
-
|
|
764
|
-
|
|
985
|
+
useEffect3(() => {
|
|
986
|
+
const online = () => setIsOnline(true);
|
|
987
|
+
const offline = () => setIsOnline(false);
|
|
988
|
+
window.addEventListener("online", online);
|
|
989
|
+
window.addEventListener("offline", offline);
|
|
990
|
+
return () => {
|
|
991
|
+
window.removeEventListener("online", online);
|
|
992
|
+
window.removeEventListener("offline", offline);
|
|
993
|
+
};
|
|
994
|
+
}, []);
|
|
995
|
+
const resolvedSaveState = !isOnline && editor.isDirty ? "offline" : saveState === "offline" && editor.isDirty ? "dirty" : saveState === "saved" && editor.isDirty ? "dirty" : saveState;
|
|
996
|
+
const canPersistDraft = Boolean(draftPersistence || onSave);
|
|
997
|
+
const save = useCallback2(async () => {
|
|
998
|
+
if (!canPersistDraft || request !== "idle" || validationIssues.length > 0) return;
|
|
999
|
+
if (!isOnline) {
|
|
1000
|
+
setSaveState("offline");
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
const document = editor.document;
|
|
1004
|
+
setRequest("saving");
|
|
1005
|
+
setSaveState("saving");
|
|
1006
|
+
setNotice(null);
|
|
1007
|
+
try {
|
|
1008
|
+
const result = draftPersistence ? await draftPersistence.saveDraft({ document, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document);
|
|
1009
|
+
if (result?.revision !== void 0) setDraftRevision(result.revision);
|
|
1010
|
+
editor.markSaved(document);
|
|
1011
|
+
setSaveState("saved");
|
|
1012
|
+
} catch {
|
|
1013
|
+
setNotice("saveFailed");
|
|
1014
|
+
setSaveState(typeof navigator !== "undefined" && !navigator.onLine ? "offline" : "failed");
|
|
1015
|
+
} finally {
|
|
1016
|
+
setRequest("idle");
|
|
1017
|
+
}
|
|
1018
|
+
}, [canPersistDraft, draftPersistence, draftRevision, editor, isOnline, onSave, request, validationIssues.length]);
|
|
1019
|
+
useEffect3(() => {
|
|
1020
|
+
if (!autoSave || !canPersistDraft || !editor.isDirty || resolvedSaveState !== "dirty" || !isOnline || request !== "idle" || validationIssues.length > 0 || editor.sessionState !== "active") return;
|
|
1021
|
+
const timer = window.setTimeout(() => {
|
|
1022
|
+
void save();
|
|
1023
|
+
}, autoSaveDelayMs);
|
|
1024
|
+
return () => window.clearTimeout(timer);
|
|
1025
|
+
}, [autoSave, autoSaveDelayMs, canPersistDraft, editor.isDirty, editor.sessionState, isOnline, request, resolvedSaveState, save, validationIssues.length]);
|
|
1026
|
+
const publish = async () => {
|
|
1027
|
+
if (!publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline) return;
|
|
1028
|
+
const document = editor.document;
|
|
1029
|
+
setRequest("publishing");
|
|
1030
|
+
setNotice(null);
|
|
1031
|
+
try {
|
|
1032
|
+
if (publishAction) await publishAction.publish({ document, expectedRevision: draftRevision, session: editor.session, validationIssues });
|
|
1033
|
+
else await onPublish?.(document);
|
|
1034
|
+
editor.markSaved(document);
|
|
1035
|
+
setSaveState("saved");
|
|
1036
|
+
setNotice("published");
|
|
1037
|
+
} catch {
|
|
1038
|
+
setNotice("publishFailed");
|
|
1039
|
+
} finally {
|
|
1040
|
+
setRequest("idle");
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx7(EditorStatus, { state: editor.loadState });
|
|
1044
|
+
if (editor.sessionState !== "active") return /* @__PURE__ */ jsx7(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
|
|
1045
|
+
const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
|
|
1046
|
+
const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
|
|
765
1047
|
const addFromLibrary = (type, beforeId) => {
|
|
766
1048
|
const id = editor.addBlock(type, beforeId);
|
|
767
1049
|
if (id) editor.requestCanvasSelection(id);
|
|
@@ -797,120 +1079,147 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
|
|
|
797
1079
|
event.stopPropagation();
|
|
798
1080
|
setDraggingLibraryType(null);
|
|
799
1081
|
};
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
} catch {
|
|
808
|
-
setNotice("saveFailed");
|
|
809
|
-
} finally {
|
|
810
|
-
setRequest("idle");
|
|
811
|
-
}
|
|
1082
|
+
const saveBadgeTone = resolvedSaveState === "failed" ? "critical" : resolvedSaveState === "offline" || resolvedSaveState === "dirty" ? "attention" : "success";
|
|
1083
|
+
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");
|
|
1084
|
+
const saveButtonLabel = request === "saving" ? i18n.t("saving") : resolvedSaveState === "failed" ? i18n.t("retrySave") : i18n.t("save");
|
|
1085
|
+
const requestBack = () => {
|
|
1086
|
+
if (!onBack) return;
|
|
1087
|
+
if (editor.isDirty || request === "saving") setConfirmBack(true);
|
|
1088
|
+
else onBack();
|
|
812
1089
|
};
|
|
813
|
-
|
|
814
|
-
if (!
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
"PageDocument V",
|
|
835
|
-
editor.document.schemaVersion,
|
|
836
|
-
" \xB7 ",
|
|
837
|
-
editor.document.target
|
|
1090
|
+
return /* @__PURE__ */ jsx7(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => {
|
|
1091
|
+
if (!editor.updateFromCanvas(fromEngineData(data, editor.document, registry))) setCanvasResetVersion((version) => version + 1);
|
|
1092
|
+
}, children: /* @__PURE__ */ jsxs5(Puck2.Layout, { children: [
|
|
1093
|
+
/* @__PURE__ */ jsx7(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion }),
|
|
1094
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-shell pb-shell--v04", "data-testid": "page-document-editor", "data-page-id": editor.document.pageId, "data-dirty": editor.isDirty, "data-editor-state": editor.loadState, "data-editor-session-state": editor.sessionState, "data-save-state": resolvedSaveState, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
|
|
1095
|
+
/* @__PURE__ */ jsx7("header", { className: "pb-header", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-content", children: [
|
|
1096
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-title-group", children: [
|
|
1097
|
+
onBack ? /* @__PURE__ */ jsx7(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
|
|
1098
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-page-title", children: [
|
|
1099
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
|
|
1100
|
+
/* @__PURE__ */ jsxs5(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: [
|
|
1101
|
+
"PageDocument V",
|
|
1102
|
+
editor.document.schemaVersion,
|
|
1103
|
+
" \xB7 ",
|
|
1104
|
+
editor.document.target
|
|
1105
|
+
] }),
|
|
1106
|
+
pageStatus ? /* @__PURE__ */ jsx7(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
|
|
1107
|
+
] }),
|
|
1108
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-sidebar-toggles", role: "group", "aria-label": i18n.t("toggleSidebars"), "data-testid": "sidebar-toggles", children: [
|
|
1109
|
+
/* @__PURE__ */ jsx7(IconButton2, { type: "button", title: i18n.t("toggleLeftSidebar"), onClick: () => setLeftRailOpen((open) => !open), children: /* @__PURE__ */ jsx7(PuckPanelIcon, { side: "left" }) }),
|
|
1110
|
+
/* @__PURE__ */ jsx7(IconButton2, { type: "button", title: i18n.t("toggleRightSidebar"), onClick: () => setRightPanelOpen((open) => !open), children: /* @__PURE__ */ jsx7(PuckPanelIcon, { side: "right" }) })
|
|
838
1111
|
] })
|
|
839
1112
|
] }),
|
|
840
|
-
/* @__PURE__ */
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
/* @__PURE__ */
|
|
846
|
-
|
|
1113
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx7(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx7(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }),
|
|
1115
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-zoom-control", children: /* @__PURE__ */ jsx7(Select2, { label: i18n.t("zoom"), labelHidden: true, options: [{ label: i18n.t("zoomAuto"), value: "auto" }, { label: "50%", value: "50" }, { label: "70%", value: "70" }, { label: "100%", value: "100" }], value: zoom, onChange: (value) => setZoom(value) }) })
|
|
1116
|
+
] }),
|
|
1117
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: false, children: [
|
|
1118
|
+
/* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
|
|
1119
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canPersistDraft || !editor.isDirty || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void save(), children: saveButtonLabel }),
|
|
1120
|
+
onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
|
|
1121
|
+
onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
|
|
1122
|
+
/* @__PURE__ */ jsx7(Button2, { variant: "primary", disabled: !publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") }),
|
|
1123
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
|
|
1124
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
|
|
1125
|
+
] }) })
|
|
847
1126
|
] }) }),
|
|
848
|
-
editor.loadState === "success" ? /* @__PURE__ */
|
|
849
|
-
notice ? /* @__PURE__ */
|
|
850
|
-
validationIssues.length > 0 ? /* @__PURE__ */
|
|
1127
|
+
editor.loadState === "success" ? /* @__PURE__ */ jsx7(Banner, { tone: "success", children: i18n.t("success") }) : null,
|
|
1128
|
+
notice ? /* @__PURE__ */ jsx7(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
|
|
1129
|
+
validationIssues.length > 0 ? /* @__PURE__ */ jsx7(Banner, { tone: "critical", title: "\u533A\u5757\u5C5E\u6027\u672A\u901A\u8FC7\u6821\u9A8C", children: /* @__PURE__ */ jsx7("ul", { children: validationIssues.map((issue) => /* @__PURE__ */ jsxs5("li", { children: [
|
|
851
1130
|
issue.path,
|
|
852
1131
|
": ",
|
|
853
1132
|
issue.message
|
|
854
1133
|
] }, `${issue.path}-${issue.message}`)) }) }) : null,
|
|
855
|
-
/* @__PURE__ */
|
|
856
|
-
/* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
|
|
1134
|
+
/* @__PURE__ */ jsxs5("div", { className: `pb-workspace pb-workspace--document${leftRailOpen ? "" : " pb-workspace--left-closed"}${rightPanelOpen ? "" : " pb-workspace--right-closed"}`, "data-left-panel": leftRailOpen ? "open" : "closed", "data-right-panel": rightPanelOpen ? "open" : "closed", children: [
|
|
1135
|
+
/* @__PURE__ */ jsxs5("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
|
|
1136
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks" && leftRailOpen, variant: "tertiary", onClick: () => {
|
|
1137
|
+
setBlockView("blocks");
|
|
1138
|
+
setLeftRailOpen(true);
|
|
1139
|
+
} }),
|
|
1140
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline" && leftRailOpen, variant: "tertiary", onClick: () => {
|
|
1141
|
+
setBlockView("outline");
|
|
1142
|
+
setLeftRailOpen(true);
|
|
1143
|
+
} })
|
|
859
1144
|
] }),
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
|
|
1145
|
+
/* @__PURE__ */ jsxs5("aside", { className: `pb-left-panel${leftRailOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u533A\u5757", children: [
|
|
1146
|
+
/* @__PURE__ */ jsxs5(InlineStack3, { align: "space-between", blockAlign: "center", children: [
|
|
1147
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }),
|
|
1148
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseLeft"), icon: XIcon2, variant: "tertiary", onClick: () => setLeftRailOpen(false) })
|
|
1149
|
+
] }),
|
|
1150
|
+
blockView === "blocks" ? /* @__PURE__ */ jsx7("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs5("div", { className: `pb-document-block-row pb-document-block-row--library ${editor.selectedBlock?.type === type ? "pb-document-block-row--selected" : ""}`, "data-block-type": type, "data-selected": editor.selectedBlock?.type === type, role: "listitem", draggable: editor.canAddBlock(type), "aria-disabled": !editor.canAddBlock(type), "aria-label": `${blockTypeLabel(type, registry)}\uFF0C\u62D6\u62FD\u81F3\u753B\u5E03\u4EE5\u6DFB\u52A0${editor.selectedBlock?.type === type ? "\uFF0C\u5F53\u524D\u9009\u4E2D\u7C7B\u578B" : ""}`, onDragStart: (event) => {
|
|
1151
|
+
if (!editor.canAddBlock(type)) {
|
|
1152
|
+
event.preventDefault();
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
863
1155
|
event.dataTransfer.setData("application/x-page-document-block", type);
|
|
864
1156
|
event.dataTransfer.effectAllowed = "copy";
|
|
865
1157
|
setDraggingLibraryType(type);
|
|
866
1158
|
}, onDragEnd: () => setDraggingLibraryType(null), children: [
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
869
|
-
/* @__PURE__ */
|
|
1159
|
+
/* @__PURE__ */ jsxs5("span", { className: "pb-library-block-title", children: [
|
|
1160
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
|
|
1161
|
+
/* @__PURE__ */ jsx7("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(DragHandleIcon2, {}) })
|
|
870
1162
|
] }),
|
|
871
|
-
/* @__PURE__ */
|
|
872
|
-
] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
/* @__PURE__ */
|
|
1163
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
|
|
1164
|
+
] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx7("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs5("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: () => editor.requestCanvasSelection(block.id), children: [
|
|
1165
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
|
|
1166
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
|
|
875
1167
|
] }, block.id)) })
|
|
876
1168
|
] }),
|
|
877
|
-
/* @__PURE__ */
|
|
878
|
-
/* @__PURE__ */
|
|
879
|
-
|
|
880
|
-
/* @__PURE__ */
|
|
881
|
-
draggingLibraryType ? /* @__PURE__ */ jsxs4("div", { className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": "\u533A\u5757\u6295\u653E\u533A", onDragOver: (event) => event.preventDefault(), onDrop: dropFromLibrary, children: [
|
|
1169
|
+
/* @__PURE__ */ jsxs5("main", { className: "pb-canvas-area", children: [
|
|
1170
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-canvas-stage", children: [
|
|
1171
|
+
/* @__PURE__ */ jsx7("div", { ref: canvasFrameRef, className: `pb-canvas-frame pb-canvas-frame--${editor.device}${zoom === "auto" ? "" : ` pb-canvas-frame--zoom-${zoom}`}`, "data-device": editor.device, "data-zoom": zoom, children: /* @__PURE__ */ jsx7(Puck2.Preview, {}) }),
|
|
1172
|
+
draggingLibraryType ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": "\u533A\u5757\u6295\u653E\u533A", onDragOver: (event) => event.preventDefault(), onDrop: dropFromLibrary, children: [
|
|
882
1173
|
"\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
|
|
883
1174
|
blockTypeLabel(draggingLibraryType, registry)
|
|
884
1175
|
] }) : null,
|
|
885
|
-
editor.selectedBlock ? /* @__PURE__ */
|
|
886
|
-
/* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
1176
|
+
editor.selectedBlock ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
|
|
1177
|
+
/* @__PURE__ */ jsx7("span", { children: blockLabel(editor.selectedBlock, registry) }),
|
|
1178
|
+
/* @__PURE__ */ jsx7("span", { children: "Selected" })
|
|
888
1179
|
] }) : null
|
|
889
|
-
] })
|
|
1180
|
+
] }),
|
|
1181
|
+
!leftRailOpen || !rightPanelOpen ? /* @__PURE__ */ jsxs5("div", { className: "pb-collapsed-actions", children: [
|
|
1182
|
+
!leftRailOpen ? /* @__PURE__ */ jsx7(Button2, { onClick: () => setLeftRailOpen(true), children: i18n.t("expandLeft") }) : null,
|
|
1183
|
+
!rightPanelOpen ? /* @__PURE__ */ jsx7(Button2, { onClick: () => setRightPanelOpen(true), children: i18n.t("expandRight") }) : null
|
|
1184
|
+
] }) : null
|
|
890
1185
|
] }),
|
|
891
|
-
/* @__PURE__ */
|
|
892
|
-
/* @__PURE__ */
|
|
893
|
-
|
|
1186
|
+
/* @__PURE__ */ jsxs5("aside", { className: `pb-right-panel${rightPanelOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u5C5E\u6027", children: [
|
|
1187
|
+
/* @__PURE__ */ jsxs5(InlineStack3, { align: "space-between", blockAlign: "center", children: [
|
|
1188
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
|
|
1189
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
|
|
1190
|
+
] }),
|
|
1191
|
+
editor.selectedBlock ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx7(DocumentInspector, { pageId: editor.document.pageId, assetPicker, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }),
|
|
1193
|
+
/* @__PURE__ */ jsx7(InspectorActions, { block: editor.selectedBlock, canDuplicate: editor.actionState.canDuplicate, canDelete: editor.actionState.canDelete, canMove: editor.actionState.canReorder, canMoveUp: editor.document.blocks[0]?.id !== editor.selectedBlock.id, canMoveDown: editor.document.blocks.at(-1)?.id !== editor.selectedBlock.id, i18n, onDuplicate: () => editor.duplicateBlock(editor.selectedBlock.id), onDelete: () => editor.requestDeleteBlock(editor.selectedBlock.id), onMove: (direction) => editor.moveBlock(editor.selectedBlock.id, direction) })
|
|
1194
|
+
] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
|
|
894
1195
|
] })
|
|
895
|
-
] })
|
|
1196
|
+
] }),
|
|
1197
|
+
editor.pendingDeleteBlock ? /* @__PURE__ */ jsx7(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
|
|
1198
|
+
confirmBack ? /* @__PURE__ */ jsx7(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
|
|
1199
|
+
setConfirmBack(false);
|
|
1200
|
+
onBack?.();
|
|
1201
|
+
} }) : null
|
|
896
1202
|
] })
|
|
897
1203
|
] }) });
|
|
898
1204
|
}
|
|
899
|
-
function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion }) {
|
|
1205
|
+
function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion, canvasResetVersion }) {
|
|
900
1206
|
const puck = usePuck();
|
|
901
1207
|
const lastSelectedId = useRef4(null);
|
|
902
1208
|
const lastSyncedData = useRef4(null);
|
|
903
1209
|
const lastCanvasMutationVersion = useRef4(0);
|
|
1210
|
+
const lastCanvasResetVersion = useRef4(0);
|
|
904
1211
|
const serializedData = JSON.stringify(data);
|
|
905
1212
|
useEffect3(() => {
|
|
906
|
-
|
|
1213
|
+
const forceReset = canvasResetVersion > lastCanvasResetVersion.current;
|
|
1214
|
+
if (lastSyncedData.current === serializedData && !forceReset) return;
|
|
907
1215
|
lastSyncedData.current = serializedData;
|
|
1216
|
+
if (forceReset) lastCanvasResetVersion.current = canvasResetVersion;
|
|
908
1217
|
if (canvasMutationVersion > lastCanvasMutationVersion.current) {
|
|
909
1218
|
lastCanvasMutationVersion.current = canvasMutationVersion;
|
|
910
1219
|
return;
|
|
911
1220
|
}
|
|
912
1221
|
puck.dispatch({ type: "setData", data });
|
|
913
|
-
}, [canvasMutationVersion, data, puck, serializedData]);
|
|
1222
|
+
}, [canvasMutationVersion, canvasResetVersion, data, puck, serializedData]);
|
|
914
1223
|
useEffect3(() => {
|
|
915
1224
|
if (!requestedBlockId) return;
|
|
916
1225
|
const selector = puck.getSelectorForId(requestedBlockId);
|
|
@@ -929,39 +1238,79 @@ function EditorStatus({ state }) {
|
|
|
929
1238
|
const i18n = createAdminI18n();
|
|
930
1239
|
const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
|
|
931
1240
|
const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
|
|
932
|
-
return /* @__PURE__ */
|
|
1241
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx7(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
|
|
1242
|
+
}
|
|
1243
|
+
function EditorSessionStatus({ state, message }) {
|
|
1244
|
+
const i18n = createAdminI18n();
|
|
1245
|
+
const tone = state === "locked" ? "warning" : state === "readonly" ? "info" : "critical";
|
|
1246
|
+
const title = state === "acquiring" ? i18n.t("acquiringLock") : state === "locked" ? i18n.t("locked") : state === "readonly" ? i18n.t("readonly") : i18n.t("lockLost");
|
|
1247
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-editor-status", "data-testid": "page-document-editor-session-state", "data-editor-session-state": state, children: /* @__PURE__ */ jsx7(Banner, { tone, title, children: message ?? title }) });
|
|
933
1248
|
}
|
|
934
1249
|
function InspectorSection({ title, children, defaultOpen = true }) {
|
|
935
|
-
return /* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
1250
|
+
return /* @__PURE__ */ jsxs5("details", { className: "pb-inspector-section", open: defaultOpen, children: [
|
|
1251
|
+
/* @__PURE__ */ jsxs5("summary", { children: [
|
|
1252
|
+
/* @__PURE__ */ jsx7("span", { children: title }),
|
|
1253
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2304" })
|
|
939
1254
|
] }),
|
|
940
|
-
/* @__PURE__ */
|
|
1255
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-inspector-section__body", children })
|
|
941
1256
|
] });
|
|
942
1257
|
}
|
|
943
1258
|
function InspectorTextControl({ label, value, control, disabled, onChange }) {
|
|
944
1259
|
const stringValue = typeof value === "string" ? value : "";
|
|
945
|
-
return /* @__PURE__ */
|
|
1260
|
+
if (control === "color") return /* @__PURE__ */ jsx7("input", { "aria-label": label, type: "color", value: /^#[\da-f]{6}$/i.test(stringValue) ? stringValue : "#000000", disabled, onChange: (event) => onChange(event.currentTarget.value) });
|
|
1261
|
+
return /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
|
|
946
1262
|
}
|
|
947
1263
|
function InspectorField({ name, field, value, registry, disabled, onChange }) {
|
|
948
1264
|
const label = field.label ?? name;
|
|
1265
|
+
const issues = validateFieldValue(field, value);
|
|
949
1266
|
const Field = registry?.getField(field.field)?.component;
|
|
950
|
-
return /* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
field.description ? /* @__PURE__ */
|
|
1267
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
|
|
1268
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field__heading", children: [
|
|
1269
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
|
|
1270
|
+
field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
|
|
954
1271
|
] }),
|
|
955
|
-
field.control ? /* @__PURE__ */
|
|
1272
|
+
field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
|
|
1273
|
+
issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
|
|
956
1274
|
] });
|
|
957
1275
|
}
|
|
1276
|
+
function InspectorActions({ block, canDuplicate, canDelete, canMove, canMoveUp, canMoveDown, i18n, onDuplicate, onDelete, onMove }) {
|
|
1277
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1278
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
|
|
1279
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
|
|
1280
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canDuplicate, onClick: onDuplicate, children: i18n.t("duplicate") }),
|
|
1281
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
|
|
1282
|
+
] }) });
|
|
1283
|
+
}
|
|
1284
|
+
function DeleteConfirmation({ block, config, i18n, onCancel, onConfirm }) {
|
|
1285
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs5("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-delete-confirmation-title", children: [
|
|
1286
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
|
|
1287
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
|
|
1288
|
+
/* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1289
|
+
/* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
|
|
1290
|
+
/* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
|
|
1291
|
+
] })
|
|
1292
|
+
] }) });
|
|
1293
|
+
}
|
|
1294
|
+
function LeaveConfirmation({ i18n, onCancel, onConfirm }) {
|
|
1295
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs5("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-leave-confirmation-title", children: [
|
|
1296
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
|
|
1297
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
|
|
1298
|
+
/* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1299
|
+
/* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
|
|
1300
|
+
/* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
|
|
1301
|
+
] })
|
|
1302
|
+
] }) });
|
|
1303
|
+
}
|
|
958
1304
|
function inspectorFieldConfig(name, field) {
|
|
959
1305
|
const key = name.toLowerCase();
|
|
960
1306
|
const group = /(?:href|url)/.test(key) ? "Links" : /(?:default|shipment|query|hide)/.test(key) ? "Tracking settings" : /(?:id|variant|theme)/.test(key) ? "Advanced" : "Content";
|
|
961
1307
|
const description = field.description ?? (field.control === "textarea" ? "\u9002\u5408\u8F83\u957F\u6216\u591A\u884C\u7684\u5C55\u793A\u6587\u6848\u3002" : field.control === "url" ? "\u4F7F\u7528\u7AD9\u5185\u76F8\u5BF9\u8DEF\u5F84\u6216 HTTPS \u5730\u5740\u3002" : key.includes("shipment") ? "\u591A\u4E2A\u5305\u88F9\u6807\u7B7E\u4F7F\u7528 | \u5206\u9694\u3002" : key.includes("default") ? "\u4EC5\u7528\u4E8E\u7F16\u8F91\u5668\u548C\u7A7A\u72B6\u6001\u9884\u89C8\u3002" : void 0);
|
|
962
1308
|
return { ...field, group: field.group ?? group, description };
|
|
963
1309
|
}
|
|
964
|
-
|
|
1310
|
+
var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
|
|
1311
|
+
function DocumentInspector({ pageId, assetPicker, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1312
|
+
const [assetError, setAssetError] = useState3(null);
|
|
1313
|
+
const [selectingAsset, setSelectingAsset] = useState3(false);
|
|
965
1314
|
const definition = registry?.getBlock(block.type);
|
|
966
1315
|
const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
|
|
967
1316
|
const [name, field] = entry;
|
|
@@ -970,21 +1319,42 @@ function DocumentInspector({ block, registry, disabled, onChange }) {
|
|
|
970
1319
|
(groups[group] ??= []).push([name, configuredField]);
|
|
971
1320
|
return groups;
|
|
972
1321
|
}, {}) : {};
|
|
973
|
-
return /* @__PURE__ */
|
|
974
|
-
/* @__PURE__ */
|
|
975
|
-
/* @__PURE__ */
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
1322
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
|
|
1323
|
+
/* @__PURE__ */ jsxs5("header", { className: "pb-inspector__header", children: [
|
|
1324
|
+
/* @__PURE__ */ jsx7(Badge3, { children: block.type }),
|
|
1325
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1326
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
|
|
1327
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
|
|
979
1328
|
] })
|
|
980
1329
|
] }),
|
|
981
|
-
block.type === "core.text" ? /* @__PURE__ */
|
|
982
|
-
block.type === "core.image" ? /* @__PURE__ */
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
1330
|
+
block.type === "core.text" ? /* @__PURE__ */ jsx7(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx7(InspectorField, { name: "content", field: { field: "", label: "\u6587\u672C\u5185\u5BB9", control: "textarea", description: "\u652F\u6301\u8F83\u957F\u7684\u6B63\u6587\u5185\u5BB9\u3002" }, value: block.props.content, registry, disabled, onChange: (content) => onChange({ content }) }) }) : null,
|
|
1331
|
+
block.type === "core.image" ? /* @__PURE__ */ jsxs5(InspectorSection, { title: "Image", children: [
|
|
1332
|
+
/* @__PURE__ */ jsx7(InspectorField, { name: "src", field: { field: "", label: "\u56FE\u7247 URL", control: "url", description: "\u4F7F\u7528 HTTPS \u56FE\u7247\u5730\u5740\u3002" }, value: block.props.src, registry, disabled, onChange: (src) => onChange({ src }) }),
|
|
1333
|
+
/* @__PURE__ */ jsx7(InspectorField, { name: "alt", field: { field: "", label: "\u66FF\u4EE3\u6587\u672C", control: "text", description: "\u7528\u4E8E\u65E0\u969C\u788D\u9605\u8BFB\u548C\u56FE\u7247\u52A0\u8F7D\u5931\u8D25\u573A\u666F\u3002" }, value: block.props.alt, registry, disabled, onChange: (alt) => onChange({ alt }) }),
|
|
1334
|
+
assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1335
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
|
|
1336
|
+
setSelectingAsset(true);
|
|
1337
|
+
setAssetError(null);
|
|
1338
|
+
try {
|
|
1339
|
+
const asset = await assetPicker.selectAsset({ pageId, blockId: block.id, current: { id: typeof block.props.assetId === "string" ? block.props.assetId : void 0, url: typeof block.props.src === "string" ? block.props.src : void 0, alt: typeof block.props.alt === "string" ? block.props.alt : void 0 } });
|
|
1340
|
+
if (asset) onChange({ ...block.props, assetId: asset.id, src: asset.url, alt: asset.alt ?? (typeof block.props.alt === "string" ? block.props.alt : "") });
|
|
1341
|
+
} catch {
|
|
1342
|
+
setAssetError("\u65E0\u6CD5\u9009\u62E9\u7D20\u6750\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
1343
|
+
} finally {
|
|
1344
|
+
setSelectingAsset(false);
|
|
1345
|
+
}
|
|
1346
|
+
})(), children: selectingAsset ? "\u6B63\u5728\u9009\u62E9\u7D20\u6750\u2026" : "\u9009\u62E9\u7D20\u6750" }),
|
|
1347
|
+
assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
|
|
1348
|
+
] }) : null
|
|
985
1349
|
] }) : null,
|
|
986
|
-
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */
|
|
987
|
-
definition ? /* @__PURE__ */
|
|
1350
|
+
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx7(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx7(InspectorField, { name, field, value: block.props[name], registry, disabled, onChange: (value) => onChange({ ...block.props, [name]: value }) }, name)) }, group)),
|
|
1351
|
+
appearanceControls && definition?.variants?.length ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u5916\u89C2", children: /* @__PURE__ */ jsx7(Select2, { label: "\u6837\u5F0F\u53D8\u4F53", options: definition.variants.map((variant) => ({ label: variant.label, value: variant.id })), value: block.variant, disabled, onChange: (variant) => onPresentationChange({ variant, style: block.style }) }) }) : null,
|
|
1352
|
+
appearanceControls && definition ? /* @__PURE__ */ jsx7(InspectorSection, { title: "\u6837\u5F0F\u8986\u76D6", defaultOpen: false, children: appearanceTokens.map(([token, label]) => /* @__PURE__ */ jsx7(TextField, { label, value: block.style[token] ?? "", placeholder: "\u7EE7\u627F\u9875\u9762\u6216\u6A21\u677F\u8BBE\u7F6E", autoComplete: "off", disabled, onChange: (value) => {
|
|
1353
|
+
const style = { ...block.style };
|
|
1354
|
+
if (value.trim()) style[token] = value;
|
|
1355
|
+
else delete style[token];
|
|
1356
|
+
onPresentationChange({ variant: block.variant, style });
|
|
1357
|
+
} }, token)) }) : null
|
|
988
1358
|
] });
|
|
989
1359
|
}
|
|
990
1360
|
export {
|
|
@@ -993,6 +1363,7 @@ export {
|
|
|
993
1363
|
ExtensionRegistry,
|
|
994
1364
|
ExtensionRegistryError,
|
|
995
1365
|
PageDocumentEditorShell,
|
|
1366
|
+
PageStatusCard,
|
|
996
1367
|
TemplateRegistry,
|
|
997
1368
|
WebRenderer,
|
|
998
1369
|
createAdminI18n,
|
|
@@ -1000,12 +1371,18 @@ export {
|
|
|
1000
1371
|
createPageDocument,
|
|
1001
1372
|
createTemplateRegistry,
|
|
1002
1373
|
fromEngineData,
|
|
1374
|
+
maximumBlockInstances,
|
|
1375
|
+
mergeBlockPolicies,
|
|
1003
1376
|
mergeThemeTokens,
|
|
1004
1377
|
migratePageDocument,
|
|
1378
|
+
minimumBlockInstances,
|
|
1005
1379
|
normalizeThemeTokens,
|
|
1006
1380
|
systemThemeTokens,
|
|
1007
1381
|
toEngineData,
|
|
1008
1382
|
toThemeStyle,
|
|
1009
1383
|
useEditorContext,
|
|
1010
|
-
|
|
1384
|
+
validateBlockPolicy,
|
|
1385
|
+
validateFieldValue,
|
|
1386
|
+
validatePageDocument,
|
|
1387
|
+
validatePageDocumentWithRegistry
|
|
1011
1388
|
};
|