@page-speed/forms 0.5.2 → 0.5.3

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