@vertebr/react 0.2.0 → 0.4.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 (89) hide show
  1. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  2. package/dist/components/admin/VertebrAdmin.js +26 -4
  3. package/dist/components/config/ConfigForm.d.ts +2 -18
  4. package/dist/components/config/ConfigForm.d.ts.map +1 -1
  5. package/dist/components/config/ConfigForm.js +1 -281
  6. package/dist/components/config/DescriptionInput.d.ts +18 -0
  7. package/dist/components/config/DescriptionInput.d.ts.map +1 -0
  8. package/dist/components/config/DescriptionInput.js +152 -0
  9. package/dist/components/config/EnumInput.d.ts +10 -0
  10. package/dist/components/config/EnumInput.d.ts.map +1 -0
  11. package/dist/components/config/EnumInput.js +19 -0
  12. package/dist/components/config/EnumListInput.d.ts +10 -0
  13. package/dist/components/config/EnumListInput.d.ts.map +1 -0
  14. package/dist/components/config/EnumListInput.js +46 -0
  15. package/dist/components/config/ImagePickerModal.d.ts +18 -0
  16. package/dist/components/config/ImagePickerModal.d.ts.map +1 -0
  17. package/dist/components/config/ImagePickerModal.js +120 -0
  18. package/dist/components/config/PriceInput.d.ts +12 -0
  19. package/dist/components/config/PriceInput.d.ts.map +1 -0
  20. package/dist/components/config/PriceInput.js +20 -0
  21. package/dist/components/config/VisibilityEye.d.ts +8 -0
  22. package/dist/components/config/VisibilityEye.d.ts.map +1 -0
  23. package/dist/components/config/VisibilityEye.js +18 -0
  24. package/dist/components/config/form/ConfigField.d.ts +22 -0
  25. package/dist/components/config/form/ConfigField.d.ts.map +1 -0
  26. package/dist/components/config/form/ConfigField.js +53 -0
  27. package/dist/components/config/form/ConfigForm.d.ts +4 -0
  28. package/dist/components/config/form/ConfigForm.d.ts.map +1 -0
  29. package/dist/components/config/form/ConfigForm.js +75 -0
  30. package/dist/components/config/form/ConfigFormActions.d.ts +11 -0
  31. package/dist/components/config/form/ConfigFormActions.d.ts.map +1 -0
  32. package/dist/components/config/form/ConfigFormActions.js +4 -0
  33. package/dist/components/config/form/RecordConfigForm.d.ts +22 -0
  34. package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -0
  35. package/dist/components/config/form/RecordConfigForm.js +45 -0
  36. package/dist/components/config/form/SettingsConfigForm.d.ts +22 -0
  37. package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -0
  38. package/dist/components/config/form/SettingsConfigForm.js +7 -0
  39. package/dist/components/config/form/helpers/fields.d.ts +7 -0
  40. package/dist/components/config/form/helpers/fields.d.ts.map +1 -0
  41. package/dist/components/config/form/helpers/fields.js +31 -0
  42. package/dist/components/config/form/helpers/groups.d.ts +4 -0
  43. package/dist/components/config/form/helpers/groups.d.ts.map +1 -0
  44. package/dist/components/config/form/helpers/groups.js +107 -0
  45. package/dist/components/config/form/helpers/images.d.ts +4 -0
  46. package/dist/components/config/form/helpers/images.d.ts.map +1 -0
  47. package/dist/components/config/form/helpers/images.js +27 -0
  48. package/dist/components/config/form/helpers/index.d.ts +7 -0
  49. package/dist/components/config/form/helpers/index.d.ts.map +1 -0
  50. package/dist/components/config/form/helpers/index.js +6 -0
  51. package/dist/components/config/form/helpers/labels.d.ts +5 -0
  52. package/dist/components/config/form/helpers/labels.d.ts.map +1 -0
  53. package/dist/components/config/form/helpers/labels.js +28 -0
  54. package/dist/components/config/form/helpers/schema.d.ts +8 -0
  55. package/dist/components/config/form/helpers/schema.d.ts.map +1 -0
  56. package/dist/components/config/form/helpers/schema.js +30 -0
  57. package/dist/components/config/form/helpers/visibility.d.ts +13 -0
  58. package/dist/components/config/form/helpers/visibility.d.ts.map +1 -0
  59. package/dist/components/config/form/helpers/visibility.js +86 -0
  60. package/dist/components/config/form/types.d.ts +29 -0
  61. package/dist/components/config/form/types.d.ts.map +1 -0
  62. package/dist/components/config/form/types.js +1 -0
  63. package/dist/components/resources/ResourceCell.d.ts.map +1 -1
  64. package/dist/components/resources/ResourceCell.js +5 -19
  65. package/dist/components/resources/ResourceCreateModal.d.ts +18 -0
  66. package/dist/components/resources/ResourceCreateModal.d.ts.map +1 -0
  67. package/dist/components/resources/ResourceCreateModal.js +22 -0
  68. package/dist/components/resources/ResourceTable.d.ts +7 -2
  69. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  70. package/dist/components/resources/ResourceTable.js +92 -7
  71. package/dist/hooks/useVertebrControls.d.ts.map +1 -1
  72. package/dist/hooks/useVertebrControls.js +5 -4
  73. package/dist/index.d.ts +11 -2
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +6 -1
  76. package/dist/lib/description.d.ts +18 -0
  77. package/dist/lib/description.d.ts.map +1 -0
  78. package/dist/lib/description.js +85 -0
  79. package/dist/lib/images.d.ts +14 -0
  80. package/dist/lib/images.d.ts.map +1 -0
  81. package/dist/lib/images.js +63 -0
  82. package/dist/lib/price.d.ts +15 -0
  83. package/dist/lib/price.d.ts.map +1 -0
  84. package/dist/lib/price.js +50 -0
  85. package/dist/styles/form.css +420 -0
  86. package/dist/styles/modal.css +133 -0
  87. package/dist/styles/panel.css +18 -1
  88. package/dist/styles/table.css +10 -1
  89. package/package.json +10 -3
