@arqel-dev/ui 0.11.0 → 0.13.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 (111) hide show
  1. package/SKILL.md +5 -2
  2. package/dist/AreaChart-NMQ2RQGA.js +33 -0
  3. package/dist/AreaChart-NMQ2RQGA.js.map +1 -0
  4. package/dist/ArqelCreatePage-P3G6TKIK.js +8 -0
  5. package/dist/ArqelCreatePage-P3G6TKIK.js.map +1 -0
  6. package/dist/ArqelDashboardPage-PX3Y75AA.js +6 -0
  7. package/dist/ArqelDashboardPage-PX3Y75AA.js.map +1 -0
  8. package/dist/ArqelEditPage-HKQSE6E5.js +8 -0
  9. package/dist/ArqelEditPage-HKQSE6E5.js.map +1 -0
  10. package/dist/ArqelIndexPage-MRH3572Z.js +12 -0
  11. package/dist/ArqelIndexPage-MRH3572Z.js.map +1 -0
  12. package/dist/ArqelShowPage-4FS5MTUN.js +6 -0
  13. package/dist/ArqelShowPage-4FS5MTUN.js.map +1 -0
  14. package/dist/BarChart-KLHKTLO2.js +19 -0
  15. package/dist/BarChart-KLHKTLO2.js.map +1 -0
  16. package/dist/DonutChart-L56R4QF2.js +11 -0
  17. package/dist/DonutChart-L56R4QF2.js.map +1 -0
  18. package/dist/LineChart-GHMPHFZZ.js +30 -0
  19. package/dist/LineChart-GHMPHFZZ.js.map +1 -0
  20. package/dist/PieChart-T2VQOFPP.js +4 -0
  21. package/dist/PieChart-T2VQOFPP.js.map +1 -0
  22. package/dist/RadarChart-CSWR5AO7.js +32 -0
  23. package/dist/RadarChart-CSWR5AO7.js.map +1 -0
  24. package/dist/Topbar-DffcNdcJ.d.ts +50 -0
  25. package/dist/action.d.ts +12 -2
  26. package/dist/action.js +6 -1090
  27. package/dist/action.js.map +1 -1
  28. package/dist/auth.js +1 -10
  29. package/dist/auth.js.map +1 -1
  30. package/dist/chunk-2LTZJ4R7.js +110 -0
  31. package/dist/chunk-2LTZJ4R7.js.map +1 -0
  32. package/dist/chunk-3JJOHYD7.js +57 -0
  33. package/dist/chunk-3JJOHYD7.js.map +1 -0
  34. package/dist/chunk-47HD7ALI.js +370 -0
  35. package/dist/chunk-47HD7ALI.js.map +1 -0
  36. package/dist/chunk-6UJQP7KU.js +737 -0
  37. package/dist/chunk-6UJQP7KU.js.map +1 -0
  38. package/dist/chunk-7LSZADUC.js +50 -0
  39. package/dist/chunk-7LSZADUC.js.map +1 -0
  40. package/dist/chunk-B7ULUJVO.js +11 -0
  41. package/dist/chunk-B7ULUJVO.js.map +1 -0
  42. package/dist/chunk-C5H44NSP.js +37 -0
  43. package/dist/chunk-C5H44NSP.js.map +1 -0
  44. package/dist/chunk-DSULVRB6.js +101 -0
  45. package/dist/chunk-DSULVRB6.js.map +1 -0
  46. package/dist/chunk-ERT7LL6C.js +199 -0
  47. package/dist/chunk-ERT7LL6C.js.map +1 -0
  48. package/dist/chunk-EYAUPJ6J.js +64 -0
  49. package/dist/chunk-EYAUPJ6J.js.map +1 -0
  50. package/dist/chunk-FHRQ7PWE.js +58 -0
  51. package/dist/chunk-FHRQ7PWE.js.map +1 -0
  52. package/dist/chunk-FV7RESKL.js +285 -0
  53. package/dist/chunk-FV7RESKL.js.map +1 -0
  54. package/dist/chunk-K7QY56XN.js +610 -0
  55. package/dist/chunk-K7QY56XN.js.map +1 -0
  56. package/dist/chunk-L3JBBN4H.js +27 -0
  57. package/dist/chunk-L3JBBN4H.js.map +1 -0
  58. package/dist/chunk-MSRTGCTJ.js +597 -0
  59. package/dist/chunk-MSRTGCTJ.js.map +1 -0
  60. package/dist/chunk-MTNFFSVA.js +92 -0
  61. package/dist/chunk-MTNFFSVA.js.map +1 -0
  62. package/dist/chunk-MTZJDFQ6.js +32 -0
  63. package/dist/chunk-MTZJDFQ6.js.map +1 -0
  64. package/dist/chunk-MXUXMHOK.js +16 -0
  65. package/dist/chunk-MXUXMHOK.js.map +1 -0
  66. package/dist/chunk-PXON56HL.js +35 -0
  67. package/dist/chunk-PXON56HL.js.map +1 -0
  68. package/dist/chunk-PZEVKMZX.js +598 -0
  69. package/dist/chunk-PZEVKMZX.js.map +1 -0
  70. package/dist/chunk-R4BOUU6Y.js +55 -0
  71. package/dist/chunk-R4BOUU6Y.js.map +1 -0
  72. package/dist/chunk-RMPOR2YZ.js +12 -0
  73. package/dist/chunk-RMPOR2YZ.js.map +1 -0
  74. package/dist/chunk-RNAMHQM6.js +12 -0
  75. package/dist/chunk-RNAMHQM6.js.map +1 -0
  76. package/dist/chunk-TNJEHYRF.js +387 -0
  77. package/dist/chunk-TNJEHYRF.js.map +1 -0
  78. package/dist/chunk-VSTRVXXZ.js +49 -0
  79. package/dist/chunk-VSTRVXXZ.js.map +1 -0
  80. package/dist/chunk-WKWV36PG.js +140 -0
  81. package/dist/chunk-WKWV36PG.js.map +1 -0
  82. package/dist/cn-By8XWiSA.d.ts +12 -0
  83. package/dist/flash.js +2 -104
  84. package/dist/flash.js.map +1 -1
  85. package/dist/form.js +4 -673
  86. package/dist/form.js.map +1 -1
  87. package/dist/index.d.ts +2 -2
  88. package/dist/index.js +18 -3706
  89. package/dist/index.js.map +1 -1
  90. package/dist/pages.js +23 -3025
  91. package/dist/pages.js.map +1 -1
  92. package/dist/palette.js +1 -283
  93. package/dist/palette.js.map +1 -1
  94. package/dist/primitives.js +3 -658
  95. package/dist/primitives.js.map +1 -1
  96. package/dist/resource.js +5 -720
  97. package/dist/resource.js.map +1 -1
  98. package/dist/shell.d.ts +26 -46
  99. package/dist/shell.js +46 -1039
  100. package/dist/shell.js.map +1 -1
  101. package/dist/table.js +4 -618
  102. package/dist/table.js.map +1 -1
  103. package/dist/utility.js +3 -104
  104. package/dist/utility.js.map +1 -1
  105. package/dist/utils.d.ts +12 -6
  106. package/dist/utils.js +2 -9
  107. package/dist/utils.js.map +1 -1
  108. package/dist/widgets.d.ts +7 -2
  109. package/dist/widgets.js +3 -675
  110. package/dist/widgets.js.map +1 -1
  111. package/package.json +15 -14
