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