@pipelex/mthds-form 0.1.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.
@@ -0,0 +1,1118 @@
1
+ "use client";
2
+ import { toStoredDateValue, toDateInputValue, isFilled } from '../chunk-DAP47OGP.js';
3
+ import { clsx } from 'clsx';
4
+ import { twMerge } from 'tailwind-merge';
5
+ import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image, FileText, CalendarDays, Type, Loader2, Upload, Link2, ImageOff, EyeOff, Eye, X, Minus, Plus, Trash2 } from 'lucide-react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import * as React2 from 'react';
8
+ import { createContext, useMemo, useContext, useState, useRef, useCallback, useEffect } from 'react';
9
+ import * as SwitchPrimitives from '@radix-ui/react-switch';
10
+ import { useDropzone } from 'react-dropzone';
11
+ import * as SelectPrimitive from '@radix-ui/react-select';
12
+ import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
13
+ import * as TogglePrimitive from '@radix-ui/react-toggle';
14
+ import { cva } from 'class-variance-authority';
15
+
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ var CATEGORY_ICON = {
20
+ text: Type,
21
+ date: CalendarDays,
22
+ document: FileText,
23
+ image: Image,
24
+ number: Hash,
25
+ boolean: ToggleLeft,
26
+ choice: ListChecks,
27
+ structured: Braces,
28
+ list: List
29
+ };
30
+ function ConceptPill({ conceptRef, category, className }) {
31
+ const Icon2 = CATEGORY_ICON[category];
32
+ const label = conceptRef ?? category;
33
+ return /* @__PURE__ */ jsxs(
34
+ "span",
35
+ {
36
+ className: cn(
37
+ "inline-flex items-center gap-1 font-mono text-[10.5px] leading-none text-muted-foreground",
38
+ className
39
+ ),
40
+ children: [
41
+ /* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3 shrink-0 opacity-70", strokeWidth: 1.75, "aria-hidden": true }),
42
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: label })
43
+ ]
44
+ }
45
+ );
46
+ }
47
+ var Switch = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
48
+ SwitchPrimitives.Root,
49
+ {
50
+ className: cn(
51
+ "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
52
+ className
53
+ ),
54
+ ...props,
55
+ ref,
56
+ children: /* @__PURE__ */ jsx(
57
+ SwitchPrimitives.Thumb,
58
+ {
59
+ className: cn(
60
+ "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
61
+ )
62
+ }
63
+ )
64
+ }
65
+ ));
66
+ Switch.displayName = SwitchPrimitives.Root.displayName;
67
+ function BooleanField({ field, value, onChange, id, disabled }) {
68
+ return /* @__PURE__ */ jsxs(
69
+ "div",
70
+ {
71
+ className: cn(
72
+ "flex items-center justify-between gap-4 rounded-md border border-border bg-input px-3.5 py-3",
73
+ disabled && "opacity-50"
74
+ ),
75
+ children: [
76
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
77
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
78
+ /* @__PURE__ */ jsx(
79
+ "label",
80
+ {
81
+ htmlFor: id,
82
+ className: "font-mono text-[13px] font-medium leading-none text-foreground",
83
+ children: field.title ?? field.name
84
+ }
85
+ ),
86
+ /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "boolean" })
87
+ ] }),
88
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description })
89
+ ] }),
90
+ /* @__PURE__ */ jsx(
91
+ Switch,
92
+ {
93
+ id,
94
+ checked: value ?? false,
95
+ onCheckedChange: onChange,
96
+ disabled,
97
+ className: "shrink-0"
98
+ }
99
+ )
100
+ ]
101
+ }
102
+ );
103
+ }
104
+ var DEFAULT_FIELD_STRINGS = {
105
+ optionalBadge: "optional",
106
+ selectPlaceholder: "Select\u2026",
107
+ typeValuePlaceholder: "Type a value\u2026",
108
+ writeHerePlaceholder: "Write here\u2026",
109
+ jsonHint: "Enter this value as raw JSON.",
110
+ decrease: "Decrease",
111
+ increase: "Increase",
112
+ noItemsYet: "No items yet",
113
+ addItem: "Add item",
114
+ removeItemAria: (index) => `Remove item ${index}`,
115
+ itemsCount: (count) => count === 1 ? "1 item" : `${count} items`,
116
+ uploading: "Uploading\u2026",
117
+ dropToUpload: "Drop to upload",
118
+ dropOrBrowse: "Drop a file or click to browse",
119
+ pasteUrlInstead: "paste a URL instead",
120
+ urlPlaceholder: "https://\u2026 or pipelex-storage://\u2026",
121
+ uploadedFile: "Uploaded file",
122
+ preview: "Preview",
123
+ removeFileAria: "Remove file",
124
+ previewUnavailablePdf: "Preview unavailable - open to view.",
125
+ hideOptionalFields: "Hide optional fields",
126
+ hideOptionalInputs: "Hide optional inputs",
127
+ optionalFieldsCount: (count) => count === 1 ? "1 optional field" : `${count} optional fields`,
128
+ optionalInputsCount: (count) => count === 1 ? "1 optional input" : `${count} optional inputs`
129
+ };
130
+ var FieldStringsContext = createContext(DEFAULT_FIELD_STRINGS);
131
+ function FieldStringsProvider({
132
+ strings,
133
+ children
134
+ }) {
135
+ const merged = useMemo(() => ({ ...DEFAULT_FIELD_STRINGS, ...strings }), [strings]);
136
+ return /* @__PURE__ */ jsx(FieldStringsContext.Provider, { value: merged, children });
137
+ }
138
+ function useFieldStrings() {
139
+ return useContext(FieldStringsContext);
140
+ }
141
+ function FieldShell({
142
+ name,
143
+ title,
144
+ conceptRef,
145
+ category,
146
+ description,
147
+ required,
148
+ error,
149
+ htmlFor,
150
+ children,
151
+ className
152
+ }) {
153
+ const s = useFieldStrings();
154
+ const Label2 = htmlFor ? "label" : "div";
155
+ const labelText = title !== void 0 ? title : name;
156
+ const showLabel = labelText.trim() !== "";
157
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
158
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
159
+ showLabel && /* @__PURE__ */ jsx(
160
+ Label2,
161
+ {
162
+ ...htmlFor ? { htmlFor } : {},
163
+ className: "font-mono text-[13px] font-medium leading-none text-foreground",
164
+ children: labelText
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsx(ConceptPill, { conceptRef, category }),
168
+ !required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70", children: s.optionalBadge })
169
+ ] }),
170
+ description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: description }),
171
+ children,
172
+ error && /* @__PURE__ */ jsx("p", { className: "flex items-center gap-1 text-[11px] text-destructive", role: "alert", children: error })
173
+ ] });
174
+ }
175
+
176
+ // src/react/field-styles.ts
177
+ var fieldControlClass = cn(
178
+ "w-full rounded-md border border-border bg-input text-sm text-foreground",
179
+ "placeholder:text-muted-foreground/70 transition-colors",
180
+ "focus-visible:outline-none focus-visible:border-primary focus-visible:ring-1 focus-visible:ring-primary/40",
181
+ "disabled:cursor-not-allowed disabled:opacity-50",
182
+ "aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/30"
183
+ );
184
+ function DateField({ field, value, onChange, id, error, disabled }) {
185
+ return /* @__PURE__ */ jsx(
186
+ FieldShell,
187
+ {
188
+ name: field.name,
189
+ title: field.title,
190
+ conceptRef: field.conceptRef,
191
+ category: "date",
192
+ description: field.description,
193
+ required: field.required,
194
+ error,
195
+ htmlFor: id,
196
+ children: /* @__PURE__ */ jsx(
197
+ "input",
198
+ {
199
+ id,
200
+ type: "date",
201
+ value: toDateInputValue(value),
202
+ disabled,
203
+ "aria-invalid": !!error,
204
+ onChange: (e) => onChange(toStoredDateValue(e.target.value, field.datetime)),
205
+ className: cn(fieldControlClass, "h-11 px-3 font-mono tabular-nums")
206
+ }
207
+ )
208
+ }
209
+ );
210
+ }
211
+ var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
212
+ var PDF_EXT_RE = /\.pdf(\?|$)/i;
213
+ function DocumentField(props) {
214
+ return /* @__PURE__ */ jsx(FileField, { ...props, category: "document" });
215
+ }
216
+ function ImageField(props) {
217
+ return /* @__PURE__ */ jsx(FileField, { ...props, category: "image" });
218
+ }
219
+ function FileField({
220
+ field,
221
+ value,
222
+ onDropFile,
223
+ onChange,
224
+ id,
225
+ uploading,
226
+ resolveUrl,
227
+ error,
228
+ disabled,
229
+ category
230
+ }) {
231
+ const s = useFieldStrings();
232
+ const [showUrl, setShowUrl] = useState(false);
233
+ const [previewOpen, setPreviewOpen] = useState(false);
234
+ const [localPreview, setLocalPreview] = useState(null);
235
+ const localUrlRef = useRef(null);
236
+ const setLocal = useCallback((next) => {
237
+ if (localUrlRef.current) URL.revokeObjectURL(localUrlRef.current);
238
+ localUrlRef.current = next?.url ?? null;
239
+ setLocalPreview(next);
240
+ }, []);
241
+ useEffect(() => () => setLocal(null), [setLocal]);
242
+ const handleFile = useCallback(
243
+ (file) => {
244
+ setLocal({ url: URL.createObjectURL(file), type: file.type });
245
+ onDropFile(file);
246
+ },
247
+ [setLocal, onDropFile]
248
+ );
249
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
250
+ maxFiles: 1,
251
+ multiple: false,
252
+ disabled: disabled || uploading,
253
+ onDrop: (files) => {
254
+ const file = files[0];
255
+ if (file) handleFile(file);
256
+ }
257
+ });
258
+ const clear = useCallback(() => {
259
+ setLocal(null);
260
+ setResolvedSrc(null);
261
+ setPreviewOpen(false);
262
+ onChange(void 0);
263
+ }, [setLocal, onChange]);
264
+ const hasFile = !!value?.url;
265
+ const ref = `${value?.filename ?? ""} ${value?.url ?? ""}`;
266
+ const isImage = category === "image" || localPreview?.type.startsWith("image/") || IMAGE_EXT_RE.test(ref);
267
+ const isPdf = localPreview?.type === "application/pdf" || PDF_EXT_RE.test(ref);
268
+ const canPreview = isImage || isPdf;
269
+ const [resolvedSrc, setResolvedSrc] = useState(null);
270
+ const storageUri = value?.url && !value.url.startsWith("http") ? value.url : null;
271
+ useEffect(() => {
272
+ if (!previewOpen || !storageUri || localPreview || !resolveUrl) return;
273
+ let cancelled = false;
274
+ void resolveUrl(storageUri).then((url) => {
275
+ if (!cancelled) setResolvedSrc(url);
276
+ });
277
+ return () => {
278
+ cancelled = true;
279
+ };
280
+ }, [previewOpen, storageUri, localPreview, resolveUrl]);
281
+ const previewSrc = localPreview?.url ?? (value?.url?.startsWith("http") ? value.url : resolvedSrc ?? void 0);
282
+ return /* @__PURE__ */ jsxs(
283
+ FieldShell,
284
+ {
285
+ name: field.name,
286
+ title: field.title,
287
+ conceptRef: field.conceptRef,
288
+ category,
289
+ description: field.description,
290
+ required: field.required,
291
+ error,
292
+ children: [
293
+ hasFile && !uploading ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
294
+ /* @__PURE__ */ jsx(
295
+ FileChip,
296
+ {
297
+ value,
298
+ disabled,
299
+ onClear: clear,
300
+ canPreview,
301
+ previewOpen,
302
+ onTogglePreview: () => setPreviewOpen((v) => !v)
303
+ }
304
+ ),
305
+ previewOpen && (previewSrc && isImage ? /* @__PURE__ */ jsx(ImagePreview, { src: previewSrc, filename: value?.filename }) : previewSrc && isPdf ? /* @__PURE__ */ jsx(PdfPreview, { src: previewSrc }) : /* @__PURE__ */ jsx("div", { className: "flex h-24 items-center justify-center rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }))
306
+ ] }) : /* @__PURE__ */ jsxs(
307
+ "div",
308
+ {
309
+ ...getRootProps(),
310
+ className: cn(
311
+ "flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed px-4 py-6 text-center",
312
+ "cursor-pointer select-none transition-colors",
313
+ isDragActive ? "border-primary/60 bg-primary/5" : "border-border bg-input hover:border-border hover:bg-muted",
314
+ (disabled || uploading) && "cursor-not-allowed opacity-60"
315
+ ),
316
+ children: [
317
+ /* @__PURE__ */ jsx("input", { ...getInputProps(), id, "aria-invalid": !!error }),
318
+ uploading ? /* @__PURE__ */ jsxs(Fragment, { children: [
319
+ /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }),
320
+ /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground", children: s.uploading })
321
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
322
+ /* @__PURE__ */ jsx(Upload, { className: "h-5 w-5 text-muted-foreground", strokeWidth: 1.75 }),
323
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
324
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropToUpload : s.dropOrBrowse }),
325
+ field.accept && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: field.accept })
326
+ ] })
327
+ ] })
328
+ ]
329
+ }
330
+ ),
331
+ !showUrl ? /* @__PURE__ */ jsxs(
332
+ "button",
333
+ {
334
+ type: "button",
335
+ disabled,
336
+ onClick: () => setShowUrl(true),
337
+ className: "inline-flex items-center gap-1 font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50",
338
+ children: [
339
+ /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
340
+ s.pasteUrlInstead
341
+ ]
342
+ }
343
+ ) : /* @__PURE__ */ jsx(
344
+ "input",
345
+ {
346
+ type: "text",
347
+ autoFocus: true,
348
+ value: value?.url ?? "",
349
+ disabled,
350
+ placeholder: s.urlPlaceholder,
351
+ onChange: (e) => {
352
+ setLocal(null);
353
+ onChange(e.target.value ? { url: e.target.value } : void 0);
354
+ },
355
+ className: cn(fieldControlClass, "h-9 px-3 font-mono text-[12px]")
356
+ }
357
+ )
358
+ ]
359
+ }
360
+ );
361
+ }
362
+ function ImagePreview({ src, filename }) {
363
+ const s = useFieldStrings();
364
+ const [broken, setBroken] = useState(false);
365
+ if (broken) {
366
+ return /* @__PURE__ */ jsx("div", { className: "flex h-28 items-center justify-center rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(ImageOff, { className: "h-5 w-5 text-muted-foreground" }) });
367
+ }
368
+ return /* @__PURE__ */ jsx("div", { className: "flex max-h-[640px] justify-center overflow-auto rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(
369
+ "img",
370
+ {
371
+ src,
372
+ alt: filename ?? s.preview,
373
+ className: "h-auto w-full max-w-full object-contain",
374
+ onError: () => setBroken(true)
375
+ }
376
+ ) });
377
+ }
378
+ function PdfPreview({ src }) {
379
+ const s = useFieldStrings();
380
+ return /* @__PURE__ */ jsx(
381
+ "object",
382
+ {
383
+ data: `${src}#view=FitH`,
384
+ type: "application/pdf",
385
+ className: "h-[78vh] min-h-[520px] w-full rounded-md border border-border bg-muted",
386
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-28 flex-col items-center justify-center gap-1.5 text-center", children: [
387
+ /* @__PURE__ */ jsx(FileText, { className: "h-5 w-5 text-muted-foreground" }),
388
+ /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground", children: s.previewUnavailablePdf })
389
+ ] })
390
+ }
391
+ );
392
+ }
393
+ function FileChip({
394
+ value,
395
+ disabled,
396
+ onClear,
397
+ canPreview,
398
+ previewOpen,
399
+ onTogglePreview
400
+ }) {
401
+ const s = useFieldStrings();
402
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-input px-3 py-2.5", children: [
403
+ /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }) }),
404
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
405
+ /* @__PURE__ */ jsx("p", { className: "truncate text-[13px] font-medium text-foreground", children: value?.filename ?? s.uploadedFile }),
406
+ /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[10.5px] text-muted-foreground", children: value?.url })
407
+ ] }),
408
+ canPreview && /* @__PURE__ */ jsxs(
409
+ "button",
410
+ {
411
+ type: "button",
412
+ onClick: onTogglePreview,
413
+ className: "inline-flex h-7 shrink-0 items-center gap-1 rounded-md border border-border px-2 text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
414
+ children: [
415
+ previewOpen ? /* @__PURE__ */ jsx(EyeOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" }),
416
+ s.preview
417
+ ]
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsx(ClearButton, { disabled, onClick: onClear })
421
+ ] });
422
+ }
423
+ function ClearButton({ disabled, onClick }) {
424
+ const s = useFieldStrings();
425
+ return /* @__PURE__ */ jsx(
426
+ "button",
427
+ {
428
+ type: "button",
429
+ onClick,
430
+ disabled,
431
+ "aria-label": s.removeFileAria,
432
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",
433
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
434
+ }
435
+ );
436
+ }
437
+ var Select = SelectPrimitive.Root;
438
+ var SelectValue = SelectPrimitive.Value;
439
+ var SelectTrigger = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
440
+ SelectPrimitive.Trigger,
441
+ {
442
+ ref,
443
+ className: cn(
444
+ // Adapted from the registry to DESIGN.md ("Inputs"): the `--input` surface,
445
+ // a hairline `--border`, and a border-color focus signal instead of the
446
+ // upstream 2px focus ring ("No focus glow by default").
447
+ "flex h-10 w-full items-center justify-between rounded-md border border-border bg-input px-3 py-2 text-sm transition-colors data-[placeholder]:text-muted-foreground focus:border-ring focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:border-destructive [&>span]:line-clamp-1",
448
+ className
449
+ ),
450
+ ...props,
451
+ children: [
452
+ children,
453
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
454
+ ]
455
+ }
456
+ ));
457
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
458
+ var SelectScrollUpButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
459
+ SelectPrimitive.ScrollUpButton,
460
+ {
461
+ ref,
462
+ className: cn("flex cursor-default items-center justify-center py-1", className),
463
+ ...props,
464
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
465
+ }
466
+ ));
467
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
468
+ var SelectScrollDownButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
469
+ SelectPrimitive.ScrollDownButton,
470
+ {
471
+ ref,
472
+ className: cn("flex cursor-default items-center justify-center py-1", className),
473
+ ...props,
474
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
475
+ }
476
+ ));
477
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
478
+ var SelectContent = React2.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
479
+ SelectPrimitive.Content,
480
+ {
481
+ ref,
482
+ className: cn(
483
+ "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
484
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
485
+ className
486
+ ),
487
+ position,
488
+ ...props,
489
+ children: [
490
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
491
+ /* @__PURE__ */ jsx(
492
+ SelectPrimitive.Viewport,
493
+ {
494
+ className: cn(
495
+ "p-1",
496
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
497
+ ),
498
+ children
499
+ }
500
+ ),
501
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
502
+ ]
503
+ }
504
+ ) }));
505
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
506
+ var SelectLabel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
507
+ SelectPrimitive.Label,
508
+ {
509
+ ref,
510
+ className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
511
+ ...props
512
+ }
513
+ ));
514
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
515
+ var SelectItem = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
516
+ SelectPrimitive.Item,
517
+ {
518
+ ref,
519
+ className: cn(
520
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
521
+ className
522
+ ),
523
+ ...props,
524
+ children: [
525
+ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
526
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
527
+ ]
528
+ }
529
+ ));
530
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
531
+ var SelectSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
532
+ SelectPrimitive.Separator,
533
+ {
534
+ ref,
535
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
536
+ ...props
537
+ }
538
+ ));
539
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
540
+ var toggleVariants = cva(
541
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
542
+ {
543
+ variants: {
544
+ variant: {
545
+ default: "bg-transparent",
546
+ outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground"
547
+ },
548
+ size: {
549
+ default: "h-10 px-3 min-w-10",
550
+ sm: "h-9 px-2.5 min-w-9",
551
+ lg: "h-11 px-5 min-w-11"
552
+ }
553
+ },
554
+ defaultVariants: {
555
+ variant: "default",
556
+ size: "default"
557
+ }
558
+ }
559
+ );
560
+ var Toggle = React2.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
561
+ TogglePrimitive.Root,
562
+ {
563
+ ref,
564
+ className: cn(toggleVariants({ variant, size, className })),
565
+ ...props
566
+ }
567
+ ));
568
+ Toggle.displayName = TogglePrimitive.Root.displayName;
569
+ var ToggleGroupContext = React2.createContext({
570
+ size: "default",
571
+ variant: "default"
572
+ });
573
+ var ToggleGroup = React2.forwardRef(({ className, variant, size, children, ...props }, ref) => /* @__PURE__ */ jsx(
574
+ ToggleGroupPrimitive.Root,
575
+ {
576
+ ref,
577
+ className: cn("flex items-center justify-center gap-1", className),
578
+ ...props,
579
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
580
+ }
581
+ ));
582
+ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
583
+ var ToggleGroupItem = React2.forwardRef(({ className, children, variant, size, ...props }, ref) => {
584
+ const context = React2.useContext(ToggleGroupContext);
585
+ return /* @__PURE__ */ jsx(
586
+ ToggleGroupPrimitive.Item,
587
+ {
588
+ ref,
589
+ className: cn(
590
+ toggleVariants({
591
+ variant: context.variant || variant,
592
+ size: context.size || size
593
+ }),
594
+ className
595
+ ),
596
+ ...props,
597
+ children
598
+ }
599
+ );
600
+ });
601
+ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
602
+ var CLEAR_VALUE = "__none__";
603
+ function EnumField({ field, value, onChange, id, error, disabled }) {
604
+ const s = useFieldStrings();
605
+ const isSegmented = field.options.length <= 4 && field.options.every((o) => o.length <= 16);
606
+ return /* @__PURE__ */ jsx(
607
+ FieldShell,
608
+ {
609
+ name: field.name,
610
+ title: field.title,
611
+ conceptRef: field.conceptRef,
612
+ category: "choice",
613
+ description: field.description,
614
+ required: field.required,
615
+ error,
616
+ htmlFor: isSegmented ? void 0 : id,
617
+ children: isSegmented ? (
618
+ // `type="single"` gives the roving tabindex and arrow-key navigation a
619
+ // radiogroup owes its user, and emits `''` when the active item is
620
+ // pressed again - which is the field's "clear" path.
621
+ /* @__PURE__ */ jsx(
622
+ ToggleGroup,
623
+ {
624
+ type: "single",
625
+ value: value ?? "",
626
+ onValueChange: (next) => onChange(next === "" ? void 0 : next),
627
+ disabled,
628
+ "aria-label": field.title ?? field.name,
629
+ className: "flex flex-wrap justify-start gap-1.5",
630
+ children: field.options.map((option) => /* @__PURE__ */ jsxs(
631
+ ToggleGroupItem,
632
+ {
633
+ value: option,
634
+ variant: "outline",
635
+ className: "h-9 min-w-0 gap-1.5 border-border bg-input px-3 text-[13px] text-muted-foreground hover:bg-muted hover:text-foreground data-[state=on]:border-primary/40 data-[state=on]:bg-primary/10 data-[state=on]:font-medium data-[state=on]:text-foreground",
636
+ children: [
637
+ value === option && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary", strokeWidth: 2.5, "aria-hidden": "true" }),
638
+ option
639
+ ]
640
+ },
641
+ option
642
+ ))
643
+ }
644
+ )
645
+ ) : /* @__PURE__ */ jsxs(
646
+ Select,
647
+ {
648
+ value: value ?? "",
649
+ disabled,
650
+ onValueChange: (next) => onChange(next === CLEAR_VALUE ? void 0 : next),
651
+ children: [
652
+ /* @__PURE__ */ jsx(SelectTrigger, { id, "aria-invalid": !!error, className: "h-11", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: s.selectPlaceholder }) }),
653
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
654
+ /* @__PURE__ */ jsx(SelectItem, { value: CLEAR_VALUE, children: s.selectPlaceholder }),
655
+ field.options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option }, option))
656
+ ] })
657
+ ]
658
+ }
659
+ )
660
+ }
661
+ );
662
+ }
663
+ function NumberField({ field, value, onChange, id, error, disabled }) {
664
+ const s = useFieldStrings();
665
+ const step = field.integer ? 1 : 0.1;
666
+ const clamp = (n) => {
667
+ let next = n;
668
+ if (typeof field.min === "number") next = Math.max(field.min, next);
669
+ if (typeof field.max === "number") next = Math.min(field.max, next);
670
+ return field.integer ? Math.round(next) : next;
671
+ };
672
+ const nudge = (delta) => onChange(clamp((value ?? 0) + delta));
673
+ const rangeHint = field.min !== void 0 || field.max !== void 0 ? `${field.min ?? "\u2212\u221E"} \u2013 ${field.max ?? "\u221E"}` : void 0;
674
+ return /* @__PURE__ */ jsx(
675
+ FieldShell,
676
+ {
677
+ name: field.name,
678
+ title: field.title,
679
+ conceptRef: field.conceptRef,
680
+ category: "number",
681
+ description: field.description ?? rangeHint,
682
+ required: field.required,
683
+ error,
684
+ htmlFor: id,
685
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-1.5", children: [
686
+ /* @__PURE__ */ jsx(
687
+ "input",
688
+ {
689
+ id,
690
+ type: "number",
691
+ inputMode: field.integer ? "numeric" : "decimal",
692
+ value: value ?? "",
693
+ step,
694
+ min: field.min,
695
+ max: field.max,
696
+ disabled,
697
+ "aria-invalid": !!error,
698
+ placeholder: "0",
699
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value)),
700
+ className: cn(
701
+ fieldControlClass,
702
+ "h-11 px-3 font-mono tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
703
+ )
704
+ }
705
+ ),
706
+ /* @__PURE__ */ jsx(StepButton, { onClick: () => nudge(-step), disabled, label: s.decrease, children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" }) }),
707
+ /* @__PURE__ */ jsx(StepButton, { onClick: () => nudge(step), disabled, label: s.increase, children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) })
708
+ ] })
709
+ }
710
+ );
711
+ }
712
+ function StepButton({
713
+ onClick,
714
+ disabled,
715
+ label,
716
+ children
717
+ }) {
718
+ return /* @__PURE__ */ jsx(
719
+ "button",
720
+ {
721
+ type: "button",
722
+ onClick,
723
+ disabled,
724
+ "aria-label": label,
725
+ className: cn(
726
+ "flex h-11 w-11 shrink-0 items-center justify-center rounded-md border border-border bg-input text-muted-foreground transition-colors",
727
+ "hover:border-border hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
728
+ ),
729
+ children
730
+ }
731
+ );
732
+ }
733
+ function TextField({ field, value, onChange, id, error, disabled }) {
734
+ const s = useFieldStrings();
735
+ return /* @__PURE__ */ jsx(
736
+ FieldShell,
737
+ {
738
+ name: field.name,
739
+ title: field.title,
740
+ conceptRef: field.conceptRef,
741
+ category: "text",
742
+ description: field.description,
743
+ required: field.required,
744
+ error,
745
+ htmlFor: id,
746
+ children: /* @__PURE__ */ jsx(
747
+ "input",
748
+ {
749
+ id,
750
+ type: "text",
751
+ value: value ?? "",
752
+ disabled,
753
+ "aria-invalid": !!error,
754
+ placeholder: field.placeholder ?? s.typeValuePlaceholder,
755
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
756
+ className: cn(fieldControlClass, "h-11 px-3")
757
+ }
758
+ )
759
+ }
760
+ );
761
+ }
762
+ function ProseField({ field, value, onChange, id, error, disabled }) {
763
+ const s = useFieldStrings();
764
+ return /* @__PURE__ */ jsx(
765
+ FieldShell,
766
+ {
767
+ name: field.name,
768
+ title: field.title,
769
+ conceptRef: field.conceptRef,
770
+ category: "text",
771
+ description: field.description,
772
+ required: field.required,
773
+ error,
774
+ htmlFor: id,
775
+ children: /* @__PURE__ */ jsx(
776
+ "textarea",
777
+ {
778
+ id,
779
+ value: value ?? "",
780
+ disabled,
781
+ "aria-invalid": !!error,
782
+ rows: 3,
783
+ placeholder: field.placeholder ?? s.writeHerePlaceholder,
784
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
785
+ className: cn(
786
+ fieldControlClass,
787
+ "min-h-[88px] resize-y px-3 py-2.5 leading-relaxed [field-sizing:content]"
788
+ )
789
+ }
790
+ )
791
+ }
792
+ );
793
+ }
794
+ function OptionalToggle({ count, expanded, onToggle, noun = "field" }) {
795
+ const s = useFieldStrings();
796
+ return /* @__PURE__ */ jsxs(
797
+ "button",
798
+ {
799
+ type: "button",
800
+ onClick: onToggle,
801
+ className: "inline-flex items-center gap-1.5 text-[11px] text-muted-foreground transition-colors hover:text-foreground",
802
+ children: [
803
+ expanded ? /* @__PURE__ */ jsx(Minus, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
804
+ expanded ? noun === "field" ? s.hideOptionalFields : s.hideOptionalInputs : noun === "field" ? s.optionalFieldsCount(count) : s.optionalInputsCount(count)
805
+ ]
806
+ }
807
+ );
808
+ }
809
+ function ObjectField({ field, value, onChange, id, error, env }) {
810
+ const s = useFieldStrings();
811
+ const [showOptional, setShowOptional] = useState(false);
812
+ const data = value ?? {};
813
+ const setChild = (name, childValue) => onChange({ ...data, [name]: childValue });
814
+ const optionalEmpty = field.fields.filter((f) => !f.required && !isFilled(data[f.name]));
815
+ const visible = showOptional ? field.fields : field.fields.filter((f) => f.required || isFilled(data[f.name]));
816
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
817
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
818
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[13px] font-medium leading-none text-foreground", children: field.title ?? field.name }),
819
+ /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" }),
820
+ !field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70", children: s.optionalBadge })
821
+ ] }),
822
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
823
+ /* @__PURE__ */ jsxs(
824
+ "div",
825
+ {
826
+ className: cn(
827
+ "space-y-4 rounded-lg border bg-card/40 p-4",
828
+ error ? "border-destructive/50" : "border-border"
829
+ ),
830
+ children: [
831
+ visible.map((child) => /* @__PURE__ */ jsx(
832
+ FieldRenderer,
833
+ {
834
+ field: child,
835
+ value: data[child.name],
836
+ onChange: (v) => setChild(child.name, v),
837
+ id: `${id}.${child.name}`,
838
+ env
839
+ },
840
+ child.name
841
+ )),
842
+ optionalEmpty.length > 0 && /* @__PURE__ */ jsx(
843
+ OptionalToggle,
844
+ {
845
+ count: optionalEmpty.length,
846
+ expanded: showOptional,
847
+ onToggle: () => setShowOptional((v) => !v),
848
+ noun: "field"
849
+ }
850
+ )
851
+ ]
852
+ }
853
+ ),
854
+ error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
855
+ ] });
856
+ }
857
+ function ListField({ field, value, onChange, id, error, env }) {
858
+ const s = useFieldStrings();
859
+ const items = value ?? [];
860
+ const setItem = (index, itemValue) => {
861
+ const next = items.slice();
862
+ next[index] = itemValue;
863
+ onChange(next);
864
+ };
865
+ const removeItem = (index) => onChange(items.filter((_, i) => i !== index));
866
+ const addItem = () => onChange([...items, emptyValue(field.item)]);
867
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
868
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
869
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[13px] font-medium leading-none text-foreground", children: field.title ?? field.name }),
870
+ /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "list" }),
871
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: s.itemsCount(items.length) }),
872
+ !field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70", children: s.optionalBadge })
873
+ ] }),
874
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
875
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
876
+ items.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
877
+ items.map((item, index) => /* @__PURE__ */ jsxs(
878
+ "div",
879
+ {
880
+ className: "flex items-start gap-2 rounded-md border border-border bg-card/40 p-3",
881
+ children: [
882
+ /* @__PURE__ */ jsx("span", { className: "mt-1 w-5 shrink-0 text-center font-mono text-[11px] text-muted-foreground", children: index + 1 }),
883
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
884
+ FieldRenderer,
885
+ {
886
+ field: { ...field.item, title: "", name: `${field.name}[${index}]` },
887
+ value: item,
888
+ onChange: (v) => setItem(index, v),
889
+ id: `${id}.${index}`,
890
+ env
891
+ }
892
+ ) }),
893
+ /* @__PURE__ */ jsx(
894
+ "button",
895
+ {
896
+ type: "button",
897
+ onClick: () => removeItem(index),
898
+ disabled: env?.disabled,
899
+ "aria-label": s.removeItemAria(index + 1),
900
+ className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-destructive disabled:opacity-50",
901
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
902
+ }
903
+ )
904
+ ]
905
+ },
906
+ index
907
+ )),
908
+ /* @__PURE__ */ jsxs(
909
+ "button",
910
+ {
911
+ type: "button",
912
+ onClick: addItem,
913
+ disabled: env?.disabled,
914
+ className: cn(
915
+ "inline-flex h-9 w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-border text-[13px] text-muted-foreground transition-colors",
916
+ "hover:border-primary/40 hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
917
+ ),
918
+ children: [
919
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
920
+ s.addItem
921
+ ]
922
+ }
923
+ )
924
+ ] }),
925
+ error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
926
+ ] });
927
+ }
928
+ function emptyValue(field) {
929
+ switch (field.kind) {
930
+ case "boolean":
931
+ return false;
932
+ case "object":
933
+ return {};
934
+ case "list":
935
+ return [];
936
+ case "document":
937
+ case "image":
938
+ return void 0;
939
+ default:
940
+ return void 0;
941
+ }
942
+ }
943
+ function UnknownField({ field, value, onChange, id, error, disabled }) {
944
+ const s = useFieldStrings();
945
+ return /* @__PURE__ */ jsx(
946
+ FieldShell,
947
+ {
948
+ name: field.name,
949
+ title: field.title,
950
+ conceptRef: field.conceptRef,
951
+ category: "structured",
952
+ description: field.description ?? s.jsonHint,
953
+ required: field.required,
954
+ error,
955
+ htmlFor: id,
956
+ children: /* @__PURE__ */ jsx(
957
+ "textarea",
958
+ {
959
+ id,
960
+ value: value ?? "",
961
+ disabled,
962
+ "aria-invalid": !!error,
963
+ rows: 3,
964
+ spellCheck: false,
965
+ placeholder: '{\n "key": "value"\n}',
966
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
967
+ className: cn(fieldControlClass, "min-h-[80px] resize-y px-3 py-2.5 font-mono text-[12px]")
968
+ }
969
+ )
970
+ }
971
+ );
972
+ }
973
+ function FieldRenderer({ field, value, onChange, id, error, env }) {
974
+ const disabled = env?.disabled;
975
+ switch (field.kind) {
976
+ case "text":
977
+ return /* @__PURE__ */ jsx(
978
+ TextField,
979
+ {
980
+ field,
981
+ value,
982
+ onChange,
983
+ id,
984
+ error,
985
+ disabled
986
+ }
987
+ );
988
+ case "prose":
989
+ return /* @__PURE__ */ jsx(
990
+ ProseField,
991
+ {
992
+ field,
993
+ value,
994
+ onChange,
995
+ id,
996
+ error,
997
+ disabled
998
+ }
999
+ );
1000
+ case "date":
1001
+ return /* @__PURE__ */ jsx(
1002
+ DateField,
1003
+ {
1004
+ field,
1005
+ value,
1006
+ onChange,
1007
+ id,
1008
+ error,
1009
+ disabled
1010
+ }
1011
+ );
1012
+ case "number":
1013
+ return /* @__PURE__ */ jsx(
1014
+ NumberField,
1015
+ {
1016
+ field,
1017
+ value,
1018
+ onChange,
1019
+ id,
1020
+ error,
1021
+ disabled
1022
+ }
1023
+ );
1024
+ case "boolean":
1025
+ return /* @__PURE__ */ jsx(
1026
+ BooleanField,
1027
+ {
1028
+ field,
1029
+ value,
1030
+ onChange,
1031
+ id,
1032
+ disabled
1033
+ }
1034
+ );
1035
+ case "enum":
1036
+ return /* @__PURE__ */ jsx(
1037
+ EnumField,
1038
+ {
1039
+ field,
1040
+ value,
1041
+ onChange,
1042
+ id,
1043
+ error,
1044
+ disabled
1045
+ }
1046
+ );
1047
+ case "document":
1048
+ return /* @__PURE__ */ jsx(
1049
+ DocumentField,
1050
+ {
1051
+ field,
1052
+ value,
1053
+ onChange,
1054
+ onDropFile: (file) => env?.onDropFile?.(id, file),
1055
+ uploading: env?.uploadingIds?.has(id),
1056
+ resolveUrl: env?.resolveUrl,
1057
+ id,
1058
+ error,
1059
+ disabled
1060
+ }
1061
+ );
1062
+ case "image":
1063
+ return /* @__PURE__ */ jsx(
1064
+ ImageField,
1065
+ {
1066
+ field,
1067
+ value,
1068
+ onChange,
1069
+ onDropFile: (file) => env?.onDropFile?.(id, file),
1070
+ uploading: env?.uploadingIds?.has(id),
1071
+ resolveUrl: env?.resolveUrl,
1072
+ id,
1073
+ error,
1074
+ disabled
1075
+ }
1076
+ );
1077
+ case "object":
1078
+ return /* @__PURE__ */ jsx(
1079
+ ObjectField,
1080
+ {
1081
+ field,
1082
+ value,
1083
+ onChange,
1084
+ id,
1085
+ error,
1086
+ env
1087
+ }
1088
+ );
1089
+ case "list":
1090
+ return /* @__PURE__ */ jsx(
1091
+ ListField,
1092
+ {
1093
+ field,
1094
+ value,
1095
+ onChange,
1096
+ id,
1097
+ error,
1098
+ env
1099
+ }
1100
+ );
1101
+ default:
1102
+ return /* @__PURE__ */ jsx(
1103
+ UnknownField,
1104
+ {
1105
+ field,
1106
+ value,
1107
+ onChange,
1108
+ id,
1109
+ error,
1110
+ disabled
1111
+ }
1112
+ );
1113
+ }
1114
+ }
1115
+
1116
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldRenderer, FieldShell, FieldStringsProvider, ImageField, ListField, NumberField, ObjectField, OptionalToggle, ProseField, TextField, UnknownField, fieldControlClass, useFieldStrings };
1117
+ //# sourceMappingURL=index.js.map
1118
+ //# sourceMappingURL=index.js.map