@@ -1 +1 @@
1
- {"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"AAkBA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtF,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAiCF,wBAAgB,YAAY,CAAC,EAC3B,OAAsB,EACtB,KAAuB,EACvB,aAA6B,EAC7B,eAAiC,EACjC,eAAiC,EACjC,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE,iBAAiB,+BAolBnB"}
1
+ {"version":3,"file":"VertebrAdmin.d.ts","sourceRoot":"","sources":["../../../src/components/admin/VertebrAdmin.tsx"],"names":[],"mappings":"AAkBA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtF,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAiCF,wBAAgB,YAAY,CAAC,EAC3B,OAAsB,EACtB,KAAuB,EACvB,aAA6B,EAC7B,eAAiC,EACjC,eAAiC,EACjC,cAAc,EACd,aAAa,EACb,SAAS,GACV,EAAE,iBAAiB,+BAgnBnB"}
@@ -214,13 +214,35 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
214
214
  const refreshGallery = useCallback(async () => {
215
215
  setGalleryLoading(true);
216
216
  try {
217
- const data = await client.getGallery();
217
+ const data = await client.getGallery({ fresh: true });
218
218
  setGalleryData(data);
219
219
  }
220
220
  finally {
221
221
  setGalleryLoading(false);
222
222
  }
223
223
  }, [client]);
224
+ const listGallery = useCallback(async () => {
225
+ const data = await client.getGallery();
226
+ return data.items;
227
+ }, [client]);
228
+ const uploadGallery = useCallback(async (files) => {
229
+ if (files.length === 1 && files[0]) {
230
+ const { item } = await client.uploadGalleryFile(files[0]);
231
+ return [item];
232
+ }
233
+ const { items } = await client.uploadGalleryFiles(files);
234
+ return items;
235
+ }, [client]);
236
+ const galleryPicker = catalog?.gallery
237
+ ? {
238
+ galleryUpload: {
239
+ accept: catalog.gallery.accept,
240
+ maxBytes: catalog.gallery.maxBytes,
241
+ },
242
+ onListGallery: listGallery,
243
+ onUploadGallery: uploadGallery,
244
+ }
245
+ : {};
224
246
  const envList = catalog?.environments ?? bootstrap?.environments ?? [];
225
247
  const activeEnv = envList.find((env) => env.id === environment);
226
248
  const authRequired = Boolean(activeEnv?.auth?.required);
@@ -310,7 +332,7 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
310
332
  await client.deleteGalleryItem(id);
311
333
  await refreshGallery();
312
334
  }
