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