@standhigher/puck-page-builder 0.12.4 → 0.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -1
- package/dist/index.js +515 -263
- package/package.json +1 -1
- package/src/styles.css +25 -8
package/dist/index.js
CHANGED
|
@@ -260,13 +260,7 @@ function EditorShell({ initialState, iframe = true }) {
|
|
|
260
260
|
] }),
|
|
261
261
|
/* @__PURE__ */ jsxs2("main", { className: "pb-canvas-area", children: [
|
|
262
262
|
/* @__PURE__ */ jsx2(CanvasToolbar, { device: state.device, zoom: state.zoom, onDevice: (device) => update({ device }), onZoom: (zoom) => update({ zoom }) }),
|
|
263
|
-
/* @__PURE__ */
|
|
264
|
-
/* @__PURE__ */ jsx2("div", { className: `pb-canvas-frame pb-canvas-frame--${state.device} pb-canvas-frame--zoom-${state.zoom}`, "data-device": state.device, "data-zoom": state.zoom, children: /* @__PURE__ */ jsx2(Puck.Preview, {}) }),
|
|
265
|
-
selectedBlock ? /* @__PURE__ */ jsxs2("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${selectedBlock.label}`, children: [
|
|
266
|
-
/* @__PURE__ */ jsx2("span", { children: selectedBlock.label }),
|
|
267
|
-
/* @__PURE__ */ jsx2("span", { children: "Selected" })
|
|
268
|
-
] }) : null
|
|
269
|
-
] }),
|
|
263
|
+
/* @__PURE__ */ jsx2("div", { className: "pb-canvas-stage", children: /* @__PURE__ */ jsx2("div", { className: `pb-canvas-frame pb-canvas-frame--${state.device} pb-canvas-frame--zoom-${state.zoom}`, "data-device": state.device, "data-zoom": state.zoom, children: /* @__PURE__ */ jsx2(Puck.Preview, {}) }) }),
|
|
270
264
|
!state.isLeftRailOpen || !state.isRightPanelOpen ? /* @__PURE__ */ jsxs2("div", { className: "pb-collapsed-actions", children: [
|
|
271
265
|
!state.isLeftRailOpen ? /* @__PURE__ */ jsx2(Button, { onClick: () => update({ isLeftRailOpen: true }), children: "\u6253\u5F00\u533A\u5757\u9762\u677F" }) : null,
|
|
272
266
|
!state.isRightPanelOpen ? /* @__PURE__ */ jsx2(Button, { onClick: () => update({ isRightPanelOpen: true }), children: "\u6253\u5F00\u5C5E\u6027\u9762\u677F" }) : null
|
|
@@ -353,77 +347,132 @@ function PropertyPanel({
|
|
|
353
347
|
import { Puck as Puck2, usePuck } from "@puckeditor/core";
|
|
354
348
|
import { Badge as Badge3, Banner, Button as Button2, ButtonGroup as ButtonGroup2, Frame, InlineStack as InlineStack3, Select as Select2, Text as Text3, TextField, Toast } from "@shopify/polaris";
|
|
355
349
|
import { DragHandleIcon as DragHandleIcon2, LayoutSectionIcon as LayoutSectionIcon2, MenuIcon as MenuIcon2, ProductIcon, RedoIcon as RedoIcon2, UndoIcon as UndoIcon2, XIcon as XIcon2 } from "@shopify/polaris-icons";
|
|
356
|
-
import { useCallback as useCallback2, useEffect as
|
|
350
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useLayoutEffect, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
|
|
357
351
|
|
|
358
|
-
// src/adapters/puck/canvas-
|
|
352
|
+
// src/adapters/puck/use-canvas-editing.ts
|
|
359
353
|
import { registerOverlayPortal } from "@puckeditor/core";
|
|
360
354
|
import { useEffect, useRef as useRef2 } from "react";
|
|
355
|
+
var editingControlSelector = "input, textarea, select, button, a[href], [contenteditable]:not([contenteditable=false]), [role=textbox]";
|
|
356
|
+
function useCanvasEditing(active) {
|
|
357
|
+
const ref = useRef2(null);
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
const root = ref.current;
|
|
360
|
+
if (!active || !root) return;
|
|
361
|
+
const cleanupPortal = registerOverlayPortal(root, { disableDragOnFocus: false });
|
|
362
|
+
const protectEditingControl = (event) => {
|
|
363
|
+
const target = event.target;
|
|
364
|
+
const control = typeof target?.closest === "function" ? target.closest(editingControlSelector) : null;
|
|
365
|
+
if (control && root.contains(control)) event.stopPropagation();
|
|
366
|
+
};
|
|
367
|
+
root.addEventListener("pointerdown", protectEditingControl, { capture: true });
|
|
368
|
+
return () => {
|
|
369
|
+
root.removeEventListener("pointerdown", protectEditingControl, { capture: true });
|
|
370
|
+
cleanupPortal?.();
|
|
371
|
+
};
|
|
372
|
+
}, [active]);
|
|
373
|
+
return ref;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/adapters/puck/canvas-extension-block.tsx
|
|
361
377
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
362
|
-
function CanvasExtensionBlock({ active, children, label, onSelect }) {
|
|
363
|
-
const editorRef =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
378
|
+
function CanvasExtensionBlock({ active, blockId, children, label, onSelect }) {
|
|
379
|
+
const editorRef = useCanvasEditing(active);
|
|
380
|
+
return /* @__PURE__ */ jsx3("div", { ref: editorRef, className: "pb-document-canvas__extension", "data-page-document-block-id": blockId, "aria-label": "Select " + label + " in canvas", role: "group", tabIndex: 0, onClick: onSelect, onKeyDown: (event) => {
|
|
381
|
+
if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
onSelect();
|
|
384
|
+
}
|
|
367
385
|
}, children });
|
|
368
386
|
}
|
|
369
387
|
|
|
388
|
+
// src/adapters/puck/canvas-core-blocks.tsx
|
|
389
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
390
|
+
function CanvasTextBlock({ id, content, editable, onSelect, onPropsChange }) {
|
|
391
|
+
const ref = useCanvasEditing(editable);
|
|
392
|
+
return /* @__PURE__ */ jsx4("section", { ref, className: "pb-document-canvas__text", "data-page-document-block-id": id, "aria-label": "Select Text in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
|
|
393
|
+
if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
|
|
394
|
+
event.preventDefault();
|
|
395
|
+
onSelect(id);
|
|
396
|
+
}
|
|
397
|
+
}, children: /* @__PURE__ */ jsx4("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
|
|
398
|
+
if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" }, true);
|
|
399
|
+
}, children: content }) });
|
|
400
|
+
}
|
|
401
|
+
function CanvasImageBlock({ id, src, alt, editable, onSelect, onPropsChange }) {
|
|
402
|
+
const ref = useCanvasEditing(editable);
|
|
403
|
+
return /* @__PURE__ */ jsxs3("figure", { ref, className: "pb-document-canvas__image", "data-page-document-block-id": id, "aria-label": "Select Image in canvas", role: "button", tabIndex: 0, onClick: () => onSelect(id), onKeyDown: (event) => {
|
|
404
|
+
if (event.target === event.currentTarget && (event.key === "Enter" || event.key === " ")) {
|
|
405
|
+
event.preventDefault();
|
|
406
|
+
onSelect(id);
|
|
407
|
+
}
|
|
408
|
+
}, children: [
|
|
409
|
+
/* @__PURE__ */ jsx4("img", { src, alt }),
|
|
410
|
+
editable ? /* @__PURE__ */ jsx4("input", { "aria-label": "\u753B\u5E03\u56FE\u7247 URL", value: src, onChange: (event) => onPropsChange(id, { src: event.currentTarget.value }, true), onClick: (event) => event.stopPropagation() }) : null
|
|
411
|
+
] });
|
|
412
|
+
}
|
|
413
|
+
|
|
370
414
|
// src/adapters/puck/page-document-config.tsx
|
|
371
|
-
import { Fragment, jsx as
|
|
415
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
416
|
+
function withBlockPermissions(component, type, resolvePermissions) {
|
|
417
|
+
if (!resolvePermissions) return component;
|
|
418
|
+
return {
|
|
419
|
+
...component,
|
|
420
|
+
// Keep actions hidden until the policy resolver has produced the current
|
|
421
|
+
// per-block result. This prevents a one-frame clickable action that may be
|
|
422
|
+
// rejected by the PageDocument policy after Puck emits onChange.
|
|
423
|
+
permissions: { drag: false, duplicate: false, delete: false, edit: false, insert: false },
|
|
424
|
+
resolvePermissions: (item) => {
|
|
425
|
+
const id = typeof item?.props?.id === "string" ? item.props.id : "";
|
|
426
|
+
return resolvePermissions({ id, type });
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
372
430
|
function canvasFieldFallback({ block, props, registry, onPropsChange }) {
|
|
373
431
|
const fields = Object.entries(block.fields);
|
|
374
432
|
if (!fields.length) return null;
|
|
375
|
-
return /* @__PURE__ */
|
|
433
|
+
return /* @__PURE__ */ jsx5("div", { "aria-label": "Edit " + block.label + " values in canvas", onClick: (event) => event.stopPropagation(), style: { display: "grid", gap: 8, marginTop: 12, padding: 12, border: "1px dashed #94a3b8", borderRadius: 6, background: "#f8fafc" }, children: fields.map(([name, field]) => {
|
|
376
434
|
const Field = registry?.getField(field.field)?.component;
|
|
377
|
-
return Field ? /* @__PURE__ */
|
|
435
|
+
return Field ? /* @__PURE__ */ jsxs4("label", { style: { display: "grid", gap: 4, fontSize: 12, fontWeight: 600 }, children: [
|
|
378
436
|
field.label ?? name,
|
|
379
|
-
/* @__PURE__ */
|
|
437
|
+
/* @__PURE__ */ jsx5(Field, { value: props[name], onChange: (value) => onPropsChange({ [name]: value }) })
|
|
380
438
|
] }, name) : null;
|
|
381
439
|
}) });
|
|
382
440
|
}
|
|
383
|
-
function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId, registry) {
|
|
441
|
+
function createPageDocumentPuckConfig(onSelect, onPropsChange, selectedBlockId, registry, resolveBlockPermissions) {
|
|
384
442
|
const extensionComponents = Object.fromEntries((registry?.blocks ?? []).flatMap((block) => {
|
|
385
443
|
const BlockRenderer = block.render.web;
|
|
386
444
|
if (!BlockRenderer) return [];
|
|
387
|
-
return [[block.type, {
|
|
445
|
+
return [[block.type, withBlockPermissions({
|
|
388
446
|
render: (props) => {
|
|
389
447
|
const id = typeof props.id === "string" ? props.id : `unknown-${block.type}`;
|
|
390
448
|
const EditorRenderer = block.render.editor;
|
|
391
449
|
const active = id === selectedBlockId;
|
|
392
|
-
return /* @__PURE__ */
|
|
393
|
-
/* @__PURE__ */
|
|
450
|
+
return /* @__PURE__ */ jsx5(CanvasExtensionBlock, { active, blockId: id, label: block.label, onSelect: () => onSelect(id), children: EditorRenderer ? /* @__PURE__ */ jsx5(EditorRenderer, { ...props, blockId: id, selected: active, onPropsChange: (next) => onPropsChange(id, next) }) : /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
451
|
+
/* @__PURE__ */ jsx5(BlockRenderer, { ...props }),
|
|
394
452
|
active ? canvasFieldFallback({ block, props, registry, onPropsChange: (next) => onPropsChange(id, next) }) : null
|
|
395
453
|
] }) });
|
|
396
454
|
}
|
|
397
|
-
}]];
|
|
455
|
+
}, block.type, resolveBlockPermissions)]];
|
|
398
456
|
}));
|
|
399
457
|
return {
|
|
400
458
|
components: {
|
|
401
|
-
Text: {
|
|
459
|
+
Text: withBlockPermissions({
|
|
402
460
|
render: (props) => {
|
|
403
461
|
const id = typeof props.id === "string" ? props.id : "unknown-text";
|
|
404
462
|
const content = typeof props.content === "string" ? props.content : "";
|
|
405
463
|
const editable = id === selectedBlockId;
|
|
406
|
-
return /* @__PURE__ */
|
|
407
|
-
if (event.key === "Enter" || event.key === " ") onSelect(id);
|
|
408
|
-
}, children: /* @__PURE__ */ jsx4("p", { contentEditable: editable, suppressContentEditableWarning: true, onInput: (event) => {
|
|
409
|
-
if (editable) onPropsChange(id, { content: event.currentTarget.textContent ?? "" }, true);
|
|
410
|
-
}, children: content }) });
|
|
464
|
+
return /* @__PURE__ */ jsx5(CanvasTextBlock, { id, content, editable, onSelect, onPropsChange });
|
|
411
465
|
}
|
|
412
|
-
},
|
|
413
|
-
Image: {
|
|
466
|
+
}, "core.text", resolveBlockPermissions),
|
|
467
|
+
Image: withBlockPermissions({
|
|
414
468
|
render: (props) => {
|
|
415
469
|
const id = typeof props.id === "string" ? props.id : "unknown-image";
|
|
416
470
|
const src = typeof props.src === "string" ? props.src : "";
|
|
417
471
|
const alt = typeof props.alt === "string" ? props.alt : "";
|
|
418
472
|
const editable = id === selectedBlockId;
|
|
419
|
-
return /* @__PURE__ */
|
|
420
|
-
if (event.key === "Enter" || event.key === " ") onSelect(id);
|
|
421
|
-
}, children: [
|
|
422
|
-
/* @__PURE__ */ jsx4("img", { src, alt }),
|
|
423
|
-
editable ? /* @__PURE__ */ jsx4("input", { "aria-label": "\u753B\u5E03\u56FE\u7247 URL", value: src, onChange: (event) => onPropsChange(id, { src: event.currentTarget.value }, true), onClick: (event) => event.stopPropagation() }) : null
|
|
424
|
-
] });
|
|
473
|
+
return /* @__PURE__ */ jsx5(CanvasImageBlock, { id, src, alt, editable, onSelect, onPropsChange });
|
|
425
474
|
}
|
|
426
|
-
},
|
|
475
|
+
}, "core.image", resolveBlockPermissions),
|
|
427
476
|
...extensionComponents
|
|
428
477
|
}
|
|
429
478
|
};
|
|
@@ -440,10 +489,10 @@ function toEngineBlock(block, registry) {
|
|
|
440
489
|
if (!type) throw new Error(`V0.2 \u4E0D\u652F\u6301\u8F6C\u6362\u533A\u5757\u7C7B\u578B\uFF1A${block.type}`);
|
|
441
490
|
return { type, props: { id: block.id, ...block.props } };
|
|
442
491
|
}
|
|
443
|
-
function toEngineData(
|
|
492
|
+
function toEngineData(document2, registry) {
|
|
444
493
|
return {
|
|
445
|
-
root: { props: { ...
|
|
446
|
-
content:
|
|
494
|
+
root: { props: { ...document2.root } },
|
|
495
|
+
content: document2.blocks.map((block) => toEngineBlock(block, registry))
|
|
447
496
|
};
|
|
448
497
|
}
|
|
449
498
|
function fromEngineData(data, base, registry) {
|
|
@@ -530,7 +579,7 @@ function canApplyCanvasDocument(previous, next, getDefinition, policy) {
|
|
|
530
579
|
}
|
|
531
580
|
|
|
532
581
|
// src/editor/context/EditorContext.tsx
|
|
533
|
-
import { jsx as
|
|
582
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
534
583
|
function historyReducer(state, action) {
|
|
535
584
|
if (action.type === "select") return { ...state, selectedBlockId: action.id };
|
|
536
585
|
if (action.type === "device") return { ...state, device: action.device };
|
|
@@ -563,7 +612,7 @@ function defaultBlock(type, blocks, registry) {
|
|
|
563
612
|
return { id: uniqueBlockId(type, blocks), type, version: definition.version, props: definition.defaultProps, variant: definition.defaultVariant ?? "default", style: {} };
|
|
564
613
|
}
|
|
565
614
|
function EditorProvider({ initialDocument, registry, policy, loadState = "ready", sessionState = "active", session, sessionMessage, leaveWarning = "You have unsaved changes.", onDocumentChange, children }) {
|
|
566
|
-
const [history, dispatch] = useReducer(historyReducer, initialDocument, (
|
|
615
|
+
const [history, dispatch] = useReducer(historyReducer, initialDocument, (document2) => ({ document: document2, selectedBlockId: document2.blocks[0]?.id ?? null, past: [], future: [], device: "desktop", savedDocument: document2 }));
|
|
567
616
|
const historyRef = useRef3(history);
|
|
568
617
|
useEffect2(() => {
|
|
569
618
|
historyRef.current = history;
|
|
@@ -573,7 +622,7 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
|
|
|
573
622
|
const editable = (loadState === "ready" || loadState === "success") && sessionState === "active";
|
|
574
623
|
const selectedBlock = history.document.blocks.find((block) => block.id === history.selectedBlockId) ?? null;
|
|
575
624
|
const pendingDeleteBlock = history.document.blocks.find((block) => block.id === pendingDeleteBlockId) ?? null;
|
|
576
|
-
const replace = useCallback((
|
|
625
|
+
const replace = useCallback((document2, selectedBlockId) => dispatch({ type: "replace", document: document2, selectedBlockId }), []);
|
|
577
626
|
const requestCanvasSelection = useCallback((id) => {
|
|
578
627
|
if (editable) setCanvasSelectionRequest(id);
|
|
579
628
|
}, [editable]);
|
|
@@ -581,11 +630,11 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
|
|
|
581
630
|
setCanvasSelectionRequest(null);
|
|
582
631
|
dispatch({ type: "select", id });
|
|
583
632
|
}, []);
|
|
584
|
-
const updateFromCanvas = useCallback((
|
|
633
|
+
const updateFromCanvas = useCallback((document2) => {
|
|
585
634
|
const current = historyRef.current;
|
|
586
|
-
if (!editable || JSON.stringify(
|
|
587
|
-
if (!canApplyCanvasDocument(current.document,
|
|
588
|
-
replace(
|
|
635
|
+
if (!editable || JSON.stringify(document2) === JSON.stringify(current.document)) return false;
|
|
636
|
+
if (!canApplyCanvasDocument(current.document, document2, (type) => registry?.getBlock(type), policy)) return false;
|
|
637
|
+
replace(document2, current.selectedBlockId);
|
|
589
638
|
return true;
|
|
590
639
|
}, [editable, policy, registry, replace]);
|
|
591
640
|
const updateBlockProps = useCallback((id, props) => {
|
|
@@ -658,6 +707,14 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
|
|
|
658
707
|
const block = history.document.blocks.find((item) => item.id === id);
|
|
659
708
|
return editable && canDragBlock(block, registry?.getBlock(block?.type ?? ""), policy);
|
|
660
709
|
},
|
|
710
|
+
canDeleteBlock: (id) => {
|
|
711
|
+
const block = history.document.blocks.find((item) => item.id === id);
|
|
712
|
+
return editable && canDeleteBlock(block, history.document.blocks, registry?.getBlock(block?.type ?? ""), policy);
|
|
713
|
+
},
|
|
714
|
+
canDuplicateBlock: (id) => {
|
|
715
|
+
const block = history.document.blocks.find((item) => item.id === id);
|
|
716
|
+
return editable && canDuplicateBlock(block, history.document.blocks, registry?.getBlock(block?.type ?? ""), policy);
|
|
717
|
+
},
|
|
661
718
|
requestCanvasSelection,
|
|
662
719
|
confirmCanvasSelection,
|
|
663
720
|
updateFromCanvas,
|
|
@@ -733,10 +790,10 @@ function EditorProvider({ initialDocument, registry, policy, loadState = "ready"
|
|
|
733
790
|
redo: () => {
|
|
734
791
|
if (editable) dispatch({ type: "redo" });
|
|
735
792
|
},
|
|
736
|
-
markSaved: (
|
|
793
|
+
markSaved: (document2 = historyRef.current.document) => dispatch({ type: "saved", document: document2 })
|
|
737
794
|
};
|
|
738
795
|
}, [canvasSelectionRequest, confirmCanvasSelection, editable, history, isDirty, loadState, pendingDeleteBlock, pendingDeleteBlockId, policy, registry, replace, requestCanvasSelection, selectedBlock, session, sessionMessage, sessionState, updateBlockPresentation, updateBlockProps, updateFromCanvas]);
|
|
739
|
-
return /* @__PURE__ */
|
|
796
|
+
return /* @__PURE__ */ jsx6(EditorContext.Provider, { value, children });
|
|
740
797
|
}
|
|
741
798
|
function useEditorContext() {
|
|
742
799
|
const context = useContext(EditorContext);
|
|
@@ -746,18 +803,18 @@ function useEditorContext() {
|
|
|
746
803
|
|
|
747
804
|
// src/editor/components/PageStatusCard.tsx
|
|
748
805
|
import { Badge as Badge2, InlineStack as InlineStack2, Text as Text2 } from "@shopify/polaris";
|
|
749
|
-
import { jsx as
|
|
806
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
750
807
|
function PageStatusCard({ status, sessionState }) {
|
|
751
808
|
const publicationTone = status.publicationStatus === "published" ? "success" : "attention";
|
|
752
809
|
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;
|
|
753
|
-
return /* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
755
|
-
/* @__PURE__ */
|
|
756
|
-
status.draftLabel ? /* @__PURE__ */
|
|
757
|
-
status.publishedVersionLabel ? /* @__PURE__ */
|
|
758
|
-
sessionLabel ? /* @__PURE__ */
|
|
810
|
+
return /* @__PURE__ */ jsxs5("div", { className: "pb-page-status-card", "data-testid": "page-status-card", "data-publication-status": status.publicationStatus, "data-session-state": sessionState, children: [
|
|
811
|
+
/* @__PURE__ */ jsxs5(InlineStack2, { gap: "150", blockAlign: "center", wrap: true, children: [
|
|
812
|
+
/* @__PURE__ */ jsx7(Badge2, { tone: publicationTone, children: status.publicationStatus === "published" ? "\u7EBF\u4E0A" : "\u672A\u53D1\u5E03" }),
|
|
813
|
+
status.draftLabel ? /* @__PURE__ */ jsx7(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.draftLabel }) : null,
|
|
814
|
+
status.publishedVersionLabel ? /* @__PURE__ */ jsx7(Text2, { as: "span", variant: "bodySm", tone: "subdued", children: status.publishedVersionLabel }) : null,
|
|
815
|
+
sessionLabel ? /* @__PURE__ */ jsx7(Badge2, { tone: sessionState === "lost" ? "critical" : "attention", children: sessionLabel }) : null
|
|
759
816
|
] }),
|
|
760
|
-
status.lastSavedAt ? /* @__PURE__ */
|
|
817
|
+
status.lastSavedAt ? /* @__PURE__ */ jsxs5(Text2, { as: "p", variant: "bodySm", tone: "subdued", children: [
|
|
761
818
|
"\u6700\u540E\u4FDD\u5B58\uFF1A",
|
|
762
819
|
status.lastSavedAt
|
|
763
820
|
] }) : null
|
|
@@ -774,6 +831,7 @@ var messages = {
|
|
|
774
831
|
undo: "\u64A4\u9500",
|
|
775
832
|
redo: "\u6062\u590D",
|
|
776
833
|
duplicate: "\u590D\u5236",
|
|
834
|
+
duplicateUnavailable: "\u6B64\u533A\u5757\u5DF2\u8FBE\u5230\u5141\u8BB8\u6570\u91CF\u4E0A\u9650\uFF0C\u6216\u590D\u5236\u64CD\u4F5C\u5DF2\u88AB\u7981\u7528\u3002",
|
|
777
835
|
remove: "\u5220\u9664",
|
|
778
836
|
confirmDeleteTitle: "\u786E\u8BA4\u5220\u9664\u533A\u5757\uFF1F",
|
|
779
837
|
confirmDeleteMessage: "\u5220\u9664\u540E\u53EF\u4F7F\u7528\u64A4\u9500\u6062\u590D\u3002",
|
|
@@ -838,6 +896,7 @@ var messages = {
|
|
|
838
896
|
undo: "Undo",
|
|
839
897
|
redo: "Redo",
|
|
840
898
|
duplicate: "Duplicate",
|
|
899
|
+
duplicateUnavailable: "This block has reached its limit or duplication is disabled.",
|
|
841
900
|
remove: "Delete",
|
|
842
901
|
confirmDeleteTitle: "Delete this block?",
|
|
843
902
|
confirmDeleteMessage: "You can restore it with Undo after deleting.",
|
|
@@ -900,17 +959,197 @@ function createAdminI18n(locale) {
|
|
|
900
959
|
return { locale: resolvedLocale, t: (key) => messages[resolvedLocale][key] };
|
|
901
960
|
}
|
|
902
961
|
|
|
962
|
+
// src/editor/shell/CanvasLibraryDropTarget.tsx
|
|
963
|
+
import { useEffect as useEffect3, useRef as useRef4, useState as useState3 } from "react";
|
|
964
|
+
|
|
903
965
|
// src/editor/shell/drop-position.ts
|
|
904
966
|
function nearestBlockIdAtY(blocks, y) {
|
|
905
967
|
return blocks.find((block) => y < block.top + block.height / 2)?.id;
|
|
906
968
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
969
|
+
|
|
970
|
+
// src/editor/shell/CanvasLibraryDropTarget.tsx
|
|
971
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
972
|
+
function uniqueBlocks(blocks) {
|
|
973
|
+
const seen = /* @__PURE__ */ new Set();
|
|
974
|
+
return blocks.filter((block) => !seen.has(block.id) && seen.add(block.id));
|
|
975
|
+
}
|
|
976
|
+
function scrollableAncestors(start) {
|
|
977
|
+
const result = [];
|
|
978
|
+
let node = start;
|
|
979
|
+
while (node) {
|
|
980
|
+
const style = window.getComputedStyle(node);
|
|
981
|
+
if (["auto", "scroll", "overlay"].includes(style.overflowY) && node.scrollHeight > node.clientHeight) result.push(node);
|
|
982
|
+
node = node.parentElement;
|
|
983
|
+
}
|
|
984
|
+
return result;
|
|
985
|
+
}
|
|
986
|
+
function scrollDelta(pointerY, rect, elapsedMs) {
|
|
987
|
+
const top = Math.max(0, rect.top);
|
|
988
|
+
const bottom = Math.min(window.innerHeight, rect.bottom);
|
|
989
|
+
const height = bottom - top;
|
|
990
|
+
if (height <= 0 || pointerY < top || pointerY > bottom) return 0;
|
|
991
|
+
const edge = Math.min(96, Math.max(36, height * 0.24));
|
|
992
|
+
const speed = 900;
|
|
993
|
+
if (pointerY < top + edge) return -speed * Math.min(1, (top + edge - pointerY) / edge) * elapsedMs / 1e3;
|
|
994
|
+
if (pointerY > bottom - edge) return speed * Math.min(1, (pointerY - (bottom - edge)) / edge) * elapsedMs / 1e3;
|
|
995
|
+
return 0;
|
|
996
|
+
}
|
|
997
|
+
function scrollElement(element, delta) {
|
|
998
|
+
const canScroll = delta < 0 ? element.scrollTop > 0 : element.scrollTop < element.scrollHeight - element.clientHeight;
|
|
999
|
+
if (!delta || !canScroll) return false;
|
|
1000
|
+
if (typeof element.scrollBy === "function") element.scrollBy({ top: delta, behavior: "auto" });
|
|
1001
|
+
else element.scrollTop += delta;
|
|
1002
|
+
return true;
|
|
1003
|
+
}
|
|
1004
|
+
function CanvasLibraryDropTarget({ frameRef, blocks, label, onDrop, onCancel, adminLocale }) {
|
|
1005
|
+
const targetRef = useRef4(null);
|
|
1006
|
+
const latestPreview = useRef4(null);
|
|
1007
|
+
const dragging = useRef4(false);
|
|
1008
|
+
const completed = useRef4(false);
|
|
1009
|
+
const pointerY = useRef4(0);
|
|
1010
|
+
const animationFrame = useRef4(null);
|
|
1011
|
+
const lastFrameTime = useRef4(null);
|
|
1012
|
+
const callbacks = useRef4({ onDrop, onCancel });
|
|
1013
|
+
useEffect3(() => {
|
|
1014
|
+
callbacks.current = { onDrop, onCancel };
|
|
1015
|
+
}, [onDrop, onCancel]);
|
|
1016
|
+
const orderedBlocks = uniqueBlocks(blocks);
|
|
1017
|
+
const [preview, setPreview] = useState3(null);
|
|
1018
|
+
const english = adminLocale?.toLowerCase().startsWith("en") ?? false;
|
|
1019
|
+
const stopAutoScroll = () => {
|
|
1020
|
+
if (animationFrame.current !== null) window.cancelAnimationFrame(animationFrame.current);
|
|
1021
|
+
animationFrame.current = null;
|
|
1022
|
+
lastFrameTime.current = null;
|
|
1023
|
+
};
|
|
1024
|
+
const clear = (cancel) => {
|
|
1025
|
+
stopAutoScroll();
|
|
1026
|
+
dragging.current = false;
|
|
1027
|
+
latestPreview.current = null;
|
|
1028
|
+
setPreview(null);
|
|
1029
|
+
if (cancel) {
|
|
1030
|
+
completed.current = true;
|
|
1031
|
+
callbacks.current.onCancel();
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
const calculatePreview = (clientY) => {
|
|
1035
|
+
const target = targetRef.current;
|
|
1036
|
+
const frame = frameRef.current;
|
|
1037
|
+
if (!target || !frame) return null;
|
|
1038
|
+
const targetRect = target.getBoundingClientRect();
|
|
1039
|
+
const frameRect = frame.getBoundingClientRect();
|
|
1040
|
+
const iframe = frame.querySelector("iframe");
|
|
1041
|
+
const iframeDocument = iframe?.contentDocument;
|
|
1042
|
+
const root = iframeDocument ?? frame;
|
|
1043
|
+
const allowed = new Set(orderedBlocks.map((block) => block.id));
|
|
1044
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1045
|
+
const iframeRect = iframeDocument ? iframe?.getBoundingClientRect() : void 0;
|
|
1046
|
+
const scaleY = iframe && iframeRect && iframeRect.height > 0 && iframe.clientHeight > 0 ? iframeRect.height / iframe.clientHeight : 1;
|
|
1047
|
+
const elements = Array.from(root.querySelectorAll("[data-page-document-block-id], [data-puck-dnd]"));
|
|
1048
|
+
for (const element of elements) {
|
|
1049
|
+
const id = element.dataset.pageDocumentBlockId ?? element.dataset.puckDnd;
|
|
1050
|
+
if (!id || !allowed.has(id) || byId.has(id) || element.closest('[aria-hidden="true"], [class*="DragOverlay"]')) continue;
|
|
1051
|
+
const rect = element.getBoundingClientRect();
|
|
1052
|
+
byId.set(id, { id, top: iframeRect ? iframeRect.top + rect.top * scaleY : rect.top, height: iframeRect ? rect.height * scaleY : rect.height });
|
|
1053
|
+
}
|
|
1054
|
+
const candidates = orderedBlocks.flatMap((block) => byId.get(block.id) ?? []);
|
|
1055
|
+
const beforeId = nearestBlockIdAtY(candidates, clientY);
|
|
1056
|
+
const markerCandidate = beforeId ? candidates.find((candidate) => candidate.id === beforeId) : candidates.at(-1);
|
|
1057
|
+
const markerViewportTop = markerCandidate ? beforeId ? markerCandidate.top : markerCandidate.top + markerCandidate.height : Math.min(frameRect.bottom, Math.max(frameRect.top, clientY));
|
|
1058
|
+
return { beforeId, markerTop: Math.max(0, markerViewportTop - targetRect.top), markerLeft: Math.max(0, frameRect.left - targetRect.left), markerWidth: frameRect.width };
|
|
1059
|
+
};
|
|
1060
|
+
const updatePreview = (clientY) => {
|
|
1061
|
+
pointerY.current = clientY;
|
|
1062
|
+
const next = calculatePreview(clientY);
|
|
1063
|
+
const previous = latestPreview.current;
|
|
1064
|
+
latestPreview.current = next;
|
|
1065
|
+
if (previous?.beforeId !== next?.beforeId || previous?.markerTop !== next?.markerTop || previous?.markerLeft !== next?.markerLeft || previous?.markerWidth !== next?.markerWidth) setPreview(next);
|
|
1066
|
+
};
|
|
1067
|
+
const autoScroll = (timestamp) => {
|
|
1068
|
+
if (!dragging.current) return;
|
|
1069
|
+
const elapsedMs = Math.min(64, Math.max(0, timestamp - (lastFrameTime.current ?? timestamp)));
|
|
1070
|
+
lastFrameTime.current = timestamp;
|
|
1071
|
+
const stage = targetRef.current?.parentElement;
|
|
1072
|
+
if (stage) {
|
|
1073
|
+
let scrolled = false;
|
|
1074
|
+
for (const ancestor of scrollableAncestors(stage)) {
|
|
1075
|
+
const delta = scrollDelta(pointerY.current, ancestor.getBoundingClientRect(), elapsedMs);
|
|
1076
|
+
if (scrollElement(ancestor, delta)) {
|
|
1077
|
+
scrolled = true;
|
|
1078
|
+
break;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
const documentScroller = document.scrollingElement;
|
|
1082
|
+
if (!scrolled && documentScroller) {
|
|
1083
|
+
const viewport = new DOMRect(0, 0, window.innerWidth, window.innerHeight);
|
|
1084
|
+
scrollElement(documentScroller, scrollDelta(pointerY.current, viewport, elapsedMs));
|
|
1085
|
+
}
|
|
1086
|
+
updatePreview(pointerY.current);
|
|
1087
|
+
}
|
|
1088
|
+
animationFrame.current = window.requestAnimationFrame(autoScroll);
|
|
1089
|
+
};
|
|
1090
|
+
const startAutoScroll = () => {
|
|
1091
|
+
if (animationFrame.current === null) animationFrame.current = window.requestAnimationFrame(autoScroll);
|
|
1092
|
+
};
|
|
1093
|
+
useEffect3(() => {
|
|
1094
|
+
const cancelOutside = (event) => {
|
|
1095
|
+
if (event.type === "drop" && targetRef.current?.contains(event.target)) return;
|
|
1096
|
+
if (!completed.current) clear(true);
|
|
1097
|
+
};
|
|
1098
|
+
const onKeyDown = (event) => {
|
|
1099
|
+
if (event.key === "Escape" && !completed.current) clear(true);
|
|
1100
|
+
};
|
|
1101
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1102
|
+
document.addEventListener("dragend", cancelOutside, true);
|
|
1103
|
+
document.addEventListener("drop", cancelOutside, true);
|
|
1104
|
+
return () => {
|
|
1105
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
1106
|
+
document.removeEventListener("dragend", cancelOutside, true);
|
|
1107
|
+
document.removeEventListener("drop", cancelOutside, true);
|
|
1108
|
+
stopAutoScroll();
|
|
1109
|
+
};
|
|
1110
|
+
}, []);
|
|
1111
|
+
const handleDragOver = (event) => {
|
|
1112
|
+
event.preventDefault();
|
|
1113
|
+
event.stopPropagation();
|
|
1114
|
+
event.dataTransfer.dropEffect = "copy";
|
|
1115
|
+
completed.current = false;
|
|
1116
|
+
dragging.current = true;
|
|
1117
|
+
updatePreview(event.clientY);
|
|
1118
|
+
startAutoScroll();
|
|
1119
|
+
};
|
|
1120
|
+
const handleDragLeave = (event) => {
|
|
1121
|
+
if (!event.relatedTarget || !event.currentTarget.contains(event.relatedTarget)) clear(false);
|
|
1122
|
+
};
|
|
1123
|
+
const handleDrop = (event) => {
|
|
1124
|
+
event.preventDefault();
|
|
1125
|
+
event.stopPropagation();
|
|
1126
|
+
const next = latestPreview.current ?? calculatePreview(event.clientY);
|
|
1127
|
+
completed.current = true;
|
|
1128
|
+
clear(false);
|
|
1129
|
+
callbacks.current.onDrop(next?.beforeId);
|
|
1130
|
+
};
|
|
1131
|
+
const position = preview?.beforeId ? orderedBlocks.findIndex((block) => block.id === preview.beforeId) + 1 : orderedBlocks.length + 1;
|
|
1132
|
+
const hint = english ? `Release to add ${label}${preview ? ` \xB7 position ${position}` : ""}` : `\u677E\u5F00\u4EE5\u6DFB\u52A0 ${label}${preview ? ` \xB7 \u7B2C ${position} \u4E2A\u4F4D\u7F6E` : ""}`;
|
|
1133
|
+
const style = { position: "absolute", inset: 0, zIndex: 2 };
|
|
1134
|
+
const markerLabel = english ? `Position ${position}` : `\u7B2C ${position} \u4E2A\u4F4D\u7F6E`;
|
|
1135
|
+
return /* @__PURE__ */ jsxs6("div", { ref: targetRef, className: "pb-canvas-drop-target", "data-testid": "canvas-drop-target", role: "region", "aria-label": english ? "Block drop area" : "\u533A\u5757\u6295\u653E\u533A", style, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [
|
|
1136
|
+
/* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-target__hint", "aria-live": "polite", children: hint }),
|
|
1137
|
+
preview ? /* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-marker", "aria-hidden": "true", style: { top: preview.markerTop, left: preview.markerLeft, width: preview.markerWidth }, children: /* @__PURE__ */ jsx8("span", { className: "pb-canvas-drop-marker__label", children: markerLabel }) }) : null
|
|
1138
|
+
] });
|
|
910
1139
|
}
|
|
911
1140
|
|
|
912
1141
|
// src/editor/shell/PageDocumentEditorShell.tsx
|
|
913
|
-
import { Fragment as Fragment2, jsx as
|
|
1142
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1143
|
+
function scrollCanvasBlockIntoView(blockId, frame) {
|
|
1144
|
+
const selector = `[data-page-document-block-id="${CSS.escape(blockId)}"]`;
|
|
1145
|
+
const scroll = () => {
|
|
1146
|
+
const iframe = frame?.querySelector("iframe");
|
|
1147
|
+
const element = iframe?.contentDocument?.querySelector(selector) ?? document.querySelector(selector);
|
|
1148
|
+
element?.scrollIntoView({ block: "start", inline: "nearest" });
|
|
1149
|
+
};
|
|
1150
|
+
if (typeof requestAnimationFrame === "function") requestAnimationFrame(scroll);
|
|
1151
|
+
else scroll();
|
|
1152
|
+
}
|
|
914
1153
|
var deviceLabels2 = { desktop: "desktop", tablet: "tablet", mobile: "mobile" };
|
|
915
1154
|
function blockLabel(block, registry) {
|
|
916
1155
|
return blockTypeLabel(block.type, registry);
|
|
@@ -920,12 +1159,12 @@ function blockTypeLabel(type, registry) {
|
|
|
920
1159
|
if (type === "core.image") return "\u56FE\u7247";
|
|
921
1160
|
return registry?.getBlock(type)?.label ?? type;
|
|
922
1161
|
}
|
|
923
|
-
function validateDocumentBlocks(
|
|
924
|
-
return registry ? validatePageDocumentWithRegistry(
|
|
1162
|
+
function validateDocumentBlocks(document2, registry) {
|
|
1163
|
+
return registry ? validatePageDocumentWithRegistry(document2, registry) : [];
|
|
925
1164
|
}
|
|
926
1165
|
function useEditorSession(adapter, pageId) {
|
|
927
|
-
const [managed, setManaged] =
|
|
928
|
-
|
|
1166
|
+
const [managed, setManaged] = useState4(() => adapter ? { state: "acquiring" } : { state: "active" });
|
|
1167
|
+
useEffect4(() => {
|
|
929
1168
|
let cancelled = false;
|
|
930
1169
|
let session;
|
|
931
1170
|
let heartbeatTimer;
|
|
@@ -967,27 +1206,31 @@ function useEditorSession(adapter, pageId) {
|
|
|
967
1206
|
function PageDocumentEditorShell(props) {
|
|
968
1207
|
const { sessionAdapter, initialDocument, onSessionStateChange } = props;
|
|
969
1208
|
const managedSession = useEditorSession(sessionAdapter, initialDocument.pageId);
|
|
970
|
-
|
|
1209
|
+
useEffect4(() => {
|
|
971
1210
|
onSessionStateChange?.(managedSession.state);
|
|
972
1211
|
}, [managedSession.state, onSessionStateChange]);
|
|
973
|
-
return /* @__PURE__ */
|
|
1212
|
+
return /* @__PURE__ */ jsx9(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__ */ jsx9(PageDocumentEditor, { ...props }) });
|
|
974
1213
|
}
|
|
975
1214
|
function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onPublish, draftPersistence, draftRevision: initialDraftRevision, autoSave = true, autoSaveDelayMs = 800, publishAction, assetPicker, inspectorSettings, productPicker, pageStatus, onBack, onHistory, onPreview, onAddToStore, deleteConfirmation, availableBlockTypes, appearanceControls = false }) {
|
|
976
1215
|
const editor = useEditorContext();
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
const
|
|
982
|
-
const [
|
|
983
|
-
const [
|
|
984
|
-
const [
|
|
985
|
-
const
|
|
986
|
-
const [
|
|
987
|
-
const [
|
|
988
|
-
const [
|
|
989
|
-
const [
|
|
990
|
-
const [
|
|
1216
|
+
const editorRef = useRef5(editor);
|
|
1217
|
+
useLayoutEffect(() => {
|
|
1218
|
+
editorRef.current = editor;
|
|
1219
|
+
}, [editor]);
|
|
1220
|
+
const [blockView, setBlockView] = useState4("blocks");
|
|
1221
|
+
const [leftRailOpen, setLeftRailOpen] = useState4(true);
|
|
1222
|
+
const [rightPanelOpen, setRightPanelOpen] = useState4(true);
|
|
1223
|
+
const [draggingLibraryType, setDraggingLibraryType] = useState4(null);
|
|
1224
|
+
const canvasFrameRef = useRef5(null);
|
|
1225
|
+
const [canvasMutationVersion, setCanvasMutationVersion] = useState4(0);
|
|
1226
|
+
const [canvasResetVersion, setCanvasResetVersion] = useState4(0);
|
|
1227
|
+
const [request, setRequest] = useState4("idle");
|
|
1228
|
+
const [notice, setNotice] = useState4(null);
|
|
1229
|
+
const [draftRevision, setDraftRevision] = useState4(initialDraftRevision);
|
|
1230
|
+
const [isOnline, setIsOnline] = useState4(() => typeof navigator === "undefined" || navigator.onLine);
|
|
1231
|
+
const [saveState, setSaveState] = useState4("saved");
|
|
1232
|
+
const [zoom, setZoom] = useState4("auto");
|
|
1233
|
+
const [confirmBack, setConfirmBack] = useState4(false);
|
|
991
1234
|
const i18n = createAdminI18n(adminLocale);
|
|
992
1235
|
const engineData = useMemo2(() => toEngineData(editor.document, registry), [editor.document, registry]);
|
|
993
1236
|
const canvasThemeStyle = useMemo2(() => {
|
|
@@ -1000,8 +1243,15 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1000
1243
|
if (preserveCanvasValue) setCanvasMutationVersion((version) => version + 1);
|
|
1001
1244
|
updateBlockProps(id, props);
|
|
1002
1245
|
}, [updateBlockProps]);
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1246
|
+
const resolveBlockPermissions = useCallback2(({ id, type }) => ({
|
|
1247
|
+
duplicate: editorRef.current.canDuplicateBlock(id),
|
|
1248
|
+
delete: editorRef.current.canDeleteBlock(id),
|
|
1249
|
+
drag: editorRef.current.canDragBlock(id),
|
|
1250
|
+
edit: editorRef.current.isEditable,
|
|
1251
|
+
insert: editorRef.current.canAddBlock(type)
|
|
1252
|
+
}), []);
|
|
1253
|
+
const config = useMemo2(() => createPageDocumentPuckConfig(confirmCanvasSelection, updateFromCanvasInput, selectedBlockId, registry, resolveBlockPermissions), [confirmCanvasSelection, registry, resolveBlockPermissions, selectedBlockId, updateFromCanvasInput]);
|
|
1254
|
+
useEffect4(() => {
|
|
1005
1255
|
const online = () => setIsOnline(true);
|
|
1006
1256
|
const offline = () => setIsOnline(false);
|
|
1007
1257
|
window.addEventListener("online", online);
|
|
@@ -1019,14 +1269,14 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1019
1269
|
setSaveState("offline");
|
|
1020
1270
|
return;
|
|
1021
1271
|
}
|
|
1022
|
-
const
|
|
1272
|
+
const document2 = editor.document;
|
|
1023
1273
|
setRequest("saving");
|
|
1024
1274
|
setSaveState("saving");
|
|
1025
1275
|
setNotice(null);
|
|
1026
1276
|
try {
|
|
1027
|
-
const result = draftPersistence ? await draftPersistence.saveDraft({ document, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(
|
|
1277
|
+
const result = draftPersistence ? await draftPersistence.saveDraft({ document: document2, expectedRevision: draftRevision, session: editor.session }) : await onSave?.(document2);
|
|
1028
1278
|
if (result?.revision !== void 0) setDraftRevision(result.revision);
|
|
1029
|
-
editor.markSaved(
|
|
1279
|
+
editor.markSaved(document2);
|
|
1030
1280
|
setSaveState("saved");
|
|
1031
1281
|
} catch {
|
|
1032
1282
|
setNotice("saveFailed");
|
|
@@ -1035,7 +1285,7 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1035
1285
|
setRequest("idle");
|
|
1036
1286
|
}
|
|
1037
1287
|
}, [canPersistDraft, draftPersistence, draftRevision, editor, isOnline, onSave, request, validationIssues.length]);
|
|
1038
|
-
|
|
1288
|
+
useEffect4(() => {
|
|
1039
1289
|
if (!autoSave || !canPersistDraft || !editor.isDirty || resolvedSaveState !== "dirty" && resolvedSaveState !== "failed" || !isOnline || request !== "idle" || validationIssues.length > 0 || editor.sessionState !== "active") return;
|
|
1040
1290
|
const timer = window.setTimeout(() => {
|
|
1041
1291
|
void save();
|
|
@@ -1044,13 +1294,13 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1044
1294
|
}, [autoSave, autoSaveDelayMs, canPersistDraft, editor.isDirty, editor.sessionState, isOnline, request, resolvedSaveState, save, validationIssues.length]);
|
|
1045
1295
|
const publish = async () => {
|
|
1046
1296
|
if (!publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline) return;
|
|
1047
|
-
const
|
|
1297
|
+
const document2 = editor.document;
|
|
1048
1298
|
setRequest("publishing");
|
|
1049
1299
|
setNotice(null);
|
|
1050
1300
|
try {
|
|
1051
|
-
if (publishAction) await publishAction.publish({ document, expectedRevision: draftRevision, session: editor.session, validationIssues });
|
|
1052
|
-
else await onPublish?.(
|
|
1053
|
-
editor.markSaved(
|
|
1301
|
+
if (publishAction) await publishAction.publish({ document: document2, expectedRevision: draftRevision, session: editor.session, validationIssues });
|
|
1302
|
+
else await onPublish?.(document2);
|
|
1303
|
+
editor.markSaved(document2);
|
|
1054
1304
|
setSaveState("saved");
|
|
1055
1305
|
setNotice("published");
|
|
1056
1306
|
} catch {
|
|
@@ -1059,40 +1309,14 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1059
1309
|
setRequest("idle");
|
|
1060
1310
|
}
|
|
1061
1311
|
};
|
|
1062
|
-
if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */
|
|
1063
|
-
if (editor.sessionState !== "active") return /* @__PURE__ */
|
|
1312
|
+
if (editor.loadState !== "ready" && editor.loadState !== "success") return /* @__PURE__ */ jsx9(EditorStatus, { state: editor.loadState });
|
|
1313
|
+
if (editor.sessionState !== "active") return /* @__PURE__ */ jsx9(EditorSessionStatus, { state: editor.sessionState, message: editor.sessionMessage });
|
|
1064
1314
|
const allBlockTypes = ["core.text", "core.image", ...registry?.blocks.map((block) => block.type) ?? []];
|
|
1065
1315
|
const blockTypes = availableBlockTypes ? allBlockTypes.filter((type) => availableBlockTypes.includes(type)) : allBlockTypes;
|
|
1066
1316
|
const addFromLibrary = (type, beforeId) => {
|
|
1067
1317
|
const id = editor.addBlock(type, beforeId);
|
|
1068
1318
|
if (id) editor.requestCanvasSelection(id);
|
|
1069
1319
|
};
|
|
1070
|
-
const getDropBeforeId = (clientY) => {
|
|
1071
|
-
const frame = canvasFrameRef.current;
|
|
1072
|
-
if (!frame) return void 0;
|
|
1073
|
-
const iframe2 = frame.querySelector("iframe");
|
|
1074
|
-
const root = iframe2?.contentDocument ?? frame;
|
|
1075
|
-
const offsetTop = iframe2?.getBoundingClientRect().top ?? 0;
|
|
1076
|
-
const elements = Array.from(root.querySelectorAll("[data-page-document-block-id]"));
|
|
1077
|
-
const puckElements = elements.length > 0 ? elements : Array.from(root.querySelectorAll("[data-puck-dnd]"));
|
|
1078
|
-
const blockElements = puckElements.length > 0 ? puckElements : Array.from(window.document.querySelectorAll("[data-page-document-block-id], [data-puck-dnd]"));
|
|
1079
|
-
const candidates = blockElements.map((element) => ({
|
|
1080
|
-
id: element.dataset.pageDocumentBlockId ?? element.dataset.puckDnd,
|
|
1081
|
-
top: element.getBoundingClientRect().top,
|
|
1082
|
-
height: element.getBoundingClientRect().height
|
|
1083
|
-
})).filter((item) => typeof item.id === "string");
|
|
1084
|
-
const nearestId = nearestBlockIdAtY(candidates, clientY - offsetTop);
|
|
1085
|
-
if (nearestId || candidates.length > 0) return nearestId;
|
|
1086
|
-
const rect = frame.getBoundingClientRect();
|
|
1087
|
-
if (rect.height <= 0) return void 0;
|
|
1088
|
-
return blockIdAtRelativeY(editor.document.blocks.map((block) => block.id), (clientY - rect.top) / rect.height);
|
|
1089
|
-
};
|
|
1090
|
-
const dropFromLibrary = (event) => {
|
|
1091
|
-
event.preventDefault();
|
|
1092
|
-
const type = event.dataTransfer.getData("application/x-page-document-block") || draggingLibraryType;
|
|
1093
|
-
if (type && blockTypes.includes(type)) addFromLibrary(type, getDropBeforeId(event.clientY));
|
|
1094
|
-
setDraggingLibraryType(null);
|
|
1095
|
-
};
|
|
1096
1320
|
const cancelLibraryDrop = (event) => {
|
|
1097
1321
|
event.preventDefault();
|
|
1098
1322
|
event.stopPropagation();
|
|
@@ -1106,58 +1330,63 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1106
1330
|
else onBack();
|
|
1107
1331
|
};
|
|
1108
1332
|
const dismissPublishedNotice = () => setNotice((current) => current === "published" ? null : current);
|
|
1109
|
-
return /* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1333
|
+
return /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1334
|
+
/* @__PURE__ */ jsx9(Puck2, { config, data: engineData, iframe: { enabled: iframe }, dnd: { behavior: "static" }, overrides: { actionBar: CanvasSelectionActionBar }, onChange: (data) => {
|
|
1111
1335
|
if (!editor.updateFromCanvas(fromEngineData(data, editor.document, registry))) setCanvasResetVersion((version) => version + 1);
|
|
1112
|
-
}, children: /* @__PURE__ */
|
|
1113
|
-
/* @__PURE__ */
|
|
1114
|
-
/* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1117
|
-
onBack ? /* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1120
|
-
pageStatus ? /* @__PURE__ */
|
|
1336
|
+
}, children: /* @__PURE__ */ jsxs7(Puck2.Layout, { children: [
|
|
1337
|
+
/* @__PURE__ */ jsx9(CanvasSelectionBridge, { data: engineData, requestedBlockId: editor.canvasSelectionRequest, onCanvasSelected: confirmCanvasSelection, canvasMutationVersion, canvasResetVersion, permissionsVersion: `${editor.isEditable}:${editor.document.blocks.map((block) => `${block.id}:${block.type}`).join("|")}` }),
|
|
1338
|
+
/* @__PURE__ */ jsxs7("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: [
|
|
1339
|
+
/* @__PURE__ */ jsx9("header", { className: "pb-header", children: /* @__PURE__ */ jsxs7("div", { className: "pb-header-content", children: [
|
|
1340
|
+
/* @__PURE__ */ jsxs7("div", { className: "pb-header-title-group", children: [
|
|
1341
|
+
onBack ? /* @__PURE__ */ jsx9(Button2, { variant: "tertiary", onClick: requestBack, children: i18n.t("back") }) : null,
|
|
1342
|
+
/* @__PURE__ */ jsxs7("div", { className: "pb-page-title", children: [
|
|
1343
|
+
/* @__PURE__ */ jsx9(Text3, { as: "h1", variant: "headingSm", children: editor.document.settings.seoTitle ?? editor.document.pageId }),
|
|
1344
|
+
pageStatus ? /* @__PURE__ */ jsx9(PageStatusCard, { status: pageStatus, sessionState: editor.sessionState }) : null
|
|
1121
1345
|
] })
|
|
1122
1346
|
] }),
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
/* @__PURE__ */
|
|
1347
|
+
/* @__PURE__ */ jsx9("div", { className: "pb-header-controls", children: /* @__PURE__ */ jsxs7("div", { className: "pb-header-device-toolbar", children: [
|
|
1348
|
+
/* @__PURE__ */ jsx9(ButtonGroup2, { variant: "segmented", children: Object.keys(deviceLabels2).map((device) => /* @__PURE__ */ jsx9(Button2, { pressed: editor.device === device, onClick: () => editor.setDevice(device), children: i18n.t(deviceLabels2[device]) }, device)) }),
|
|
1349
|
+
/* @__PURE__ */ jsx9("div", { className: "pb-zoom-control", children: /* @__PURE__ */ jsx9(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) }) })
|
|
1126
1350
|
] }) }),
|
|
1127
|
-
/* @__PURE__ */
|
|
1128
|
-
/* @__PURE__ */
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
resolvedSaveState === "saved" ? null : /* @__PURE__ */
|
|
1131
|
-
onHistory ? /* @__PURE__ */
|
|
1132
|
-
onPreview ? /* @__PURE__ */
|
|
1133
|
-
onAddToStore ? /* @__PURE__ */
|
|
1134
|
-
/* @__PURE__ */
|
|
1351
|
+
/* @__PURE__ */ jsx9("div", { className: "pb-header-actions", children: /* @__PURE__ */ jsxs7(InlineStack3, { gap: "150", blockAlign: "center", wrap: true, children: [
|
|
1352
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("undo"), icon: UndoIcon2, variant: "tertiary", disabled: !editor.actionState.canUndo, onClick: editor.undo }),
|
|
1353
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("redo"), icon: RedoIcon2, variant: "tertiary", disabled: !editor.actionState.canRedo, onClick: editor.redo }),
|
|
1354
|
+
resolvedSaveState === "saved" ? null : /* @__PURE__ */ jsx9(Badge3, { tone: saveBadgeTone, children: saveBadgeLabel }),
|
|
1355
|
+
onHistory ? /* @__PURE__ */ jsx9("span", { "data-editor-history": "", children: /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle", onClick: () => onHistory({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("history") }) }) : null,
|
|
1356
|
+
onPreview ? /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle" || !isOnline, onClick: () => void onPreview({ document: editor.document, draftRevision, session: editor.session }), children: i18n.t("preview") }) : null,
|
|
1357
|
+
onAddToStore ? /* @__PURE__ */ jsx9(Button2, { disabled: request !== "idle" || pageStatus?.publicationStatus === "unpublished", onClick: () => void onAddToStore({ document: editor.document, session: editor.session }), children: i18n.t("addToStore") }) : null,
|
|
1358
|
+
/* @__PURE__ */ jsx9(Button2, { variant: "primary", disabled: !publishAction && !onPublish || request !== "idle" || validationIssues.length > 0 || !isOnline, onClick: () => void publish(), children: request === "publishing" ? i18n.t("publishing") : i18n.t("publish") })
|
|
1135
1359
|
] }) })
|
|
1136
1360
|
] }) }),
|
|
1137
|
-
editor.loadState === "success" ? /* @__PURE__ */
|
|
1138
|
-
notice && notice !== "published" ? /* @__PURE__ */
|
|
1139
|
-
validationIssues.length > 0 ? /* @__PURE__ */
|
|
1361
|
+
editor.loadState === "success" ? /* @__PURE__ */ jsx9(Banner, { tone: "success", children: i18n.t("success") }) : null,
|
|
1362
|
+
notice && notice !== "published" ? /* @__PURE__ */ jsx9("div", { className: `pb-editor-notice pb-editor-notice--${notice}`, "data-testid": "editor-notice", "data-notice": notice, children: /* @__PURE__ */ jsx9(Banner, { tone: "critical", children: i18n.t(notice) }) }) : null,
|
|
1363
|
+
validationIssues.length > 0 ? /* @__PURE__ */ jsx9(Banner, { tone: "critical", title: "\u533A\u5757\u5C5E\u6027\u672A\u901A\u8FC7\u6821\u9A8C", children: /* @__PURE__ */ jsx9("ul", { children: validationIssues.map((issue) => /* @__PURE__ */ jsxs7("li", { children: [
|
|
1140
1364
|
issue.path,
|
|
1141
1365
|
": ",
|
|
1142
1366
|
issue.message
|
|
1143
1367
|
] }, `${issue.path}-${issue.message}`)) }) }) : null,
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
/* @__PURE__ */
|
|
1146
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsxs7("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: [
|
|
1369
|
+
/* @__PURE__ */ jsxs7("nav", { className: "pb-tool-rail", "aria-label": "\u7F16\u8F91\u5668\u5DE5\u5177", children: [
|
|
1370
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("blocks"), icon: LayoutSectionIcon2, pressed: blockView === "blocks" && leftRailOpen, variant: "tertiary", onClick: () => {
|
|
1147
1371
|
setBlockView("blocks");
|
|
1148
1372
|
setLeftRailOpen(true);
|
|
1149
1373
|
} }),
|
|
1150
|
-
/* @__PURE__ */
|
|
1374
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("outline"), icon: MenuIcon2, pressed: blockView === "outline" && leftRailOpen, variant: "tertiary", onClick: () => {
|
|
1151
1375
|
setBlockView("outline");
|
|
1152
1376
|
setLeftRailOpen(true);
|
|
1153
1377
|
} })
|
|
1154
1378
|
] }),
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
/* @__PURE__ */
|
|
1157
|
-
/* @__PURE__ */
|
|
1158
|
-
/* @__PURE__ */
|
|
1379
|
+
/* @__PURE__ */ jsxs7("aside", { className: `pb-left-panel${leftRailOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u533A\u5757", children: [
|
|
1380
|
+
/* @__PURE__ */ jsxs7(InlineStack3, { align: "space-between", blockAlign: "center", children: [
|
|
1381
|
+
/* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingSm", children: blockView === "blocks" ? i18n.t("blocks") : i18n.t("outline") }),
|
|
1382
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("collapseLeft"), icon: XIcon2, variant: "tertiary", onClick: () => setLeftRailOpen(false) })
|
|
1159
1383
|
] }),
|
|
1160
|
-
blockView === "blocks" ? /* @__PURE__ */
|
|
1384
|
+
blockView === "blocks" ? /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "blocks-view", "aria-label": "\u533A\u5757\u7C7B\u578B\u5E93", role: "list", onDrop: cancelLibraryDrop, children: blockTypes.map((type) => /* @__PURE__ */ jsxs7("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" : ""}`, onClick: () => {
|
|
1385
|
+
const block = editor.document.blocks.find((item) => item.type === type);
|
|
1386
|
+
if (!block) return;
|
|
1387
|
+
editor.requestCanvasSelection(block.id);
|
|
1388
|
+
scrollCanvasBlockIntoView(block.id, canvasFrameRef.current);
|
|
1389
|
+
}, onDragStart: (event) => {
|
|
1161
1390
|
if (!editor.canAddBlock(type)) {
|
|
1162
1391
|
event.preventDefault();
|
|
1163
1392
|
return;
|
|
@@ -1166,62 +1395,68 @@ function PageDocumentEditor({ iframe = false, registry, adminLocale, onSave, onP
|
|
|
1166
1395
|
event.dataTransfer.effectAllowed = "copy";
|
|
1167
1396
|
setDraggingLibraryType(type);
|
|
1168
1397
|
}, onDragEnd: () => setDraggingLibraryType(null), children: [
|
|
1169
|
-
/* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1398
|
+
/* @__PURE__ */ jsxs7("span", { className: "pb-library-block-title", children: [
|
|
1399
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockTypeLabel(type, registry) }),
|
|
1400
|
+
/* @__PURE__ */ jsx9("span", { className: "pb-library-block-drag-hint", "aria-hidden": "true", children: /* @__PURE__ */ jsx9(DragHandleIcon2, {}) })
|
|
1172
1401
|
] }),
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */
|
|
1175
|
-
|
|
1176
|
-
|
|
1402
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: type })
|
|
1403
|
+
] }, type)) }) : editor.document.blocks.length === 0 ? /* @__PURE__ */ jsx9(Text3, { as: "p", tone: "subdued", children: i18n.t("empty") }) : /* @__PURE__ */ jsx9("div", { className: "pb-block-list", "data-testid": "outline-view", children: editor.document.blocks.map((block) => /* @__PURE__ */ jsxs7("button", { type: "button", className: `pb-document-block-row pb-document-block-row--library ${block.id === editor.selectedBlockId ? "pb-document-block-row--selected" : ""}`, "aria-pressed": block.id === editor.selectedBlockId, onClick: () => {
|
|
1404
|
+
editor.requestCanvasSelection(block.id);
|
|
1405
|
+
scrollCanvasBlockIntoView(block.id, canvasFrameRef.current);
|
|
1406
|
+
}, children: [
|
|
1407
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", fontWeight: "semibold", children: blockLabel(block, registry) }),
|
|
1408
|
+
/* @__PURE__ */ jsx9(Text3, { as: "span", variant: "bodySm", tone: "subdued", children: block.id })
|
|
1177
1409
|
] }, block.id)) })
|
|
1178
1410
|
] }),
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
/* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1182
|
-
draggingLibraryType ? /* @__PURE__ */
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
editor.selectedBlock ? /* @__PURE__ */ jsxs5("div", { className: "pb-canvas-overlay", "aria-label": `\u5DF2\u9009\u62E9 ${blockLabel(editor.selectedBlock, registry)}`, children: [
|
|
1187
|
-
/* @__PURE__ */ jsx7("span", { children: blockLabel(editor.selectedBlock, registry) }),
|
|
1188
|
-
/* @__PURE__ */ jsx7("span", { children: "Selected" })
|
|
1189
|
-
] }) : null
|
|
1411
|
+
/* @__PURE__ */ jsxs7("main", { className: "pb-canvas-area", children: [
|
|
1412
|
+
/* @__PURE__ */ jsxs7("div", { className: "pb-canvas-stage", children: [
|
|
1413
|
+
/* @__PURE__ */ jsx9("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, style: canvasThemeStyle, children: /* @__PURE__ */ jsx9(Puck2.Preview, {}) }),
|
|
1414
|
+
draggingLibraryType ? /* @__PURE__ */ jsx9(CanvasLibraryDropTarget, { frameRef: canvasFrameRef, blocks: editor.document.blocks.map((block) => ({ id: block.id, label: blockLabel(block, registry) })), label: blockTypeLabel(draggingLibraryType, registry), adminLocale, onDrop: (beforeId) => {
|
|
1415
|
+
addFromLibrary(draggingLibraryType, beforeId);
|
|
1416
|
+
setDraggingLibraryType(null);
|
|
1417
|
+
}, onCancel: () => setDraggingLibraryType(null) }) : null
|
|
1190
1418
|
] }),
|
|
1191
|
-
!leftRailOpen || !rightPanelOpen ? /* @__PURE__ */
|
|
1192
|
-
!leftRailOpen ? /* @__PURE__ */
|
|
1193
|
-
!rightPanelOpen ? /* @__PURE__ */
|
|
1419
|
+
!leftRailOpen || !rightPanelOpen ? /* @__PURE__ */ jsxs7("div", { className: "pb-collapsed-actions", children: [
|
|
1420
|
+
!leftRailOpen ? /* @__PURE__ */ jsx9(Button2, { onClick: () => setLeftRailOpen(true), children: i18n.t("expandLeft") }) : null,
|
|
1421
|
+
!rightPanelOpen ? /* @__PURE__ */ jsx9(Button2, { onClick: () => setRightPanelOpen(true), children: i18n.t("expandRight") }) : null
|
|
1194
1422
|
] }) : null
|
|
1195
1423
|
] }),
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */
|
|
1199
|
-
/* @__PURE__ */
|
|
1424
|
+
/* @__PURE__ */ jsxs7("aside", { className: `pb-right-panel${rightPanelOpen ? "" : " pb-panel--closed"}`, "aria-label": "PageDocument \u5C5E\u6027", children: [
|
|
1425
|
+
/* @__PURE__ */ jsxs7(InlineStack3, { align: "space-between", blockAlign: "center", children: [
|
|
1426
|
+
/* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingSm", children: i18n.t("properties") }),
|
|
1427
|
+
/* @__PURE__ */ jsx9(Button2, { accessibilityLabel: i18n.t("collapseRight"), icon: XIcon2, variant: "tertiary", onClick: () => setRightPanelOpen(false) })
|
|
1200
1428
|
] }),
|
|
1201
|
-
editor.selectedBlock ? /* @__PURE__ */
|
|
1202
|
-
/* @__PURE__ */
|
|
1203
|
-
/* @__PURE__ */
|
|
1204
|
-
] }) : /* @__PURE__ */
|
|
1429
|
+
editor.selectedBlock ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1430
|
+
/* @__PURE__ */ jsx9(DocumentInspector, { settings: inspectorSettings?.[editor.selectedBlock.id] ?? inspectorSettings?.[editor.selectedBlock.type], pageId: editor.document.pageId, assetPicker, productPicker, i18n, block: editor.selectedBlock, registry, disabled: !editor.actionState.canEdit, appearanceControls, onChange: (props) => editor.updateBlockProps(editor.selectedBlock.id, props), onPresentationChange: (presentation) => updateBlockPresentation(editor.selectedBlock.id, presentation) }, editor.selectedBlock.id),
|
|
1431
|
+
/* @__PURE__ */ jsx9(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) })
|
|
1432
|
+
] }) : /* @__PURE__ */ jsx9(Text3, { as: "p", tone: "subdued", children: i18n.t("selectBlock") })
|
|
1205
1433
|
] })
|
|
1206
1434
|
] }),
|
|
1207
|
-
editor.pendingDeleteBlock ? /* @__PURE__ */
|
|
1208
|
-
confirmBack ? /* @__PURE__ */
|
|
1435
|
+
editor.pendingDeleteBlock ? /* @__PURE__ */ jsx9(DeleteConfirmation, { block: editor.pendingDeleteBlock, config: deleteConfirmation, i18n, onCancel: editor.cancelDeleteBlock, onConfirm: editor.confirmDeleteBlock }) : null,
|
|
1436
|
+
confirmBack ? /* @__PURE__ */ jsx9(LeaveConfirmation, { i18n, onCancel: () => setConfirmBack(false), onConfirm: () => {
|
|
1209
1437
|
setConfirmBack(false);
|
|
1210
1438
|
onBack?.();
|
|
1211
1439
|
} }) : null
|
|
1212
1440
|
] })
|
|
1213
1441
|
] }) }),
|
|
1214
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsx9("div", { className: "pb-toast-host", ...notice === "published" ? { "data-testid": "editor-notice", "data-notice": "published" } : {}, children: /* @__PURE__ */ jsx9(Frame, { children: notice === "published" ? /* @__PURE__ */ jsx9(Toast, { content: i18n.t("publishSuccessTitle"), onDismiss: dismissPublishedNotice }) : null }) })
|
|
1215
1443
|
] });
|
|
1216
1444
|
}
|
|
1217
|
-
function
|
|
1445
|
+
function CanvasSelectionActionBar({ label, children, parentAction }) {
|
|
1446
|
+
void label;
|
|
1447
|
+
void children;
|
|
1448
|
+
void parentAction;
|
|
1449
|
+
return /* @__PURE__ */ jsx9(Fragment2, {});
|
|
1450
|
+
}
|
|
1451
|
+
function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canvasMutationVersion, canvasResetVersion, permissionsVersion }) {
|
|
1218
1452
|
const puck = usePuck();
|
|
1219
|
-
const lastSelectedId =
|
|
1220
|
-
const lastSyncedData =
|
|
1221
|
-
const lastCanvasMutationVersion =
|
|
1222
|
-
const lastCanvasResetVersion =
|
|
1453
|
+
const lastSelectedId = useRef5(null);
|
|
1454
|
+
const lastSyncedData = useRef5(null);
|
|
1455
|
+
const lastCanvasMutationVersion = useRef5(0);
|
|
1456
|
+
const lastCanvasResetVersion = useRef5(0);
|
|
1223
1457
|
const serializedData = JSON.stringify(data);
|
|
1224
|
-
|
|
1458
|
+
const refreshPermissions = puck.refreshPermissions;
|
|
1459
|
+
useEffect4(() => {
|
|
1225
1460
|
const forceReset = canvasResetVersion > lastCanvasResetVersion.current;
|
|
1226
1461
|
if (lastSyncedData.current === serializedData && !forceReset) return;
|
|
1227
1462
|
lastSyncedData.current = serializedData;
|
|
@@ -1232,13 +1467,17 @@ function CanvasSelectionBridge({ data, requestedBlockId, onCanvasSelected, canva
|
|
|
1232
1467
|
}
|
|
1233
1468
|
puck.dispatch({ type: "setData", data });
|
|
1234
1469
|
}, [canvasMutationVersion, canvasResetVersion, data, puck, serializedData]);
|
|
1235
|
-
|
|
1470
|
+
useEffect4(() => {
|
|
1471
|
+
refreshPermissions?.({}, true);
|
|
1472
|
+
}, [permissionsVersion, refreshPermissions]);
|
|
1473
|
+
useEffect4(() => {
|
|
1236
1474
|
if (!requestedBlockId) return;
|
|
1237
1475
|
const selector = puck.getSelectorForId(requestedBlockId);
|
|
1238
1476
|
if (selector) puck.dispatch({ type: "setUi", ui: { itemSelector: selector } });
|
|
1477
|
+
scrollCanvasBlockIntoView(requestedBlockId);
|
|
1239
1478
|
}, [puck, requestedBlockId]);
|
|
1240
1479
|
const selectedId = typeof puck.selectedItem?.props.id === "string" ? puck.selectedItem.props.id : null;
|
|
1241
|
-
|
|
1480
|
+
useEffect4(() => {
|
|
1242
1481
|
if (selectedId && lastSelectedId.current !== selectedId) {
|
|
1243
1482
|
lastSelectedId.current = selectedId;
|
|
1244
1483
|
onCanvasSelected(selectedId);
|
|
@@ -1250,43 +1489,53 @@ function EditorStatus({ state }) {
|
|
|
1250
1489
|
const i18n = createAdminI18n();
|
|
1251
1490
|
const tone = state === "error" ? "critical" : state === "disabled" ? "warning" : "info";
|
|
1252
1491
|
const message = state === "loading" ? i18n.t("loading") : state === "empty" ? i18n.t("empty") : state === "error" ? i18n.t("error") : i18n.t("disabled");
|
|
1253
|
-
return /* @__PURE__ */
|
|
1492
|
+
return /* @__PURE__ */ jsx9("div", { className: "pb-editor-status", "data-testid": "page-document-editor-state", "data-editor-state": state, children: /* @__PURE__ */ jsx9(Banner, { tone, title: message, children: state === "disabled" ? i18n.t("disabled") : message }) });
|
|
1254
1493
|
}
|
|
1255
1494
|
function EditorSessionStatus({ state, message }) {
|
|
1256
1495
|
const i18n = createAdminI18n();
|
|
1257
1496
|
const tone = state === "locked" ? "warning" : state === "readonly" ? "info" : "critical";
|
|
1258
1497
|
const title = state === "acquiring" ? i18n.t("acquiringLock") : state === "locked" ? i18n.t("locked") : state === "readonly" ? i18n.t("readonly") : i18n.t("lockLost");
|
|
1259
|
-
return /* @__PURE__ */
|
|
1498
|
+
return /* @__PURE__ */ jsx9("div", { className: "pb-editor-status", "data-testid": "page-document-editor-session-state", "data-editor-session-state": state, children: /* @__PURE__ */ jsx9(Banner, { tone, title, children: message ?? title }) });
|
|
1260
1499
|
}
|
|
1261
1500
|
function InspectorSection({ title, children, defaultOpen = true }) {
|
|
1262
|
-
return /* @__PURE__ */
|
|
1263
|
-
/* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1265
|
-
/* @__PURE__ */
|
|
1501
|
+
return /* @__PURE__ */ jsxs7("details", { className: "pb-inspector-section", open: defaultOpen, children: [
|
|
1502
|
+
/* @__PURE__ */ jsxs7("summary", { children: [
|
|
1503
|
+
/* @__PURE__ */ jsx9("span", { children: title }),
|
|
1504
|
+
/* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2304" })
|
|
1266
1505
|
] }),
|
|
1267
|
-
/* @__PURE__ */
|
|
1506
|
+
/* @__PURE__ */ jsx9("div", { className: "pb-inspector-section__body", children })
|
|
1268
1507
|
] });
|
|
1269
1508
|
}
|
|
1270
|
-
function InspectorTextControl({ label, value, control, disabled, onChange }) {
|
|
1509
|
+
function InspectorTextControl({ label, value, control, maxLength, disabled, onChange }) {
|
|
1271
1510
|
const stringValue = typeof value === "string" ? value : "";
|
|
1272
|
-
if (control === "color") return /* @__PURE__ */
|
|
1273
|
-
|
|
1511
|
+
if (control === "color") return /* @__PURE__ */ jsx9("input", { "aria-label": label, type: "color", value: /^#[\da-f]{6}$/i.test(stringValue) ? stringValue : "#000000", disabled, onChange: (event) => onChange(event.currentTarget.value) });
|
|
1512
|
+
const length = Array.from(stringValue).length;
|
|
1513
|
+
const field = /* @__PURE__ */ jsx9(TextField, { label, labelHidden: true, value: stringValue, onChange: (next) => onChange(maxLength === void 0 ? next : Array.from(next).slice(0, maxLength).join("")), autoComplete: "off", disabled, multiline: control === "textarea" ? 4 : false, type: control === "url" ? "url" : "text", maxLength });
|
|
1514
|
+
if (maxLength === void 0) return field;
|
|
1515
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field__limited", children: [
|
|
1516
|
+
field,
|
|
1517
|
+
/* @__PURE__ */ jsxs7("span", { className: "pb-inspector-field__limit", "aria-label": `Limit ${maxLength}`, children: [
|
|
1518
|
+
length,
|
|
1519
|
+
"/",
|
|
1520
|
+
maxLength
|
|
1521
|
+
] })
|
|
1522
|
+
] });
|
|
1274
1523
|
}
|
|
1275
1524
|
function InspectorAssetControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
|
|
1276
|
-
const [busy, setBusy] =
|
|
1277
|
-
const [error, setError] =
|
|
1278
|
-
const active =
|
|
1279
|
-
|
|
1525
|
+
const [busy, setBusy] = useState4(false);
|
|
1526
|
+
const [error, setError] = useState4(null);
|
|
1527
|
+
const active = useRef5(true);
|
|
1528
|
+
useEffect4(() => {
|
|
1280
1529
|
active.current = !disabled;
|
|
1281
1530
|
return () => {
|
|
1282
1531
|
active.current = false;
|
|
1283
1532
|
};
|
|
1284
1533
|
}, [disabled]);
|
|
1285
1534
|
const url = typeof value === "string" && validateFieldValue({ field: "", control: "asset" }, value).length === 0 ? value : "";
|
|
1286
|
-
return /* @__PURE__ */
|
|
1287
|
-
url ? /* @__PURE__ */
|
|
1288
|
-
url ? /* @__PURE__ */
|
|
1289
|
-
/* @__PURE__ */
|
|
1535
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-asset-picker", "data-testid": "asset-picker", children: [
|
|
1536
|
+
url ? /* @__PURE__ */ jsx9("img", { src: url, alt: "", className: "pb-asset-picker__preview" }) : /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("emptyAsset") }),
|
|
1537
|
+
url ? /* @__PURE__ */ jsx9(Button2, { fullWidth: true, variant: "tertiary", disabled: disabled || busy, onClick: () => onChange(""), children: i18n.t("removeAsset") }) : null,
|
|
1538
|
+
/* @__PURE__ */ jsx9(Button2, { fullWidth: true, disabled: disabled || busy || !picker, onClick: () => void (async () => {
|
|
1290
1539
|
if (!picker) return;
|
|
1291
1540
|
setBusy(true);
|
|
1292
1541
|
setError(null);
|
|
@@ -1302,20 +1551,20 @@ function InspectorAssetControl({ value, disabled, picker, pageId, blockId, i18n,
|
|
|
1302
1551
|
setBusy(false);
|
|
1303
1552
|
}
|
|
1304
1553
|
})(), children: busy ? i18n.t("selectingAsset") : i18n.t("selectAsset") }),
|
|
1305
|
-
error ? /* @__PURE__ */
|
|
1554
|
+
error ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
|
|
1306
1555
|
] });
|
|
1307
1556
|
}
|
|
1308
1557
|
function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i18n, onChange }) {
|
|
1309
1558
|
const products = parseProductReferences(value);
|
|
1310
|
-
const [busy, setBusy] =
|
|
1311
|
-
const [error, setError] =
|
|
1559
|
+
const [busy, setBusy] = useState4(false);
|
|
1560
|
+
const [error, setError] = useState4(null);
|
|
1312
1561
|
const preview = products.slice(0, 4);
|
|
1313
|
-
return /* @__PURE__ */
|
|
1314
|
-
/* @__PURE__ */
|
|
1315
|
-
preview.length ? /* @__PURE__ */
|
|
1316
|
-
/* @__PURE__ */
|
|
1562
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-product-picker", "data-testid": "product-picker", children: [
|
|
1563
|
+
/* @__PURE__ */ jsxs7("div", { className: "pb-product-picker__summary", children: [
|
|
1564
|
+
preview.length ? /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumbs", "aria-hidden": "true", children: preview.map((product, index) => /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumb", style: { zIndex: preview.length - index }, children: product.imageUrl ? /* @__PURE__ */ jsx9("img", { src: product.imageUrl, alt: "" }) : /* @__PURE__ */ jsx9("span", { className: "pb-product-picker__thumb-fallback" }) }, product.id)) }) : null,
|
|
1565
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", children: products.length ? `${products.length} ${i18n.t("productCountSuffix")}` : i18n.t("emptyProducts") })
|
|
1317
1566
|
] }),
|
|
1318
|
-
/* @__PURE__ */
|
|
1567
|
+
/* @__PURE__ */ jsx9(Button2, { fullWidth: true, icon: ProductIcon, variant: "secondary", disabled: disabled || busy || !picker, onClick: () => void (async () => {
|
|
1319
1568
|
if (!picker) return;
|
|
1320
1569
|
setBusy(true);
|
|
1321
1570
|
setError(null);
|
|
@@ -1328,47 +1577,50 @@ function InspectorProductsControl({ value, disabled, picker, pageId, blockId, i1
|
|
|
1328
1577
|
setBusy(false);
|
|
1329
1578
|
}
|
|
1330
1579
|
})(), children: busy ? i18n.t("selectingProducts") : i18n.t("selectProducts") }),
|
|
1331
|
-
error ? /* @__PURE__ */
|
|
1580
|
+
error ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: error }) : null
|
|
1332
1581
|
] });
|
|
1333
1582
|
}
|
|
1334
1583
|
function InspectorField({ name, field, value, registry, disabled, assetPicker, productPicker, pageId, blockId, i18n, onChange }) {
|
|
1335
1584
|
const label = field.label ?? name;
|
|
1336
1585
|
const issues = validateFieldValue(field, value);
|
|
1337
1586
|
const Field = registry?.getField(field.field)?.component;
|
|
1338
|
-
return /* @__PURE__ */
|
|
1339
|
-
/* @__PURE__ */
|
|
1340
|
-
/* @__PURE__ */
|
|
1341
|
-
field.description ? /* @__PURE__ */
|
|
1587
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field", "data-control": field.control ?? "custom", children: [
|
|
1588
|
+
/* @__PURE__ */ jsxs7("div", { className: "pb-inspector-field__heading", children: [
|
|
1589
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", fontWeight: "semibold", children: label }),
|
|
1590
|
+
field.description ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: field.description }) : null
|
|
1342
1591
|
] }),
|
|
1343
|
-
field.control === "products" ? /* @__PURE__ */
|
|
1344
|
-
issues.map((issue) => /* @__PURE__ */
|
|
1592
|
+
field.control === "products" ? /* @__PURE__ */ jsx9(InspectorProductsControl, { value, disabled, picker: productPicker, pageId, blockId, i18n, onChange }) : field.control === "asset" ? /* @__PURE__ */ jsx9(InspectorAssetControl, { value, disabled, picker: assetPicker, pageId, blockId, i18n, onChange }) : field.control ? /* @__PURE__ */ jsx9(InspectorTextControl, { label, value, control: field.control, maxLength: field.validation?.maxLength, disabled, onChange: (next) => onChange(next) }) : Field ? /* @__PURE__ */ jsx9(Field, { value, onChange }) : null,
|
|
1593
|
+
issues.map((issue) => /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: issue.message }, issue.message))
|
|
1345
1594
|
] });
|
|
1346
1595
|
}
|
|
1347
1596
|
function InspectorActions({ block, canDuplicate, canDelete, canMove, canMoveUp, canMoveDown, i18n, onDuplicate, onDelete, onMove }) {
|
|
1348
|
-
return /* @__PURE__ */
|
|
1349
|
-
/* @__PURE__ */
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1597
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector-actions", "aria-label": `Actions for ${block.id}`, children: [
|
|
1598
|
+
/* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
|
|
1599
|
+
/* @__PURE__ */ jsx9(Button2, { disabled: !canMove || !canMoveUp, onClick: () => onMove(-1), children: i18n.t("moveUp") }),
|
|
1600
|
+
/* @__PURE__ */ jsx9(Button2, { disabled: !canMove || !canMoveDown, onClick: () => onMove(1), children: i18n.t("moveDown") }),
|
|
1601
|
+
/* @__PURE__ */ jsx9(Button2, { disabled: !canDuplicate, accessibilityLabel: canDuplicate ? i18n.t("duplicate") : i18n.t("duplicateUnavailable"), onClick: onDuplicate, children: i18n.t("duplicate") }),
|
|
1602
|
+
/* @__PURE__ */ jsx9(Button2, { disabled: !canDelete, tone: "critical", onClick: onDelete, children: i18n.t("remove") })
|
|
1603
|
+
] }),
|
|
1604
|
+
!canDuplicate ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: i18n.t("duplicateUnavailable") }) : null
|
|
1605
|
+
] });
|
|
1354
1606
|
}
|
|
1355
1607
|
function DeleteConfirmation({ block, config, i18n, onCancel, onConfirm }) {
|
|
1356
|
-
return /* @__PURE__ */
|
|
1357
|
-
/* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
/* @__PURE__ */
|
|
1360
|
-
/* @__PURE__ */
|
|
1361
|
-
/* @__PURE__ */
|
|
1608
|
+
return /* @__PURE__ */ jsx9("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs7("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-delete-confirmation-title", children: [
|
|
1609
|
+
/* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingMd", id: "pb-delete-confirmation-title", children: config?.title ?? i18n.t("confirmDeleteTitle") }),
|
|
1610
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodyMd", children: config?.message?.(block) ?? i18n.t("confirmDeleteMessage") }),
|
|
1611
|
+
/* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
|
|
1612
|
+
/* @__PURE__ */ jsx9(Button2, { onClick: onCancel, children: config?.cancelLabel ?? i18n.t("cancel") }),
|
|
1613
|
+
/* @__PURE__ */ jsx9(Button2, { tone: "critical", onClick: onConfirm, children: config?.confirmLabel ?? i18n.t("confirm") })
|
|
1362
1614
|
] })
|
|
1363
1615
|
] }) });
|
|
1364
1616
|
}
|
|
1365
1617
|
function LeaveConfirmation({ i18n, onCancel, onConfirm }) {
|
|
1366
|
-
return /* @__PURE__ */
|
|
1367
|
-
/* @__PURE__ */
|
|
1368
|
-
/* @__PURE__ */
|
|
1369
|
-
/* @__PURE__ */
|
|
1370
|
-
/* @__PURE__ */
|
|
1371
|
-
/* @__PURE__ */
|
|
1618
|
+
return /* @__PURE__ */ jsx9("div", { className: "pb-delete-confirmation-backdrop", role: "presentation", children: /* @__PURE__ */ jsxs7("section", { className: "pb-delete-confirmation", role: "dialog", "aria-modal": "true", "aria-labelledby": "pb-leave-confirmation-title", children: [
|
|
1619
|
+
/* @__PURE__ */ jsx9(Text3, { as: "h2", variant: "headingMd", id: "pb-leave-confirmation-title", children: i18n.t("confirmLeaveTitle") }),
|
|
1620
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodyMd", children: i18n.t("leaveWarning") }),
|
|
1621
|
+
/* @__PURE__ */ jsxs7(ButtonGroup2, { children: [
|
|
1622
|
+
/* @__PURE__ */ jsx9(Button2, { onClick: onCancel, children: i18n.t("cancel") }),
|
|
1623
|
+
/* @__PURE__ */ jsx9(Button2, { tone: "critical", onClick: onConfirm, children: i18n.t("leave") })
|
|
1372
1624
|
] })
|
|
1373
1625
|
] }) });
|
|
1374
1626
|
}
|
|
@@ -1380,8 +1632,8 @@ function inspectorFieldConfig(name, field) {
|
|
|
1380
1632
|
}
|
|
1381
1633
|
var appearanceTokens = [["color.primary", "\u4E3B\u8272"], ["color.surface", "\u8868\u9762\u8272"], ["radius", "\u5706\u89D2"], ["spacing", "\u95F4\u8DDD"]];
|
|
1382
1634
|
function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n, block, registry, disabled, appearanceControls, onChange, onPresentationChange }) {
|
|
1383
|
-
const [assetError, setAssetError] =
|
|
1384
|
-
const [selectingAsset, setSelectingAsset] =
|
|
1635
|
+
const [assetError, setAssetError] = useState4(null);
|
|
1636
|
+
const [selectingAsset, setSelectingAsset] = useState4(false);
|
|
1385
1637
|
const definition = registry?.getBlock(block.type);
|
|
1386
1638
|
const groupedFields = definition ? Object.entries(definition.fields).reduce((groups, entry) => {
|
|
1387
1639
|
const [name, field] = entry;
|
|
@@ -1390,20 +1642,20 @@ function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n,
|
|
|
1390
1642
|
(groups[group] ??= []).push([name, configuredField]);
|
|
1391
1643
|
return groups;
|
|
1392
1644
|
}, {}) : {};
|
|
1393
|
-
return /* @__PURE__ */
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
/* @__PURE__ */
|
|
1397
|
-
/* @__PURE__ */
|
|
1398
|
-
/* @__PURE__ */
|
|
1645
|
+
return /* @__PURE__ */ jsxs7("div", { className: "pb-inspector", "data-testid": "document-inspector", children: [
|
|
1646
|
+
/* @__PURE__ */ jsxs7("header", { className: "pb-inspector__header", children: [
|
|
1647
|
+
/* @__PURE__ */ jsx9(Badge3, { children: block.type }),
|
|
1648
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1649
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "headingSm", children: blockLabel(block, registry) }),
|
|
1650
|
+
/* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "subdued", children: definition?.category ?? "Core block" })
|
|
1399
1651
|
] })
|
|
1400
1652
|
] }),
|
|
1401
|
-
block.type === "core.text" ? /* @__PURE__ */
|
|
1402
|
-
block.type === "core.image" ? /* @__PURE__ */
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
assetPicker ? /* @__PURE__ */
|
|
1406
|
-
/* @__PURE__ */
|
|
1653
|
+
block.type === "core.text" ? /* @__PURE__ */ jsx9(InspectorSection, { title: "Content", children: /* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (content) => onChange({ content }) }) }) : null,
|
|
1654
|
+
block.type === "core.image" ? /* @__PURE__ */ jsxs7(InspectorSection, { title: "Image", children: [
|
|
1655
|
+
/* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (src) => onChange({ src }) }),
|
|
1656
|
+
/* @__PURE__ */ jsx9(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, productPicker, pageId, blockId: block.id, i18n, onChange: (alt) => onChange({ alt }) }),
|
|
1657
|
+
assetPicker ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1658
|
+
/* @__PURE__ */ jsx9(Button2, { disabled: disabled || selectingAsset, onClick: () => void (async () => {
|
|
1407
1659
|
setSelectingAsset(true);
|
|
1408
1660
|
setAssetError(null);
|
|
1409
1661
|
try {
|
|
@@ -1415,13 +1667,13 @@ function DocumentInspector({ settings, pageId, assetPicker, productPicker, i18n,
|
|
|
1415
1667
|
setSelectingAsset(false);
|
|
1416
1668
|
}
|
|
1417
1669
|
})(), children: selectingAsset ? "\u6B63\u5728\u9009\u62E9\u7D20\u6750\u2026" : "\u9009\u62E9\u7D20\u6750" }),
|
|
1418
|
-
assetError ? /* @__PURE__ */
|
|
1670
|
+
assetError ? /* @__PURE__ */ jsx9(Text3, { as: "p", variant: "bodySm", tone: "critical", children: assetError }) : null
|
|
1419
1671
|
] }) : null
|
|
1420
1672
|
] }) : null,
|
|
1421
|
-
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */
|
|
1673
|
+
Object.entries(groupedFields).map(([group, fields]) => /* @__PURE__ */ jsx9(InspectorSection, { title: group, defaultOpen: group !== "Advanced", children: fields.map(([name, field]) => /* @__PURE__ */ jsx9(InspectorField, { name, field, value: field.persist === false ? settings?.values[name] : block.props[name], registry, disabled: disabled || field.persist === false && (!settings || Boolean(settings.disabled)), assetPicker, productPicker, pageId, blockId: block.id, i18n, onChange: (value) => field.persist === false ? settings?.onChange(name, value) : onChange({ [name]: value }) }, name)) }, group)),
|
|
1422
1674
|
Object.values(definition?.fields ?? {}).some((field) => field.persist === false) ? settings?.footer : null,
|
|
1423
|
-
appearanceControls && definition?.variants?.length ? /* @__PURE__ */
|
|
1424
|
-
appearanceControls && definition ? /* @__PURE__ */
|
|
1675
|
+
appearanceControls && definition?.variants?.length ? /* @__PURE__ */ jsx9(InspectorSection, { title: "\u5916\u89C2", children: /* @__PURE__ */ jsx9(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,
|
|
1676
|
+
appearanceControls && definition ? /* @__PURE__ */ jsx9(InspectorSection, { title: "\u6837\u5F0F\u8986\u76D6", defaultOpen: false, children: appearanceTokens.map(([token, label]) => /* @__PURE__ */ jsx9(TextField, { label, value: block.style[token] ?? "", placeholder: "\u7EE7\u627F\u9875\u9762\u6216\u6A21\u677F\u8BBE\u7F6E", autoComplete: "off", disabled, onChange: (value) => {
|
|
1425
1677
|
const style = { ...block.style };
|
|
1426
1678
|
if (value.trim()) style[token] = value;
|
|
1427
1679
|
else delete style[token];
|