package/dist/form.js CHANGED
@@ -1,675 +1,6 @@
1
- import { useId, useState, useRef } from 'react';
2
- import { clsx } from 'clsx';
3
- import { twMerge } from 'tailwind-merge';
4
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { cva } from 'class-variance-authority';
6
- import { Slot } from 'radix-ui';
7
- import { isFieldEntry, isLayoutEntry } from '@arqel-dev/types/forms';
8
-
9
- // src/form/FieldRegistry.tsx
10
- var registry = /* @__PURE__ */ new Map();
11
- function registerField(name, component) {
12
- registry.set(name, component);
13
- }
14
- function getFieldComponent(name) {
15
- return registry.get(name);
16
- }
17
- function unregisterField(name) {
18
- registry.delete(name);
19
- }
20
- function getRegisteredFields() {
21
- return Array.from(registry.keys()).sort();
22
- }
23
- function clearFieldRegistry() {
24
- registry.clear();
25
- }
26
- function cn(...inputs) {
27
- return twMerge(clsx(inputs));
28
- }
29
- var inputClasses = cn(
30
- "h-9 rounded-sm border border-[var(--input)]",
31
- "bg-background px-3 text-sm",
32
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
33
- "disabled:cursor-not-allowed disabled:opacity-50"
34
- );
35
- function NativeFieldInput(props) {
36
- const { field } = props;
37
- switch (field.type) {
38
- case "text":
39
- case "email":
40
- case "url":
41
- case "password":
42
- case "slug":
43
- return /* @__PURE__ */ jsx(TextLike, { ...props, field });
44
- case "textarea":
45
- return /* @__PURE__ */ jsx(Textarea, { ...props, field });
46
- case "number":
47
- case "currency":
48
- return /* @__PURE__ */ jsx(NumericInput, { ...props, field });
49
- case "boolean":
50
- case "toggle":
51
- return /* @__PURE__ */ jsx(Checkbox, { ...props, field });
52
- case "select":
53
- return /* @__PURE__ */ jsx(SelectInput, { ...props, field });
54
- case "multiSelect":
55
- case "hasMany":
56
- return /* @__PURE__ */ jsx(MultiSelectInput, { ...props, field });
57
- case "radio":
58
- return /* @__PURE__ */ jsx(RadioGroup, { ...props, field });
59
- case "belongsTo":
60
- return /* @__PURE__ */ jsx(BelongsToInput, { ...props, field });
61
- case "date":
62
- case "dateTime":
63
- return /* @__PURE__ */ jsx(DateInput, { ...props, field });
64
- case "file":
65
- case "image":
66
- return /* @__PURE__ */ jsx(FileInput, { ...props });
67
- default:
68
- return null;
69
- }
70
- }
71
- function TextLike({
72
- field,
73
- value,
74
- onChange,
75
- disabled,
76
- inputId,
77
- describedBy
78
- }) {
79
- const htmlType = field.type === "email" ? "email" : field.type === "password" ? "password" : field.type === "url" ? "url" : "text";
80
- const isDisabled = disabled || field.disabled || field.readonly;
81
- return /* @__PURE__ */ jsx(
82
- "input",
83
- {
84
- id: inputId,
85
- type: htmlType,
86
- className: inputClasses,
87
- value: typeof value === "string" ? value : (value ?? "")?.toString() ?? "",
88
- placeholder: field.placeholder ?? void 0,
89
- disabled: isDisabled,
90
- readOnly: field.readonly === true,
91
- required: field.required === true,
92
- "aria-describedby": describedBy,
93
- onChange: (e) => onChange(e.target.value)
94
- }
95
- );
96
- }
97
- function Textarea({
98
- field,
99
- value,
100
- onChange,
101
- disabled,
102
- inputId,
103
- describedBy
104
- }) {
105
- return /* @__PURE__ */ jsx(
106
- "textarea",
107
- {
108
- id: inputId,
109
- className: cn(inputClasses, "h-auto min-h-[5rem] py-2"),
110
- value: typeof value === "string" ? value : "",
111
- placeholder: field.placeholder ?? void 0,
112
- disabled: disabled || field.disabled || field.readonly,
113
- readOnly: field.readonly === true,
114
- required: field.required === true,
115
- rows: 4,
116
- "aria-describedby": describedBy,
117
- onChange: (e) => onChange(e.target.value)
118
- }
119
- );
120
- }
121
- function NumericInput({
122
- field,
123
- value,
124
- onChange,
125
- disabled,
126
- inputId,
127
- describedBy
128
- }) {
129
- return /* @__PURE__ */ jsx(
130
- "input",
131
- {
132
- id: inputId,
133
- type: "number",
134
- className: inputClasses,
135
- value: value === null || value === void 0 ? "" : String(value),
136
- placeholder: field.placeholder ?? void 0,
137
- disabled: disabled || field.disabled || field.readonly,
138
- readOnly: field.readonly === true,
139
- required: field.required === true,
140
- "aria-describedby": describedBy,
141
- onChange: (e) => {
142
- const raw = e.target.value;
143
- if (raw === "") {
144
- onChange(null);
145
- return;
146
- }
147
- const num = Number(raw);
148
- onChange(Number.isNaN(num) ? null : num);
149
- }
150
- }
151
- );
152
- }
153
- function Checkbox({
154
- field,
155
- value,
156
- onChange,
157
- disabled,
158
- inputId,
159
- describedBy
160
- }) {
161
- return /* @__PURE__ */ jsx(
162
- "input",
163
- {
164
- id: inputId,
165
- type: "checkbox",
166
- className: "h-4 w-4 rounded border-[var(--input)]",
167
- checked: value === true,
168
- disabled: disabled || field.disabled || field.readonly,
169
- "aria-describedby": describedBy,
170
- onChange: (e) => onChange(e.target.checked)
171
- }
172
- );
173
- }
174
- function normaliseOptions(options) {
175
- if (!options) return [];
176
- if (Array.isArray(options)) return options;
177
- return Object.entries(options).map(([value, label]) => ({ value, label }));
178
- }
179
- function SelectInput({
180
- field,
181
- value,
182
- onChange,
183
- disabled,
184
- inputId,
185
- describedBy
186
- }) {
187
- const options = normaliseOptions(field.props.options);
188
- return /* @__PURE__ */ jsxs(
189
- "select",
190
- {
191
- id: inputId,
192
- className: inputClasses,
193
- value: value === null || value === void 0 ? "" : String(value),
194
- disabled: disabled || field.disabled || field.readonly,
195
- required: field.required === true,
196
- "aria-describedby": describedBy,
197
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
198
- children: [
199
- field.placeholder && /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder }),
200
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
201
- ]
202
- }
203
- );
204
- }
205
- function MultiSelectInput({
206
- field,
207
- value,
208
- onChange,
209
- disabled,
210
- inputId,
211
- describedBy
212
- }) {
213
- const arr = Array.isArray(value) ? value : [];
214
- const options = field.type === "multiSelect" ? normaliseOptions(field.props.options) : [];
215
- return /* @__PURE__ */ jsx(
216
- "select",
217
- {
218
- id: inputId,
219
- multiple: true,
220
- className: cn(inputClasses, "h-auto min-h-[6rem] py-1"),
221
- value: arr.map(String),
222
- disabled: disabled || field.disabled || field.readonly,
223
- "aria-describedby": describedBy,
224
- onChange: (e) => {
225
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
226
- onChange(next);
227
- },
228
- children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
229
- }
230
- );
231
- }
232
- function RadioGroup({
233
- field,
234
- value,
235
- onChange,
236
- disabled,
237
- inputId,
238
- describedBy
239
- }) {
240
- return /* @__PURE__ */ jsx(
241
- "div",
242
- {
243
- id: inputId,
244
- className: "flex flex-col gap-1",
245
- "aria-describedby": describedBy,
246
- role: "radiogroup",
247
- children: (field.props.options ?? []).map((opt) => /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm", children: [
248
- /* @__PURE__ */ jsx(
249
- "input",
250
- {
251
- type: "radio",
252
- name: field.name,
253
- value: String(opt.value),
254
- checked: String(value ?? "") === String(opt.value),
255
- disabled: disabled || field.disabled || field.readonly,
256
- onChange: () => onChange(opt.value)
257
- }
258
- ),
259
- opt.label
260
- ] }, opt.value))
261
- }
262
- );
263
- }
264
- function BelongsToInput({
265
- field,
266
- value,
267
- onChange,
268
- disabled,
269
- inputId,
270
- describedBy
271
- }) {
272
- return /* @__PURE__ */ jsx(
273
- "input",
274
- {
275
- id: inputId,
276
- type: "text",
277
- className: inputClasses,
278
- value: value === null || value === void 0 ? "" : String(value),
279
- placeholder: field.placeholder ?? `Search ${field.props.relatedResource}\u2026`,
280
- disabled: disabled || field.disabled || field.readonly,
281
- readOnly: field.readonly === true,
282
- "aria-describedby": describedBy,
283
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
284
- }
285
- );
286
- }
287
- function DateInput({
288
- field,
289
- value,
290
- onChange,
291
- disabled,
292
- inputId,
293
- describedBy
294
- }) {
295
- return /* @__PURE__ */ jsx(
296
- "input",
297
- {
298
- id: inputId,
299
- type: field.type === "dateTime" ? "datetime-local" : "date",
300
- className: inputClasses,
301
- value: typeof value === "string" ? value : "",
302
- disabled: disabled || field.disabled || field.readonly,
303
- readOnly: field.readonly === true,
304
- required: field.required === true,
305
- "aria-describedby": describedBy,
306
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
307
- }
308
- );
309
- }
310
- function FileInput({
311
- field,
312
- onChange,
313
- disabled,
314
- inputId,
315
- describedBy
316
- }) {
317
- return /* @__PURE__ */ jsx(
318
- "input",
319
- {
320
- id: inputId,
321
- type: "file",
322
- className: "text-sm",
323
- disabled: disabled || field.disabled || field.readonly,
324
- "aria-describedby": describedBy,
325
- onChange: (e) => onChange(e.target.files?.[0] ?? null)
326
- }
327
- );
328
- }
329
- function FieldRenderer(props) {
330
- const { field, errors, className } = props;
331
- const id = useId();
332
- const errorId = `${id}-error`;
333
- const helperId = `${id}-helper`;
334
- const hasError = errors && errors.length > 0;
335
- const Custom = field.component ? getFieldComponent(field.component) : void 0;
336
- const Input = Custom ?? NativeFieldInput;
337
- const describedBy = [field.helperText ? helperId : null, hasError ? errorId : null].filter(Boolean).join(" ");
338
- return /* @__PURE__ */ jsxs(
339
- "div",
340
- {
341
- "data-arqel-field": field.name,
342
- "data-field-type": field.type,
343
- className: cn("flex flex-col gap-1", className),
344
- children: [
345
- field.label !== null && field.label !== "" && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-sm font-medium", children: [
346
- field.label,
347
- field.required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })
348
- ] }),
349
- /* @__PURE__ */ jsx(Input, { ...props, inputId: id, describedBy: describedBy || void 0 }),
350
- field.helperText && !hasError && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-xs text-muted-foreground", children: field.helperText }),
351
- hasError && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-xs text-destructive", children: errors[0] })
352
- ]
353
- }
354
- );
355
- }
356
- var buttonVariants = cva(
357
- "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
358
- {
359
- variants: {
360
- variant: {
361
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
362
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
363
- outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
364
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
365
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
366
- link: "text-primary underline-offset-4 hover:underline"
367
- },
368
- size: {
369
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
370
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
371
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
372
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
373
- icon: "size-9",
374
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
375
- "icon-sm": "size-8",
376
- "icon-lg": "size-10"
377
- }
378
- },
379
- defaultVariants: {
380
- variant: "default",
381
- size: "default"
382
- }
383
- }
384
- );
385
- function Button({
386
- className,
387
- variant = "default",
388
- size = "default",
389
- asChild = false,
390
- ...props
391
- }) {
392
- const Comp = asChild ? Slot.Root : "button";
393
- return /* @__PURE__ */ jsx(
394
- Comp,
395
- {
396
- "data-slot": "button",
397
- "data-variant": variant,
398
- "data-size": size,
399
- className: cn(buttonVariants({ variant, size, className })),
400
- ...props
401
- }
402
- );
403
- }
404
- function FormActions({
405
- submitLabel = "Save",
406
- cancelLabel = "Cancel",
407
- onCancel,
408
- processing = false,
409
- disabled = false,
410
- extra,
411
- className
412
- }) {
413
- return /* @__PURE__ */ jsxs(
414
- "div",
415
- {
416
- className: cn(
417
- "flex flex-wrap items-center justify-end gap-2 border-t border-border pt-4",
418
- className
419
- ),
420
- children: [
421
- extra && /* @__PURE__ */ jsx("div", { className: "mr-auto", children: extra }),
422
- onCancel && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: processing, children: cancelLabel }),
423
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: processing || disabled, children: processing ? "Saving\u2026" : submitLabel })
424
- ]
425
- }
426
- );
427
- }
428
- function FormFieldset({ config, children, className }) {
429
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("rounded-md border border-border p-4", className), children: [
430
- /* @__PURE__ */ jsx("legend", { className: "px-1 text-sm font-medium", children: config.legend }),
431
- /* @__PURE__ */ jsx(
432
- "div",
433
- {
434
- className: "grid gap-4",
435
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
436
- children
437
- }
438
- )
439
- ] });
440
- }
441
- function FormGrid({ config, children, className }) {
442
- const columns = typeof config.columns === "number" ? config.columns : config.columns["md"] ?? 1;
443
- return /* @__PURE__ */ jsx(
444
- "div",
445
- {
446
- className: cn("grid", className),
447
- style: {
448
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
449
- gap: config.gap ?? "1rem"
450
- },
451
- children
452
- }
453
- );
454
- }
455
- function FormSection({ config, children, className }) {
456
- const [open, setOpen] = useState(!config.collapsed);
457
- const collapsible = config.collapsible === true;
458
- const heading = /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
459
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
460
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: config.heading }),
461
- config.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: config.description })
462
- ] }),
463
- collapsible && /* @__PURE__ */ jsx(
464
- "button",
465
- {
466
- type: "button",
467
- "aria-expanded": open,
468
- onClick: () => setOpen((v) => !v),
469
- className: "text-sm text-muted-foreground hover:underline",
470
- children: open ? "Hide" : "Show"
471
- }
472
- )
473
- ] });
474
- const body = open && /* @__PURE__ */ jsx(
475
- "div",
476
- {
477
- className: "grid gap-4",
478
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
479
- children
480
- }
481
- );
482
- return /* @__PURE__ */ jsx(
483
- "section",
484
- {
485
- "data-arqel-section": "",
486
- className: cn(
487
- "rounded-md border border-border bg-background",
488
- config.compact ? "p-3" : "p-4",
489
- className
490
- ),
491
- children: config.aside ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[16rem_1fr]", children: [
492
- /* @__PURE__ */ jsx("div", { children: heading }),
493
- /* @__PURE__ */ jsx("div", { children: body })
494
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
495
- /* @__PURE__ */ jsx("div", { className: "mb-3", children: heading }),
496
- body
497
- ] })
498
- }
499
- );
500
- }
501
- function FormTabs({ config, tabs, className }) {
502
- const [active, setActive] = useState(config.defaultTab ?? tabs[0]?.id ?? null);
503
- const baseId = useId();
504
- const refs = useRef([]);
505
- if (tabs.length === 0 || active === null) return null;
506
- const activeIndex = Math.max(
507
- 0,
508
- tabs.findIndex((t) => t.id === active)
509
- );
510
- const activeTab = tabs[activeIndex];
511
- if (!activeTab) return null;
512
- const focus = (i) => {
513
- const target = tabs[i];
514
- if (!target) return;
515
- setActive(target.id);
516
- refs.current[i]?.focus();
517
- };
518
- const onKeyDown = (e) => {
519
- if (e.key === "ArrowRight") {
520
- e.preventDefault();
521
- focus((activeIndex + 1) % tabs.length);
522
- } else if (e.key === "ArrowLeft") {
523
- e.preventDefault();
524
- focus((activeIndex - 1 + tabs.length) % tabs.length);
525
- } else if (e.key === "Home") {
526
- e.preventDefault();
527
- focus(0);
528
- } else if (e.key === "End") {
529
- e.preventDefault();
530
- focus(tabs.length - 1);
531
- }
532
- };
533
- return /* @__PURE__ */ jsxs("div", { className, children: [
534
- /* @__PURE__ */ jsx(
535
- "div",
536
- {
537
- role: "tablist",
538
- "aria-orientation": config.orientation,
539
- className: "flex gap-1 border-b border-border",
540
- children: tabs.map((tab, i) => {
541
- const selected = tab.id === active;
542
- return /* @__PURE__ */ jsxs(
543
- "button",
544
- {
545
- ref: (el) => {
546
- refs.current[i] = el;
547
- },
548
- type: "button",
549
- role: "tab",
550
- id: `${baseId}-tab-${tab.id}`,
551
- "aria-controls": `${baseId}-panel-${tab.id}`,
552
- "aria-selected": selected,
553
- tabIndex: selected ? 0 : -1,
554
- onClick: () => setActive(tab.id),
555
- onKeyDown,
556
- className: cn(
557
- "-mb-px border-b-2 px-3 py-2 text-sm transition-colors",
558
- selected ? "border-primary font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
559
- ),
560
- children: [
561
- tab.label,
562
- tab.badge !== void 0 && tab.badge !== null && /* @__PURE__ */ jsx("span", { className: "ml-1 rounded-full bg-muted px-1.5 text-xs", children: tab.badge })
563
- ]
564
- },
565
- tab.id
566
- );
567
- })
568
- }
569
- ),
570
- /* @__PURE__ */ jsx(
571
- "div",
572
- {
573
- role: "tabpanel",
574
- id: `${baseId}-panel-${activeTab.id}`,
575
- "aria-labelledby": `${baseId}-tab-${activeTab.id}`,
576
- className: "pt-4",
577
- children: activeTab.content
578
- }
579
- )
580
- ] });
581
- }
582
- function FormRenderer({
583
- schema,
584
- fields,
585
- values,
586
- errors = {},
587
- onChange,
588
- disabled = false,
589
- className
590
- }) {
591
- const fieldsByName = new Map(fields.map((f) => [f.name, f]));
592
- const renderEntry = (entry, key) => {
593
- if (isFieldEntry(entry)) {
594
- return renderFieldEntry(entry, key);
595
- }
596
- if (isLayoutEntry(entry)) {
597
- return renderLayoutEntry(entry, key);
598
- }
599
- return null;
600
- };
601
- const renderFieldEntry = (entry, key) => {
602
- const field = fieldsByName.get(entry.name);
603
- if (!field) return null;
604
- return /* @__PURE__ */ jsx(
605
- FieldRenderer,
606
- {
607
- field,
608
- value: values[entry.name],
609
- errors: errors[entry.name],
610
- disabled: disabled || schema.disabled === true,
611
- onChange: (value) => onChange(entry.name, value)
612
- },
613
- key
614
- );
615
- };
616
- const renderChildren = (entries) => {
617
- if (!entries) return null;
618
- return entries.map((child, i) => renderEntry(child, `${i}`));
619
- };
620
- const renderLayoutEntry = (entry, key) => {
621
- switch (entry.type) {
622
- case "section":
623
- return /* @__PURE__ */ jsx(FormSection, { config: entry.props, children: renderChildren(entry.schema) }, key);
624
- case "fieldset":
625
- return /* @__PURE__ */ jsx(FormFieldset, { config: entry.props, children: renderChildren(entry.schema) }, key);
626
- case "grid":
627
- case "columns":
628
- return /* @__PURE__ */ jsx(
629
- FormGrid,
630
- {
631
- config: entry.type === "columns" ? { columns: entry.props.columns } : entry.props,
632
- children: renderChildren(entry.schema)
633
- },
634
- key
635
- );
636
- case "group":
637
- return /* @__PURE__ */ jsx(
638
- "div",
639
- {
640
- className: cn(
641
- "flex gap-4",
642
- entry.props.orientation === "vertical" ? "flex-col" : "flex-row"
643
- ),
644
- children: renderChildren(entry.schema)
645
- },
646
- key
647
- );
648
- case "tabs": {
649
- const tabs = (entry.schema ?? []).filter((c) => isLayoutEntry(c) && c.type === "tab").map((tab) => ({
650
- id: tab.props.id,
651
- label: tab.props.label,
652
- badge: tab.props.badge,
653
- content: renderChildren(tab.schema) ?? /* @__PURE__ */ jsx("span", {}, `tab-${tab.props.id}-empty`)
654
- }));
655
- return /* @__PURE__ */ jsx(FormTabs, { config: entry.props, tabs }, key);
656
- }
657
- case "tab":
658
- return null;
659
- }
660
- };
661
- const columns = schema.columns ?? 1;
662
- return /* @__PURE__ */ jsx(
663
- "div",
664
- {
665
- "data-arqel-form": "",
666
- className: cn("grid gap-4", className),
667
- style: columns > 1 ? { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` } : void 0,
668
- children: schema.schema.map((entry, i) => renderEntry(entry, `${i}`))
669
- }
670
- );
671
- }
672
-
673
- export { FieldRenderer, FormActions, FormFieldset, FormGrid, FormRenderer, FormSection, FormTabs, NativeFieldInput, clearFieldRegistry, getFieldComponent, getRegisteredFields, registerField, unregisterField };
1
+ export { FormActions } from './chunk-MTZJDFQ6.js';
2
+ export { FieldRenderer, FormFieldset, FormGrid, FormRenderer, FormSection, FormTabs, NativeFieldInput, clearFieldRegistry, getFieldComponent, getRegisteredFields, registerField, unregisterField } from './chunk-MSRTGCTJ.js';
3
+ import './chunk-FHRQ7PWE.js';
4
+ import './chunk-B7ULUJVO.js';
674
5
  //# sourceMappingURL=form.js.map
675
6
  //# sourceMappingURL=form.js.map