@page-speed/forms 0.5.2 → 0.5.4

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 (55) hide show
  1. package/dist/chunk-232KNGJI.js +207 -0
  2. package/dist/chunk-232KNGJI.js.map +1 -0
  3. package/dist/chunk-24RPM43T.js +373 -0
  4. package/dist/chunk-24RPM43T.js.map +1 -0
  5. package/dist/chunk-27JUYRDE.cjs +173 -0
  6. package/dist/chunk-27JUYRDE.cjs.map +1 -0
  7. package/dist/chunk-5NT5T5XY.js +4136 -0
  8. package/dist/chunk-5NT5T5XY.js.map +1 -0
  9. package/dist/chunk-AVAKC6R7.cjs +236 -0
  10. package/dist/chunk-AVAKC6R7.cjs.map +1 -0
  11. package/dist/chunk-DKLLPKZN.cjs +238 -0
  12. package/dist/chunk-DKLLPKZN.cjs.map +1 -0
  13. package/dist/chunk-EX6CRLKG.cjs +397 -0
  14. package/dist/chunk-EX6CRLKG.cjs.map +1 -0
  15. package/dist/chunk-H6NNFV64.js +127 -0
  16. package/dist/chunk-H6NNFV64.js.map +1 -0
  17. package/dist/chunk-JBEWTBFH.js +217 -0
  18. package/dist/chunk-JBEWTBFH.js.map +1 -0
  19. package/dist/chunk-JBEZLX3H.cjs +138 -0
  20. package/dist/chunk-JBEZLX3H.cjs.map +1 -0
  21. package/dist/chunk-VLGZG2VP.js +150 -0
  22. package/dist/chunk-VLGZG2VP.js.map +1 -0
  23. package/dist/chunk-ZYFTT6DB.cjs +4169 -0
  24. package/dist/chunk-ZYFTT6DB.cjs.map +1 -0
  25. package/dist/core.cjs +23 -733
  26. package/dist/core.cjs.map +1 -1
  27. package/dist/core.js +3 -716
  28. package/dist/core.js.map +1 -1
  29. package/dist/index.cjs +43 -738
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.js +3 -716
  32. package/dist/index.js.map +1 -1
  33. package/dist/inputs.cjs +44 -4359
  34. package/dist/inputs.cjs.map +1 -1
  35. package/dist/inputs.js +2 -4337
  36. package/dist/inputs.js.map +1 -1
  37. package/dist/integration.cjs +65 -4658
  38. package/dist/integration.cjs.map +1 -1
  39. package/dist/integration.d.cts +7 -1
  40. package/dist/integration.d.ts +7 -1
  41. package/dist/integration.js +42 -4635
  42. package/dist/integration.js.map +1 -1
  43. package/dist/validation-rules.cjs +75 -231
  44. package/dist/validation-rules.cjs.map +1 -1
  45. package/dist/validation-rules.js +1 -215
  46. package/dist/validation-rules.js.map +1 -1
  47. package/dist/validation-utils.cjs +43 -133
  48. package/dist/validation-utils.cjs.map +1 -1
  49. package/dist/validation-utils.js +1 -125
  50. package/dist/validation-utils.js.map +1 -1
  51. package/dist/validation.cjs +115 -364
  52. package/dist/validation.cjs.map +1 -1
  53. package/dist/validation.js +2 -339
  54. package/dist/validation.js.map +1 -1
  55. package/package.json +1 -1
