@standhigher/puck-page-builder 0.9.0 → 0.10.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 +199 -9
- package/dist/index.js +477 -143
- 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 +12 -1
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";
|
|
@@ -342,7 +348,7 @@ function PropertyPanel({
|
|
|
342
348
|
|
|
343
349
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
344
350
|
import { Puck as Puck2, usePuck } from "@puckeditor/core";
|
|
345
|
-
import { Badge as
|
|
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";
|
|
346
352
|
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2 } 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
|
|
|
@@ -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,8 +790,21 @@ 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",
|
|
@@ -680,6 +820,10 @@ var messages = {
|
|
|
680
820
|
redo: "Redo",
|
|
681
821
|
duplicate: "Duplicate",
|
|
682
822
|
remove: "Delete",
|
|
823
|
+
confirmDeleteTitle: "Delete this block?",
|
|
824
|
+
confirmDeleteMessage: "You can restore it with Undo after deleting.",
|
|
825
|
+
cancel: "Cancel",
|
|
826
|
+
confirm: "Delete block",
|
|
683
827
|
moveUp: "Move up",
|
|
684
828
|
moveDown: "Move down",
|
|
685
829
|
loading: "Loading editor\u2026",
|
|
@@ -694,8 +838,21 @@ var messages = {
|
|
|
694
838
|
publish: "Publish",
|
|
695
839
|
publishing: "Publishing\u2026",
|
|
696
840
|
saveFailed: "Could not save draft",
|
|
841
|
+
retrySave: "Retry save",
|
|
842
|
+
offline: "Offline \u2014 changes will save when connection returns",
|
|
697
843
|
publishFailed: "Could not publish",
|
|
698
844
|
published: "Published",
|
|
845
|
+
back: "Back",
|
|
846
|
+
confirmLeaveTitle: "Leave the editor?",
|
|
847
|
+
leave: "Leave",
|
|
848
|
+
zoom: "Zoom",
|
|
849
|
+
zoomAuto: "Auto zoom",
|
|
850
|
+
preview: "Preview",
|
|
851
|
+
addToStore: "Add to store",
|
|
852
|
+
acquiringLock: "Acquiring edit lock\u2026",
|
|
853
|
+
locked: "This page is being edited",
|
|
854
|
+
lockLost: "The edit lock has expired",
|
|
855
|
+
readonly: "This page is read-only",
|
|
699
856
|
leaveWarning: "You have unsaved changes. Are you sure you want to leave?",
|
|
700
857
|
selectBlock: "Select a block to edit it.",
|
|
701
858
|
desktop: "Desktop",
|
|
@@ -718,7 +875,7 @@ function blockIdAtRelativeY(ids, relativeY) {
|
|
|
718
875
|
}
|
|
719
876
|
|
|
720
877
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
721
|
-
import { jsx as
|
|
878
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
722
879
|
var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
|
|
723
880
|
function blockLabel(block, registry) {
|
|
724
881
|
return blockTypeLabel(block.type, registry);
|
|
@@ -729,39 +886,142 @@ function blockTypeLabel(type, registry) {
|
|
|
729
886
|
return registry?.getBlock(type)?.label ?? type;
|
|
730
887
|
}
|
|
731
888
|
function validateDocumentBlocks(document, registry) {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
889
|
+
return registry ? validatePageDocumentWithRegistry(document, registry) : [];
|
|
890
|
+
}
|
|
891
|
+
function useEditorSession(adapter, pageId) {
|
|
892
|
+
const [managed, setManaged] = useState3(() => adapter ? { state: "acquiring" } : { state: "active" });
|
|
893
|
+
useEffect3(() => {
|
|
894
|
+
let cancelled = false;
|
|
895
|
+
let session;
|
|
896
|
+
let heartbeatTimer;
|
|
897
|
+
const stopHeartbeat = () => {
|
|
898
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
899
|
+
heartbeatTimer = void 0;
|
|
900
|
+
};
|
|
901
|
+
if (!adapter) return;
|
|
902
|
+
void adapter.acquire({ pageId }).then((result) => {
|
|
903
|
+
if (cancelled) return;
|
|
904
|
+
if (result.state !== "active") {
|
|
905
|
+
setManaged({ state: result.state, message: result.message ?? (result.state === "locked" && result.editorName ? `${result.editorName} \u6B63\u5728\u7F16\u8F91` : void 0) });
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
session = result.session;
|
|
909
|
+
setManaged({ state: "active", session });
|
|
910
|
+
if (!adapter.heartbeat) return;
|
|
911
|
+
const heartbeat = () => {
|
|
912
|
+
if (typeof navigator !== "undefined" && !navigator.onLine) return;
|
|
913
|
+
void adapter.heartbeat?.({ pageId, session: result.session }).catch(() => {
|
|
914
|
+
if (!cancelled) {
|
|
915
|
+
stopHeartbeat();
|
|
916
|
+
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" });
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
};
|
|
920
|
+
heartbeatTimer = setInterval(heartbeat, adapter.heartbeatIntervalMs ?? 3e4);
|
|
921
|
+
}).catch(() => {
|
|
922
|
+
if (!cancelled) setManaged({ state: "lost", message: "\u65E0\u6CD5\u83B7\u53D6\u7F16\u8F91\u9501\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
|
|
923
|
+
});
|
|
924
|
+
return () => {
|
|
925
|
+
cancelled = true;
|
|
926
|
+
stopHeartbeat();
|
|
927
|
+
if (session) void adapter.release?.({ pageId, session });
|
|
928
|
+
};
|
|
929
|
+
}, [adapter, pageId]);
|
|
930
|
+
return managed;
|
|
742
931
|
}
|
|
743
932
|
function PageDocumentEditorShell(props) {
|
|
744
|
-
|
|
933
|
+
const { sessionAdapter, initialDocument, onSessionStateChange } = props;
|
|
934
|
+
const managedSession = useEditorSession(sessionAdapter, initialDocument.pageId);
|
|
935
|
+
useEffect3(() => {
|
|
936
|
+
onSessionStateChange?.(managedSession.state);
|
|
937
|
+
}, [managedSession.state, onSessionStateChange]);
|
|
938
|
+
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
939
|
}
|
|
746
|
-
function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPublish }) {
|
|
940
|
+
function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, pageStatus, onBack, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
747
941
|
const editor = useEditorContext();
|
|
748
942
|
const [blockView, setBlockView] = useState3("blocks");
|
|
749
943
|
const [draggingLibraryType, setDraggingLibraryType] = useState3(null);
|
|
750
944
|
const canvasFrameRef = useRef4(null);
|
|
751
945
|
const [canvasMutationVersion, setCanvasMutationVersion] = useState3(0);
|
|
946
|
+
const [canvasResetVersion, setCanvasResetVersion] = useState3(0);
|
|
752
947
|
const [request, setRequest] = useState3("idle");
|
|
753
948
|
const [notice, setNotice] = useState3(null);
|
|
949
|
+
const [draftRevision, setDraftRevision] = useState3(initialDraftRevision);
|
|
950
|
+
const [isOnline, setIsOnline] = useState3(() => typeof navigator === "undefined" || navigator.onLine);
|
|
951
|
+
const [saveState, setSaveState] = useState3("saved");
|
|
952
|
+
const [zoom, setZoom] = useState3("auto");
|
|
953
|
+
const [confirmBack, setConfirmBack] = useState3(false);
|
|
754
954
|
const i18n = createAdminI18n(adminLocale);
|
|
755
955
|
const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
|
|
756
956
|
const validationIssues = useMemo2(() => validateDocumentBlocks(editor.document, registry), [editor.document, registry]);
|
|
757
|
-
const { confirmCanvasSelection, selectedBlockId, updateBlockProps } = editor;
|
|
957
|
+
const { confirmCanvasSelection, selectedBlockId, updateBlockProps, updateBlockPresentation } = editor;
|
|
758
958
|
const updateFromCanvasInput = useCallback2((id, props, preserveCanvasValue = false) => {
|
|
759
959
|
if (preserveCanvasValue) setCanvasMutationVersion((version) => version + 1);
|
|
760
960
|
updateBlockProps(id, props);
|
|
761
961
|
}, [updateBlockProps]);
|
|
762
962
|
const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry), [confirmCanvasSelection, registry, selectedBlockId, updateFromCanvasInput]);
|
|
763
|
-
|
|
764
|
-
|
|
963
|
+
useEffect3(() => {
|
|
964
|
+
const online = () => setIsOnline(true);
|
|
965
|
+
const offline = () => setIsOnline(false);
|
|
966
|
+
window.addEventListener("online", online);
|
|
967
|
+
window.addEventListener("offline", offline);
|
|
968
|
+
return () => {
|
|
969
|
+
window.removeEventListener("online", online);
|
|
970
|
+
window.removeEventListener("offline", offline);
|
|
971
|
+
};
|
|
972
|
+
}, []);
|
|
973
|
+
const resolvedSaveState = !isOnline && editor.isDirty ? "offline" : saveState === "offline" && editor.isDirty ? "dirty" : saveState === "saved" && editor.isDirty ? "dirty" : saveState;
|
|
974
|
+
const canPersistDraft = Boolean(draftPersistence || onSave);
|
|
975
|
+
const save = useCallback2(async () => {
|
|
976
|
+
if (!canPersistDraft || request !== "idle" || validationIssues.length > 0) return;
|
|
977
|
+
if (!isOnline) {
|
|
978
|
+
setSaveState("offline");
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const document = editor.document;
|
|
982
|
+
setRequest("saving");
|
|
983
|
+
setSaveState("saving");
|
|
984
|
+
setNotice(null);
|
|
985
|
+
try {
|
|
986
|
+
const result = draftPersistence ? await draftPersistence.saveDraft({ document, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document);
|
|
987
|
+
if (result?.revision !== void 0) setDraftRevision(result.revision);
|
|
988
|
+
editor.markSaved(document);
|
|
989
|
+
setSaveState("saved");
|
|
990
|
+
} catch {
|
|
991
|
+
setNotice("saveFailed");
|
|
992
|
+
setSaveState(typeof navigator !== "undefined" && !navigator.onLine ? "offline" : "failed");
|
|
993
|
+
} finally {
|
|
994
|
+
setRequest("idle");
|
|
995
|
+
}
|
|
996
|
+
}, [canPersistDraft, draftPersistence, draftRevision, editor, isOnline, onSave, request, validationIssues.length]);
|
|
997
|
+
useEffect3(() => {
|
|
998
|
+
if (!autoSave || !canPersistDraft || !editor.isDirty || resolvedSaveState !== "dirty" || !isOnline || request !== "idle" || validationIssues.length > 0 || editor.sessionState !== "active") return;
|
|
999
|
+
const timer = window.setTimeout(() => {
|
|
1000
|
+
void save();
|
|
1001
|
+
}, autoSaveDelayMs);
|
|
1002
|
+
return () => window.clearTimeout(timer);
|
|
1003
|
+
}, [autoSave, autoSaveDelayMs, canPersistDraft, editor.isDirty, editor.sessionState, isOnline, request, resolvedSaveState, save, validationIssues.length]);
|
|
1004
|
+
const publish = async () => {
|
|
1005
|
+
if (!publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline) return;
|
|
1006
|
+
const document = editor.document;
|
|
1007
|
+
setRequest("publishing");
|
|
1008
|
+
setNotice(null);
|
|
1009
|
+
try {
|
|
1010
|
+
if (publishAction) await publishAction.publish({ document, expectedRevision: draftRevision, session: editor.session, validationIssues });
|
|
1011
|
+
else await onPublish?.(document);
|
|
1012
|
+
editor.markSaved(document);
|
|
1013
|
+
setSaveState("saved");
|
|
1014
|
+
setNotice("published");
|
|
1015
|
+
} catch {
|
|
1016
|
+
setNotice("publishFailed");
|
|
1017
|
+
} finally {
|
|
1018
|
+
setRequest("idle");
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx7(EditorStatus, { state: editor.loadState });
|
|
1022
|
+
if (editor.sessionState !== "active") return /* @__PURE__ */ jsx7(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
|
|
1023
|
+
const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
|
|
1024
|
+
const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
|
|
765
1025
|
const addFromLibrary = (type, beforeId) => {
|
|
766
1026
|
const id = editor.addBlock(type, beforeId);
|
|
767
1027
|
if (id) editor.requestCanvasSelection(id);
|
|
@@ -797,120 +1057,125 @@ function PageDocumentEditor({ iframe = true, registry, adminLocale, onSave, onPu
|
|
|
797
1057
|
event.stopPropagation();
|
|
798
1058
|
setDraggingLibraryType(null);
|
|
799
1059
|
};
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
} catch {
|
|
808
|
-
setNotice("saveFailed");
|
|
809
|
-
} finally {
|
|
810
|
-
setRequest("idle");
|
|
811
|
-
}
|
|
1060
|
+
const saveBadgeTone = resolvedSaveState === "failed" ? "critical" : resolvedSaveState === "offline" || resolvedSaveState === "dirty" ? "attention" : "success";
|
|
1061
|
+
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");
|
|
1062
|
+
const saveButtonLabel = request === "saving" ? i18n.t("saving") : resolvedSaveState === "failed" ? i18n.t("retrySave") : i18n.t("save");
|
|
1063
|
+
const requestBack = () => {
|
|
1064
|
+
if (!onBack) return;
|
|
1065
|
+
if (editor.isDirty || request === "saving") setConfirmBack(true);
|
|
1066
|
+
else onBack();
|
|
812
1067
|
};
|
|
813
|
-
|
|
814
|
-
if (!
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
/* @__PURE__ */ jsx6("header", { className: "pb-header", children: /* @__PURE__ */ jsxs4(InlineStack2, { align: "space-between", blockAlign: "center", gap: "300", wrap: false, children: [
|
|
831
|
-
/* @__PURE__ */ jsxs4("div", { className: "pb-page-title", children: [
|
|
832
|
-
/* @__PURE__ */ jsx6(Text2, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
|
|
833
|
-
/* @__PURE__ */ jsxs4(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
|
|
834
|
-
"PageDocument V",
|
|
835
|
-
editor.document.schemaVersion,
|
|
836
|
-
" \xB7 ",
|
|
837
|
-
editor.document.target
|
|
1068
|
+
return /* @__PURE__ */ jsx7(Puck2, { config, data: engineData, iframe: { enabled: iframe }, onChange: (data) => {
|
|
1069
|
+
if (!editor.updateFromCanvas(fromEngineData(data, editor.document, registry))) setCanvasResetVersion((version) => version + 1);
|
|
1070
|
+
}, children: /* @__PURE__ */ jsxs5(Puck2.Layout, { children: [
|
|
1071
|
+
/* @__PURE__ */ jsx7(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion }),
|
|
1072
|
+
/* @__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, children: [
|
|
1073
|
+
/* @__PURE__ */ jsx7("header", { className: "pb-header", children: /* @__PURE__ */ jsxs5("div", { className: "pb-header-content", children: [
|
|
1074
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-title-group", children: [
|
|
1075
|
+
onBack ? /* @__PURE__ */ jsx7(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
|
|
1076
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-page-title", children: [
|
|
1077
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
|
|
1078
|
+
/* @__PURE__ */ jsxs5(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: [
|
|
1079
|
+
"PageDocument V",
|
|
1080
|
+
editor.document.schemaVersion,
|
|
1081
|
+
" \xB7 ",
|
|
1082
|
+
editor.document.target
|
|
1083
|
+
] }),
|
|
1084
|
+
pageStatus ? /* @__PURE__ */ jsx7(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
|
|
838
1085
|
] })
|
|
839
1086
|
] }),
|
|
840
|
-
/* @__PURE__ */
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
/* @__PURE__ */
|
|
846
|
-
|
|
1087
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-header-device-toolbar", children: [
|
|
1088
|
+
/* @__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)) }),
|
|
1089
|
+
/* @__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) }) })
|
|
1090
|
+
] }),
|
|
1091
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs5(InlineStack3, { gap: "150", blockAlign: "center", wrap: false, children: [
|
|
1092
|
+
/* @__PURE__ */ jsx7(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
|
|
1093
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canPersistDraft || !editor.isDirty || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void save(), children: saveButtonLabel }),
|
|
1094
|
+
onPreview ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
|
|
1095
|
+
onAddToStore ? /* @__PURE__ */ jsx7(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
|
|
1096
|
+
/* @__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") }),
|
|
1097
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
|
|
1098
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo })
|
|
1099
|
+
] }) })
|
|
847
1100
|
] }) }),
|
|
848
|
-
editor.loadState === "success" ? /* @__PURE__ */
|
|
849
|
-
notice ? /* @__PURE__ */
|
|
850
|
-
validationIssues.length > 0 ? /* @__PURE__ */
|
|
1101
|
+
editor.loadState === "success" ? /* @__PURE__ */ jsx7(Banner, { tone: "success", children: i18n.t("success") }) : null,
|
|
1102
|
+
notice ? /* @__PURE__ */ jsx7(Banner, { tone: notice === "published" ? "success" : "critical", children: i18n.t(notice) }) : null,
|
|
1103
|
+
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
1104
|
issue.path,
|
|
852
1105
|
": ",
|
|
853
1106
|
issue.message
|
|
854
1107
|
] }, `${issue.path}-${issue.message}`)) }) }) : null,
|
|
855
|
-
/* @__PURE__ */
|
|
856
|
-
/* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
1108
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-workspace pb-workspace--document", children: [
|
|
1109
|
+
/* @__PURE__ */ jsxs5("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
|
|
1110
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks", variant: "tertiary", onClick: () => setBlockView("blocks") }),
|
|
1111
|
+
/* @__PURE__ */ jsx7(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline", variant: "tertiary", onClick: () => setBlockView("outline") })
|
|
859
1112
|
] }),
|
|
860
|
-
/* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
blockView === "blocks" ? /* @__PURE__ */
|
|
1113
|
+
/* @__PURE__ */ jsxs5("aside", { className: "pb-left-panel", "aria-label": "PageDocument \u533A\u5757", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx7(InlineStack3, { align: "space-between", blockAlign: "center", children: /* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }) }),
|
|
1115
|
+
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) => {
|
|
1116
|
+
if (!editor.canAddBlock(type)) {
|
|
1117
|
+
event.preventDefault();
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
863
1120
|
event.dataTransfer.setData("application/x-page-document-block", type);
|
|
864
1121
|
event.dataTransfer.effectAllowed = "copy";
|
|
865
1122
|
setDraggingLibraryType(type);
|
|
866
1123
|
}, onDragEnd: () => setDraggingLibraryType(null), children: [
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
869
|
-
/* @__PURE__ */
|
|
1124
|
+
/* @__PURE__ */ jsxs5("span", { className: "pb-library-block-title", children: [
|
|
1125
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
|
|
1126
|
+
/* @__PURE__ */ jsx7("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx7(DragHandleIcon2, {}) })
|
|
870
1127
|
] }),
|
|
871
|
-
/* @__PURE__ */
|
|
872
|
-
] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
/* @__PURE__ */
|
|
1128
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
|
|
1129
|
+
] }, 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: [
|
|
1130
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
|
|
1131
|
+
/* @__PURE__ */ jsx7(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
|
|
875
1132
|
] }, block.id)) })
|
|
876
1133
|
] }),
|
|
877
|
-
/* @__PURE__ */
|
|
878
|
-
/* @__PURE__ */
|
|
879
|
-
/* @__PURE__ */
|
|
880
|
-
|
|
881
|
-
draggingLibraryType
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1134
|
+
/* @__PURE__ */ jsx7("main", { className: "pb-canvas-area", children: /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-stage", children: [
|
|
1135
|
+
/* @__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, {}) }),
|
|
1136
|
+
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: [
|
|
1137
|
+
"\u677E\u5F00\u4EE5\u6DFB\u52A0 ",
|
|
1138
|
+
blockTypeLabel(draggingLibraryType, registry)
|
|
1139
|
+
] }) : null,
|
|
1140
|
+
editor.selectedBlock ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
|
|
1141
|
+
/* @__PURE__ */ jsx7("span", { children: blockLabel(editor.selectedBlock, registry) }),
|
|
1142
|
+
/* @__PURE__ */ jsx7("span", { children: "Selected" })
|
|
1143
|
+
] }) : null
|
|
1144
|
+
] }) }),
|
|
1145
|
+
/* @__PURE__ */ jsxs5("aside", { className: "pb-right-panel", "aria-label": "PageDocument \u5C5E\u6027", children: [
|
|
1146
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
|
|
1147
|
+
editor.selectedBlock ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1148
|
+
/* @__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) }),
|
|
1149
|
+
/* @__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) })
|
|
1150
|
+
] }) : /* @__PURE__ */ jsx7(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
|
|
894
1151
|
] })
|
|
895
|
-
] })
|
|
1152
|
+
] }),
|
|
1153
|
+
editor.pendingDeleteBlock ? /* @__PURE__ */ jsx7(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
|
|
1154
|
+
confirmBack ? /* @__PURE__ */ jsx7(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
|
|
1155
|
+
setConfirmBack(false);
|
|
1156
|
+
onBack?.();
|
|
1157
|
+
} }) : null
|
|
896
1158
|
] })
|
|
897
1159
|
] }) });
|
|
898
1160
|
}
|
|
899
|
-
function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion }) {
|
|
1161
|
+
function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion, canvasResetVersion }) {
|
|
900
1162
|
const puck = usePuck();
|
|
901
1163
|
const lastSelectedId = useRef4(null);
|
|
902
1164
|
const lastSyncedData = useRef4(null);
|
|
903
1165
|
const lastCanvasMutationVersion = useRef4(0);
|
|
1166
|
+
const lastCanvasResetVersion = useRef4(0);
|
|
904
1167
|
const serializedData = JSON.stringify(data);
|
|
905
1168
|
useEffect3(() => {
|
|
906
|
-
|
|
1169
|
+
const forceReset = canvasResetVersion > lastCanvasResetVersion.current;
|
|
1170
|
+
if (lastSyncedData.current === serializedData && !forceReset) return;
|
|
907
1171
|
lastSyncedData.current = serializedData;
|
|
1172
|
+
if (forceReset) lastCanvasResetVersion.current = canvasResetVersion;
|
|
908
1173
|
if (canvasMutationVersion > lastCanvasMutationVersion.current) {
|
|
909
1174
|
lastCanvasMutationVersion.current = canvasMutationVersion;
|
|
910
1175
|
return;
|
|
911
1176
|
}
|
|
912
1177
|
puck.dispatch({ type: "setData", data });
|
|
913
|
-
}, [canvasMutationVersion, data, puck, serializedData]);
|
|
1178
|
+
}, [canvasMutationVersion, canvasResetVersion, data, puck, serializedData]);
|
|
914
1179
|
useEffect3(() => {
|
|
915
1180
|
if (!requestedBlockId) return;
|
|
916
1181
|
const selector = puck.getSelectorForId(requestedBlockId);
|
|
@@ -929,39 +1194,79 @@ function EditorStatus({ state }) {
|
|
|
929
1194
|
const i18n = createAdminI18n();
|
|
930
1195
|
const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
|
|
931
1196
|
const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
|
|
932
|
-
return /* @__PURE__ */
|
|
1197
|
+
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 }) });
|
|
1198
|
+
}
|
|
1199
|
+
function EditorSessionStatus({ state, message }) {
|
|
1200
|
+
const i18n = createAdminI18n();
|
|
1201
|
+
const tone = state === "locked" ? "warning" : state === "readonly" ? "info" : "critical";
|
|
1202
|
+
const title = state === "acquiring" ? i18n.t("acquiringLock") : state === "locked" ? i18n.t("locked") : state === "readonly" ? i18n.t("readonly") : i18n.t("lockLost");
|
|
1203
|
+
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
1204
|
}
|
|
934
1205
|
function InspectorSection({ title, children, defaultOpen = true }) {
|
|
935
|
-
return /* @__PURE__ */
|
|
936
|
-
/* @__PURE__ */
|
|
937
|
-
/* @__PURE__ */
|
|
938
|
-
/* @__PURE__ */
|
|
1206
|
+
return /* @__PURE__ */ jsxs5("details", { className: "pb-inspector-section", open: defaultOpen, children: [
|
|
1207
|
+
/* @__PURE__ */ jsxs5("summary", { children: [
|
|
1208
|
+
/* @__PURE__ */ jsx7("span", { children: title }),
|
|
1209
|
+
/* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2304" })
|
|
939
1210
|
] }),
|
|
940
|
-
/* @__PURE__ */
|
|
1211
|
+
/* @__PURE__ */ jsx7("div", { className: "pb-inspector-section__body", children })
|
|
941
1212
|
] });
|
|
942
1213
|
}
|
|
943
1214
|
function InspectorTextControl({ label, value, control, disabled, onChange }) {
|
|
944
1215
|
const stringValue = typeof value === "string" ? value : "";
|
|
945
|
-
return /* @__PURE__ */
|
|
1216
|
+
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) });
|
|
1217
|
+
return /* @__PURE__ */ jsx7(TextField, { label, labelHidden: true, value: stringValue, onChange, autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text" });
|
|
946
1218
|
}
|
|
947
1219
|
function InspectorField({ name, field, value, registry, disabled, onChange }) {
|
|
948
1220
|
const label = field.label ?? name;
|
|
1221
|
+
const issues = validateFieldValue(field, value);
|
|
949
1222
|
const Field = registry?.getField(field.field)?.component;
|
|
950
|
-
return /* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
field.description ? /* @__PURE__ */
|
|
1223
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
|
|
1224
|
+
/* @__PURE__ */ jsxs5("div", { className: "pb-inspector-field__heading", children: [
|
|
1225
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
|
|
1226
|
+
field.description ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
|
|
954
1227
|
] }),
|
|
955
|
-
field.control ? /* @__PURE__ */
|
|
1228
|
+
field.control ? /* @__PURE__ */ jsx7(InspectorTextControl, { label, value, control: field.control, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx7(Field, { value, onChange }) : null,
|
|
1229
|
+
issues.map((issue) => /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
|
|
956
1230
|
] });
|
|
957
1231
|
}
|
|
1232
|
+
function InspectorActions({ block, canDuplicate, canDelete, canMove, canMoveUp, canMoveDown, i18n, onDuplicate, onDelete, onMove }) {
|
|
1233
|
+
return /* @__PURE__ */ jsx7("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: /* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1234
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
|
|
1235
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
|
|
1236
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canDuplicate, onClick: onDuplicate, children: i18n.t("duplicate") }),
|
|
1237
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
|
|
1238
|
+
] }) });
|
|
1239
|
+
}
|
|
1240
|
+
function DeleteConfirmation({ block, config, i18n, onCancel, onConfirm }) {
|
|
1241
|
+
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: [
|
|
1242
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
|
|
1243
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
|
|
1244
|
+
/* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1245
|
+
/* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
|
|
1246
|
+
/* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
|
|
1247
|
+
] })
|
|
1248
|
+
] }) });
|
|
1249
|
+
}
|
|
1250
|
+
function LeaveConfirmation({ i18n, onCancel, onConfirm }) {
|
|
1251
|
+
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: [
|
|
1252
|
+
/* @__PURE__ */ jsx7(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
|
|
1253
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
|
|
1254
|
+
/* @__PURE__ */ jsxs5(ButtonGroup2, { children: [
|
|
1255
|
+
/* @__PURE__ */ jsx7(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
|
|
1256
|
+
/* @__PURE__ */ jsx7(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
|
|
1257
|
+
] })
|
|
1258
|
+
] }) });
|
|
1259
|
+
}
|
|
958
1260
|
function inspectorFieldConfig(name, field) {
|
|
959
1261
|
const key = name.toLowerCase();
|
|
960
1262
|
const group = /(?:href|url)/.test(key) ? "Links" : /(?:default|shipment|query|hide)/.test(key) ? "Tracking settings" : /(?:id|variant|theme)/.test(key) ? "Advanced" : "Content";
|
|
961
1263
|
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
1264
|
return { ...field, group: field.group ?? group, description };
|
|
963
1265
|
}
|
|
964
|
-
|
|
1266
|
+
var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
|
|
1267
|
+
function DocumentInspector({ pageId, assetPicker, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1268
|
+
const [assetError, setAssetError] = useState3(null);
|
|
1269
|
+
const [selectingAsset, setSelectingAsset] = useState3(false);
|
|
965
1270
|
const definition = registry?.getBlock(block.type);
|
|
966
1271
|
const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
|
|
967
1272
|
const [name, field] = entry;
|
|
@@ -970,21 +1275,43 @@ function DocumentInspector({ block, registry, disabled, onChange }) {
|
|
|
970
1275
|
(groups[group] ??= []).push([name, configuredField]);
|
|
971
1276
|
return groups;
|
|
972
1277
|
}, {}) : {};
|
|
973
|
-
return /* @__PURE__ */
|
|
974
|
-
/* @__PURE__ */
|
|
975
|
-
/* @__PURE__ */
|
|
976
|
-
/* @__PURE__ */
|
|
977
|
-
/* @__PURE__ */
|
|
978
|
-
/* @__PURE__ */
|
|
1278
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
|
|
1279
|
+
/* @__PURE__ */ jsxs5("header", { className: "pb-inspector__header", children: [
|
|
1280
|
+
/* @__PURE__ */ jsx7(Badge3, { children: block.type }),
|
|
1281
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1282
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
|
|
1283
|
+
/* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
|
|
979
1284
|
] })
|
|
980
1285
|
] }),
|
|
981
|
-
block.type === "core.text" ? /* @__PURE__ */
|
|
982
|
-
block.type === "core.image" ? /* @__PURE__ */
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
1286
|
+
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,
|
|
1287
|
+
block.type === "core.image" ? /* @__PURE__ */ jsxs5(InspectorSection, { title: "Image", children: [
|
|
1288
|
+
/* @__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 }) }),
|
|
1289
|
+
/* @__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 }) }),
|
|
1290
|
+
assetPicker ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1291
|
+
/* @__PURE__ */ jsx7(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
|
|
1292
|
+
setSelectingAsset(true);
|
|
1293
|
+
setAssetError(null);
|
|
1294
|
+
try {
|
|
1295
|
+
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 } });
|
|
1296
|
+
if (asset) onChange({ ...block.props, assetId: asset.id, src: asset.url, alt: asset.alt ?? (typeof block.props.alt === "string" ? block.props.alt : "") });
|
|
1297
|
+
} catch {
|
|
1298
|
+
setAssetError("\u65E0\u6CD5\u9009\u62E9\u7D20\u6750\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
1299
|
+
} finally {
|
|
1300
|
+
setSelectingAsset(false);
|
|
1301
|
+
}
|
|
1302
|
+
})(), children: selectingAsset ? "\u6B63\u5728\u9009\u62E9\u7D20\u6750\u2026" : "\u9009\u62E9\u7D20\u6750" }),
|
|
1303
|
+
assetError ? /* @__PURE__ */ jsx7(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
|
|
1304
|
+
] }) : null
|
|
985
1305
|
] }) : null,
|
|
986
|
-
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */
|
|
987
|
-
definition ? /* @__PURE__ */
|
|
1306
|
+
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)),
|
|
1307
|
+
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,
|
|
1308
|
+
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) => {
|
|
1309
|
+
const style = { ...block.style };
|
|
1310
|
+
if (value.trim()) style[token] = value;
|
|
1311
|
+
else delete style[token];
|
|
1312
|
+
onPresentationChange({ variant: block.variant, style });
|
|
1313
|
+
} }, token)) }) : null,
|
|
1314
|
+
definition ? /* @__PURE__ */ jsx7("p", { className: "pb-inspector__hint", children: "\u753B\u5E03\u4E2D\u5E26\u865A\u7EBF\u8FB9\u6846\u7684\u5185\u5BB9\u53EF\u76F4\u63A5\u7F16\u8F91\u3002" }) : null
|
|
988
1315
|
] });
|
|
989
1316
|
}
|
|
990
1317
|
export {
|
|
@@ -993,6 +1320,7 @@ export {
|
|
|
993
1320
|
ExtensionRegistry,
|
|
994
1321
|
ExtensionRegistryError,
|
|
995
1322
|
PageDocumentEditorShell,
|
|
1323
|
+
PageStatusCard,
|
|
996
1324
|
TemplateRegistry,
|
|
997
1325
|
WebRenderer,
|
|
998
1326
|
createAdminI18n,
|
|
@@ -1000,12 +1328,18 @@ export {
|
|
|
1000
1328
|
createPageDocument,
|
|
1001
1329
|
createTemplateRegistry,
|
|
1002
1330
|
fromEngineData,
|
|
1331
|
+
maximumBlockInstances,
|
|
1332
|
+
mergeBlockPolicies,
|
|
1003
1333
|
mergeThemeTokens,
|
|
1004
1334
|
migratePageDocument,
|
|
1335
|
+
minimumBlockInstances,
|
|
1005
1336
|
normalizeThemeTokens,
|
|
1006
1337
|
systemThemeTokens,
|
|
1007
1338
|
toEngineData,
|
|
1008
1339
|
toThemeStyle,
|
|
1009
1340
|
useEditorContext,
|
|
1010
|
-
|
|
1341
|
+
validateBlockPolicy,
|
|
1342
|
+
validateFieldValue,
|
|
1343
|
+
validatePageDocument,
|
|
1344
|
+
validatePageDocumentWithRegistry
|
|
1011
1345
|
};
|