@vertebr/react 0.6.0 → 0.8.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 (58) hide show
  1. package/dist/components/admin/AdminContent.d.ts +2 -1
  2. package/dist/components/admin/AdminContent.d.ts.map +1 -1
  3. package/dist/components/admin/AdminContent.js +10 -2
  4. package/dist/components/admin/AdminHeader.d.ts +3 -2
  5. package/dist/components/admin/AdminHeader.d.ts.map +1 -1
  6. package/dist/components/admin/AdminHeader.js +5 -3
  7. package/dist/components/admin/AdminSaveBar.d.ts +2 -0
  8. package/dist/components/admin/AdminSaveBar.d.ts.map +1 -0
  9. package/dist/components/admin/AdminSaveBar.js +10 -0
  10. package/dist/components/admin/AdminSidebar.d.ts +2 -20
  11. package/dist/components/admin/AdminSidebar.d.ts.map +1 -1
  12. package/dist/components/admin/AdminSidebar.js +8 -12
  13. package/dist/components/admin/AdminToastStack.d.ts +2 -0
  14. package/dist/components/admin/AdminToastStack.d.ts.map +1 -0
  15. package/dist/components/admin/AdminToastStack.js +8 -0
  16. package/dist/components/admin/AdminTopBar.d.ts +24 -0
  17. package/dist/components/admin/AdminTopBar.d.ts.map +1 -0
  18. package/dist/components/admin/AdminTopBar.js +7 -0
  19. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  20. package/dist/components/admin/VertebrAdmin.js +15 -1
  21. package/dist/components/admin/breadcrumbs.d.ts +8 -0
  22. package/dist/components/admin/breadcrumbs.d.ts.map +1 -0
  23. package/dist/components/admin/breadcrumbs.js +44 -0
  24. package/dist/components/admin/routes.d.ts.map +1 -1
  25. package/dist/components/admin/routes.js +9 -2
  26. package/dist/components/admin/types.d.ts +6 -0
  27. package/dist/components/admin/types.d.ts.map +1 -1
  28. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  29. package/dist/components/config/form/ConfigForm.js +44 -7
  30. package/dist/components/fonts/FontPickerField.d.ts.map +1 -1
  31. package/dist/components/fonts/FontPickerField.js +12 -1
  32. package/dist/components/resources/ResourceTable.d.ts +3 -1
  33. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  34. package/dist/components/resources/ResourceTable.js +20 -5
  35. package/dist/components/ui/icons.d.ts +15 -0
  36. package/dist/components/ui/icons.d.ts.map +1 -0
  37. package/dist/components/ui/icons.js +60 -0
  38. package/dist/components/ui/primitives.d.ts +27 -0
  39. package/dist/components/ui/primitives.d.ts.map +1 -0
  40. package/dist/components/ui/primitives.js +16 -0
  41. package/dist/hooks/useSaveBar.d.ts +21 -0
  42. package/dist/hooks/useSaveBar.d.ts.map +1 -0
  43. package/dist/hooks/useSaveBar.js +30 -0
  44. package/dist/hooks/useToast.d.ts +18 -0
  45. package/dist/hooks/useToast.d.ts.map +1 -0
  46. package/dist/hooks/useToast.js +23 -0
  47. package/dist/styles/base.css +8 -14
  48. package/dist/styles/buttons.css +3 -12
  49. package/dist/styles/index.css +1 -0
  50. package/dist/styles/main.css +3 -7
  51. package/dist/styles/nav.css +3 -1
  52. package/dist/styles/shell.css +314 -0
  53. package/dist/styles/sidebar.css +26 -5
  54. package/dist/themes/dark.css +48 -46
  55. package/dist/themes/index.css +1 -0
  56. package/dist/themes/light.css +91 -85
  57. package/dist/themes/tokens.css +52 -0
  58. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FontPickerField.d.ts","sourceRoot":"","sources":["../../../src/components/fonts/FontPickerField.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAUF,wBAAgB,eAAe,CAAC,EAC9B,EAAE,EACF,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,KAAK,EACL,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAA0B,GAC3B,EAAE,oBAAoB,+BA0LtB"}