@@ -0,0 +1,4169 @@
1
+ 'use strict';
2
+
3
+ var chunkDKLLPKZN_cjs = require('./chunk-DKLLPKZN.cjs');
4
+ var React21 = require('react');
5
+ var radixUi = require('radix-ui');
6
+ var cmdk = require('cmdk');
7
+ var reactDirection = require('@radix-ui/react-direction');
8
+ var reactSlot = require('@radix-ui/react-slot');
9
+ var reactDayPicker = require('react-day-picker');
10
+
11
+ function _interopNamespace(e) {
12
+ if (e && e.__esModule) return e;
13
+ var n = Object.create(null);
14
+ if (e) {
15
+ Object.keys(e).forEach(function (k) {
16
+ if (k !== 'default') {
17
+ var d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: function () { return e[k]; }
21
+ });
22
+ }
23
+ });
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+
29
+ var React21__namespace = /*#__PURE__*/_interopNamespace(React21);
30
+
31
+ var Input = React21__namespace.forwardRef(
32
+ ({ className, type, ...props }, ref) => {
33
+ return /* @__PURE__ */ React21__namespace.createElement(
34
+ "input",
35
+ {
36
+ ref,
37
+ type,
38
+ "data-slot": "input",
39
+ className: chunkDKLLPKZN_cjs.cn(
40
+ // Core structure - no hardcoded colors, uses CSS variables
41
+ "flex h-9 w-full min-w-0 rounded-md border border-input",
42
+ "bg-transparent px-3 py-1 text-base shadow-sm",
43
+ "transition-colors outline-none md:text-sm",
44
+ // Focus state - uses ring-ring CSS variable (adapts to theme)
45
+ "focus-visible:ring-1 focus-visible:ring-ring",
46
+ // Error state - uses destructive CSS variables (adapts to theme)
47
+ "aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive",
48
+ // Disabled state - no color hardcoding
49
+ "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
50
+ // File input specific - inherits text color from parent
51
+ "file:inline-flex file:h-7 file:border-0 file:bg-transparent",
52
+ "file:text-sm file:font-medium",
53
+ // Autofill reset - prevents browser from overriding our dynamic colors
54
+ chunkDKLLPKZN_cjs.INPUT_AUTOFILL_RESET_CLASSES,
55
+ className
56
+ ),
57
+ ...props
58
+ }
59
+ );
60
+ }
61
+ );
62
+ Input.displayName = "Input";
63
+
64
+ // src/inputs/TextInput.tsx
65
+ function TextInput({
66
+ name,
67
+ value,
68
+ onChange,
69
+ onBlur,
70
+ placeholder,
71
+ disabled = false,
72
+ required = false,
73
+ error = false,
74
+ className = "",
75
+ type = "text",
76
+ id = "text",
77
+ ...props
78
+ }) {
79
+ const handleChange = (e) => {
80
+ onChange(e.target.value);
81
+ };
82
+ const handleBlur = () => {
83
+ onBlur?.();
84
+ };
85
+ const hasValue = String(value ?? "").trim().length > 0;
86
+ return /* @__PURE__ */ React21__namespace.createElement(
87
+ Input,
88
+ {
89
+ type,
90
+ id,
91
+ name,
92
+ value: value ?? "",
93
+ onChange: handleChange,
94
+ onBlur: handleBlur,
95
+ placeholder,
96
+ disabled,
97
+ required,
98
+ className: chunkDKLLPKZN_cjs.cn(
99
+ // Valid value indicator - ring-2 when has value and no error
100
+ !error && hasValue && "ring-2 ring-ring",
101
+ // Error state - handled by Input component via aria-invalid
102
+ className
103
+ ),
104
+ "aria-invalid": error || props["aria-invalid"],
105
+ "aria-describedby": props["aria-describedby"],
106
+ "aria-required": required || props["aria-required"],
107
+ ...props
108
+ }
109
+ );
110
+ }
111
+ TextInput.displayName = "TextInput";
112
+ function Textarea({ className, ...props }) {
113
+ return /* @__PURE__ */ React21__namespace.createElement(
114
+ "textarea",
115
+ {
116
+ "data-slot": "textarea",
117
+ className: chunkDKLLPKZN_cjs.cn(
118
+ // Core structure - uses CSS variables only
119
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input",
120
+ "bg-transparent px-3 py-2 text-base shadow-xs",
121
+ "transition-[color,box-shadow] outline-none md:text-sm",
122
+ // Focus state - uses ring-ring CSS variable
123
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
124
+ // Error state - uses destructive CSS variables
125
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
126
+ // Disabled state
127
+ "disabled:cursor-not-allowed disabled:opacity-50",
128
+ className
129
+ ),
130
+ ...props
131
+ }
132
+ );
133
+ }
134
+
135
+ // src/inputs/TextArea.tsx
136
+ function TextArea({
137
+ name,
138
+ value,
139
+ onChange,
140
+ onBlur,
141
+ placeholder,
142
+ disabled = false,
143
+ required = false,
144
+ error = false,
145
+ className = "",
146
+ rows = 3,
147
+ cols,
148
+ maxLength,
149
+ minLength,
150
+ wrap = "soft",
151
+ ...props
152
+ }) {
153
+ const handleChange = (e) => {
154
+ onChange(e.target.value);
155
+ };
156
+ const handleBlur = () => {
157
+ onBlur?.();
158
+ };
159
+ const hasValue = String(value ?? "").trim().length > 0;
160
+ return /* @__PURE__ */ React21__namespace.createElement(
161
+ Textarea,
162
+ {
163
+ name,
164
+ value: value ?? "",
165
+ onChange: handleChange,
166
+ onBlur: handleBlur,
167
+ placeholder,
168
+ disabled,
169
+ required,
170
+ className: chunkDKLLPKZN_cjs.cn(
171
+ // Valid value indicator - ring-2 when has value and no error
172
+ !error && hasValue && "ring-2 ring-ring",
173
+ // Error state - handled by Textarea component via aria-invalid
174
+ className
175
+ ),
176
+ rows,
177
+ cols,
178
+ maxLength,
179
+ minLength,
180
+ wrap,
181
+ "aria-invalid": error || props["aria-invalid"],
182
+ "aria-describedby": props["aria-describedby"],
183
+ "aria-required": required || props["aria-required"],
184
+ ...props
185
+ }
186
+ );
187
+ }
188
+ TextArea.displayName = "TextArea";
189
+ function Checkbox({
190
+ className,
191
+ ...props
192
+ }) {
193
+ return /* @__PURE__ */ React21__namespace.createElement(
194
+ radixUi.Checkbox.Root,
195
+ {
196
+ "data-slot": "checkbox",
197
+ className: chunkDKLLPKZN_cjs.cn(
198
+ // Core structure - uses CSS variables
199
+ "peer size-4 shrink-0 rounded-[4px] border border-input bg-transparent shadow-xs",
200
+ "transition-shadow outline-none",
201
+ // Checked state - uses primary CSS variables
202
+ "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
203
+ "data-[state=checked]:border-primary",
204
+ // Focus state - uses ring-ring CSS variable
205
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
206
+ // Error state - uses destructive CSS variables
207
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
208
+ // Disabled state
209
+ "disabled:cursor-not-allowed disabled:opacity-50",
210
+ className
211
+ ),
212
+ ...props
213
+ },
214
+ /* @__PURE__ */ React21__namespace.createElement(
215
+ radixUi.Checkbox.Indicator,
216
+ {
217
+ "data-slot": "checkbox-indicator",
218
+ className: "grid place-content-center text-current transition-none"
219
+ },
220
+ /* @__PURE__ */ React21__namespace.createElement(
221
+ "svg",
222
+ {
223
+ className: "size-3.5",
224
+ viewBox: "0 0 24 24",
225
+ fill: "none",
226
+ stroke: "currentColor",
227
+ strokeWidth: "3",
228
+ strokeLinecap: "round",
229
+ strokeLinejoin: "round"
230
+ },
231
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "20 6 9 17 4 12" })
232
+ )
233
+ )
234
+ );
235
+ }
236
+
237
+ // src/inputs/Checkbox.tsx
238
+ function Checkbox2({
239
+ name,
240
+ value,
241
+ onChange,
242
+ onBlur,
243
+ disabled = false,
244
+ required = false,
245
+ error = false,
246
+ className = "",
247
+ label,
248
+ description,
249
+ useChoiceCard = false,
250
+ ...props
251
+ }) {
252
+ const checkboxId = props.id || `checkbox-${name}`;
253
+ const handleCheckedChange = (checked) => {
254
+ onChange(checked);
255
+ };
256
+ const handleBlur = () => {
257
+ onBlur?.();
258
+ };
259
+ const showChoiceCard = useChoiceCard || !!description;
260
+ const checkbox = /* @__PURE__ */ React21__namespace.createElement(React21__namespace.Fragment, null, /* @__PURE__ */ React21__namespace.createElement(
261
+ "input",
262
+ {
263
+ type: "checkbox",
264
+ name,
265
+ checked: value,
266
+ onChange: () => {
267
+ },
268
+ disabled,
269
+ required,
270
+ tabIndex: -1,
271
+ "aria-hidden": "true",
272
+ style: {
273
+ position: "absolute",
274
+ width: "1px",
275
+ height: "1px",
276
+ padding: 0,
277
+ margin: "-1px",
278
+ overflow: "hidden",
279
+ clip: "rect(0, 0, 0, 0)",
280
+ whiteSpace: "nowrap",
281
+ border: 0
282
+ }
283
+ }
284
+ ), /* @__PURE__ */ React21__namespace.createElement(
285
+ Checkbox,
286
+ {
287
+ id: checkboxId,
288
+ checked: value,
289
+ onCheckedChange: handleCheckedChange,
290
+ onBlur: handleBlur,
291
+ disabled,
292
+ "aria-invalid": error || props["aria-invalid"],
293
+ "aria-describedby": description ? `${checkboxId}-description` : props["aria-describedby"],
294
+ "aria-required": required || props["aria-required"],
295
+ ...props
296
+ }
297
+ ));
298
+ if (!label) {
299
+ return /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className }, checkbox);
300
+ }
301
+ return /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "gap-0", invalid: Boolean(error) }, /* @__PURE__ */ React21__namespace.createElement(
302
+ chunkDKLLPKZN_cjs.FieldLabel,
303
+ {
304
+ htmlFor: checkboxId,
305
+ className: chunkDKLLPKZN_cjs.cn(
306
+ "flex gap-3 p-3 duration-200 select-auto font-normal leading-normal",
307
+ showChoiceCard && "border rounded-lg hover:ring-2 hover:ring-ring/50",
308
+ showChoiceCard && value && "ring-2 ring-ring",
309
+ showChoiceCard && error && "border-destructive",
310
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
311
+ className
312
+ )
313
+ },
314
+ /* @__PURE__ */ React21__namespace.createElement(
315
+ "div",
316
+ {
317
+ className: chunkDKLLPKZN_cjs.cn(
318
+ "flex w-full gap-3",
319
+ showChoiceCard ? "items-start" : "items-center"
320
+ )
321
+ },
322
+ checkbox,
323
+ /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "flex-1 gap-1" }, /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-sm font-medium leading-none" }, label), description && /* @__PURE__ */ React21__namespace.createElement(
324
+ chunkDKLLPKZN_cjs.FieldDescription,
325
+ {
326
+ id: `${checkboxId}-description`,
327
+ className: "leading-snug"
328
+ },
329
+ description
330
+ ))
331
+ )
332
+ ));
333
+ }
334
+ Checkbox2.displayName = "Checkbox";
335
+ function CheckboxGroup({
336
+ name,
337
+ value = [],
338
+ onChange,
339
+ onBlur,
340
+ disabled = false,
341
+ required = false,
342
+ error = false,
343
+ className = "",
344
+ layout = "stacked",
345
+ label,
346
+ description,
347
+ options,
348
+ showSelectAll = false,
349
+ selectAllLabel = "Select all",
350
+ minSelections,
351
+ maxSelections,
352
+ renderOption,
353
+ gridColumns = 2,
354
+ ...props
355
+ }) {
356
+ const enabledOptions = options.filter((opt) => !opt.disabled);
357
+ const enabledValues = enabledOptions.map((opt) => opt.value);
358
+ const selectedEnabledCount = value.filter(
359
+ (v) => enabledValues.includes(v)
360
+ ).length;
361
+ const allSelected = selectedEnabledCount === enabledOptions.length;
362
+ const someSelected = selectedEnabledCount > 0 && !allSelected;
363
+ const useChoiceCard = React21__namespace.useMemo(() => {
364
+ if (!options) return false;
365
+ return options?.some((opt) => opt.description);
366
+ }, [options]);
367
+ const countableValue = React21__namespace.useMemo(() => {
368
+ if (value?.length > 0) {
369
+ return value.length;
370
+ }
371
+ return 0;
372
+ }, [value]);
373
+ const handleChange = (optionValue, checked) => {
374
+ const newValues = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
375
+ if (maxSelections && checked && newValues.length > maxSelections) {
376
+ return;
377
+ }
378
+ onChange(newValues);
379
+ };
380
+ const handleSelectAll = (checked) => {
381
+ if (checked) {
382
+ const allValues = enabledOptions.map((opt) => opt.value);
383
+ onChange(allValues);
384
+ } else {
385
+ onChange([]);
386
+ }
387
+ };
388
+ const handleBlur = () => {
389
+ onBlur?.();
390
+ };
391
+ const maxReached = Boolean(maxSelections && countableValue >= maxSelections);
392
+ const containerClass = React21__namespace.useMemo(() => {
393
+ return chunkDKLLPKZN_cjs.cn(
394
+ "w-full gap-3 grid grid-cols-1 border-0 m-0 p-0 min-w-0",
395
+ (layout === "grid" || layout === "inline") && "md:grid-cols-2",
396
+ className
397
+ );
398
+ }, [layout, className]);
399
+ const groupDescriptionId = description ? `${name}-description` : void 0;
400
+ const groupAriaDescribedBy = [props["aria-describedby"], groupDescriptionId].filter(Boolean).join(" ") || void 0;
401
+ return /* @__PURE__ */ React21__namespace.createElement(
402
+ "fieldset",
403
+ {
404
+ className: containerClass,
405
+ role: "group",
406
+ "aria-invalid": error || props["aria-invalid"],
407
+ "aria-describedby": groupAriaDescribedBy,
408
+ "aria-required": required || props["aria-required"],
409
+ "aria-label": typeof label === "string" ? label : props["aria-label"]
410
+ },
411
+ /* @__PURE__ */ React21__namespace.createElement(
412
+ chunkDKLLPKZN_cjs.LabelGroup,
413
+ {
414
+ labelHtmlFor: name,
415
+ required,
416
+ variant: "legend",
417
+ secondaryId: groupDescriptionId,
418
+ secondary: description,
419
+ primary: label
420
+ }
421
+ ),
422
+ showSelectAll && enabledOptions.length > 0 && /* @__PURE__ */ React21__namespace.createElement(
423
+ Checkbox2,
424
+ {
425
+ name: `${name}-select-all`,
426
+ id: `${name}-select-all`,
427
+ value: allSelected,
428
+ onChange: handleSelectAll,
429
+ onBlur: handleBlur,
430
+ indeterminate: someSelected,
431
+ label: selectAllLabel,
432
+ useChoiceCard,
433
+ disabled,
434
+ "aria-label": selectAllLabel
435
+ }
436
+ ),
437
+ options.map((option) => {
438
+ const isChecked = value.includes(option.value);
439
+ const isDisabled = disabled || option.disabled || maxReached && !isChecked;
440
+ return /* @__PURE__ */ React21__namespace.createElement(
441
+ Checkbox2,
442
+ {
443
+ key: option.value,
444
+ name,
445
+ id: `${name}-${option.value}`,
446
+ value: isChecked,
447
+ onChange: (checked) => handleChange(option.value, checked),
448
+ onBlur: handleBlur,
449
+ disabled: isDisabled,
450
+ required: required && minSelections ? value.length < minSelections : false,
451
+ error,
452
+ label: renderOption ? renderOption(option) : option.label,
453
+ description: renderOption ? void 0 : option.description,
454
+ useChoiceCard
455
+ }
456
+ );
457
+ }),
458
+ (minSelections || maxSelections) && /* @__PURE__ */ React21__namespace.createElement(
459
+ chunkDKLLPKZN_cjs.FieldDescription,
460
+ {
461
+ className: chunkDKLLPKZN_cjs.cn(
462
+ "p-2 rounded-lg border font-semibold mt-2 leading-snug",
463
+ minSelections && countableValue < minSelections ? "border-destructive bg-destructive/80 text-destructive-foreground" : "border-border bg-card text-card-foreground"
464
+ ),
465
+ "aria-live": "polite"
466
+ },
467
+ minSelections && countableValue < minSelections && /* @__PURE__ */ React21__namespace.createElement("span", null, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""),
468
+ maxSelections && /* @__PURE__ */ React21__namespace.createElement("span", null, countableValue, "/", maxSelections, " selected")
469
+ )
470
+ );
471
+ }
472
+ CheckboxGroup.displayName = "CheckboxGroup";
473
+ function RadioGroup({
474
+ className,
475
+ ...props
476
+ }) {
477
+ return /* @__PURE__ */ React21__namespace.createElement(
478
+ radixUi.RadioGroup.Root,
479
+ {
480
+ "data-slot": "radio-group",
481
+ className: chunkDKLLPKZN_cjs.cn("grid gap-3", className),
482
+ ...props
483
+ }
484
+ );
485
+ }
486
+ function RadioGroupItem({
487
+ className,
488
+ ...props
489
+ }) {
490
+ return /* @__PURE__ */ React21__namespace.createElement(
491
+ radixUi.RadioGroup.Item,
492
+ {
493
+ "data-slot": "radio-group-item",
494
+ className: chunkDKLLPKZN_cjs.cn(
495
+ // Core structure - uses CSS variables
496
+ "aspect-square size-4 shrink-0 rounded-full border border-input bg-transparent shadow-xs",
497
+ "text-primary transition-[color,box-shadow] outline-none",
498
+ // Focus state - uses ring-ring CSS variable
499
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
500
+ // Error state - uses destructive CSS variables
501
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
502
+ // Disabled state
503
+ "disabled:cursor-not-allowed disabled:opacity-50",
504
+ className
505
+ ),
506
+ ...props
507
+ },
508
+ /* @__PURE__ */ React21__namespace.createElement(
509
+ radixUi.RadioGroup.Indicator,
510
+ {
511
+ "data-slot": "radio-group-indicator",
512
+ className: "relative flex items-center justify-center"
513
+ },
514
+ /* @__PURE__ */ React21__namespace.createElement(
515
+ "svg",
516
+ {
517
+ className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2",
518
+ viewBox: "0 0 24 24"
519
+ },
520
+ /* @__PURE__ */ React21__namespace.createElement("circle", { cx: "12", cy: "12", r: "12" })
521
+ )
522
+ )
523
+ );
524
+ }
525
+
526
+ // src/inputs/Radio.tsx
527
+ function Radio({
528
+ name,
529
+ value,
530
+ onChange,
531
+ onBlur,
532
+ disabled = false,
533
+ required = false,
534
+ error = false,
535
+ className = "",
536
+ layout = "stacked",
537
+ label,
538
+ description,
539
+ options,
540
+ ...props
541
+ }) {
542
+ const handleValueChange = (selectedValue) => {
543
+ onChange(selectedValue);
544
+ };
545
+ const handleBlur = () => {
546
+ onBlur?.();
547
+ };
548
+ const useChoiceCard = React21__namespace.useMemo(() => {
549
+ return options.some((option) => option.description);
550
+ }, [options]);
551
+ const groupDescriptionId = description ? `${name}-description` : void 0;
552
+ return /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: chunkDKLLPKZN_cjs.cn("w-full", className), invalid: Boolean(error) }, (label || description) && /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "mb-3 gap-1" }, label && /* @__PURE__ */ React21__namespace.createElement("div", { className: "text-base font-medium leading-none" }, label), description && /* @__PURE__ */ React21__namespace.createElement(
553
+ chunkDKLLPKZN_cjs.FieldDescription,
554
+ {
555
+ id: groupDescriptionId,
556
+ className: "leading-snug"
557
+ },
558
+ description
559
+ )), /* @__PURE__ */ React21__namespace.createElement(
560
+ RadioGroup,
561
+ {
562
+ name,
563
+ value,
564
+ onValueChange: handleValueChange,
565
+ onBlur: handleBlur,
566
+ disabled,
567
+ required,
568
+ className: chunkDKLLPKZN_cjs.cn(
569
+ "gap-3",
570
+ layout === "grid" && "grid grid-cols-1 md:grid-cols-2",
571
+ layout === "inline" && "flex flex-wrap"
572
+ ),
573
+ "aria-invalid": error || props["aria-invalid"],
574
+ "aria-describedby": groupDescriptionId || props["aria-describedby"],
575
+ "aria-required": required || props["aria-required"]
576
+ },
577
+ options.map((option) => {
578
+ const isSelected = value === option.value;
579
+ const isDisabled = disabled || option.disabled;
580
+ const radioId = `${name}-${option.value}`;
581
+ const hasDescription = !!option.description;
582
+ return /* @__PURE__ */ React21__namespace.createElement(
583
+ chunkDKLLPKZN_cjs.FieldLabel,
584
+ {
585
+ key: option.value,
586
+ htmlFor: radioId,
587
+ className: chunkDKLLPKZN_cjs.cn(
588
+ "flex gap-3 p-3 duration-200 select-auto font-normal leading-normal",
589
+ useChoiceCard && "border rounded-lg hover:ring-2 hover:ring-ring/50",
590
+ useChoiceCard && isSelected && "ring-2 ring-ring",
591
+ useChoiceCard && error && "border-destructive",
592
+ isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
593
+ )
594
+ },
595
+ /* @__PURE__ */ React21__namespace.createElement(
596
+ chunkDKLLPKZN_cjs.Field,
597
+ {
598
+ orientation: "horizontal",
599
+ className: chunkDKLLPKZN_cjs.cn(
600
+ "flex w-full gap-3",
601
+ useChoiceCard ? "items-start" : "items-center"
602
+ )
603
+ },
604
+ /* @__PURE__ */ React21__namespace.createElement(
605
+ RadioGroupItem,
606
+ {
607
+ value: option.value,
608
+ id: radioId,
609
+ disabled: isDisabled,
610
+ className: "mt-0.5",
611
+ "aria-describedby": hasDescription ? `${radioId}-description` : void 0
612
+ }
613
+ ),
614
+ /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "flex-1 gap-1" }, /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-sm font-medium leading-none" }, option.label), option.description && /* @__PURE__ */ React21__namespace.createElement(
615
+ chunkDKLLPKZN_cjs.FieldDescription,
616
+ {
617
+ id: `${radioId}-description`,
618
+ className: "leading-snug"
619
+ },
620
+ option.description
621
+ ))
622
+ )
623
+ );
624
+ })
625
+ ));
626
+ }
627
+ Radio.displayName = "Radio";
628
+ function Switch({
629
+ className,
630
+ size = "default",
631
+ ...props
632
+ }) {
633
+ return /* @__PURE__ */ React21__namespace.createElement(
634
+ radixUi.Switch.Root,
635
+ {
636
+ "data-slot": "switch",
637
+ "data-size": size,
638
+ className: chunkDKLLPKZN_cjs.cn(
639
+ // Core structure - uses CSS variables
640
+ "peer group/switch inline-flex shrink-0 items-center rounded-full",
641
+ "border border-transparent shadow-xs transition-all outline-none",
642
+ // State-based backgrounds - use CSS variables
643
+ "data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
644
+ // Focus state
645
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
646
+ // Disabled state
647
+ "disabled:cursor-not-allowed disabled:opacity-50",
648
+ // Size variants
649
+ "data-[size=default]:h-[1.15rem] data-[size=default]:w-8",
650
+ "data-[size=sm]:h-3.5 data-[size=sm]:w-6",
651
+ className
652
+ ),
653
+ ...props
654
+ },
655
+ /* @__PURE__ */ React21__namespace.createElement(
656
+ radixUi.Switch.Thumb,
657
+ {
658
+ "data-slot": "switch-thumb",
659
+ className: chunkDKLLPKZN_cjs.cn(
660
+ // Thumb appearance - inherits from parent theme
661
+ "bg-background pointer-events-none block rounded-full ring-0 transition-transform",
662
+ // Size variants
663
+ "group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3",
664
+ // Position based on state
665
+ "data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
666
+ )
667
+ }
668
+ )
669
+ );
670
+ }
671
+
672
+ // src/inputs/Switch.tsx
673
+ function Switch2({
674
+ name,
675
+ value,
676
+ onChange,
677
+ onBlur,
678
+ disabled = false,
679
+ required = false,
680
+ error = false,
681
+ className = "",
682
+ label,
683
+ description,
684
+ size = "default",
685
+ ...props
686
+ }) {
687
+ const switchId = props.id || `switch-${name}`;
688
+ const handleCheckedChange = (checked) => {
689
+ onChange(checked);
690
+ };
691
+ const handleBlur = () => {
692
+ onBlur?.();
693
+ };
694
+ const switchElement = /* @__PURE__ */ React21__namespace.createElement(
695
+ Switch,
696
+ {
697
+ id: switchId,
698
+ checked: value,
699
+ onCheckedChange: handleCheckedChange,
700
+ onBlur: handleBlur,
701
+ disabled,
702
+ size,
703
+ "aria-invalid": error || props["aria-invalid"],
704
+ "aria-describedby": description ? `${switchId}-description` : props["aria-describedby"],
705
+ "aria-required": required || props["aria-required"],
706
+ ...props
707
+ }
708
+ );
709
+ if (!label) {
710
+ return /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className }, switchElement);
711
+ }
712
+ return /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "gap-0", invalid: Boolean(error) }, /* @__PURE__ */ React21__namespace.createElement(
713
+ chunkDKLLPKZN_cjs.FieldLabel,
714
+ {
715
+ htmlFor: switchId,
716
+ className: chunkDKLLPKZN_cjs.cn(
717
+ "flex items-center gap-3 cursor-pointer select-auto font-normal leading-normal",
718
+ disabled && "opacity-50 cursor-not-allowed",
719
+ className
720
+ )
721
+ },
722
+ switchElement,
723
+ /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Field, { className: "gap-1" }, /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-sm font-medium leading-none" }, label), description && /* @__PURE__ */ React21__namespace.createElement(
724
+ chunkDKLLPKZN_cjs.FieldDescription,
725
+ {
726
+ id: `${switchId}-description`,
727
+ className: "leading-snug"
728
+ },
729
+ description
730
+ ))
731
+ ));
732
+ }
733
+ Switch2.displayName = "Switch";
734
+ function Select({
735
+ ...props
736
+ }) {
737
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.Root, { "data-slot": "select", ...props });
738
+ }
739
+ function SelectGroup({
740
+ ...props
741
+ }) {
742
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.Group, { "data-slot": "select-group", ...props });
743
+ }
744
+ function SelectValue({
745
+ ...props
746
+ }) {
747
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.Value, { "data-slot": "select-value", ...props });
748
+ }
749
+ function SelectTrigger({
750
+ className,
751
+ size = "default",
752
+ children,
753
+ ...props
754
+ }) {
755
+ return /* @__PURE__ */ React21__namespace.createElement(
756
+ radixUi.Select.Trigger,
757
+ {
758
+ "data-slot": "select-trigger",
759
+ "data-size": size,
760
+ className: chunkDKLLPKZN_cjs.cn(
761
+ // Core structure - uses CSS variables
762
+ "flex w-fit items-center justify-between gap-2 rounded-md border border-input",
763
+ "bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs",
764
+ "transition-[color,box-shadow] outline-none",
765
+ // Focus state
766
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
767
+ // Error state
768
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
769
+ // Disabled state
770
+ "disabled:cursor-not-allowed disabled:opacity-50",
771
+ // Size variants
772
+ "data-[size=default]:h-9 data-[size=sm]:h-8",
773
+ // Value styling
774
+ "*:data-[slot=select-value]:line-clamp-1",
775
+ "*:data-[slot=select-value]:flex",
776
+ "*:data-[slot=select-value]:items-center",
777
+ "*:data-[slot=select-value]:gap-2",
778
+ // SVG styling
779
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
780
+ className
781
+ ),
782
+ ...props
783
+ },
784
+ children,
785
+ /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.Icon, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
786
+ "svg",
787
+ {
788
+ className: "size-4 opacity-50",
789
+ viewBox: "0 0 24 24",
790
+ fill: "none",
791
+ stroke: "currentColor",
792
+ strokeWidth: "2",
793
+ strokeLinecap: "round",
794
+ strokeLinejoin: "round"
795
+ },
796
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "6 9 12 15 18 9" })
797
+ ))
798
+ );
799
+ }
800
+ function SelectContent({
801
+ className,
802
+ children,
803
+ position = "item-aligned",
804
+ align = "center",
805
+ ...props
806
+ }) {
807
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.Portal, null, /* @__PURE__ */ React21__namespace.createElement(
808
+ radixUi.Select.Content,
809
+ {
810
+ "data-slot": "select-content",
811
+ className: chunkDKLLPKZN_cjs.cn(
812
+ "bg-popover text-popover-foreground 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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
813
+ 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",
814
+ className
815
+ ),
816
+ position,
817
+ align,
818
+ ...props
819
+ },
820
+ /* @__PURE__ */ React21__namespace.createElement(SelectScrollUpButton, null),
821
+ /* @__PURE__ */ React21__namespace.createElement(
822
+ radixUi.Select.Viewport,
823
+ {
824
+ className: chunkDKLLPKZN_cjs.cn(
825
+ "p-1",
826
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
827
+ )
828
+ },
829
+ children
830
+ ),
831
+ /* @__PURE__ */ React21__namespace.createElement(SelectScrollDownButton, null)
832
+ ));
833
+ }
834
+ function SelectLabel({
835
+ className,
836
+ ...props
837
+ }) {
838
+ return /* @__PURE__ */ React21__namespace.createElement(
839
+ radixUi.Select.Label,
840
+ {
841
+ "data-slot": "select-label",
842
+ className: chunkDKLLPKZN_cjs.cn("px-2 py-1.5 text-xs opacity-70", className),
843
+ ...props
844
+ }
845
+ );
846
+ }
847
+ function SelectItem({
848
+ className,
849
+ children,
850
+ ...props
851
+ }) {
852
+ return /* @__PURE__ */ React21__namespace.createElement(
853
+ radixUi.Select.Item,
854
+ {
855
+ "data-slot": "select-item",
856
+ className: chunkDKLLPKZN_cjs.cn(
857
+ // Core structure - inherits text color
858
+ "relative flex w-full cursor-default items-center gap-2 rounded-sm",
859
+ "py-1.5 pr-8 pl-2 text-sm outline-hidden select-none",
860
+ // Focus state - uses accent CSS variable
861
+ "focus:bg-accent focus:text-accent-foreground",
862
+ // Disabled state
863
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
864
+ // SVG styling
865
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
866
+ // Span styling
867
+ "*:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
868
+ className
869
+ ),
870
+ ...props
871
+ },
872
+ /* @__PURE__ */ React21__namespace.createElement(
873
+ "span",
874
+ {
875
+ "data-slot": "select-item-indicator",
876
+ className: "absolute right-2 flex size-3.5 items-center justify-center"
877
+ },
878
+ /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.ItemIndicator, null, /* @__PURE__ */ React21__namespace.createElement(
879
+ "svg",
880
+ {
881
+ className: "size-4",
882
+ viewBox: "0 0 24 24",
883
+ fill: "none",
884
+ stroke: "currentColor",
885
+ strokeWidth: "3",
886
+ strokeLinecap: "round",
887
+ strokeLinejoin: "round"
888
+ },
889
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "20 6 9 17 4 12" })
890
+ ))
891
+ ),
892
+ /* @__PURE__ */ React21__namespace.createElement(radixUi.Select.ItemText, null, children)
893
+ );
894
+ }
895
+ function SelectScrollUpButton({
896
+ className,
897
+ ...props
898
+ }) {
899
+ return /* @__PURE__ */ React21__namespace.createElement(
900
+ radixUi.Select.ScrollUpButton,
901
+ {
902
+ "data-slot": "select-scroll-up-button",
903
+ className: chunkDKLLPKZN_cjs.cn(
904
+ "flex cursor-default items-center justify-center py-1",
905
+ className
906
+ ),
907
+ ...props
908
+ },
909
+ /* @__PURE__ */ React21__namespace.createElement(
910
+ "svg",
911
+ {
912
+ className: "size-4",
913
+ viewBox: "0 0 24 24",
914
+ fill: "none",
915
+ stroke: "currentColor",
916
+ strokeWidth: "2",
917
+ strokeLinecap: "round",
918
+ strokeLinejoin: "round"
919
+ },
920
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "18 15 12 9 6 15" })
921
+ )
922
+ );
923
+ }
924
+ function SelectScrollDownButton({
925
+ className,
926
+ ...props
927
+ }) {
928
+ return /* @__PURE__ */ React21__namespace.createElement(
929
+ radixUi.Select.ScrollDownButton,
930
+ {
931
+ "data-slot": "select-scroll-down-button",
932
+ className: chunkDKLLPKZN_cjs.cn(
933
+ "flex cursor-default items-center justify-center py-1",
934
+ className
935
+ ),
936
+ ...props
937
+ },
938
+ /* @__PURE__ */ React21__namespace.createElement(
939
+ "svg",
940
+ {
941
+ className: "size-4",
942
+ viewBox: "0 0 24 24",
943
+ fill: "none",
944
+ stroke: "currentColor",
945
+ strokeWidth: "2",
946
+ strokeLinecap: "round",
947
+ strokeLinejoin: "round"
948
+ },
949
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "6 9 12 15 18 9" })
950
+ )
951
+ );
952
+ }
953
+
954
+ // src/inputs/Select.tsx
955
+ function Select2({
956
+ name,
957
+ value,
958
+ onChange,
959
+ onBlur,
960
+ onFocus,
961
+ disabled = false,
962
+ required = false,
963
+ error = false,
964
+ className = "",
965
+ placeholder = "Select...",
966
+ options = [],
967
+ optionGroups = [],
968
+ renderOption,
969
+ ...props
970
+ }) {
971
+ const [hasInteracted, setHasInteracted] = React21__namespace.useState(false);
972
+ const allOptions = React21__namespace.useMemo(() => {
973
+ if (optionGroups.length > 0) {
974
+ return optionGroups.flatMap((group) => group.options);
975
+ }
976
+ return options;
977
+ }, [options, optionGroups]);
978
+ const hasValue = Boolean(value);
979
+ const selectValue = value ? String(value) : void 0;
980
+ const handleValueChange = (newValue) => {
981
+ onChange(newValue);
982
+ };
983
+ const handleOpenChange = (open) => {
984
+ if (open) {
985
+ if (!hasInteracted) {
986
+ setHasInteracted(true);
987
+ }
988
+ onFocus?.();
989
+ } else if (hasInteracted) {
990
+ onBlur?.();
991
+ }
992
+ };
993
+ return /* @__PURE__ */ React21__namespace.createElement(React21__namespace.Fragment, null, /* @__PURE__ */ React21__namespace.createElement(
994
+ "input",
995
+ {
996
+ type: "hidden",
997
+ name,
998
+ value: value ?? "",
999
+ disabled,
1000
+ required,
1001
+ tabIndex: -1,
1002
+ "aria-hidden": "true",
1003
+ style: {
1004
+ position: "absolute",
1005
+ width: "1px",
1006
+ height: "1px",
1007
+ padding: "0",
1008
+ margin: "-1px",
1009
+ overflow: "hidden",
1010
+ clip: "rect(0, 0, 0, 0)",
1011
+ whiteSpace: "nowrap",
1012
+ border: "0"
1013
+ }
1014
+ }
1015
+ ), /* @__PURE__ */ React21__namespace.createElement(
1016
+ Select,
1017
+ {
1018
+ value: selectValue,
1019
+ onValueChange: handleValueChange,
1020
+ onOpenChange: handleOpenChange,
1021
+ disabled
1022
+ },
1023
+ /* @__PURE__ */ React21__namespace.createElement(
1024
+ SelectTrigger,
1025
+ {
1026
+ className: chunkDKLLPKZN_cjs.cn(
1027
+ // Valid value indicator - ring-2 when has value and no error
1028
+ !error && hasValue && "ring-2 ring-ring",
1029
+ // Error state - handled by SelectTrigger via aria-invalid
1030
+ className
1031
+ ),
1032
+ "aria-invalid": error || props["aria-invalid"],
1033
+ "aria-describedby": props["aria-describedby"],
1034
+ "aria-required": required || props["aria-required"]
1035
+ },
1036
+ /* @__PURE__ */ React21__namespace.createElement(SelectValue, { placeholder })
1037
+ ),
1038
+ /* @__PURE__ */ React21__namespace.createElement(SelectContent, null, optionGroups.length > 0 ? (
1039
+ // Render grouped options
1040
+ optionGroups.map((group, groupIndex) => /* @__PURE__ */ React21__namespace.createElement(SelectGroup, { key: groupIndex }, /* @__PURE__ */ React21__namespace.createElement(SelectLabel, null, group.label), group.options.map((option) => /* @__PURE__ */ React21__namespace.createElement(
1041
+ SelectItem,
1042
+ {
1043
+ key: option.value,
1044
+ value: option.value,
1045
+ disabled: option.disabled
1046
+ },
1047
+ renderOption ? renderOption(option) : option.label
1048
+ ))))
1049
+ ) : (
1050
+ // Render flat options
1051
+ allOptions.map((option) => /* @__PURE__ */ React21__namespace.createElement(
1052
+ SelectItem,
1053
+ {
1054
+ key: option.value,
1055
+ value: option.value,
1056
+ disabled: option.disabled
1057
+ },
1058
+ renderOption ? renderOption(option) : option.label
1059
+ ))
1060
+ ))
1061
+ ));
1062
+ }
1063
+ Select2.displayName = "Select";
1064
+ function Dialog({
1065
+ ...props
1066
+ }) {
1067
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Dialog.Root, { "data-slot": "dialog", ...props });
1068
+ }
1069
+ function DialogPortal({
1070
+ ...props
1071
+ }) {
1072
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Dialog.Portal, { "data-slot": "dialog-portal", ...props });
1073
+ }
1074
+ function DialogClose({
1075
+ ...props
1076
+ }) {
1077
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Dialog.Close, { "data-slot": "dialog-close", ...props });
1078
+ }
1079
+ var DialogOverlay = React21__namespace.forwardRef(({ className, ...props }, ref) => {
1080
+ return /* @__PURE__ */ React21__namespace.createElement(
1081
+ radixUi.Dialog.Overlay,
1082
+ {
1083
+ ref,
1084
+ "data-slot": "dialog-overlay",
1085
+ className: chunkDKLLPKZN_cjs.cn(
1086
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
1087
+ className
1088
+ ),
1089
+ ...props
1090
+ }
1091
+ );
1092
+ });
1093
+ DialogOverlay.displayName = radixUi.Dialog.Overlay.displayName;
1094
+ var DialogContent = React21__namespace.forwardRef(({ className, children, showCloseButton = true, ...props }, ref) => {
1095
+ return /* @__PURE__ */ React21__namespace.createElement(DialogPortal, { "data-slot": "dialog-portal" }, /* @__PURE__ */ React21__namespace.createElement(DialogOverlay, null), /* @__PURE__ */ React21__namespace.createElement(
1096
+ radixUi.Dialog.Content,
1097
+ {
1098
+ ref,
1099
+ "data-slot": "dialog-content",
1100
+ className: chunkDKLLPKZN_cjs.cn(
1101
+ "bg-background 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 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 p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
1102
+ className
1103
+ ),
1104
+ ...props
1105
+ },
1106
+ children,
1107
+ showCloseButton && /* @__PURE__ */ React21__namespace.createElement(
1108
+ radixUi.Dialog.Close,
1109
+ {
1110
+ "data-slot": "dialog-close",
1111
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
1112
+ },
1113
+ /* @__PURE__ */ React21__namespace.createElement(
1114
+ "svg",
1115
+ {
1116
+ className: "size-4",
1117
+ viewBox: "0 0 24 24",
1118
+ fill: "none",
1119
+ stroke: "currentColor",
1120
+ strokeWidth: "2",
1121
+ strokeLinecap: "round",
1122
+ strokeLinejoin: "round"
1123
+ },
1124
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1125
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1126
+ ),
1127
+ /* @__PURE__ */ React21__namespace.createElement("span", { className: "sr-only" }, "Close")
1128
+ )
1129
+ ));
1130
+ });
1131
+ DialogContent.displayName = radixUi.Dialog.Content.displayName;
1132
+ function DialogHeader({ className, ...props }) {
1133
+ return /* @__PURE__ */ React21__namespace.createElement(
1134
+ "div",
1135
+ {
1136
+ "data-slot": "dialog-header",
1137
+ className: chunkDKLLPKZN_cjs.cn("flex flex-col gap-2 text-center sm:text-left", className),
1138
+ ...props
1139
+ }
1140
+ );
1141
+ }
1142
+ function DialogTitle({
1143
+ className,
1144
+ ...props
1145
+ }) {
1146
+ return /* @__PURE__ */ React21__namespace.createElement(
1147
+ radixUi.Dialog.Title,
1148
+ {
1149
+ "data-slot": "dialog-title",
1150
+ className: chunkDKLLPKZN_cjs.cn("text-lg leading-none font-semibold", className),
1151
+ ...props
1152
+ }
1153
+ );
1154
+ }
1155
+
1156
+ // src/components/ui/command.tsx
1157
+ function Command({
1158
+ className,
1159
+ ...props
1160
+ }) {
1161
+ return /* @__PURE__ */ React21__namespace.createElement(
1162
+ cmdk.Command,
1163
+ {
1164
+ "data-slot": "command",
1165
+ className: chunkDKLLPKZN_cjs.cn(
1166
+ "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
1167
+ className
1168
+ ),
1169
+ ...props
1170
+ }
1171
+ );
1172
+ }
1173
+ function CommandInput({
1174
+ className,
1175
+ ...props
1176
+ }) {
1177
+ return /* @__PURE__ */ React21__namespace.createElement(
1178
+ "div",
1179
+ {
1180
+ "data-slot": "command-input-wrapper",
1181
+ className: "flex h-9 items-center gap-2 border-b px-3"
1182
+ },
1183
+ /* @__PURE__ */ React21__namespace.createElement(
1184
+ "svg",
1185
+ {
1186
+ className: "size-4 shrink-0 opacity-50",
1187
+ viewBox: "0 0 24 24",
1188
+ fill: "none",
1189
+ stroke: "currentColor",
1190
+ strokeWidth: "2",
1191
+ strokeLinecap: "round",
1192
+ strokeLinejoin: "round"
1193
+ },
1194
+ /* @__PURE__ */ React21__namespace.createElement("circle", { cx: "11", cy: "11", r: "8" }),
1195
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "m21 21-4.3-4.3" })
1196
+ ),
1197
+ /* @__PURE__ */ React21__namespace.createElement(
1198
+ cmdk.Command.Input,
1199
+ {
1200
+ "data-slot": "command-input",
1201
+ className: chunkDKLLPKZN_cjs.cn(
1202
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
1203
+ className
1204
+ ),
1205
+ ...props
1206
+ }
1207
+ )
1208
+ );
1209
+ }
1210
+ function CommandList({
1211
+ className,
1212
+ ...props
1213
+ }) {
1214
+ return /* @__PURE__ */ React21__namespace.createElement(
1215
+ cmdk.Command.List,
1216
+ {
1217
+ "data-slot": "command-list",
1218
+ className: chunkDKLLPKZN_cjs.cn(
1219
+ "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
1220
+ className
1221
+ ),
1222
+ ...props
1223
+ }
1224
+ );
1225
+ }
1226
+ function CommandEmpty({
1227
+ ...props
1228
+ }) {
1229
+ return /* @__PURE__ */ React21__namespace.createElement(
1230
+ cmdk.Command.Empty,
1231
+ {
1232
+ "data-slot": "command-empty",
1233
+ className: "py-6 text-center text-sm",
1234
+ ...props
1235
+ }
1236
+ );
1237
+ }
1238
+ function CommandGroup({
1239
+ className,
1240
+ ...props
1241
+ }) {
1242
+ return /* @__PURE__ */ React21__namespace.createElement(
1243
+ cmdk.Command.Group,
1244
+ {
1245
+ "data-slot": "command-group",
1246
+ className: chunkDKLLPKZN_cjs.cn(
1247
+ "overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:opacity-70",
1248
+ className
1249
+ ),
1250
+ ...props
1251
+ }
1252
+ );
1253
+ }
1254
+ function Popover({
1255
+ ...props
1256
+ }) {
1257
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Popover.Root, { "data-slot": "popover", ...props });
1258
+ }
1259
+ function PopoverTrigger({
1260
+ ...props
1261
+ }) {
1262
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
1263
+ }
1264
+ function PopoverContent({
1265
+ className,
1266
+ align = "center",
1267
+ sideOffset = 4,
1268
+ ...props
1269
+ }) {
1270
+ return /* @__PURE__ */ React21__namespace.createElement(radixUi.Popover.Portal, null, /* @__PURE__ */ React21__namespace.createElement(
1271
+ radixUi.Popover.Content,
1272
+ {
1273
+ "data-slot": "popover-content",
1274
+ align,
1275
+ sideOffset,
1276
+ className: chunkDKLLPKZN_cjs.cn(
1277
+ "bg-popover text-popover-foreground 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 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
1278
+ className
1279
+ ),
1280
+ ...props
1281
+ }
1282
+ ));
1283
+ }
1284
+
1285
+ // src/inputs/MultiSelect.tsx
1286
+ function ensureResizeObserver() {
1287
+ if (typeof window === "undefined") return;
1288
+ const windowWithResizeObserver = window;
1289
+ if (windowWithResizeObserver.ResizeObserver) return;
1290
+ windowWithResizeObserver.ResizeObserver = class ResizeObserverMock {
1291
+ observe() {
1292
+ }
1293
+ unobserve() {
1294
+ }
1295
+ disconnect() {
1296
+ }
1297
+ };
1298
+ if (typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.scrollIntoView !== "function") {
1299
+ HTMLElement.prototype.scrollIntoView = () => {
1300
+ };
1301
+ }
1302
+ }
1303
+ function optionLabelText(option) {
1304
+ if (typeof option.label === "string") {
1305
+ return option.label;
1306
+ }
1307
+ return String(option.label);
1308
+ }
1309
+ function MultiSelect({
1310
+ name,
1311
+ value = [],
1312
+ onChange,
1313
+ onBlur,
1314
+ onFocus,
1315
+ disabled = false,
1316
+ required = false,
1317
+ error = false,
1318
+ className = "",
1319
+ placeholder = "Select...",
1320
+ searchable = true,
1321
+ clearable = true,
1322
+ loading = false,
1323
+ maxSelections,
1324
+ showSelectAll = false,
1325
+ options = [],
1326
+ optionGroups = [],
1327
+ renderOption,
1328
+ renderValue,
1329
+ ...props
1330
+ }) {
1331
+ const [isOpen, setIsOpen] = React21__namespace.useState(false);
1332
+ const [searchQuery, setSearchQuery] = React21__namespace.useState("");
1333
+ const [focusedIndex, setFocusedIndex] = React21__namespace.useState(-1);
1334
+ const [hasInteracted, setHasInteracted] = React21__namespace.useState(false);
1335
+ const triggerRef = React21__namespace.useRef(null);
1336
+ const dropdownId = `${name}-dropdown`;
1337
+ const searchInputId = `${name}-search`;
1338
+ ensureResizeObserver();
1339
+ const allOptions = React21__namespace.useMemo(() => {
1340
+ if (optionGroups.length > 0) {
1341
+ return optionGroups.flatMap((group) => group.options);
1342
+ }
1343
+ return options;
1344
+ }, [options, optionGroups]);
1345
+ const filteredOptions = React21__namespace.useMemo(() => {
1346
+ if (!searchQuery.trim()) {
1347
+ return allOptions;
1348
+ }
1349
+ const query = searchQuery.toLowerCase();
1350
+ return allOptions.filter(
1351
+ (option) => optionLabelText(option).toLowerCase().includes(query)
1352
+ );
1353
+ }, [allOptions, searchQuery]);
1354
+ const selectedOptions = React21__namespace.useMemo(() => {
1355
+ return allOptions.filter((option) => value.includes(option.value));
1356
+ }, [allOptions, value]);
1357
+ const hasValue = value.length > 0;
1358
+ const isMaxReached = React21__namespace.useMemo(() => {
1359
+ return maxSelections !== void 0 && value.length >= maxSelections;
1360
+ }, [maxSelections, value.length]);
1361
+ const getEnabledOptions = React21__namespace.useCallback(() => {
1362
+ return filteredOptions.filter(
1363
+ (option) => !option.disabled && (!isMaxReached || value.includes(option.value))
1364
+ );
1365
+ }, [filteredOptions, isMaxReached, value]);
1366
+ React21__namespace.useEffect(() => {
1367
+ if (!isOpen) return;
1368
+ if (!searchable) return;
1369
+ const id = window.setTimeout(() => {
1370
+ const searchInput = document.getElementById(
1371
+ searchInputId
1372
+ );
1373
+ searchInput?.focus();
1374
+ }, 0);
1375
+ return () => {
1376
+ window.clearTimeout(id);
1377
+ };
1378
+ }, [isOpen, searchable, searchInputId]);
1379
+ const handleToggleOption = React21__namespace.useCallback(
1380
+ (optionValue) => {
1381
+ const isSelected = value.includes(optionValue);
1382
+ if (isSelected) {
1383
+ onChange(value.filter((entry) => entry !== optionValue));
1384
+ } else if (!isMaxReached) {
1385
+ onChange([...value, optionValue]);
1386
+ }
1387
+ setSearchQuery("");
1388
+ },
1389
+ [isMaxReached, onChange, value]
1390
+ );
1391
+ const handleSelectAll = React21__namespace.useCallback(() => {
1392
+ const enabledOptions = filteredOptions.filter((option) => !option.disabled);
1393
+ onChange(enabledOptions.map((option) => option.value));
1394
+ setSearchQuery("");
1395
+ }, [filteredOptions, onChange]);
1396
+ const handleClearAll = React21__namespace.useCallback(
1397
+ (e) => {
1398
+ e.stopPropagation();
1399
+ onChange([]);
1400
+ setSearchQuery("");
1401
+ setFocusedIndex(-1);
1402
+ },
1403
+ [onChange]
1404
+ );
1405
+ const handleRemoveValue = React21__namespace.useCallback(
1406
+ (optionValue, e) => {
1407
+ e.stopPropagation();
1408
+ onChange(value.filter((entry) => entry !== optionValue));
1409
+ },
1410
+ [onChange, value]
1411
+ );
1412
+ const handleOpenChange = React21__namespace.useCallback(
1413
+ (nextOpen) => {
1414
+ if (disabled) {
1415
+ setIsOpen(false);
1416
+ return;
1417
+ }
1418
+ if (nextOpen) {
1419
+ if (!hasInteracted) {
1420
+ setHasInteracted(true);
1421
+ }
1422
+ setIsOpen(true);
1423
+ onFocus?.();
1424
+ return;
1425
+ }
1426
+ if (isOpen && hasInteracted) {
1427
+ onBlur?.();
1428
+ }
1429
+ setIsOpen(false);
1430
+ setSearchQuery("");
1431
+ setFocusedIndex(-1);
1432
+ },
1433
+ [disabled, hasInteracted, isOpen, onBlur, onFocus]
1434
+ );
1435
+ const handleTriggerBlur = React21__namespace.useCallback(() => {
1436
+ if (!isOpen) {
1437
+ onBlur?.();
1438
+ }
1439
+ }, [isOpen, onBlur]);
1440
+ const handleKeyDown = React21__namespace.useCallback(
1441
+ (event) => {
1442
+ if (disabled) return;
1443
+ const enabledOptions = getEnabledOptions();
1444
+ switch (event.key) {
1445
+ case "ArrowDown": {
1446
+ event.preventDefault();
1447
+ if (!isOpen) {
1448
+ setHasInteracted(true);
1449
+ setIsOpen(true);
1450
+ onFocus?.();
1451
+ if (enabledOptions.length > 0) {
1452
+ setFocusedIndex(filteredOptions.indexOf(enabledOptions[0]));
1453
+ }
1454
+ return;
1455
+ }
1456
+ if (enabledOptions.length === 0) return;
1457
+ const currentOption = filteredOptions[focusedIndex];
1458
+ const currentEnabledIndex = enabledOptions.findIndex(
1459
+ (option) => option === currentOption
1460
+ );
1461
+ const nextEnabledIndex = currentEnabledIndex === -1 ? 0 : (currentEnabledIndex + 1) % enabledOptions.length;
1462
+ setFocusedIndex(filteredOptions.indexOf(enabledOptions[nextEnabledIndex]));
1463
+ break;
1464
+ }
1465
+ case "ArrowUp": {
1466
+ event.preventDefault();
1467
+ if (!isOpen || enabledOptions.length === 0) return;
1468
+ const currentOption = filteredOptions[focusedIndex];
1469
+ const currentEnabledIndex = enabledOptions.findIndex(
1470
+ (option) => option === currentOption
1471
+ );
1472
+ const previousEnabledIndex = currentEnabledIndex === -1 ? enabledOptions.length - 1 : (currentEnabledIndex - 1 + enabledOptions.length) % enabledOptions.length;
1473
+ setFocusedIndex(
1474
+ filteredOptions.indexOf(enabledOptions[previousEnabledIndex])
1475
+ );
1476
+ break;
1477
+ }
1478
+ case "Enter": {
1479
+ event.preventDefault();
1480
+ if (isOpen && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
1481
+ const focusedOption = filteredOptions[focusedIndex];
1482
+ const optionDisabled = focusedOption.disabled || isMaxReached && !value.includes(focusedOption.value);
1483
+ if (!optionDisabled) {
1484
+ handleToggleOption(focusedOption.value);
1485
+ }
1486
+ return;
1487
+ }
1488
+ if (!isOpen) {
1489
+ setHasInteracted(true);
1490
+ setIsOpen(true);
1491
+ onFocus?.();
1492
+ }
1493
+ break;
1494
+ }
1495
+ case "Escape": {
1496
+ if (!isOpen) return;
1497
+ event.preventDefault();
1498
+ setIsOpen(false);
1499
+ setSearchQuery("");
1500
+ setFocusedIndex(-1);
1501
+ break;
1502
+ }
1503
+ case " ": {
1504
+ if (isOpen && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
1505
+ event.preventDefault();
1506
+ const focusedOption = filteredOptions[focusedIndex];
1507
+ const optionDisabled = focusedOption.disabled || isMaxReached && !value.includes(focusedOption.value);
1508
+ if (!optionDisabled) {
1509
+ handleToggleOption(focusedOption.value);
1510
+ }
1511
+ return;
1512
+ }
1513
+ if (!isOpen && !searchable) {
1514
+ event.preventDefault();
1515
+ setHasInteracted(true);
1516
+ setIsOpen(true);
1517
+ onFocus?.();
1518
+ }
1519
+ break;
1520
+ }
1521
+ }
1522
+ },
1523
+ [
1524
+ disabled,
1525
+ filteredOptions,
1526
+ focusedIndex,
1527
+ getEnabledOptions,
1528
+ handleToggleOption,
1529
+ isMaxReached,
1530
+ isOpen,
1531
+ onFocus,
1532
+ searchable,
1533
+ value
1534
+ ]
1535
+ );
1536
+ const combinedClassName = chunkDKLLPKZN_cjs.cn("relative w-full", className);
1537
+ return /* @__PURE__ */ React21__namespace.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React21__namespace.createElement(
1538
+ "select",
1539
+ {
1540
+ name,
1541
+ value,
1542
+ onChange: () => {
1543
+ },
1544
+ disabled,
1545
+ required,
1546
+ "aria-hidden": "true",
1547
+ tabIndex: -1,
1548
+ style: { display: "none" },
1549
+ multiple: true
1550
+ },
1551
+ /* @__PURE__ */ React21__namespace.createElement("option", { value: "" }, "Select..."),
1552
+ allOptions.map((option) => /* @__PURE__ */ React21__namespace.createElement("option", { key: option.value, value: option.value }, optionLabelText(option)))
1553
+ ), /* @__PURE__ */ React21__namespace.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React21__namespace.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
1554
+ "div",
1555
+ {
1556
+ ref: triggerRef,
1557
+ className: chunkDKLLPKZN_cjs.cn(
1558
+ "flex min-h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm",
1559
+ "cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1560
+ !error && hasValue && "ring-2 ring-ring",
1561
+ disabled && "cursor-not-allowed opacity-50 pointer-events-none",
1562
+ error && "border-destructive ring-1 ring-destructive"
1563
+ ),
1564
+ onKeyDown: handleKeyDown,
1565
+ onBlur: handleTriggerBlur,
1566
+ role: "combobox",
1567
+ "aria-expanded": isOpen,
1568
+ "aria-controls": dropdownId,
1569
+ "aria-invalid": error || props["aria-invalid"],
1570
+ "aria-describedby": props["aria-describedby"],
1571
+ "aria-required": required || props["aria-required"],
1572
+ "aria-disabled": disabled,
1573
+ tabIndex: disabled ? -1 : 0
1574
+ },
1575
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex flex-1 items-center overflow-hidden" }, selectedOptions.length > 0 ? /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex flex-wrap gap-1" }, selectedOptions.map((option) => /* @__PURE__ */ React21__namespace.createElement(
1576
+ "span",
1577
+ {
1578
+ key: option.value,
1579
+ className: "inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium"
1580
+ },
1581
+ renderValue ? renderValue(option) : /* @__PURE__ */ React21__namespace.createElement(React21__namespace.Fragment, null, /* @__PURE__ */ React21__namespace.createElement("span", { className: "max-w-40 overflow-hidden text-ellipsis whitespace-nowrap" }, option.label), !disabled && /* @__PURE__ */ React21__namespace.createElement(
1582
+ "button",
1583
+ {
1584
+ type: "button",
1585
+ className: "flex h-3.5 w-3.5 items-center justify-center rounded-sm border-none bg-transparent p-0 text-[0.625rem] transition-opacity hover:opacity-70",
1586
+ onClick: (e) => handleRemoveValue(option.value, e),
1587
+ "aria-label": `Remove ${optionLabelText(option)}`,
1588
+ tabIndex: -1
1589
+ },
1590
+ "\u2715"
1591
+ ))
1592
+ ))) : /* @__PURE__ */ React21__namespace.createElement("span", { className: "relative" }, placeholder)),
1593
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "ml-2 flex items-center gap-1" }, loading && /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-xs" }, "\u23F3"), clearable && value.length > 0 && !disabled && !loading && /* @__PURE__ */ React21__namespace.createElement(
1594
+ "button",
1595
+ {
1596
+ type: "button",
1597
+ className: "flex h-4 w-4 items-center justify-center rounded-sm border-none bg-transparent p-0 text-xs transition-opacity hover:opacity-70",
1598
+ onClick: handleClearAll,
1599
+ "aria-label": "Clear all selections",
1600
+ tabIndex: -1
1601
+ },
1602
+ "\u2715"
1603
+ ), /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-xs leading-none", "aria-hidden": "true" }, isOpen ? "\u25B2" : "\u25BC"))
1604
+ )), isOpen && /* @__PURE__ */ React21__namespace.createElement(
1605
+ PopoverContent,
1606
+ {
1607
+ id: dropdownId,
1608
+ align: "start",
1609
+ sideOffset: 4,
1610
+ className: "w-full min-w-[var(--radix-popover-trigger-width)] p-0",
1611
+ onOpenAutoFocus: (event) => {
1612
+ event.preventDefault();
1613
+ }
1614
+ },
1615
+ /* @__PURE__ */ React21__namespace.createElement(
1616
+ Command,
1617
+ {
1618
+ shouldFilter: false,
1619
+ className: "max-h-80",
1620
+ onKeyDown: handleKeyDown
1621
+ },
1622
+ searchable && /* @__PURE__ */ React21__namespace.createElement(
1623
+ CommandInput,
1624
+ {
1625
+ id: searchInputId,
1626
+ className: chunkDKLLPKZN_cjs.cn(chunkDKLLPKZN_cjs.INPUT_AUTOFILL_RESET_CLASSES),
1627
+ placeholder: "Search...",
1628
+ value: searchQuery,
1629
+ onValueChange: (nextValue) => {
1630
+ setSearchQuery(nextValue);
1631
+ setFocusedIndex(0);
1632
+ },
1633
+ "aria-label": "Search options"
1634
+ }
1635
+ ),
1636
+ showSelectAll && filteredOptions.length > 0 && /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex gap-2 border-b border-input p-2" }, /* @__PURE__ */ React21__namespace.createElement(
1637
+ "button",
1638
+ {
1639
+ type: "button",
1640
+ className: "flex-1 rounded border border-input bg-transparent px-3 py-1.5 text-xs font-medium transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
1641
+ onClick: handleSelectAll,
1642
+ disabled
1643
+ },
1644
+ "Select All"
1645
+ ), value.length > 0 && /* @__PURE__ */ React21__namespace.createElement(
1646
+ "button",
1647
+ {
1648
+ type: "button",
1649
+ className: "flex-1 rounded border border-input bg-transparent px-3 py-1.5 text-xs font-medium transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
1650
+ onClick: handleClearAll,
1651
+ disabled
1652
+ },
1653
+ "Clear All"
1654
+ )),
1655
+ isMaxReached && /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-b border-destructive bg-destructive/80 px-2 py-1 text-xs font-medium text-destructive-foreground" }, "Maximum ", maxSelections, " selection", maxSelections !== 1 ? "s" : "", " ", "reached"),
1656
+ /* @__PURE__ */ React21__namespace.createElement(CommandList, { role: "listbox", "aria-multiselectable": "true" }, /* @__PURE__ */ React21__namespace.createElement(CommandEmpty, null, "No options found"), optionGroups.length > 0 ? optionGroups.map((group, groupIndex) => {
1657
+ const groupOptions = group.options.filter(
1658
+ (option) => filteredOptions.includes(option)
1659
+ );
1660
+ if (groupOptions.length === 0) return null;
1661
+ return /* @__PURE__ */ React21__namespace.createElement(
1662
+ CommandGroup,
1663
+ {
1664
+ key: `${group.label}-${groupIndex}`,
1665
+ heading: group.label
1666
+ },
1667
+ groupOptions.map((option) => {
1668
+ const globalIndex = filteredOptions.indexOf(option);
1669
+ const isSelected = value.includes(option.value);
1670
+ const isFocused = globalIndex === focusedIndex;
1671
+ const optionDisabled = option.disabled || isMaxReached && !isSelected;
1672
+ return /* @__PURE__ */ React21__namespace.createElement(
1673
+ "div",
1674
+ {
1675
+ key: option.value,
1676
+ role: "option",
1677
+ "aria-selected": isSelected,
1678
+ "aria-disabled": optionDisabled,
1679
+ onMouseEnter: () => {
1680
+ setFocusedIndex(globalIndex);
1681
+ },
1682
+ onClick: () => {
1683
+ if (!optionDisabled) {
1684
+ handleToggleOption(option.value);
1685
+ }
1686
+ },
1687
+ className: chunkDKLLPKZN_cjs.cn(
1688
+ "relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent",
1689
+ isFocused && "bg-accent",
1690
+ isSelected && "bg-accent font-medium",
1691
+ optionDisabled && "pointer-events-none opacity-50"
1692
+ )
1693
+ },
1694
+ /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-base leading-none" }, isSelected ? "\u2611" : "\u2610"),
1695
+ /* @__PURE__ */ React21__namespace.createElement("span", { className: "flex-1" }, renderOption ? renderOption(option) : option.label)
1696
+ );
1697
+ })
1698
+ );
1699
+ }) : filteredOptions.map((option, index) => {
1700
+ const isSelected = value.includes(option.value);
1701
+ const isFocused = index === focusedIndex;
1702
+ const optionDisabled = option.disabled || isMaxReached && !isSelected;
1703
+ return /* @__PURE__ */ React21__namespace.createElement(
1704
+ "div",
1705
+ {
1706
+ key: option.value,
1707
+ role: "option",
1708
+ "aria-selected": isSelected,
1709
+ "aria-disabled": optionDisabled,
1710
+ onMouseEnter: () => {
1711
+ setFocusedIndex(index);
1712
+ },
1713
+ onClick: () => {
1714
+ if (!optionDisabled) {
1715
+ handleToggleOption(option.value);
1716
+ }
1717
+ },
1718
+ className: chunkDKLLPKZN_cjs.cn(
1719
+ "relative flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent",
1720
+ isFocused && "bg-accent",
1721
+ isSelected && "bg-accent font-medium",
1722
+ optionDisabled && "pointer-events-none opacity-50"
1723
+ )
1724
+ },
1725
+ /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-base leading-none" }, isSelected ? "\u2611" : "\u2610"),
1726
+ /* @__PURE__ */ React21__namespace.createElement("span", { className: "flex-1" }, renderOption ? renderOption(option) : option.label)
1727
+ );
1728
+ }))
1729
+ )
1730
+ )));
1731
+ }
1732
+ MultiSelect.displayName = "MultiSelect";
1733
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React21__namespace.useLayoutEffect : React21__namespace.useEffect;
1734
+
1735
+ // src/hooks/use-as-ref.ts
1736
+ function useAsRef(props) {
1737
+ const ref = React21__namespace.useRef(props);
1738
+ useIsomorphicLayoutEffect(() => {
1739
+ ref.current = props;
1740
+ });
1741
+ return ref;
1742
+ }
1743
+ function useLazyRef(fn) {
1744
+ const ref = React21__namespace.useRef(null);
1745
+ if (ref.current === null) {
1746
+ ref.current = fn();
1747
+ }
1748
+ return ref;
1749
+ }
1750
+
1751
+ // src/components/ui/file-upload.tsx
1752
+ var ROOT_NAME = "FileUpload";
1753
+ var DROPZONE_NAME = "FileUploadDropzone";
1754
+ var LIST_NAME = "FileUploadList";
1755
+ var ITEM_NAME = "FileUploadItem";
1756
+ var ITEM_PREVIEW_NAME = "FileUploadItemPreview";
1757
+ var ITEM_METADATA_NAME = "FileUploadItemMetadata";
1758
+ var ITEM_DELETE_NAME = "FileUploadItemDelete";
1759
+ function BaseFileIcon({
1760
+ children,
1761
+ className
1762
+ }) {
1763
+ return /* @__PURE__ */ React21__namespace.createElement(
1764
+ "svg",
1765
+ {
1766
+ viewBox: "0 0 24 24",
1767
+ fill: "none",
1768
+ stroke: "currentColor",
1769
+ strokeWidth: "2",
1770
+ strokeLinecap: "round",
1771
+ strokeLinejoin: "round",
1772
+ className: chunkDKLLPKZN_cjs.cn("size-5", className),
1773
+ "aria-hidden": "true"
1774
+ },
1775
+ children
1776
+ );
1777
+ }
1778
+ function FileVideoIcon() {
1779
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("rect", { x: "8", y: "12", width: "6", height: "4", rx: "1" }), /* @__PURE__ */ React21__namespace.createElement("path", { d: "m14 13 3-1.5v5L14 15" }));
1780
+ }
1781
+ function FileAudioIcon() {
1782
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("path", { d: "M10 16a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z" }), /* @__PURE__ */ React21__namespace.createElement("path", { d: "M13 17V11l3-1" }));
1783
+ }
1784
+ function FileTextIcon() {
1785
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("line", { x1: "8", y1: "13", x2: "16", y2: "13" }), /* @__PURE__ */ React21__namespace.createElement("line", { x1: "8", y1: "17", x2: "14", y2: "17" }));
1786
+ }
1787
+ function FileCodeIcon() {
1788
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "11 14 9 16 11 18" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "13 14 15 16 13 18" }));
1789
+ }
1790
+ function FileArchiveIcon() {
1791
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("rect", { x: "9", y: "11", width: "6", height: "2" }), /* @__PURE__ */ React21__namespace.createElement("path", { d: "M12 13v5" }));
1792
+ }
1793
+ function FileCogIcon() {
1794
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React21__namespace.createElement("circle", { cx: "12", cy: "16", r: "2" }), /* @__PURE__ */ React21__namespace.createElement("path", { d: "m12 12 .4.9m2.7 1.1 .9.4m-.9 2.7-.9.4m-2.7 1.1-.4.9m-2.3-.9-.4-.9m-2.7-1.1-.9-.4m.9-2.7.9-.4m2.7-1.1.4-.9" }));
1795
+ }
1796
+ function FileIcon() {
1797
+ return /* @__PURE__ */ React21__namespace.createElement(BaseFileIcon, null, /* @__PURE__ */ React21__namespace.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "14 2 14 8 20 8" }));
1798
+ }
1799
+ function formatBytes(bytes) {
1800
+ if (bytes === 0) return "0 B";
1801
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
1802
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
1803
+ return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)} ${sizes[i]}`;
1804
+ }
1805
+ function getFileIcon(file) {
1806
+ const type = file.type;
1807
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
1808
+ if (type.startsWith("video/")) {
1809
+ return /* @__PURE__ */ React21__namespace.createElement(FileVideoIcon, null);
1810
+ }
1811
+ if (type.startsWith("audio/")) {
1812
+ return /* @__PURE__ */ React21__namespace.createElement(FileAudioIcon, null);
1813
+ }
1814
+ if (type.startsWith("text/") || ["txt", "md", "rtf", "pdf"].includes(extension)) {
1815
+ return /* @__PURE__ */ React21__namespace.createElement(FileTextIcon, null);
1816
+ }
1817
+ if ([
1818
+ "html",
1819
+ "css",
1820
+ "js",
1821
+ "jsx",
1822
+ "ts",
1823
+ "tsx",
1824
+ "json",
1825
+ "xml",
1826
+ "php",
1827
+ "py",
1828
+ "rb",
1829
+ "java",
1830
+ "c",
1831
+ "cpp",
1832
+ "cs"
1833
+ ].includes(extension)) {
1834
+ return /* @__PURE__ */ React21__namespace.createElement(FileCodeIcon, null);
1835
+ }
1836
+ if (["zip", "rar", "7z", "tar", "gz", "bz2"].includes(extension)) {
1837
+ return /* @__PURE__ */ React21__namespace.createElement(FileArchiveIcon, null);
1838
+ }
1839
+ if (["exe", "msi", "app", "apk", "deb", "rpm"].includes(extension) || type.startsWith("application/")) {
1840
+ return /* @__PURE__ */ React21__namespace.createElement(FileCogIcon, null);
1841
+ }
1842
+ return /* @__PURE__ */ React21__namespace.createElement(FileIcon, null);
1843
+ }
1844
+ var StoreContext = React21__namespace.createContext(null);
1845
+ function useStoreContext(consumerName) {
1846
+ const context = React21__namespace.useContext(StoreContext);
1847
+ if (!context) {
1848
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
1849
+ }
1850
+ return context;
1851
+ }
1852
+ function useStore(selector) {
1853
+ const store = useStoreContext("useStore");
1854
+ const lastValueRef = useLazyRef(
1855
+ () => null
1856
+ );
1857
+ const getSnapshot = React21__namespace.useCallback(() => {
1858
+ const state = store.getState();
1859
+ const prevValue = lastValueRef.current;
1860
+ if (prevValue && prevValue.state === state) {
1861
+ return prevValue.value;
1862
+ }
1863
+ const nextValue = selector(state);
1864
+ lastValueRef.current = { value: nextValue, state };
1865
+ return nextValue;
1866
+ }, [store, selector, lastValueRef]);
1867
+ return React21__namespace.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
1868
+ }
1869
+ var FileUploadContext = React21__namespace.createContext(
1870
+ null
1871
+ );
1872
+ function useFileUploadContext(consumerName) {
1873
+ const context = React21__namespace.useContext(FileUploadContext);
1874
+ if (!context) {
1875
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
1876
+ }
1877
+ return context;
1878
+ }
1879
+ function FileUpload(props) {
1880
+ const {
1881
+ value,
1882
+ defaultValue,
1883
+ onValueChange,
1884
+ onAccept,
1885
+ onFileAccept,
1886
+ onFileReject,
1887
+ onFileValidate,
1888
+ onUpload,
1889
+ accept,
1890
+ maxFiles,
1891
+ maxSize,
1892
+ dir: dirProp,
1893
+ label,
1894
+ name,
1895
+ asChild,
1896
+ disabled = false,
1897
+ invalid = false,
1898
+ multiple = false,
1899
+ required = false,
1900
+ inputProps,
1901
+ children,
1902
+ className,
1903
+ ...rootProps
1904
+ } = props;
1905
+ const inputId = React21__namespace.useId();
1906
+ const dropzoneId = React21__namespace.useId();
1907
+ const listId = React21__namespace.useId();
1908
+ const labelId = React21__namespace.useId();
1909
+ const dir = reactDirection.useDirection(dirProp);
1910
+ const listeners = useLazyRef(() => /* @__PURE__ */ new Set()).current;
1911
+ const files = useLazyRef(() => /* @__PURE__ */ new Map()).current;
1912
+ const urlCache = useLazyRef(() => /* @__PURE__ */ new WeakMap()).current;
1913
+ const inputRef = React21__namespace.useRef(null);
1914
+ const isControlled = value !== void 0;
1915
+ const propsRef = useAsRef({
1916
+ onValueChange,
1917
+ onAccept,
1918
+ onFileAccept,
1919
+ onFileReject,
1920
+ onFileValidate,
1921
+ onUpload
1922
+ });
1923
+ const store = React21__namespace.useMemo(() => {
1924
+ let state = {
1925
+ files,
1926
+ dragOver: false,
1927
+ invalid
1928
+ };
1929
+ function reducer(state2, action) {
1930
+ switch (action.type) {
1931
+ case "ADD_FILES": {
1932
+ for (const file of action.files) {
1933
+ files.set(file, {
1934
+ file,
1935
+ progress: 0,
1936
+ status: "idle"
1937
+ });
1938
+ }
1939
+ if (propsRef.current.onValueChange) {
1940
+ const fileList = Array.from(files.values()).map(
1941
+ (fileState) => fileState.file
1942
+ );
1943
+ propsRef.current.onValueChange(fileList);
1944
+ }
1945
+ return { ...state2, files };
1946
+ }
1947
+ case "SET_FILES": {
1948
+ const newFileSet = new Set(action.files);
1949
+ for (const existingFile of files.keys()) {
1950
+ if (!newFileSet.has(existingFile)) {
1951
+ files.delete(existingFile);
1952
+ }
1953
+ }
1954
+ for (const file of action.files) {
1955
+ const existingState = files.get(file);
1956
+ if (!existingState) {
1957
+ files.set(file, {
1958
+ file,
1959
+ progress: 0,
1960
+ status: "idle"
1961
+ });
1962
+ }
1963
+ }
1964
+ return { ...state2, files };
1965
+ }
1966
+ case "SET_PROGRESS": {
1967
+ const fileState = files.get(action.file);
1968
+ if (fileState) {
1969
+ files.set(action.file, {
1970
+ ...fileState,
1971
+ progress: action.progress,
1972
+ status: "uploading"
1973
+ });
1974
+ }
1975
+ return { ...state2, files };
1976
+ }
1977
+ case "SET_SUCCESS": {
1978
+ const fileState = files.get(action.file);
1979
+ if (fileState) {
1980
+ files.set(action.file, {
1981
+ ...fileState,
1982
+ progress: 100,
1983
+ status: "success"
1984
+ });
1985
+ }
1986
+ return { ...state2, files };
1987
+ }
1988
+ case "SET_ERROR": {
1989
+ const fileState = files.get(action.file);
1990
+ if (fileState) {
1991
+ files.set(action.file, {
1992
+ ...fileState,
1993
+ error: action.error,
1994
+ status: "error"
1995
+ });
1996
+ }
1997
+ return { ...state2, files };
1998
+ }
1999
+ case "REMOVE_FILE": {
2000
+ const cachedUrl = urlCache.get(action.file);
2001
+ if (cachedUrl) {
2002
+ URL.revokeObjectURL(cachedUrl);
2003
+ urlCache.delete(action.file);
2004
+ }
2005
+ files.delete(action.file);
2006
+ if (propsRef.current.onValueChange) {
2007
+ const fileList = Array.from(files.values()).map(
2008
+ (fileState) => fileState.file
2009
+ );
2010
+ propsRef.current.onValueChange(fileList);
2011
+ }
2012
+ return { ...state2, files };
2013
+ }
2014
+ case "SET_DRAG_OVER": {
2015
+ return { ...state2, dragOver: action.dragOver };
2016
+ }
2017
+ case "SET_INVALID": {
2018
+ return { ...state2, invalid: action.invalid };
2019
+ }
2020
+ case "CLEAR": {
2021
+ for (const file of files.keys()) {
2022
+ const cachedUrl = urlCache.get(file);
2023
+ if (cachedUrl) {
2024
+ URL.revokeObjectURL(cachedUrl);
2025
+ urlCache.delete(file);
2026
+ }
2027
+ }
2028
+ files.clear();
2029
+ if (propsRef.current.onValueChange) {
2030
+ propsRef.current.onValueChange([]);
2031
+ }
2032
+ return { ...state2, files, invalid: false };
2033
+ }
2034
+ default:
2035
+ return state2;
2036
+ }
2037
+ }
2038
+ return {
2039
+ getState: () => state,
2040
+ dispatch: (action) => {
2041
+ state = reducer(state, action);
2042
+ for (const listener of listeners) {
2043
+ listener();
2044
+ }
2045
+ },
2046
+ subscribe: (listener) => {
2047
+ listeners.add(listener);
2048
+ return () => listeners.delete(listener);
2049
+ }
2050
+ };
2051
+ }, [listeners, files, invalid, propsRef, urlCache]);
2052
+ const acceptTypes = React21__namespace.useMemo(
2053
+ () => accept?.split(",").map((t) => t.trim()) ?? null,
2054
+ [accept]
2055
+ );
2056
+ const onProgress = useLazyRef(() => {
2057
+ let frame = 0;
2058
+ return (file, progress) => {
2059
+ if (frame) return;
2060
+ frame = requestAnimationFrame(() => {
2061
+ frame = 0;
2062
+ store.dispatch({
2063
+ type: "SET_PROGRESS",
2064
+ file,
2065
+ progress: Math.min(Math.max(0, progress), 100)
2066
+ });
2067
+ });
2068
+ };
2069
+ }).current;
2070
+ React21__namespace.useEffect(() => {
2071
+ if (isControlled) {
2072
+ store.dispatch({ type: "SET_FILES", files: value });
2073
+ } else if (defaultValue && defaultValue.length > 0 && !store.getState().files.size) {
2074
+ store.dispatch({ type: "SET_FILES", files: defaultValue });
2075
+ }
2076
+ }, [value, defaultValue, isControlled, store]);
2077
+ React21__namespace.useEffect(() => {
2078
+ return () => {
2079
+ for (const file of files.keys()) {
2080
+ const cachedUrl = urlCache.get(file);
2081
+ if (cachedUrl) {
2082
+ URL.revokeObjectURL(cachedUrl);
2083
+ }
2084
+ }
2085
+ };
2086
+ }, [files, urlCache]);
2087
+ const onFilesUpload = React21__namespace.useCallback(
2088
+ async (files2) => {
2089
+ try {
2090
+ for (const file of files2) {
2091
+ store.dispatch({ type: "SET_PROGRESS", file, progress: 0 });
2092
+ }
2093
+ if (propsRef.current.onUpload) {
2094
+ await propsRef.current.onUpload(files2, {
2095
+ onProgress,
2096
+ onSuccess: (file) => {
2097
+ store.dispatch({ type: "SET_SUCCESS", file });
2098
+ },
2099
+ onError: (file, error) => {
2100
+ store.dispatch({
2101
+ type: "SET_ERROR",
2102
+ file,
2103
+ error: error.message ?? "Upload failed"
2104
+ });
2105
+ }
2106
+ });
2107
+ } else {
2108
+ for (const file of files2) {
2109
+ store.dispatch({ type: "SET_SUCCESS", file });
2110
+ }
2111
+ }
2112
+ } catch (error) {
2113
+ const errorMessage = error instanceof Error ? error.message : "Upload failed";
2114
+ for (const file of files2) {
2115
+ store.dispatch({
2116
+ type: "SET_ERROR",
2117
+ file,
2118
+ error: errorMessage
2119
+ });
2120
+ }
2121
+ }
2122
+ },
2123
+ [store, propsRef, onProgress]
2124
+ );
2125
+ const onFilesChange = React21__namespace.useCallback(
2126
+ (originalFiles) => {
2127
+ if (disabled) return;
2128
+ let filesToProcess = [...originalFiles];
2129
+ let invalid2 = false;
2130
+ if (maxFiles) {
2131
+ const currentCount = store.getState().files.size;
2132
+ const remainingSlotCount = Math.max(0, maxFiles - currentCount);
2133
+ if (remainingSlotCount < filesToProcess.length) {
2134
+ const rejectedFiles2 = filesToProcess.slice(remainingSlotCount);
2135
+ invalid2 = true;
2136
+ filesToProcess = filesToProcess.slice(0, remainingSlotCount);
2137
+ for (const file of rejectedFiles2) {
2138
+ let rejectionMessage = `Maximum ${maxFiles} files allowed`;
2139
+ if (propsRef.current.onFileValidate) {
2140
+ const validationMessage = propsRef.current.onFileValidate(file);
2141
+ if (validationMessage) {
2142
+ rejectionMessage = validationMessage;
2143
+ }
2144
+ }
2145
+ propsRef.current.onFileReject?.(file, rejectionMessage);
2146
+ }
2147
+ }
2148
+ }
2149
+ const acceptedFiles = [];
2150
+ for (const file of filesToProcess) {
2151
+ let rejected = false;
2152
+ let rejectionMessage = "";
2153
+ if (propsRef.current.onFileValidate) {
2154
+ const validationMessage = propsRef.current.onFileValidate(file);
2155
+ if (validationMessage) {
2156
+ rejectionMessage = validationMessage;
2157
+ propsRef.current.onFileReject?.(file, rejectionMessage);
2158
+ rejected = true;
2159
+ invalid2 = true;
2160
+ continue;
2161
+ }
2162
+ }
2163
+ if (acceptTypes) {
2164
+ const fileType = file.type;
2165
+ const fileExtension = `.${file.name.split(".").pop()}`;
2166
+ if (!acceptTypes.some(
2167
+ (type) => type === fileType || type === fileExtension || type.includes("/*") && fileType.startsWith(type.replace("/*", "/"))
2168
+ )) {
2169
+ rejectionMessage = "File type not accepted";
2170
+ propsRef.current.onFileReject?.(file, rejectionMessage);
2171
+ rejected = true;
2172
+ invalid2 = true;
2173
+ }
2174
+ }
2175
+ if (maxSize && file.size > maxSize) {
2176
+ rejectionMessage = "File too large";
2177
+ propsRef.current.onFileReject?.(file, rejectionMessage);
2178
+ rejected = true;
2179
+ invalid2 = true;
2180
+ }
2181
+ if (!rejected) {
2182
+ acceptedFiles.push(file);
2183
+ }
2184
+ }
2185
+ if (invalid2) {
2186
+ store.dispatch({ type: "SET_INVALID", invalid: invalid2 });
2187
+ setTimeout(() => {
2188
+ store.dispatch({ type: "SET_INVALID", invalid: false });
2189
+ }, 2e3);
2190
+ }
2191
+ if (acceptedFiles.length > 0) {
2192
+ store.dispatch({ type: "ADD_FILES", files: acceptedFiles });
2193
+ if (isControlled && propsRef.current.onValueChange) {
2194
+ const currentFiles = Array.from(store.getState().files.values()).map(
2195
+ (f) => f.file
2196
+ );
2197
+ propsRef.current.onValueChange([...currentFiles]);
2198
+ }
2199
+ if (propsRef.current.onAccept) {
2200
+ propsRef.current.onAccept(acceptedFiles);
2201
+ }
2202
+ for (const file of acceptedFiles) {
2203
+ propsRef.current.onFileAccept?.(file);
2204
+ }
2205
+ if (propsRef.current.onUpload) {
2206
+ requestAnimationFrame(() => {
2207
+ onFilesUpload(acceptedFiles);
2208
+ });
2209
+ }
2210
+ }
2211
+ },
2212
+ [
2213
+ store,
2214
+ isControlled,
2215
+ propsRef,
2216
+ onFilesUpload,
2217
+ maxFiles,
2218
+ acceptTypes,
2219
+ maxSize,
2220
+ disabled
2221
+ ]
2222
+ );
2223
+ const onInputChange = React21__namespace.useCallback(
2224
+ (event) => {
2225
+ const files2 = Array.from(event.target.files ?? []);
2226
+ onFilesChange(files2);
2227
+ event.target.value = "";
2228
+ },
2229
+ [onFilesChange]
2230
+ );
2231
+ const contextValue = React21__namespace.useMemo(
2232
+ () => ({
2233
+ dropzoneId,
2234
+ inputId,
2235
+ listId,
2236
+ labelId,
2237
+ dir,
2238
+ disabled,
2239
+ inputRef,
2240
+ urlCache
2241
+ }),
2242
+ [dropzoneId, inputId, listId, labelId, dir, disabled, urlCache]
2243
+ );
2244
+ const RootPrimitive = asChild ? reactSlot.Slot : "div";
2245
+ const inputAriaDescribedBy = [
2246
+ contextValue.dropzoneId,
2247
+ inputProps?.["aria-describedby"]
2248
+ ].filter(Boolean).join(" ").trim();
2249
+ return /* @__PURE__ */ React21__namespace.createElement(StoreContext.Provider, { value: store }, /* @__PURE__ */ React21__namespace.createElement(FileUploadContext.Provider, { value: contextValue }, /* @__PURE__ */ React21__namespace.createElement(
2250
+ RootPrimitive,
2251
+ {
2252
+ "data-disabled": disabled ? "" : void 0,
2253
+ "data-slot": "file-upload",
2254
+ dir,
2255
+ ...rootProps,
2256
+ className: chunkDKLLPKZN_cjs.cn("relative flex flex-col gap-2", className)
2257
+ },
2258
+ children,
2259
+ /* @__PURE__ */ React21__namespace.createElement(
2260
+ "input",
2261
+ {
2262
+ type: "file",
2263
+ id: inputId,
2264
+ "aria-labelledby": inputProps?.["aria-labelledby"] ? `${labelId} ${inputProps["aria-labelledby"]}` : labelId,
2265
+ "aria-describedby": inputAriaDescribedBy || void 0,
2266
+ ref: inputRef,
2267
+ tabIndex: -1,
2268
+ accept,
2269
+ name,
2270
+ className: "sr-only",
2271
+ disabled,
2272
+ multiple,
2273
+ required,
2274
+ onChange: onInputChange,
2275
+ ...inputProps
2276
+ }
2277
+ ),
2278
+ /* @__PURE__ */ React21__namespace.createElement("div", { id: labelId, className: "sr-only" }, label ?? "File upload")
2279
+ )));
2280
+ }
2281
+ function FileUploadDropzone(props) {
2282
+ const {
2283
+ asChild,
2284
+ className,
2285
+ onClick: onClickProp,
2286
+ onDragOver: onDragOverProp,
2287
+ onDragEnter: onDragEnterProp,
2288
+ onDragLeave: onDragLeaveProp,
2289
+ onDrop: onDropProp,
2290
+ onPaste: onPasteProp,
2291
+ onKeyDown: onKeyDownProp,
2292
+ ...dropzoneProps
2293
+ } = props;
2294
+ const context = useFileUploadContext(DROPZONE_NAME);
2295
+ const store = useStoreContext(DROPZONE_NAME);
2296
+ const dragOver = useStore((state) => state.dragOver);
2297
+ const invalid = useStore((state) => state.invalid);
2298
+ const propsRef = useAsRef({
2299
+ onClick: onClickProp,
2300
+ onDragOver: onDragOverProp,
2301
+ onDragEnter: onDragEnterProp,
2302
+ onDragLeave: onDragLeaveProp,
2303
+ onDrop: onDropProp,
2304
+ onPaste: onPasteProp,
2305
+ onKeyDown: onKeyDownProp
2306
+ });
2307
+ const onClick = React21__namespace.useCallback(
2308
+ (event) => {
2309
+ propsRef.current.onClick?.(event);
2310
+ if (event.defaultPrevented) return;
2311
+ const target = event.target;
2312
+ const isFromTrigger = target instanceof HTMLElement && target.closest('[data-slot="file-upload-trigger"]');
2313
+ if (!isFromTrigger) {
2314
+ context.inputRef.current?.click();
2315
+ }
2316
+ },
2317
+ [context.inputRef, propsRef]
2318
+ );
2319
+ const onDragOver = React21__namespace.useCallback(
2320
+ (event) => {
2321
+ propsRef.current.onDragOver?.(event);
2322
+ if (event.defaultPrevented) return;
2323
+ event.preventDefault();
2324
+ store.dispatch({ type: "SET_DRAG_OVER", dragOver: true });
2325
+ },
2326
+ [store, propsRef]
2327
+ );
2328
+ const onDragEnter = React21__namespace.useCallback(
2329
+ (event) => {
2330
+ propsRef.current.onDragEnter?.(event);
2331
+ if (event.defaultPrevented) return;
2332
+ event.preventDefault();
2333
+ store.dispatch({ type: "SET_DRAG_OVER", dragOver: true });
2334
+ },
2335
+ [store, propsRef]
2336
+ );
2337
+ const onDragLeave = React21__namespace.useCallback(
2338
+ (event) => {
2339
+ propsRef.current.onDragLeave?.(event);
2340
+ if (event.defaultPrevented) return;
2341
+ const relatedTarget = event.relatedTarget;
2342
+ if (relatedTarget && relatedTarget instanceof Node && event.currentTarget.contains(relatedTarget)) {
2343
+ return;
2344
+ }
2345
+ event.preventDefault();
2346
+ store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
2347
+ },
2348
+ [store, propsRef]
2349
+ );
2350
+ const onDrop = React21__namespace.useCallback(
2351
+ (event) => {
2352
+ propsRef.current.onDrop?.(event);
2353
+ if (event.defaultPrevented) return;
2354
+ if (context.disabled) return;
2355
+ event.preventDefault();
2356
+ store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
2357
+ const files = Array.from(event.dataTransfer.files);
2358
+ const inputElement = context.inputRef.current;
2359
+ if (!inputElement) return;
2360
+ if (typeof DataTransfer === "undefined") return;
2361
+ const dataTransfer = new DataTransfer();
2362
+ for (const file of files) {
2363
+ dataTransfer.items.add(file);
2364
+ }
2365
+ inputElement.files = dataTransfer.files;
2366
+ inputElement.dispatchEvent(new Event("change", { bubbles: true }));
2367
+ },
2368
+ [store, context.inputRef, propsRef]
2369
+ );
2370
+ const onPaste = React21__namespace.useCallback(
2371
+ (event) => {
2372
+ propsRef.current.onPaste?.(event);
2373
+ if (event.defaultPrevented) return;
2374
+ if (context.disabled) return;
2375
+ event.preventDefault();
2376
+ store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
2377
+ const items = event.clipboardData?.items;
2378
+ if (!items) return;
2379
+ const files = [];
2380
+ for (let i = 0; i < items.length; i++) {
2381
+ const item = items[i];
2382
+ if (item?.kind === "file") {
2383
+ const file = item.getAsFile();
2384
+ if (file) {
2385
+ files.push(file);
2386
+ }
2387
+ }
2388
+ }
2389
+ if (files.length === 0) return;
2390
+ const inputElement = context.inputRef.current;
2391
+ if (!inputElement) return;
2392
+ if (typeof DataTransfer === "undefined") return;
2393
+ const dataTransfer = new DataTransfer();
2394
+ for (const file of files) {
2395
+ dataTransfer.items.add(file);
2396
+ }
2397
+ inputElement.files = dataTransfer.files;
2398
+ inputElement.dispatchEvent(new Event("change", { bubbles: true }));
2399
+ },
2400
+ [store, context.inputRef, propsRef]
2401
+ );
2402
+ const onKeyDown = React21__namespace.useCallback(
2403
+ (event) => {
2404
+ propsRef.current.onKeyDown?.(event);
2405
+ if (!event.defaultPrevented && (event.key === "Enter" || event.key === " ")) {
2406
+ event.preventDefault();
2407
+ context.inputRef.current?.click();
2408
+ }
2409
+ },
2410
+ [context.inputRef, propsRef]
2411
+ );
2412
+ const DropzonePrimitive = asChild ? reactSlot.Slot : "div";
2413
+ return /* @__PURE__ */ React21__namespace.createElement(
2414
+ DropzonePrimitive,
2415
+ {
2416
+ role: "region",
2417
+ id: context.dropzoneId,
2418
+ "aria-controls": `${context.inputId} ${context.listId}`,
2419
+ "aria-disabled": context.disabled,
2420
+ "aria-invalid": invalid,
2421
+ "data-disabled": context.disabled ? "" : void 0,
2422
+ "data-dragging": dragOver ? "" : void 0,
2423
+ "data-invalid": invalid ? "" : void 0,
2424
+ "data-slot": "file-upload-dropzone",
2425
+ dir: context.dir,
2426
+ tabIndex: context.disabled ? -1 : 0,
2427
+ ...dropzoneProps,
2428
+ className: chunkDKLLPKZN_cjs.cn(
2429
+ "relative flex select-none flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed p-6 outline-none transition-colors hover:bg-accent/30 focus-visible:border-ring/50 data-disabled:pointer-events-none data-dragging:border-primary/30 data-dragging:bg-accent/30 data-invalid:ring-destructive/20",
2430
+ className
2431
+ ),
2432
+ onClick,
2433
+ onDragEnter,
2434
+ onDragLeave,
2435
+ onDragOver,
2436
+ onDrop,
2437
+ onKeyDown,
2438
+ onPaste
2439
+ }
2440
+ );
2441
+ }
2442
+ function FileUploadList(props) {
2443
+ const {
2444
+ className,
2445
+ orientation = "vertical",
2446
+ asChild,
2447
+ forceMount,
2448
+ ...listProps
2449
+ } = props;
2450
+ const context = useFileUploadContext(LIST_NAME);
2451
+ const fileCount = useStore((state) => state.files.size);
2452
+ const shouldRender = forceMount || fileCount > 0;
2453
+ if (!shouldRender) return null;
2454
+ const ListPrimitive = asChild ? reactSlot.Slot : "div";
2455
+ return /* @__PURE__ */ React21__namespace.createElement(
2456
+ ListPrimitive,
2457
+ {
2458
+ role: "list",
2459
+ id: context.listId,
2460
+ "aria-orientation": orientation,
2461
+ "data-orientation": orientation,
2462
+ "data-slot": "file-upload-list",
2463
+ "data-state": shouldRender ? "active" : "inactive",
2464
+ dir: context.dir,
2465
+ ...listProps,
2466
+ className: chunkDKLLPKZN_cjs.cn(
2467
+ "data-[state=inactive]:fade-out-0 data-[state=active]:fade-in-0 data-[state=inactive]:slide-out-to-top-2 data-[state=active]:slide-in-from-top-2 flex flex-col gap-2 data-[state=active]:animate-in data-[state=inactive]:animate-out",
2468
+ orientation === "horizontal" && "flex-row overflow-x-auto p-1.5",
2469
+ className
2470
+ )
2471
+ }
2472
+ );
2473
+ }
2474
+ var FileUploadItemContext = React21__namespace.createContext(null);
2475
+ function useFileUploadItemContext(consumerName) {
2476
+ const context = React21__namespace.useContext(FileUploadItemContext);
2477
+ if (!context) {
2478
+ throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME}\``);
2479
+ }
2480
+ return context;
2481
+ }
2482
+ function FileUploadItem(props) {
2483
+ const { value, asChild, className, ...itemProps } = props;
2484
+ const id = React21__namespace.useId();
2485
+ const statusId = `${id}-status`;
2486
+ const nameId = `${id}-name`;
2487
+ const sizeId = `${id}-size`;
2488
+ const messageId = `${id}-message`;
2489
+ const context = useFileUploadContext(ITEM_NAME);
2490
+ const fileState = useStore((state) => state.files.get(value));
2491
+ const fileCount = useStore((state) => state.files.size);
2492
+ const fileIndex = useStore((state) => {
2493
+ const files = Array.from(state.files.keys());
2494
+ return files.indexOf(value) + 1;
2495
+ });
2496
+ const itemContext = React21__namespace.useMemo(
2497
+ () => ({
2498
+ id,
2499
+ fileState,
2500
+ nameId,
2501
+ sizeId,
2502
+ statusId,
2503
+ messageId
2504
+ }),
2505
+ [id, fileState, statusId, nameId, sizeId, messageId]
2506
+ );
2507
+ if (!fileState) return null;
2508
+ const statusText = fileState.error ? `Error: ${fileState.error}` : fileState.status === "uploading" ? `Uploading: ${fileState.progress}% complete` : fileState.status === "success" ? "Upload complete" : "Ready to upload";
2509
+ const ItemPrimitive = asChild ? reactSlot.Slot : "div";
2510
+ return /* @__PURE__ */ React21__namespace.createElement(FileUploadItemContext.Provider, { value: itemContext }, /* @__PURE__ */ React21__namespace.createElement(
2511
+ ItemPrimitive,
2512
+ {
2513
+ role: "listitem",
2514
+ id,
2515
+ "aria-setsize": fileCount,
2516
+ "aria-posinset": fileIndex,
2517
+ "aria-describedby": `${nameId} ${sizeId} ${statusId} ${fileState.error ? messageId : ""}`,
2518
+ "aria-labelledby": nameId,
2519
+ "data-slot": "file-upload-item",
2520
+ dir: context.dir,
2521
+ ...itemProps,
2522
+ className: chunkDKLLPKZN_cjs.cn(
2523
+ "relative flex items-center gap-2.5 rounded-md border p-3",
2524
+ className
2525
+ )
2526
+ },
2527
+ props.children,
2528
+ /* @__PURE__ */ React21__namespace.createElement("span", { id: statusId, className: "sr-only" }, statusText)
2529
+ ));
2530
+ }
2531
+ function FileUploadItemPreview(props) {
2532
+ const { render, asChild, children, className, ...previewProps } = props;
2533
+ const itemContext = useFileUploadItemContext(ITEM_PREVIEW_NAME);
2534
+ const context = useFileUploadContext(ITEM_PREVIEW_NAME);
2535
+ const getDefaultRender = React21__namespace.useCallback(
2536
+ (file) => {
2537
+ if (itemContext.fileState?.file.type.startsWith("image/")) {
2538
+ let url = context.urlCache.get(file);
2539
+ if (!url) {
2540
+ url = URL.createObjectURL(file);
2541
+ context.urlCache.set(file, url);
2542
+ }
2543
+ return (
2544
+ // biome-ignore lint/performance/noImgElement: dynamic file URLs from user uploads don't work well with Next.js Image optimization
2545
+ /* @__PURE__ */ React21__namespace.createElement("img", { src: url, alt: file.name, className: "size-full object-cover" })
2546
+ );
2547
+ }
2548
+ return getFileIcon(file);
2549
+ },
2550
+ [itemContext.fileState?.file.type, context.urlCache]
2551
+ );
2552
+ const onPreviewRender = React21__namespace.useCallback(
2553
+ (file) => {
2554
+ if (render) {
2555
+ return render(file, () => getDefaultRender(file));
2556
+ }
2557
+ return getDefaultRender(file);
2558
+ },
2559
+ [render, getDefaultRender]
2560
+ );
2561
+ if (!itemContext.fileState) return null;
2562
+ const ItemPreviewPrimitive = asChild ? reactSlot.Slot : "div";
2563
+ return /* @__PURE__ */ React21__namespace.createElement(
2564
+ ItemPreviewPrimitive,
2565
+ {
2566
+ "aria-labelledby": itemContext.nameId,
2567
+ "data-slot": "file-upload-preview",
2568
+ ...previewProps,
2569
+ className: chunkDKLLPKZN_cjs.cn(
2570
+ "relative flex size-10 shrink-0 items-center justify-center overflow-hidden rounded border bg-accent/50 [&>svg]:size-10",
2571
+ className
2572
+ )
2573
+ },
2574
+ onPreviewRender(itemContext.fileState.file),
2575
+ children
2576
+ );
2577
+ }
2578
+ function FileUploadItemMetadata(props) {
2579
+ const {
2580
+ asChild,
2581
+ size = "default",
2582
+ children,
2583
+ className,
2584
+ ...metadataProps
2585
+ } = props;
2586
+ const context = useFileUploadContext(ITEM_METADATA_NAME);
2587
+ const itemContext = useFileUploadItemContext(ITEM_METADATA_NAME);
2588
+ if (!itemContext.fileState) return null;
2589
+ const ItemMetadataPrimitive = asChild ? reactSlot.Slot : "div";
2590
+ return /* @__PURE__ */ React21__namespace.createElement(
2591
+ ItemMetadataPrimitive,
2592
+ {
2593
+ "data-slot": "file-upload-metadata",
2594
+ dir: context.dir,
2595
+ ...metadataProps,
2596
+ className: chunkDKLLPKZN_cjs.cn("flex min-w-0 flex-1 flex-col", className)
2597
+ },
2598
+ children ?? /* @__PURE__ */ React21__namespace.createElement(React21__namespace.Fragment, null, /* @__PURE__ */ React21__namespace.createElement(
2599
+ "span",
2600
+ {
2601
+ id: itemContext.nameId,
2602
+ className: chunkDKLLPKZN_cjs.cn(
2603
+ "truncate font-medium text-sm",
2604
+ size === "sm" && "font-normal text-[13px] leading-snug"
2605
+ )
2606
+ },
2607
+ itemContext.fileState.file.name
2608
+ ), /* @__PURE__ */ React21__namespace.createElement(
2609
+ "span",
2610
+ {
2611
+ id: itemContext.sizeId,
2612
+ className: chunkDKLLPKZN_cjs.cn(
2613
+ "truncate text-xs opacity-70",
2614
+ size === "sm" && "text-[11px] leading-snug"
2615
+ )
2616
+ },
2617
+ formatBytes(itemContext.fileState.file.size)
2618
+ ), itemContext.fileState.error && /* @__PURE__ */ React21__namespace.createElement(
2619
+ "span",
2620
+ {
2621
+ id: itemContext.messageId,
2622
+ className: "text-destructive text-xs"
2623
+ },
2624
+ itemContext.fileState.error
2625
+ ))
2626
+ );
2627
+ }
2628
+ function FileUploadItemDelete(props) {
2629
+ const { asChild, onClick: onClickProp, ...deleteProps } = props;
2630
+ const store = useStoreContext(ITEM_DELETE_NAME);
2631
+ const itemContext = useFileUploadItemContext(ITEM_DELETE_NAME);
2632
+ const onClick = React21__namespace.useCallback(
2633
+ (event) => {
2634
+ onClickProp?.(event);
2635
+ if (!itemContext.fileState || event.defaultPrevented) return;
2636
+ store.dispatch({
2637
+ type: "REMOVE_FILE",
2638
+ file: itemContext.fileState.file
2639
+ });
2640
+ },
2641
+ [store, itemContext.fileState, onClickProp]
2642
+ );
2643
+ if (!itemContext.fileState) return null;
2644
+ const ItemDeletePrimitive = asChild ? reactSlot.Slot : "button";
2645
+ return /* @__PURE__ */ React21__namespace.createElement(
2646
+ ItemDeletePrimitive,
2647
+ {
2648
+ type: "button",
2649
+ "aria-controls": itemContext.id,
2650
+ "aria-describedby": itemContext.nameId,
2651
+ "data-slot": "file-upload-item-delete",
2652
+ ...deleteProps,
2653
+ onClick
2654
+ }
2655
+ );
2656
+ }
2657
+
2658
+ // src/inputs/FileInput.tsx
2659
+ function FileInput({
2660
+ name,
2661
+ value = [],
2662
+ onChange,
2663
+ onBlur,
2664
+ placeholder = "Choose file...",
2665
+ disabled = false,
2666
+ required = false,
2667
+ error = false,
2668
+ className = "",
2669
+ accept,
2670
+ maxSize = 5 * 1024 * 1024,
2671
+ // 5MB default
2672
+ maxFiles = 1,
2673
+ multiple = false,
2674
+ showPreview = true,
2675
+ showProgress = true,
2676
+ uploadProgress = {},
2677
+ enableCropping = false,
2678
+ cropAspectRatio,
2679
+ onCropComplete,
2680
+ onValidationError,
2681
+ onFileRemove,
2682
+ ...props
2683
+ }) {
2684
+ const normalizedValue = React21__namespace.useMemo(() => {
2685
+ const safeValue = Array.isArray(value) ? value : [];
2686
+ return multiple ? safeValue : safeValue.slice(0, 1);
2687
+ }, [multiple, value]);
2688
+ const [cropperOpen, setCropperOpen] = React21__namespace.useState(false);
2689
+ const [imageToCrop, setImageToCrop] = React21__namespace.useState(null);
2690
+ const [crop, setCrop] = React21__namespace.useState({ x: 0, y: 0 });
2691
+ const [zoom, setZoom] = React21__namespace.useState(1);
2692
+ const [croppedAreaPixels, setCroppedAreaPixels] = React21__namespace.useState(null);
2693
+ const validateFile = React21__namespace.useCallback(
2694
+ (file) => {
2695
+ if (accept) {
2696
+ const acceptedTypes = accept.split(",").map((type) => type.trim());
2697
+ const isValidType = acceptedTypes.some((type) => {
2698
+ if (type.startsWith(".")) {
2699
+ return file.name.toLowerCase().endsWith(type.toLowerCase());
2700
+ }
2701
+ if (type.endsWith("/*")) {
2702
+ const baseType = type.split("/")[0];
2703
+ return file.type.startsWith(`${baseType}/`);
2704
+ }
2705
+ return file.type === type;
2706
+ });
2707
+ if (!isValidType) {
2708
+ return {
2709
+ file,
2710
+ error: "type",
2711
+ message: `File type "${file.type}" is not accepted. Accepted types: ${accept}`
2712
+ };
2713
+ }
2714
+ }
2715
+ if (file.size > maxSize) {
2716
+ const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(2);
2717
+ const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
2718
+ return {
2719
+ file,
2720
+ error: "size",
2721
+ message: `File size ${fileSizeMB}MB exceeds maximum ${maxSizeMB}MB`
2722
+ };
2723
+ }
2724
+ return null;
2725
+ },
2726
+ [accept, maxSize]
2727
+ );
2728
+ const mapRejectedFileError = React21__namespace.useCallback(
2729
+ (file, message) => {
2730
+ const normalizedMessage = message.toLowerCase();
2731
+ if (normalizedMessage.includes("maximum") && normalizedMessage.includes("files")) {
2732
+ return { file, error: "count", message };
2733
+ }
2734
+ if (normalizedMessage.includes("size") || normalizedMessage.includes("large")) {
2735
+ return { file, error: "size", message };
2736
+ }
2737
+ if (normalizedMessage.includes("type") || normalizedMessage.includes("accept")) {
2738
+ return { file, error: "type", message };
2739
+ }
2740
+ if (file.size > maxSize) {
2741
+ return { file, error: "size", message };
2742
+ }
2743
+ return { file, error: "type", message };
2744
+ },
2745
+ [maxSize]
2746
+ );
2747
+ const handleFileValidate = React21__namespace.useCallback(
2748
+ (file) => {
2749
+ const validationError = validateFile(file);
2750
+ return validationError?.message ?? null;
2751
+ },
2752
+ [validateFile]
2753
+ );
2754
+ const handleFileReject = React21__namespace.useCallback(
2755
+ (file, message) => {
2756
+ const validationError = mapRejectedFileError(file, message);
2757
+ onValidationError?.([validationError]);
2758
+ },
2759
+ [mapRejectedFileError, onValidationError]
2760
+ );
2761
+ const handleBlur = React21__namespace.useCallback(() => {
2762
+ onBlur?.();
2763
+ }, [onBlur]);
2764
+ const fileIdentity = React21__namespace.useCallback((file) => {
2765
+ return `${file.name}-${file.size}-${file.lastModified}`;
2766
+ }, []);
2767
+ const handleValueChange = React21__namespace.useCallback(
2768
+ (incomingFiles) => {
2769
+ const nextFiles = multiple ? incomingFiles : incomingFiles.slice(-1);
2770
+ if (onFileRemove && nextFiles.length < normalizedValue.length) {
2771
+ const nextFileIds = new Set(nextFiles.map((file) => fileIdentity(file)));
2772
+ normalizedValue.forEach((file, index) => {
2773
+ if (!nextFileIds.has(fileIdentity(file))) {
2774
+ onFileRemove(file, index);
2775
+ }
2776
+ });
2777
+ }
2778
+ if (enableCropping && !multiple) {
2779
+ const nextImageFile = nextFiles[0];
2780
+ const previousFile = normalizedValue[0];
2781
+ const isNewSingleImage = Boolean(
2782
+ nextImageFile && nextImageFile.type.startsWith("image/") && nextImageFile !== previousFile
2783
+ );
2784
+ if (isNewSingleImage) {
2785
+ const previewUrl = URL.createObjectURL(nextImageFile);
2786
+ setImageToCrop({ file: nextImageFile, url: previewUrl });
2787
+ setCropperOpen(true);
2788
+ return;
2789
+ }
2790
+ }
2791
+ onChange(nextFiles);
2792
+ },
2793
+ [
2794
+ enableCropping,
2795
+ maxFiles,
2796
+ multiple,
2797
+ normalizedValue,
2798
+ onChange,
2799
+ onFileRemove,
2800
+ fileIdentity
2801
+ ]
2802
+ );
2803
+ const createCroppedImage = React21__namespace.useCallback(
2804
+ async (imageUrl, cropArea) => {
2805
+ return new Promise((resolve, reject) => {
2806
+ const image = new Image();
2807
+ image.onload = () => {
2808
+ const canvas = document.createElement("canvas");
2809
+ const ctx = canvas.getContext("2d");
2810
+ if (!ctx) {
2811
+ reject(new Error("Failed to get canvas context"));
2812
+ return;
2813
+ }
2814
+ canvas.width = cropArea.width;
2815
+ canvas.height = cropArea.height;
2816
+ ctx.drawImage(
2817
+ image,
2818
+ cropArea.x,
2819
+ cropArea.y,
2820
+ cropArea.width,
2821
+ cropArea.height,
2822
+ 0,
2823
+ 0,
2824
+ cropArea.width,
2825
+ cropArea.height
2826
+ );
2827
+ canvas.toBlob(
2828
+ (blob) => {
2829
+ if (blob) {
2830
+ resolve(blob);
2831
+ } else {
2832
+ reject(new Error("Failed to create blob from canvas"));
2833
+ }
2834
+ },
2835
+ "image/jpeg",
2836
+ 0.95
2837
+ );
2838
+ };
2839
+ image.onerror = () => {
2840
+ reject(new Error("Failed to load image"));
2841
+ };
2842
+ image.src = imageUrl;
2843
+ });
2844
+ },
2845
+ []
2846
+ );
2847
+ const handleCropSave = React21__namespace.useCallback(async () => {
2848
+ if (!imageToCrop || !croppedAreaPixels) return;
2849
+ try {
2850
+ const croppedBlob = await createCroppedImage(
2851
+ imageToCrop.url,
2852
+ croppedAreaPixels
2853
+ );
2854
+ if (onCropComplete) {
2855
+ onCropComplete(croppedBlob, imageToCrop.file);
2856
+ }
2857
+ const croppedFile = new File([croppedBlob], imageToCrop.file.name, {
2858
+ type: "image/jpeg"
2859
+ });
2860
+ let updatedFiles;
2861
+ if (!multiple) {
2862
+ updatedFiles = [croppedFile];
2863
+ } else {
2864
+ const existingIndex = normalizedValue.findIndex(
2865
+ (file) => file === imageToCrop.file
2866
+ );
2867
+ if (existingIndex === -1) {
2868
+ updatedFiles = [...normalizedValue, croppedFile].slice(0, maxFiles);
2869
+ } else {
2870
+ updatedFiles = normalizedValue.map(
2871
+ (file, index) => index === existingIndex ? croppedFile : file
2872
+ );
2873
+ }
2874
+ }
2875
+ onChange(updatedFiles);
2876
+ setCropperOpen(false);
2877
+ URL.revokeObjectURL(imageToCrop.url);
2878
+ setImageToCrop(null);
2879
+ setCrop({ x: 0, y: 0 });
2880
+ setZoom(1);
2881
+ setCroppedAreaPixels(null);
2882
+ } catch (cropError) {
2883
+ console.error("Failed to crop image:", cropError);
2884
+ }
2885
+ }, [
2886
+ createCroppedImage,
2887
+ croppedAreaPixels,
2888
+ imageToCrop,
2889
+ maxFiles,
2890
+ multiple,
2891
+ normalizedValue,
2892
+ onChange,
2893
+ onCropComplete
2894
+ ]);
2895
+ const handleCropCancel = React21__namespace.useCallback(() => {
2896
+ if (imageToCrop) {
2897
+ URL.revokeObjectURL(imageToCrop.url);
2898
+ }
2899
+ setCropperOpen(false);
2900
+ setImageToCrop(null);
2901
+ setCrop({ x: 0, y: 0 });
2902
+ setZoom(1);
2903
+ setCroppedAreaPixels(null);
2904
+ }, [imageToCrop]);
2905
+ const handleCrop = React21__namespace.useCallback((file) => {
2906
+ if (!file.type.startsWith("image/")) return;
2907
+ const previewUrl = URL.createObjectURL(file);
2908
+ setImageToCrop({ file, url: previewUrl });
2909
+ setCropperOpen(true);
2910
+ }, []);
2911
+ const onCropChange = React21__namespace.useCallback((nextCrop) => {
2912
+ setCrop(nextCrop);
2913
+ }, []);
2914
+ const onZoomChange = React21__namespace.useCallback((nextZoom) => {
2915
+ setZoom(nextZoom);
2916
+ }, []);
2917
+ const onCropCompleteInternal = React21__namespace.useCallback(
2918
+ (_, nextCroppedAreaPixels) => {
2919
+ setCroppedAreaPixels(nextCroppedAreaPixels);
2920
+ },
2921
+ []
2922
+ );
2923
+ const formatFileSize = React21__namespace.useCallback((bytes) => {
2924
+ if (bytes === 0) return "0 Bytes";
2925
+ const unit = 1024;
2926
+ const units = ["Bytes", "KB", "MB", "GB"];
2927
+ const index = Math.floor(Math.log(bytes) / Math.log(unit));
2928
+ return Math.round(bytes / Math.pow(unit, index) * 100) / 100 + " " + units[index];
2929
+ }, []);
2930
+ React21__namespace.useEffect(() => {
2931
+ return () => {
2932
+ if (imageToCrop) {
2933
+ URL.revokeObjectURL(imageToCrop.url);
2934
+ }
2935
+ };
2936
+ }, [imageToCrop]);
2937
+ const fileCountLabel = normalizedValue.length > 0 ? `${normalizedValue.length} file(s) selected` : placeholder;
2938
+ return /* @__PURE__ */ React21__namespace.createElement(React21__namespace.Fragment, null, /* @__PURE__ */ React21__namespace.createElement(
2939
+ FileUpload,
2940
+ {
2941
+ name,
2942
+ value: normalizedValue,
2943
+ onValueChange: handleValueChange,
2944
+ onFileValidate: handleFileValidate,
2945
+ onFileReject: handleFileReject,
2946
+ accept,
2947
+ maxSize,
2948
+ maxFiles: multiple ? maxFiles : void 0,
2949
+ multiple,
2950
+ disabled,
2951
+ required: required && normalizedValue.length === 0,
2952
+ invalid: Boolean(error || props["aria-invalid"]),
2953
+ label: "File upload",
2954
+ className: chunkDKLLPKZN_cjs.cn(className),
2955
+ inputProps: {
2956
+ ...props,
2957
+ onBlur: handleBlur,
2958
+ style: { display: "none" },
2959
+ "aria-invalid": error || props["aria-invalid"],
2960
+ "aria-required": required || props["aria-required"],
2961
+ "aria-describedby": props["aria-describedby"]
2962
+ }
2963
+ },
2964
+ /* @__PURE__ */ React21__namespace.createElement(
2965
+ FileUploadDropzone,
2966
+ {
2967
+ role: "button",
2968
+ "aria-label": placeholder,
2969
+ className: chunkDKLLPKZN_cjs.cn(
2970
+ "flex min-h-32 w-full cursor-pointer items-center justify-center border-input bg-transparent p-6 transition-colors",
2971
+ "hover:bg-accent/50 hover:border-ring",
2972
+ "data-[dragging]:bg-accent data-[dragging]:border-ring",
2973
+ disabled && "cursor-not-allowed opacity-50",
2974
+ error && "border-destructive"
2975
+ )
2976
+ },
2977
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex flex-col items-center gap-2 text-center" }, /* @__PURE__ */ React21__namespace.createElement(
2978
+ "svg",
2979
+ {
2980
+ width: "48",
2981
+ height: "48",
2982
+ viewBox: "0 0 24 24",
2983
+ fill: "none",
2984
+ stroke: "currentColor",
2985
+ strokeWidth: "2",
2986
+ strokeLinecap: "round",
2987
+ strokeLinejoin: "round",
2988
+ "aria-hidden": "true"
2989
+ },
2990
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
2991
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "17 8 12 3 7 8" }),
2992
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
2993
+ ), /* @__PURE__ */ React21__namespace.createElement("p", { className: "text-sm font-medium" }, fileCountLabel), accept && /* @__PURE__ */ React21__namespace.createElement("p", { className: "text-xs" }, "Accepted: ", accept), /* @__PURE__ */ React21__namespace.createElement("p", { className: "text-xs" }, "Max size: ", formatFileSize(maxSize)))
2994
+ ),
2995
+ /* @__PURE__ */ React21__namespace.createElement(FileUploadList, { className: "mt-4" }, normalizedValue.map((file, index) => {
2996
+ const progressValue = uploadProgress[file.name];
2997
+ const hasProgress = showProgress && typeof progressValue === "number";
2998
+ return /* @__PURE__ */ React21__namespace.createElement(
2999
+ FileUploadItem,
3000
+ {
3001
+ key: `${file.name}-${index}`,
3002
+ value: file,
3003
+ className: "flex items-center gap-3 border-border bg-card text-card-foreground hover:bg-primary/50 transition-colors"
3004
+ },
3005
+ showPreview ? /* @__PURE__ */ React21__namespace.createElement(FileUploadItemPreview, { className: "h-12 w-12 rounded [&>img]:h-full [&>img]:w-full [&>img]:object-cover [&>svg]:size-6" }) : null,
3006
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex min-w-0 flex-1 flex-col" }, /* @__PURE__ */ React21__namespace.createElement(FileUploadItemMetadata, { className: "min-w-0" }), /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-xs" }, formatFileSize(file.size)), hasProgress ? /* @__PURE__ */ React21__namespace.createElement("div", { className: "mt-1 flex items-center gap-2" }, /* @__PURE__ */ React21__namespace.createElement(
3007
+ "div",
3008
+ {
3009
+ className: "h-1.5 flex-1 overflow-hidden rounded-full bg-accent/40",
3010
+ role: "progressbar",
3011
+ "aria-valuenow": progressValue,
3012
+ "aria-valuemin": 0,
3013
+ "aria-valuemax": 100,
3014
+ "aria-label": `Upload progress: ${progressValue}%`
3015
+ },
3016
+ /* @__PURE__ */ React21__namespace.createElement(
3017
+ "div",
3018
+ {
3019
+ className: "h-full bg-primary transition-all",
3020
+ style: { width: `${progressValue}%` }
3021
+ }
3022
+ )
3023
+ ), /* @__PURE__ */ React21__namespace.createElement("span", { className: "text-xs" }, progressValue, "%")) : null),
3024
+ enableCropping && file.type.startsWith("image/") ? /* @__PURE__ */ React21__namespace.createElement(
3025
+ chunkDKLLPKZN_cjs.Button,
3026
+ {
3027
+ type: "button",
3028
+ variant: "ghost",
3029
+ size: "icon",
3030
+ onClick: (event) => {
3031
+ event.stopPropagation();
3032
+ handleCrop(file);
3033
+ },
3034
+ disabled,
3035
+ className: "h-8 w-8 p-0",
3036
+ "aria-label": `Crop ${file.name}`
3037
+ },
3038
+ /* @__PURE__ */ React21__namespace.createElement(
3039
+ "svg",
3040
+ {
3041
+ width: "20",
3042
+ height: "20",
3043
+ viewBox: "0 0 24 24",
3044
+ fill: "none",
3045
+ stroke: "currentColor",
3046
+ strokeWidth: "2",
3047
+ strokeLinecap: "round",
3048
+ strokeLinejoin: "round",
3049
+ "aria-hidden": "true"
3050
+ },
3051
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M6.13 1L6 16a2 2 0 0 0 2 2h15" }),
3052
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M1 6.13L16 6a2 2 0 0 1 2 2v15" })
3053
+ )
3054
+ ) : null,
3055
+ /* @__PURE__ */ React21__namespace.createElement(FileUploadItemDelete, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
3056
+ chunkDKLLPKZN_cjs.Button,
3057
+ {
3058
+ type: "button",
3059
+ variant: "ghost",
3060
+ size: "icon",
3061
+ disabled,
3062
+ className: "h-8 w-8 p-0",
3063
+ "aria-label": `Remove ${file.name}`
3064
+ },
3065
+ /* @__PURE__ */ React21__namespace.createElement(
3066
+ "svg",
3067
+ {
3068
+ width: "20",
3069
+ height: "20",
3070
+ viewBox: "0 0 24 24",
3071
+ fill: "none",
3072
+ stroke: "currentColor",
3073
+ strokeWidth: "2",
3074
+ strokeLinecap: "round",
3075
+ strokeLinejoin: "round",
3076
+ "aria-hidden": "true"
3077
+ },
3078
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
3079
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
3080
+ )
3081
+ ))
3082
+ );
3083
+ }))
3084
+ ), /* @__PURE__ */ React21__namespace.createElement(
3085
+ Dialog,
3086
+ {
3087
+ open: cropperOpen && Boolean(imageToCrop),
3088
+ onOpenChange: (open) => {
3089
+ if (!open) {
3090
+ handleCropCancel();
3091
+ }
3092
+ }
3093
+ },
3094
+ imageToCrop ? /* @__PURE__ */ React21__namespace.createElement(
3095
+ DialogContent,
3096
+ {
3097
+ showCloseButton: false,
3098
+ className: "max-w-3xl gap-0 p-0",
3099
+ "aria-describedby": void 0
3100
+ },
3101
+ /* @__PURE__ */ React21__namespace.createElement(DialogHeader, { className: "flex-row items-center justify-between border-b border-border px-4 py-3" }, /* @__PURE__ */ React21__namespace.createElement(DialogTitle, null, "Crop Image"), /* @__PURE__ */ React21__namespace.createElement(DialogClose, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
3102
+ chunkDKLLPKZN_cjs.Button,
3103
+ {
3104
+ type: "button",
3105
+ variant: "ghost",
3106
+ size: "icon",
3107
+ className: "h-8 w-8 p-0",
3108
+ "aria-label": "Close"
3109
+ },
3110
+ /* @__PURE__ */ React21__namespace.createElement(
3111
+ "svg",
3112
+ {
3113
+ width: "16",
3114
+ height: "16",
3115
+ viewBox: "0 0 24 24",
3116
+ fill: "none",
3117
+ stroke: "currentColor",
3118
+ strokeWidth: "2",
3119
+ strokeLinecap: "round",
3120
+ strokeLinejoin: "round",
3121
+ "aria-hidden": "true"
3122
+ },
3123
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
3124
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
3125
+ )
3126
+ ))),
3127
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "p-4" }, /* @__PURE__ */ React21__namespace.createElement(
3128
+ "div",
3129
+ {
3130
+ className: "relative h-96 w-full overflow-hidden rounded-md bg-accent/40",
3131
+ onMouseDown: (event) => {
3132
+ event.preventDefault();
3133
+ const startX = event.clientX - crop.x;
3134
+ const startY = event.clientY - crop.y;
3135
+ const handleMouseMove = (moveEvent) => {
3136
+ onCropChange({
3137
+ x: moveEvent.clientX - startX,
3138
+ y: moveEvent.clientY - startY
3139
+ });
3140
+ };
3141
+ const handleMouseUp = () => {
3142
+ document.removeEventListener("mousemove", handleMouseMove);
3143
+ document.removeEventListener("mouseup", handleMouseUp);
3144
+ };
3145
+ document.addEventListener("mousemove", handleMouseMove);
3146
+ document.addEventListener("mouseup", handleMouseUp);
3147
+ }
3148
+ },
3149
+ /* @__PURE__ */ React21__namespace.createElement(
3150
+ "img",
3151
+ {
3152
+ src: imageToCrop.url,
3153
+ alt: "Crop preview",
3154
+ className: "absolute inset-0 h-full w-full object-contain",
3155
+ style: {
3156
+ transform: `translate(${crop.x}px, ${crop.y}px) scale(${zoom})`
3157
+ },
3158
+ draggable: false,
3159
+ onLoad: (event) => {
3160
+ const image = event.currentTarget;
3161
+ const containerWidth = 600;
3162
+ const containerHeight = 400;
3163
+ const cropWidth = cropAspectRatio ? Math.min(
3164
+ containerWidth * 0.8,
3165
+ containerHeight * 0.8 * cropAspectRatio
3166
+ ) : containerWidth * 0.8;
3167
+ const cropHeight = cropAspectRatio ? cropWidth / cropAspectRatio : containerHeight * 0.8;
3168
+ const imageWidth = image.naturalWidth;
3169
+ const imageHeight = image.naturalHeight;
3170
+ const scale = zoom;
3171
+ const centerX = containerWidth / 2;
3172
+ const centerY = containerHeight / 2;
3173
+ const cropX = (centerX - crop.x - cropWidth / 2) / scale;
3174
+ const cropY = (centerY - crop.y - cropHeight / 2) / scale;
3175
+ onCropCompleteInternal(null, {
3176
+ x: Math.max(0, cropX),
3177
+ y: Math.max(0, cropY),
3178
+ width: Math.min(cropWidth / scale, imageWidth),
3179
+ height: Math.min(cropHeight / scale, imageHeight)
3180
+ });
3181
+ }
3182
+ }
3183
+ ),
3184
+ /* @__PURE__ */ React21__namespace.createElement(
3185
+ "div",
3186
+ {
3187
+ className: "pointer-events-none absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded border-2 border-primary",
3188
+ style: {
3189
+ width: cropAspectRatio ? `${Math.min(80, 80 * cropAspectRatio)}%` : "80%",
3190
+ aspectRatio: cropAspectRatio ? String(cropAspectRatio) : void 0
3191
+ }
3192
+ },
3193
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "absolute inset-0 grid grid-cols-3 grid-rows-3" }, /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React21__namespace.createElement("div", null))
3194
+ )
3195
+ ), /* @__PURE__ */ React21__namespace.createElement("div", { className: "mt-4 flex items-center gap-3" }, /* @__PURE__ */ React21__namespace.createElement(
3196
+ "label",
3197
+ {
3198
+ htmlFor: "zoom-slider",
3199
+ className: "whitespace-nowrap text-sm font-medium"
3200
+ },
3201
+ "Zoom: ",
3202
+ zoom.toFixed(1),
3203
+ "x"
3204
+ ), /* @__PURE__ */ React21__namespace.createElement(
3205
+ "input",
3206
+ {
3207
+ id: "zoom-slider",
3208
+ type: "range",
3209
+ min: "1",
3210
+ max: "3",
3211
+ step: "0.1",
3212
+ value: zoom,
3213
+ onChange: (event) => onZoomChange(parseFloat(event.target.value)),
3214
+ className: "h-2 flex-1 cursor-pointer appearance-none rounded-lg bg-accent/60",
3215
+ "aria-label": "Zoom level"
3216
+ }
3217
+ ))),
3218
+ /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex items-center justify-end gap-2 border-t border-border p-4" }, /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Button, { type: "button", variant: "outline", onClick: handleCropCancel }, "Cancel"), /* @__PURE__ */ React21__namespace.createElement(chunkDKLLPKZN_cjs.Button, { type: "button", onClick: handleCropSave }, "Save"))
3219
+ ) : null
3220
+ ));
3221
+ }
3222
+ FileInput.displayName = "FileInput";
3223
+ function Calendar({
3224
+ className,
3225
+ classNames,
3226
+ showOutsideDays = true,
3227
+ captionLayout = "label",
3228
+ buttonVariant = "ghost",
3229
+ formatters,
3230
+ components,
3231
+ ...props
3232
+ }) {
3233
+ const defaultClassNames = reactDayPicker.getDefaultClassNames();
3234
+ return /* @__PURE__ */ React21__namespace.createElement(
3235
+ reactDayPicker.DayPicker,
3236
+ {
3237
+ showOutsideDays,
3238
+ className: chunkDKLLPKZN_cjs.cn(
3239
+ "bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
3240
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
3241
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
3242
+ className
3243
+ ),
3244
+ captionLayout,
3245
+ formatters: {
3246
+ formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
3247
+ ...formatters
3248
+ },
3249
+ classNames: {
3250
+ root: chunkDKLLPKZN_cjs.cn("w-fit", defaultClassNames.root),
3251
+ months: chunkDKLLPKZN_cjs.cn(
3252
+ "flex gap-4 flex-col md:flex-row relative",
3253
+ defaultClassNames.months
3254
+ ),
3255
+ month: chunkDKLLPKZN_cjs.cn("flex flex-col w-full gap-4", defaultClassNames.month),
3256
+ nav: chunkDKLLPKZN_cjs.cn(
3257
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
3258
+ defaultClassNames.nav
3259
+ ),
3260
+ button_previous: chunkDKLLPKZN_cjs.cn(
3261
+ chunkDKLLPKZN_cjs.buttonVariants({ variant: buttonVariant }),
3262
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
3263
+ defaultClassNames.button_previous
3264
+ ),
3265
+ button_next: chunkDKLLPKZN_cjs.cn(
3266
+ chunkDKLLPKZN_cjs.buttonVariants({ variant: buttonVariant }),
3267
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
3268
+ defaultClassNames.button_next
3269
+ ),
3270
+ month_caption: chunkDKLLPKZN_cjs.cn(
3271
+ "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
3272
+ defaultClassNames.month_caption
3273
+ ),
3274
+ dropdowns: chunkDKLLPKZN_cjs.cn(
3275
+ "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
3276
+ defaultClassNames.dropdowns
3277
+ ),
3278
+ dropdown_root: chunkDKLLPKZN_cjs.cn(
3279
+ "relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
3280
+ defaultClassNames.dropdown_root
3281
+ ),
3282
+ dropdown: chunkDKLLPKZN_cjs.cn(
3283
+ "absolute bg-popover inset-0 opacity-0",
3284
+ defaultClassNames.dropdown
3285
+ ),
3286
+ caption_label: chunkDKLLPKZN_cjs.cn(
3287
+ "select-none font-medium",
3288
+ captionLayout === "label" ? "text-sm" : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:opacity-70 [&>svg]:size-3.5",
3289
+ defaultClassNames.caption_label
3290
+ ),
3291
+ table: "w-full border-collapse",
3292
+ weekdays: chunkDKLLPKZN_cjs.cn("flex", defaultClassNames.weekdays),
3293
+ weekday: chunkDKLLPKZN_cjs.cn(
3294
+ "opacity-70 rounded-md flex-1 font-normal text-[0.8rem] select-none",
3295
+ defaultClassNames.weekday
3296
+ ),
3297
+ week: chunkDKLLPKZN_cjs.cn("flex w-full mt-2", defaultClassNames.week),
3298
+ week_number_header: chunkDKLLPKZN_cjs.cn(
3299
+ "select-none w-(--cell-size)",
3300
+ defaultClassNames.week_number_header
3301
+ ),
3302
+ week_number: chunkDKLLPKZN_cjs.cn(
3303
+ "text-[0.8rem] select-none opacity-70",
3304
+ defaultClassNames.week_number
3305
+ ),
3306
+ day: chunkDKLLPKZN_cjs.cn(
3307
+ "relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
3308
+ props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md" : "[&:first-child[data-selected=true]_button]:rounded-l-md",
3309
+ defaultClassNames.day
3310
+ ),
3311
+ range_start: chunkDKLLPKZN_cjs.cn(
3312
+ "rounded-l-md bg-accent",
3313
+ defaultClassNames.range_start
3314
+ ),
3315
+ range_middle: chunkDKLLPKZN_cjs.cn("rounded-none", defaultClassNames.range_middle),
3316
+ range_end: chunkDKLLPKZN_cjs.cn("rounded-r-md bg-accent", defaultClassNames.range_end),
3317
+ today: chunkDKLLPKZN_cjs.cn(
3318
+ "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
3319
+ defaultClassNames.today
3320
+ ),
3321
+ outside: chunkDKLLPKZN_cjs.cn(
3322
+ "opacity-50",
3323
+ defaultClassNames.outside
3324
+ ),
3325
+ disabled: chunkDKLLPKZN_cjs.cn(
3326
+ "opacity-50",
3327
+ defaultClassNames.disabled
3328
+ ),
3329
+ hidden: chunkDKLLPKZN_cjs.cn("invisible", defaultClassNames.hidden),
3330
+ ...classNames
3331
+ },
3332
+ components: {
3333
+ Root: ({ className: className2, rootRef, ...props2 }) => {
3334
+ return /* @__PURE__ */ React21__namespace.createElement(
3335
+ "div",
3336
+ {
3337
+ "data-slot": "calendar",
3338
+ ref: rootRef,
3339
+ className: chunkDKLLPKZN_cjs.cn(className2),
3340
+ ...props2
3341
+ }
3342
+ );
3343
+ },
3344
+ Chevron: ({ className: className2, orientation, ...props2 }) => {
3345
+ if (orientation === "left") {
3346
+ return /* @__PURE__ */ React21__namespace.createElement(
3347
+ "svg",
3348
+ {
3349
+ className: chunkDKLLPKZN_cjs.cn("size-4", className2),
3350
+ viewBox: "0 0 24 24",
3351
+ fill: "none",
3352
+ stroke: "currentColor",
3353
+ strokeWidth: "2",
3354
+ strokeLinecap: "round",
3355
+ strokeLinejoin: "round",
3356
+ ...props2
3357
+ },
3358
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "15 18 9 12 15 6" })
3359
+ );
3360
+ }
3361
+ if (orientation === "right") {
3362
+ return /* @__PURE__ */ React21__namespace.createElement(
3363
+ "svg",
3364
+ {
3365
+ className: chunkDKLLPKZN_cjs.cn("size-4", className2),
3366
+ viewBox: "0 0 24 24",
3367
+ fill: "none",
3368
+ stroke: "currentColor",
3369
+ strokeWidth: "2",
3370
+ strokeLinecap: "round",
3371
+ strokeLinejoin: "round",
3372
+ ...props2
3373
+ },
3374
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "9 18 15 12 9 6" })
3375
+ );
3376
+ }
3377
+ return /* @__PURE__ */ React21__namespace.createElement(
3378
+ "svg",
3379
+ {
3380
+ className: chunkDKLLPKZN_cjs.cn("size-4", className2),
3381
+ viewBox: "0 0 24 24",
3382
+ fill: "none",
3383
+ stroke: "currentColor",
3384
+ strokeWidth: "2",
3385
+ strokeLinecap: "round",
3386
+ strokeLinejoin: "round",
3387
+ ...props2
3388
+ },
3389
+ /* @__PURE__ */ React21__namespace.createElement("polyline", { points: "6 9 12 15 18 9" })
3390
+ );
3391
+ },
3392
+ DayButton: CalendarDayButton,
3393
+ WeekNumber: ({ children, ...props2 }) => {
3394
+ return /* @__PURE__ */ React21__namespace.createElement("td", { ...props2 }, /* @__PURE__ */ React21__namespace.createElement("div", { className: "flex size-(--cell-size) items-center justify-center text-center" }, children));
3395
+ },
3396
+ ...components
3397
+ },
3398
+ ...props
3399
+ }
3400
+ );
3401
+ }
3402
+ function CalendarDayButton({
3403
+ className,
3404
+ day,
3405
+ modifiers,
3406
+ ...props
3407
+ }) {
3408
+ const defaultClassNames = reactDayPicker.getDefaultClassNames();
3409
+ const ref = React21__namespace.useRef(null);
3410
+ React21__namespace.useEffect(() => {
3411
+ if (modifiers.focused) ref.current?.focus();
3412
+ }, [modifiers.focused]);
3413
+ return /* @__PURE__ */ React21__namespace.createElement(
3414
+ chunkDKLLPKZN_cjs.Button,
3415
+ {
3416
+ ref,
3417
+ variant: "ghost",
3418
+ size: "icon",
3419
+ "data-day": day.date.toLocaleDateString(),
3420
+ "data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
3421
+ "data-range-start": modifiers.range_start,
3422
+ "data-range-end": modifiers.range_end,
3423
+ "data-range-middle": modifiers.range_middle,
3424
+ className: chunkDKLLPKZN_cjs.cn(
3425
+ // Core structure
3426
+ "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal",
3427
+ // Selected states - uses CSS variables
3428
+ "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground",
3429
+ "data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground",
3430
+ "data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground",
3431
+ "data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground",
3432
+ // Focus state
3433
+ "group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10",
3434
+ "group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-[3px]",
3435
+ // Rounding based on position
3436
+ "data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md",
3437
+ "data-[range-middle=true]:rounded-none",
3438
+ "data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md",
3439
+ // Nested span styling
3440
+ "[&>span]:text-xs [&>span]:opacity-70",
3441
+ defaultClassNames.day,
3442
+ className
3443
+ ),
3444
+ ...props
3445
+ }
3446
+ );
3447
+ }
3448
+
3449
+ // src/inputs/DatePicker.tsx
3450
+ function formatDate(date, format) {
3451
+ if (!date) return "";
3452
+ const d = new Date(date);
3453
+ const month = String(d.getMonth() + 1).padStart(2, "0");
3454
+ const day = String(d.getDate()).padStart(2, "0");
3455
+ const year = d.getFullYear();
3456
+ return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
3457
+ }
3458
+ function DatePickerDayButton({
3459
+ day,
3460
+ modifiers,
3461
+ className,
3462
+ children,
3463
+ ...props
3464
+ }) {
3465
+ return /* @__PURE__ */ React21__namespace.createElement(
3466
+ "button",
3467
+ {
3468
+ type: "button",
3469
+ className: chunkDKLLPKZN_cjs.cn(
3470
+ "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
3471
+ "hover:bg-accent",
3472
+ modifiers.selected && "bg-primary text-primary-foreground font-semibold",
3473
+ !modifiers.selected && modifiers.today && "border border-primary",
3474
+ modifiers.disabled && "cursor-not-allowed opacity-50 pointer-events-none",
3475
+ className
3476
+ ),
3477
+ ...props
3478
+ },
3479
+ children ?? day.date.getDate()
3480
+ );
3481
+ }
3482
+ function DatePicker({
3483
+ name,
3484
+ value,
3485
+ onChange,
3486
+ onBlur,
3487
+ disabled = false,
3488
+ required = false,
3489
+ error = false,
3490
+ className = "",
3491
+ placeholder = "Select date...",
3492
+ format = "MM/dd/yyyy",
3493
+ minDate,
3494
+ maxDate,
3495
+ disabledDates = [],
3496
+ isDateDisabled,
3497
+ clearable = true,
3498
+ showIcon = true,
3499
+ ...props
3500
+ }) {
3501
+ const [isOpen, setIsOpen] = React21__namespace.useState(false);
3502
+ const [hasInteracted, setHasInteracted] = React21__namespace.useState(false);
3503
+ const [selectedMonth, setSelectedMonth] = React21__namespace.useState(
3504
+ value || /* @__PURE__ */ new Date()
3505
+ );
3506
+ const inputRef = React21__namespace.useRef(null);
3507
+ React21__namespace.useEffect(() => {
3508
+ if (value) {
3509
+ setSelectedMonth(value);
3510
+ }
3511
+ }, [value]);
3512
+ const disabledMatchers = React21__namespace.useMemo(() => {
3513
+ const matchers = [];
3514
+ if (minDate) {
3515
+ matchers.push({ before: minDate });
3516
+ }
3517
+ if (maxDate) {
3518
+ matchers.push({ after: maxDate });
3519
+ }
3520
+ if (disabledDates.length > 0) {
3521
+ matchers.push(disabledDates);
3522
+ }
3523
+ if (isDateDisabled) {
3524
+ matchers.push(isDateDisabled);
3525
+ }
3526
+ return matchers;
3527
+ }, [disabledDates, isDateDisabled, maxDate, minDate]);
3528
+ const handleDateSelect = React21__namespace.useCallback(
3529
+ (date) => {
3530
+ if (!date) return;
3531
+ onChange(date);
3532
+ setSelectedMonth(date);
3533
+ setIsOpen(false);
3534
+ onBlur?.();
3535
+ },
3536
+ [onBlur, onChange]
3537
+ );
3538
+ const handleClear = React21__namespace.useCallback(
3539
+ (e) => {
3540
+ e.stopPropagation();
3541
+ onChange(null);
3542
+ setIsOpen(false);
3543
+ inputRef.current?.focus();
3544
+ },
3545
+ [onChange]
3546
+ );
3547
+ const handleOpenChange = React21__namespace.useCallback(
3548
+ (nextOpen) => {
3549
+ if (disabled) {
3550
+ setIsOpen(false);
3551
+ return;
3552
+ }
3553
+ if (nextOpen) {
3554
+ if (!hasInteracted) {
3555
+ setHasInteracted(true);
3556
+ }
3557
+ setIsOpen(true);
3558
+ return;
3559
+ }
3560
+ if (isOpen && hasInteracted) {
3561
+ onBlur?.();
3562
+ }
3563
+ setIsOpen(false);
3564
+ },
3565
+ [disabled, hasInteracted, isOpen, onBlur]
3566
+ );
3567
+ const handleInputBlur = React21__namespace.useCallback(() => {
3568
+ if (!isOpen) {
3569
+ onBlur?.();
3570
+ }
3571
+ }, [isOpen, onBlur]);
3572
+ const handleInputClick = React21__namespace.useCallback(() => {
3573
+ if (!hasInteracted) {
3574
+ setHasInteracted(true);
3575
+ }
3576
+ }, [hasInteracted]);
3577
+ const hasValue = Boolean(value);
3578
+ const displayValue = formatDate(value, format);
3579
+ const combinedClassName = chunkDKLLPKZN_cjs.cn("relative", className);
3580
+ return /* @__PURE__ */ React21__namespace.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React21__namespace.createElement(
3581
+ "input",
3582
+ {
3583
+ type: "hidden",
3584
+ name,
3585
+ value: value ? value.toISOString() : ""
3586
+ }
3587
+ ), /* @__PURE__ */ React21__namespace.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React21__namespace.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React21__namespace.createElement(
3588
+ "span",
3589
+ {
3590
+ className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
3591
+ "aria-hidden": "true"
3592
+ },
3593
+ /* @__PURE__ */ React21__namespace.createElement(
3594
+ "svg",
3595
+ {
3596
+ xmlns: "http://www.w3.org/2000/svg",
3597
+ width: "18",
3598
+ height: "18",
3599
+ viewBox: "0 0 24 24",
3600
+ fill: "none",
3601
+ stroke: "currentColor",
3602
+ strokeLinecap: "round",
3603
+ strokeLinejoin: "round",
3604
+ strokeWidth: "2"
3605
+ },
3606
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
3607
+ )
3608
+ ), /* @__PURE__ */ React21__namespace.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
3609
+ "input",
3610
+ {
3611
+ ref: inputRef,
3612
+ id: props.id,
3613
+ type: "text",
3614
+ className: chunkDKLLPKZN_cjs.cn(
3615
+ "flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
3616
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
3617
+ "disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
3618
+ chunkDKLLPKZN_cjs.INPUT_AUTOFILL_RESET_CLASSES,
3619
+ showIcon ? "pl-10" : "pl-3",
3620
+ clearable && value ? "pr-10" : "pr-3",
3621
+ !error && hasValue && "ring-2 ring-ring",
3622
+ error && "border-destructive ring-1 ring-destructive"
3623
+ ),
3624
+ value: displayValue,
3625
+ onClick: handleInputClick,
3626
+ onBlur: handleInputBlur,
3627
+ disabled,
3628
+ required,
3629
+ placeholder,
3630
+ "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
3631
+ "aria-describedby": props["aria-describedby"],
3632
+ "aria-required": required || props["aria-required"],
3633
+ readOnly: true
3634
+ }
3635
+ )), clearable && value && !disabled && /* @__PURE__ */ React21__namespace.createElement(
3636
+ "button",
3637
+ {
3638
+ type: "button",
3639
+ className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
3640
+ onClick: handleClear,
3641
+ "aria-label": "Clear date",
3642
+ tabIndex: -1
3643
+ },
3644
+ "\u2715"
3645
+ )), !disabled && /* @__PURE__ */ React21__namespace.createElement(
3646
+ PopoverContent,
3647
+ {
3648
+ align: "start",
3649
+ sideOffset: 4,
3650
+ className: "w-auto p-0",
3651
+ onOpenAutoFocus: (event) => {
3652
+ event.preventDefault();
3653
+ }
3654
+ },
3655
+ /* @__PURE__ */ React21__namespace.createElement(
3656
+ Calendar,
3657
+ {
3658
+ mode: "single",
3659
+ selected: value ?? void 0,
3660
+ onSelect: handleDateSelect,
3661
+ month: selectedMonth,
3662
+ onMonthChange: setSelectedMonth,
3663
+ disabled: disabledMatchers,
3664
+ showOutsideDays: true,
3665
+ labels: {
3666
+ labelGrid: () => "Calendar",
3667
+ labelDayButton: (date) => formatDate(date, format),
3668
+ labelPrevious: () => "Previous month",
3669
+ labelNext: () => "Next month"
3670
+ },
3671
+ components: {
3672
+ DayButton: DatePickerDayButton
3673
+ },
3674
+ classNames: {
3675
+ today: "border border-primary rounded-md bg-transparent"
3676
+ }
3677
+ }
3678
+ )
3679
+ )));
3680
+ }
3681
+ DatePicker.displayName = "DatePicker";
3682
+ function normalizeToNativeTime(value) {
3683
+ if (!value) return "";
3684
+ const twelveHourMatch = value.match(
3685
+ /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(AM|PM)$/i
3686
+ );
3687
+ if (twelveHourMatch) {
3688
+ const rawHour = parseInt(twelveHourMatch[1], 10);
3689
+ const minute = parseInt(twelveHourMatch[2], 10);
3690
+ const period = twelveHourMatch[4].toUpperCase();
3691
+ if (Number.isNaN(rawHour) || Number.isNaN(minute) || rawHour < 1 || rawHour > 12 || minute < 0 || minute > 59) {
3692
+ return "";
3693
+ }
3694
+ const normalizedHour = period === "PM" ? rawHour === 12 ? 12 : rawHour + 12 : rawHour === 12 ? 0 : rawHour;
3695
+ return `${String(normalizedHour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
3696
+ }
3697
+ const twentyFourHourMatch = value.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
3698
+ if (twentyFourHourMatch) {
3699
+ const hour = parseInt(twentyFourHourMatch[1], 10);
3700
+ const minute = parseInt(twentyFourHourMatch[2], 10);
3701
+ if (Number.isNaN(hour) || Number.isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
3702
+ return "";
3703
+ }
3704
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
3705
+ }
3706
+ return "";
3707
+ }
3708
+ function formatFromNativeTime(nativeValue, use24Hour) {
3709
+ if (!nativeValue) return "";
3710
+ const [hourValue, minuteValue] = nativeValue.split(":");
3711
+ const hour = parseInt(hourValue, 10);
3712
+ const minute = parseInt(minuteValue, 10);
3713
+ if (Number.isNaN(hour) || Number.isNaN(minute)) {
3714
+ return "";
3715
+ }
3716
+ if (use24Hour) {
3717
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
3718
+ }
3719
+ const period = hour >= 12 ? "PM" : "AM";
3720
+ const hour12 = hour % 12 || 12;
3721
+ return `${hour12}:${String(minute).padStart(2, "0")} ${period}`;
3722
+ }
3723
+ function TimePicker({
3724
+ name,
3725
+ value,
3726
+ onChange,
3727
+ onBlur,
3728
+ disabled = false,
3729
+ required = false,
3730
+ error = false,
3731
+ className = "",
3732
+ placeholder = "Select time...",
3733
+ use24Hour = false,
3734
+ minuteStep = 1,
3735
+ clearable = true,
3736
+ showIcon = true,
3737
+ ...props
3738
+ }) {
3739
+ const inputRef = React21__namespace.useRef(null);
3740
+ const [nativeValue, setNativeValue] = React21__namespace.useState(
3741
+ normalizeToNativeTime(value)
3742
+ );
3743
+ React21__namespace.useEffect(() => {
3744
+ setNativeValue(normalizeToNativeTime(value));
3745
+ }, [value]);
3746
+ const handleChange = (e) => {
3747
+ const nextNativeValue = e.target.value;
3748
+ setNativeValue(nextNativeValue);
3749
+ onChange(formatFromNativeTime(nextNativeValue, use24Hour));
3750
+ };
3751
+ const handleClear = (e) => {
3752
+ e.stopPropagation();
3753
+ setNativeValue("");
3754
+ onChange("");
3755
+ inputRef.current?.focus();
3756
+ };
3757
+ const hasValue = Boolean(value);
3758
+ const stepInSeconds = Math.max(1, minuteStep * 60);
3759
+ return /* @__PURE__ */ React21__namespace.createElement("div", { className: chunkDKLLPKZN_cjs.cn("relative", className) }, /* @__PURE__ */ React21__namespace.createElement("input", { type: "hidden", name, value }), /* @__PURE__ */ React21__namespace.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React21__namespace.createElement(
3760
+ "span",
3761
+ {
3762
+ className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
3763
+ "aria-hidden": "true"
3764
+ },
3765
+ /* @__PURE__ */ React21__namespace.createElement(
3766
+ "svg",
3767
+ {
3768
+ xmlns: "http://www.w3.org/2000/svg",
3769
+ width: "18",
3770
+ height: "18",
3771
+ viewBox: "0 0 24 24",
3772
+ fill: "none",
3773
+ stroke: "currentColor",
3774
+ strokeLinecap: "round",
3775
+ strokeLinejoin: "round",
3776
+ strokeWidth: "2"
3777
+ },
3778
+ /* @__PURE__ */ React21__namespace.createElement("circle", { cx: "12", cy: "12", r: "10" }),
3779
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M12 6v6l4 2" })
3780
+ )
3781
+ ), /* @__PURE__ */ React21__namespace.createElement(
3782
+ Input,
3783
+ {
3784
+ ref: inputRef,
3785
+ type: "time",
3786
+ className: chunkDKLLPKZN_cjs.cn(
3787
+ "appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none",
3788
+ chunkDKLLPKZN_cjs.INPUT_AUTOFILL_RESET_CLASSES,
3789
+ showIcon ? "pl-10" : "pl-3",
3790
+ clearable && value ? "pr-10" : "pr-3",
3791
+ !error && hasValue && "ring-2 ring-ring",
3792
+ error && "border-destructive ring-1 ring-destructive"
3793
+ ),
3794
+ value: nativeValue,
3795
+ onChange: handleChange,
3796
+ onBlur,
3797
+ disabled,
3798
+ required,
3799
+ step: stepInSeconds,
3800
+ placeholder,
3801
+ "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
3802
+ "aria-describedby": props["aria-describedby"],
3803
+ "aria-required": required || props["aria-required"],
3804
+ ...props
3805
+ }
3806
+ ), clearable && value && !disabled && /* @__PURE__ */ React21__namespace.createElement(
3807
+ chunkDKLLPKZN_cjs.Button,
3808
+ {
3809
+ type: "button",
3810
+ variant: "ghost",
3811
+ size: "icon",
3812
+ className: "absolute right-1.5 top-1/2 h-7 w-7 -translate-y-1/2 p-0",
3813
+ onClick: handleClear,
3814
+ "aria-label": "Clear time",
3815
+ tabIndex: -1
3816
+ },
3817
+ /* @__PURE__ */ React21__namespace.createElement(
3818
+ "svg",
3819
+ {
3820
+ width: "14",
3821
+ height: "14",
3822
+ viewBox: "0 0 24 24",
3823
+ fill: "none",
3824
+ stroke: "currentColor",
3825
+ strokeWidth: "2",
3826
+ strokeLinecap: "round",
3827
+ strokeLinejoin: "round",
3828
+ "aria-hidden": "true"
3829
+ },
3830
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
3831
+ /* @__PURE__ */ React21__namespace.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
3832
+ )
3833
+ )));
3834
+ }
3835
+ TimePicker.displayName = "TimePicker";
3836
+ function formatDate2(date, format) {
3837
+ if (!date) return "";
3838
+ const d = new Date(date);
3839
+ const month = String(d.getMonth() + 1).padStart(2, "0");
3840
+ const day = String(d.getDate()).padStart(2, "0");
3841
+ const year = d.getFullYear();
3842
+ return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
3843
+ }
3844
+ function toDayTimestamp(date) {
3845
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
3846
+ }
3847
+ function isSameDay(date, target) {
3848
+ if (!target) return false;
3849
+ return toDayTimestamp(date) === toDayTimestamp(target);
3850
+ }
3851
+ function isDateInRange(date, start, end) {
3852
+ if (!start || !end) return false;
3853
+ const value = toDayTimestamp(date);
3854
+ const startTs = toDayTimestamp(start);
3855
+ const endTs = toDayTimestamp(end);
3856
+ return value >= Math.min(startTs, endTs) && value <= Math.max(startTs, endTs);
3857
+ }
3858
+ function DateRangePicker({
3859
+ name,
3860
+ value = { start: null, end: null },
3861
+ onChange,
3862
+ onBlur,
3863
+ disabled = false,
3864
+ required = false,
3865
+ error = false,
3866
+ className = "",
3867
+ placeholder = "Select date range...",
3868
+ format = "MM/dd/yyyy",
3869
+ minDate,
3870
+ maxDate,
3871
+ disabledDates = [],
3872
+ isDateDisabled,
3873
+ clearable = true,
3874
+ showIcon = true,
3875
+ separator = " - ",
3876
+ ...props
3877
+ }) {
3878
+ const [isOpen, setIsOpen] = React21__namespace.useState(false);
3879
+ const [hasInteracted, setHasInteracted] = React21__namespace.useState(false);
3880
+ const [selectedMonth, setSelectedMonth] = React21__namespace.useState(
3881
+ value.start || /* @__PURE__ */ new Date()
3882
+ );
3883
+ const [rangeStart, setRangeStart] = React21__namespace.useState(value.start);
3884
+ const [rangeEnd, setRangeEnd] = React21__namespace.useState(value.end);
3885
+ const [hoverDate, setHoverDate] = React21__namespace.useState(null);
3886
+ const inputRef = React21__namespace.useRef(null);
3887
+ React21__namespace.useEffect(() => {
3888
+ setRangeStart(value.start);
3889
+ setRangeEnd(value.end);
3890
+ if (value.start) {
3891
+ setSelectedMonth(value.start);
3892
+ }
3893
+ }, [value]);
3894
+ const disabledMatchers = React21__namespace.useMemo(() => {
3895
+ const matchers = [];
3896
+ if (minDate) {
3897
+ matchers.push({ before: minDate });
3898
+ }
3899
+ if (maxDate) {
3900
+ matchers.push({ after: maxDate });
3901
+ }
3902
+ if (disabledDates.length > 0) {
3903
+ matchers.push(disabledDates);
3904
+ }
3905
+ if (isDateDisabled) {
3906
+ matchers.push(isDateDisabled);
3907
+ }
3908
+ return matchers;
3909
+ }, [disabledDates, isDateDisabled, maxDate, minDate]);
3910
+ const handleDateSelect = React21__namespace.useCallback(
3911
+ (date) => {
3912
+ if (!rangeStart || rangeEnd) {
3913
+ setRangeStart(date);
3914
+ setRangeEnd(null);
3915
+ setHoverDate(null);
3916
+ setSelectedMonth(date);
3917
+ onChange({ start: date, end: null });
3918
+ onBlur?.();
3919
+ return;
3920
+ }
3921
+ if (toDayTimestamp(date) < toDayTimestamp(rangeStart)) {
3922
+ setRangeStart(date);
3923
+ setRangeEnd(rangeStart);
3924
+ setHoverDate(null);
3925
+ setSelectedMonth(date);
3926
+ onChange({ start: date, end: rangeStart });
3927
+ setIsOpen(false);
3928
+ onBlur?.();
3929
+ return;
3930
+ }
3931
+ setRangeEnd(date);
3932
+ setHoverDate(null);
3933
+ setSelectedMonth(date);
3934
+ onChange({ start: rangeStart, end: date });
3935
+ setIsOpen(false);
3936
+ onBlur?.();
3937
+ },
3938
+ [onBlur, onChange, rangeEnd, rangeStart]
3939
+ );
3940
+ const handleClear = React21__namespace.useCallback(
3941
+ (e) => {
3942
+ e.stopPropagation();
3943
+ setRangeStart(null);
3944
+ setRangeEnd(null);
3945
+ setHoverDate(null);
3946
+ setIsOpen(false);
3947
+ onChange({ start: null, end: null });
3948
+ inputRef.current?.focus();
3949
+ },
3950
+ [onChange]
3951
+ );
3952
+ const handleOpenChange = React21__namespace.useCallback(
3953
+ (nextOpen) => {
3954
+ if (disabled) {
3955
+ setIsOpen(false);
3956
+ return;
3957
+ }
3958
+ if (nextOpen) {
3959
+ if (!hasInteracted) {
3960
+ setHasInteracted(true);
3961
+ }
3962
+ setIsOpen(true);
3963
+ return;
3964
+ }
3965
+ if (isOpen && hasInteracted) {
3966
+ onBlur?.();
3967
+ }
3968
+ setHoverDate(null);
3969
+ setIsOpen(false);
3970
+ },
3971
+ [disabled, hasInteracted, isOpen, onBlur]
3972
+ );
3973
+ const handleInputBlur = React21__namespace.useCallback(() => {
3974
+ if (!isOpen) {
3975
+ onBlur?.();
3976
+ }
3977
+ }, [isOpen, onBlur]);
3978
+ const handleInputClick = React21__namespace.useCallback(() => {
3979
+ if (!hasInteracted) {
3980
+ setHasInteracted(true);
3981
+ }
3982
+ }, [hasInteracted]);
3983
+ const RangeDayButton = React21__namespace.useCallback(
3984
+ ({
3985
+ day,
3986
+ modifiers,
3987
+ className: dayClassName,
3988
+ children,
3989
+ onClick,
3990
+ onMouseEnter,
3991
+ onMouseLeave,
3992
+ ...rest
3993
+ }) => {
3994
+ const date = day.date;
3995
+ const isStart = isSameDay(date, rangeStart);
3996
+ const isEnd = isSameDay(date, rangeEnd);
3997
+ const isRangeEndpoint = isStart || isEnd;
3998
+ const isInCommittedRange = isDateInRange(date, rangeStart, rangeEnd);
3999
+ const isInHoverRange = !!rangeStart && !rangeEnd && !!hoverDate && isDateInRange(date, rangeStart, hoverDate);
4000
+ const isRangeHighlight = (isInCommittedRange || isInHoverRange) && !isRangeEndpoint;
4001
+ const isToday = isSameDay(date, /* @__PURE__ */ new Date());
4002
+ return /* @__PURE__ */ React21__namespace.createElement(
4003
+ "button",
4004
+ {
4005
+ type: "button",
4006
+ ...rest,
4007
+ className: chunkDKLLPKZN_cjs.cn(
4008
+ "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
4009
+ "hover:bg-accent",
4010
+ isRangeEndpoint && "bg-primary text-primary-foreground font-semibold",
4011
+ isRangeHighlight && "bg-accent",
4012
+ !isRangeEndpoint && !isRangeHighlight && isToday && "border border-primary",
4013
+ modifiers.disabled && "cursor-not-allowed opacity-50 pointer-events-none",
4014
+ dayClassName
4015
+ ),
4016
+ onClick: (event) => {
4017
+ onClick?.(event);
4018
+ if (modifiers.disabled) return;
4019
+ handleDateSelect(date);
4020
+ },
4021
+ onMouseEnter: (event) => {
4022
+ onMouseEnter?.(event);
4023
+ if (modifiers.disabled) {
4024
+ setHoverDate(null);
4025
+ return;
4026
+ }
4027
+ setHoverDate(date);
4028
+ },
4029
+ onMouseLeave: (event) => {
4030
+ onMouseLeave?.(event);
4031
+ setHoverDate(null);
4032
+ }
4033
+ },
4034
+ children ?? date.getDate()
4035
+ );
4036
+ },
4037
+ [handleDateSelect, hoverDate, rangeEnd, rangeStart]
4038
+ );
4039
+ const hasValue = Boolean(rangeStart || rangeEnd);
4040
+ const selectedRange = rangeStart || rangeEnd ? {
4041
+ from: rangeStart ?? void 0,
4042
+ to: rangeEnd ?? void 0
4043
+ } : void 0;
4044
+ const displayValue = rangeStart && rangeEnd ? `${formatDate2(rangeStart, format)}${separator}${formatDate2(rangeEnd, format)}` : rangeStart ? formatDate2(rangeStart, format) : "";
4045
+ const combinedClassName = chunkDKLLPKZN_cjs.cn("relative", className);
4046
+ return /* @__PURE__ */ React21__namespace.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React21__namespace.createElement(
4047
+ "input",
4048
+ {
4049
+ type: "hidden",
4050
+ name: `${name}[start]`,
4051
+ value: rangeStart ? rangeStart.toISOString() : ""
4052
+ }
4053
+ ), /* @__PURE__ */ React21__namespace.createElement(
4054
+ "input",
4055
+ {
4056
+ type: "hidden",
4057
+ name: `${name}[end]`,
4058
+ value: rangeEnd ? rangeEnd.toISOString() : ""
4059
+ }
4060
+ ), /* @__PURE__ */ React21__namespace.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React21__namespace.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React21__namespace.createElement(
4061
+ "span",
4062
+ {
4063
+ className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
4064
+ "aria-hidden": "true"
4065
+ },
4066
+ /* @__PURE__ */ React21__namespace.createElement(
4067
+ "svg",
4068
+ {
4069
+ xmlns: "http://www.w3.org/2000/svg",
4070
+ width: "18",
4071
+ height: "18",
4072
+ viewBox: "0 0 24 24",
4073
+ fill: "none",
4074
+ stroke: "currentColor",
4075
+ strokeLinecap: "round",
4076
+ strokeLinejoin: "round",
4077
+ strokeWidth: "2"
4078
+ },
4079
+ /* @__PURE__ */ React21__namespace.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
4080
+ )
4081
+ ), /* @__PURE__ */ React21__namespace.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React21__namespace.createElement(
4082
+ "input",
4083
+ {
4084
+ ref: inputRef,
4085
+ id: props.id,
4086
+ type: "text",
4087
+ className: chunkDKLLPKZN_cjs.cn(
4088
+ "flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
4089
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
4090
+ "disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
4091
+ chunkDKLLPKZN_cjs.INPUT_AUTOFILL_RESET_CLASSES,
4092
+ showIcon ? "pl-10" : "pl-3",
4093
+ clearable && (rangeStart || rangeEnd) ? "pr-10" : "pr-3",
4094
+ !error && hasValue && "ring-2 ring-ring",
4095
+ error && "border-destructive ring-1 ring-destructive"
4096
+ ),
4097
+ value: displayValue,
4098
+ onClick: handleInputClick,
4099
+ onBlur: handleInputBlur,
4100
+ disabled,
4101
+ required,
4102
+ placeholder,
4103
+ "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
4104
+ "aria-describedby": props["aria-describedby"],
4105
+ "aria-required": required || props["aria-required"],
4106
+ readOnly: true
4107
+ }
4108
+ )), clearable && (rangeStart || rangeEnd) && !disabled && /* @__PURE__ */ React21__namespace.createElement(
4109
+ "button",
4110
+ {
4111
+ type: "button",
4112
+ className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
4113
+ onClick: handleClear,
4114
+ "aria-label": "Clear date range",
4115
+ tabIndex: -1
4116
+ },
4117
+ "\u2715"
4118
+ )), !disabled && /* @__PURE__ */ React21__namespace.createElement(
4119
+ PopoverContent,
4120
+ {
4121
+ align: "start",
4122
+ sideOffset: 4,
4123
+ className: "w-auto p-0",
4124
+ onOpenAutoFocus: (event) => {
4125
+ event.preventDefault();
4126
+ }
4127
+ },
4128
+ /* @__PURE__ */ React21__namespace.createElement(
4129
+ Calendar,
4130
+ {
4131
+ mode: "range",
4132
+ selected: selectedRange,
4133
+ month: selectedMonth,
4134
+ onMonthChange: setSelectedMonth,
4135
+ disabled: disabledMatchers,
4136
+ labels: {
4137
+ labelGrid: () => "Calendar",
4138
+ labelDayButton: (date) => formatDate2(date, format),
4139
+ labelPrevious: () => "Previous month",
4140
+ labelNext: () => "Next month"
4141
+ },
4142
+ components: {
4143
+ DayButton: RangeDayButton
4144
+ },
4145
+ classNames: {
4146
+ today: "border border-primary rounded-md bg-transparent"
4147
+ },
4148
+ showOutsideDays: true
4149
+ }
4150
+ ),
4151
+ rangeStart && !rangeEnd && /* @__PURE__ */ React21__namespace.createElement("div", { className: "border-t border-input px-3 py-2 text-center text-xs opacity-70" }, "Select end date")
4152
+ )));
4153
+ }
4154
+ DateRangePicker.displayName = "DateRangePicker";
4155
+
4156
+ exports.Checkbox = Checkbox2;
4157
+ exports.CheckboxGroup = CheckboxGroup;
4158
+ exports.DatePicker = DatePicker;
4159
+ exports.DateRangePicker = DateRangePicker;
4160
+ exports.FileInput = FileInput;
4161
+ exports.MultiSelect = MultiSelect;
4162
+ exports.Radio = Radio;
4163
+ exports.Select = Select2;
4164
+ exports.Switch = Switch2;
4165
+ exports.TextArea = TextArea;
4166
+ exports.TextInput = TextInput;
4167
+ exports.TimePicker = TimePicker;
4168
+ //# sourceMappingURL=chunk-ZYFTT6DB.cjs.map
4169
+ //# sourceMappingURL=chunk-ZYFTT6DB.cjs.map