@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/action.js CHANGED
@@ -1,1092 +1,8 @@
1
- import { useState, useMemo, useEffect, useId, useRef } from 'react';
2
- import { cva } from 'class-variance-authority';
3
- import { Slot, Dialog as Dialog$1, DropdownMenu as DropdownMenu$1 } from 'radix-ui';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { isFieldEntry, isLayoutEntry } from '@arqel-dev/types/forms';
8
- import { XIcon } from 'lucide-react';
9
-
10
- // src/action/ActionButton.tsx
11
- function cn(...inputs) {
12
- return twMerge(clsx(inputs));
13
- }
14
- var buttonVariants = cva(
15
- "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",
16
- {
17
- variants: {
18
- variant: {
19
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
20
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
21
- 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",
22
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
23
- ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
24
- link: "text-primary underline-offset-4 hover:underline"
25
- },
26
- size: {
27
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
28
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
29
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
30
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
31
- icon: "size-9",
32
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
33
- "icon-sm": "size-8",
34
- "icon-lg": "size-10"
35
- }
36
- },
37
- defaultVariants: {
38
- variant: "default",
39
- size: "default"
40
- }
41
- }
42
- );
43
- function Button({
44
- className,
45
- variant = "default",
46
- size = "default",
47
- asChild = false,
48
- ...props
49
- }) {
50
- const Comp = asChild ? Slot.Root : "button";
51
- return /* @__PURE__ */ jsx(
52
- Comp,
53
- {
54
- "data-slot": "button",
55
- "data-variant": variant,
56
- "data-size": size,
57
- className: cn(buttonVariants({ variant, size, className })),
58
- ...props
59
- }
60
- );
61
- }
62
- function FormActions({
63
- submitLabel = "Save",
64
- cancelLabel = "Cancel",
65
- onCancel,
66
- processing = false,
67
- disabled = false,
68
- extra,
69
- className
70
- }) {
71
- return /* @__PURE__ */ jsxs(
72
- "div",
73
- {
74
- className: cn(
75
- "flex flex-wrap items-center justify-end gap-2 border-t border-border pt-4",
76
- className
77
- ),
78
- children: [
79
- extra && /* @__PURE__ */ jsx("div", { className: "mr-auto", children: extra }),
80
- onCancel && /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: onCancel, disabled: processing, children: cancelLabel }),
81
- /* @__PURE__ */ jsx(Button, { type: "submit", disabled: processing || disabled, children: processing ? "Saving\u2026" : submitLabel })
82
- ]
83
- }
84
- );
85
- }
86
-
87
- // src/form/FieldRegistry.tsx
88
- var registry = /* @__PURE__ */ new Map();
89
- function getFieldComponent(name) {
90
- return registry.get(name);
91
- }
92
- var inputClasses = cn(
93
- "h-9 rounded-sm border border-[var(--input)]",
94
- "bg-background px-3 text-sm",
95
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
96
- "disabled:cursor-not-allowed disabled:opacity-50"
97
- );
98
- function NativeFieldInput(props) {
99
- const { field } = props;
100
- switch (field.type) {
101
- case "text":
102
- case "email":
103
- case "url":
104
- case "password":
105
- case "slug":
106
- return /* @__PURE__ */ jsx(TextLike, { ...props, field });
107
- case "textarea":
108
- return /* @__PURE__ */ jsx(Textarea, { ...props, field });
109
- case "number":
110
- case "currency":
111
- return /* @__PURE__ */ jsx(NumericInput, { ...props, field });
112
- case "boolean":
113
- case "toggle":
114
- return /* @__PURE__ */ jsx(Checkbox, { ...props, field });
115
- case "select":
116
- return /* @__PURE__ */ jsx(SelectInput, { ...props, field });
117
- case "multiSelect":
118
- case "hasMany":
119
- return /* @__PURE__ */ jsx(MultiSelectInput, { ...props, field });
120
- case "radio":
121
- return /* @__PURE__ */ jsx(RadioGroup, { ...props, field });
122
- case "belongsTo":
123
- return /* @__PURE__ */ jsx(BelongsToInput, { ...props, field });
124
- case "date":
125
- case "dateTime":
126
- return /* @__PURE__ */ jsx(DateInput, { ...props, field });
127
- case "file":
128
- case "image":
129
- return /* @__PURE__ */ jsx(FileInput, { ...props });
130
- default:
131
- return null;
132
- }
133
- }
134
- function TextLike({
135
- field,
136
- value,
137
- onChange,
138
- disabled,
139
- inputId,
140
- describedBy
141
- }) {
142
- const htmlType = field.type === "email" ? "email" : field.type === "password" ? "password" : field.type === "url" ? "url" : "text";
143
- const isDisabled = disabled || field.disabled || field.readonly;
144
- return /* @__PURE__ */ jsx(
145
- "input",
146
- {
147
- id: inputId,
148
- type: htmlType,
149
- className: inputClasses,
150
- value: typeof value === "string" ? value : (value ?? "")?.toString() ?? "",
151
- placeholder: field.placeholder ?? void 0,
152
- disabled: isDisabled,
153
- readOnly: field.readonly === true,
154
- required: field.required === true,
155
- "aria-describedby": describedBy,
156
- onChange: (e) => onChange(e.target.value)
157
- }
158
- );
159
- }
160
- function Textarea({
161
- field,
162
- value,
163
- onChange,
164
- disabled,
165
- inputId,
166
- describedBy
167
- }) {
168
- return /* @__PURE__ */ jsx(
169
- "textarea",
170
- {
171
- id: inputId,
172
- className: cn(inputClasses, "h-auto min-h-[5rem] py-2"),
173
- value: typeof value === "string" ? value : "",
174
- placeholder: field.placeholder ?? void 0,
175
- disabled: disabled || field.disabled || field.readonly,
176
- readOnly: field.readonly === true,
177
- required: field.required === true,
178
- rows: 4,
179
- "aria-describedby": describedBy,
180
- onChange: (e) => onChange(e.target.value)
181
- }
182
- );
183
- }
184
- function NumericInput({
185
- field,
186
- value,
187
- onChange,
188
- disabled,
189
- inputId,
190
- describedBy
191
- }) {
192
- return /* @__PURE__ */ jsx(
193
- "input",
194
- {
195
- id: inputId,
196
- type: "number",
197
- className: inputClasses,
198
- value: value === null || value === void 0 ? "" : String(value),
199
- placeholder: field.placeholder ?? void 0,
200
- disabled: disabled || field.disabled || field.readonly,
201
- readOnly: field.readonly === true,
202
- required: field.required === true,
203
- "aria-describedby": describedBy,
204
- onChange: (e) => {
205
- const raw = e.target.value;
206
- if (raw === "") {
207
- onChange(null);
208
- return;
209
- }
210
- const num = Number(raw);
211
- onChange(Number.isNaN(num) ? null : num);
212
- }
213
- }
214
- );
215
- }
216
- function Checkbox({
217
- field,
218
- value,
219
- onChange,
220
- disabled,
221
- inputId,
222
- describedBy
223
- }) {
224
- return /* @__PURE__ */ jsx(
225
- "input",
226
- {
227
- id: inputId,
228
- type: "checkbox",
229
- className: "h-4 w-4 rounded border-[var(--input)]",
230
- checked: value === true,
231
- disabled: disabled || field.disabled || field.readonly,
232
- "aria-describedby": describedBy,
233
- onChange: (e) => onChange(e.target.checked)
234
- }
235
- );
236
- }
237
- function normaliseOptions(options) {
238
- if (!options) return [];
239
- if (Array.isArray(options)) return options;
240
- return Object.entries(options).map(([value, label]) => ({ value, label }));
241
- }
242
- function SelectInput({
243
- field,
244
- value,
245
- onChange,
246
- disabled,
247
- inputId,
248
- describedBy
249
- }) {
250
- const options = normaliseOptions(field.props.options);
251
- return /* @__PURE__ */ jsxs(
252
- "select",
253
- {
254
- id: inputId,
255
- className: inputClasses,
256
- value: value === null || value === void 0 ? "" : String(value),
257
- disabled: disabled || field.disabled || field.readonly,
258
- required: field.required === true,
259
- "aria-describedby": describedBy,
260
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
261
- children: [
262
- field.placeholder && /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder }),
263
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
264
- ]
265
- }
266
- );
267
- }
268
- function MultiSelectInput({
269
- field,
270
- value,
271
- onChange,
272
- disabled,
273
- inputId,
274
- describedBy
275
- }) {
276
- const arr = Array.isArray(value) ? value : [];
277
- const options = field.type === "multiSelect" ? normaliseOptions(field.props.options) : [];
278
- return /* @__PURE__ */ jsx(
279
- "select",
280
- {
281
- id: inputId,
282
- multiple: true,
283
- className: cn(inputClasses, "h-auto min-h-[6rem] py-1"),
284
- value: arr.map(String),
285
- disabled: disabled || field.disabled || field.readonly,
286
- "aria-describedby": describedBy,
287
- onChange: (e) => {
288
- const next = Array.from(e.target.selectedOptions, (o) => o.value);
289
- onChange(next);
290
- },
291
- children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
292
- }
293
- );
294
- }
295
- function RadioGroup({
296
- field,
297
- value,
298
- onChange,
299
- disabled,
300
- inputId,
301
- describedBy
302
- }) {
303
- return /* @__PURE__ */ jsx(
304
- "div",
305
- {
306
- id: inputId,
307
- className: "flex flex-col gap-1",
308
- "aria-describedby": describedBy,
309
- role: "radiogroup",
310
- children: (field.props.options ?? []).map((opt) => /* @__PURE__ */ jsxs("label", { className: "inline-flex items-center gap-2 text-sm", children: [
311
- /* @__PURE__ */ jsx(
312
- "input",
313
- {
314
- type: "radio",
315
- name: field.name,
316
- value: String(opt.value),
317
- checked: String(value ?? "") === String(opt.value),
318
- disabled: disabled || field.disabled || field.readonly,
319
- onChange: () => onChange(opt.value)
320
- }
321
- ),
322
- opt.label
323
- ] }, opt.value))
324
- }
325
- );
326
- }
327
- function BelongsToInput({
328
- field,
329
- value,
330
- onChange,
331
- disabled,
332
- inputId,
333
- describedBy
334
- }) {
335
- return /* @__PURE__ */ jsx(
336
- "input",
337
- {
338
- id: inputId,
339
- type: "text",
340
- className: inputClasses,
341
- value: value === null || value === void 0 ? "" : String(value),
342
- placeholder: field.placeholder ?? `Search ${field.props.relatedResource}\u2026`,
343
- disabled: disabled || field.disabled || field.readonly,
344
- readOnly: field.readonly === true,
345
- "aria-describedby": describedBy,
346
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
347
- }
348
- );
349
- }
350
- function DateInput({
351
- field,
352
- value,
353
- onChange,
354
- disabled,
355
- inputId,
356
- describedBy
357
- }) {
358
- return /* @__PURE__ */ jsx(
359
- "input",
360
- {
361
- id: inputId,
362
- type: field.type === "dateTime" ? "datetime-local" : "date",
363
- className: inputClasses,
364
- value: typeof value === "string" ? value : "",
365
- disabled: disabled || field.disabled || field.readonly,
366
- readOnly: field.readonly === true,
367
- required: field.required === true,
368
- "aria-describedby": describedBy,
369
- onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
370
- }
371
- );
372
- }
373
- function FileInput({
374
- field,
375
- onChange,
376
- disabled,
377
- inputId,
378
- describedBy
379
- }) {
380
- return /* @__PURE__ */ jsx(
381
- "input",
382
- {
383
- id: inputId,
384
- type: "file",
385
- className: "text-sm",
386
- disabled: disabled || field.disabled || field.readonly,
387
- "aria-describedby": describedBy,
388
- onChange: (e) => onChange(e.target.files?.[0] ?? null)
389
- }
390
- );
391
- }
392
- function FieldRenderer(props) {
393
- const { field, errors, className } = props;
394
- const id = useId();
395
- const errorId = `${id}-error`;
396
- const helperId = `${id}-helper`;
397
- const hasError = errors && errors.length > 0;
398
- const Custom = field.component ? getFieldComponent(field.component) : void 0;
399
- const Input = Custom ?? NativeFieldInput;
400
- const describedBy = [field.helperText ? helperId : null, hasError ? errorId : null].filter(Boolean).join(" ");
401
- return /* @__PURE__ */ jsxs(
402
- "div",
403
- {
404
- "data-arqel-field": field.name,
405
- "data-field-type": field.type,
406
- className: cn("flex flex-col gap-1", className),
407
- children: [
408
- field.label !== null && field.label !== "" && /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "text-sm font-medium", children: [
409
- field.label,
410
- field.required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-0.5 text-destructive", children: "*" })
411
- ] }),
412
- /* @__PURE__ */ jsx(Input, { ...props, inputId: id, describedBy: describedBy || void 0 }),
413
- field.helperText && !hasError && /* @__PURE__ */ jsx("p", { id: helperId, className: "text-xs text-muted-foreground", children: field.helperText }),
414
- hasError && /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-xs text-destructive", children: errors[0] })
415
- ]
416
- }
417
- );
418
- }
419
- function FormFieldset({ config, children, className }) {
420
- return /* @__PURE__ */ jsxs("fieldset", { className: cn("rounded-md border border-border p-4", className), children: [
421
- /* @__PURE__ */ jsx("legend", { className: "px-1 text-sm font-medium", children: config.legend }),
422
- /* @__PURE__ */ jsx(
423
- "div",
424
- {
425
- className: "grid gap-4",
426
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
427
- children
428
- }
429
- )
430
- ] });
431
- }
432
- function FormGrid({ config, children, className }) {
433
- const columns = typeof config.columns === "number" ? config.columns : config.columns["md"] ?? 1;
434
- return /* @__PURE__ */ jsx(
435
- "div",
436
- {
437
- className: cn("grid", className),
438
- style: {
439
- gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
440
- gap: config.gap ?? "1rem"
441
- },
442
- children
443
- }
444
- );
445
- }
446
- function FormSection({ config, children, className }) {
447
- const [open, setOpen] = useState(!config.collapsed);
448
- const collapsible = config.collapsible === true;
449
- const heading = /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
450
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
451
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: config.heading }),
452
- config.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: config.description })
453
- ] }),
454
- collapsible && /* @__PURE__ */ jsx(
455
- "button",
456
- {
457
- type: "button",
458
- "aria-expanded": open,
459
- onClick: () => setOpen((v) => !v),
460
- className: "text-sm text-muted-foreground hover:underline",
461
- children: open ? "Hide" : "Show"
462
- }
463
- )
464
- ] });
465
- const body = open && /* @__PURE__ */ jsx(
466
- "div",
467
- {
468
- className: "grid gap-4",
469
- style: { gridTemplateColumns: `repeat(${config.columns ?? 1}, minmax(0, 1fr))` },
470
- children
471
- }
472
- );
473
- return /* @__PURE__ */ jsx(
474
- "section",
475
- {
476
- "data-arqel-section": "",
477
- className: cn(
478
- "rounded-md border border-border bg-background",
479
- config.compact ? "p-3" : "p-4",
480
- className
481
- ),
482
- children: config.aside ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[16rem_1fr]", children: [
483
- /* @__PURE__ */ jsx("div", { children: heading }),
484
- /* @__PURE__ */ jsx("div", { children: body })
485
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
486
- /* @__PURE__ */ jsx("div", { className: "mb-3", children: heading }),
487
- body
488
- ] })
489
- }
490
- );
491
- }
492
- function FormTabs({ config, tabs, className }) {
493
- const [active, setActive] = useState(config.defaultTab ?? tabs[0]?.id ?? null);
494
- const baseId = useId();
495
- const refs = useRef([]);
496
- if (tabs.length === 0 || active === null) return null;
497
- const activeIndex = Math.max(
498
- 0,
499
- tabs.findIndex((t) => t.id === active)
500
- );
501
- const activeTab = tabs[activeIndex];
502
- if (!activeTab) return null;
503
- const focus = (i) => {
504
- const target = tabs[i];
505
- if (!target) return;
506
- setActive(target.id);
507
- refs.current[i]?.focus();
508
- };
509
- const onKeyDown = (e) => {
510
- if (e.key === "ArrowRight") {
511
- e.preventDefault();
512
- focus((activeIndex + 1) % tabs.length);
513
- } else if (e.key === "ArrowLeft") {
514
- e.preventDefault();
515
- focus((activeIndex - 1 + tabs.length) % tabs.length);
516
- } else if (e.key === "Home") {
517
- e.preventDefault();
518
- focus(0);
519
- } else if (e.key === "End") {
520
- e.preventDefault();
521
- focus(tabs.length - 1);
522
- }
523
- };
524
- return /* @__PURE__ */ jsxs("div", { className, children: [
525
- /* @__PURE__ */ jsx(
526
- "div",
527
- {
528
- role: "tablist",
529
- "aria-orientation": config.orientation,
530
- className: "flex gap-1 border-b border-border",
531
- children: tabs.map((tab, i) => {
532
- const selected = tab.id === active;
533
- return /* @__PURE__ */ jsxs(
534
- "button",
535
- {
536
- ref: (el) => {
537
- refs.current[i] = el;
538
- },
539
- type: "button",
540
- role: "tab",
541
- id: `${baseId}-tab-${tab.id}`,
542
- "aria-controls": `${baseId}-panel-${tab.id}`,
543
- "aria-selected": selected,
544
- tabIndex: selected ? 0 : -1,
545
- onClick: () => setActive(tab.id),
546
- onKeyDown,
547
- className: cn(
548
- "-mb-px border-b-2 px-3 py-2 text-sm transition-colors",
549
- selected ? "border-primary font-medium text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
550
- ),
551
- children: [
552
- tab.label,
553
- 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 })
554
- ]
555
- },
556
- tab.id
557
- );
558
- })
559
- }
560
- ),
561
- /* @__PURE__ */ jsx(
562
- "div",
563
- {
564
- role: "tabpanel",
565
- id: `${baseId}-panel-${activeTab.id}`,
566
- "aria-labelledby": `${baseId}-tab-${activeTab.id}`,
567
- className: "pt-4",
568
- children: activeTab.content
569
- }
570
- )
571
- ] });
572
- }
573
- function FormRenderer({
574
- schema,
575
- fields,
576
- values,
577
- errors = {},
578
- onChange,
579
- disabled = false,
580
- className
581
- }) {
582
- const fieldsByName = new Map(fields.map((f) => [f.name, f]));
583
- const renderEntry = (entry, key) => {
584
- if (isFieldEntry(entry)) {
585
- return renderFieldEntry(entry, key);
586
- }
587
- if (isLayoutEntry(entry)) {
588
- return renderLayoutEntry(entry, key);
589
- }
590
- return null;
591
- };
592
- const renderFieldEntry = (entry, key) => {
593
- const field = fieldsByName.get(entry.name);
594
- if (!field) return null;
595
- return /* @__PURE__ */ jsx(
596
- FieldRenderer,
597
- {
598
- field,
599
- value: values[entry.name],
600
- errors: errors[entry.name],
601
- disabled: disabled || schema.disabled === true,
602
- onChange: (value) => onChange(entry.name, value)
603
- },
604
- key
605
- );
606
- };
607
- const renderChildren = (entries) => {
608
- if (!entries) return null;
609
- return entries.map((child, i) => renderEntry(child, `${i}`));
610
- };
611
- const renderLayoutEntry = (entry, key) => {
612
- switch (entry.type) {
613
- case "section":
614
- return /* @__PURE__ */ jsx(FormSection, { config: entry.props, children: renderChildren(entry.schema) }, key);
615
- case "fieldset":
616
- return /* @__PURE__ */ jsx(FormFieldset, { config: entry.props, children: renderChildren(entry.schema) }, key);
617
- case "grid":
618
- case "columns":
619
- return /* @__PURE__ */ jsx(
620
- FormGrid,
621
- {
622
- config: entry.type === "columns" ? { columns: entry.props.columns } : entry.props,
623
- children: renderChildren(entry.schema)
624
- },
625
- key
626
- );
627
- case "group":
628
- return /* @__PURE__ */ jsx(
629
- "div",
630
- {
631
- className: cn(
632
- "flex gap-4",
633
- entry.props.orientation === "vertical" ? "flex-col" : "flex-row"
634
- ),
635
- children: renderChildren(entry.schema)
636
- },
637
- key
638
- );
639
- case "tabs": {
640
- const tabs = (entry.schema ?? []).filter((c) => isLayoutEntry(c) && c.type === "tab").map((tab) => ({
641
- id: tab.props.id,
642
- label: tab.props.label,
643
- badge: tab.props.badge,
644
- content: renderChildren(tab.schema) ?? /* @__PURE__ */ jsx("span", {}, `tab-${tab.props.id}-empty`)
645
- }));
646
- return /* @__PURE__ */ jsx(FormTabs, { config: entry.props, tabs }, key);
647
- }
648
- case "tab":
649
- return null;
650
- }
651
- };
652
- const columns = schema.columns ?? 1;
653
- return /* @__PURE__ */ jsx(
654
- "div",
655
- {
656
- "data-arqel-form": "",
657
- className: cn("grid gap-4", className),
658
- style: columns > 1 ? { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` } : void 0,
659
- children: schema.schema.map((entry, i) => renderEntry(entry, `${i}`))
660
- }
661
- );
662
- }
663
- function Dialog({
664
- ...props
665
- }) {
666
- return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
667
- }
668
- function DialogPortal({
669
- ...props
670
- }) {
671
- return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
672
- }
673
- function DialogClose({
674
- ...props
675
- }) {
676
- return /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
677
- }
678
- function DialogOverlay({
679
- className,
680
- ...props
681
- }) {
682
- return /* @__PURE__ */ jsx(
683
- Dialog$1.Overlay,
684
- {
685
- "data-slot": "dialog-overlay",
686
- className: cn(
687
- "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
688
- className
689
- ),
690
- ...props
691
- }
692
- );
693
- }
694
- function DialogContent({
695
- className,
696
- children,
697
- showCloseButton = true,
698
- ...props
699
- }) {
700
- return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
701
- /* @__PURE__ */ jsx(DialogOverlay, {}),
702
- /* @__PURE__ */ jsxs(
703
- Dialog$1.Content,
704
- {
705
- "data-slot": "dialog-content",
706
- className: cn(
707
- "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
708
- className
709
- ),
710
- ...props,
711
- children: [
712
- children,
713
- showCloseButton && /* @__PURE__ */ jsxs(
714
- Dialog$1.Close,
715
- {
716
- "data-slot": "dialog-close",
717
- className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
718
- children: [
719
- /* @__PURE__ */ jsx(XIcon, {}),
720
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
721
- ]
722
- }
723
- )
724
- ]
725
- }
726
- )
727
- ] });
728
- }
729
- function DialogHeader({ className, ...props }) {
730
- return /* @__PURE__ */ jsx(
731
- "div",
732
- {
733
- "data-slot": "dialog-header",
734
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
735
- ...props
736
- }
737
- );
738
- }
739
- function DialogFooter({
740
- className,
741
- showCloseButton = false,
742
- children,
743
- ...props
744
- }) {
745
- return /* @__PURE__ */ jsxs(
746
- "div",
747
- {
748
- "data-slot": "dialog-footer",
749
- className: cn(
750
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
751
- className
752
- ),
753
- ...props,
754
- children: [
755
- children,
756
- showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
757
- ]
758
- }
759
- );
760
- }
761
- function DialogTitle({
762
- className,
763
- ...props
764
- }) {
765
- return /* @__PURE__ */ jsx(
766
- Dialog$1.Title,
767
- {
768
- "data-slot": "dialog-title",
769
- className: cn("text-lg leading-none font-semibold", className),
770
- ...props
771
- }
772
- );
773
- }
774
- function DialogDescription({
775
- className,
776
- ...props
777
- }) {
778
- return /* @__PURE__ */ jsx(
779
- Dialog$1.Description,
780
- {
781
- "data-slot": "dialog-description",
782
- className: cn("text-sm text-muted-foreground", className),
783
- ...props
784
- }
785
- );
786
- }
787
- var SIZE_CLASSES = {
788
- sm: "sm:max-w-[24rem]",
789
- md: "sm:max-w-[32rem]",
790
- lg: "sm:max-w-[44rem]",
791
- xl: "sm:max-w-[56rem]",
792
- full: "sm:max-w-[90vw] h-[90vh]"
793
- };
794
- function ActionFormModal({
795
- open,
796
- onOpenChange,
797
- action,
798
- fields,
799
- onSubmit,
800
- processing = false,
801
- errors = {},
802
- initialValues = {}
803
- }) {
804
- const [values, setValues] = useState(initialValues);
805
- const size = action.modalSize ?? "md";
806
- const schema = useMemo(
807
- () => ({
808
- schema: (action.form ?? []).map((entry) => ({
809
- kind: "field",
810
- name: entry.name,
811
- type: entry.type
812
- })),
813
- columns: 1,
814
- model: null,
815
- inline: false,
816
- disabled: false
817
- }),
818
- [action.form]
819
- );
820
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: cn("overflow-auto", SIZE_CLASSES[size]), children: [
821
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: action.label }) }),
822
- /* @__PURE__ */ jsxs(
823
- "form",
824
- {
825
- className: "flex flex-col gap-4",
826
- onSubmit: (e) => {
827
- e.preventDefault();
828
- onSubmit(values);
829
- },
830
- children: [
831
- /* @__PURE__ */ jsx(
832
- FormRenderer,
833
- {
834
- schema,
835
- fields,
836
- values,
837
- errors,
838
- onChange: (name, value) => setValues((prev) => ({ ...prev, [name]: value })),
839
- disabled: processing
840
- }
841
- ),
842
- /* @__PURE__ */ jsx(
843
- FormActions,
844
- {
845
- processing,
846
- submitLabel: action.label,
847
- extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: "Cancel" }) })
848
- }
849
- )
850
- ]
851
- }
852
- )
853
- ] }) });
854
- }
855
- var COLOR_VARIANT = {
856
- destructive: "destructive",
857
- warning: "default",
858
- info: "default"
859
- };
860
- function ConfirmDialog({
861
- open,
862
- onOpenChange,
863
- config,
864
- onConfirm,
865
- processing = false
866
- }) {
867
- const [typed, setTyped] = useState("");
868
- useEffect(() => {
869
- if (!open) setTyped("");
870
- }, [open]);
871
- const requiresText = config?.requiresText;
872
- const submitDisabled = processing || requiresText !== void 0 && typed !== requiresText;
873
- const variant = COLOR_VARIANT[config?.color ?? "destructive"];
874
- const submitLabel = config?.submitLabel ?? (config?.color === "info" ? "Confirm" : "Delete");
875
- const cancelLabel = config?.cancelLabel ?? "Cancel";
876
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[28rem]", showCloseButton: false, children: [
877
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
878
- /* @__PURE__ */ jsx(DialogTitle, { children: config?.heading ?? "Are you sure?" }),
879
- config?.description && /* @__PURE__ */ jsx(DialogDescription, { children: config.description })
880
- ] }),
881
- requiresText !== void 0 && /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
882
- /* @__PURE__ */ jsxs("span", { children: [
883
- "Type ",
884
- /* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: requiresText }),
885
- " to confirm"
886
- ] }),
887
- /* @__PURE__ */ jsx(
888
- "input",
889
- {
890
- type: "text",
891
- ref: (el) => el?.focus(),
892
- value: typed,
893
- onChange: (e) => setTyped(e.target.value),
894
- onKeyDown: (e) => {
895
- if (e.key === "Enter" && !submitDisabled) {
896
- e.preventDefault();
897
- onConfirm();
898
- }
899
- },
900
- className: "h-9 rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
901
- }
902
- )
903
- ] }),
904
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
905
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: cancelLabel }) }),
906
- /* @__PURE__ */ jsx(Button, { type: "button", variant, disabled: submitDisabled, onClick: onConfirm, children: processing ? "Working\u2026" : submitLabel })
907
- ] })
908
- ] }) });
909
- }
910
- var COLOR_TO_VARIANT = {
911
- primary: "default",
912
- secondary: "outline",
913
- destructive: "destructive",
914
- success: "default",
915
- warning: "default",
916
- info: "default"
917
- };
918
- var VARIANT_OVERRIDE = {
919
- default: "default",
920
- outline: "outline",
921
- ghost: "ghost",
922
- destructive: "destructive"
923
- };
924
- function ActionButton({
925
- action,
926
- formFields = [],
927
- onInvoke,
928
- processing = false,
929
- errors,
930
- size = "default",
931
- className
932
- }) {
933
- const [confirmOpen, setConfirmOpen] = useState(false);
934
- const [formOpen, setFormOpen] = useState(false);
935
- const variant = VARIANT_OVERRIDE[action.variant] ?? COLOR_TO_VARIANT[action.color];
936
- const handleClick = () => {
937
- if (action.disabled) return;
938
- if (action.requiresConfirmation) {
939
- setConfirmOpen(true);
940
- } else if (action.form && action.form.length > 0) {
941
- setFormOpen(true);
942
- } else {
943
- onInvoke();
944
- }
945
- };
946
- const handleConfirm = () => {
947
- setConfirmOpen(false);
948
- if (action.form && action.form.length > 0) {
949
- setFormOpen(true);
950
- } else {
951
- onInvoke();
952
- }
953
- };
954
- const handleFormSubmit = (values) => {
955
- onInvoke(values);
956
- setFormOpen(false);
957
- };
958
- return /* @__PURE__ */ jsxs(Fragment, { children: [
959
- /* @__PURE__ */ jsx(
960
- Button,
961
- {
962
- variant,
963
- size,
964
- disabled: action.disabled === true || processing,
965
- title: action.tooltip,
966
- onClick: handleClick,
967
- className,
968
- children: action.label
969
- }
970
- ),
971
- action.requiresConfirmation && /* @__PURE__ */ jsx(
972
- ConfirmDialog,
973
- {
974
- open: confirmOpen,
975
- onOpenChange: setConfirmOpen,
976
- config: action.confirmation,
977
- onConfirm: handleConfirm,
978
- processing
979
- }
980
- ),
981
- action.form && action.form.length > 0 && /* @__PURE__ */ jsx(
982
- ActionFormModal,
983
- {
984
- open: formOpen,
985
- onOpenChange: setFormOpen,
986
- action,
987
- fields: formFields,
988
- onSubmit: handleFormSubmit,
989
- processing,
990
- errors
991
- }
992
- )
993
- ] });
994
- }
995
- function DropdownMenu({
996
- ...props
997
- }) {
998
- return /* @__PURE__ */ jsx(DropdownMenu$1.Root, { "data-slot": "dropdown-menu", ...props });
999
- }
1000
- function DropdownMenuTrigger({
1001
- ...props
1002
- }) {
1003
- return /* @__PURE__ */ jsx(
1004
- DropdownMenu$1.Trigger,
1005
- {
1006
- "data-slot": "dropdown-menu-trigger",
1007
- ...props
1008
- }
1009
- );
1010
- }
1011
- function DropdownMenuContent({
1012
- className,
1013
- sideOffset = 4,
1014
- ...props
1015
- }) {
1016
- return /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(
1017
- DropdownMenu$1.Content,
1018
- {
1019
- "data-slot": "dropdown-menu-content",
1020
- sideOffset,
1021
- className: cn(
1022
- "z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1023
- className
1024
- ),
1025
- ...props
1026
- }
1027
- ) });
1028
- }
1029
- function DropdownMenuItem({
1030
- className,
1031
- inset,
1032
- variant = "default",
1033
- ...props
1034
- }) {
1035
- return /* @__PURE__ */ jsx(
1036
- DropdownMenu$1.Item,
1037
- {
1038
- "data-slot": "dropdown-menu-item",
1039
- "data-inset": inset,
1040
- "data-variant": variant,
1041
- className: cn(
1042
- "relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
1043
- className
1044
- ),
1045
- ...props
1046
- }
1047
- );
1048
- }
1049
- function ActionMenu({
1050
- actions,
1051
- formFieldsByAction = {},
1052
- onInvoke,
1053
- inlineThreshold = 3,
1054
- processing = false,
1055
- trigger,
1056
- className
1057
- }) {
1058
- if (actions.length === 0) return null;
1059
- if (actions.length <= inlineThreshold) {
1060
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-1", className), children: actions.map((action) => /* @__PURE__ */ jsx(
1061
- ActionButton,
1062
- {
1063
- action,
1064
- formFields: formFieldsByAction[action.name] ?? [],
1065
- onInvoke: (values) => onInvoke(action, values),
1066
- processing,
1067
- size: "sm"
1068
- },
1069
- action.name
1070
- )) });
1071
- }
1072
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1073
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Actions", children: "\u22EF" }) }),
1074
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", sideOffset: 6, className: cn("min-w-[12rem]", className), children: actions.map((action) => /* @__PURE__ */ jsx(
1075
- DropdownMenuItem,
1076
- {
1077
- disabled: action.disabled === true,
1078
- variant: action.color === "destructive" ? "destructive" : "default",
1079
- onSelect: () => {
1080
- if (action.disabled) return;
1081
- onInvoke(action);
1082
- },
1083
- children: action.label
1084
- },
1085
- action.name
1086
- )) })
1087
- ] });
1088
- }
1089
-
1090
- export { ActionButton, ActionFormModal, ActionMenu, Button, ConfirmDialog, buttonVariants };
1
+ export { ActionButton, ActionFormModal, ActionMenu, ConfirmDialog } from './chunk-TNJEHYRF.js';
2
+ import './chunk-EYAUPJ6J.js';
3
+ import './chunk-MTZJDFQ6.js';
4
+ import './chunk-MSRTGCTJ.js';
5
+ export { Button, buttonVariants } from './chunk-FHRQ7PWE.js';
6
+ import './chunk-B7ULUJVO.js';
1091
7
  //# sourceMappingURL=action.js.map
1092
8
  //# sourceMappingURL=action.js.map