@vertebr/react 0.12.0 → 0.15.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.
Files changed (29) hide show
  1. package/dist/components/admin/AdminSidebar.d.ts.map +1 -1
  2. package/dist/components/admin/AdminSidebar.js +3 -2
  3. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  4. package/dist/components/admin/VertebrAdmin.js +72 -11
  5. package/dist/components/config/DescriptionInput.d.ts +3 -1
  6. package/dist/components/config/DescriptionInput.d.ts.map +1 -1
  7. package/dist/components/config/DescriptionInput.js +2 -2
  8. package/dist/components/config/DocumentAttachmentField.d.ts +17 -0
  9. package/dist/components/config/DocumentAttachmentField.d.ts.map +1 -0
  10. package/dist/components/config/DocumentAttachmentField.js +32 -0
  11. package/dist/components/config/ImagePickerModal.d.ts +4 -1
  12. package/dist/components/config/ImagePickerModal.d.ts.map +1 -1
  13. package/dist/components/config/ImagePickerModal.js +45 -23
  14. package/dist/components/config/MarkdownDocumentModal.d.ts +19 -0
  15. package/dist/components/config/MarkdownDocumentModal.d.ts.map +1 -0
  16. package/dist/components/config/MarkdownDocumentModal.js +34 -0
  17. package/dist/components/config/form/ConfigField.d.ts.map +1 -1
  18. package/dist/components/config/form/ConfigField.js +95 -5
  19. package/dist/components/gallery/GalleryBoard.d.ts.map +1 -1
  20. package/dist/components/gallery/GalleryBoard.js +37 -5
  21. package/dist/components/ui/icons.d.ts +30 -2
  22. package/dist/components/ui/icons.d.ts.map +1 -1
  23. package/dist/components/ui/icons.js +199 -15
  24. package/dist/lib/documents.d.ts +14 -0
  25. package/dist/lib/documents.d.ts.map +1 -0
  26. package/dist/lib/documents.js +42 -0
  27. package/dist/styles/form.css +15 -0
  28. package/dist/styles/gallery.css +237 -0
  29. package/package.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AA6FF,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAwBvE"}
1
+ {"version":3,"file":"AdminSidebar.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AdminSidebar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,SAAS,CAAC;AAG7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AA+FF,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,iBAAiB,+BAwBvE"}
@@ -1,13 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from "react";
3
3
  import { AdminPoweredBy } from "./AdminPoweredBy";
4
- import { iconForNavKey } from "../ui/icons";
4
+ import { resolveNavIcon } from "../ui/icons";
5
5
  function SidebarNavItem({ item, collapsed, }) {
6
6
  const hasChildren = Boolean(item.children?.length);
7
7
  const childActive = item.children?.some((child) => child.active) ?? false;
8
8
  const sectionActive = item.active || childActive;
9
9
  const [open, setOpen] = useState(sectionActive);
10
- const Icon = item.icon ?? iconForNavKey(item.key);
10
+ const Icon = item.icon ??
11
+ resolveNavIcon({ key: item.key, label: item.label, group: item.group });
11
12
  useEffect(() => {
12
13
  if (sectionActive)
13
14
  setOpen(true);
@@ -1 +1 @@
1
- {"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAGV,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,wBAAgB,YAAY,CAAC,EAC3B,OAAsB,EACtB,QAAQ,EAAE,YAAY,EACtB,KAAuB,EACvB,aAA6B,EAC7B,eAAiC,EACjC,eAAiC,EACjC,sBAAgD,EAChD,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE,iBAAiB,+BAkuBnB"}
1
+ {"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAGV,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,wBAAgB,YAAY,CAAC,EAC3B,OAAsB,EACtB,QAAQ,EAAE,YAAY,EACtB,KAAuB,EACvB,aAA6B,EAC7B,eAAiC,EACjC,eAAiC,EACjC,sBAAgD,EAChD,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE,iBAAiB,+BA+xBnB"}
@@ -17,7 +17,7 @@ import { sidebarGroups } from "./sidebar";
17
17
  import { clearStored, readStored, writeStored } from "./storage";
18
18
  import { SaveBarProvider } from "../../hooks/useSaveBar";
19
19
  import { ToastProvider } from "../../hooks/useToast";
20
- import { iconForNavKey } from "../ui/icons";
20
+ import { resolveNavIcon } from "../ui/icons";
21
21
  export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, title = "Control plane", envStorageKey = "vertebr:env", tokenStorageKey = "vertebr:token", themeStorageKey = "vertebr:theme", metricsRangeStorageKey = "vertebr:metrics-range", getAccessToken, authUserLabel, onSignOut, }) {
22
22
  const basePath = basePathProp ?? adminBaseFromApiBase(apiBase);
23
23
  const { theme, setTheme } = useVertebrTheme(themeStorageKey);
@@ -457,7 +457,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
457
457
  key: "overview",
458
458
  group: catalog.metrics.group ?? "Analytics",
459
459
  label: catalog.metrics.label,
460
- icon: iconForNavKey("overview"),
460
+ icon: resolveNavIcon({
461
+ key: "overview",
462
+ id: catalog.metrics.id,
463
+ label: catalog.metrics.label,
464
+ group: catalog.metrics.group,
465
+ icon: catalog.metrics.icon,
466
+ }),
461
467
  active: selection?.kind === "overview",
462
468
  onSelect: () => setSelection({ kind: "overview" }),
463
469
  });