1
+ {"version":3,"file":"FontPickerField.d.ts","sourceRoot":"","sources":["../../../src/components/fonts/FontPickerField.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAUF,wBAAgB,eAAe,CAAC,EAC9B,EAAE,EACF,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,KAAK,EACL,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAA0B,GAC3B,EAAE,oBAAoB,+BAqMtB"}
@@ -42,7 +42,18 @@ export function FontPickerField({ id, value, label, description, readOnly, fonts
42
42
  return null;
43
43
  return fonts.find((item) => item.id === previewId) ?? null;
44
44
  }, [fonts, previewId]);
45
- return (_jsxs("div", { className: "vertebr-font-picker", children: [_jsx(FontFaceInjector, { items: fonts, apiBase: fontApiBase }), _jsxs("div", { className: "vertebr-font-picker-current", children: [_jsxs("div", { children: [_jsx("strong", { children: selected?.label ?? "Default system fonts" }), selected ? (_jsx("span", { children: selected.family })) : (_jsx("span", { children: "Manrope / Syne until a library font is chosen" }))] }), !readOnly ? (_jsx("button", { id: id, type: "button", className: "vertebr-btn", onClick: () => setOpen(true), children: "Choose font" })) : (_jsx("span", { id: id, className: "vertebr-font-picker-ro-anchor" }))] }), selected ? (_jsx("div", { className: "vertebr-font-picker-preview", children: _jsx(FontPreviewSample, { family: selected.family, bodyWeight: weightForItem(selected, "body"), headingWeight: weightForItem(selected, "heading"), compact: true }) })) : null, open ? (_jsx("div", { className: "vertebr-font-picker-modal-backdrop", onClick: () => closeModal(true), children: _jsxs("div", { className: "vertebr-font-picker-modal", role: "dialog", "aria-labelledby": `${id}-font-picker-title`, onClick: (event) => event.stopPropagation(), children: [_jsxs("div", { className: "vertebr-font-picker-modal-head", children: [_jsxs("div", { children: [_jsx("h3", { id: `${id}-font-picker-title`, children: label ?? "Choose a font" }), description ? _jsx("p", { children: description }) : null] }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => closeModal(true), children: "Close" })] }), loading ? (_jsx("div", { className: "vertebr-empty", children: "Loading fonts\u2026" })) : fonts.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: "No fonts in the library yet. Open Font Library to upload or add Google Fonts." })) : (_jsxs("div", { className: "vertebr-font-picker-modal-body", children: [_jsx("div", { className: "vertebr-font-picker-grid-scroll", children: _jsxs("div", { className: "vertebr-font-picker-grid", children: [_jsxs("button", { type: "button", className: "vertebr-font-picker-option", "data-active": previewId === "" || undefined, onClick: () => previewFont(""), children: [_jsx("strong", { children: "Default" }), _jsx("span", { children: "Manrope / Syne" })] }), fonts.map((item) => (_jsxs("button", { type: "button", className: "vertebr-font-picker-option", "data-active": item.id === previewId || undefined, style: {
45
+ const faceItems = useMemo(() => {
46
+ const items = [];
47
+ const seen = new Set();
48
+ for (const item of [selected, previewItem]) {
49
+ if (!item || seen.has(item.id))
50
+ continue;
51
+ seen.add(item.id);
52
+ items.push(item);
53
+ }
54
+ return items;
55
+ }, [previewItem, selected]);
56
+ return (_jsxs("div", { className: "vertebr-font-picker", children: [_jsx(FontFaceInjector, { items: faceItems, apiBase: fontApiBase }), _jsxs("div", { className: "vertebr-font-picker-current", children: [_jsxs("div", { children: [_jsx("strong", { children: selected?.label ?? "Default system fonts" }), selected ? (_jsx("span", { children: selected.family })) : (_jsx("span", { children: "Manrope / Syne until a library font is chosen" }))] }), !readOnly ? (_jsx("button", { id: id, type: "button", className: "vertebr-btn", onClick: () => setOpen(true), children: "Choose font" })) : (_jsx("span", { id: id, className: "vertebr-font-picker-ro-anchor" }))] }), selected ? (_jsx("div", { className: "vertebr-font-picker-preview", children: _jsx(FontPreviewSample, { family: selected.family, bodyWeight: weightForItem(selected, "body"), headingWeight: weightForItem(selected, "heading"), compact: true }) })) : null, open ? (_jsx("div", { className: "vertebr-font-picker-modal-backdrop", onClick: () => closeModal(true), children: _jsxs("div", { className: "vertebr-font-picker-modal", role: "dialog", "aria-labelledby": `${id}-font-picker-title`, onClick: (event) => event.stopPropagation(), children: [_jsxs("div", { className: "vertebr-font-picker-modal-head", children: [_jsxs("div", { children: [_jsx("h3", { id: `${id}-font-picker-title`, children: label ?? "Choose a font" }), description ? _jsx("p", { children: description }) : null] }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => closeModal(true), children: "Close" })] }), loading ? (_jsx("div", { className: "vertebr-empty", children: "Loading fonts\u2026" })) : fonts.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: "No fonts in the library yet. Open Font Library to upload or add Google Fonts." })) : (_jsxs("div", { className: "vertebr-font-picker-modal-body", children: [_jsx("div", { className: "vertebr-font-picker-grid-scroll", children: _jsxs("div", { className: "vertebr-font-picker-grid", children: [_jsxs("button", { type: "button", className: "vertebr-font-picker-option", "data-active": previewId === "" || undefined, onClick: () => previewFont(""), children: [_jsx("strong", { children: "Default" }), _jsx("span", { children: "Manrope / Syne" })] }), fonts.map((item) => (_jsxs("button", { type: "button", className: "vertebr-font-picker-option", "data-active": item.id === previewId || undefined, style: {
46
57
  fontFamily: `"${item.family}", ui-sans-serif, sans-serif`,
47
58
  fontWeight: weightForItem(item, "heading"),
48
59
  }, onClick: () => previewFont(item.id), children: [_jsx("strong", { children: item.label }), _jsx("span", { children: item.family }), _jsx("em", { children: "The quick brown fox" })] }, item.id)))] }) }), _jsxs("aside", { className: "vertebr-font-picker-detail", children: [_jsx("p", { className: "vertebr-font-picker-detail-label", children: "Preview" }), previewId === "" ? (_jsx(FontPreviewSample, { family: "Manrope", bodyWeight: 400, headingWeight: 700 })) : previewItem ? (_jsx(FontPreviewSample, { family: previewItem.family, bodyWeight: weightForItem(previewItem, "body"), headingWeight: weightForItem(previewItem, "heading") })) : (_jsx("div", { className: "vertebr-empty", children: "Select a font to preview." }))] })] })), _jsxs("div", { className: "vertebr-font-picker-modal-actions", children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => closeModal(true), children: "Cancel" }), _jsx("button", { type: "button", className: "vertebr-btn", disabled: loading, onClick: () => closeModal(false), children: "Done" })] })] }) })) : null] }));
@@ -29,8 +29,10 @@ type ResourcePanelProps = {
29
29
  galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
30
30
  onListGallery?: () => Promise<GalleryItem[]>;
31
31
  onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
32
+ initialEditRowId?: string;
33
+ onEditRowIdChange?: (rowId: string | null) => void;
32
34
  };
33
- export declare function ResourcePanel({ schema, items, permissions, actions, label, uploads, rowIdKeys, storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions, onCollectionAction, onUpload, galleryUpload, onListGallery, onUploadGallery, }: ResourcePanelProps): import("react").JSX.Element;
35
+ export declare function ResourcePanel({ schema, items, permissions, actions, label, uploads, rowIdKeys, storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions, onCollectionAction, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }: ResourcePanelProps): import("react").JSX.Element;
34
36
  /** @deprecated Prefer ResourcePanel */
35
37
  export declare function ResourceTable(props: {
36
38
  schema: Record<string, FieldSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceTable.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ResourceTable.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ResourceTable.d.ts","sourceRoot":"","sources":["../../../src/components/resources/ResourceTable.tsx"],"names":[],"mappings":"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"}
@@ -130,7 +130,7 @@ function titleFromFilename(name) {
130
130
  .replace(/\s+/g, " ")
131
131
  .trim();
132
132
  }
133
- export function ResourcePanel({ schema, items, permissions, actions = {}, label, uploads, rowIdKeys = ["id", "slug", "uid", "email"], storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions = {}, onCollectionAction, onUpload, galleryUpload, onListGallery, onUploadGallery, }) {
133
+ export function ResourcePanel({ schema, items, permissions, actions = {}, label, uploads, rowIdKeys = ["id", "slug", "uid", "email"], storageKey, onUpdate, onCreate, onDelete, onAction, onActionDone, onPreviewMail, collectionActions = {}, onCollectionAction, onUpload, galleryUpload, onListGallery, onUploadGallery, initialEditRowId, onEditRowIdChange, }) {
134
134
  const [editing, setEditing] = useState(null);
135
135
  const [creating, setCreating] = useState(false);
136
136
  const [viewing, setViewing] = useState(null);
@@ -159,6 +159,21 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
159
159
  const fieldKeys = Object.keys(schema);
160
160
  const visibilityFields = useMemo(() => fieldKeys.filter((key) => isListVisibilityField(key, schema[key])), [fieldKeys, schema]);
161
161
  const selectable = Boolean(onAction && actionEntries.length > 0);
162
+ const openEdit = useCallback((row) => {
163
+ setEditing(row);
164
+ onEditRowIdChange?.(resolveRowId(row, rowIdKeys));
165
+ }, [onEditRowIdChange, rowIdKeys]);
166
+ const closeEdit = useCallback(() => {
167
+ setEditing(null);
168
+ onEditRowIdChange?.(null);
169
+ }, [onEditRowIdChange]);
170
+ useEffect(() => {
171
+ if (!initialEditRowId)
172
+ return;
173
+ const row = items.find((item) => resolveRowId(item, rowIdKeys) === initialEditRowId);
174
+ if (row)
175
+ setEditing(row);
176
+ }, [initialEditRowId, items, rowIdKeys]);
162
177
  useEffect(() => {
163
178
  setVisibilityDrafts({});
164
179
  }, [items]);
@@ -436,7 +451,7 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
436
451
  enableHiding: false,
437
452
  cell: ({ row }) => {
438
453
  const rowId = resolveRowId(row.original, rowIdKeys);
439
- return (_jsxs("div", { className: "vertebr-row-actions", onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setViewing(row.original), children: "View" }), canUpdate ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setEditing(row.original), children: "Edit" })) : null, rowId
454
+ return (_jsxs("div", { className: "vertebr-row-actions", onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setViewing(row.original), children: "View" }), canUpdate ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => openEdit(row.original), children: "Edit" })) : null, rowId
440
455
  ? actionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueAction(actionId, [rowId]), children: meta.label }, actionId)))
441
456
  : null, canDelete && rowId ? (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-danger-quiet", onClick: () => {
442
457
  if (window.confirm("Delete this row? This cannot be undone.")) {
@@ -490,9 +505,9 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
490
505
  if (editing && editingId && onUpdate) {
491
506
  const editableSchema = Object.fromEntries(Object.entries(schema).filter(([, field]) => !field.readOnly));
492
507
  const title = rowTitle(editing, rowIdKeys);
493
- return (_jsxs("div", { className: "vertebr-resource-edit", children: [_jsxs("div", { className: "vertebr-resource-edit-head", children: [_jsxs("div", { className: "vertebr-resource-edit-heading", children: [_jsx("p", { className: "vertebr-resource-edit-eyebrow", children: "Editing record" }), _jsx("h2", { children: title }), _jsx("p", { className: "vertebr-resource-edit-id", children: editingId })] }), _jsx("button", { type: "button", className: "vertebr-btn", onClick: () => setEditing(null), children: "Back" })] }), _jsx(ConfigForm, { variant: "record", schema: editableSchema, value: editing, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onSave: async (next) => {
508
+ return (_jsxs("div", { className: "vertebr-resource-edit", children: [_jsxs("div", { className: "vertebr-resource-edit-head", children: [_jsxs("div", { className: "vertebr-resource-edit-heading", children: [_jsx("p", { className: "vertebr-resource-edit-eyebrow", children: "Editing record" }), _jsx("h2", { children: title }), _jsx("p", { className: "vertebr-resource-edit-id", children: editingId })] }), _jsx("button", { type: "button", className: "vertebr-btn", onClick: () => closeEdit(), children: "Back" })] }), _jsx(ConfigForm, { variant: "record", schema: editableSchema, value: editing, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, onSave: async (next) => {
494
509
  await onUpdate(editingId, next);
495
- setEditing(null);
510
+ closeEdit();
496
511
  } })] }));
497
512
  }
498
513
  const toolbar = (_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsxs("div", { className: "vertebr-table-toolbar-start", children: [columnOptions.length > 0 ? (_jsx(ColumnVisibilityMenu, { columns: columnOptions, visibility: columnVisibility, onChange: setColumnVisibility })) : null, canUpload ? (_jsxs("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon", onClick: () => setBulkOpen(true), children: [_jsx(ImageUploadIcon, {}), "Bulk upload"] })) : null, visibilityFields.length > 0 && dirtyVisibilityCount > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [dirtyVisibilityCount, " visibility change", dirtyVisibilityCount === 1 ? "" : "s"] }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-primary", disabled: savingVisibility, onClick: () => void saveVisibility(), children: savingVisibility ? "Saving…" : "Save visibility" }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: savingVisibility, onClick: () => setVisibilityDrafts({}), children: "Discard" })] })) : null, collectionActionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueCollectionAction(actionId), children: meta.label }, actionId))), selectable && selected.size > 0 ? (_jsxs(_Fragment, { children: [_jsxs("span", { className: "vertebr-count", children: [selected.size, " selected"] }), actionEntries.map(([actionId, meta]) => (_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busyAction != null, onClick: () => queueAction(actionId, [...selected]), children: meta.label }, actionId))), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", onClick: () => setSelected(new Set()), children: "Clear" })] })) : null] }), tableOverflow.overflow ? (_jsxs("div", { className: "vertebr-table-scroll-controls", role: "group", "aria-label": "Scroll columns", children: [_jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon vertebr-table-scroll-btn", disabled: !tableOverflow.canLeft, onClick: () => scrollTable("left"), "aria-label": "Scroll columns left", children: _jsx(ChevronLeftIcon, {}) }), _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet vertebr-btn-icon vertebr-table-scroll-btn", disabled: !tableOverflow.canRight, onClick: () => scrollTable("right"), "aria-label": "Scroll columns right", children: _jsx(ChevronRightIcon, {}) })] })) : null] }));
@@ -551,7 +566,7 @@ export function ResourcePanel({ schema, items, permissions, actions = {}, label,
551
566
  : cell.column.id === "_select"
552
567
  ? "vertebr-col-select"
553
568
  : undefined, children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id))) })] }) }), viewing ? (_jsx(ResourceViewModal, { schema: schema, row: viewing, title: rowTitle(viewing, rowIdKeys), canEdit: canUpdate, onClose: () => setViewing(null), onEdit: () => {
554
- setEditing(viewing);
569
+ openEdit(viewing);
555
570
  setViewing(null);
556
571
  } })) : null, pendingActionModal, pendingCollectionModal, bulkModal, createModal] }));