313
- : undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, permissions: resourceData.permissions, actions: resourceData.actions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
335
+ : undefined })) : null, selection?.kind === "resource" && resourceData ? (_jsx(ResourcePanel, { schema: resourceData.schema, items: resourceData.items, label: resourceData.label, permissions: resourceData.permissions, actions: resourceData.actions, uploads: resourceData.uploads, storageKey: resourceData.id, onUpdate: resourceData.permissions.update
314
336
  ? async (rowId, value) => {
315
337
  await client.updateResource(resourceData.id, rowId, value);
316
338
  await refreshResource();
@@ -339,13 +361,13 @@ export function VertebrAdmin({ apiBase = "/admin/api", title = "Control plane",
339
361
  const result = await client.uploadResourceFiles(resourceData.id, field, files);
340
362
  return result.files;
341
363
  }
342
- : undefined })) : null, selection?.kind === "config" && configData ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : "Settings" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] }), _jsx(ConfigForm, { schema: configData.schema, value: configData.value, onSave: async (next) => {
364
+ : undefined, ...galleryPicker })) : null, selection?.kind === "config" && configData ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "vertebr-panel-head", children: [_jsx("h2", { children: isPageSelection ? "Page content" : "Settings" }), _jsxs("span", { className: "vertebr-count", children: [Object.keys(configData.schema).length, " fields"] })] }), _jsx(ConfigForm, { schema: configData.schema, value: configData.value, onSave: async (next) => {
343
365
  const saved = await client.setConfig(configData.id, next);
344
366
  setConfigData({
345
367
  ...configData,
346
368
  value: saved.value,
347
369
  });
348
- } })] })) : null] })) : (_jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
370
+ }, ...galleryPicker })] })) : null] })) : (_jsx("section", { className: "vertebr-panel", children: _jsx("div", { className: "vertebr-empty", children: authRequired
349
371
  ? "Authenticated access required for this environment."
350
372
  : loading
351
373
  ? "Loading…"
@@ -1,19 +1,3 @@
1
- import type { FieldSchema, ResourceUploadMeta } from "@vertebr/client";
2
- export type ConfigFormVariant = "config" | "record";
3
- type ConfigFormProps = {
4
- schema: Record<string, FieldSchema>;
5
- value: Record<string, unknown>;
6
- onSave: (value: Record<string, unknown>) => Promise<void>;
7
- /** `record` uses a dense product-style editor. */
8
- variant?: ConfigFormVariant;
9
- /** Field-keyed upload capabilities from the host resource. */
10
- uploads?: Record<string, ResourceUploadMeta>;
11
- /** Upload files for a field; return public URL(s). */
12
- onUpload?: (field: string, files: File[]) => Promise<Array<{
13
- url: string;
14
- name?: string;
15
- }>>;
16
- };
17
- export declare function ConfigForm({ schema, value, onSave, variant, uploads, onUpload, }: ConfigFormProps): import("react").JSX.Element;
18
- export {};
1
+ export { ConfigForm } from "./form/ConfigForm";
2
+ export type { ConfigFormProps, ConfigFormVariant } from "./form/types";
19
3
  //# sourceMappingURL=ConfigForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../src/components/config/ConfigForm.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,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;CACrD,CAAC;AAqNF,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAkB,EAClB,OAAO,EACP,QAAQ,GACT,EAAE,eAAe,+BAyUjB"}
1
+ {"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../src/components/config/ConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
@@ -1,281 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useState } from "react";
3
- import { useForm, Controller, useWatch } from "react-hook-form";
4
- import { z } from "zod";
5
- import { zodResolver } from "@hookform/resolvers/zod";
6
- import * as Label from "@radix-ui/react-label";
7
- import * as Switch from "@radix-ui/react-switch";
8
- import { ImageUploadControl } from "../resources/ImageUploadControl";
9
- const RECORD_GROUP_ORDER = [
10
- "Details",
11
- "Content",
12
- "Pricing",
13
- "Inventory",
14
- "Media",
15
- "Status",
16
- ];
17
- function buildZodSchema(schema) {
18
- const shape = {};
19
- for (const [key, field] of Object.entries(schema)) {
20
- let zodField;
21
- switch (field.type) {
22
- case "number":
23
- zodField = z.number();
24
- if (field.min != null)
25
- zodField = zodField.min(field.min);
26
- if (field.max != null)
27
- zodField = zodField.max(field.max);
28
- break;
29
- case "boolean":
30
- zodField = z.boolean();
31
- break;
32
- default:
33
- zodField = z.string();
34
- }
35
- if (!field.required) {
36
- zodField = zodField.optional();
37
- }
38
- shape[key] = zodField;
39
- }
40
- return z.object(shape);
41
- }
42
- function humanizeKey(key) {
43
- return key
44
- .replace(/([a-z])([A-Z])/g, "$1 $2")
45
- .replace(/[_-]+/g, " ")
46
- .trim();
47
- }
48
- function escapeRegExp(value) {
49
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
- }
51
- function looksLikeUrl(value) {
52
- return /^https?:\/\//i.test(value) || value.startsWith("//");
53
- }
54
- function isImageField(key, field) {
55
- const hay = `${key} ${field?.label ?? ""}`;
56
- return /(image|img|photo|avatar|thumbnail|thumb|cover)/i.test(hay);
57
- }
58
- function isStockField(key, field) {
59
- const hay = `${key} ${field?.label ?? ""}`;
60
- return /(stock|inventory|qty|quantity)/i.test(hay);
61
- }
62
- function spanFor(key, field, variant) {
63
- if (field.type === "boolean")
64
- return "full";
65
- if (field.type === "richtext")
66
- return "full";
67
- if (variant === "record" && isImageField(key, field))
68
- return "full";
69
- return undefined;
70
- }
71
- function inferConfigGroup(key, field) {
72
- if (field.group?.trim())
73
- return field.group.trim();
74
- const source = field.label?.trim() || humanizeKey(key);
75
- const words = source.split(/\s+/).filter(Boolean);
76
- if (words.length >= 2)
77
- return words[0];
78
- return "General";
79
- }
80
- function inferRecordGroup(key, field) {
81
- if (field.group?.trim())
82
- return field.group.trim();
83
- const k = key.toLowerCase();
84
- const label = (field.label ?? "").toLowerCase();
85
- const hay = `${k} ${label}`;
86
- if (/(image|img|photo|avatar|thumbnail|thumb|cover|media_url)/i.test(hay)) {
87
- return "Media";
88
- }
89
- if (/(price|cost|cents|amount|msrp)/i.test(hay)) {
90
- return "Pricing";
91
- }
92
- if (/(stock|inventory|qty|quantity)/i.test(hay)) {
93
- return "Inventory";
94
- }
95
- if (field.type === "boolean" ||
96
- /(^|_)(active|enabled|published|status|visible)(_|$)/i.test(k) ||
97
- /^(active|enabled|published|status|visible)$/i.test(label)) {
98
- return "Status";
99
- }
100
- if (field.type === "richtext" ||
101
- /(description|body|content|summary|bio)/i.test(hay)) {
102
- return "Content";
103
- }
104
- return "Details";
105
- }
106
- function displayLabel(key, field, group) {
107
- const label = field.label?.trim() || humanizeKey(key);
108
- const stripped = label.replace(new RegExp(`^${escapeRegExp(group)}\\s+`, "i"), "");
109
- if (/^inventory$/i.test(group)) {
110
- const stock = label.replace(/^stock\s+/i, "").trim();
111
- if (stock && stock !== label)
112
- return stock;
113
- }
114
- if (/^pricing$/i.test(group) && /cents/i.test(label)) {
115
- return label.replace(/\s*\(cents\)/i, "").trim() || label;
116
- }
117
- return stripped || label;
118
- }
119
- function sortGroups(groups, variant) {
120
- if (variant !== "record")
121
- return groups;
122
- const rank = (id) => {
123
- const index = RECORD_GROUP_ORDER.indexOf(id);
124
- return index === -1 ? RECORD_GROUP_ORDER.length + 1 : index;
125
- };
126
- return [...groups].sort((a, b) => rank(a.id) - rank(b.id));
127
- }
128
- function groupFields(schema, variant) {
129
- const order = [];
130
- const map = new Map();
131
- const explicit = new Set();
132
- for (const entry of Object.entries(schema)) {
133
- const [key, field] = entry;
134
- const group = variant === "record"
135
- ? inferRecordGroup(key, field)
136
- : inferConfigGroup(key, field);
137
- if (field.group?.trim())
138
- explicit.add(group);
139
- if (!map.has(group)) {
140
- map.set(group, []);
141
- order.push(group);
142
- }
143
- map.get(group).push(entry);
144
- }
145
- if (variant === "config") {
146
- for (const [group, fields] of [...map.entries()]) {
147
- if (fields.length > 1 || explicit.has(group) || group === "General") {
148
- continue;
149
- }
150
- if (!map.has("General")) {
151
- map.set("General", []);
152
- order.unshift("General");
153
- }
154
- map.get("General").push(...fields);
155
- map.delete(group);
156
- const idx = order.indexOf(group);
157
- if (idx >= 0)
158
- order.splice(idx, 1);
159
- }
160
- }
161
- const groups = order
162
- .filter((id) => map.has(id))
163
- .map((id) => ({
164
- id,
165
- label: id,
166
- fields: map.get(id),
167
- }));
168
- return sortGroups(groups, variant);
169
- }
170
- function findPrimaryImageKey(schema, values) {
171
- for (const [key, field] of Object.entries(schema)) {
172
- if (!isImageField(key, field))
173
- continue;
174
- const value = values?.[key];
175
- if (typeof value === "string" && looksLikeUrl(value))
176
- return key;
177
- }
178
- for (const [key, field] of Object.entries(schema)) {
179
- if (isImageField(key, field))
180
- return key;
181
- }
182
- return null;
183
- }
184
- export function ConfigForm({ schema, value, onSave, variant = "config", uploads, onUpload, }) {
185
- const zodSchema = useMemo(() => buildZodSchema(schema), [schema]);
186
- const groups = useMemo(() => groupFields(schema, variant), [schema, variant]);
187
- const [status, setStatus] = useState(null);
188
- const [tone, setTone] = useState();
189
- const form = useForm({
190
- resolver: zodResolver(zodSchema),
191
- defaultValues: value,
192
- });
193
- const watched = useWatch({ control: form.control });
194
- useEffect(() => {
195
- form.reset(value);
196
- }, [value, form]);
197
- const onSubmit = form.handleSubmit(async (data) => {
198
- setStatus("Saving…");
199
- setTone(undefined);
200
- try {
201
- await onSave(data);
202
- setStatus("Saved");
203
- setTone("ok");
204
- }
205
- catch (error) {
206
- setStatus(error instanceof Error ? error.message : "Save failed");
207
- setTone("danger");
208
- }
209
- });
210
- const isDirty = form.formState.isDirty;
211
- useEffect(() => {
212
- const onKeyDown = (event) => {
213
- if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "s") {
214
- event.preventDefault();
215
- void onSubmit();
216
- }
217
- };
218
- window.addEventListener("keydown", onKeyDown);
219
- return () => window.removeEventListener("keydown", onKeyDown);
220
- }, [onSubmit]);
221
- const renderField = (key, field, groupLabel, options) => {
222
- const label = displayLabel(key, field, groupLabel);
223
- const showInlinePreview = !options?.hideImagePreview &&
224
- isImageField(key, field) &&
225
- typeof watched?.[key] === "string" &&
226
- looksLikeUrl(String(watched[key]));
227
- if (field.type === "boolean") {
228
- return (_jsxs("div", { className: "vertebr-switch-row", children: [_jsx(Label.Root, { htmlFor: key, children: label }), _jsx(Controller, { control: form.control, name: key, render: ({ field: controllerField }) => (_jsx(Switch.Root, { className: "vertebr-switch", id: key, checked: Boolean(controllerField.value), onCheckedChange: controllerField.onChange, children: _jsx(Switch.Thumb, { className: "vertebr-switch-thumb" }) })) })] }, key));
229
- }
230
- return (_jsxs("div", { className: "vertebr-field", "data-span": spanFor(key, field, variant), "data-kind": isImageField(key, field) ? "image" : undefined, children: [_jsx(Label.Root, { htmlFor: key, children: label }), showInlinePreview ? (_jsxs("div", { className: "vertebr-field-media", children: [_jsx("img", { src: String(watched[key]), alt: "" }), _jsx("input", { id: key, type: "text", readOnly: field.readOnly, ...form.register(key) })] })) : field.type === "richtext" ? (_jsx("textarea", { id: key, rows: 3, readOnly: field.readOnly, ...form.register(key) })) : field.type === "number" ? (_jsx(Controller, { control: form.control, name: key, render: ({ field: controllerField }) => (_jsx("input", { id: key, type: "number", readOnly: field.readOnly, value: typeof controllerField.value === "number" &&
231
- Number.isFinite(controllerField.value)
232
- ? controllerField.value
233
- : "", onBlur: controllerField.onBlur, onChange: (event) => {
234
- const next = event.target.value;
235
- controllerField.onChange(next === "" ? undefined : Number(next));
236
- }, name: controllerField.name, ref: controllerField.ref })) })) : (_jsx("input", { id: key, type: "text", readOnly: field.readOnly, ...form.register(key) })), !options?.hideUpload &&
237
- uploads?.[key] &&
238
- onUpload &&
239
- !field.readOnly ? (_jsx(ImageUploadControl, { meta: uploads[key], onUpload: async (files) => {
240
- const [first] = await onUpload(key, files);
241
- if (first?.url) {
242
- form.setValue(key, first.url, {
243
- shouldDirty: true,
244
- shouldTouch: true,
245
- });
246
- }
247
- } })) : null] }, key));
248
- };
249
- const renderActions = () => (_jsxs("div", { className: "vertebr-actions", children: [_jsx("button", { className: "vertebr-btn vertebr-btn-primary", type: "submit", disabled: form.formState.isSubmitting || !isDirty, children: form.formState.isSubmitting ? "Saving…" : "Save changes" }), isDirty ? (_jsx("button", { className: "vertebr-btn", type: "button", onClick: () => {
250
- form.reset(value);
251
- setStatus(null);
252
- setTone(undefined);
253
- }, children: "Discard" })) : null, isDirty ? (_jsx("span", { className: "vertebr-dirty", children: "Unsaved" })) : status ? (_jsx("span", { className: "vertebr-status", "data-tone": tone, children: status })) : null, _jsx("span", { className: "vertebr-actions-spacer" }), _jsx("span", { className: "vertebr-kbd", children: "\u2318S" })] }));
254
- if (variant === "record") {
255
- const imageKey = findPrimaryImageKey(schema, watched);
256
- const imageValue = imageKey && typeof watched?.[imageKey] === "string"
257
- ? String(watched[imageKey])
258
- : "";
259
- const asideGroups = groups.filter((g) => /^(Media|Status)$/i.test(g.id));
260
- const mainGroups = groups.filter((g) => !/^(Media|Status)$/i.test(g.id));
261
- return (_jsxs("form", { className: "vertebr-form", "data-variant": "record", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: _jsxs("div", { className: "vertebr-record-layout", children: [_jsxs("aside", { className: "vertebr-record-aside", children: [_jsx("div", { className: "vertebr-record-preview", children: imageValue && looksLikeUrl(imageValue) ? (_jsx("img", { src: imageValue, alt: "" })) : (_jsx("div", { className: "vertebr-record-preview-empty", children: "No image" })) }), imageKey && uploads?.[imageKey] && onUpload ? (_jsx(ImageUploadControl, { meta: uploads[imageKey], label: "Drop a new image or browse", onUpload: async (files) => {
262
- const [first] = await onUpload(imageKey, files);
263
- if (first?.url) {
264
- form.setValue(imageKey, first.url, {
265
- shouldDirty: true,
266
- shouldTouch: true,
267
- });
268
- }
269
- } })) : null, asideGroups.map((group) => (_jsxs("div", { className: "vertebr-record-block", "data-layout": /^inventory$/i.test(group.id) ? "chips" : undefined, children: [_jsx("div", { className: "vertebr-record-block-label", children: group.label }), _jsx("div", { className: "vertebr-form-fields", "data-layout": isStockField(group.fields[0]?.[0] ?? "", group.fields[0]?.[1])
270
- ? "chips"
271
- : undefined, children: group.fields.map(([key, field]) => renderField(key, field, group.label, {
272
- hideImagePreview: true,
273
- hideUpload: key === imageKey,
274
- })) })] }, group.id)))] }), _jsx("div", { className: "vertebr-record-main", children: mainGroups.map((group) => {
275
- const inventory = /^inventory$/i.test(group.id);
276
- return (_jsxs("div", { className: "vertebr-record-block", "data-layout": inventory ? "chips" : undefined, children: [_jsx("div", { className: "vertebr-record-block-label", children: group.label }), _jsx("div", { className: "vertebr-form-fields", "data-layout": inventory ? "chips" : undefined, children: group.fields.map(([key, field]) => renderField(key, field, group.label)) })] }, group.id));
277
- }) })] }) }), renderActions()] }));
278
- }
279
- const singleGroup = groups.length === 1;
280
- return (_jsxs("form", { className: "vertebr-form", "data-variant": "config", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: groups.map((group) => (_jsxs("div", { className: "vertebr-form-section", "data-plain": singleGroup ? "true" : undefined, children: [!singleGroup ? (_jsxs("div", { className: "vertebr-form-section-head", children: [_jsx("h3", { children: group.label }), _jsx("span", { className: "vertebr-form-section-count", children: group.fields.length })] })) : null, _jsx("div", { className: "vertebr-form-fields", children: group.fields.map(([key, field]) => renderField(key, field, group.label)) })] }, group.id))) }), renderActions()] }));
281
- }
1
+ export { ConfigForm } from "./form/ConfigForm";
@@ -0,0 +1,18 @@
1
+ import type { GalleryItem, ResourceUploadMeta } from "@vertebr/client";
2
+ import type { RecordImage } from "../../lib/images";
3
+ export type DescriptionInputProps = {
4
+ id: string;
5
+ name?: string;
6
+ value: unknown;
7
+ readOnly?: boolean;
8
+ placeholder?: string;
9
+ recordImages?: RecordImage[];
10
+ galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
11
+ onListGallery?: () => Promise<GalleryItem[]>;
12
+ onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
13
+ onBlur?: () => void;
14
+ onChange: (next: string) => void;
15
+ inputRef?: React.Ref<HTMLElement>;
16
+ };
17
+ export declare function DescriptionInput({ id, name, value, readOnly, placeholder, recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, }: DescriptionInputProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=DescriptionInput.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,152 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { EditorContent, useEditor, useEditorState } from "@tiptap/react";
4
+ import StarterKit from "@tiptap/starter-kit";
5
+ import { Markdown } from "@tiptap/markdown";
6
+ import { Placeholder } from "@tiptap/extensions";
7
+ import Image from "@tiptap/extension-image";
8
+ import { TableKit } from "@tiptap/extension-table";
9
+ import { descriptionStats } from "../../lib/description";
10
+ import { ImagePickerModal } from "./ImagePickerModal";
11
+ function assignRef(ref, node) {
12
+ if (!ref)
13
+ return;
14
+ if (typeof ref === "function")
15
+ ref(node);
16
+ else
17
+ ref.current = node;
18
+ }
19
+ function ToolbarButton({ active, disabled, title, onClick, children, }) {
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
+ }
22
+ export function DescriptionInput({ id, name, value, readOnly, placeholder = "Write a description…", recordImages, galleryUpload, onListGallery, onUploadGallery, onBlur, onChange, inputRef, }) {
23
+ const text = typeof value === "string" ? value : "";
24
+ const onChangeRef = useRef(onChange);
25
+ const onBlurRef = useRef(onBlur);
26
+ const lastEmitted = useRef(text);
27
+ const [pickerOpen, setPickerOpen] = useState(false);
28
+ onChangeRef.current = onChange;
29
+ onBlurRef.current = onBlur;
30
+ const editor = useEditor({
31
+ immediatelyRender: false,
32
+ shouldRerenderOnTransaction: false,
33
+ content: text,
34
+ contentType: "markdown",
35
+ editable: !readOnly,
36
+ extensions: [
37
+ StarterKit.configure({
38
+ heading: { levels: [2, 3] },
39
+ underline: false,
40
+ link: {
41
+ openOnClick: false,
42
+ defaultProtocol: "https",
43
+ },
44
+ }),
45
+ Image.configure({ inline: false, allowBase64: false }),
46
+ TableKit.configure({
47
+ table: { resizable: false },
48
+ }),
49
+ Placeholder.configure({ placeholder }),
50
+ Markdown.configure({
51
+ markedOptions: { gfm: true },
52
+ }),
53
+ ],
54
+ editorProps: {
55
+ attributes: {
56
+ id,
57
+ class: "vertebr-md-content",
58
+ },
59
+ },
60
+ onUpdate: ({ editor: next }) => {
61
+ const markdown = next.getMarkdown();
62
+ lastEmitted.current = markdown;
63
+ onChangeRef.current(markdown);
64
+ },
65
+ onBlur: () => {
66
+ onBlurRef.current?.();
67
+ },
68
+ });
69
+ useEffect(() => {
70
+ if (!editor)
71
+ return;
72
+ assignRef(inputRef, editor.view.dom);
73
+ return () => assignRef(inputRef, null);
74
+ }, [editor, inputRef]);
75
+ useEffect(() => {
76
+ if (!editor)
77
+ return;
78
+ editor.setEditable(!readOnly);
79
+ }, [editor, readOnly]);
80
+ useEffect(() => {
81
+ if (!editor)
82
+ return;
83
+ if (text === lastEmitted.current)
84
+ return;
85
+ if (text === editor.getMarkdown()) {
86
+ lastEmitted.current = text;
87
+ return;
88
+ }
89
+ editor.commands.setContent(text, {
90
+ contentType: "markdown",
91
+ emitUpdate: false,
92
+ });
93
+ lastEmitted.current = text;
94
+ }, [editor, text]);
95
+ const marks = useEditorState({
96
+ editor,
97
+ selector: ({ editor: next }) => ({
98
+ bold: next?.isActive("bold") ?? false,
99
+ italic: next?.isActive("italic") ?? false,
100
+ strike: next?.isActive("strike") ?? false,
101
+ code: next?.isActive("code") ?? false,
102
+ codeBlock: next?.isActive("codeBlock") ?? false,
103
+ heading: next?.isActive("heading") ?? false,
104
+ quote: next?.isActive("blockquote") ?? false,
105
+ bullet: next?.isActive("bulletList") ?? false,
106
+ ordered: next?.isActive("orderedList") ?? false,
107
+ table: next?.isActive("table") ?? false,
108
+ link: next?.isActive("link") ?? false,
109
+ stats: descriptionStats(next?.getText() ?? text),
110
+ }),
111
+ });
112
+ const stats = marks?.stats ?? descriptionStats(text);
113
+ const locked = readOnly || !editor;
114
+ const setLink = () => {
115
+ if (!editor || readOnly)
116
+ return;
117
+ const previous = editor.getAttributes("link").href;
118
+ const href = window.prompt("Link URL", typeof previous === "string" && previous ? previous : "https://");
119
+ if (href === null)
120
+ return;
121
+ if (!href.trim()) {
122
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
123
+ return;
124
+ }
125
+ editor.chain().focus().extendMarkRange("link").setLink({ href: href.trim() }).run();
126
+ };
127
+ const insertImages = (images) => {
128
+ if (!editor || images.length === 0)
129
+ return;
130
+ editor
131
+ .chain()
132
+ .focus()
133
+ .insertContent(images.map((image) => ({
134
+ type: "image",
135
+ attrs: { src: image.url, alt: image.name },
136
+ })))
137
+ .run();
138
+ setPickerOpen(false);
139
+ };
140
+ return (_jsxs("div", { className: "vertebr-field-prose", onKeyDown: (event) => {
141
+ if (!(event.metaKey || event.ctrlKey))
142
+ return;
143
+ if (event.key.toLowerCase() !== "k")
144
+ return;
145
+ event.preventDefault();
146
+ setLink();
147
+ }, children: [name ? _jsx("input", { type: "hidden", name: name, value: text, readOnly: true }) : null, _jsxs("div", { className: "vertebr-md-toolbar", role: "toolbar", "aria-label": "Format", children: [_jsx(ToolbarButton, { active: marks?.bold, disabled: locked, title: "Bold", onClick: () => editor?.chain().focus().toggleBold().run(), children: _jsx("span", { className: "vertebr-md-glyph", children: "B" }) }), _jsx(ToolbarButton, { active: marks?.italic, disabled: locked, title: "Italic", onClick: () => editor?.chain().focus().toggleItalic().run(), children: _jsx("span", { className: "vertebr-md-glyph vertebr-md-italic", children: "I" }) }), _jsx(ToolbarButton, { active: marks?.strike, disabled: locked, title: "Strikethrough", onClick: () => editor?.chain().focus().toggleStrike().run(), children: _jsx("span", { className: "vertebr-md-glyph vertebr-md-strike", children: "S" }) }), _jsx(ToolbarButton, { active: marks?.code, disabled: locked, title: "Inline code", onClick: () => editor?.chain().focus().toggleCode().run(), children: "</>" }), _jsx(ToolbarButton, { active: marks?.codeBlock, disabled: locked, title: "Code block", onClick: () => editor?.chain().focus().toggleCodeBlock().run(), children: "{ }" }), _jsx("span", { className: "vertebr-md-sep", "aria-hidden": "true" }), _jsx(ToolbarButton, { active: marks?.heading, disabled: locked, title: "Heading", onClick: () => editor?.chain().focus().toggleHeading({ level: 2 }).run(), children: "H" }), _jsx(ToolbarButton, { active: marks?.quote, disabled: locked, title: "Quote", onClick: () => editor?.chain().focus().toggleBlockquote().run(), children: "\u201C" }), _jsx(ToolbarButton, { active: marks?.bullet, disabled: locked, title: "Bulleted list", onClick: () => editor?.chain().focus().toggleBulletList().run(), children: "\u2022" }), _jsx(ToolbarButton, { active: marks?.ordered, disabled: locked, title: "Numbered list", onClick: () => editor?.chain().focus().toggleOrderedList().run(), children: "1." }), _jsx("span", { className: "vertebr-md-sep", "aria-hidden": "true" }), _jsx(ToolbarButton, { active: marks?.table, disabled: locked, title: "Table", onClick: () => editor?.chain().focus().insertTable({
148
+ rows: 3,
149
+ cols: 3,
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] }));
152
+ }
@@ -0,0 +1,10 @@
1
+ import type { FieldSchema } from "@vertebr/client";
2
+ export type EnumInputProps = {
3
+ id: string;
4
+ value: unknown;
5
+ field: FieldSchema;
6
+ readOnly?: boolean;
7
+ onChange: (value: string) => void;
8
+ };
9
+ export declare function EnumInput({ id, value, field, readOnly, onChange, }: EnumInputProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=EnumInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnumInput.d.ts","sourceRoot":"","sources":["../../../src/components/config/EnumInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,wBAAgB,SAAS,CAAC,EACxB,EAAE,EACF,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE,cAAc,+BAmDhB"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as Select from "@radix-ui/react-select";
3
+ import { optionLabel } from "./form/helpers/labels";
4
+ export function EnumInput({ id, value, field, readOnly, onChange, }) {
5
+ const options = field.options?.filter(Boolean) ?? [];
6
+ const current = typeof value === "string" && options.includes(value)
7
+ ? value
8
+ : (options[0] ?? "");
9
+ if (options.length === 0) {
10
+ return (_jsx("input", { id: id, type: "text", readOnly: readOnly, value: current, disabled: true }));
11
+ }
12
+ return (_jsxs(Select.Root, { value: current, onValueChange: onChange, disabled: readOnly, children: [_jsxs(Select.Trigger, { id: id, className: "vertebr-select-trigger", children: [_jsx("span", { className: "vertebr-select-value", children: _jsx(Select.Value, { children: optionLabel(current, field) }) }), _jsx(Select.Icon, { className: "vertebr-select-icon", children: _jsx(ChevronIcon, {}) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { className: "vertebr-select-content", position: "popper", sideOffset: 6, children: _jsx(Select.Viewport, { className: "vertebr-select-viewport", children: options.map((option) => (_jsxs(Select.Item, { value: option, className: "vertebr-select-item", children: [_jsx(Select.ItemText, { children: optionLabel(option, field) }), _jsx(Select.ItemIndicator, { className: "vertebr-select-check", children: _jsx(CheckIcon, {}) })] }, option))) }) }) })] }));
13
+ }
14
+ function ChevronIcon() {
15
+ return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: _jsx("path", { d: "M2.5 4.5 6 8l3.5-3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }));
16
+ }
17
+ function CheckIcon() {
18
+ return (_jsx("svg", { width: "12", height: "12", viewBox: "0 0 12 12", "aria-hidden": "true", children: _jsx("path", { d: "M2.5 6.2 5 8.7 9.5 3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }));
19
+ }
@@ -0,0 +1,10 @@
1
+ import type { FieldSchema } from "@vertebr/client";
2
+ export type EnumListInputProps = {
3
+ id: string;
4
+ value: unknown;
5
+ field: FieldSchema;
6
+ readOnly?: boolean;
7
+ onChange: (value: string[]) => void;
8
+ };
9
+ export declare function EnumListInput({ id, value, field, readOnly, onChange, }: EnumListInputProps): import("react").JSX.Element;
10
+ //# sourceMappingURL=EnumListInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnumListInput.d.ts","sourceRoot":"","sources":["../../../src/components/config/EnumListInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACrC,CAAC;AAiBF,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE,kBAAkB,+BAiEpB"}