@@ -467,7 +473,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
467
473
  key: "audit",
468
474
  group: catalog.audit.group ?? "Analytics",
469
475
  label: catalog.audit.label,
470
- icon: iconForNavKey("audit"),
476
+ icon: resolveNavIcon({
477
+ key: "audit",
478
+ id: catalog.audit.id,
479
+ label: catalog.audit.label,
480
+ group: catalog.audit.group,
481
+ icon: catalog.audit.icon,
482
+ }),
471
483
  active: selection?.kind === "audit",
472
484
  onSelect: () => setSelection({ kind: "audit" }),
473
485
  });
@@ -477,7 +489,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
477
489
  key: `resource:${resource.id}`,
478
490
  group: resource.group ?? "Resources",
479
491
  label: resource.label,
480
- icon: iconForNavKey(`resource:${resource.id}`),
492
+ icon: resolveNavIcon({
493
+ key: `resource:${resource.id}`,
494
+ id: resource.id,
495
+ label: resource.label,
496
+ group: resource.group,
497
+ icon: resource.icon,
498
+ }),
481
499
  active: selection?.kind === "resource" && selection.id === resource.id,
482
500
  onSelect: () => setSelection({ kind: "resource", id: resource.id }),
483
501
  });
@@ -487,7 +505,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
487
505
  key: "gallery",
488
506
  group: catalog.gallery.group ?? "Resources",
489
507
  label: catalog.gallery.label,
490
- icon: iconForNavKey("gallery"),
508
+ icon: resolveNavIcon({
509
+ key: "gallery",
510
+ id: catalog.gallery.id,
511
+ label: catalog.gallery.label,
512
+ group: catalog.gallery.group,
513
+ icon: catalog.gallery.icon,
514
+ }),
491
515
  active: selection?.kind === "gallery",
492
516
  onSelect: () => setSelection({ kind: "gallery" }),
493
517
  });
@@ -497,7 +521,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
497
521
  key: "fonts",
498
522
  group: catalog.fontLibrary.group ?? "Resources",
499
523
  label: catalog.fontLibrary.label,
500
- icon: iconForNavKey("fonts"),
524
+ icon: resolveNavIcon({
525
+ key: "fonts",
526
+ id: catalog.fontLibrary.id,
527
+ label: catalog.fontLibrary.label,
528
+ group: catalog.fontLibrary.group,
529
+ icon: catalog.fontLibrary.icon,
530
+ }),
501
531
  active: selection?.kind === "fontLibrary",
502
532
  onSelect: () => setSelection({ kind: "fontLibrary" }),
503
533
  });
@@ -508,7 +538,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
508
538
  key: "emails",
509
539
  group: catalog.emails.group ?? "Growth",
510
540
  label: catalog.emails.label,
511
- icon: iconForNavKey("emails"),
541
+ icon: resolveNavIcon({
542
+ key: "emails",
543
+ id: catalog.emails.id,
544
+ label: catalog.emails.label,
545
+ group: catalog.emails.group,
546
+ icon: catalog.emails.icon,
547
+ }),
512
548
  active: selection?.kind === "emails",
513
549
  onSelect: () => setSelection({ kind: "emails", tab: "compose" }),
514
550
  children: [
@@ -538,7 +574,12 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
538
574
  key: "addons:overview",
539
575
  group: "Add-ons",
540
576
  label: "Browse add-ons",
541
- icon: iconForNavKey("addons:overview"),
577
+ icon: resolveNavIcon({
578
+ key: "addons:overview",
579
+ id: "addons",
580
+ label: "Browse add-ons",
581
+ group: "Add-ons",
582
+ }),
542
583
  active: selection?.kind === "addons",
543
584
  onSelect: () => setSelection({ kind: "addons" }),
544
585
  });
@@ -548,7 +589,15 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
548
589
  key: `addon:${addon.id}`,
549
590
  group: addon.group ?? "Add-ons",
550
591
  label: addon.label,
551
- icon: iconForNavKey(`addon:${addon.id}`),
592
+ icon: resolveNavIcon({
593
+ key: `addon:${addon.id}`,
594
+ id: addon.id,
595
+ label: addon.label,
596
+ group: addon.group,
597
+ category: addon.category,
598
+ section: "addon",
599
+ icon: addon.icon,
600
+ }),
552
601
  active: selection?.kind === "addon" && selection.id === addon.id,
553
602
  onSelect: () => setSelection({ kind: "addon", id: addon.id }),
554
603
  });
@@ -559,7 +608,14 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
559
608
  key: `config:${config.id}`,
560
609
  group: config.group ?? fallback,
561
610
  label: config.label,