557
572
  }
@@ -0,0 +1,15 @@
1
+ import type { SVGProps } from "react";
2
+ type IconProps = SVGProps<SVGSVGElement>;
3
+ export declare function IconHome(props: IconProps): import("react").JSX.Element;
4
+ export declare function IconProducts(props: IconProps): import("react").JSX.Element;
5
+ export declare function IconGallery(props: IconProps): import("react").JSX.Element;
6
+ export declare function IconFonts(props: IconProps): import("react").JSX.Element;
7
+ export declare function IconMail(props: IconProps): import("react").JSX.Element;
8
+ export declare function IconPages(props: IconProps): import("react").JSX.Element;
9
+ export declare function IconSettings(props: IconProps): import("react").JSX.Element;
10
+ export declare function IconChart(props: IconProps): import("react").JSX.Element;
11
+ export declare function IconChevronLeft(props: IconProps): import("react").JSX.Element;
12
+ export declare function IconSearch(props: IconProps): import("react").JSX.Element;
13
+ export declare function iconForNavKey(key: string): typeof IconHome;
14
+ export {};
15
+ //# sourceMappingURL=icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../src/components/ui/icons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,KAAK,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAiBzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAMxC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,+BAO5C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,+BAQ3C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAOzC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,+BAOxC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAOzC;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,+BAO5C;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,+BAMzC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,+BAM/C;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,+BAO1C;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,mBAQxC"}
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function base(props) {
3
+ return {
4
+ width: 18,
5
+ height: 18,
6
+ viewBox: "0 0 20 20",
7
+ fill: "none",
8
+ stroke: "currentColor",
9
+ strokeWidth: 1.6,
10
+ strokeLinecap: "round",
11
+ strokeLinejoin: "round",
12
+ "aria-hidden": true,
13
+ ...props,
14
+ };
15
+ }
16
+ export function IconHome(props) {
17
+ return (_jsx("svg", { ...base(props), children: _jsx("path", { d: "M3 8.5 10 2.5l7 6v8.5a1 1 0 0 1-1 1h-4.5v-5H8.5v5H4a1 1 0 0 1-1-1V8.5z" }) }));
18
+ }
19
+ export function IconProducts(props) {
20
+ return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M3 6.5 10 2.5l7 4v9H3v-9z" }), _jsx("path", { d: "M7 11.5h6M7 14h4" })] }));
21
+ }
22
+ export function IconGallery(props) {
23
+ return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "4.5", width: "15", height: "11", rx: "1.5" }), _jsx("path", { d: "m3.5 13 3.5-3.5 2.5 2.5L12.5 9l4 4.5" }), _jsx("circle", { cx: "7", cy: "7.5", r: "1", fill: "currentColor", stroke: "none" })] }));
24
+ }
25
+ export function IconFonts(props) {
26
+ return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M5 16.5V5.5h10M7.5 16.5V9h5v7.5" }), _jsx("path", { d: "M7 5.5h6" })] }));
27
+ }
28
+ export function IconMail(props) {
29
+ return (_jsxs("svg", { ...base(props), children: [_jsx("rect", { x: "2.5", y: "4.5", width: "15", height: "11", rx: "1.5" }), _jsx("path", { d: "m3.5 6.5 6.5 4.5 3.5 2.5 6.5-2.5 6.5 4.5" })] }));
30
+ }
31
+ export function IconPages(props) {
32
+ return (_jsxs("svg", { ...base(props), children: [_jsx("path", { d: "M6 3.5h5.5L16 8v8.5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-12a1 1 0 0 1 1-1z" }), _jsx("path", { d: "M11.5 3.5V8H16" })] }));
33
+ }
34
+ export function IconSettings(props) {
35
+ return (_jsxs("svg", { ...base(props), children: [_jsx("circle", { cx: "10", cy: "10", r: "2.25" }), _jsx("path", { d: "M10 2.5v2M10 15.5v2M3.5 10h2M14.5 10h2M5.4 5.4l1.4 1.4M13.2 13.2l1.4 1.4M5.4 14.6l1.4-1.4M13.2 6.8l1.4-1.4" })] }));
36
+ }
37
+ export function IconChart(props) {
38
+ return (_jsx("svg", { ...base(props), children: _jsx("path", { d: "M4 16.5V12M8 16.5V8M12 16.5V10M16 16.5V5" }) }));
39
+ }
40
+ export function IconChevronLeft(props) {
41
+ return (_jsx("svg", { ...base(props), children: _jsx("path", { d: "M12 4.5 7.5 10 12 15.5" }) }));
42
+ }
43
+ export function IconSearch(props) {
44
+ return (_jsxs("svg", { ...base(props), children: [_jsx("circle", { cx: "9", cy: "9", r: "4.75" }), _jsx("path", { d: "M13 13l3.5 3.5" })] }));
45
+ }
46
+ export function iconForNavKey(key) {
47
+ if (key === "overview")
48
+ return IconHome;
49
+ if (key.startsWith("resource:"))
50
+ return IconProducts;
51
+ if (key === "gallery")
52
+ return IconGallery;
53
+ if (key === "fonts")
54
+ return IconFonts;
55
+ if (key === "emails")
56
+ return IconMail;
57
+ if (key.startsWith("config:"))
58
+ return IconPages;
59
+ return IconSettings;
60
+ }
@@ -0,0 +1,27 @@
1
+ import type { ReactNode } from "react";
2
+ export type CardProps = {
3
+ title?: string;
4
+ children: ReactNode;
5
+ subdued?: boolean;
6
+ };
7
+ export declare function Card({ title, children, subdued }: CardProps): import("react").JSX.Element;
8
+ export type BadgeProps = {
9
+ children: ReactNode;
10
+ tone?: "neutral" | "ok" | "danger" | "warn";
11
+ };
12
+ export declare function Badge({ children, tone }: BadgeProps): import("react").JSX.Element;
13
+ export type BannerProps = {
14
+ children: ReactNode;
15
+ tone?: "neutral" | "ok" | "danger" | "warn";
16
+ };
17
+ export declare function Banner({ children, tone }: BannerProps): import("react").JSX.Element;
18
+ export type EmptyStateProps = {
19
+ title: string;
20
+ description?: string;
21
+ action?: ReactNode;
22
+ };
23
+ export declare function EmptyState({ title, description, action }: EmptyStateProps): import("react").JSX.Element;
24
+ export declare function Skeleton({ lines }: {
25
+ lines?: number;
26
+ }): import("react").JSX.Element;
27
+ //# sourceMappingURL=primitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../../src/components/ui/primitives.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,SAAS,+BAO3D;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC7C,CAAC;AAEF,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,IAAgB,EAAE,EAAE,UAAU,+BAM/D;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC7C,CAAC;AAEF,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAgB,EAAE,EAAE,WAAW,+BAMjE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,eAAe,+BAQzE;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAS,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAQzD"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function Card({ title, children, subdued }) {
3
+ return (_jsxs("section", { className: "vertebr-card", "data-subdued": subdued || undefined, children: [title ? _jsx("header", { className: "vertebr-card-head", children: title }) : null, _jsx("div", { className: "vertebr-card-body", children: children })] }));
4
+ }
5
+ export function Badge({ children, tone = "neutral" }) {
6
+ return (_jsx("span", { className: "vertebr-badge", "data-tone": tone, children: children }));
7
+ }
8
+ export function Banner({ children, tone = "neutral" }) {
9
+ return (_jsx("div", { className: "vertebr-banner", "data-tone": tone, role: "status", children: children }));
10
+ }
11
+ export function EmptyState({ title, description, action }) {
12
+ return (_jsxs("div", { className: "vertebr-empty-state", children: [_jsx("h3", { children: title }), description ? _jsx("p", { children: description }) : null, action ? _jsx("div", { className: "vertebr-empty-state-action", children: action }) : null] }));
13
+ }
14
+ export function Skeleton({ lines = 3 }) {
15
+ return (_jsx("div", { className: "vertebr-skeleton", "aria-hidden": "true", children: Array.from({ length: lines }, (_, index) => (_jsx("span", { style: { width: `${88 - index * 12}%` } }, index))) }));
16
+ }
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ export type SaveBarState = {
3
+ visible: boolean;
4
+ message?: string;
5
+ tone?: "ok" | "danger";
6
+ isDirty: boolean;
7
+ saving: boolean;
8
+ onSave?: () => void | Promise<void>;
9
+ onDiscard?: () => void;
10
+ };
11
+ type SaveBarContextValue = {
12
+ state: SaveBarState;
13
+ register: (next: Partial<SaveBarState>) => void;
14
+ clear: () => void;
15
+ };
16
+ export declare function SaveBarProvider({ children }: {
17
+ children: ReactNode;
18
+ }): import("react").JSX.Element;
19
+ export declare function useSaveBar(): SaveBarContextValue;
20
+ export {};
21
+ //# sourceMappingURL=useSaveBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSaveBar.d.ts","sourceRoot":"","sources":["../../src/hooks/useSaveBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AAUF,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAuBpE;AAED,wBAAgB,UAAU,wBAMzB"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useMemo, useState, } from "react";
3
+ const defaultState = {
4
+ visible: false,
5
+ isDirty: false,
6
+ saving: false,
7
+ };
8
+ const SaveBarContext = createContext(null);
9
+ export function SaveBarProvider({ children }) {
10
+ const [state, setState] = useState(defaultState);
11
+ const register = useCallback((next) => {
12
+ setState((current) => ({
13
+ ...current,
14
+ ...next,
15
+ visible: next.visible ?? Boolean(next.isDirty || next.saving),
16
+ }));
17
+ }, []);
18
+ const clear = useCallback(() => {
19
+ setState(defaultState);
20
+ }, []);
21
+ const value = useMemo(() => ({ state, register, clear }), [state, register, clear]);
22
+ return (_jsx(SaveBarContext.Provider, { value: value, children: children }));
23
+ }
24
+ export function useSaveBar() {
25
+ const ctx = useContext(SaveBarContext);
26
+ if (!ctx) {
27
+ throw new Error("useSaveBar must be used within SaveBarProvider");
28
+ }
29
+ return ctx;
30
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ export type ToastTone = "ok" | "danger" | "neutral";
3
+ export type ToastItem = {
4
+ id: string;
5
+ message: string;
6
+ tone: ToastTone;
7
+ };
8
+ type ToastContextValue = {
9
+ toasts: ToastItem[];
10
+ push: (message: string, tone?: ToastTone) => void;
11
+ dismiss: (id: string) => void;
12
+ };
13
+ export declare function ToastProvider({ children }: {
14
+ children: ReactNode;
15
+ }): import("react").JSX.Element;
16
+ export declare function useToast(): ToastContextValue;
17
+ export {};
18
+ //# sourceMappingURL=useToast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToast.d.ts","sourceRoot":"","sources":["../../src/hooks/useToast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEpD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEF,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAIF,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAwBlE;AAED,wBAAgB,QAAQ,sBAMvB"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useCallback, useContext, useMemo, useState, } from "react";
3
+ const ToastContext = createContext(null);
4
+ export function ToastProvider({ children }) {
5
+ const [toasts, setToasts] = useState([]);
6
+ const dismiss = useCallback((id) => {
7
+ setToasts((current) => current.filter((item) => item.id !== id));
8
+ }, []);
9
+ const push = useCallback((message, tone = "neutral") => {
10
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
11
+ setToasts((current) => [...current, { id, message, tone }]);
12
+ window.setTimeout(() => dismiss(id), 4000);
13
+ }, [dismiss]);
14
+ const value = useMemo(() => ({ toasts, push, dismiss }), [toasts, push, dismiss]);
15
+ return (_jsx(ToastContext.Provider, { value: value, children: children }));
16
+ }
17
+ export function useToast() {
18
+ const ctx = useContext(ToastContext);
19
+ if (!ctx) {
20
+ throw new Error("useToast must be used within ToastProvider");
21
+ }
22
+ return ctx;
23
+ }
@@ -27,27 +27,21 @@ html:has(.vertebr-error) body {
27
27
  width: 100%;
28
28
  display: grid;
29
29
  grid-template-columns: var(--vb-sidebar-width) 1fr;
30
- background:
31
- radial-gradient(
32
- 900px 480px at 0% -10%,
33
- var(--vb-glow-accent),
34
- transparent 55%
35
- ),
36
- radial-gradient(
37
- 700px 400px at 100% 100%,
38
- var(--vb-glow-cool),
39
- transparent 50%
40
- ),
41
- linear-gradient(180deg, var(--vb-bg-deep) 0%, var(--vb-bg) 45%);
30
+ grid-template-rows: var(--vb-topbar-height) 1fr;
31
+ background: var(--vb-bg);
42
32
  color: var(--vb-text);
43
33
  font-family: var(--vb-font);
44
- font-size: 15px;
45
- line-height: 1.45;
34
+ font-size: var(--vb-font-size-300);
35
+ line-height: var(--vb-line-height-normal);
46
36
  -webkit-font-smoothing: antialiased;
47
37
  isolation: isolate;
48
38
  overflow: clip;
49
39
  }
50
40
 
41
+ .vertebr-root[data-sidebar-collapsed="true"] {
42
+ grid-template-columns: var(--vb-sidebar-width-collapsed) 1fr;
43
+ }
44
+
51
45
  .vertebr-root *,
52
46
  .vertebr-root *::before,
53
47
  .vertebr-root *::after {
@@ -27,23 +27,14 @@
27
27
  }
28
28
 
29
29
  .vertebr-btn-primary {
30
- background: linear-gradient(
31
- 180deg,
32
- var(--vb-btn-primary-from) 0%,
33
- var(--vb-btn-primary-to) 100%
34
- );
30
+ background: var(--vb-btn-primary-from);
35
31
  border-color: var(--vb-btn-primary-border);
36
32
  color: var(--vb-accent-contrast);
37
- font-weight: 600;
38
- box-shadow: 0 1px 0 var(--vb-surface-tint-strong) inset;
33
+ font-weight: var(--vb-font-weight-semibold);
39
34
  }
40
35
 
41
36
  .vertebr-btn-primary:hover:not(:disabled) {
42
- background: linear-gradient(
43
- 180deg,
44
- var(--vb-btn-primary-hover-from) 0%,
45
- var(--vb-btn-primary-hover-to) 100%
46
- );
37
+ background: var(--vb-btn-primary-hover-from);
47
38
  border-color: var(--vb-btn-primary-hover-border);
48
39
  }
49
40
 
@@ -20,4 +20,5 @@
20
20
  @import "./gallery.css";
21
21
  @import "./font-library.css";
22
22
  @import "./page-preview.css";
23
+ @import "./shell.css";
23
24
  @import "./states.css";
@@ -1,6 +1,7 @@
1
1
  /* ── Main ────────────────────────────────────────────── */
2
2
 
3
3
  .vertebr-main {
4
+ grid-row: 2;
4
5
  min-width: 0;
5
6
  min-height: 0;
6
7
  height: 100%;
@@ -17,13 +18,8 @@
17
18
  align-items: flex-end;
18
19
  gap: 1rem;
19
20
  padding: 1.6rem 2rem 1.15rem;
20
- border-bottom: 1px solid color-mix(in srgb, var(--vb-border) 80%, transparent);
21
- background: linear-gradient(
22
- 180deg,
23
- var(--vb-header-from) 0%,
24
- var(--vb-header-to) 100%
25
- );
26
- backdrop-filter: blur(10px);
21
+ border-bottom: 1px solid var(--vb-border-subdued);
22
+ background: var(--vb-header-from);
27
23
  }
28
24
 
29
25
  .vertebr-content {
@@ -24,7 +24,9 @@
24
24
 
25
25
  .vertebr-nav button {
26
26
  appearance: none;
27
- display: block;
27
+ display: flex;
28
+ align-items: center;
29
+ gap: var(--vb-space-300);
28
30
  width: 100%;
29
31
  border: 0;
30
32
  background: transparent;