@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
@@ -1,15 +1,8 @@
1
- import * as React27 from 'react';
2
- import { useState, useCallback, useMemo, useRef, useContext } from 'react';
3
- import { useObservable, useSelector } from '@legendapp/state/react';
4
- import { useMap } from '@opensite/hooks/useMap';
5
- import { Dialog as Dialog$1, Label as Label$1, Select as Select$1, Popover as Popover$1, RadioGroup as RadioGroup$1, Checkbox as Checkbox$1, Slot } from 'radix-ui';
6
- import { clsx } from 'clsx';
7
- import { twMerge } from 'tailwind-merge';
8
- import { Command as Command$1 } from 'cmdk';
9
- import { cva } from 'class-variance-authority';
10
- import { useDirection } from '@radix-ui/react-direction';
11
- import { Slot as Slot$1 } from '@radix-ui/react-slot';
12
- import { getDefaultClassNames, DayPicker } from 'react-day-picker';
1
+ import { useForm, Field } from './chunk-24RPM43T.js';
2
+ import { TextInput, TextArea, Select, MultiSelect, Radio, Checkbox, CheckboxGroup, DatePicker, DateRangePicker, TimePicker, FileInput } from './chunk-5NT5T5XY.js';
3
+ import './chunk-232KNGJI.js';
4
+ import * as React2 from 'react';
5
+ import { useState, useCallback, useMemo } from 'react';
13
6
 
14
7
  // src/integration/ContactFormSerializer.ts
15
8
  var STANDARD_FIELDS = [
@@ -150,7 +143,7 @@ function deserializeErrors(railsErrors) {
150
143
  }
151
144
  return formErrors;
152
145
  }