562
- icon: iconForNavKey(`config:${config.id}`),
611
+ icon: resolveNavIcon({
612
+ key: `config:${config.id}`,
613
+ id: config.id,
614
+ label: config.label,
615
+ group: config.group,
616
+ section: config.section === "page" ? "page" : "config",
617
+ icon: config.icon,
618
+ }),
563
619
  active: selection?.kind === "config" && selection.id === config.id,
564
620
  onSelect: () => setSelection({ kind: "config", id: config.id }),
565
621
  });
@@ -569,7 +625,12 @@ export function VertebrAdmin({ apiBase = "/admin/api", basePath: basePathProp, t
569
625
  key: "sync",
570
626
  group: "Settings",
571
627
  label: "Sync environments",
572
- icon: iconForNavKey("sync"),
628
+ icon: resolveNavIcon({
629
+ key: "sync",
630
+ id: "sync",
631
+ label: "Sync environments",
632
+ group: "Settings",
633
+ }),
573
634
  active: selection?.kind === "sync",
574
635
  onSelect: () => setSelection({ kind: "sync" }),
575
636
  });
@@ -13,6 +13,8 @@ export type DescriptionInputProps = {
13
13
  onBlur?: () => void;
14
14
  onChange: (next: string) => void;
15
15
  inputRef?: React.Ref<HTMLElement>;
16
+ /** Opens a document-style modal for focused policy editing. */
17
+ onOpenDocumentView?: () => void;
16
18
  };
17
- export declare function DescriptionInput({ id, name, value, readOnly, placeholder, recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, }: DescriptionInputProps): import("react").JSX.Element;
19
+ export declare function DescriptionInput({ id, name, value, readOnly, placeholder, recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, onOpenDocumentView, }: DescriptionInputProps): import("react").JSX.Element;
18
20
  //# sourceMappingURL=DescriptionInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DescriptionInput.d.ts","sourceRoot":"","sources":["../../../src/components/config/DescriptionInput.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;CACnC,CAAC;AAoCF,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,WAAoC,EACpC,YAAY,EACZ,aAAa,EACb,aAAa,EACb,eAAe,EACf,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,EAAE,qBAAqB,+BA6RvB"}
1
+ {"version":3,"file":"DescriptionInput.d.ts","sourceRoot":"","sources":["../../../src/components/config/DescriptionInput.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAClC,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CACjC,CAAC;AAoCF,wBAAgB,gBAAgB,CAAC,EAC/B,EAAE,EACF,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,WAAoC,EACpC,YAAY,EACZ,aAAa,EACb,aAAa,EACb,eAAe,EACf,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,kBAAkB,GACnB,EAAE,qBAAqB,+BAqTvB"}
@@ -19,7 +19,7 @@ function assignRef(ref, node) {
19
19
  function ToolbarButton({ active, disabled, title, onClick, children, }) {
20
20
  return (_jsx("button", { type: "button", className: "vertebr-md-btn", "data-active": active || undefined, disabled: disabled, title: title, onMouseDown: (event) => event.preventDefault(), onClick: onClick, children: children }));
21
21
  }
22
- export function DescriptionInput({ id, name, value, readOnly, placeholder = "Write a description…", recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, }) {
22
+ export function DescriptionInput({ id, name, value, readOnly, placeholder = "Write a description…", recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, onOpenDocumentView, }) {
23
23
  const text = typeof value === "string" ? value : "";
24
24
  const onChangeRef = useRef(onChange);
25
25
  const onBlurRef = useRef(onBlur);
@@ -148,5 +148,5 @@ export function DescriptionInput({ id, name, value, readOnly, placeholder = "Wri
148
148
  rows: 3,
149
149
  cols: 3,
150
150
  withHeaderRow: true,
151
- }).run(), children: "Table" }), marks?.table ? (_jsxs(_Fragment, { children: [_jsx(ToolbarButton, { disabled: locked, title: "Add column", onClick: () => editor?.chain().focus().addColumnAfter().run(), children: "+Col" }), _jsx(ToolbarButton, { disabled: locked, title: "Add row", onClick: () => editor?.chain().focus().addRowAfter().run(), children: "+Row" }), _jsx(ToolbarButton, { disabled: locked, title: "Delete table", onClick: () => editor?.chain().focus().deleteTable().run(), children: "Del" })] })) : null, _jsx(ToolbarButton, { active: marks?.link, disabled: locked, title: "Link", onClick: setLink, children: "Link" }), _jsx(ToolbarButton, { disabled: locked, title: "Insert images", onClick: () => setPickerOpen(true), children: "Image" })] }), _jsx(EditorContent, { editor: editor }), _jsxs("p", { className: "vertebr-field-prose-meta", children: ["Markdown", _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), stats.words, " ", stats.words === 1 ? "word" : "words", _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), stats.characters, " ", stats.characters === 1 ? "character" : "characters"] }), pickerOpen ? (_jsx(ImagePickerModal, { recordImages: recordImages, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onClose: () => setPickerOpen(false), onInsert: insertImages })) : null] }));
151
+ }).run(), children: "Table" }), marks?.table ? (_jsxs(_Fragment, { children: [_jsx(ToolbarButton, { disabled: locked, title: "Add column", onClick: () => editor?.chain().focus().addColumnAfter().run(), children: "+Col" }), _jsx(ToolbarButton, { disabled: locked, title: "Add row", onClick: () => editor?.chain().focus().addRowAfter().run(), children: "+Row" }), _jsx(ToolbarButton, { disabled: locked, title: "Delete table", onClick: () => editor?.chain().focus().deleteTable().run(), children: "Del" })] })) : null, _jsx(ToolbarButton, { active: marks?.link, disabled: locked, title: "Link", onClick: setLink, children: "Link" }), _jsx(ToolbarButton, { disabled: locked, title: "Insert images", onClick: () => setPickerOpen(true), children: "Image" }), onOpenDocumentView ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "vertebr-md-sep", "aria-hidden": "true" }), _jsx(ToolbarButton, { disabled: false, title: "Open document view", onClick: onOpenDocumentView, children: "Doc" })] })) : null] }), _jsx(EditorContent, { editor: editor }), _jsxs("p", { className: "vertebr-field-prose-meta", children: ["Markdown", _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), stats.words, " ", stats.words === 1 ? "word" : "words", _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), stats.characters, " ", stats.characters === 1 ? "character" : "characters", onOpenDocumentView ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), _jsx("button", { type: "button", className: "vertebr-md-doc-link", onClick: onOpenDocumentView, children: "Open in document view" })] })) : null] }), pickerOpen ? (_jsx(ImagePickerModal, { recordImages: recordImages, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onClose: () => setPickerOpen(false), onInsert: insertImages })) : null] }));
152
152
  }
@@ -0,0 +1,17 @@
1
+ export type DocumentAttachmentFieldProps = {
2
+ id: string;
3
+ label: string;
4
+ value: string;
5
+ readOnly?: boolean;
6
+ description?: string;
7
+ /** Sibling markdown body available for document-view editing. */
8
+ markdownBody?: string;
9
+ canPickFiles?: boolean;
10
+ canUpload?: boolean;
11
+ onChangeUrl: (next: string) => void;
12
+ onOpenPicker?: () => void;
13
+ onUploadFiles?: (files: File[]) => Promise<void>;
14
+ onEditMarkdown?: () => void;
15
+ };
16
+ export declare function DocumentAttachmentField({ id, label, value, readOnly, description, markdownBody, canPickFiles, canUpload, onChangeUrl, onOpenPicker, onUploadFiles, onEditMarkdown, }: DocumentAttachmentFieldProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=DocumentAttachmentField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentAttachmentField.d.ts","sourceRoot":"","sources":["../../../src/components/config/DocumentAttachmentField.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,EACtC,EAAE,EACF,KAAK,EACL,KAAK,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,GACf,EAAE,4BAA4B,+BAqI9B"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef, useState } from "react";
3
+ import { pdfFileName } from "../../lib/documents";
4
+ export function DocumentAttachmentField({ id, label, value, readOnly, description, markdownBody, canPickFiles, canUpload, onChangeUrl, onOpenPicker, onUploadFiles, onEditMarkdown, }) {
5
+ const inputRef = useRef(null);
6
+ const [busy, setBusy] = useState(false);
7
+ const [showUrl, setShowUrl] = useState(() => Boolean(value.trim()));
8
+ const url = value.trim();
9
+ const hasPdf = Boolean(url);
10
+ const canEditMd = Boolean(onEditMarkdown && markdownBody !== undefined);
11
+ const handleFiles = async (files) => {
12
+ if (!files?.length || !onUploadFiles || readOnly)
13
+ return;
14
+ setBusy(true);
15
+ try {
16
+ await onUploadFiles([...files]);
17
+ setShowUrl(true);
18
+ }
19
+ finally {
20
+ setBusy(false);
21
+ if (inputRef.current)
22
+ inputRef.current.value = "";
23
+ }
24
+ };
25
+ return (_jsxs("div", { className: "vertebr-doc-attach", children: [_jsxs("div", { className: "vertebr-doc-attach-card", "data-has-file": hasPdf || undefined, children: [_jsxs("div", { className: "vertebr-doc-attach-main", children: [_jsx("div", { className: "vertebr-doc-attach-icon", "aria-hidden": "true", children: "PDF" }), _jsxs("div", { className: "vertebr-doc-attach-copy", children: [_jsx("p", { className: "vertebr-doc-attach-title", children: hasPdf ? pdfFileName(url) : "No PDF attached" }), _jsx("p", { className: "vertebr-doc-attach-hint", children: hasPdf
26
+ ? "Optional download for shoppers. Markdown remains the live policy."
27
+ : description?.trim() ||
28
+ "Optional PDF download. Markdown remains the live policy." })] })] }), _jsxs("div", { className: "vertebr-doc-attach-actions", children: [canEditMd ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", onClick: onEditMarkdown, children: "Edit document" })) : null, canPickFiles && !readOnly ? (_jsx("button", { type: "button", className: "vertebr-btn", onClick: onOpenPicker, disabled: busy, children: "Choose PDF" })) : null, canUpload && !readOnly ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "vertebr-btn", disabled: busy, onClick: () => inputRef.current?.click(), children: busy ? "Uploading…" : "Upload" }), _jsx("input", { ref: inputRef, type: "file", accept: "application/pdf,.pdf", hidden: true, onChange: (event) => void handleFiles(event.target.files) })] })) : null, hasPdf ? (_jsx("a", { className: "vertebr-btn", href: url, target: "_blank", rel: "noreferrer", children: "Open" })) : null, hasPdf && !readOnly ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => {
29
+ onChangeUrl("");
30
+ setShowUrl(false);
31
+ }, children: "Remove" })) : null] })] }), !readOnly ? (showUrl ? (_jsxs("label", { className: "vertebr-doc-attach-url", htmlFor: id, children: [_jsxs("span", { children: [label, " URL"] }), _jsx("input", { id: id, type: "url", placeholder: "https://\u2026/policy.pdf", value: value, onChange: (event) => onChangeUrl(event.target.value) })] })) : (_jsx("button", { type: "button", className: "vertebr-doc-attach-url-toggle", onClick: () => setShowUrl(true), children: "Paste PDF URL" }))) : hasPdf ? (_jsxs("label", { className: "vertebr-doc-attach-url", htmlFor: id, children: [_jsxs("span", { children: [label, " URL"] }), _jsx("input", { id: id, type: "url", readOnly: true, value: value })] })) : null] }));
32
+ }
@@ -7,6 +7,7 @@ export type PickerImage = {
7
7
  source: "record" | "gallery" | "url";
8
8
  };