153
- var BlockErrorBoundary = class extends React27.Component {
146
+ var BlockErrorBoundary = class extends React2.Component {
154
147
  constructor(props) {
155
148
  super(props);
156
149
  this.state = { error: null };
@@ -170,16 +163,16 @@ var BlockErrorBoundary = class extends React27.Component {
170
163
  if (this.props.fallback) {
171
164
  return this.props.fallback(this.state.error, this.props.block);
172
165
  }
173
- return /* @__PURE__ */ React27.createElement(
166
+ return /* @__PURE__ */ React2.createElement(
174
167
  "div",
175
168
  {
176
169
  className: "block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground",
177
170
  "data-block-id": this.props.block._id,
178
171
  "data-block-type": this.props.block._type
179
172
  },
180
- /* @__PURE__ */ React27.createElement("p", { className: "font-semibold" }, "Block Render Error"),
181
- /* @__PURE__ */ React27.createElement("p", { className: "text-sm" }, "Block: ", this.props.block._name || this.props.block._id, " (", this.props.block._type, ")"),
182
- /* @__PURE__ */ React27.createElement("p", { className: "text-sm mt-1" }, this.state.error.message)
173
+ /* @__PURE__ */ React2.createElement("p", { className: "font-semibold" }, "Block Render Error"),
174
+ /* @__PURE__ */ React2.createElement("p", { className: "text-sm" }, "Block: ", this.props.block._name || this.props.block._id, " (", this.props.block._type, ")"),
175
+ /* @__PURE__ */ React2.createElement("p", { className: "text-sm mt-1" }, this.state.error.message)
183
176
  );
184
177
  }
185
178
  return this.props.children;
@@ -210,9 +203,9 @@ function createBlockAdapter(Component2, options = {}) {
210
203
  ...dataAttrs
211
204
  };
212
205
  const renderedChildren = renderChildren ? renderChildren(block._id) : children;
213
- const element = /* @__PURE__ */ React27.createElement(Component2, { ...componentProps }, renderedChildren);
206
+ const element = /* @__PURE__ */ React2.createElement(Component2, { ...componentProps }, renderedChildren);
214
207
  if (withErrorBoundary) {
215
- return /* @__PURE__ */ React27.createElement(BlockErrorBoundary, { block, fallback: errorFallback }, element);
208
+ return /* @__PURE__ */ React2.createElement(BlockErrorBoundary, { block, fallback: errorFallback }, element);
216
209
  }
217
210
  return element;
218
211
  };
@@ -488,251 +481,6 @@ function useFileUpload(options) {
488
481
  resetUpload
489
482
  };
490
483
  }
491
- function useForm(options) {
492
- const {
493
- initialValues,
494
- validationSchema,
495
- validateOn = "onBlur",
496
- revalidateOn = "onChange",
497
- onSubmit,
498
- onError,
499
- debug = false
500
- } = options;
501
- const state$ = useObservable({
502
- values: initialValues,
503
- errors: {},
504
- touched: {},
505
- isSubmitting: false,
506
- status: "idle",
507
- initialValues: { ...initialValues },
508
- // Create a copy to prevent reference sharing
509
- hasValidated: {}
510
- });
511
- const validationInProgress = useRef(/* @__PURE__ */ new Set());
512
- const [, fieldMetadataActions] = useMap();
513
- const validateField = useCallback(
514
- async (field) => {
515
- const validators = validationSchema?.[field];
516
- if (!validators) return void 0;
517
- const fieldKey = String(field);
518
- validationInProgress.current.add(fieldKey);
519
- const currentMeta = fieldMetadataActions.get(fieldKey) || {
520
- validationCount: 0
521
- };
522
- fieldMetadataActions.set(fieldKey, {
523
- lastValidated: Date.now(),
524
- validationCount: currentMeta.validationCount + 1
525
- });
526
- try {
527
- const value = state$.values[field].get();
528
- const allValues = state$.values.get();
529
- const validatorArray = Array.isArray(validators) ? validators : [validators];
530
- for (const validator of validatorArray) {
531
- const error = await validator(value, allValues);
532
- if (error) {
533
- state$.errors[field].set(error);
534
- validationInProgress.current.delete(fieldKey);
535
- return error;
536
- }
537
- }
538
- state$.errors[field].set(void 0);
539
- validationInProgress.current.delete(fieldKey);
540
- return void 0;
541
- } catch (error) {
542
- validationInProgress.current.delete(fieldKey);
543
- const errorMessage = error instanceof Error ? error.message : "Validation error";
544
- state$.errors[field].set(errorMessage);
545
- return errorMessage;
546
- }
547
- },
548
- [validationSchema, state$, fieldMetadataActions]
549
- );
550
- const validateForm = useCallback(async () => {
551
- if (!validationSchema) return {};
552
- const fields = Object.keys(validationSchema);
553
- const errors2 = {};
554
- await Promise.all(
555
- fields.map(async (field) => {
556
- const error = await validateField(field);
557
- if (error) {
558
- errors2[field] = error;
559
- }
560
- })
561
- );
562
- state$.errors.set(errors2);
563
- return errors2;
564
- }, [validationSchema, validateField, state$]);
565
- const setFieldValue = useCallback(
566
- (field, value) => {
567
- state$.values[field].set(value);
568
- const shouldRevalidate = revalidateOn === "onChange" && state$.hasValidated[String(field)].get();
569
- if (shouldRevalidate && validationSchema?.[field]) {
570
- validateField(field);
571
- }
572
- if (debug) {
573
- console.log("[useForm] setFieldValue:", { field, value });
574
- }
575
- },
576
- [state$, revalidateOn, validationSchema, validateField, debug]
577
- );
578
- const setFieldTouched = useCallback(
579
- (field, touched2) => {
580
- state$.touched[field].set(touched2);
581
- if (touched2 && validateOn === "onBlur" && validationSchema?.[field]) {
582
- state$.hasValidated[String(field)].set(true);
583
- validateField(field);
584
- }
585
- if (debug) {
586
- console.log("[useForm] setFieldTouched:", { field, touched: touched2 });
587
- }
588
- },
589
- [state$, validateOn, validationSchema, validateField, debug]
590
- );
591
- const resetForm = useCallback(() => {
592
- state$.values.set(state$.initialValues.get());
593
- state$.errors.set({});
594
- state$.touched.set({});
595
- state$.isSubmitting.set(false);
596
- state$.status.set("idle");
597
- state$.hasValidated.set({});
598
- fieldMetadataActions.clear();
599
- if (debug) {
600
- console.log("[useForm] Form reset");
601
- }
602
- }, [state$, fieldMetadataActions, debug]);
603
- const handleSubmit = useCallback(
604
- async (e) => {
605
- e?.preventDefault();
606
- if (debug) {
607
- console.log("[useForm] handleSubmit started");
608
- }
609
- state$.isSubmitting.set(true);
610
- state$.status.set("submitting");
611
- try {
612
- const errors2 = await validateForm();
613
- const hasErrors = Object.keys(errors2).length > 0;
614
- if (hasErrors) {
615
- state$.status.set("error");
616
- onError?.(errors2);
617
- if (debug) {
618
- console.log("[useForm] Validation errors:", errors2);
619
- }
620
- return;
621
- }
622
- const helpers = {
623
- setValues: (values2) => {
624
- if (typeof values2 === "function") {
625
- state$.values.set(values2(state$.values.get()));
626
- } else {
627
- state$.values.set(values2);
628
- }
629
- },
630
- setFieldValue,
631
- setErrors: (errors3) => state$.errors.set(errors3),
632
- setFieldError: (field, error) => state$.errors[field].set(error),
633
- setTouched: (touched2) => state$.touched.set(touched2),
634
- setFieldTouched,
635
- setSubmitting: (submitting) => state$.isSubmitting.set(submitting),
636
- resetForm
637
- };
638
- await onSubmit(state$.values.get(), helpers);
639
- state$.status.set("success");
640
- if (debug) {
641
- console.log("[useForm] Submit successful");
642
- }
643
- } catch (error) {
644
- state$.status.set("error");
645
- if (debug) {
646
- console.error("[useForm] Submit error:", error);
647
- }
648
- throw error;
649
- } finally {
650
- state$.isSubmitting.set(false);
651
- }
652
- },
653
- [
654
- state$,
655
- validateForm,
656
- onSubmit,
657
- onError,
658
- setFieldValue,
659
- setFieldTouched,
660
- resetForm,
661
- debug
662
- ]
663
- );
664
- const getFieldProps = useCallback(
665
- (field) => {
666
- return {
667
- name: String(field),
668
- value: state$.values[field].get(),
669
- onChange: (value) => setFieldValue(field, value),
670
- onBlur: () => setFieldTouched(field, true)
671
- };
672
- },
673
- [state$, setFieldValue, setFieldTouched]
674
- );
675
- const getFieldMeta = useCallback(
676
- (field) => {
677
- const fieldKey = String(field);
678
- const metadata = fieldMetadataActions.get(fieldKey);
679
- return {
680
- error: state$.errors[field].get(),
681
- touched: state$.touched[field].get() ?? false,
682
- isDirty: state$.values[field].get() !== state$.initialValues[field].get(),
683
- isValidating: validationInProgress.current.has(fieldKey),
684
- // Additional metadata from @opensite/hooks
685
- validationCount: metadata?.validationCount,
686
- lastValidated: metadata?.lastValidated
687
- };
688
- },
689
- [state$, fieldMetadataActions]
690
- );
691
- const values = useSelector(() => state$.values.get());
692
- const errors = useSelector(() => state$.errors.get());
693
- const touched = useSelector(() => state$.touched.get());
694
- const isSubmitting = useSelector(() => state$.isSubmitting.get());
695
- const status = useSelector(() => state$.status.get());
696
- const isValid = useSelector(() => Object.keys(state$.errors.get()).length === 0);
697
- const isDirty = useSelector(() => {
698
- const currentValues = state$.values.get();
699
- const initialValues2 = state$.initialValues.get();
700
- return Object.keys(currentValues).some(
701
- (key) => currentValues[key] !== initialValues2[key]
702
- );
703
- });
704
- return {
705
- // State
706
- values,
707
- errors,
708
- touched,
709
- isSubmitting,
710
- isValid,
711
- isDirty,
712
- status,
713
- // Actions
714
- handleSubmit,
715
- setValues: (values2) => {
716
- if (typeof values2 === "function") {
717
- state$.values.set(values2(state$.values.get()));
718
- } else {
719
- state$.values.set(values2);
720
- }
721
- },
722
- setFieldValue,
723
- setErrors: (errors2) => state$.errors.set(errors2),
724
- setFieldError: (field, error) => state$.errors[field].set(error),
725
- setTouched: (touched2) => state$.touched.set(touched2),
726
- setFieldTouched,
727
- validateForm,
728
- validateField,
729
- resetForm,
730
- getFieldProps,
731
- getFieldMeta
732
- };
733
- }
734
-
735
- // src/integration/use-contact-form.ts
736
484
  function resolveRedirect(redirectUrl) {
737
485
  const trimmed = redirectUrl.trim();
738
486
  if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
@@ -860,4370 +608,29 @@ function useContactForm(options) {
860
608
  resetSubmissionState
861
609
  };
862
610
  }
863
- var FormContext = React27.createContext(null);
864
- FormContext.displayName = "FormContext";
865
-
866
- // src/core/useField.ts
867
- function useField(options) {
868
- const { name, validate, transform } = options;
869
- const form = useContext(FormContext);
870
- if (!form) {
871
- throw new Error(
872
- "useField must be used within a FormContext. Wrap your component with <Form> or use useForm's getFieldProps instead."
873
- );
874
- }
875
- const baseFieldProps = form.getFieldProps(name);
876
- const field = {
877
- ...baseFieldProps,
878
- value: baseFieldProps.value,
879
- onChange: (value) => {
880
- const transformedValue = transform ? transform(value) : value;
881
- baseFieldProps.onChange(transformedValue);
882
- if (validate) {
883
- const result = validate(transformedValue, form.values);
884
- if (result instanceof Promise) {
885
- result.then((error) => {
886
- if (error !== void 0) {
887
- form.setFieldError(name, error);
888
- }
889
- });
890
- } else if (result !== void 0) {
891
- form.setFieldError(name, result);
892
- }
893
- }
894
- }
895
- };
896
- const meta = form.getFieldMeta(name);
897
- const helpers = {
898
- setValue: useCallback(
899
- (value) => {
900
- const transformedValue = transform ? transform(value) : value;
901
- form.setFieldValue(name, transformedValue);
902
- },
903
- [name, transform, form]
904
- ),
905
- setTouched: useCallback(
906
- (touched) => {
907
- form.setFieldTouched(name, touched);
908
- },
909
- [name, form]
910
- ),
911
- setError: useCallback(
912
- (error) => {
913
- form.setFieldError(name, error);
914
- },
915
- [name, form]
916
- )
917
- };
918
- return {
919
- field,
920
- meta,
921
- helpers
922
- };
923
- }
924
- function cn(...inputs) {
925
- return twMerge(clsx(inputs));
926
- }
927
- var INPUT_AUTOFILL_RESET_CLASSES = "autofill:bg-transparent autofill:text-foreground [&:-webkit-autofill]:[-webkit-text-fill-color:hsl(var(--foreground))] [&:-webkit-autofill]:[caret-color:hsl(var(--foreground))] [&:-webkit-autofill]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:hover]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:focus]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill]:[transition:background-color_9999s_ease-out,color_9999s_ease-out]";
928
-
929
- // src/components/ui/label.tsx
930
- function Label({
931
- className,
932
- ...props
933
- }) {
934
- return /* @__PURE__ */ React27.createElement(
935
- Label$1.Root,
936
- {
937
- "data-slot": "label",
938
- className: cn(
939
- "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
940
- className
941
- ),
942
- ...props
943
- }
944
- );
945
- }
946
-
947
- // src/components/ui/field.tsx
948
- var Field = React27.forwardRef(
949
- ({ className, orientation = "vertical", invalid = false, ...props }, ref) => {
950
- return /* @__PURE__ */ React27.createElement(
951
- "div",
952
- {
953
- ref,
954
- "data-slot": "field",
955
- "data-orientation": orientation,
956
- "data-invalid": invalid || void 0,
957
- className: cn(
958
- "flex",
959
- orientation === "horizontal" ? "items-center gap-2" : "flex-col gap-1.5",
960
- className
961
- ),
962
- ...props
963
- }
964
- );
965
- }
966
- );
967
- Field.displayName = "Field";
968
- var FieldGroup = React27.forwardRef(({ className, ...props }, ref) => {
969
- return /* @__PURE__ */ React27.createElement(
970
- "div",
971
- {
972
- ref,
973
- "data-slot": "field-group",
974
- className: cn("flex flex-col gap-4", className),
975
- ...props
976
- }
977
- );
978
- });
979
- FieldGroup.displayName = "FieldGroup";
980
- var FieldLabel = React27.forwardRef(({ className, required, children, ...props }, ref) => {
981
- return /* @__PURE__ */ React27.createElement(
982
- Label,
983
- {
984
- ref,
985
- "data-slot": "field-label",
986
- className: cn(
987
- "text-sm font-medium leading-none select-none",
988
- "peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
989
- className
990
- ),
991
- ...props
992
- },
993
- children,
994
- required && /* @__PURE__ */ React27.createElement("span", { className: "text-destructive ml-1" }, "*")
995
- );
996
- });
997
- FieldLabel.displayName = "FieldLabel";
998
- var FieldDescription = React27.forwardRef(({ className, ...props }, ref) => {
999
- return /* @__PURE__ */ React27.createElement(
1000
- "p",
1001
- {
1002
- ref,
1003
- "data-slot": "field-description",
1004
- className: cn("text-sm opacity-70", className),
1005
- ...props
1006
- }
1007
- );
1008
- });
1009
- FieldDescription.displayName = "FieldDescription";
1010
- var FieldError = React27.forwardRef(({ className, ...props }, ref) => {
1011
- return /* @__PURE__ */ React27.createElement(
1012
- "p",
1013
- {
1014
- ref,
1015
- "data-slot": "field-error",
1016
- role: "alert",
1017
- "aria-live": "polite",
1018
- className: cn("text-sm text-destructive", className),
1019
- ...props
1020
- }
1021
- );
1022
- });
1023
- FieldError.displayName = "FieldError";
1024
-
1025
- // src/core/field-feedback.tsx
1026
- var FieldFeedback = ({
1027
- errorId,
1028
- errorClassName,
1029
- error,
1030
- shouldRenderError
1031
- }) => {
1032
- const errorText = Array.isArray(error) ? error.join(", ") : error;
1033
- if (!errorText || !shouldRenderError) return null;
1034
- return /* @__PURE__ */ React27.createElement(FieldError, { id: errorId, className: errorClassName }, errorText);
1035
- };
1036
- var LabelGroup = ({
1037
- labelHtmlFor,
1038
- required = false,
1039
- variant = "label",
1040
- secondaryId,
1041
- secondary,
1042
- primary,
1043
- primaryClassName,
1044
- secondaryClassName
1045
- }) => {
1046
- const primaryClasses = cn(
1047
- "text-sm font-medium leading-snug",
1048
- variant === "legend" ? "mb-1.5" : "mb-1 block",
1049
- primaryClassName
1050
- );
1051
- const requiredIndicator = required && variant !== "label" ? /* @__PURE__ */ React27.createElement("span", { className: "text-destructive pl-0.5", "aria-label": "required" }, "*") : null;
1052
- let primaryElement = null;
1053
- if (primary) {
1054
- if (variant === "label") {
1055
- primaryElement = /* @__PURE__ */ React27.createElement(
1056
- FieldLabel,
1057
- {
1058
- htmlFor: labelHtmlFor,
1059
- required,
1060
- className: primaryClasses
1061
- },
1062
- primary
1063
- );
1064
- } else if (variant === "legend") {
1065
- primaryElement = /* @__PURE__ */ React27.createElement("legend", { "data-slot": "field-legend", className: primaryClasses }, primary, requiredIndicator);
1066
- } else {
1067
- primaryElement = /* @__PURE__ */ React27.createElement("div", { "data-slot": "field-label", className: primaryClasses }, primary, requiredIndicator);
1068
- }
1069
- }
1070
- const secondaryElement = secondary ? /* @__PURE__ */ React27.createElement(
1071
- FieldDescription,
1072
- {
1073
- id: secondaryId,
1074
- className: cn("leading-normal font-normal", secondaryClassName)
1075
- },
1076
- secondary
1077
- ) : null;
1078
- if (!primaryElement && !secondaryElement) return null;
1079
- if (variant === "legend") {
1080
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, primaryElement, secondaryElement);
1081
- }
1082
- return /* @__PURE__ */ React27.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, primaryElement, secondaryElement);
1083
- };
1084
-
1085
- // src/core/Field.tsx
1086
- function Field2({
1087
- name,
1088
- label,
1089
- description,
1090
- children,
1091
- showError = true,
611
+ function DynamicFormField({
612
+ field,
1092
613
  className,
1093
- errorClassName,
1094
- required = false,
1095
- validate
614
+ uploadProgress = {},
615
+ onFileUpload,
616
+ onFileRemove,
617
+ isUploading = false,
618
+ renderLabel = false
1096
619
  }) {
1097
- const fieldState = useField({ name, validate });
1098
- const { meta } = fieldState;
1099
- const hasError = React27.useMemo(() => {
1100
- return showError && meta.touched && meta.error ? true : false;
1101
- }, [meta?.touched, meta?.error, showError]);
1102
- const errorId = `${name}-error`;
1103
- const descriptionId = `${name}-description`;
1104
- return /* @__PURE__ */ React27.createElement(
620
+ const fieldId = field.name;
621
+ const usesGroupLegend = field.type === "radio" || field.type === "checkbox-group";
622
+ const usesInlineCheckboxLabel = field.type === "checkbox";
623
+ const shouldRenderFieldLabel = renderLabel && !usesGroupLegend && !usesInlineCheckboxLabel;
624
+ return /* @__PURE__ */ React2.createElement(
1105
625
  Field,
1106
626
  {
1107
- className,
1108
- "data-field": name,
1109
- invalid: hasError
1110
- },
1111
- /* @__PURE__ */ React27.createElement(
1112
- LabelGroup,
1113
- {
1114
- labelHtmlFor: name,
1115
- required,
1116
- variant: "label",
1117
- secondaryId: descriptionId,
1118
- secondary: description,
1119
- primary: label
1120
- }
1121
- ),
1122
- /* @__PURE__ */ React27.createElement("div", { "data-slot": "field-control" }, typeof children === "function" ? children(fieldState) : children),
1123
- /* @__PURE__ */ React27.createElement(
1124
- FieldFeedback,
1125
- {
1126
- errorId,
1127
- errorClassName,
1128
- shouldRenderError: hasError,
1129
- error: meta.error
1130
- }
1131
- )
1132
- );
1133
- }
1134
- Field2.displayName = "Field";
1135
- var Input = React27.forwardRef(
1136
- ({ className, type, ...props }, ref) => {
1137
- return /* @__PURE__ */ React27.createElement(
1138
- "input",
1139
- {
1140
- ref,
1141
- type,
1142
- "data-slot": "input",
1143
- className: cn(
1144
- // Core structure - no hardcoded colors, uses CSS variables
1145
- "flex h-9 w-full min-w-0 rounded-md border border-input",
1146
- "bg-transparent px-3 py-1 text-base shadow-sm",
1147
- "transition-colors outline-none md:text-sm",
1148
- // Focus state - uses ring-ring CSS variable (adapts to theme)
1149
- "focus-visible:ring-1 focus-visible:ring-ring",
1150
- // Error state - uses destructive CSS variables (adapts to theme)
1151
- "aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive",
1152
- // Disabled state - no color hardcoding
1153
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
1154
- // File input specific - inherits text color from parent
1155
- "file:inline-flex file:h-7 file:border-0 file:bg-transparent",
1156
- "file:text-sm file:font-medium",
1157
- // Autofill reset - prevents browser from overriding our dynamic colors
1158
- INPUT_AUTOFILL_RESET_CLASSES,
1159
- className
1160
- ),
1161
- ...props
1162
- }
1163
- );
1164
- }
1165
- );
1166
- Input.displayName = "Input";
1167
-
1168
- // src/inputs/TextInput.tsx
1169
- function TextInput({
1170
- name,
1171
- value,
1172
- onChange,
1173
- onBlur,
1174
- placeholder,
1175
- disabled = false,
1176
- required = false,
1177
- error = false,
1178
- className = "",
1179
- type = "text",
1180
- id = "text",
1181
- ...props
1182
- }) {
1183
- const handleChange = (e) => {
1184
- onChange(e.target.value);
1185
- };
1186
- const handleBlur = () => {
1187
- onBlur?.();
1188
- };
1189
- const hasValue = String(value ?? "").trim().length > 0;
1190
- return /* @__PURE__ */ React27.createElement(
1191
- Input,
1192
- {
1193
- type,
1194
- id,
1195
- name,
1196
- value: value ?? "",
1197
- onChange: handleChange,
1198
- onBlur: handleBlur,
1199
- placeholder,
1200
- disabled,
1201
- required,
1202
- className: cn(
1203
- // Valid value indicator - ring-2 when has value and no error
1204
- !error && hasValue && "ring-2 ring-ring",
1205
- // Error state - handled by Input component via aria-invalid
1206
- className
1207
- ),
1208
- "aria-invalid": error || props["aria-invalid"],
1209
- "aria-describedby": props["aria-describedby"],
1210
- "aria-required": required || props["aria-required"],
1211
- ...props
1212
- }
1213
- );
1214
- }
1215
- TextInput.displayName = "TextInput";
1216
- function Textarea({ className, ...props }) {
1217
- return /* @__PURE__ */ React27.createElement(
1218
- "textarea",
1219
- {
1220
- "data-slot": "textarea",
1221
- className: cn(
1222
- // Core structure - uses CSS variables only
1223
- "flex field-sizing-content min-h-16 w-full rounded-md border border-input",
1224
- "bg-transparent px-3 py-2 text-base shadow-xs",
1225
- "transition-[color,box-shadow] outline-none md:text-sm",
1226
- // Focus state - uses ring-ring CSS variable
1227
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1228
- // Error state - uses destructive CSS variables
1229
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
1230
- // Disabled state
1231
- "disabled:cursor-not-allowed disabled:opacity-50",
1232
- className
1233
- ),
1234
- ...props
1235
- }
1236
- );
1237
- }
1238
-
1239
- // src/inputs/TextArea.tsx
1240
- function TextArea({
1241
- name,
1242
- value,
1243
- onChange,
1244
- onBlur,
1245
- placeholder,
1246
- disabled = false,
1247
- required = false,
1248
- error = false,
1249
- className = "",
1250
- rows = 3,
1251
- cols,
1252
- maxLength,
1253
- minLength,
1254
- wrap = "soft",
1255
- ...props
1256
- }) {
1257
- const handleChange = (e) => {
1258
- onChange(e.target.value);
1259
- };
1260
- const handleBlur = () => {
1261
- onBlur?.();
1262
- };
1263
- const hasValue = String(value ?? "").trim().length > 0;
1264
- return /* @__PURE__ */ React27.createElement(
1265
- Textarea,
1266
- {
1267
- name,
1268
- value: value ?? "",
1269
- onChange: handleChange,
1270
- onBlur: handleBlur,
1271
- placeholder,
1272
- disabled,
1273
- required,
1274
- className: cn(
1275
- // Valid value indicator - ring-2 when has value and no error
1276
- !error && hasValue && "ring-2 ring-ring",
1277
- // Error state - handled by Textarea component via aria-invalid
1278
- className
1279
- ),
1280
- rows,
1281
- cols,
1282
- maxLength,
1283
- minLength,
1284
- wrap,
1285
- "aria-invalid": error || props["aria-invalid"],
1286
- "aria-describedby": props["aria-describedby"],
1287
- "aria-required": required || props["aria-required"],
1288
- ...props
1289
- }
1290
- );
1291
- }
1292
- TextArea.displayName = "TextArea";
1293
- function Checkbox({
1294
- className,
1295
- ...props
1296
- }) {
1297
- return /* @__PURE__ */ React27.createElement(
1298
- Checkbox$1.Root,
1299
- {
1300
- "data-slot": "checkbox",
1301
- className: cn(
1302
- // Core structure - uses CSS variables
1303
- "peer size-4 shrink-0 rounded-[4px] border border-input bg-transparent shadow-xs",
1304
- "transition-shadow outline-none",
1305
- // Checked state - uses primary CSS variables
1306
- "data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
1307
- "data-[state=checked]:border-primary",
1308
- // Focus state - uses ring-ring CSS variable
1309
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1310
- // Error state - uses destructive CSS variables
1311
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
1312
- // Disabled state
1313
- "disabled:cursor-not-allowed disabled:opacity-50",
1314
- className
1315
- ),
1316
- ...props
1317
- },
1318
- /* @__PURE__ */ React27.createElement(
1319
- Checkbox$1.Indicator,
1320
- {
1321
- "data-slot": "checkbox-indicator",
1322
- className: "grid place-content-center text-current transition-none"
1323
- },
1324
- /* @__PURE__ */ React27.createElement(
1325
- "svg",
1326
- {
1327
- className: "size-3.5",
1328
- viewBox: "0 0 24 24",
1329
- fill: "none",
1330
- stroke: "currentColor",
1331
- strokeWidth: "3",
1332
- strokeLinecap: "round",
1333
- strokeLinejoin: "round"
1334
- },
1335
- /* @__PURE__ */ React27.createElement("polyline", { points: "20 6 9 17 4 12" })
1336
- )
1337
- )
1338
- );
1339
- }
1340
-
1341
- // src/inputs/Checkbox.tsx
1342
- function Checkbox2({
1343
- name,
1344
- value,
1345
- onChange,
1346
- onBlur,
1347
- disabled = false,
1348
- required = false,
1349
- error = false,
1350
- className = "",
1351
- label,
1352
- description,
1353
- useChoiceCard = false,
1354
- ...props
1355
- }) {
1356
- const checkboxId = props.id || `checkbox-${name}`;
1357
- const handleCheckedChange = (checked) => {
1358
- onChange(checked);
1359
- };
1360
- const handleBlur = () => {
1361
- onBlur?.();
1362
- };
1363
- const showChoiceCard = useChoiceCard || !!description;
1364
- const checkbox = /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(
1365
- "input",
1366
- {
1367
- type: "checkbox",
1368
- name,
1369
- checked: value,
1370
- onChange: () => {
1371
- },
1372
- disabled,
1373
- required,
1374
- tabIndex: -1,
1375
- "aria-hidden": "true",
1376
- style: {
1377
- position: "absolute",
1378
- width: "1px",
1379
- height: "1px",
1380
- padding: 0,
1381
- margin: "-1px",
1382
- overflow: "hidden",
1383
- clip: "rect(0, 0, 0, 0)",
1384
- whiteSpace: "nowrap",
1385
- border: 0
1386
- }
1387
- }
1388
- ), /* @__PURE__ */ React27.createElement(
1389
- Checkbox,
1390
- {
1391
- id: checkboxId,
1392
- checked: value,
1393
- onCheckedChange: handleCheckedChange,
1394
- onBlur: handleBlur,
1395
- disabled,
1396
- "aria-invalid": error || props["aria-invalid"],
1397
- "aria-describedby": description ? `${checkboxId}-description` : props["aria-describedby"],
1398
- "aria-required": required || props["aria-required"],
1399
- ...props
1400
- }
1401
- ));
1402
- if (!label) {
1403
- return /* @__PURE__ */ React27.createElement(Field, { className }, checkbox);
1404
- }
1405
- return /* @__PURE__ */ React27.createElement(Field, { className: "gap-0", invalid: Boolean(error) }, /* @__PURE__ */ React27.createElement(
1406
- FieldLabel,
1407
- {
1408
- htmlFor: checkboxId,
1409
- className: cn(
1410
- "flex gap-3 p-3 duration-200 select-auto font-normal leading-normal",
1411
- showChoiceCard && "border rounded-lg hover:ring-2 hover:ring-ring/50",
1412
- showChoiceCard && value && "ring-2 ring-ring",
1413
- showChoiceCard && error && "border-destructive",
1414
- disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
1415
- className
1416
- )
1417
- },
1418
- /* @__PURE__ */ React27.createElement(
1419
- "div",
1420
- {
1421
- className: cn(
1422
- "flex w-full gap-3",
1423
- showChoiceCard ? "items-start" : "items-center"
1424
- )
1425
- },
1426
- checkbox,
1427
- /* @__PURE__ */ React27.createElement(Field, { className: "flex-1 gap-1" }, /* @__PURE__ */ React27.createElement("span", { className: "text-sm font-medium leading-none" }, label), description && /* @__PURE__ */ React27.createElement(
1428
- FieldDescription,
1429
- {
1430
- id: `${checkboxId}-description`,
1431
- className: "leading-snug"
1432
- },
1433
- description
1434
- ))
1435
- )
1436
- ));
1437
- }
1438
- Checkbox2.displayName = "Checkbox";
1439
- function CheckboxGroup({
1440
- name,
1441
- value = [],
1442
- onChange,
1443
- onBlur,
1444
- disabled = false,
1445
- required = false,
1446
- error = false,
1447
- className = "",
1448
- layout = "stacked",
1449
- label,
1450
- description,
1451
- options,
1452
- showSelectAll = false,
1453
- selectAllLabel = "Select all",
1454
- minSelections,
1455
- maxSelections,
1456
- renderOption,
1457
- gridColumns = 2,
1458
- ...props
1459
- }) {
1460
- const enabledOptions = options.filter((opt) => !opt.disabled);
1461
- const enabledValues = enabledOptions.map((opt) => opt.value);
1462
- const selectedEnabledCount = value.filter(
1463
- (v) => enabledValues.includes(v)
1464
- ).length;
1465
- const allSelected = selectedEnabledCount === enabledOptions.length;
1466
- const someSelected = selectedEnabledCount > 0 && !allSelected;
1467
- const useChoiceCard = React27.useMemo(() => {
1468
- if (!options) return false;
1469
- return options?.some((opt) => opt.description);
1470
- }, [options]);
1471
- const countableValue = React27.useMemo(() => {
1472
- if (value?.length > 0) {
1473
- return value.length;
1474
- }
1475
- return 0;
1476
- }, [value]);
1477
- const handleChange = (optionValue, checked) => {
1478
- const newValues = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
1479
- if (maxSelections && checked && newValues.length > maxSelections) {
1480
- return;
1481
- }
1482
- onChange(newValues);
1483
- };
1484
- const handleSelectAll = (checked) => {
1485
- if (checked) {
1486
- const allValues = enabledOptions.map((opt) => opt.value);
1487
- onChange(allValues);
1488
- } else {
1489
- onChange([]);
1490
- }
1491
- };
1492
- const handleBlur = () => {
1493
- onBlur?.();
1494
- };
1495
- const maxReached = Boolean(maxSelections && countableValue >= maxSelections);
1496
- const containerClass = React27.useMemo(() => {
1497
- return cn(
1498
- "w-full gap-3 grid grid-cols-1 border-0 m-0 p-0 min-w-0",
1499
- (layout === "grid" || layout === "inline") && "md:grid-cols-2",
627
+ name: field.name,
628
+ label: shouldRenderFieldLabel ? field.label : void 0,
629
+ description: shouldRenderFieldLabel ? field.description : void 0,
630
+ required: field.required,
1500
631
  className
1501
- );
1502
- }, [layout, className]);
1503
- const groupDescriptionId = description ? `${name}-description` : void 0;
1504
- const groupAriaDescribedBy = [props["aria-describedby"], groupDescriptionId].filter(Boolean).join(" ") || void 0;
1505
- return /* @__PURE__ */ React27.createElement(
1506
- "fieldset",
1507
- {
1508
- className: containerClass,
1509
- role: "group",
1510
- "aria-invalid": error || props["aria-invalid"],
1511
- "aria-describedby": groupAriaDescribedBy,
1512
- "aria-required": required || props["aria-required"],
1513
- "aria-label": typeof label === "string" ? label : props["aria-label"]
1514
- },
1515
- /* @__PURE__ */ React27.createElement(
1516
- LabelGroup,
1517
- {
1518
- labelHtmlFor: name,
1519
- required,
1520
- variant: "legend",
1521
- secondaryId: groupDescriptionId,
1522
- secondary: description,
1523
- primary: label
1524
- }
1525
- ),
1526
- showSelectAll && enabledOptions.length > 0 && /* @__PURE__ */ React27.createElement(
1527
- Checkbox2,
1528
- {
1529
- name: `${name}-select-all`,
1530
- id: `${name}-select-all`,
1531
- value: allSelected,
1532
- onChange: handleSelectAll,
1533
- onBlur: handleBlur,
1534
- indeterminate: someSelected,
1535
- label: selectAllLabel,
1536
- useChoiceCard,
1537
- disabled,
1538
- "aria-label": selectAllLabel
1539
- }
1540
- ),
1541
- options.map((option) => {
1542
- const isChecked = value.includes(option.value);
1543
- const isDisabled = disabled || option.disabled || maxReached && !isChecked;
1544
- return /* @__PURE__ */ React27.createElement(
1545
- Checkbox2,
1546
- {
1547
- key: option.value,
1548
- name,
1549
- id: `${name}-${option.value}`,
1550
- value: isChecked,
1551
- onChange: (checked) => handleChange(option.value, checked),
1552
- onBlur: handleBlur,
1553
- disabled: isDisabled,
1554
- required: required && minSelections ? value.length < minSelections : false,
1555
- error,
1556
- label: renderOption ? renderOption(option) : option.label,
1557
- description: renderOption ? void 0 : option.description,
1558
- useChoiceCard
1559
- }
1560
- );
1561
- }),
1562
- (minSelections || maxSelections) && /* @__PURE__ */ React27.createElement(
1563
- FieldDescription,
1564
- {
1565
- className: cn(
1566
- "p-2 rounded-lg border font-semibold mt-2 leading-snug",
1567
- minSelections && countableValue < minSelections ? "border-destructive bg-destructive/80 text-destructive-foreground" : "border-border bg-card text-card-foreground"
1568
- ),
1569
- "aria-live": "polite"
1570
- },
1571
- minSelections && countableValue < minSelections && /* @__PURE__ */ React27.createElement("span", null, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""),
1572
- maxSelections && /* @__PURE__ */ React27.createElement("span", null, countableValue, "/", maxSelections, " selected")
1573
- )
1574
- );
1575
- }
1576
- CheckboxGroup.displayName = "CheckboxGroup";
1577
- function RadioGroup({
1578
- className,
1579
- ...props
1580
- }) {
1581
- return /* @__PURE__ */ React27.createElement(
1582
- RadioGroup$1.Root,
1583
- {
1584
- "data-slot": "radio-group",
1585
- className: cn("grid gap-3", className),
1586
- ...props
1587
- }
1588
- );
1589
- }
1590
- function RadioGroupItem({
1591
- className,
1592
- ...props
1593
- }) {
1594
- return /* @__PURE__ */ React27.createElement(
1595
- RadioGroup$1.Item,
1596
- {
1597
- "data-slot": "radio-group-item",
1598
- className: cn(
1599
- // Core structure - uses CSS variables
1600
- "aspect-square size-4 shrink-0 rounded-full border border-input bg-transparent shadow-xs",
1601
- "text-primary transition-[color,box-shadow] outline-none",
1602
- // Focus state - uses ring-ring CSS variable
1603
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1604
- // Error state - uses destructive CSS variables
1605
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
1606
- // Disabled state
1607
- "disabled:cursor-not-allowed disabled:opacity-50",
1608
- className
1609
- ),
1610
- ...props
1611
- },
1612
- /* @__PURE__ */ React27.createElement(
1613
- RadioGroup$1.Indicator,
1614
- {
1615
- "data-slot": "radio-group-indicator",
1616
- className: "relative flex items-center justify-center"
1617
- },
1618
- /* @__PURE__ */ React27.createElement(
1619
- "svg",
1620
- {
1621
- className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2",
1622
- viewBox: "0 0 24 24"
1623
- },
1624
- /* @__PURE__ */ React27.createElement("circle", { cx: "12", cy: "12", r: "12" })
1625
- )
1626
- )
1627
- );
1628
- }
1629
-
1630
- // src/inputs/Radio.tsx
1631
- function Radio({
1632
- name,
1633
- value,
1634
- onChange,
1635
- onBlur,
1636
- disabled = false,
1637
- required = false,
1638
- error = false,
1639
- className = "",
1640
- layout = "stacked",
1641
- label,
1642
- description,
1643
- options,
1644
- ...props
1645
- }) {
1646
- const handleValueChange = (selectedValue) => {
1647
- onChange(selectedValue);
1648
- };
1649
- const handleBlur = () => {
1650
- onBlur?.();
1651
- };
1652
- const useChoiceCard = React27.useMemo(() => {
1653
- return options.some((option) => option.description);
1654
- }, [options]);
1655
- const groupDescriptionId = description ? `${name}-description` : void 0;
1656
- return /* @__PURE__ */ React27.createElement(Field, { className: cn("w-full", className), invalid: Boolean(error) }, (label || description) && /* @__PURE__ */ React27.createElement(Field, { className: "mb-3 gap-1" }, label && /* @__PURE__ */ React27.createElement("div", { className: "text-base font-medium leading-none" }, label), description && /* @__PURE__ */ React27.createElement(
1657
- FieldDescription,
1658
- {
1659
- id: groupDescriptionId,
1660
- className: "leading-snug"
1661
- },
1662
- description
1663
- )), /* @__PURE__ */ React27.createElement(
1664
- RadioGroup,
1665
- {
1666
- name,
1667
- value,
1668
- onValueChange: handleValueChange,
1669
- onBlur: handleBlur,
1670
- disabled,
1671
- required,
1672
- className: cn(
1673
- "gap-3",
1674
- layout === "grid" && "grid grid-cols-1 md:grid-cols-2",
1675
- layout === "inline" && "flex flex-wrap"
1676
- ),
1677
- "aria-invalid": error || props["aria-invalid"],
1678
- "aria-describedby": groupDescriptionId || props["aria-describedby"],
1679
- "aria-required": required || props["aria-required"]
1680
- },
1681
- options.map((option) => {
1682
- const isSelected = value === option.value;
1683
- const isDisabled = disabled || option.disabled;
1684
- const radioId = `${name}-${option.value}`;
1685
- const hasDescription = !!option.description;
1686
- return /* @__PURE__ */ React27.createElement(
1687
- FieldLabel,
1688
- {
1689
- key: option.value,
1690
- htmlFor: radioId,
1691
- className: cn(
1692
- "flex gap-3 p-3 duration-200 select-auto font-normal leading-normal",
1693
- useChoiceCard && "border rounded-lg hover:ring-2 hover:ring-ring/50",
1694
- useChoiceCard && isSelected && "ring-2 ring-ring",
1695
- useChoiceCard && error && "border-destructive",
1696
- isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
1697
- )
1698
- },
1699
- /* @__PURE__ */ React27.createElement(
1700
- Field,
1701
- {
1702
- orientation: "horizontal",
1703
- className: cn(
1704
- "flex w-full gap-3",
1705
- useChoiceCard ? "items-start" : "items-center"
1706
- )
1707
- },
1708
- /* @__PURE__ */ React27.createElement(
1709
- RadioGroupItem,
1710
- {
1711
- value: option.value,
1712
- id: radioId,
1713
- disabled: isDisabled,
1714
- className: "mt-0.5",
1715
- "aria-describedby": hasDescription ? `${radioId}-description` : void 0
1716
- }
1717
- ),
1718
- /* @__PURE__ */ React27.createElement(Field, { className: "flex-1 gap-1" }, /* @__PURE__ */ React27.createElement("span", { className: "text-sm font-medium leading-none" }, option.label), option.description && /* @__PURE__ */ React27.createElement(
1719
- FieldDescription,
1720
- {
1721
- id: `${radioId}-description`,
1722
- className: "leading-snug"
1723
- },
1724
- option.description
1725
- ))
1726
- )
1727
- );
1728
- })
1729
- ));
1730
- }
1731
- Radio.displayName = "Radio";
1732
- function Select({
1733
- ...props
1734
- }) {
1735
- return /* @__PURE__ */ React27.createElement(Select$1.Root, { "data-slot": "select", ...props });
1736
- }
1737
- function SelectGroup({
1738
- ...props
1739
- }) {
1740
- return /* @__PURE__ */ React27.createElement(Select$1.Group, { "data-slot": "select-group", ...props });
1741
- }
1742
- function SelectValue({
1743
- ...props
1744
- }) {
1745
- return /* @__PURE__ */ React27.createElement(Select$1.Value, { "data-slot": "select-value", ...props });
1746
- }
1747
- function SelectTrigger({
1748
- className,
1749
- size = "default",
1750
- children,
1751
- ...props
1752
- }) {
1753
- return /* @__PURE__ */ React27.createElement(
1754
- Select$1.Trigger,
1755
- {
1756
- "data-slot": "select-trigger",
1757
- "data-size": size,
1758
- className: cn(
1759
- // Core structure - uses CSS variables
1760
- "flex w-fit items-center justify-between gap-2 rounded-md border border-input",
1761
- "bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs",
1762
- "transition-[color,box-shadow] outline-none",
1763
- // Focus state
1764
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1765
- // Error state
1766
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
1767
- // Disabled state
1768
- "disabled:cursor-not-allowed disabled:opacity-50",
1769
- // Size variants
1770
- "data-[size=default]:h-9 data-[size=sm]:h-8",
1771
- // Value styling
1772
- "*:data-[slot=select-value]:line-clamp-1",
1773
- "*:data-[slot=select-value]:flex",
1774
- "*:data-[slot=select-value]:items-center",
1775
- "*:data-[slot=select-value]:gap-2",
1776
- // SVG styling
1777
- "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1778
- className
1779
- ),
1780
- ...props
1781
- },
1782
- children,
1783
- /* @__PURE__ */ React27.createElement(Select$1.Icon, { asChild: true }, /* @__PURE__ */ React27.createElement(
1784
- "svg",
1785
- {
1786
- className: "size-4 opacity-50",
1787
- viewBox: "0 0 24 24",
1788
- fill: "none",
1789
- stroke: "currentColor",
1790
- strokeWidth: "2",
1791
- strokeLinecap: "round",
1792
- strokeLinejoin: "round"
1793
- },
1794
- /* @__PURE__ */ React27.createElement("polyline", { points: "6 9 12 15 18 9" })
1795
- ))
1796
- );
1797
- }
1798
- function SelectContent({
1799
- className,
1800
- children,
1801
- position = "item-aligned",
1802
- align = "center",
1803
- ...props
1804
- }) {
1805
- return /* @__PURE__ */ React27.createElement(Select$1.Portal, null, /* @__PURE__ */ React27.createElement(
1806
- Select$1.Content,
1807
- {
1808
- "data-slot": "select-content",
1809
- className: cn(
1810
- "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",
1811
- 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",
1812
- className
1813
- ),
1814
- position,
1815
- align,
1816
- ...props
1817
- },
1818
- /* @__PURE__ */ React27.createElement(SelectScrollUpButton, null),
1819
- /* @__PURE__ */ React27.createElement(
1820
- Select$1.Viewport,
1821
- {
1822
- className: cn(
1823
- "p-1",
1824
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
1825
- )
1826
- },
1827
- children
1828
- ),
1829
- /* @__PURE__ */ React27.createElement(SelectScrollDownButton, null)
1830
- ));
1831
- }
1832
- function SelectLabel({
1833
- className,
1834
- ...props
1835
- }) {
1836
- return /* @__PURE__ */ React27.createElement(
1837
- Select$1.Label,
1838
- {
1839
- "data-slot": "select-label",
1840
- className: cn("px-2 py-1.5 text-xs opacity-70", className),
1841
- ...props
1842
- }
1843
- );
1844
- }
1845
- function SelectItem({
1846
- className,
1847
- children,
1848
- ...props
1849
- }) {
1850
- return /* @__PURE__ */ React27.createElement(
1851
- Select$1.Item,
1852
- {
1853
- "data-slot": "select-item",
1854
- className: cn(
1855
- // Core structure - inherits text color
1856
- "relative flex w-full cursor-default items-center gap-2 rounded-sm",
1857
- "py-1.5 pr-8 pl-2 text-sm outline-hidden select-none",
1858
- // Focus state - uses accent CSS variable
1859
- "focus:bg-accent focus:text-accent-foreground",
1860
- // Disabled state
1861
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1862
- // SVG styling
1863
- "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1864
- // Span styling
1865
- "*:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
1866
- className
1867
- ),
1868
- ...props
1869
- },
1870
- /* @__PURE__ */ React27.createElement(
1871
- "span",
1872
- {
1873
- "data-slot": "select-item-indicator",
1874
- className: "absolute right-2 flex size-3.5 items-center justify-center"
1875
- },
1876
- /* @__PURE__ */ React27.createElement(Select$1.ItemIndicator, null, /* @__PURE__ */ React27.createElement(
1877
- "svg",
1878
- {
1879
- className: "size-4",
1880
- viewBox: "0 0 24 24",
1881
- fill: "none",
1882
- stroke: "currentColor",
1883
- strokeWidth: "3",
1884
- strokeLinecap: "round",
1885
- strokeLinejoin: "round"
1886
- },
1887
- /* @__PURE__ */ React27.createElement("polyline", { points: "20 6 9 17 4 12" })
1888
- ))
1889
- ),
1890
- /* @__PURE__ */ React27.createElement(Select$1.ItemText, null, children)
1891
- );
1892
- }
1893
- function SelectScrollUpButton({
1894
- className,
1895
- ...props
1896
- }) {
1897
- return /* @__PURE__ */ React27.createElement(
1898
- Select$1.ScrollUpButton,
1899
- {
1900
- "data-slot": "select-scroll-up-button",
1901
- className: cn(
1902
- "flex cursor-default items-center justify-center py-1",
1903
- className
1904
- ),
1905
- ...props
1906
- },
1907
- /* @__PURE__ */ React27.createElement(
1908
- "svg",
1909
- {
1910
- className: "size-4",
1911
- viewBox: "0 0 24 24",
1912
- fill: "none",
1913
- stroke: "currentColor",
1914
- strokeWidth: "2",
1915
- strokeLinecap: "round",
1916
- strokeLinejoin: "round"
1917
- },
1918
- /* @__PURE__ */ React27.createElement("polyline", { points: "18 15 12 9 6 15" })
1919
- )
1920
- );
1921
- }
1922
- function SelectScrollDownButton({
1923
- className,
1924
- ...props
1925
- }) {
1926
- return /* @__PURE__ */ React27.createElement(
1927
- Select$1.ScrollDownButton,
1928
- {
1929
- "data-slot": "select-scroll-down-button",
1930
- className: cn(
1931
- "flex cursor-default items-center justify-center py-1",
1932
- className
1933
- ),
1934
- ...props
1935
- },
1936
- /* @__PURE__ */ React27.createElement(
1937
- "svg",
1938
- {
1939
- className: "size-4",
1940
- viewBox: "0 0 24 24",
1941
- fill: "none",
1942
- stroke: "currentColor",
1943
- strokeWidth: "2",
1944
- strokeLinecap: "round",
1945
- strokeLinejoin: "round"
1946
- },
1947
- /* @__PURE__ */ React27.createElement("polyline", { points: "6 9 12 15 18 9" })
1948
- )
1949
- );
1950
- }
1951
-
1952
- // src/inputs/Select.tsx
1953
- function Select2({
1954
- name,
1955
- value,
1956
- onChange,
1957
- onBlur,
1958
- onFocus,
1959
- disabled = false,
1960
- required = false,
1961
- error = false,
1962
- className = "",
1963
- placeholder = "Select...",
1964
- options = [],
1965
- optionGroups = [],
1966
- renderOption,
1967
- ...props
1968
- }) {
1969
- const [hasInteracted, setHasInteracted] = React27.useState(false);
1970
- const allOptions = React27.useMemo(() => {
1971
- if (optionGroups.length > 0) {
1972
- return optionGroups.flatMap((group) => group.options);
1973
- }
1974
- return options;
1975
- }, [options, optionGroups]);
1976
- const hasValue = Boolean(value);
1977
- const selectValue = value ? String(value) : void 0;
1978
- const handleValueChange = (newValue) => {
1979
- onChange(newValue);
1980
- };
1981
- const handleOpenChange = (open) => {
1982
- if (open) {
1983
- if (!hasInteracted) {
1984
- setHasInteracted(true);
1985
- }
1986
- onFocus?.();
1987
- } else if (hasInteracted) {
1988
- onBlur?.();
1989
- }
1990
- };
1991
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(
1992
- "input",
1993
- {
1994
- type: "hidden",
1995
- name,
1996
- value: value ?? "",
1997
- disabled,
1998
- required,
1999
- tabIndex: -1,
2000
- "aria-hidden": "true",
2001
- style: {
2002
- position: "absolute",
2003
- width: "1px",
2004
- height: "1px",
2005
- padding: "0",
2006
- margin: "-1px",
2007
- overflow: "hidden",
2008
- clip: "rect(0, 0, 0, 0)",
2009
- whiteSpace: "nowrap",
2010
- border: "0"
2011
- }
2012
- }
2013
- ), /* @__PURE__ */ React27.createElement(
2014
- Select,
2015
- {
2016
- value: selectValue,
2017
- onValueChange: handleValueChange,
2018
- onOpenChange: handleOpenChange,
2019
- disabled
2020
- },
2021
- /* @__PURE__ */ React27.createElement(
2022
- SelectTrigger,
2023
- {
2024
- className: cn(
2025
- // Valid value indicator - ring-2 when has value and no error
2026
- !error && hasValue && "ring-2 ring-ring",
2027
- // Error state - handled by SelectTrigger via aria-invalid
2028
- className
2029
- ),
2030
- "aria-invalid": error || props["aria-invalid"],
2031
- "aria-describedby": props["aria-describedby"],
2032
- "aria-required": required || props["aria-required"]
2033
- },
2034
- /* @__PURE__ */ React27.createElement(SelectValue, { placeholder })
2035
- ),
2036
- /* @__PURE__ */ React27.createElement(SelectContent, null, optionGroups.length > 0 ? (
2037
- // Render grouped options
2038
- optionGroups.map((group, groupIndex) => /* @__PURE__ */ React27.createElement(SelectGroup, { key: groupIndex }, /* @__PURE__ */ React27.createElement(SelectLabel, null, group.label), group.options.map((option) => /* @__PURE__ */ React27.createElement(
2039
- SelectItem,
2040
- {
2041
- key: option.value,
2042
- value: option.value,
2043
- disabled: option.disabled
2044
- },
2045
- renderOption ? renderOption(option) : option.label
2046
- ))))
2047
- ) : (
2048
- // Render flat options
2049
- allOptions.map((option) => /* @__PURE__ */ React27.createElement(
2050
- SelectItem,
2051
- {
2052
- key: option.value,
2053
- value: option.value,
2054
- disabled: option.disabled
2055
- },
2056
- renderOption ? renderOption(option) : option.label
2057
- ))
2058
- ))
2059
- ));
2060
- }
2061
- Select2.displayName = "Select";
2062
- var buttonVariants = cva(
2063
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
2064
- {
2065
- variants: {
2066
- variant: {
2067
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
2068
- destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20",
2069
- outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
2070
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
2071
- ghost: "hover:bg-accent hover:text-accent-foreground",
2072
- link: "text-primary underline-offset-4 hover:underline"
2073
- },
2074
- size: {
2075
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
2076
- xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
2077
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
2078
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
2079
- icon: "size-9",
2080
- "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
2081
- "icon-sm": "size-8",
2082
- "icon-lg": "size-10"
2083
- }
2084
- },
2085
- defaultVariants: {
2086
- variant: "default",
2087
- size: "default"
2088
- }
2089
- }
2090
- );
2091
- function Button({
2092
- className,
2093
- variant = "default",
2094
- size = "default",
2095
- asChild = false,
2096
- ...props
2097
- }) {
2098
- const Comp = asChild ? Slot.Root : "button";
2099
- return /* @__PURE__ */ React27.createElement(
2100
- Comp,
2101
- {
2102
- "data-slot": "button",
2103
- "data-variant": variant,
2104
- "data-size": size,
2105
- className: cn(buttonVariants({ variant, size, className })),
2106
- ...props
2107
- }
2108
- );
2109
- }
2110
-
2111
- // src/components/ui/dialog.tsx
2112
- function Dialog({
2113
- ...props
2114
- }) {
2115
- return /* @__PURE__ */ React27.createElement(Dialog$1.Root, { "data-slot": "dialog", ...props });
2116
- }
2117
- function DialogPortal({
2118
- ...props
2119
- }) {
2120
- return /* @__PURE__ */ React27.createElement(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
2121
- }
2122
- function DialogClose({
2123
- ...props
2124
- }) {
2125
- return /* @__PURE__ */ React27.createElement(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
2126
- }
2127
- var DialogOverlay = React27.forwardRef(({ className, ...props }, ref) => {
2128
- return /* @__PURE__ */ React27.createElement(
2129
- Dialog$1.Overlay,
2130
- {
2131
- ref,
2132
- "data-slot": "dialog-overlay",
2133
- className: cn(
2134
- "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",
2135
- className
2136
- ),
2137
- ...props
2138
- }
2139
- );
2140
- });
2141
- DialogOverlay.displayName = Dialog$1.Overlay.displayName;
2142
- var DialogContent = React27.forwardRef(({ className, children, showCloseButton = true, ...props }, ref) => {
2143
- return /* @__PURE__ */ React27.createElement(DialogPortal, { "data-slot": "dialog-portal" }, /* @__PURE__ */ React27.createElement(DialogOverlay, null), /* @__PURE__ */ React27.createElement(
2144
- Dialog$1.Content,
2145
- {
2146
- ref,
2147
- "data-slot": "dialog-content",
2148
- className: cn(
2149
- "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",
2150
- className
2151
- ),
2152
- ...props
2153
- },
2154
- children,
2155
- showCloseButton && /* @__PURE__ */ React27.createElement(
2156
- Dialog$1.Close,
2157
- {
2158
- "data-slot": "dialog-close",
2159
- 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"
2160
- },
2161
- /* @__PURE__ */ React27.createElement(
2162
- "svg",
2163
- {
2164
- className: "size-4",
2165
- viewBox: "0 0 24 24",
2166
- fill: "none",
2167
- stroke: "currentColor",
2168
- strokeWidth: "2",
2169
- strokeLinecap: "round",
2170
- strokeLinejoin: "round"
2171
- },
2172
- /* @__PURE__ */ React27.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2173
- /* @__PURE__ */ React27.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2174
- ),
2175
- /* @__PURE__ */ React27.createElement("span", { className: "sr-only" }, "Close")
2176
- )
2177
- ));
2178
- });
2179
- DialogContent.displayName = Dialog$1.Content.displayName;
2180
- function DialogHeader({ className, ...props }) {
2181
- return /* @__PURE__ */ React27.createElement(
2182
- "div",
2183
- {
2184
- "data-slot": "dialog-header",
2185
- className: cn("flex flex-col gap-2 text-center sm:text-left", className),
2186
- ...props
2187
- }
2188
- );
2189
- }
2190
- function DialogTitle({
2191
- className,
2192
- ...props
2193
- }) {
2194
- return /* @__PURE__ */ React27.createElement(
2195
- Dialog$1.Title,
2196
- {
2197
- "data-slot": "dialog-title",
2198
- className: cn("text-lg leading-none font-semibold", className),
2199
- ...props
2200
- }
2201
- );
2202
- }
2203
-
2204
- // src/components/ui/command.tsx
2205
- function Command({
2206
- className,
2207
- ...props
2208
- }) {
2209
- return /* @__PURE__ */ React27.createElement(
2210
- Command$1,
2211
- {
2212
- "data-slot": "command",
2213
- className: cn(
2214
- "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
2215
- className
2216
- ),
2217
- ...props
2218
- }
2219
- );
2220
- }
2221
- function CommandInput({
2222
- className,
2223
- ...props
2224
- }) {
2225
- return /* @__PURE__ */ React27.createElement(
2226
- "div",
2227
- {
2228
- "data-slot": "command-input-wrapper",
2229
- className: "flex h-9 items-center gap-2 border-b px-3"
2230
- },
2231
- /* @__PURE__ */ React27.createElement(
2232
- "svg",
2233
- {
2234
- className: "size-4 shrink-0 opacity-50",
2235
- viewBox: "0 0 24 24",
2236
- fill: "none",
2237
- stroke: "currentColor",
2238
- strokeWidth: "2",
2239
- strokeLinecap: "round",
2240
- strokeLinejoin: "round"
2241
- },
2242
- /* @__PURE__ */ React27.createElement("circle", { cx: "11", cy: "11", r: "8" }),
2243
- /* @__PURE__ */ React27.createElement("path", { d: "m21 21-4.3-4.3" })
2244
- ),
2245
- /* @__PURE__ */ React27.createElement(
2246
- Command$1.Input,
2247
- {
2248
- "data-slot": "command-input",
2249
- className: cn(
2250
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
2251
- className
2252
- ),
2253
- ...props
2254
- }
2255
- )
2256
- );
2257
- }
2258
- function CommandList({
2259
- className,
2260
- ...props
2261
- }) {
2262
- return /* @__PURE__ */ React27.createElement(
2263
- Command$1.List,
2264
- {
2265
- "data-slot": "command-list",
2266
- className: cn(
2267
- "max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
2268
- className
2269
- ),
2270
- ...props
2271
- }
2272
- );
2273
- }
2274
- function CommandEmpty({
2275
- ...props
2276
- }) {
2277
- return /* @__PURE__ */ React27.createElement(
2278
- Command$1.Empty,
2279
- {
2280
- "data-slot": "command-empty",
2281
- className: "py-6 text-center text-sm",
2282
- ...props
2283
- }
2284
- );
2285
- }
2286
- function CommandGroup({
2287
- className,
2288
- ...props
2289
- }) {
2290
- return /* @__PURE__ */ React27.createElement(
2291
- Command$1.Group,
2292
- {
2293
- "data-slot": "command-group",
2294
- className: cn(
2295
- "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",
2296
- className
2297
- ),
2298
- ...props
2299
- }
2300
- );
2301
- }
2302
- function Popover({
2303
- ...props
2304
- }) {
2305
- return /* @__PURE__ */ React27.createElement(Popover$1.Root, { "data-slot": "popover", ...props });
2306
- }
2307
- function PopoverTrigger({
2308
- ...props
2309
- }) {
2310
- return /* @__PURE__ */ React27.createElement(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
2311
- }
2312
- function PopoverContent({
2313
- className,
2314
- align = "center",
2315
- sideOffset = 4,
2316
- ...props
2317
- }) {
2318
- return /* @__PURE__ */ React27.createElement(Popover$1.Portal, null, /* @__PURE__ */ React27.createElement(
2319
- Popover$1.Content,
2320
- {
2321
- "data-slot": "popover-content",
2322
- align,
2323
- sideOffset,
2324
- className: cn(
2325
- "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",
2326
- className
2327
- ),
2328
- ...props
2329
- }
2330
- ));
2331
- }
2332
-
2333
- // src/inputs/MultiSelect.tsx
2334
- function ensureResizeObserver() {
2335
- if (typeof window === "undefined") return;
2336
- const windowWithResizeObserver = window;
2337
- if (windowWithResizeObserver.ResizeObserver) return;
2338
- windowWithResizeObserver.ResizeObserver = class ResizeObserverMock {
2339
- observe() {
2340
- }
2341
- unobserve() {
2342
- }
2343
- disconnect() {
2344
- }
2345
- };
2346
- if (typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.scrollIntoView !== "function") {
2347
- HTMLElement.prototype.scrollIntoView = () => {
2348
- };
2349
- }
2350
- }
2351
- function optionLabelText(option) {
2352
- if (typeof option.label === "string") {
2353
- return option.label;
2354
- }
2355
- return String(option.label);
2356
- }
2357
- function MultiSelect({
2358
- name,
2359
- value = [],
2360
- onChange,
2361
- onBlur,
2362
- onFocus,
2363
- disabled = false,
2364
- required = false,
2365
- error = false,
2366
- className = "",
2367
- placeholder = "Select...",
2368
- searchable = true,
2369
- clearable = true,
2370
- loading = false,
2371
- maxSelections,
2372
- showSelectAll = false,
2373
- options = [],
2374
- optionGroups = [],
2375
- renderOption,
2376
- renderValue,
2377
- ...props
2378
- }) {
2379
- const [isOpen, setIsOpen] = React27.useState(false);
2380
- const [searchQuery, setSearchQuery] = React27.useState("");
2381
- const [focusedIndex, setFocusedIndex] = React27.useState(-1);
2382
- const [hasInteracted, setHasInteracted] = React27.useState(false);
2383
- const triggerRef = React27.useRef(null);
2384
- const dropdownId = `${name}-dropdown`;
2385
- const searchInputId = `${name}-search`;
2386
- ensureResizeObserver();
2387
- const allOptions = React27.useMemo(() => {
2388
- if (optionGroups.length > 0) {
2389
- return optionGroups.flatMap((group) => group.options);
2390
- }
2391
- return options;
2392
- }, [options, optionGroups]);
2393
- const filteredOptions = React27.useMemo(() => {
2394
- if (!searchQuery.trim()) {
2395
- return allOptions;
2396
- }
2397
- const query = searchQuery.toLowerCase();
2398
- return allOptions.filter(
2399
- (option) => optionLabelText(option).toLowerCase().includes(query)
2400
- );
2401
- }, [allOptions, searchQuery]);
2402
- const selectedOptions = React27.useMemo(() => {
2403
- return allOptions.filter((option) => value.includes(option.value));
2404
- }, [allOptions, value]);
2405
- const hasValue = value.length > 0;
2406
- const isMaxReached = React27.useMemo(() => {
2407
- return maxSelections !== void 0 && value.length >= maxSelections;
2408
- }, [maxSelections, value.length]);
2409
- const getEnabledOptions = React27.useCallback(() => {
2410
- return filteredOptions.filter(
2411
- (option) => !option.disabled && (!isMaxReached || value.includes(option.value))
2412
- );
2413
- }, [filteredOptions, isMaxReached, value]);
2414
- React27.useEffect(() => {
2415
- if (!isOpen) return;
2416
- if (!searchable) return;
2417
- const id = window.setTimeout(() => {
2418
- const searchInput = document.getElementById(
2419
- searchInputId
2420
- );
2421
- searchInput?.focus();
2422
- }, 0);
2423
- return () => {
2424
- window.clearTimeout(id);
2425
- };
2426
- }, [isOpen, searchable, searchInputId]);
2427
- const handleToggleOption = React27.useCallback(
2428
- (optionValue) => {
2429
- const isSelected = value.includes(optionValue);
2430
- if (isSelected) {
2431
- onChange(value.filter((entry) => entry !== optionValue));
2432
- } else if (!isMaxReached) {
2433
- onChange([...value, optionValue]);
2434
- }
2435
- setSearchQuery("");
2436
- },
2437
- [isMaxReached, onChange, value]
2438
- );
2439
- const handleSelectAll = React27.useCallback(() => {
2440
- const enabledOptions = filteredOptions.filter((option) => !option.disabled);
2441
- onChange(enabledOptions.map((option) => option.value));
2442
- setSearchQuery("");
2443
- }, [filteredOptions, onChange]);
2444
- const handleClearAll = React27.useCallback(
2445
- (e) => {
2446
- e.stopPropagation();
2447
- onChange([]);
2448
- setSearchQuery("");
2449
- setFocusedIndex(-1);
2450
- },
2451
- [onChange]
2452
- );
2453
- const handleRemoveValue = React27.useCallback(
2454
- (optionValue, e) => {
2455
- e.stopPropagation();
2456
- onChange(value.filter((entry) => entry !== optionValue));
2457
- },
2458
- [onChange, value]
2459
- );
2460
- const handleOpenChange = React27.useCallback(
2461
- (nextOpen) => {
2462
- if (disabled) {
2463
- setIsOpen(false);
2464
- return;
2465
- }
2466
- if (nextOpen) {
2467
- if (!hasInteracted) {
2468
- setHasInteracted(true);
2469
- }
2470
- setIsOpen(true);
2471
- onFocus?.();
2472
- return;
2473
- }
2474
- if (isOpen && hasInteracted) {
2475
- onBlur?.();
2476
- }
2477
- setIsOpen(false);
2478
- setSearchQuery("");
2479
- setFocusedIndex(-1);
2480
- },
2481
- [disabled, hasInteracted, isOpen, onBlur, onFocus]
2482
- );
2483
- const handleTriggerBlur = React27.useCallback(() => {
2484
- if (!isOpen) {
2485
- onBlur?.();
2486
- }
2487
- }, [isOpen, onBlur]);
2488
- const handleKeyDown = React27.useCallback(
2489
- (event) => {
2490
- if (disabled) return;
2491
- const enabledOptions = getEnabledOptions();
2492
- switch (event.key) {
2493
- case "ArrowDown": {
2494
- event.preventDefault();
2495
- if (!isOpen) {
2496
- setHasInteracted(true);
2497
- setIsOpen(true);
2498
- onFocus?.();
2499
- if (enabledOptions.length > 0) {
2500
- setFocusedIndex(filteredOptions.indexOf(enabledOptions[0]));
2501
- }
2502
- return;
2503
- }
2504
- if (enabledOptions.length === 0) return;
2505
- const currentOption = filteredOptions[focusedIndex];
2506
- const currentEnabledIndex = enabledOptions.findIndex(
2507
- (option) => option === currentOption
2508
- );
2509
- const nextEnabledIndex = currentEnabledIndex === -1 ? 0 : (currentEnabledIndex + 1) % enabledOptions.length;
2510
- setFocusedIndex(filteredOptions.indexOf(enabledOptions[nextEnabledIndex]));
2511
- break;
2512
- }
2513
- case "ArrowUp": {
2514
- event.preventDefault();
2515
- if (!isOpen || enabledOptions.length === 0) return;
2516
- const currentOption = filteredOptions[focusedIndex];
2517
- const currentEnabledIndex = enabledOptions.findIndex(
2518
- (option) => option === currentOption
2519
- );
2520
- const previousEnabledIndex = currentEnabledIndex === -1 ? enabledOptions.length - 1 : (currentEnabledIndex - 1 + enabledOptions.length) % enabledOptions.length;
2521
- setFocusedIndex(
2522
- filteredOptions.indexOf(enabledOptions[previousEnabledIndex])
2523
- );
2524
- break;
2525
- }
2526
- case "Enter": {
2527
- event.preventDefault();
2528
- if (isOpen && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
2529
- const focusedOption = filteredOptions[focusedIndex];
2530
- const optionDisabled = focusedOption.disabled || isMaxReached && !value.includes(focusedOption.value);
2531
- if (!optionDisabled) {
2532
- handleToggleOption(focusedOption.value);
2533
- }
2534
- return;
2535
- }
2536
- if (!isOpen) {
2537
- setHasInteracted(true);
2538
- setIsOpen(true);
2539
- onFocus?.();
2540
- }
2541
- break;
2542
- }
2543
- case "Escape": {
2544
- if (!isOpen) return;
2545
- event.preventDefault();
2546
- setIsOpen(false);
2547
- setSearchQuery("");
2548
- setFocusedIndex(-1);
2549
- break;
2550
- }
2551
- case " ": {
2552
- if (isOpen && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {
2553
- event.preventDefault();
2554
- const focusedOption = filteredOptions[focusedIndex];
2555
- const optionDisabled = focusedOption.disabled || isMaxReached && !value.includes(focusedOption.value);
2556
- if (!optionDisabled) {
2557
- handleToggleOption(focusedOption.value);
2558
- }
2559
- return;
2560
- }
2561
- if (!isOpen && !searchable) {
2562
- event.preventDefault();
2563
- setHasInteracted(true);
2564
- setIsOpen(true);
2565
- onFocus?.();
2566
- }
2567
- break;
2568
- }
2569
- }
2570
- },
2571
- [
2572
- disabled,
2573
- filteredOptions,
2574
- focusedIndex,
2575
- getEnabledOptions,
2576
- handleToggleOption,
2577
- isMaxReached,
2578
- isOpen,
2579
- onFocus,
2580
- searchable,
2581
- value
2582
- ]
2583
- );
2584
- const combinedClassName = cn("relative w-full", className);
2585
- return /* @__PURE__ */ React27.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React27.createElement(
2586
- "select",
2587
- {
2588
- name,
2589
- value,
2590
- onChange: () => {
2591
- },
2592
- disabled,
2593
- required,
2594
- "aria-hidden": "true",
2595
- tabIndex: -1,
2596
- style: { display: "none" },
2597
- multiple: true
2598
- },
2599
- /* @__PURE__ */ React27.createElement("option", { value: "" }, "Select..."),
2600
- allOptions.map((option) => /* @__PURE__ */ React27.createElement("option", { key: option.value, value: option.value }, optionLabelText(option)))
2601
- ), /* @__PURE__ */ React27.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React27.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React27.createElement(
2602
- "div",
2603
- {
2604
- ref: triggerRef,
2605
- className: cn(
2606
- "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",
2607
- "cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
2608
- !error && hasValue && "ring-2 ring-ring",
2609
- disabled && "cursor-not-allowed opacity-50 pointer-events-none",
2610
- error && "border-destructive ring-1 ring-destructive"
2611
- ),
2612
- onKeyDown: handleKeyDown,
2613
- onBlur: handleTriggerBlur,
2614
- role: "combobox",
2615
- "aria-expanded": isOpen,
2616
- "aria-controls": dropdownId,
2617
- "aria-invalid": error || props["aria-invalid"],
2618
- "aria-describedby": props["aria-describedby"],
2619
- "aria-required": required || props["aria-required"],
2620
- "aria-disabled": disabled,
2621
- tabIndex: disabled ? -1 : 0
2622
- },
2623
- /* @__PURE__ */ React27.createElement("div", { className: "flex flex-1 items-center overflow-hidden" }, selectedOptions.length > 0 ? /* @__PURE__ */ React27.createElement("div", { className: "flex flex-wrap gap-1" }, selectedOptions.map((option) => /* @__PURE__ */ React27.createElement(
2624
- "span",
2625
- {
2626
- key: option.value,
2627
- className: "inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs font-medium"
2628
- },
2629
- renderValue ? renderValue(option) : /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement("span", { className: "max-w-40 overflow-hidden text-ellipsis whitespace-nowrap" }, option.label), !disabled && /* @__PURE__ */ React27.createElement(
2630
- "button",
2631
- {
2632
- type: "button",
2633
- 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",
2634
- onClick: (e) => handleRemoveValue(option.value, e),
2635
- "aria-label": `Remove ${optionLabelText(option)}`,
2636
- tabIndex: -1
2637
- },
2638
- "\u2715"
2639
- ))
2640
- ))) : /* @__PURE__ */ React27.createElement("span", { className: "relative" }, placeholder)),
2641
- /* @__PURE__ */ React27.createElement("div", { className: "ml-2 flex items-center gap-1" }, loading && /* @__PURE__ */ React27.createElement("span", { className: "text-xs" }, "\u23F3"), clearable && value.length > 0 && !disabled && !loading && /* @__PURE__ */ React27.createElement(
2642
- "button",
2643
- {
2644
- type: "button",
2645
- 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",
2646
- onClick: handleClearAll,
2647
- "aria-label": "Clear all selections",
2648
- tabIndex: -1
2649
- },
2650
- "\u2715"
2651
- ), /* @__PURE__ */ React27.createElement("span", { className: "text-xs leading-none", "aria-hidden": "true" }, isOpen ? "\u25B2" : "\u25BC"))
2652
- )), isOpen && /* @__PURE__ */ React27.createElement(
2653
- PopoverContent,
2654
- {
2655
- id: dropdownId,
2656
- align: "start",
2657
- sideOffset: 4,
2658
- className: "w-full min-w-[var(--radix-popover-trigger-width)] p-0",
2659
- onOpenAutoFocus: (event) => {
2660
- event.preventDefault();
2661
- }
2662
- },
2663
- /* @__PURE__ */ React27.createElement(
2664
- Command,
2665
- {
2666
- shouldFilter: false,
2667
- className: "max-h-80",
2668
- onKeyDown: handleKeyDown
2669
- },
2670
- searchable && /* @__PURE__ */ React27.createElement(
2671
- CommandInput,
2672
- {
2673
- id: searchInputId,
2674
- className: cn(INPUT_AUTOFILL_RESET_CLASSES),
2675
- placeholder: "Search...",
2676
- value: searchQuery,
2677
- onValueChange: (nextValue) => {
2678
- setSearchQuery(nextValue);
2679
- setFocusedIndex(0);
2680
- },
2681
- "aria-label": "Search options"
2682
- }
2683
- ),
2684
- showSelectAll && filteredOptions.length > 0 && /* @__PURE__ */ React27.createElement("div", { className: "flex gap-2 border-b border-input p-2" }, /* @__PURE__ */ React27.createElement(
2685
- "button",
2686
- {
2687
- type: "button",
2688
- 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",
2689
- onClick: handleSelectAll,
2690
- disabled
2691
- },
2692
- "Select All"
2693
- ), value.length > 0 && /* @__PURE__ */ React27.createElement(
2694
- "button",
2695
- {
2696
- type: "button",
2697
- 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",
2698
- onClick: handleClearAll,
2699
- disabled
2700
- },
2701
- "Clear All"
2702
- )),
2703
- isMaxReached && /* @__PURE__ */ React27.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"),
2704
- /* @__PURE__ */ React27.createElement(CommandList, { role: "listbox", "aria-multiselectable": "true" }, /* @__PURE__ */ React27.createElement(CommandEmpty, null, "No options found"), optionGroups.length > 0 ? optionGroups.map((group, groupIndex) => {
2705
- const groupOptions = group.options.filter(
2706
- (option) => filteredOptions.includes(option)
2707
- );
2708
- if (groupOptions.length === 0) return null;
2709
- return /* @__PURE__ */ React27.createElement(
2710
- CommandGroup,
2711
- {
2712
- key: `${group.label}-${groupIndex}`,
2713
- heading: group.label
2714
- },
2715
- groupOptions.map((option) => {
2716
- const globalIndex = filteredOptions.indexOf(option);
2717
- const isSelected = value.includes(option.value);
2718
- const isFocused = globalIndex === focusedIndex;
2719
- const optionDisabled = option.disabled || isMaxReached && !isSelected;
2720
- return /* @__PURE__ */ React27.createElement(
2721
- "div",
2722
- {
2723
- key: option.value,
2724
- role: "option",
2725
- "aria-selected": isSelected,
2726
- "aria-disabled": optionDisabled,
2727
- onMouseEnter: () => {
2728
- setFocusedIndex(globalIndex);
2729
- },
2730
- onClick: () => {
2731
- if (!optionDisabled) {
2732
- handleToggleOption(option.value);
2733
- }
2734
- },
2735
- className: cn(
2736
- "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",
2737
- isFocused && "bg-accent",
2738
- isSelected && "bg-accent font-medium",
2739
- optionDisabled && "pointer-events-none opacity-50"
2740
- )
2741
- },
2742
- /* @__PURE__ */ React27.createElement("span", { className: "text-base leading-none" }, isSelected ? "\u2611" : "\u2610"),
2743
- /* @__PURE__ */ React27.createElement("span", { className: "flex-1" }, renderOption ? renderOption(option) : option.label)
2744
- );
2745
- })
2746
- );
2747
- }) : filteredOptions.map((option, index) => {
2748
- const isSelected = value.includes(option.value);
2749
- const isFocused = index === focusedIndex;
2750
- const optionDisabled = option.disabled || isMaxReached && !isSelected;
2751
- return /* @__PURE__ */ React27.createElement(
2752
- "div",
2753
- {
2754
- key: option.value,
2755
- role: "option",
2756
- "aria-selected": isSelected,
2757
- "aria-disabled": optionDisabled,
2758
- onMouseEnter: () => {
2759
- setFocusedIndex(index);
2760
- },
2761
- onClick: () => {
2762
- if (!optionDisabled) {
2763
- handleToggleOption(option.value);
2764
- }
2765
- },
2766
- className: cn(
2767
- "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",
2768
- isFocused && "bg-accent",
2769
- isSelected && "bg-accent font-medium",
2770
- optionDisabled && "pointer-events-none opacity-50"
2771
- )
2772
- },
2773
- /* @__PURE__ */ React27.createElement("span", { className: "text-base leading-none" }, isSelected ? "\u2611" : "\u2610"),
2774
- /* @__PURE__ */ React27.createElement("span", { className: "flex-1" }, renderOption ? renderOption(option) : option.label)
2775
- );
2776
- }))
2777
- )
2778
- )));
2779
- }
2780
- MultiSelect.displayName = "MultiSelect";
2781
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React27.useLayoutEffect : React27.useEffect;
2782
-
2783
- // src/hooks/use-as-ref.ts
2784
- function useAsRef(props) {
2785
- const ref = React27.useRef(props);
2786
- useIsomorphicLayoutEffect(() => {
2787
- ref.current = props;
2788
- });
2789
- return ref;
2790
- }
2791
- function useLazyRef(fn) {
2792
- const ref = React27.useRef(null);
2793
- if (ref.current === null) {
2794
- ref.current = fn();
2795
- }
2796
- return ref;
2797
- }
2798
-
2799
- // src/components/ui/file-upload.tsx
2800
- var ROOT_NAME = "FileUpload";
2801
- var DROPZONE_NAME = "FileUploadDropzone";
2802
- var LIST_NAME = "FileUploadList";
2803
- var ITEM_NAME = "FileUploadItem";
2804
- var ITEM_PREVIEW_NAME = "FileUploadItemPreview";
2805
- var ITEM_METADATA_NAME = "FileUploadItemMetadata";
2806
- var ITEM_DELETE_NAME = "FileUploadItemDelete";
2807
- function BaseFileIcon({
2808
- children,
2809
- className
2810
- }) {
2811
- return /* @__PURE__ */ React27.createElement(
2812
- "svg",
2813
- {
2814
- viewBox: "0 0 24 24",
2815
- fill: "none",
2816
- stroke: "currentColor",
2817
- strokeWidth: "2",
2818
- strokeLinecap: "round",
2819
- strokeLinejoin: "round",
2820
- className: cn("size-5", className),
2821
- "aria-hidden": "true"
2822
- },
2823
- children
2824
- );
2825
- }
2826
- function FileVideoIcon() {
2827
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("rect", { x: "8", y: "12", width: "6", height: "4", rx: "1" }), /* @__PURE__ */ React27.createElement("path", { d: "m14 13 3-1.5v5L14 15" }));
2828
- }
2829
- function FileAudioIcon() {
2830
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("path", { d: "M10 16a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z" }), /* @__PURE__ */ React27.createElement("path", { d: "M13 17V11l3-1" }));
2831
- }
2832
- function FileTextIcon() {
2833
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("line", { x1: "8", y1: "13", x2: "16", y2: "13" }), /* @__PURE__ */ React27.createElement("line", { x1: "8", y1: "17", x2: "14", y2: "17" }));
2834
- }
2835
- function FileCodeIcon() {
2836
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("polyline", { points: "11 14 9 16 11 18" }), /* @__PURE__ */ React27.createElement("polyline", { points: "13 14 15 16 13 18" }));
2837
- }
2838
- function FileArchiveIcon() {
2839
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("rect", { x: "9", y: "11", width: "6", height: "2" }), /* @__PURE__ */ React27.createElement("path", { d: "M12 13v5" }));
2840
- }
2841
- function FileCogIcon() {
2842
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }), /* @__PURE__ */ React27.createElement("circle", { cx: "12", cy: "16", r: "2" }), /* @__PURE__ */ React27.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" }));
2843
- }
2844
- function FileIcon() {
2845
- return /* @__PURE__ */ React27.createElement(BaseFileIcon, null, /* @__PURE__ */ React27.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), /* @__PURE__ */ React27.createElement("polyline", { points: "14 2 14 8 20 8" }));
2846
- }
2847
- function formatBytes(bytes) {
2848
- if (bytes === 0) return "0 B";
2849
- const sizes = ["B", "KB", "MB", "GB", "TB"];
2850
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
2851
- return `${(bytes / 1024 ** i).toFixed(i ? 1 : 0)} ${sizes[i]}`;
2852
- }
2853
- function getFileIcon(file) {
2854
- const type = file.type;
2855
- const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
2856
- if (type.startsWith("video/")) {
2857
- return /* @__PURE__ */ React27.createElement(FileVideoIcon, null);
2858
- }
2859
- if (type.startsWith("audio/")) {
2860
- return /* @__PURE__ */ React27.createElement(FileAudioIcon, null);
2861
- }
2862
- if (type.startsWith("text/") || ["txt", "md", "rtf", "pdf"].includes(extension)) {
2863
- return /* @__PURE__ */ React27.createElement(FileTextIcon, null);
2864
- }
2865
- if ([
2866
- "html",
2867
- "css",
2868
- "js",
2869
- "jsx",
2870
- "ts",
2871
- "tsx",
2872
- "json",
2873
- "xml",
2874
- "php",
2875
- "py",
2876
- "rb",
2877
- "java",
2878
- "c",
2879
- "cpp",
2880
- "cs"
2881
- ].includes(extension)) {
2882
- return /* @__PURE__ */ React27.createElement(FileCodeIcon, null);
2883
- }
2884
- if (["zip", "rar", "7z", "tar", "gz", "bz2"].includes(extension)) {
2885
- return /* @__PURE__ */ React27.createElement(FileArchiveIcon, null);
2886
- }
2887
- if (["exe", "msi", "app", "apk", "deb", "rpm"].includes(extension) || type.startsWith("application/")) {
2888
- return /* @__PURE__ */ React27.createElement(FileCogIcon, null);
2889
- }
2890
- return /* @__PURE__ */ React27.createElement(FileIcon, null);
2891
- }
2892
- var StoreContext = React27.createContext(null);
2893
- function useStoreContext(consumerName) {
2894
- const context = React27.useContext(StoreContext);
2895
- if (!context) {
2896
- throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
2897
- }
2898
- return context;
2899
- }
2900
- function useStore(selector) {
2901
- const store = useStoreContext("useStore");
2902
- const lastValueRef = useLazyRef(
2903
- () => null
2904
- );
2905
- const getSnapshot = React27.useCallback(() => {
2906
- const state = store.getState();
2907
- const prevValue = lastValueRef.current;
2908
- if (prevValue && prevValue.state === state) {
2909
- return prevValue.value;
2910
- }
2911
- const nextValue = selector(state);
2912
- lastValueRef.current = { value: nextValue, state };
2913
- return nextValue;
2914
- }, [store, selector, lastValueRef]);
2915
- return React27.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
2916
- }
2917
- var FileUploadContext = React27.createContext(
2918
- null
2919
- );
2920
- function useFileUploadContext(consumerName) {
2921
- const context = React27.useContext(FileUploadContext);
2922
- if (!context) {
2923
- throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``);
2924
- }
2925
- return context;
2926
- }
2927
- function FileUpload(props) {
2928
- const {
2929
- value,
2930
- defaultValue,
2931
- onValueChange,
2932
- onAccept,
2933
- onFileAccept,
2934
- onFileReject,
2935
- onFileValidate,
2936
- onUpload,
2937
- accept,
2938
- maxFiles,
2939
- maxSize,
2940
- dir: dirProp,
2941
- label,
2942
- name,
2943
- asChild,
2944
- disabled = false,
2945
- invalid = false,
2946
- multiple = false,
2947
- required = false,
2948
- inputProps,
2949
- children,
2950
- className,
2951
- ...rootProps
2952
- } = props;
2953
- const inputId = React27.useId();
2954
- const dropzoneId = React27.useId();
2955
- const listId = React27.useId();
2956
- const labelId = React27.useId();
2957
- const dir = useDirection(dirProp);
2958
- const listeners = useLazyRef(() => /* @__PURE__ */ new Set()).current;
2959
- const files = useLazyRef(() => /* @__PURE__ */ new Map()).current;
2960
- const urlCache = useLazyRef(() => /* @__PURE__ */ new WeakMap()).current;
2961
- const inputRef = React27.useRef(null);
2962
- const isControlled = value !== void 0;
2963
- const propsRef = useAsRef({
2964
- onValueChange,
2965
- onAccept,
2966
- onFileAccept,
2967
- onFileReject,
2968
- onFileValidate,
2969
- onUpload
2970
- });
2971
- const store = React27.useMemo(() => {
2972
- let state = {
2973
- files,
2974
- dragOver: false,
2975
- invalid
2976
- };
2977
- function reducer(state2, action) {
2978
- switch (action.type) {
2979
- case "ADD_FILES": {
2980
- for (const file of action.files) {
2981
- files.set(file, {
2982
- file,
2983
- progress: 0,
2984
- status: "idle"
2985
- });
2986
- }
2987
- if (propsRef.current.onValueChange) {
2988
- const fileList = Array.from(files.values()).map(
2989
- (fileState) => fileState.file
2990
- );
2991
- propsRef.current.onValueChange(fileList);
2992
- }
2993
- return { ...state2, files };
2994
- }
2995
- case "SET_FILES": {
2996
- const newFileSet = new Set(action.files);
2997
- for (const existingFile of files.keys()) {
2998
- if (!newFileSet.has(existingFile)) {
2999
- files.delete(existingFile);
3000
- }
3001
- }
3002
- for (const file of action.files) {
3003
- const existingState = files.get(file);
3004
- if (!existingState) {
3005
- files.set(file, {
3006
- file,
3007
- progress: 0,
3008
- status: "idle"
3009
- });
3010
- }
3011
- }
3012
- return { ...state2, files };
3013
- }
3014
- case "SET_PROGRESS": {
3015
- const fileState = files.get(action.file);
3016
- if (fileState) {
3017
- files.set(action.file, {
3018
- ...fileState,
3019
- progress: action.progress,
3020
- status: "uploading"
3021
- });
3022
- }
3023
- return { ...state2, files };
3024
- }
3025
- case "SET_SUCCESS": {
3026
- const fileState = files.get(action.file);
3027
- if (fileState) {
3028
- files.set(action.file, {
3029
- ...fileState,
3030
- progress: 100,
3031
- status: "success"
3032
- });
3033
- }
3034
- return { ...state2, files };
3035
- }
3036
- case "SET_ERROR": {
3037
- const fileState = files.get(action.file);
3038
- if (fileState) {
3039
- files.set(action.file, {
3040
- ...fileState,
3041
- error: action.error,
3042
- status: "error"
3043
- });
3044
- }
3045
- return { ...state2, files };
3046
- }
3047
- case "REMOVE_FILE": {
3048
- const cachedUrl = urlCache.get(action.file);
3049
- if (cachedUrl) {
3050
- URL.revokeObjectURL(cachedUrl);
3051
- urlCache.delete(action.file);
3052
- }
3053
- files.delete(action.file);
3054
- if (propsRef.current.onValueChange) {
3055
- const fileList = Array.from(files.values()).map(
3056
- (fileState) => fileState.file
3057
- );
3058
- propsRef.current.onValueChange(fileList);
3059
- }
3060
- return { ...state2, files };
3061
- }
3062
- case "SET_DRAG_OVER": {
3063
- return { ...state2, dragOver: action.dragOver };
3064
- }
3065
- case "SET_INVALID": {
3066
- return { ...state2, invalid: action.invalid };
3067
- }
3068
- case "CLEAR": {
3069
- for (const file of files.keys()) {
3070
- const cachedUrl = urlCache.get(file);
3071
- if (cachedUrl) {
3072
- URL.revokeObjectURL(cachedUrl);
3073
- urlCache.delete(file);
3074
- }
3075
- }
3076
- files.clear();
3077
- if (propsRef.current.onValueChange) {
3078
- propsRef.current.onValueChange([]);
3079
- }
3080
- return { ...state2, files, invalid: false };
3081
- }
3082
- default:
3083
- return state2;
3084
- }
3085
- }
3086
- return {
3087
- getState: () => state,
3088
- dispatch: (action) => {
3089
- state = reducer(state, action);
3090
- for (const listener of listeners) {
3091
- listener();
3092
- }
3093
- },
3094
- subscribe: (listener) => {
3095
- listeners.add(listener);
3096
- return () => listeners.delete(listener);
3097
- }
3098
- };
3099
- }, [listeners, files, invalid, propsRef, urlCache]);
3100
- const acceptTypes = React27.useMemo(
3101
- () => accept?.split(",").map((t) => t.trim()) ?? null,
3102
- [accept]
3103
- );
3104
- const onProgress = useLazyRef(() => {
3105
- let frame = 0;
3106
- return (file, progress) => {
3107
- if (frame) return;
3108
- frame = requestAnimationFrame(() => {
3109
- frame = 0;
3110
- store.dispatch({
3111
- type: "SET_PROGRESS",
3112
- file,
3113
- progress: Math.min(Math.max(0, progress), 100)
3114
- });
3115
- });
3116
- };
3117
- }).current;
3118
- React27.useEffect(() => {
3119
- if (isControlled) {
3120
- store.dispatch({ type: "SET_FILES", files: value });
3121
- } else if (defaultValue && defaultValue.length > 0 && !store.getState().files.size) {
3122
- store.dispatch({ type: "SET_FILES", files: defaultValue });
3123
- }
3124
- }, [value, defaultValue, isControlled, store]);
3125
- React27.useEffect(() => {
3126
- return () => {
3127
- for (const file of files.keys()) {
3128
- const cachedUrl = urlCache.get(file);
3129
- if (cachedUrl) {
3130
- URL.revokeObjectURL(cachedUrl);
3131
- }
3132
- }
3133
- };
3134
- }, [files, urlCache]);
3135
- const onFilesUpload = React27.useCallback(
3136
- async (files2) => {
3137
- try {
3138
- for (const file of files2) {
3139
- store.dispatch({ type: "SET_PROGRESS", file, progress: 0 });
3140
- }
3141
- if (propsRef.current.onUpload) {
3142
- await propsRef.current.onUpload(files2, {
3143
- onProgress,
3144
- onSuccess: (file) => {
3145
- store.dispatch({ type: "SET_SUCCESS", file });
3146
- },
3147
- onError: (file, error) => {
3148
- store.dispatch({
3149
- type: "SET_ERROR",
3150
- file,
3151
- error: error.message ?? "Upload failed"
3152
- });
3153
- }
3154
- });
3155
- } else {
3156
- for (const file of files2) {
3157
- store.dispatch({ type: "SET_SUCCESS", file });
3158
- }
3159
- }
3160
- } catch (error) {
3161
- const errorMessage = error instanceof Error ? error.message : "Upload failed";
3162
- for (const file of files2) {
3163
- store.dispatch({
3164
- type: "SET_ERROR",
3165
- file,
3166
- error: errorMessage
3167
- });
3168
- }
3169
- }
3170
- },
3171
- [store, propsRef, onProgress]
3172
- );
3173
- const onFilesChange = React27.useCallback(
3174
- (originalFiles) => {
3175
- if (disabled) return;
3176
- let filesToProcess = [...originalFiles];
3177
- let invalid2 = false;
3178
- if (maxFiles) {
3179
- const currentCount = store.getState().files.size;
3180
- const remainingSlotCount = Math.max(0, maxFiles - currentCount);
3181
- if (remainingSlotCount < filesToProcess.length) {
3182
- const rejectedFiles2 = filesToProcess.slice(remainingSlotCount);
3183
- invalid2 = true;
3184
- filesToProcess = filesToProcess.slice(0, remainingSlotCount);
3185
- for (const file of rejectedFiles2) {
3186
- let rejectionMessage = `Maximum ${maxFiles} files allowed`;
3187
- if (propsRef.current.onFileValidate) {
3188
- const validationMessage = propsRef.current.onFileValidate(file);
3189
- if (validationMessage) {
3190
- rejectionMessage = validationMessage;
3191
- }
3192
- }
3193
- propsRef.current.onFileReject?.(file, rejectionMessage);
3194
- }
3195
- }
3196
- }
3197
- const acceptedFiles = [];
3198
- for (const file of filesToProcess) {
3199
- let rejected = false;
3200
- let rejectionMessage = "";
3201
- if (propsRef.current.onFileValidate) {
3202
- const validationMessage = propsRef.current.onFileValidate(file);
3203
- if (validationMessage) {
3204
- rejectionMessage = validationMessage;
3205
- propsRef.current.onFileReject?.(file, rejectionMessage);
3206
- rejected = true;
3207
- invalid2 = true;
3208
- continue;
3209
- }
3210
- }
3211
- if (acceptTypes) {
3212
- const fileType = file.type;
3213
- const fileExtension = `.${file.name.split(".").pop()}`;
3214
- if (!acceptTypes.some(
3215
- (type) => type === fileType || type === fileExtension || type.includes("/*") && fileType.startsWith(type.replace("/*", "/"))
3216
- )) {
3217
- rejectionMessage = "File type not accepted";
3218
- propsRef.current.onFileReject?.(file, rejectionMessage);
3219
- rejected = true;
3220
- invalid2 = true;
3221
- }
3222
- }
3223
- if (maxSize && file.size > maxSize) {
3224
- rejectionMessage = "File too large";
3225
- propsRef.current.onFileReject?.(file, rejectionMessage);
3226
- rejected = true;
3227
- invalid2 = true;
3228
- }
3229
- if (!rejected) {
3230
- acceptedFiles.push(file);
3231
- }
3232
- }
3233
- if (invalid2) {
3234
- store.dispatch({ type: "SET_INVALID", invalid: invalid2 });
3235
- setTimeout(() => {
3236
- store.dispatch({ type: "SET_INVALID", invalid: false });
3237
- }, 2e3);
3238
- }
3239
- if (acceptedFiles.length > 0) {
3240
- store.dispatch({ type: "ADD_FILES", files: acceptedFiles });
3241
- if (isControlled && propsRef.current.onValueChange) {
3242
- const currentFiles = Array.from(store.getState().files.values()).map(
3243
- (f) => f.file
3244
- );
3245
- propsRef.current.onValueChange([...currentFiles]);
3246
- }
3247
- if (propsRef.current.onAccept) {
3248
- propsRef.current.onAccept(acceptedFiles);
3249
- }
3250
- for (const file of acceptedFiles) {
3251
- propsRef.current.onFileAccept?.(file);
3252
- }
3253
- if (propsRef.current.onUpload) {
3254
- requestAnimationFrame(() => {
3255
- onFilesUpload(acceptedFiles);
3256
- });
3257
- }
3258
- }
3259
- },
3260
- [
3261
- store,
3262
- isControlled,
3263
- propsRef,
3264
- onFilesUpload,
3265
- maxFiles,
3266
- acceptTypes,
3267
- maxSize,
3268
- disabled
3269
- ]
3270
- );
3271
- const onInputChange = React27.useCallback(
3272
- (event) => {
3273
- const files2 = Array.from(event.target.files ?? []);
3274
- onFilesChange(files2);
3275
- event.target.value = "";
3276
- },
3277
- [onFilesChange]
3278
- );
3279
- const contextValue = React27.useMemo(
3280
- () => ({
3281
- dropzoneId,
3282
- inputId,
3283
- listId,
3284
- labelId,
3285
- dir,
3286
- disabled,
3287
- inputRef,
3288
- urlCache
3289
- }),
3290
- [dropzoneId, inputId, listId, labelId, dir, disabled, urlCache]
3291
- );
3292
- const RootPrimitive = asChild ? Slot$1 : "div";
3293
- const inputAriaDescribedBy = [
3294
- contextValue.dropzoneId,
3295
- inputProps?.["aria-describedby"]
3296
- ].filter(Boolean).join(" ").trim();
3297
- return /* @__PURE__ */ React27.createElement(StoreContext.Provider, { value: store }, /* @__PURE__ */ React27.createElement(FileUploadContext.Provider, { value: contextValue }, /* @__PURE__ */ React27.createElement(
3298
- RootPrimitive,
3299
- {
3300
- "data-disabled": disabled ? "" : void 0,
3301
- "data-slot": "file-upload",
3302
- dir,
3303
- ...rootProps,
3304
- className: cn("relative flex flex-col gap-2", className)
3305
- },
3306
- children,
3307
- /* @__PURE__ */ React27.createElement(
3308
- "input",
3309
- {
3310
- type: "file",
3311
- id: inputId,
3312
- "aria-labelledby": inputProps?.["aria-labelledby"] ? `${labelId} ${inputProps["aria-labelledby"]}` : labelId,
3313
- "aria-describedby": inputAriaDescribedBy || void 0,
3314
- ref: inputRef,
3315
- tabIndex: -1,
3316
- accept,
3317
- name,
3318
- className: "sr-only",
3319
- disabled,
3320
- multiple,
3321
- required,
3322
- onChange: onInputChange,
3323
- ...inputProps
3324
- }
3325
- ),
3326
- /* @__PURE__ */ React27.createElement("div", { id: labelId, className: "sr-only" }, label ?? "File upload")
3327
- )));
3328
- }
3329
- function FileUploadDropzone(props) {
3330
- const {
3331
- asChild,
3332
- className,
3333
- onClick: onClickProp,
3334
- onDragOver: onDragOverProp,
3335
- onDragEnter: onDragEnterProp,
3336
- onDragLeave: onDragLeaveProp,
3337
- onDrop: onDropProp,
3338
- onPaste: onPasteProp,
3339
- onKeyDown: onKeyDownProp,
3340
- ...dropzoneProps
3341
- } = props;
3342
- const context = useFileUploadContext(DROPZONE_NAME);
3343
- const store = useStoreContext(DROPZONE_NAME);
3344
- const dragOver = useStore((state) => state.dragOver);
3345
- const invalid = useStore((state) => state.invalid);
3346
- const propsRef = useAsRef({
3347
- onClick: onClickProp,
3348
- onDragOver: onDragOverProp,
3349
- onDragEnter: onDragEnterProp,
3350
- onDragLeave: onDragLeaveProp,
3351
- onDrop: onDropProp,
3352
- onPaste: onPasteProp,
3353
- onKeyDown: onKeyDownProp
3354
- });
3355
- const onClick = React27.useCallback(
3356
- (event) => {
3357
- propsRef.current.onClick?.(event);
3358
- if (event.defaultPrevented) return;
3359
- const target = event.target;
3360
- const isFromTrigger = target instanceof HTMLElement && target.closest('[data-slot="file-upload-trigger"]');
3361
- if (!isFromTrigger) {
3362
- context.inputRef.current?.click();
3363
- }
3364
- },
3365
- [context.inputRef, propsRef]
3366
- );
3367
- const onDragOver = React27.useCallback(
3368
- (event) => {
3369
- propsRef.current.onDragOver?.(event);
3370
- if (event.defaultPrevented) return;
3371
- event.preventDefault();
3372
- store.dispatch({ type: "SET_DRAG_OVER", dragOver: true });
3373
- },
3374
- [store, propsRef]
3375
- );
3376
- const onDragEnter = React27.useCallback(
3377
- (event) => {
3378
- propsRef.current.onDragEnter?.(event);
3379
- if (event.defaultPrevented) return;
3380
- event.preventDefault();
3381
- store.dispatch({ type: "SET_DRAG_OVER", dragOver: true });
3382
- },
3383
- [store, propsRef]
3384
- );
3385
- const onDragLeave = React27.useCallback(
3386
- (event) => {
3387
- propsRef.current.onDragLeave?.(event);
3388
- if (event.defaultPrevented) return;
3389
- const relatedTarget = event.relatedTarget;
3390
- if (relatedTarget && relatedTarget instanceof Node && event.currentTarget.contains(relatedTarget)) {
3391
- return;
3392
- }
3393
- event.preventDefault();
3394
- store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
3395
- },
3396
- [store, propsRef]
3397
- );
3398
- const onDrop = React27.useCallback(
3399
- (event) => {
3400
- propsRef.current.onDrop?.(event);
3401
- if (event.defaultPrevented) return;
3402
- if (context.disabled) return;
3403
- event.preventDefault();
3404
- store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
3405
- const files = Array.from(event.dataTransfer.files);
3406
- const inputElement = context.inputRef.current;
3407
- if (!inputElement) return;
3408
- if (typeof DataTransfer === "undefined") return;
3409
- const dataTransfer = new DataTransfer();
3410
- for (const file of files) {
3411
- dataTransfer.items.add(file);
3412
- }
3413
- inputElement.files = dataTransfer.files;
3414
- inputElement.dispatchEvent(new Event("change", { bubbles: true }));
3415
- },
3416
- [store, context.inputRef, propsRef]
3417
- );
3418
- const onPaste = React27.useCallback(
3419
- (event) => {
3420
- propsRef.current.onPaste?.(event);
3421
- if (event.defaultPrevented) return;
3422
- if (context.disabled) return;
3423
- event.preventDefault();
3424
- store.dispatch({ type: "SET_DRAG_OVER", dragOver: false });
3425
- const items = event.clipboardData?.items;
3426
- if (!items) return;
3427
- const files = [];
3428
- for (let i = 0; i < items.length; i++) {
3429
- const item = items[i];
3430
- if (item?.kind === "file") {
3431
- const file = item.getAsFile();
3432
- if (file) {
3433
- files.push(file);
3434
- }
3435
- }
3436
- }
3437
- if (files.length === 0) return;
3438
- const inputElement = context.inputRef.current;
3439
- if (!inputElement) return;
3440
- if (typeof DataTransfer === "undefined") return;
3441
- const dataTransfer = new DataTransfer();
3442
- for (const file of files) {
3443
- dataTransfer.items.add(file);
3444
- }
3445
- inputElement.files = dataTransfer.files;
3446
- inputElement.dispatchEvent(new Event("change", { bubbles: true }));
3447
- },
3448
- [store, context.inputRef, propsRef]
3449
- );
3450
- const onKeyDown = React27.useCallback(
3451
- (event) => {
3452
- propsRef.current.onKeyDown?.(event);
3453
- if (!event.defaultPrevented && (event.key === "Enter" || event.key === " ")) {
3454
- event.preventDefault();
3455
- context.inputRef.current?.click();
3456
- }
3457
- },
3458
- [context.inputRef, propsRef]
3459
- );
3460
- const DropzonePrimitive = asChild ? Slot$1 : "div";
3461
- return /* @__PURE__ */ React27.createElement(
3462
- DropzonePrimitive,
3463
- {
3464
- role: "region",
3465
- id: context.dropzoneId,
3466
- "aria-controls": `${context.inputId} ${context.listId}`,
3467
- "aria-disabled": context.disabled,
3468
- "aria-invalid": invalid,
3469
- "data-disabled": context.disabled ? "" : void 0,
3470
- "data-dragging": dragOver ? "" : void 0,
3471
- "data-invalid": invalid ? "" : void 0,
3472
- "data-slot": "file-upload-dropzone",
3473
- dir: context.dir,
3474
- tabIndex: context.disabled ? -1 : 0,
3475
- ...dropzoneProps,
3476
- className: cn(
3477
- "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",
3478
- className
3479
- ),
3480
- onClick,
3481
- onDragEnter,
3482
- onDragLeave,
3483
- onDragOver,
3484
- onDrop,
3485
- onKeyDown,
3486
- onPaste
3487
- }
3488
- );
3489
- }
3490
- function FileUploadList(props) {
3491
- const {
3492
- className,
3493
- orientation = "vertical",
3494
- asChild,
3495
- forceMount,
3496
- ...listProps
3497
- } = props;
3498
- const context = useFileUploadContext(LIST_NAME);
3499
- const fileCount = useStore((state) => state.files.size);
3500
- const shouldRender = forceMount || fileCount > 0;
3501
- if (!shouldRender) return null;
3502
- const ListPrimitive = asChild ? Slot$1 : "div";
3503
- return /* @__PURE__ */ React27.createElement(
3504
- ListPrimitive,
3505
- {
3506
- role: "list",
3507
- id: context.listId,
3508
- "aria-orientation": orientation,
3509
- "data-orientation": orientation,
3510
- "data-slot": "file-upload-list",
3511
- "data-state": shouldRender ? "active" : "inactive",
3512
- dir: context.dir,
3513
- ...listProps,
3514
- className: cn(
3515
- "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",
3516
- orientation === "horizontal" && "flex-row overflow-x-auto p-1.5",
3517
- className
3518
- )
3519
- }
3520
- );
3521
- }
3522
- var FileUploadItemContext = React27.createContext(null);
3523
- function useFileUploadItemContext(consumerName) {
3524
- const context = React27.useContext(FileUploadItemContext);
3525
- if (!context) {
3526
- throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME}\``);
3527
- }
3528
- return context;
3529
- }
3530
- function FileUploadItem(props) {
3531
- const { value, asChild, className, ...itemProps } = props;
3532
- const id = React27.useId();
3533
- const statusId = `${id}-status`;
3534
- const nameId = `${id}-name`;
3535
- const sizeId = `${id}-size`;
3536
- const messageId = `${id}-message`;
3537
- const context = useFileUploadContext(ITEM_NAME);
3538
- const fileState = useStore((state) => state.files.get(value));
3539
- const fileCount = useStore((state) => state.files.size);
3540
- const fileIndex = useStore((state) => {
3541
- const files = Array.from(state.files.keys());
3542
- return files.indexOf(value) + 1;
3543
- });
3544
- const itemContext = React27.useMemo(
3545
- () => ({
3546
- id,
3547
- fileState,
3548
- nameId,
3549
- sizeId,
3550
- statusId,
3551
- messageId
3552
- }),
3553
- [id, fileState, statusId, nameId, sizeId, messageId]
3554
- );
3555
- if (!fileState) return null;
3556
- const statusText = fileState.error ? `Error: ${fileState.error}` : fileState.status === "uploading" ? `Uploading: ${fileState.progress}% complete` : fileState.status === "success" ? "Upload complete" : "Ready to upload";
3557
- const ItemPrimitive = asChild ? Slot$1 : "div";
3558
- return /* @__PURE__ */ React27.createElement(FileUploadItemContext.Provider, { value: itemContext }, /* @__PURE__ */ React27.createElement(
3559
- ItemPrimitive,
3560
- {
3561
- role: "listitem",
3562
- id,
3563
- "aria-setsize": fileCount,
3564
- "aria-posinset": fileIndex,
3565
- "aria-describedby": `${nameId} ${sizeId} ${statusId} ${fileState.error ? messageId : ""}`,
3566
- "aria-labelledby": nameId,
3567
- "data-slot": "file-upload-item",
3568
- dir: context.dir,
3569
- ...itemProps,
3570
- className: cn(
3571
- "relative flex items-center gap-2.5 rounded-md border p-3",
3572
- className
3573
- )
3574
- },
3575
- props.children,
3576
- /* @__PURE__ */ React27.createElement("span", { id: statusId, className: "sr-only" }, statusText)
3577
- ));
3578
- }
3579
- function FileUploadItemPreview(props) {
3580
- const { render, asChild, children, className, ...previewProps } = props;
3581
- const itemContext = useFileUploadItemContext(ITEM_PREVIEW_NAME);
3582
- const context = useFileUploadContext(ITEM_PREVIEW_NAME);
3583
- const getDefaultRender = React27.useCallback(
3584
- (file) => {
3585
- if (itemContext.fileState?.file.type.startsWith("image/")) {
3586
- let url = context.urlCache.get(file);
3587
- if (!url) {
3588
- url = URL.createObjectURL(file);
3589
- context.urlCache.set(file, url);
3590
- }
3591
- return (
3592
- // biome-ignore lint/performance/noImgElement: dynamic file URLs from user uploads don't work well with Next.js Image optimization
3593
- /* @__PURE__ */ React27.createElement("img", { src: url, alt: file.name, className: "size-full object-cover" })
3594
- );
3595
- }
3596
- return getFileIcon(file);
3597
- },
3598
- [itemContext.fileState?.file.type, context.urlCache]
3599
- );
3600
- const onPreviewRender = React27.useCallback(
3601
- (file) => {
3602
- if (render) {
3603
- return render(file, () => getDefaultRender(file));
3604
- }
3605
- return getDefaultRender(file);
3606
- },
3607
- [render, getDefaultRender]
3608
- );
3609
- if (!itemContext.fileState) return null;
3610
- const ItemPreviewPrimitive = asChild ? Slot$1 : "div";
3611
- return /* @__PURE__ */ React27.createElement(
3612
- ItemPreviewPrimitive,
3613
- {
3614
- "aria-labelledby": itemContext.nameId,
3615
- "data-slot": "file-upload-preview",
3616
- ...previewProps,
3617
- className: cn(
3618
- "relative flex size-10 shrink-0 items-center justify-center overflow-hidden rounded border bg-accent/50 [&>svg]:size-10",
3619
- className
3620
- )
3621
- },
3622
- onPreviewRender(itemContext.fileState.file),
3623
- children
3624
- );
3625
- }
3626
- function FileUploadItemMetadata(props) {
3627
- const {
3628
- asChild,
3629
- size = "default",
3630
- children,
3631
- className,
3632
- ...metadataProps
3633
- } = props;
3634
- const context = useFileUploadContext(ITEM_METADATA_NAME);
3635
- const itemContext = useFileUploadItemContext(ITEM_METADATA_NAME);
3636
- if (!itemContext.fileState) return null;
3637
- const ItemMetadataPrimitive = asChild ? Slot$1 : "div";
3638
- return /* @__PURE__ */ React27.createElement(
3639
- ItemMetadataPrimitive,
3640
- {
3641
- "data-slot": "file-upload-metadata",
3642
- dir: context.dir,
3643
- ...metadataProps,
3644
- className: cn("flex min-w-0 flex-1 flex-col", className)
3645
- },
3646
- children ?? /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(
3647
- "span",
3648
- {
3649
- id: itemContext.nameId,
3650
- className: cn(
3651
- "truncate font-medium text-sm",
3652
- size === "sm" && "font-normal text-[13px] leading-snug"
3653
- )
3654
- },
3655
- itemContext.fileState.file.name
3656
- ), /* @__PURE__ */ React27.createElement(
3657
- "span",
3658
- {
3659
- id: itemContext.sizeId,
3660
- className: cn(
3661
- "truncate text-xs opacity-70",
3662
- size === "sm" && "text-[11px] leading-snug"
3663
- )
3664
- },
3665
- formatBytes(itemContext.fileState.file.size)
3666
- ), itemContext.fileState.error && /* @__PURE__ */ React27.createElement(
3667
- "span",
3668
- {
3669
- id: itemContext.messageId,
3670
- className: "text-destructive text-xs"
3671
- },
3672
- itemContext.fileState.error
3673
- ))
3674
- );
3675
- }
3676
- function FileUploadItemDelete(props) {
3677
- const { asChild, onClick: onClickProp, ...deleteProps } = props;
3678
- const store = useStoreContext(ITEM_DELETE_NAME);
3679
- const itemContext = useFileUploadItemContext(ITEM_DELETE_NAME);
3680
- const onClick = React27.useCallback(
3681
- (event) => {
3682
- onClickProp?.(event);
3683
- if (!itemContext.fileState || event.defaultPrevented) return;
3684
- store.dispatch({
3685
- type: "REMOVE_FILE",
3686
- file: itemContext.fileState.file
3687
- });
3688
- },
3689
- [store, itemContext.fileState, onClickProp]
3690
- );
3691
- if (!itemContext.fileState) return null;
3692
- const ItemDeletePrimitive = asChild ? Slot$1 : "button";
3693
- return /* @__PURE__ */ React27.createElement(
3694
- ItemDeletePrimitive,
3695
- {
3696
- type: "button",
3697
- "aria-controls": itemContext.id,
3698
- "aria-describedby": itemContext.nameId,
3699
- "data-slot": "file-upload-item-delete",
3700
- ...deleteProps,
3701
- onClick
3702
- }
3703
- );
3704
- }
3705
-
3706
- // src/inputs/FileInput.tsx
3707
- function FileInput({
3708
- name,
3709
- value = [],
3710
- onChange,
3711
- onBlur,
3712
- placeholder = "Choose file...",
3713
- disabled = false,
3714
- required = false,
3715
- error = false,
3716
- className = "",
3717
- accept,
3718
- maxSize = 5 * 1024 * 1024,
3719
- // 5MB default
3720
- maxFiles = 1,
3721
- multiple = false,
3722
- showPreview = true,
3723
- showProgress = true,
3724
- uploadProgress = {},
3725
- enableCropping = false,
3726
- cropAspectRatio,
3727
- onCropComplete,
3728
- onValidationError,
3729
- onFileRemove,
3730
- ...props
3731
- }) {
3732
- const normalizedValue = React27.useMemo(() => {
3733
- const safeValue = Array.isArray(value) ? value : [];
3734
- return multiple ? safeValue : safeValue.slice(0, 1);
3735
- }, [multiple, value]);
3736
- const [cropperOpen, setCropperOpen] = React27.useState(false);
3737
- const [imageToCrop, setImageToCrop] = React27.useState(null);
3738
- const [crop, setCrop] = React27.useState({ x: 0, y: 0 });
3739
- const [zoom, setZoom] = React27.useState(1);
3740
- const [croppedAreaPixels, setCroppedAreaPixels] = React27.useState(null);
3741
- const validateFile = React27.useCallback(
3742
- (file) => {
3743
- if (accept) {
3744
- const acceptedTypes = accept.split(",").map((type) => type.trim());
3745
- const isValidType = acceptedTypes.some((type) => {
3746
- if (type.startsWith(".")) {
3747
- return file.name.toLowerCase().endsWith(type.toLowerCase());
3748
- }
3749
- if (type.endsWith("/*")) {
3750
- const baseType = type.split("/")[0];
3751
- return file.type.startsWith(`${baseType}/`);
3752
- }
3753
- return file.type === type;
3754
- });
3755
- if (!isValidType) {
3756
- return {
3757
- file,
3758
- error: "type",
3759
- message: `File type "${file.type}" is not accepted. Accepted types: ${accept}`
3760
- };
3761
- }
3762
- }
3763
- if (file.size > maxSize) {
3764
- const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(2);
3765
- const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
3766
- return {
3767
- file,
3768
- error: "size",
3769
- message: `File size ${fileSizeMB}MB exceeds maximum ${maxSizeMB}MB`
3770
- };
3771
- }
3772
- return null;
3773
- },
3774
- [accept, maxSize]
3775
- );
3776
- const mapRejectedFileError = React27.useCallback(
3777
- (file, message) => {
3778
- const normalizedMessage = message.toLowerCase();
3779
- if (normalizedMessage.includes("maximum") && normalizedMessage.includes("files")) {
3780
- return { file, error: "count", message };
3781
- }
3782
- if (normalizedMessage.includes("size") || normalizedMessage.includes("large")) {
3783
- return { file, error: "size", message };
3784
- }
3785
- if (normalizedMessage.includes("type") || normalizedMessage.includes("accept")) {
3786
- return { file, error: "type", message };
3787
- }
3788
- if (file.size > maxSize) {
3789
- return { file, error: "size", message };
3790
- }
3791
- return { file, error: "type", message };
3792
- },
3793
- [maxSize]
3794
- );
3795
- const handleFileValidate = React27.useCallback(
3796
- (file) => {
3797
- const validationError = validateFile(file);
3798
- return validationError?.message ?? null;
3799
- },
3800
- [validateFile]
3801
- );
3802
- const handleFileReject = React27.useCallback(
3803
- (file, message) => {
3804
- const validationError = mapRejectedFileError(file, message);
3805
- onValidationError?.([validationError]);
3806
- },
3807
- [mapRejectedFileError, onValidationError]
3808
- );
3809
- const handleBlur = React27.useCallback(() => {
3810
- onBlur?.();
3811
- }, [onBlur]);
3812
- const fileIdentity = React27.useCallback((file) => {
3813
- return `${file.name}-${file.size}-${file.lastModified}`;
3814
- }, []);
3815
- const handleValueChange = React27.useCallback(
3816
- (incomingFiles) => {
3817
- const nextFiles = multiple ? incomingFiles : incomingFiles.slice(-1);
3818
- if (onFileRemove && nextFiles.length < normalizedValue.length) {
3819
- const nextFileIds = new Set(nextFiles.map((file) => fileIdentity(file)));
3820
- normalizedValue.forEach((file, index) => {
3821
- if (!nextFileIds.has(fileIdentity(file))) {
3822
- onFileRemove(file, index);
3823
- }
3824
- });
3825
- }
3826
- if (enableCropping && !multiple) {
3827
- const nextImageFile = nextFiles[0];
3828
- const previousFile = normalizedValue[0];
3829
- const isNewSingleImage = Boolean(
3830
- nextImageFile && nextImageFile.type.startsWith("image/") && nextImageFile !== previousFile
3831
- );
3832
- if (isNewSingleImage) {
3833
- const previewUrl = URL.createObjectURL(nextImageFile);
3834
- setImageToCrop({ file: nextImageFile, url: previewUrl });
3835
- setCropperOpen(true);
3836
- return;
3837
- }
3838
- }
3839
- onChange(nextFiles);
3840
- },
3841
- [
3842
- enableCropping,
3843
- maxFiles,
3844
- multiple,
3845
- normalizedValue,
3846
- onChange,
3847
- onFileRemove,
3848
- fileIdentity
3849
- ]
3850
- );
3851
- const createCroppedImage = React27.useCallback(
3852
- async (imageUrl, cropArea) => {
3853
- return new Promise((resolve, reject) => {
3854
- const image = new Image();
3855
- image.onload = () => {
3856
- const canvas = document.createElement("canvas");
3857
- const ctx = canvas.getContext("2d");
3858
- if (!ctx) {
3859
- reject(new Error("Failed to get canvas context"));
3860
- return;
3861
- }
3862
- canvas.width = cropArea.width;
3863
- canvas.height = cropArea.height;
3864
- ctx.drawImage(
3865
- image,
3866
- cropArea.x,
3867
- cropArea.y,
3868
- cropArea.width,
3869
- cropArea.height,
3870
- 0,
3871
- 0,
3872
- cropArea.width,
3873
- cropArea.height
3874
- );
3875
- canvas.toBlob(
3876
- (blob) => {
3877
- if (blob) {
3878
- resolve(blob);
3879
- } else {
3880
- reject(new Error("Failed to create blob from canvas"));
3881
- }
3882
- },
3883
- "image/jpeg",
3884
- 0.95
3885
- );
3886
- };
3887
- image.onerror = () => {
3888
- reject(new Error("Failed to load image"));
3889
- };
3890
- image.src = imageUrl;
3891
- });
3892
- },
3893
- []
3894
- );
3895
- const handleCropSave = React27.useCallback(async () => {
3896
- if (!imageToCrop || !croppedAreaPixels) return;
3897
- try {
3898
- const croppedBlob = await createCroppedImage(
3899
- imageToCrop.url,
3900
- croppedAreaPixels
3901
- );
3902
- if (onCropComplete) {
3903
- onCropComplete(croppedBlob, imageToCrop.file);
3904
- }
3905
- const croppedFile = new File([croppedBlob], imageToCrop.file.name, {
3906
- type: "image/jpeg"
3907
- });
3908
- let updatedFiles;
3909
- if (!multiple) {
3910
- updatedFiles = [croppedFile];
3911
- } else {
3912
- const existingIndex = normalizedValue.findIndex(
3913
- (file) => file === imageToCrop.file
3914
- );
3915
- if (existingIndex === -1) {
3916
- updatedFiles = [...normalizedValue, croppedFile].slice(0, maxFiles);
3917
- } else {
3918
- updatedFiles = normalizedValue.map(
3919
- (file, index) => index === existingIndex ? croppedFile : file
3920
- );
3921
- }
3922
- }
3923
- onChange(updatedFiles);
3924
- setCropperOpen(false);
3925
- URL.revokeObjectURL(imageToCrop.url);
3926
- setImageToCrop(null);
3927
- setCrop({ x: 0, y: 0 });
3928
- setZoom(1);
3929
- setCroppedAreaPixels(null);
3930
- } catch (cropError) {
3931
- console.error("Failed to crop image:", cropError);
3932
- }
3933
- }, [
3934
- createCroppedImage,
3935
- croppedAreaPixels,
3936
- imageToCrop,
3937
- maxFiles,
3938
- multiple,
3939
- normalizedValue,
3940
- onChange,
3941
- onCropComplete
3942
- ]);
3943
- const handleCropCancel = React27.useCallback(() => {
3944
- if (imageToCrop) {
3945
- URL.revokeObjectURL(imageToCrop.url);
3946
- }
3947
- setCropperOpen(false);
3948
- setImageToCrop(null);
3949
- setCrop({ x: 0, y: 0 });
3950
- setZoom(1);
3951
- setCroppedAreaPixels(null);
3952
- }, [imageToCrop]);
3953
- const handleCrop = React27.useCallback((file) => {
3954
- if (!file.type.startsWith("image/")) return;
3955
- const previewUrl = URL.createObjectURL(file);
3956
- setImageToCrop({ file, url: previewUrl });
3957
- setCropperOpen(true);
3958
- }, []);
3959
- const onCropChange = React27.useCallback((nextCrop) => {
3960
- setCrop(nextCrop);
3961
- }, []);
3962
- const onZoomChange = React27.useCallback((nextZoom) => {
3963
- setZoom(nextZoom);
3964
- }, []);
3965
- const onCropCompleteInternal = React27.useCallback(
3966
- (_, nextCroppedAreaPixels) => {
3967
- setCroppedAreaPixels(nextCroppedAreaPixels);
3968
- },
3969
- []
3970
- );
3971
- const formatFileSize = React27.useCallback((bytes) => {
3972
- if (bytes === 0) return "0 Bytes";
3973
- const unit = 1024;
3974
- const units = ["Bytes", "KB", "MB", "GB"];
3975
- const index = Math.floor(Math.log(bytes) / Math.log(unit));
3976
- return Math.round(bytes / Math.pow(unit, index) * 100) / 100 + " " + units[index];
3977
- }, []);
3978
- React27.useEffect(() => {
3979
- return () => {
3980
- if (imageToCrop) {
3981
- URL.revokeObjectURL(imageToCrop.url);
3982
- }
3983
- };
3984
- }, [imageToCrop]);
3985
- const fileCountLabel = normalizedValue.length > 0 ? `${normalizedValue.length} file(s) selected` : placeholder;
3986
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement(
3987
- FileUpload,
3988
- {
3989
- name,
3990
- value: normalizedValue,
3991
- onValueChange: handleValueChange,
3992
- onFileValidate: handleFileValidate,
3993
- onFileReject: handleFileReject,
3994
- accept,
3995
- maxSize,
3996
- maxFiles: multiple ? maxFiles : void 0,
3997
- multiple,
3998
- disabled,
3999
- required: required && normalizedValue.length === 0,
4000
- invalid: Boolean(error || props["aria-invalid"]),
4001
- label: "File upload",
4002
- className: cn(className),
4003
- inputProps: {
4004
- ...props,
4005
- onBlur: handleBlur,
4006
- style: { display: "none" },
4007
- "aria-invalid": error || props["aria-invalid"],
4008
- "aria-required": required || props["aria-required"],
4009
- "aria-describedby": props["aria-describedby"]
4010
- }
4011
- },
4012
- /* @__PURE__ */ React27.createElement(
4013
- FileUploadDropzone,
4014
- {
4015
- role: "button",
4016
- "aria-label": placeholder,
4017
- className: cn(
4018
- "flex min-h-32 w-full cursor-pointer items-center justify-center border-input bg-transparent p-6 transition-colors",
4019
- "hover:bg-accent/50 hover:border-ring",
4020
- "data-[dragging]:bg-accent data-[dragging]:border-ring",
4021
- disabled && "cursor-not-allowed opacity-50",
4022
- error && "border-destructive"
4023
- )
4024
- },
4025
- /* @__PURE__ */ React27.createElement("div", { className: "flex flex-col items-center gap-2 text-center" }, /* @__PURE__ */ React27.createElement(
4026
- "svg",
4027
- {
4028
- width: "48",
4029
- height: "48",
4030
- viewBox: "0 0 24 24",
4031
- fill: "none",
4032
- stroke: "currentColor",
4033
- strokeWidth: "2",
4034
- strokeLinecap: "round",
4035
- strokeLinejoin: "round",
4036
- "aria-hidden": "true"
4037
- },
4038
- /* @__PURE__ */ React27.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
4039
- /* @__PURE__ */ React27.createElement("polyline", { points: "17 8 12 3 7 8" }),
4040
- /* @__PURE__ */ React27.createElement("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
4041
- ), /* @__PURE__ */ React27.createElement("p", { className: "text-sm font-medium" }, fileCountLabel), accept && /* @__PURE__ */ React27.createElement("p", { className: "text-xs" }, "Accepted: ", accept), /* @__PURE__ */ React27.createElement("p", { className: "text-xs" }, "Max size: ", formatFileSize(maxSize)))
4042
- ),
4043
- /* @__PURE__ */ React27.createElement(FileUploadList, { className: "mt-4" }, normalizedValue.map((file, index) => {
4044
- const progressValue = uploadProgress[file.name];
4045
- const hasProgress = showProgress && typeof progressValue === "number";
4046
- return /* @__PURE__ */ React27.createElement(
4047
- FileUploadItem,
4048
- {
4049
- key: `${file.name}-${index}`,
4050
- value: file,
4051
- className: "flex items-center gap-3 border-border bg-card text-card-foreground hover:bg-primary/50 transition-colors"
4052
- },
4053
- showPreview ? /* @__PURE__ */ React27.createElement(FileUploadItemPreview, { className: "h-12 w-12 rounded [&>img]:h-full [&>img]:w-full [&>img]:object-cover [&>svg]:size-6" }) : null,
4054
- /* @__PURE__ */ React27.createElement("div", { className: "flex min-w-0 flex-1 flex-col" }, /* @__PURE__ */ React27.createElement(FileUploadItemMetadata, { className: "min-w-0" }), /* @__PURE__ */ React27.createElement("span", { className: "text-xs" }, formatFileSize(file.size)), hasProgress ? /* @__PURE__ */ React27.createElement("div", { className: "mt-1 flex items-center gap-2" }, /* @__PURE__ */ React27.createElement(
4055
- "div",
4056
- {
4057
- className: "h-1.5 flex-1 overflow-hidden rounded-full bg-accent/40",
4058
- role: "progressbar",
4059
- "aria-valuenow": progressValue,
4060
- "aria-valuemin": 0,
4061
- "aria-valuemax": 100,
4062
- "aria-label": `Upload progress: ${progressValue}%`
4063
- },
4064
- /* @__PURE__ */ React27.createElement(
4065
- "div",
4066
- {
4067
- className: "h-full bg-primary transition-all",
4068
- style: { width: `${progressValue}%` }
4069
- }
4070
- )
4071
- ), /* @__PURE__ */ React27.createElement("span", { className: "text-xs" }, progressValue, "%")) : null),
4072
- enableCropping && file.type.startsWith("image/") ? /* @__PURE__ */ React27.createElement(
4073
- Button,
4074
- {
4075
- type: "button",
4076
- variant: "ghost",
4077
- size: "icon",
4078
- onClick: (event) => {
4079
- event.stopPropagation();
4080
- handleCrop(file);
4081
- },
4082
- disabled,
4083
- className: "h-8 w-8 p-0",
4084
- "aria-label": `Crop ${file.name}`
4085
- },
4086
- /* @__PURE__ */ React27.createElement(
4087
- "svg",
4088
- {
4089
- width: "20",
4090
- height: "20",
4091
- viewBox: "0 0 24 24",
4092
- fill: "none",
4093
- stroke: "currentColor",
4094
- strokeWidth: "2",
4095
- strokeLinecap: "round",
4096
- strokeLinejoin: "round",
4097
- "aria-hidden": "true"
4098
- },
4099
- /* @__PURE__ */ React27.createElement("path", { d: "M6.13 1L6 16a2 2 0 0 0 2 2h15" }),
4100
- /* @__PURE__ */ React27.createElement("path", { d: "M1 6.13L16 6a2 2 0 0 1 2 2v15" })
4101
- )
4102
- ) : null,
4103
- /* @__PURE__ */ React27.createElement(FileUploadItemDelete, { asChild: true }, /* @__PURE__ */ React27.createElement(
4104
- Button,
4105
- {
4106
- type: "button",
4107
- variant: "ghost",
4108
- size: "icon",
4109
- disabled,
4110
- className: "h-8 w-8 p-0",
4111
- "aria-label": `Remove ${file.name}`
4112
- },
4113
- /* @__PURE__ */ React27.createElement(
4114
- "svg",
4115
- {
4116
- width: "20",
4117
- height: "20",
4118
- viewBox: "0 0 24 24",
4119
- fill: "none",
4120
- stroke: "currentColor",
4121
- strokeWidth: "2",
4122
- strokeLinecap: "round",
4123
- strokeLinejoin: "round",
4124
- "aria-hidden": "true"
4125
- },
4126
- /* @__PURE__ */ React27.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
4127
- /* @__PURE__ */ React27.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
4128
- )
4129
- ))
4130
- );
4131
- }))
4132
- ), /* @__PURE__ */ React27.createElement(
4133
- Dialog,
4134
- {
4135
- open: cropperOpen && Boolean(imageToCrop),
4136
- onOpenChange: (open) => {
4137
- if (!open) {
4138
- handleCropCancel();
4139
- }
4140
- }
4141
- },
4142
- imageToCrop ? /* @__PURE__ */ React27.createElement(
4143
- DialogContent,
4144
- {
4145
- showCloseButton: false,
4146
- className: "max-w-3xl gap-0 p-0",
4147
- "aria-describedby": void 0
4148
- },
4149
- /* @__PURE__ */ React27.createElement(DialogHeader, { className: "flex-row items-center justify-between border-b border-border px-4 py-3" }, /* @__PURE__ */ React27.createElement(DialogTitle, null, "Crop Image"), /* @__PURE__ */ React27.createElement(DialogClose, { asChild: true }, /* @__PURE__ */ React27.createElement(
4150
- Button,
4151
- {
4152
- type: "button",
4153
- variant: "ghost",
4154
- size: "icon",
4155
- className: "h-8 w-8 p-0",
4156
- "aria-label": "Close"
4157
- },
4158
- /* @__PURE__ */ React27.createElement(
4159
- "svg",
4160
- {
4161
- width: "16",
4162
- height: "16",
4163
- viewBox: "0 0 24 24",
4164
- fill: "none",
4165
- stroke: "currentColor",
4166
- strokeWidth: "2",
4167
- strokeLinecap: "round",
4168
- strokeLinejoin: "round",
4169
- "aria-hidden": "true"
4170
- },
4171
- /* @__PURE__ */ React27.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
4172
- /* @__PURE__ */ React27.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
4173
- )
4174
- ))),
4175
- /* @__PURE__ */ React27.createElement("div", { className: "p-4" }, /* @__PURE__ */ React27.createElement(
4176
- "div",
4177
- {
4178
- className: "relative h-96 w-full overflow-hidden rounded-md bg-accent/40",
4179
- onMouseDown: (event) => {
4180
- event.preventDefault();
4181
- const startX = event.clientX - crop.x;
4182
- const startY = event.clientY - crop.y;
4183
- const handleMouseMove = (moveEvent) => {
4184
- onCropChange({
4185
- x: moveEvent.clientX - startX,
4186
- y: moveEvent.clientY - startY
4187
- });
4188
- };
4189
- const handleMouseUp = () => {
4190
- document.removeEventListener("mousemove", handleMouseMove);
4191
- document.removeEventListener("mouseup", handleMouseUp);
4192
- };
4193
- document.addEventListener("mousemove", handleMouseMove);
4194
- document.addEventListener("mouseup", handleMouseUp);
4195
- }
4196
- },
4197
- /* @__PURE__ */ React27.createElement(
4198
- "img",
4199
- {
4200
- src: imageToCrop.url,
4201
- alt: "Crop preview",
4202
- className: "absolute inset-0 h-full w-full object-contain",
4203
- style: {
4204
- transform: `translate(${crop.x}px, ${crop.y}px) scale(${zoom})`
4205
- },
4206
- draggable: false,
4207
- onLoad: (event) => {
4208
- const image = event.currentTarget;
4209
- const containerWidth = 600;
4210
- const containerHeight = 400;
4211
- const cropWidth = cropAspectRatio ? Math.min(
4212
- containerWidth * 0.8,
4213
- containerHeight * 0.8 * cropAspectRatio
4214
- ) : containerWidth * 0.8;
4215
- const cropHeight = cropAspectRatio ? cropWidth / cropAspectRatio : containerHeight * 0.8;
4216
- const imageWidth = image.naturalWidth;
4217
- const imageHeight = image.naturalHeight;
4218
- const scale = zoom;
4219
- const centerX = containerWidth / 2;
4220
- const centerY = containerHeight / 2;
4221
- const cropX = (centerX - crop.x - cropWidth / 2) / scale;
4222
- const cropY = (centerY - crop.y - cropHeight / 2) / scale;
4223
- onCropCompleteInternal(null, {
4224
- x: Math.max(0, cropX),
4225
- y: Math.max(0, cropY),
4226
- width: Math.min(cropWidth / scale, imageWidth),
4227
- height: Math.min(cropHeight / scale, imageHeight)
4228
- });
4229
- }
4230
- }
4231
- ),
4232
- /* @__PURE__ */ React27.createElement(
4233
- "div",
4234
- {
4235
- className: "pointer-events-none absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded border-2 border-primary",
4236
- style: {
4237
- width: cropAspectRatio ? `${Math.min(80, 80 * cropAspectRatio)}%` : "80%",
4238
- aspectRatio: cropAspectRatio ? String(cropAspectRatio) : void 0
4239
- }
4240
- },
4241
- /* @__PURE__ */ React27.createElement("div", { className: "absolute inset-0 grid grid-cols-3 grid-rows-3" }, /* @__PURE__ */ React27.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-r border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-b border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React27.createElement("div", { className: "border-r border-primary/30" }), /* @__PURE__ */ React27.createElement("div", null))
4242
- )
4243
- ), /* @__PURE__ */ React27.createElement("div", { className: "mt-4 flex items-center gap-3" }, /* @__PURE__ */ React27.createElement(
4244
- "label",
4245
- {
4246
- htmlFor: "zoom-slider",
4247
- className: "whitespace-nowrap text-sm font-medium"
4248
- },
4249
- "Zoom: ",
4250
- zoom.toFixed(1),
4251
- "x"
4252
- ), /* @__PURE__ */ React27.createElement(
4253
- "input",
4254
- {
4255
- id: "zoom-slider",
4256
- type: "range",
4257
- min: "1",
4258
- max: "3",
4259
- step: "0.1",
4260
- value: zoom,
4261
- onChange: (event) => onZoomChange(parseFloat(event.target.value)),
4262
- className: "h-2 flex-1 cursor-pointer appearance-none rounded-lg bg-accent/60",
4263
- "aria-label": "Zoom level"
4264
- }
4265
- ))),
4266
- /* @__PURE__ */ React27.createElement("div", { className: "flex items-center justify-end gap-2 border-t border-border p-4" }, /* @__PURE__ */ React27.createElement(Button, { type: "button", variant: "outline", onClick: handleCropCancel }, "Cancel"), /* @__PURE__ */ React27.createElement(Button, { type: "button", onClick: handleCropSave }, "Save"))
4267
- ) : null
4268
- ));
4269
- }
4270
- FileInput.displayName = "FileInput";
4271
- function Calendar({
4272
- className,
4273
- classNames,
4274
- showOutsideDays = true,
4275
- captionLayout = "label",
4276
- buttonVariant = "ghost",
4277
- formatters,
4278
- components,
4279
- ...props
4280
- }) {
4281
- const defaultClassNames = getDefaultClassNames();
4282
- return /* @__PURE__ */ React27.createElement(
4283
- DayPicker,
4284
- {
4285
- showOutsideDays,
4286
- className: cn(
4287
- "bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
4288
- String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
4289
- String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
4290
- className
4291
- ),
4292
- captionLayout,
4293
- formatters: {
4294
- formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
4295
- ...formatters
4296
- },
4297
- classNames: {
4298
- root: cn("w-fit", defaultClassNames.root),
4299
- months: cn(
4300
- "flex gap-4 flex-col md:flex-row relative",
4301
- defaultClassNames.months
4302
- ),
4303
- month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
4304
- nav: cn(
4305
- "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
4306
- defaultClassNames.nav
4307
- ),
4308
- button_previous: cn(
4309
- buttonVariants({ variant: buttonVariant }),
4310
- "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
4311
- defaultClassNames.button_previous
4312
- ),
4313
- button_next: cn(
4314
- buttonVariants({ variant: buttonVariant }),
4315
- "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
4316
- defaultClassNames.button_next
4317
- ),
4318
- month_caption: cn(
4319
- "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
4320
- defaultClassNames.month_caption
4321
- ),
4322
- dropdowns: cn(
4323
- "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
4324
- defaultClassNames.dropdowns
4325
- ),
4326
- dropdown_root: cn(
4327
- "relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
4328
- defaultClassNames.dropdown_root
4329
- ),
4330
- dropdown: cn(
4331
- "absolute bg-popover inset-0 opacity-0",
4332
- defaultClassNames.dropdown
4333
- ),
4334
- caption_label: cn(
4335
- "select-none font-medium",
4336
- 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",
4337
- defaultClassNames.caption_label
4338
- ),
4339
- table: "w-full border-collapse",
4340
- weekdays: cn("flex", defaultClassNames.weekdays),
4341
- weekday: cn(
4342
- "opacity-70 rounded-md flex-1 font-normal text-[0.8rem] select-none",
4343
- defaultClassNames.weekday
4344
- ),
4345
- week: cn("flex w-full mt-2", defaultClassNames.week),
4346
- week_number_header: cn(
4347
- "select-none w-(--cell-size)",
4348
- defaultClassNames.week_number_header
4349
- ),
4350
- week_number: cn(
4351
- "text-[0.8rem] select-none opacity-70",
4352
- defaultClassNames.week_number
4353
- ),
4354
- day: cn(
4355
- "relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
4356
- props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md" : "[&:first-child[data-selected=true]_button]:rounded-l-md",
4357
- defaultClassNames.day
4358
- ),
4359
- range_start: cn(
4360
- "rounded-l-md bg-accent",
4361
- defaultClassNames.range_start
4362
- ),
4363
- range_middle: cn("rounded-none", defaultClassNames.range_middle),
4364
- range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
4365
- today: cn(
4366
- "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
4367
- defaultClassNames.today
4368
- ),
4369
- outside: cn(
4370
- "opacity-50",
4371
- defaultClassNames.outside
4372
- ),
4373
- disabled: cn(
4374
- "opacity-50",
4375
- defaultClassNames.disabled
4376
- ),
4377
- hidden: cn("invisible", defaultClassNames.hidden),
4378
- ...classNames
4379
- },
4380
- components: {
4381
- Root: ({ className: className2, rootRef, ...props2 }) => {
4382
- return /* @__PURE__ */ React27.createElement(
4383
- "div",
4384
- {
4385
- "data-slot": "calendar",
4386
- ref: rootRef,
4387
- className: cn(className2),
4388
- ...props2
4389
- }
4390
- );
4391
- },
4392
- Chevron: ({ className: className2, orientation, ...props2 }) => {
4393
- if (orientation === "left") {
4394
- return /* @__PURE__ */ React27.createElement(
4395
- "svg",
4396
- {
4397
- className: cn("size-4", className2),
4398
- viewBox: "0 0 24 24",
4399
- fill: "none",
4400
- stroke: "currentColor",
4401
- strokeWidth: "2",
4402
- strokeLinecap: "round",
4403
- strokeLinejoin: "round",
4404
- ...props2
4405
- },
4406
- /* @__PURE__ */ React27.createElement("polyline", { points: "15 18 9 12 15 6" })
4407
- );
4408
- }
4409
- if (orientation === "right") {
4410
- return /* @__PURE__ */ React27.createElement(
4411
- "svg",
4412
- {
4413
- className: cn("size-4", className2),
4414
- viewBox: "0 0 24 24",
4415
- fill: "none",
4416
- stroke: "currentColor",
4417
- strokeWidth: "2",
4418
- strokeLinecap: "round",
4419
- strokeLinejoin: "round",
4420
- ...props2
4421
- },
4422
- /* @__PURE__ */ React27.createElement("polyline", { points: "9 18 15 12 9 6" })
4423
- );
4424
- }
4425
- return /* @__PURE__ */ React27.createElement(
4426
- "svg",
4427
- {
4428
- className: cn("size-4", className2),
4429
- viewBox: "0 0 24 24",
4430
- fill: "none",
4431
- stroke: "currentColor",
4432
- strokeWidth: "2",
4433
- strokeLinecap: "round",
4434
- strokeLinejoin: "round",
4435
- ...props2
4436
- },
4437
- /* @__PURE__ */ React27.createElement("polyline", { points: "6 9 12 15 18 9" })
4438
- );
4439
- },
4440
- DayButton: CalendarDayButton,
4441
- WeekNumber: ({ children, ...props2 }) => {
4442
- return /* @__PURE__ */ React27.createElement("td", { ...props2 }, /* @__PURE__ */ React27.createElement("div", { className: "flex size-(--cell-size) items-center justify-center text-center" }, children));
4443
- },
4444
- ...components
4445
- },
4446
- ...props
4447
- }
4448
- );
4449
- }
4450
- function CalendarDayButton({
4451
- className,
4452
- day,
4453
- modifiers,
4454
- ...props
4455
- }) {
4456
- const defaultClassNames = getDefaultClassNames();
4457
- const ref = React27.useRef(null);
4458
- React27.useEffect(() => {
4459
- if (modifiers.focused) ref.current?.focus();
4460
- }, [modifiers.focused]);
4461
- return /* @__PURE__ */ React27.createElement(
4462
- Button,
4463
- {
4464
- ref,
4465
- variant: "ghost",
4466
- size: "icon",
4467
- "data-day": day.date.toLocaleDateString(),
4468
- "data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
4469
- "data-range-start": modifiers.range_start,
4470
- "data-range-end": modifiers.range_end,
4471
- "data-range-middle": modifiers.range_middle,
4472
- className: cn(
4473
- // Core structure
4474
- "flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal",
4475
- // Selected states - uses CSS variables
4476
- "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground",
4477
- "data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground",
4478
- "data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground",
4479
- "data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground",
4480
- // Focus state
4481
- "group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10",
4482
- "group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-[3px]",
4483
- // Rounding based on position
4484
- "data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md",
4485
- "data-[range-middle=true]:rounded-none",
4486
- "data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md",
4487
- // Nested span styling
4488
- "[&>span]:text-xs [&>span]:opacity-70",
4489
- defaultClassNames.day,
4490
- className
4491
- ),
4492
- ...props
4493
- }
4494
- );
4495
- }
4496
-
4497
- // src/inputs/DatePicker.tsx
4498
- function formatDate(date, format) {
4499
- if (!date) return "";
4500
- const d = new Date(date);
4501
- const month = String(d.getMonth() + 1).padStart(2, "0");
4502
- const day = String(d.getDate()).padStart(2, "0");
4503
- const year = d.getFullYear();
4504
- return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
4505
- }
4506
- function DatePickerDayButton({
4507
- day,
4508
- modifiers,
4509
- className,
4510
- children,
4511
- ...props
4512
- }) {
4513
- return /* @__PURE__ */ React27.createElement(
4514
- "button",
4515
- {
4516
- type: "button",
4517
- className: cn(
4518
- "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
4519
- "hover:bg-accent",
4520
- modifiers.selected && "bg-primary text-primary-foreground font-semibold",
4521
- !modifiers.selected && modifiers.today && "border border-primary",
4522
- modifiers.disabled && "cursor-not-allowed opacity-50 pointer-events-none",
4523
- className
4524
- ),
4525
- ...props
4526
- },
4527
- children ?? day.date.getDate()
4528
- );
4529
- }
4530
- function DatePicker({
4531
- name,
4532
- value,
4533
- onChange,
4534
- onBlur,
4535
- disabled = false,
4536
- required = false,
4537
- error = false,
4538
- className = "",
4539
- placeholder = "Select date...",
4540
- format = "MM/dd/yyyy",
4541
- minDate,
4542
- maxDate,
4543
- disabledDates = [],
4544
- isDateDisabled,
4545
- clearable = true,
4546
- showIcon = true,
4547
- ...props
4548
- }) {
4549
- const [isOpen, setIsOpen] = React27.useState(false);
4550
- const [hasInteracted, setHasInteracted] = React27.useState(false);
4551
- const [selectedMonth, setSelectedMonth] = React27.useState(
4552
- value || /* @__PURE__ */ new Date()
4553
- );
4554
- const inputRef = React27.useRef(null);
4555
- React27.useEffect(() => {
4556
- if (value) {
4557
- setSelectedMonth(value);
4558
- }
4559
- }, [value]);
4560
- const disabledMatchers = React27.useMemo(() => {
4561
- const matchers = [];
4562
- if (minDate) {
4563
- matchers.push({ before: minDate });
4564
- }
4565
- if (maxDate) {
4566
- matchers.push({ after: maxDate });
4567
- }
4568
- if (disabledDates.length > 0) {
4569
- matchers.push(disabledDates);
4570
- }
4571
- if (isDateDisabled) {
4572
- matchers.push(isDateDisabled);
4573
- }
4574
- return matchers;
4575
- }, [disabledDates, isDateDisabled, maxDate, minDate]);
4576
- const handleDateSelect = React27.useCallback(
4577
- (date) => {
4578
- if (!date) return;
4579
- onChange(date);
4580
- setSelectedMonth(date);
4581
- setIsOpen(false);
4582
- onBlur?.();
4583
- },
4584
- [onBlur, onChange]
4585
- );
4586
- const handleClear = React27.useCallback(
4587
- (e) => {
4588
- e.stopPropagation();
4589
- onChange(null);
4590
- setIsOpen(false);
4591
- inputRef.current?.focus();
4592
- },
4593
- [onChange]
4594
- );
4595
- const handleOpenChange = React27.useCallback(
4596
- (nextOpen) => {
4597
- if (disabled) {
4598
- setIsOpen(false);
4599
- return;
4600
- }
4601
- if (nextOpen) {
4602
- if (!hasInteracted) {
4603
- setHasInteracted(true);
4604
- }
4605
- setIsOpen(true);
4606
- return;
4607
- }
4608
- if (isOpen && hasInteracted) {
4609
- onBlur?.();
4610
- }
4611
- setIsOpen(false);
4612
- },
4613
- [disabled, hasInteracted, isOpen, onBlur]
4614
- );
4615
- const handleInputBlur = React27.useCallback(() => {
4616
- if (!isOpen) {
4617
- onBlur?.();
4618
- }
4619
- }, [isOpen, onBlur]);
4620
- const handleInputClick = React27.useCallback(() => {
4621
- if (!hasInteracted) {
4622
- setHasInteracted(true);
4623
- }
4624
- }, [hasInteracted]);
4625
- const hasValue = Boolean(value);
4626
- const displayValue = formatDate(value, format);
4627
- const combinedClassName = cn("relative", className);
4628
- return /* @__PURE__ */ React27.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React27.createElement(
4629
- "input",
4630
- {
4631
- type: "hidden",
4632
- name,
4633
- value: value ? value.toISOString() : ""
4634
- }
4635
- ), /* @__PURE__ */ React27.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React27.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React27.createElement(
4636
- "span",
4637
- {
4638
- className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
4639
- "aria-hidden": "true"
4640
- },
4641
- /* @__PURE__ */ React27.createElement(
4642
- "svg",
4643
- {
4644
- xmlns: "http://www.w3.org/2000/svg",
4645
- width: "18",
4646
- height: "18",
4647
- viewBox: "0 0 24 24",
4648
- fill: "none",
4649
- stroke: "currentColor",
4650
- strokeLinecap: "round",
4651
- strokeLinejoin: "round",
4652
- strokeWidth: "2"
4653
- },
4654
- /* @__PURE__ */ React27.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" })
4655
- )
4656
- ), /* @__PURE__ */ React27.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React27.createElement(
4657
- "input",
4658
- {
4659
- ref: inputRef,
4660
- id: props.id,
4661
- type: "text",
4662
- className: cn(
4663
- "flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
4664
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
4665
- "disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
4666
- INPUT_AUTOFILL_RESET_CLASSES,
4667
- showIcon ? "pl-10" : "pl-3",
4668
- clearable && value ? "pr-10" : "pr-3",
4669
- !error && hasValue && "ring-2 ring-ring",
4670
- error && "border-destructive ring-1 ring-destructive"
4671
- ),
4672
- value: displayValue,
4673
- onClick: handleInputClick,
4674
- onBlur: handleInputBlur,
4675
- disabled,
4676
- required,
4677
- placeholder,
4678
- "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
4679
- "aria-describedby": props["aria-describedby"],
4680
- "aria-required": required || props["aria-required"],
4681
- readOnly: true
4682
- }
4683
- )), clearable && value && !disabled && /* @__PURE__ */ React27.createElement(
4684
- "button",
4685
- {
4686
- type: "button",
4687
- className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
4688
- onClick: handleClear,
4689
- "aria-label": "Clear date",
4690
- tabIndex: -1
4691
- },
4692
- "\u2715"
4693
- )), !disabled && /* @__PURE__ */ React27.createElement(
4694
- PopoverContent,
4695
- {
4696
- align: "start",
4697
- sideOffset: 4,
4698
- className: "w-auto p-0",
4699
- onOpenAutoFocus: (event) => {
4700
- event.preventDefault();
4701
- }
4702
- },
4703
- /* @__PURE__ */ React27.createElement(
4704
- Calendar,
4705
- {
4706
- mode: "single",
4707
- selected: value ?? void 0,
4708
- onSelect: handleDateSelect,
4709
- month: selectedMonth,
4710
- onMonthChange: setSelectedMonth,
4711
- disabled: disabledMatchers,
4712
- showOutsideDays: true,
4713
- labels: {
4714
- labelGrid: () => "Calendar",
4715
- labelDayButton: (date) => formatDate(date, format),
4716
- labelPrevious: () => "Previous month",
4717
- labelNext: () => "Next month"
4718
- },
4719
- components: {
4720
- DayButton: DatePickerDayButton
4721
- },
4722
- classNames: {
4723
- today: "border border-primary rounded-md bg-transparent"
4724
- }
4725
- }
4726
- )
4727
- )));
4728
- }
4729
- DatePicker.displayName = "DatePicker";
4730
- function normalizeToNativeTime(value) {
4731
- if (!value) return "";
4732
- const twelveHourMatch = value.match(
4733
- /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s*(AM|PM)$/i
4734
- );
4735
- if (twelveHourMatch) {
4736
- const rawHour = parseInt(twelveHourMatch[1], 10);
4737
- const minute = parseInt(twelveHourMatch[2], 10);
4738
- const period = twelveHourMatch[4].toUpperCase();
4739
- if (Number.isNaN(rawHour) || Number.isNaN(minute) || rawHour < 1 || rawHour > 12 || minute < 0 || minute > 59) {
4740
- return "";
4741
- }
4742
- const normalizedHour = period === "PM" ? rawHour === 12 ? 12 : rawHour + 12 : rawHour === 12 ? 0 : rawHour;
4743
- return `${String(normalizedHour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
4744
- }
4745
- const twentyFourHourMatch = value.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
4746
- if (twentyFourHourMatch) {
4747
- const hour = parseInt(twentyFourHourMatch[1], 10);
4748
- const minute = parseInt(twentyFourHourMatch[2], 10);
4749
- if (Number.isNaN(hour) || Number.isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) {
4750
- return "";
4751
- }
4752
- return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
4753
- }
4754
- return "";
4755
- }
4756
- function formatFromNativeTime(nativeValue, use24Hour) {
4757
- if (!nativeValue) return "";
4758
- const [hourValue, minuteValue] = nativeValue.split(":");
4759
- const hour = parseInt(hourValue, 10);
4760
- const minute = parseInt(minuteValue, 10);
4761
- if (Number.isNaN(hour) || Number.isNaN(minute)) {
4762
- return "";
4763
- }
4764
- if (use24Hour) {
4765
- return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
4766
- }
4767
- const period = hour >= 12 ? "PM" : "AM";
4768
- const hour12 = hour % 12 || 12;
4769
- return `${hour12}:${String(minute).padStart(2, "0")} ${period}`;
4770
- }
4771
- function TimePicker({
4772
- name,
4773
- value,
4774
- onChange,
4775
- onBlur,
4776
- disabled = false,
4777
- required = false,
4778
- error = false,
4779
- className = "",
4780
- placeholder = "Select time...",
4781
- use24Hour = false,
4782
- minuteStep = 1,
4783
- clearable = true,
4784
- showIcon = true,
4785
- ...props
4786
- }) {
4787
- const inputRef = React27.useRef(null);
4788
- const [nativeValue, setNativeValue] = React27.useState(
4789
- normalizeToNativeTime(value)
4790
- );
4791
- React27.useEffect(() => {
4792
- setNativeValue(normalizeToNativeTime(value));
4793
- }, [value]);
4794
- const handleChange = (e) => {
4795
- const nextNativeValue = e.target.value;
4796
- setNativeValue(nextNativeValue);
4797
- onChange(formatFromNativeTime(nextNativeValue, use24Hour));
4798
- };
4799
- const handleClear = (e) => {
4800
- e.stopPropagation();
4801
- setNativeValue("");
4802
- onChange("");
4803
- inputRef.current?.focus();
4804
- };
4805
- const hasValue = Boolean(value);
4806
- const stepInSeconds = Math.max(1, minuteStep * 60);
4807
- return /* @__PURE__ */ React27.createElement("div", { className: cn("relative", className) }, /* @__PURE__ */ React27.createElement("input", { type: "hidden", name, value }), /* @__PURE__ */ React27.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React27.createElement(
4808
- "span",
4809
- {
4810
- className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
4811
- "aria-hidden": "true"
4812
- },
4813
- /* @__PURE__ */ React27.createElement(
4814
- "svg",
4815
- {
4816
- xmlns: "http://www.w3.org/2000/svg",
4817
- width: "18",
4818
- height: "18",
4819
- viewBox: "0 0 24 24",
4820
- fill: "none",
4821
- stroke: "currentColor",
4822
- strokeLinecap: "round",
4823
- strokeLinejoin: "round",
4824
- strokeWidth: "2"
4825
- },
4826
- /* @__PURE__ */ React27.createElement("circle", { cx: "12", cy: "12", r: "10" }),
4827
- /* @__PURE__ */ React27.createElement("path", { d: "M12 6v6l4 2" })
4828
- )
4829
- ), /* @__PURE__ */ React27.createElement(
4830
- Input,
4831
- {
4832
- ref: inputRef,
4833
- type: "time",
4834
- className: cn(
4835
- "appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none",
4836
- INPUT_AUTOFILL_RESET_CLASSES,
4837
- showIcon ? "pl-10" : "pl-3",
4838
- clearable && value ? "pr-10" : "pr-3",
4839
- !error && hasValue && "ring-2 ring-ring",
4840
- error && "border-destructive ring-1 ring-destructive"
4841
- ),
4842
- value: nativeValue,
4843
- onChange: handleChange,
4844
- onBlur,
4845
- disabled,
4846
- required,
4847
- step: stepInSeconds,
4848
- placeholder,
4849
- "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
4850
- "aria-describedby": props["aria-describedby"],
4851
- "aria-required": required || props["aria-required"],
4852
- ...props
4853
- }
4854
- ), clearable && value && !disabled && /* @__PURE__ */ React27.createElement(
4855
- Button,
4856
- {
4857
- type: "button",
4858
- variant: "ghost",
4859
- size: "icon",
4860
- className: "absolute right-1.5 top-1/2 h-7 w-7 -translate-y-1/2 p-0",
4861
- onClick: handleClear,
4862
- "aria-label": "Clear time",
4863
- tabIndex: -1
4864
- },
4865
- /* @__PURE__ */ React27.createElement(
4866
- "svg",
4867
- {
4868
- width: "14",
4869
- height: "14",
4870
- viewBox: "0 0 24 24",
4871
- fill: "none",
4872
- stroke: "currentColor",
4873
- strokeWidth: "2",
4874
- strokeLinecap: "round",
4875
- strokeLinejoin: "round",
4876
- "aria-hidden": "true"
4877
- },
4878
- /* @__PURE__ */ React27.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
4879
- /* @__PURE__ */ React27.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
4880
- )
4881
- )));
4882
- }
4883
- TimePicker.displayName = "TimePicker";
4884
- function formatDate2(date, format) {
4885
- if (!date) return "";
4886
- const d = new Date(date);
4887
- const month = String(d.getMonth() + 1).padStart(2, "0");
4888
- const day = String(d.getDate()).padStart(2, "0");
4889
- const year = d.getFullYear();
4890
- return format.replace("MM", month).replace("dd", day).replace("yyyy", String(year)).replace("yy", String(year).slice(2));
4891
- }
4892
- function toDayTimestamp(date) {
4893
- return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
4894
- }
4895
- function isSameDay(date, target) {
4896
- if (!target) return false;
4897
- return toDayTimestamp(date) === toDayTimestamp(target);
4898
- }
4899
- function isDateInRange(date, start, end) {
4900
- if (!start || !end) return false;
4901
- const value = toDayTimestamp(date);
4902
- const startTs = toDayTimestamp(start);
4903
- const endTs = toDayTimestamp(end);
4904
- return value >= Math.min(startTs, endTs) && value <= Math.max(startTs, endTs);
4905
- }
4906
- function DateRangePicker({
4907
- name,
4908
- value = { start: null, end: null },
4909
- onChange,
4910
- onBlur,
4911
- disabled = false,
4912
- required = false,
4913
- error = false,
4914
- className = "",
4915
- placeholder = "Select date range...",
4916
- format = "MM/dd/yyyy",
4917
- minDate,
4918
- maxDate,
4919
- disabledDates = [],
4920
- isDateDisabled,
4921
- clearable = true,
4922
- showIcon = true,
4923
- separator = " - ",
4924
- ...props
4925
- }) {
4926
- const [isOpen, setIsOpen] = React27.useState(false);
4927
- const [hasInteracted, setHasInteracted] = React27.useState(false);
4928
- const [selectedMonth, setSelectedMonth] = React27.useState(
4929
- value.start || /* @__PURE__ */ new Date()
4930
- );
4931
- const [rangeStart, setRangeStart] = React27.useState(value.start);
4932
- const [rangeEnd, setRangeEnd] = React27.useState(value.end);
4933
- const [hoverDate, setHoverDate] = React27.useState(null);
4934
- const inputRef = React27.useRef(null);
4935
- React27.useEffect(() => {
4936
- setRangeStart(value.start);
4937
- setRangeEnd(value.end);
4938
- if (value.start) {
4939
- setSelectedMonth(value.start);
4940
- }
4941
- }, [value]);
4942
- const disabledMatchers = React27.useMemo(() => {
4943
- const matchers = [];
4944
- if (minDate) {
4945
- matchers.push({ before: minDate });
4946
- }
4947
- if (maxDate) {
4948
- matchers.push({ after: maxDate });
4949
- }
4950
- if (disabledDates.length > 0) {
4951
- matchers.push(disabledDates);
4952
- }
4953
- if (isDateDisabled) {
4954
- matchers.push(isDateDisabled);
4955
- }
4956
- return matchers;
4957
- }, [disabledDates, isDateDisabled, maxDate, minDate]);
4958
- const handleDateSelect = React27.useCallback(
4959
- (date) => {
4960
- if (!rangeStart || rangeEnd) {
4961
- setRangeStart(date);
4962
- setRangeEnd(null);
4963
- setHoverDate(null);
4964
- setSelectedMonth(date);
4965
- onChange({ start: date, end: null });
4966
- onBlur?.();
4967
- return;
4968
- }
4969
- if (toDayTimestamp(date) < toDayTimestamp(rangeStart)) {
4970
- setRangeStart(date);
4971
- setRangeEnd(rangeStart);
4972
- setHoverDate(null);
4973
- setSelectedMonth(date);
4974
- onChange({ start: date, end: rangeStart });
4975
- setIsOpen(false);
4976
- onBlur?.();
4977
- return;
4978
- }
4979
- setRangeEnd(date);
4980
- setHoverDate(null);
4981
- setSelectedMonth(date);
4982
- onChange({ start: rangeStart, end: date });
4983
- setIsOpen(false);
4984
- onBlur?.();
4985
- },
4986
- [onBlur, onChange, rangeEnd, rangeStart]
4987
- );
4988
- const handleClear = React27.useCallback(
4989
- (e) => {
4990
- e.stopPropagation();
4991
- setRangeStart(null);
4992
- setRangeEnd(null);
4993
- setHoverDate(null);
4994
- setIsOpen(false);
4995
- onChange({ start: null, end: null });
4996
- inputRef.current?.focus();
4997
- },
4998
- [onChange]
4999
- );
5000
- const handleOpenChange = React27.useCallback(
5001
- (nextOpen) => {
5002
- if (disabled) {
5003
- setIsOpen(false);
5004
- return;
5005
- }
5006
- if (nextOpen) {
5007
- if (!hasInteracted) {
5008
- setHasInteracted(true);
5009
- }
5010
- setIsOpen(true);
5011
- return;
5012
- }
5013
- if (isOpen && hasInteracted) {
5014
- onBlur?.();
5015
- }
5016
- setHoverDate(null);
5017
- setIsOpen(false);
5018
- },
5019
- [disabled, hasInteracted, isOpen, onBlur]
5020
- );
5021
- const handleInputBlur = React27.useCallback(() => {
5022
- if (!isOpen) {
5023
- onBlur?.();
5024
- }
5025
- }, [isOpen, onBlur]);
5026
- const handleInputClick = React27.useCallback(() => {
5027
- if (!hasInteracted) {
5028
- setHasInteracted(true);
5029
- }
5030
- }, [hasInteracted]);
5031
- const RangeDayButton = React27.useCallback(
5032
- ({
5033
- day,
5034
- modifiers,
5035
- className: dayClassName,
5036
- children,
5037
- onClick,
5038
- onMouseEnter,
5039
- onMouseLeave,
5040
- ...rest
5041
- }) => {
5042
- const date = day.date;
5043
- const isStart = isSameDay(date, rangeStart);
5044
- const isEnd = isSameDay(date, rangeEnd);
5045
- const isRangeEndpoint = isStart || isEnd;
5046
- const isInCommittedRange = isDateInRange(date, rangeStart, rangeEnd);
5047
- const isInHoverRange = !!rangeStart && !rangeEnd && !!hoverDate && isDateInRange(date, rangeStart, hoverDate);
5048
- const isRangeHighlight = (isInCommittedRange || isInHoverRange) && !isRangeEndpoint;
5049
- const isToday = isSameDay(date, /* @__PURE__ */ new Date());
5050
- return /* @__PURE__ */ React27.createElement(
5051
- "button",
5052
- {
5053
- type: "button",
5054
- ...rest,
5055
- className: cn(
5056
- "flex items-center justify-center h-8 w-8 rounded-md border-none bg-transparent cursor-pointer text-sm transition-colors",
5057
- "hover:bg-accent",
5058
- isRangeEndpoint && "bg-primary text-primary-foreground font-semibold",
5059
- isRangeHighlight && "bg-accent",
5060
- !isRangeEndpoint && !isRangeHighlight && isToday && "border border-primary",
5061
- modifiers.disabled && "cursor-not-allowed opacity-50 pointer-events-none",
5062
- dayClassName
5063
- ),
5064
- onClick: (event) => {
5065
- onClick?.(event);
5066
- if (modifiers.disabled) return;
5067
- handleDateSelect(date);
5068
- },
5069
- onMouseEnter: (event) => {
5070
- onMouseEnter?.(event);
5071
- if (modifiers.disabled) {
5072
- setHoverDate(null);
5073
- return;
5074
- }
5075
- setHoverDate(date);
5076
- },
5077
- onMouseLeave: (event) => {
5078
- onMouseLeave?.(event);
5079
- setHoverDate(null);
5080
- }
5081
- },
5082
- children ?? date.getDate()
5083
- );
5084
- },
5085
- [handleDateSelect, hoverDate, rangeEnd, rangeStart]
5086
- );
5087
- const hasValue = Boolean(rangeStart || rangeEnd);
5088
- const selectedRange = rangeStart || rangeEnd ? {
5089
- from: rangeStart ?? void 0,
5090
- to: rangeEnd ?? void 0
5091
- } : void 0;
5092
- const displayValue = rangeStart && rangeEnd ? `${formatDate2(rangeStart, format)}${separator}${formatDate2(rangeEnd, format)}` : rangeStart ? formatDate2(rangeStart, format) : "";
5093
- const combinedClassName = cn("relative", className);
5094
- return /* @__PURE__ */ React27.createElement("div", { className: combinedClassName }, /* @__PURE__ */ React27.createElement(
5095
- "input",
5096
- {
5097
- type: "hidden",
5098
- name: `${name}[start]`,
5099
- value: rangeStart ? rangeStart.toISOString() : ""
5100
- }
5101
- ), /* @__PURE__ */ React27.createElement(
5102
- "input",
5103
- {
5104
- type: "hidden",
5105
- name: `${name}[end]`,
5106
- value: rangeEnd ? rangeEnd.toISOString() : ""
5107
- }
5108
- ), /* @__PURE__ */ React27.createElement(Popover, { open: isOpen, onOpenChange: handleOpenChange }, /* @__PURE__ */ React27.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React27.createElement(
5109
- "span",
5110
- {
5111
- className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
5112
- "aria-hidden": "true"
5113
- },
5114
- /* @__PURE__ */ React27.createElement(
5115
- "svg",
5116
- {
5117
- xmlns: "http://www.w3.org/2000/svg",
5118
- width: "18",
5119
- height: "18",
5120
- viewBox: "0 0 24 24",
5121
- fill: "none",
5122
- stroke: "currentColor",
5123
- strokeLinecap: "round",
5124
- strokeLinejoin: "round",
5125
- strokeWidth: "2"
5126
- },
5127
- /* @__PURE__ */ React27.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" })
5128
- )
5129
- ), /* @__PURE__ */ React27.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React27.createElement(
5130
- "input",
5131
- {
5132
- ref: inputRef,
5133
- id: props.id,
5134
- type: "text",
5135
- className: cn(
5136
- "flex h-9 w-full rounded-md border border-input bg-transparent py-1 text-base shadow-sm transition-colors",
5137
- "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
5138
- "disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
5139
- INPUT_AUTOFILL_RESET_CLASSES,
5140
- showIcon ? "pl-10" : "pl-3",
5141
- clearable && (rangeStart || rangeEnd) ? "pr-10" : "pr-3",
5142
- !error && hasValue && "ring-2 ring-ring",
5143
- error && "border-destructive ring-1 ring-destructive"
5144
- ),
5145
- value: displayValue,
5146
- onClick: handleInputClick,
5147
- onBlur: handleInputBlur,
5148
- disabled,
5149
- required,
5150
- placeholder,
5151
- "aria-invalid": error || props["aria-invalid"] ? "true" : "false",
5152
- "aria-describedby": props["aria-describedby"],
5153
- "aria-required": required || props["aria-required"],
5154
- readOnly: true
5155
- }
5156
- )), clearable && (rangeStart || rangeEnd) && !disabled && /* @__PURE__ */ React27.createElement(
5157
- "button",
5158
- {
5159
- type: "button",
5160
- className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
5161
- onClick: handleClear,
5162
- "aria-label": "Clear date range",
5163
- tabIndex: -1
5164
- },
5165
- "\u2715"
5166
- )), !disabled && /* @__PURE__ */ React27.createElement(
5167
- PopoverContent,
5168
- {
5169
- align: "start",
5170
- sideOffset: 4,
5171
- className: "w-auto p-0",
5172
- onOpenAutoFocus: (event) => {
5173
- event.preventDefault();
5174
- }
5175
- },
5176
- /* @__PURE__ */ React27.createElement(
5177
- Calendar,
5178
- {
5179
- mode: "range",
5180
- selected: selectedRange,
5181
- month: selectedMonth,
5182
- onMonthChange: setSelectedMonth,
5183
- disabled: disabledMatchers,
5184
- labels: {
5185
- labelGrid: () => "Calendar",
5186
- labelDayButton: (date) => formatDate2(date, format),
5187
- labelPrevious: () => "Previous month",
5188
- labelNext: () => "Next month"
5189
- },
5190
- components: {
5191
- DayButton: RangeDayButton
5192
- },
5193
- classNames: {
5194
- today: "border border-primary rounded-md bg-transparent"
5195
- },
5196
- showOutsideDays: true
5197
- }
5198
- ),
5199
- rangeStart && !rangeEnd && /* @__PURE__ */ React27.createElement("div", { className: "border-t border-input px-3 py-2 text-center text-xs opacity-70" }, "Select end date")
5200
- )));
5201
- }
5202
- DateRangePicker.displayName = "DateRangePicker";
5203
-
5204
- // src/integration/DynamicFormField.tsx
5205
- function DynamicFormField({
5206
- field,
5207
- className,
5208
- uploadProgress = {},
5209
- onFileUpload,
5210
- onFileRemove,
5211
- isUploading = false
5212
- }) {
5213
- const fieldId = field.name;
5214
- const usesGroupLegend = field.type === "radio" || field.type === "checkbox-group";
5215
- const usesInlineCheckboxLabel = field.type === "checkbox";
5216
- const shouldRenderFieldLabel = !usesGroupLegend && !usesInlineCheckboxLabel;
5217
- return /* @__PURE__ */ React27.createElement(
5218
- Field2,
5219
- {
5220
- name: field.name,
5221
- label: shouldRenderFieldLabel ? field.label : void 0,
5222
- description: shouldRenderFieldLabel ? field.description : void 0,
5223
- required: field.required,
5224
- className: cn("space-y-2", className)
5225
632
  },
5226
- ({ field: formField, meta }) => /* @__PURE__ */ React27.createElement("div", null, (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ React27.createElement(
633
+ ({ field: formField, meta }) => /* @__PURE__ */ React2.createElement("div", null, (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ React2.createElement(
5227
634
  TextInput,
5228
635
  {
5229
636
  ...formField,
@@ -5234,7 +641,7 @@ function DynamicFormField({
5234
641
  disabled: field.disabled,
5235
642
  "aria-label": field.label
5236
643
  }
5237
- ), field.type === "number" && /* @__PURE__ */ React27.createElement(
644
+ ), field.type === "number" && /* @__PURE__ */ React2.createElement(
5238
645
  TextInput,
5239
646
  {
5240
647
  ...formField,
@@ -5245,7 +652,7 @@ function DynamicFormField({
5245
652
  disabled: field.disabled,
5246
653
  "aria-label": field.label
5247
654
  }
5248
- ), field.type === "textarea" && /* @__PURE__ */ React27.createElement(
655
+ ), field.type === "textarea" && /* @__PURE__ */ React2.createElement(
5249
656
  TextArea,
5250
657
  {
5251
658
  ...formField,
@@ -5256,8 +663,8 @@ function DynamicFormField({
5256
663
  disabled: field.disabled,
5257
664
  "aria-label": field.label
5258
665
  }
5259
- ), field.type === "select" && field.options && /* @__PURE__ */ React27.createElement(
5260
- Select2,
666
+ ), field.type === "select" && field.options && /* @__PURE__ */ React2.createElement(
667
+ Select,
5261
668
  {
5262
669
  ...formField,
5263
670
  id: fieldId,
@@ -5267,7 +674,7 @@ function DynamicFormField({
5267
674
  disabled: field.disabled,
5268
675
  "aria-label": field.label
5269
676
  }
5270
- ), field.type === "multi-select" && field.options && /* @__PURE__ */ React27.createElement(
677
+ ), field.type === "multi-select" && field.options && /* @__PURE__ */ React2.createElement(
5271
678
  MultiSelect,
5272
679
  {
5273
680
  ...formField,
@@ -5278,7 +685,7 @@ function DynamicFormField({
5278
685
  disabled: field.disabled,
5279
686
  "aria-label": field.label
5280
687
  }
5281
- ), field.type === "radio" && field.options && /* @__PURE__ */ React27.createElement(
688
+ ), field.type === "radio" && field.options && /* @__PURE__ */ React2.createElement(
5282
689
  Radio,
5283
690
  {
5284
691
  ...formField,
@@ -5292,8 +699,8 @@ function DynamicFormField({
5292
699
  error: meta.touched && !!meta.error,
5293
700
  "aria-label": field.label
5294
701
  }
5295
- ), field.type === "checkbox" && /* @__PURE__ */ React27.createElement(
5296
- Checkbox2,
702
+ ), field.type === "checkbox" && /* @__PURE__ */ React2.createElement(
703
+ Checkbox,
5297
704
  {
5298
705
  ...formField,
5299
706
  id: fieldId,
@@ -5306,7 +713,7 @@ function DynamicFormField({
5306
713
  error: meta.touched && !!meta.error,
5307
714
  "aria-label": field.label
5308
715
  }
5309
- ), field.type === "checkbox-group" && field.options && /* @__PURE__ */ React27.createElement(
716
+ ), field.type === "checkbox-group" && field.options && /* @__PURE__ */ React2.createElement(
5310
717
  CheckboxGroup,
5311
718
  {
5312
719
  ...formField,
@@ -5320,7 +727,7 @@ function DynamicFormField({
5320
727
  error: meta.touched && !!meta.error,
5321
728
  "aria-label": field.label
5322
729
  }
5323
- ), (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ React27.createElement(
730
+ ), (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ React2.createElement(
5324
731
  DatePicker,
5325
732
  {
5326
733
  ...formField,
@@ -5330,7 +737,7 @@ function DynamicFormField({
5330
737
  disabled: field.disabled,
5331
738
  "aria-label": field.label
5332
739
  }
5333
- ), field.type === "date-range" && /* @__PURE__ */ React27.createElement(
740
+ ), field.type === "date-range" && /* @__PURE__ */ React2.createElement(
5334
741
  DateRangePicker,
5335
742
  {
5336
743
  ...formField,
@@ -5340,7 +747,7 @@ function DynamicFormField({
5340
747
  disabled: field.disabled,
5341
748
  "aria-label": field.label
5342
749
  }
5343
- ), field.type === "time" && /* @__PURE__ */ React27.createElement(
750
+ ), field.type === "time" && /* @__PURE__ */ React2.createElement(
5344
751
  TimePicker,
5345
752
  {
5346
753
  ...formField,
@@ -5350,7 +757,7 @@ function DynamicFormField({
5350
757
  disabled: field.disabled,
5351
758
  "aria-label": field.label
5352
759
  }
5353
- ), field.type === "file" && /* @__PURE__ */ React27.createElement(
760
+ ), field.type === "file" && /* @__PURE__ */ React2.createElement(
5354
761
  FileInput,
5355
762
  {
5356
763
  ...formField,