9
9
  type Tab = "record" | "gallery";
10
+ type AssetKind = "image" | "document" | "any";
10
11
  export type ImagePickerModalProps = {
11
12
  recordImages?: RecordImage[];
12
13
  galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
@@ -20,7 +21,9 @@ export type ImagePickerModalProps = {
20
21
  confirmLabel?: string;
21
22
  /** Prefer Files tab when both product photos and gallery exist. */
22
23
  defaultTab?: Tab;
24
+ /** Limit Files library to images or PDFs. @default "image" */
25
+ assetKind?: AssetKind;
23
26
  };
24
- export declare function ImagePickerModal({ recordImages, galleryUpload, onListGallery, onUploadGallery, onClose, onInsert, title, confirmLabel, defaultTab, }: ImagePickerModalProps): import("react").ReactPortal;
27
+ export declare function ImagePickerModal({ recordImages, galleryUpload, onListGallery, onUploadGallery, onClose, onInsert, title, confirmLabel, defaultTab, assetKind, }: ImagePickerModalProps): import("react").ReactPortal;
25
28
  export {};
26
29
  //# sourceMappingURL=ImagePickerModal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePickerModal.d.ts","sourceRoot":"","sources":["../../../src/components/config/ImagePickerModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;CACtC,CAAC;AAEF,KAAK,GAAG,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEhC,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IAC1C,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,YAAiB,EACjB,aAAa,EACb,aAAa,EACb,eAAe,EACf,OAAO,EACP,QAAQ,EACR,KAAuB,EACvB,YAAoB,EACpB,UAAU,GACX,EAAE,qBAAqB,+BAiQvB"}
1
+ {"version":3,"file":"ImagePickerModal.d.ts","sourceRoot":"","sources":["../../../src/components/config/ImagePickerModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;CACtC,CAAC;AAEF,KAAK,GAAG,GAAG,QAAQ,GAAG,SAAS,CAAC;AAChC,KAAK,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,KAAK,CAAC;AAE9C,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IAC1C,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,YAAiB,EACjB,aAAa,EACb,aAAa,EACb,eAAe,EACf,OAAO,EACP,QAAQ,EACR,KAAuB,EACvB,YAAoB,EACpB,UAAU,EACV,SAAmB,GACpB,EAAE,qBAAqB,+BAqSvB"}
@@ -1,13 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
3
  import { createPortal } from "react-dom";
4
+ import { filterGalleryByKind, isPdfAsset } from "../../lib/documents";
4
5
  import { ImageUploadControl } from "../resources/ImageUploadControl";
5
- export function ImagePickerModal({ recordImages = [], galleryUpload, onListGallery, onUploadGallery, onClose, onInsert, title = "Choose images", confirmLabel = "Use", defaultTab, }) {
6
+ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGallery, onUploadGallery, onClose, onInsert, title = "Choose images", confirmLabel = "Use", defaultTab, assetKind = "image", }) {
6
7
  const hasGallery = Boolean(onListGallery);
8
+ const docsOnly = assetKind === "document";
7
9
  const [tab, setTab] = useState(() => {
8
10
  if (defaultTab)
9
11
  return defaultTab;
10
- if (recordImages.length)
12
+ if (!docsOnly && recordImages.length)
11
13
  return "record";
12
14
  return "gallery";
13
15
  });
@@ -38,7 +40,7 @@ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGalle
38
40
  void onListGallery()
39
41
  .then((items) => {
40
42
  if (!cancelled)
41
- setGallery(items);
43
+ setGallery(filterGalleryByKind(items, assetKind));
42
44
  })
43
45
  .catch((err) => {
44
46
  if (!cancelled) {
@@ -52,13 +54,15 @@ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGalle
52
54
  return () => {
53
55
  cancelled = true;
54
56
  };
55
- }, [hasGallery, onListGallery]);
56
- const recordTiles = useMemo(() => recordImages.map((image) => ({
57
- id: `record:${image.id}`,
58
- url: image.url,
59
- name: image.name,
60
- source: "record",
61
- })), [recordImages]);
57
+ }, [hasGallery, onListGallery, assetKind]);
58
+ const recordTiles = useMemo(() => docsOnly
59
+ ? []
60
+ : recordImages.map((image) => ({
61
+ id: `record:${image.id}`,
62
+ url: image.url,
63
+ name: image.name,
64
+ source: "record",
65
+ })), [recordImages, docsOnly]);
62
66
  const galleryTiles = useMemo(() => gallery.map((item) => ({
63
67
  id: `gallery:${item.id}`,
64
68
  url: item.url,
@@ -68,6 +72,8 @@ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGalle
68
72
  const tiles = tab === "record" ? recordTiles : galleryTiles;
69
73
  const picked = Object.values(selected);
70
74
  const canGalleryUpload = Boolean(galleryUpload && onUploadGallery);
75
+ const unit = docsOnly ? "document" : "image";
76
+ const units = docsOnly ? "documents" : "images";
71
77
  const toggle = (image) => {
72
78
  setSelected((current) => {
73
79
  const next = { ...current };
@@ -85,22 +91,33 @@ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGalle
85
91
  const image = {
86
92
  id: `url:${href}`,
87
93
  url: href,
88
- name: "Image",
94
+ name: docsOnly ? "Document" : "Image",
89
95
  source: "url",
90
96
  };
91
97
  setSelected((current) => ({ ...current, [image.id]: image }));
92
98
  setUrl("");
93
99
  };
94
- return createPortal(_jsx("div", { className: "vertebr-modal-backdrop", role: "presentation", onClick: onClose, children: _jsxs("div", { className: "vertebr-modal", role: "dialog", "aria-modal": "true", "aria-label": title, onClick: (event) => event.stopPropagation(), children: [_jsxs("header", { className: "vertebr-modal-head", children: [_jsxs("div", { className: "vertebr-modal-heading", children: [_jsx("p", { className: "vertebr-modal-eyebrow", children: "Media" }), _jsx("h2", { children: title })] }), _jsx("button", { type: "button", className: "vertebr-btn", onClick: onClose, children: "Close" })] }), _jsxs("div", { className: "vertebr-modal-body vertebr-picker-body", children: [recordImages.length > 0 && hasGallery ? (_jsxs("div", { className: "vertebr-picker-tabs", role: "tablist", children: [_jsxs("button", { type: "button", role: "tab", "aria-selected": tab === "record", "data-active": tab === "record" || undefined, onClick: () => setTab("record"), children: ["Product photos", _jsx("span", { children: recordTiles.length })] }), _jsxs("button", { type: "button", role: "tab", "aria-selected": tab === "gallery", "data-active": tab === "gallery" || undefined, onClick: () => setTab("gallery"), children: ["Files", _jsx("span", { children: loading ? "…" : galleryTiles.length })] })] })) : null, tab === "gallery" && canGalleryUpload ? (_jsx(ImageUploadControl, { meta: {
100
+ const galleryAccept = docsOnly && galleryUpload
101
+ ? {
102
+ ...galleryUpload,
103
+ accept: galleryUpload.accept.some((entry) => /pdf/i.test(entry))
104
+ ? galleryUpload.accept
105
+ : [...galleryUpload.accept, "application/pdf"],
106
+ }
107
+ : galleryUpload;
108
+ return createPortal(_jsx("div", { className: "vertebr-modal-backdrop", role: "presentation", onClick: onClose, children: _jsxs("div", { className: "vertebr-modal", role: "dialog", "aria-modal": "true", "aria-label": title, onClick: (event) => event.stopPropagation(), children: [_jsxs("header", { className: "vertebr-modal-head", children: [_jsxs("div", { className: "vertebr-modal-heading", children: [_jsx("p", { className: "vertebr-modal-eyebrow", children: docsOnly ? "Documents" : "Media" }), _jsx("h2", { children: title })] }), _jsx("button", { type: "button", className: "vertebr-btn", onClick: onClose, children: "Close" })] }), _jsxs("div", { className: "vertebr-modal-body vertebr-picker-body", children: [!docsOnly && recordImages.length > 0 && hasGallery ? (_jsxs("div", { className: "vertebr-picker-tabs", role: "tablist", children: [_jsxs("button", { type: "button", role: "tab", "aria-selected": tab === "record", "data-active": tab === "record" || undefined, onClick: () => setTab("record"), children: ["Product photos", _jsx("span", { children: recordTiles.length })] }), _jsxs("button", { type: "button", role: "tab", "aria-selected": tab === "gallery", "data-active": tab === "gallery" || undefined, onClick: () => setTab("gallery"), children: ["Files", _jsx("span", { children: loading ? "…" : galleryTiles.length })] })] })) : null, tab === "gallery" && canGalleryUpload && galleryAccept ? (_jsx(ImageUploadControl, { meta: {
95
109
  field: "gallery",
96
- accept: galleryUpload.accept,
97
- maxBytes: galleryUpload.maxBytes,
98
- }, multiple: true, label: "Drop images to add them to Files", onUpload: async (files) => {
110
+ accept: galleryAccept.accept,
111
+ maxBytes: galleryAccept.maxBytes,
112
+ }, multiple: true, showOptimizeHint: !docsOnly, label: docsOnly
113
+ ? "Drop PDFs to add them to Files"
114
+ : "Drop images to add them to Files", onUpload: async (files) => {
99
115
  const items = await onUploadGallery(files);
100
- setGallery((current) => [...items, ...current]);
116
+ const kept = filterGalleryByKind(items, assetKind);
117
+ setGallery((current) => [...kept, ...current]);
101
118
  setSelected((current) => {
102
119
  const next = { ...current };
103
- for (const item of items) {
120
+ for (const item of kept) {
104
121
  const image = {
105
122
  id: `gallery:${item.id}`,
106
123
  url: item.url,
@@ -111,17 +128,22 @@ export function ImagePickerModal({ recordImages = [], galleryUpload, onListGalle
111
128
  }
112
129
  return next;
113
130
  });
114
- } })) : null, _jsxs("label", { className: "vertebr-field", children: [_jsx("span", { children: "Or paste an image URL" }), _jsxs("div", { className: "vertebr-picker-url", children: [_jsx("input", { type: "url", value: url, placeholder: "https://", onChange: (event) => setUrl(event.target.value), onKeyDown: (event) => {
131
+ } })) : null, _jsxs("label", { className: "vertebr-field", children: [_jsxs("span", { children: ["Or paste a ", docsOnly ? "PDF" : "image", " URL"] }), _jsxs("div", { className: "vertebr-picker-url", children: [_jsx("input", { type: "url", value: url, placeholder: "https://", onChange: (event) => setUrl(event.target.value), onKeyDown: (event) => {
115
132
  if (event.key === "Enter") {
116
133
  event.preventDefault();
117
134
  addUrl();
118
135
  }
119
- } }), _jsx("button", { type: "button", className: "vertebr-btn", disabled: !url.trim(), onClick: addUrl, children: "Add" })] })] }), error ? (_jsx("p", { className: "vertebr-status", "data-tone": "danger", children: error })) : null, loading && tab === "gallery" ? (_jsx("div", { className: "vertebr-empty", children: "Loading gallery\u2026" })) : tiles.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: tab === "record"
136
+ } }), _jsx("button", { type: "button", className: "vertebr-btn", disabled: !url.trim(), onClick: addUrl, children: "Add" })] })] }), error ? (_jsx("p", { className: "vertebr-status", "data-tone": "danger", children: error })) : null, loading && tab === "gallery" ? (_jsx("div", { className: "vertebr-empty", children: "Loading Files\u2026" })) : tiles.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: tab === "record"
120
137
  ? "No product photos on this record yet."
121
138
  : hasGallery
122
- ? "No images in Files yet."
123
- : "No Files library is configured. Paste a URL or add product photos." })) : (_jsx("div", { className: "vertebr-picker-grid", children: tiles.map((image) => {
139
+ ? docsOnly
140
+ ? "No PDFs in Files yet."
141
+ : "No images in Files yet."
142
+ : "No Files library is configured. Paste a URL instead." })) : (_jsx("div", { className: "vertebr-picker-grid", children: tiles.map((image) => {
124
143
  const active = Boolean(selected[image.id]);
125
- return (_jsxs("button", { type: "button", className: "vertebr-picker-tile", "data-selected": active || undefined, onClick: () => toggle(image), children: [_jsx("span", { className: "vertebr-picker-thumb", children: _jsx("img", { src: image.url, alt: "" }) }), _jsx("span", { className: "vertebr-picker-name", children: image.name })] }, image.id));
126
- }) }))] }), _jsxs("footer", { className: "vertebr-picker-foot", children: [_jsxs("span", { className: "vertebr-picker-count", children: [picked.length, " selected"] }), _jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: picked.length === 0, onClick: () => onInsert(picked), children: [confirmLabel, " ", picked.length || "", picked.length === 1 ? " image" : " images"] })] })] }) }), document.body);
144
+ const pdf = docsOnly || isPdfAsset(image);
145
+ return (_jsxs("button", { type: "button", className: "vertebr-picker-tile", "data-selected": active || undefined, onClick: () => toggle(image), children: [_jsx("span", { className: pdf
146
+ ? "vertebr-picker-thumb vertebr-gallery-thumb-file"
147
+ : "vertebr-picker-thumb", children: pdf ? (_jsx("span", { className: "vertebr-gallery-file-badge", children: "PDF" })) : (_jsx("img", { src: image.url, alt: "" })) }), _jsx("span", { className: "vertebr-picker-name", children: image.name })] }, image.id));
148
+ }) }))] }), _jsxs("footer", { className: "vertebr-picker-foot", children: [_jsxs("span", { className: "vertebr-picker-count", children: [picked.length, " selected"] }), _jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: picked.length === 0, onClick: () => onInsert(picked), children: [confirmLabel, " ", picked.length || "", picked.length === 1 ? ` ${unit}` : ` ${units}`] })] })] }) }), document.body);
127
149
  }
@@ -0,0 +1,19 @@
1
+ import type { GalleryItem, ResourceUploadMeta } from "@vertebr/client";
2
+ import type { RecordImage } from "../../lib/images";
3
+ export type MarkdownDocumentModalProps = {
4
+ title?: string;
5
+ value: string;
6
+ readOnly?: boolean;
7
+ recordImages?: RecordImage[];
8
+ galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
9
+ onListGallery?: () => Promise<GalleryItem[]>;
10
+ onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
11
+ onChange: (next: string) => void;
12
+ onClose: () => void;
13
+ };
14
+ /**
15
+ * Full-page document chrome for editing policy markdown
16
+ * (PDF-like page, not a binary PDF editor).
17
+ */
18
+ export declare function MarkdownDocumentModal({ title, value, readOnly, recordImages, galleryUpload, onListGallery, onUploadGallery, onChange, onClose, }: MarkdownDocumentModalProps): import("react").ReactPortal;
19
+ //# sourceMappingURL=MarkdownDocumentModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownDocumentModal.d.ts","sourceRoot":"","sources":["../../../src/components/config/MarkdownDocumentModal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAyB,EACzB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,GACR,EAAE,0BAA0B,+BAkF5B"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { descriptionStats } from "../../lib/description";
5
+ import { DescriptionInput } from "./DescriptionInput";
6
+ /**
7
+ * Full-page document chrome for editing policy markdown
8
+ * (PDF-like page, not a binary PDF editor).
9
+ */
10
+ export function MarkdownDocumentModal({ title = "Policy document", value, readOnly, recordImages, galleryUpload, onListGallery, onUploadGallery, onChange, onClose, }) {
11
+ const [draft, setDraft] = useState(value);
12
+ const stats = descriptionStats(draft);
13
+ useEffect(() => {
14
+ setDraft(value);
15
+ }, [value]);
16
+ useEffect(() => {
17
+ const onKeyDown = (event) => {
18
+ if (event.key === "Escape")
19
+ onClose();
20
+ };
21
+ window.addEventListener("keydown", onKeyDown);
22
+ const previous = document.body.style.overflow;
23
+ document.body.style.overflow = "hidden";
24
+ return () => {
25
+ window.removeEventListener("keydown", onKeyDown);
26
+ document.body.style.overflow = previous;
27
+ };
28
+ }, [onClose]);
29
+ const saveAndClose = () => {
30
+ onChange(draft);
31
+ onClose();
32
+ };
33
+ return createPortal(_jsx("div", { className: "vertebr-modal-backdrop vertebr-doc-modal-backdrop", role: "presentation", onClick: onClose, children: _jsxs("div", { className: "vertebr-modal vertebr-doc-modal", role: "dialog", "aria-modal": "true", "aria-label": title, onClick: (event) => event.stopPropagation(), children: [_jsxs("header", { className: "vertebr-modal-head", children: [_jsxs("div", { className: "vertebr-modal-heading", children: [_jsx("p", { className: "vertebr-modal-eyebrow", children: "Document" }), _jsx("h2", { children: title })] }), _jsxs("div", { className: "vertebr-doc-modal-actions", children: [_jsxs("span", { className: "vertebr-doc-modal-stats", children: [stats.words, " words \u00B7 ", stats.characters, " characters"] }), !readOnly ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", onClick: saveAndClose, children: "Done" })) : null, _jsx("button", { type: "button", className: "vertebr-btn", onClick: onClose, children: "Close" })] })] }), _jsx("div", { className: "vertebr-modal-body vertebr-doc-modal-body", children: _jsx("div", { className: "vertebr-doc-page", children: _jsx(DescriptionInput, { id: "vertebr-doc-modal-editor", value: draft, readOnly: readOnly, placeholder: "Write the policy\u2026", recordImages: recordImages, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onChange: setDraft }) }) })] }) }), document.body);
34
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwBtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEvF,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAC5C,mBAAmB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,EACX,MAAM,GACP,EAAE,gBAAgB,+BAyXlB"}
1
+ {"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA2BtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmBvF,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAC5C,mBAAmB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,EACX,MAAM,GACP,EAAE,gBAAgB,+BA4elB"}