@underverse-ui/underverse 0.1.31 → 0.1.32
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/index.cjs +520 -544
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +520 -544
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -613,583 +613,557 @@ var import_react3 = __toESM(require("react"), 1);
|
|
|
613
613
|
var import_next_intl = require("next-intl");
|
|
614
614
|
var import_lucide_react3 = require("lucide-react");
|
|
615
615
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
616
|
-
var Input = (0, import_react3.forwardRef)(
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
"text-foreground group-focus-within:text-primary",
|
|
747
|
-
|
|
616
|
+
var Input = (0, import_react3.forwardRef)(
|
|
617
|
+
({
|
|
618
|
+
label,
|
|
619
|
+
error,
|
|
620
|
+
description,
|
|
621
|
+
className,
|
|
622
|
+
required,
|
|
623
|
+
variant = "default",
|
|
624
|
+
size = "md",
|
|
625
|
+
leftIcon: LeftIcon,
|
|
626
|
+
rightIcon: RightIcon,
|
|
627
|
+
clearable = false,
|
|
628
|
+
loading: loading2 = false,
|
|
629
|
+
success = false,
|
|
630
|
+
onClear,
|
|
631
|
+
hint,
|
|
632
|
+
counter = false,
|
|
633
|
+
type = "text",
|
|
634
|
+
value,
|
|
635
|
+
maxLength,
|
|
636
|
+
...rest
|
|
637
|
+
}, ref) => {
|
|
638
|
+
const [localError, setLocalError] = (0, import_react3.useState)(error);
|
|
639
|
+
const [showPassword, setShowPassword] = (0, import_react3.useState)(false);
|
|
640
|
+
const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
|
|
641
|
+
const tv = (0, import_next_intl.useTranslations)("ValidationInput");
|
|
642
|
+
const autoId = (0, import_react3.useId)();
|
|
643
|
+
const needsId = !!(label || description || hint || error);
|
|
644
|
+
const resolvedId = rest.id || (needsId ? `input-${autoId}` : void 0);
|
|
645
|
+
const errMsg = error || localError;
|
|
646
|
+
const hasValue = value !== void 0 && value !== null && value !== "";
|
|
647
|
+
const charCount = typeof value === "string" ? value.length : 0;
|
|
648
|
+
const errorId = errMsg && resolvedId ? `${resolvedId}-error` : void 0;
|
|
649
|
+
const descId = !errMsg && (description || hint) && resolvedId ? `${resolvedId}-desc` : void 0;
|
|
650
|
+
const containerSpacing = size === "sm" ? "space-y-1.5" : "space-y-2";
|
|
651
|
+
const variantStyles6 = {
|
|
652
|
+
default: {
|
|
653
|
+
container: "bg-background border border-input hover:border-accent-foreground/20",
|
|
654
|
+
focus: "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
655
|
+
error: "border-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-destructive focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent"
|
|
656
|
+
},
|
|
657
|
+
filled: {
|
|
658
|
+
container: "bg-muted/50 border border-transparent hover:bg-muted/70",
|
|
659
|
+
focus: "focus-visible:outline-none focus-visible:bg-background focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
660
|
+
error: "bg-destructive/10 border-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-destructive focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent"
|
|
661
|
+
},
|
|
662
|
+
outlined: {
|
|
663
|
+
container: "bg-transparent border border-border hover:border-accent-foreground/30",
|
|
664
|
+
focus: "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
665
|
+
error: "border-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-destructive focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent"
|
|
666
|
+
},
|
|
667
|
+
minimal: {
|
|
668
|
+
container: "bg-transparent border-0 border-b border-border hover:border-accent-foreground/30",
|
|
669
|
+
focus: "focus-visible:outline-none focus-visible:border-ring focus-visible:ring-0 rounded-none",
|
|
670
|
+
error: "border-destructive focus-visible:outline-none focus-visible:border-destructive"
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
const sizeStyles8 = {
|
|
674
|
+
sm: {
|
|
675
|
+
input: "px-3 py-1.5 text-sm h-8 md:h-7 md:py-1 md:text-xs",
|
|
676
|
+
icon: "w-4 h-4",
|
|
677
|
+
button: "h-7 w-7"
|
|
678
|
+
},
|
|
679
|
+
md: {
|
|
680
|
+
input: "px-4 py-2 text-sm h-10",
|
|
681
|
+
icon: "w-5 h-5",
|
|
682
|
+
button: "h-8 w-8"
|
|
683
|
+
},
|
|
684
|
+
lg: {
|
|
685
|
+
input: "px-5 py-4 text-base h-12",
|
|
686
|
+
icon: "w-6 h-6",
|
|
687
|
+
button: "h-10 w-10"
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
const getErrorKey = (v) => {
|
|
691
|
+
if (v.valueMissing) return "required";
|
|
692
|
+
if (v.typeMismatch) return "typeMismatch";
|
|
693
|
+
if (v.patternMismatch) return "pattern";
|
|
694
|
+
if (v.tooShort) return "tooShort";
|
|
695
|
+
if (v.tooLong) return "tooLong";
|
|
696
|
+
if (v.rangeUnderflow) return "rangeUnderflow";
|
|
697
|
+
if (v.rangeOverflow) return "rangeOverflow";
|
|
698
|
+
if (v.stepMismatch) return "stepMismatch";
|
|
699
|
+
if (v.badInput) return "badInput";
|
|
700
|
+
return "invalid";
|
|
701
|
+
};
|
|
702
|
+
const getStatusIcon = () => {
|
|
703
|
+
if (loading2) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Loader2, { className: cn("animate-spin text-muted-foreground", sizeStyles8[size].icon) });
|
|
704
|
+
if (success) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.CheckCircle, { className: cn("text-success", sizeStyles8[size].icon) });
|
|
705
|
+
if (errMsg) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.AlertCircle, { className: cn("text-destructive", sizeStyles8[size].icon) });
|
|
706
|
+
return null;
|
|
707
|
+
};
|
|
708
|
+
const showPasswordToggle = type === "password";
|
|
709
|
+
const actualType = showPasswordToggle && showPassword ? "text" : type;
|
|
710
|
+
const { onFocus: onFocusProp, onBlur: onBlurProp, disabled } = rest;
|
|
711
|
+
const {
|
|
712
|
+
label: _label,
|
|
713
|
+
error: _error,
|
|
714
|
+
description: _description,
|
|
715
|
+
variant: _variant,
|
|
716
|
+
size: _size,
|
|
717
|
+
leftIcon: _leftIcon,
|
|
718
|
+
rightIcon: _rightIcon,
|
|
719
|
+
clearable: _clearable,
|
|
720
|
+
loading: _loading,
|
|
721
|
+
success: _success,
|
|
722
|
+
onClear: _onClear,
|
|
723
|
+
hint: _hint,
|
|
724
|
+
counter: _counter,
|
|
725
|
+
...restInput
|
|
726
|
+
} = rest;
|
|
727
|
+
const handleFocus = (e) => {
|
|
728
|
+
setIsFocused(true);
|
|
729
|
+
onFocusProp?.(e);
|
|
730
|
+
};
|
|
731
|
+
const handleBlur = (e) => {
|
|
732
|
+
setIsFocused(false);
|
|
733
|
+
onBlurProp?.(e);
|
|
734
|
+
};
|
|
735
|
+
const radiusClass = size === "sm" ? "rounded-md" : "rounded-lg";
|
|
736
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("w-full group", containerSpacing), children: [
|
|
737
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
739
|
+
"label",
|
|
740
|
+
{
|
|
741
|
+
className: cn(
|
|
742
|
+
// Label size follows input size
|
|
743
|
+
size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm",
|
|
744
|
+
"font-medium transition-colors duration-200",
|
|
745
|
+
// default color and highlight while any descendant focused
|
|
746
|
+
disabled ? "text-muted-foreground" : cn("text-foreground group-focus-within:text-primary", success && "text-primary"),
|
|
747
|
+
errMsg && "text-destructive"
|
|
748
748
|
),
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
775
|
-
"input",
|
|
776
|
-
{
|
|
777
|
-
ref,
|
|
778
|
-
type: actualType,
|
|
779
|
-
...value !== void 0 ? { value } : {},
|
|
780
|
-
maxLength,
|
|
781
|
-
required,
|
|
782
|
-
id: resolvedId,
|
|
783
|
-
autoComplete: type === "password" ? "current-password" : type === "email" ? "email" : rest.autoComplete,
|
|
784
|
-
onFocus: handleFocus,
|
|
785
|
-
onBlur: handleBlur,
|
|
786
|
-
onInvalid: (e) => {
|
|
787
|
-
e.preventDefault();
|
|
788
|
-
const key = getErrorKey(e.currentTarget.validity);
|
|
789
|
-
setLocalError(tv(key));
|
|
790
|
-
},
|
|
791
|
-
onInput: () => setLocalError(void 0),
|
|
792
|
-
"aria-invalid": !!errMsg,
|
|
793
|
-
"aria-describedby": [errorId, descId].filter(Boolean).join(" ") || void 0,
|
|
794
|
-
className: cn(
|
|
795
|
-
"w-full text-foreground transition-all duration-200 ease-out",
|
|
796
|
-
"placeholder:text-muted-foreground focus:outline-none focus-visible:outline-none",
|
|
797
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
798
|
-
// Size styles
|
|
799
|
-
sizeStyles8[size].input,
|
|
800
|
-
radiusClass,
|
|
801
|
-
// Icon padding adjustments
|
|
802
|
-
LeftIcon && "pl-10",
|
|
803
|
-
(RightIcon || showPasswordToggle || clearable || loading2 || success || errMsg) && "pr-10",
|
|
804
|
-
// Variant styles
|
|
805
|
-
variantStyles6[variant].container,
|
|
806
|
-
errMsg ? variantStyles6[variant].error : variantStyles6[variant].focus,
|
|
807
|
-
// Reduce visual weight for sm: no default drop shadows
|
|
808
|
-
size !== "sm" && variant !== "minimal" && "shadow-sm",
|
|
809
|
-
size !== "sm" && isFocused && "shadow-md",
|
|
810
|
-
className
|
|
811
|
-
),
|
|
812
|
-
disabled,
|
|
813
|
-
...restInput
|
|
814
|
-
}
|
|
815
|
-
),
|
|
816
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [
|
|
817
|
-
getStatusIcon(),
|
|
818
|
-
RightIcon && !loading2 && !success && !errMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RightIcon, { className: cn("text-muted-foreground", sizeStyles8[size].icon) }),
|
|
819
|
-
clearable && hasValue && !loading2 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
820
|
-
"button",
|
|
749
|
+
children: [
|
|
750
|
+
label,
|
|
751
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-destructive ml-1", children: "*" })
|
|
752
|
+
]
|
|
753
|
+
}
|
|
754
|
+
),
|
|
755
|
+
counter && maxLength && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
756
|
+
"span",
|
|
757
|
+
{
|
|
758
|
+
className: cn(
|
|
759
|
+
"text-xs transition-colors duration-200",
|
|
760
|
+
charCount > maxLength * 0.9 ? "text-warning" : "text-muted-foreground",
|
|
761
|
+
charCount >= maxLength && "text-destructive"
|
|
762
|
+
),
|
|
763
|
+
children: [
|
|
764
|
+
charCount,
|
|
765
|
+
"/",
|
|
766
|
+
maxLength
|
|
767
|
+
]
|
|
768
|
+
}
|
|
769
|
+
)
|
|
770
|
+
] }),
|
|
771
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative group", children: [
|
|
772
|
+
LeftIcon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
773
|
+
"div",
|
|
821
774
|
{
|
|
822
|
-
type: "button",
|
|
823
|
-
onClick: () => {
|
|
824
|
-
if (onClear) return onClear();
|
|
825
|
-
rest.onChange?.({ target: { value: "" } });
|
|
826
|
-
},
|
|
827
775
|
className: cn(
|
|
828
|
-
"
|
|
829
|
-
"
|
|
830
|
-
|
|
831
|
-
|
|
776
|
+
"absolute left-3 top-1/2 -translate-y-1/2 z-10",
|
|
777
|
+
"text-muted-foreground transition-colors duration-200",
|
|
778
|
+
// highlight icon when input (or controls) focused
|
|
779
|
+
"group-focus-within:text-primary"
|
|
832
780
|
),
|
|
833
|
-
|
|
834
|
-
"aria-label": "Clear input",
|
|
835
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.X, { className: sizeStyles8[size].icon })
|
|
781
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LeftIcon, { className: sizeStyles8[size].icon })
|
|
836
782
|
}
|
|
837
783
|
),
|
|
838
|
-
|
|
839
|
-
"
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
785
|
+
"input",
|
|
840
786
|
{
|
|
841
|
-
|
|
842
|
-
|
|
787
|
+
ref,
|
|
788
|
+
type: actualType,
|
|
789
|
+
...value !== void 0 ? { value } : {},
|
|
790
|
+
maxLength,
|
|
791
|
+
required,
|
|
792
|
+
id: resolvedId,
|
|
793
|
+
autoComplete: type === "password" ? "current-password" : type === "email" ? "email" : rest.autoComplete,
|
|
794
|
+
onFocus: handleFocus,
|
|
795
|
+
onBlur: handleBlur,
|
|
796
|
+
onInvalid: (e) => {
|
|
797
|
+
e.preventDefault();
|
|
798
|
+
const key = getErrorKey(e.currentTarget.validity);
|
|
799
|
+
setLocalError(tv(key));
|
|
800
|
+
},
|
|
801
|
+
onInput: () => setLocalError(void 0),
|
|
802
|
+
"aria-invalid": !!errMsg,
|
|
803
|
+
"aria-describedby": [errorId, descId].filter(Boolean).join(" ") || void 0,
|
|
843
804
|
className: cn(
|
|
844
|
-
"
|
|
845
|
-
"
|
|
846
|
-
"
|
|
847
|
-
|
|
805
|
+
"w-full text-foreground transition-all duration-200 ease-out",
|
|
806
|
+
"placeholder:text-muted-foreground focus:outline-none focus-visible:outline-none",
|
|
807
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
808
|
+
// Size styles
|
|
809
|
+
sizeStyles8[size].input,
|
|
810
|
+
radiusClass,
|
|
811
|
+
// Icon padding adjustments
|
|
812
|
+
LeftIcon && "pl-10",
|
|
813
|
+
(RightIcon || showPasswordToggle || clearable || loading2 || success || errMsg) && "pr-10",
|
|
814
|
+
// Variant styles
|
|
815
|
+
variantStyles6[variant].container,
|
|
816
|
+
errMsg ? variantStyles6[variant].error : variantStyles6[variant].focus,
|
|
817
|
+
// Reduce visual weight for sm: no default drop shadows
|
|
818
|
+
size !== "sm" && variant !== "minimal" && "shadow-sm",
|
|
819
|
+
size !== "sm" && isFocused && "shadow-md",
|
|
820
|
+
className
|
|
848
821
|
),
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
822
|
+
disabled,
|
|
823
|
+
...restInput
|
|
824
|
+
}
|
|
825
|
+
),
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [
|
|
827
|
+
getStatusIcon(),
|
|
828
|
+
RightIcon && !loading2 && !success && !errMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RightIcon, { className: cn("text-muted-foreground", sizeStyles8[size].icon) }),
|
|
829
|
+
clearable && hasValue && !loading2 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
830
|
+
"button",
|
|
831
|
+
{
|
|
832
|
+
type: "button",
|
|
833
|
+
onClick: () => {
|
|
834
|
+
if (onClear) return onClear();
|
|
835
|
+
rest.onChange?.({ target: { value: "" } });
|
|
836
|
+
},
|
|
837
|
+
className: cn(
|
|
838
|
+
"flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors rounded-sm",
|
|
839
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
840
|
+
"active:bg-accent active:text-accent-foreground",
|
|
841
|
+
sizeStyles8[size].button
|
|
842
|
+
),
|
|
843
|
+
tabIndex: 0,
|
|
844
|
+
"aria-label": "Clear input",
|
|
845
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.X, { className: sizeStyles8[size].icon })
|
|
846
|
+
}
|
|
847
|
+
),
|
|
848
|
+
showPasswordToggle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
849
|
+
"button",
|
|
850
|
+
{
|
|
851
|
+
type: "button",
|
|
852
|
+
onClick: () => setShowPassword(!showPassword),
|
|
853
|
+
className: cn(
|
|
854
|
+
"flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors rounded-full",
|
|
855
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-ring/40",
|
|
856
|
+
"active:bg-accent/50 active:text-accent-foreground",
|
|
857
|
+
sizeStyles8[size].button
|
|
858
|
+
),
|
|
859
|
+
tabIndex: 0,
|
|
860
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
861
|
+
children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.EyeOff, { className: sizeStyles8[size].icon }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.Eye, { className: sizeStyles8[size].icon })
|
|
862
|
+
}
|
|
863
|
+
)
|
|
864
|
+
] }),
|
|
865
|
+
variant === "minimal" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
866
|
+
"div",
|
|
867
|
+
{
|
|
868
|
+
className: cn(
|
|
869
|
+
"absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-primary to-primary/60 transition-all duration-300",
|
|
870
|
+
// default hidden
|
|
871
|
+
"w-0 opacity-0",
|
|
872
|
+
// expand underline when focused within input container
|
|
873
|
+
"group-focus-within:w-full group-focus-within:opacity-100"
|
|
874
|
+
)
|
|
852
875
|
}
|
|
853
876
|
)
|
|
854
877
|
] }),
|
|
855
|
-
|
|
856
|
-
"
|
|
878
|
+
errMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { id: errorId, className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
879
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
880
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: errMsg })
|
|
881
|
+
] }),
|
|
882
|
+
(description || hint) && !errMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
883
|
+
"p",
|
|
857
884
|
{
|
|
885
|
+
id: descId,
|
|
858
886
|
className: cn(
|
|
859
|
-
"
|
|
860
|
-
//
|
|
861
|
-
"
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
)
|
|
887
|
+
"text-xs transition-colors duration-200",
|
|
888
|
+
// follow focus state of the whole field area
|
|
889
|
+
"text-muted-foreground group-focus-within:text-primary/70"
|
|
890
|
+
),
|
|
891
|
+
children: hint || description
|
|
865
892
|
}
|
|
866
893
|
)
|
|
867
|
-
] })
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: errMsg })
|
|
871
|
-
] }),
|
|
872
|
-
(description || hint) && !errMsg && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { id: descId, className: cn(
|
|
873
|
-
"text-xs transition-colors duration-200",
|
|
874
|
-
// follow focus state of the whole field area
|
|
875
|
-
"text-muted-foreground group-focus-within:text-primary/70"
|
|
876
|
-
), children: hint || description })
|
|
877
|
-
] });
|
|
878
|
-
});
|
|
894
|
+
] });
|
|
895
|
+
}
|
|
896
|
+
);
|
|
879
897
|
Input.displayName = "Input";
|
|
880
|
-
var SearchInput = (0, import_react3.forwardRef)(
|
|
881
|
-
onSearch,
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
}, searchDelay);
|
|
892
|
-
return () => clearTimeout(timer);
|
|
893
|
-
}, [searchValue, onSearch, searchDelay]);
|
|
894
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
895
|
-
Input,
|
|
896
|
-
{
|
|
897
|
-
ref,
|
|
898
|
-
type: "search",
|
|
899
|
-
leftIcon: import_lucide_react3.Search,
|
|
900
|
-
placeholder,
|
|
901
|
-
clearable: true,
|
|
902
|
-
value: searchValue,
|
|
903
|
-
onChange: (e) => setSearchValue(e.target.value),
|
|
904
|
-
onClear: () => setSearchValue(""),
|
|
905
|
-
...props
|
|
906
|
-
}
|
|
907
|
-
);
|
|
908
|
-
});
|
|
909
|
-
SearchInput.displayName = "SearchInput";
|
|
910
|
-
var PasswordInput = (0, import_react3.forwardRef)(({
|
|
911
|
-
showStrength = false,
|
|
912
|
-
strengthLabels = ["Weak", "Fair", "Good", "Strong"],
|
|
913
|
-
...props
|
|
914
|
-
}, ref) => {
|
|
915
|
-
const getPasswordStrength = (password) => {
|
|
916
|
-
let score = 0;
|
|
917
|
-
if (password.length >= 8) score++;
|
|
918
|
-
if (/[a-z]/.test(password)) score++;
|
|
919
|
-
if (/[A-Z]/.test(password)) score++;
|
|
920
|
-
if (/[0-9]/.test(password)) score++;
|
|
921
|
-
if (/[^A-Za-z0-9]/.test(password)) score++;
|
|
922
|
-
return Math.min(score, 4);
|
|
923
|
-
};
|
|
924
|
-
const strength = showStrength && typeof props.value === "string" ? getPasswordStrength(props.value) : 0;
|
|
925
|
-
const strengthColors = ["bg-destructive", "bg-warning", "bg-warning", "bg-success"];
|
|
926
|
-
const strengthLabel = strengthLabels[strength - 1];
|
|
927
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-2", children: [
|
|
928
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Input, { ref, type: "password", ...props }),
|
|
929
|
-
showStrength && props.value && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-1", children: [
|
|
930
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((level) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
931
|
-
"div",
|
|
932
|
-
{
|
|
933
|
-
className: cn(
|
|
934
|
-
"h-1 flex-1 rounded-full transition-colors duration-300",
|
|
935
|
-
level <= strength ? strengthColors[strength - 1] : "bg-muted"
|
|
936
|
-
)
|
|
937
|
-
},
|
|
938
|
-
level
|
|
939
|
-
)) }),
|
|
940
|
-
strengthLabel && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: cn(
|
|
941
|
-
"text-xs font-medium",
|
|
942
|
-
strength <= 1 ? "text-destructive" : strength <= 2 ? "text-warning" : strength <= 3 ? "text-warning" : "text-success"
|
|
943
|
-
), children: strengthLabel })
|
|
944
|
-
] })
|
|
945
|
-
] });
|
|
946
|
-
});
|
|
947
|
-
PasswordInput.displayName = "PasswordInput";
|
|
948
|
-
var NumberInput = (0, import_react3.forwardRef)(({
|
|
949
|
-
min,
|
|
950
|
-
max,
|
|
951
|
-
step = 1,
|
|
952
|
-
showSteppers = true,
|
|
953
|
-
onIncrement,
|
|
954
|
-
onDecrement,
|
|
955
|
-
formatThousands = false,
|
|
956
|
-
locale = "vi-VN",
|
|
957
|
-
value,
|
|
958
|
-
onChange,
|
|
959
|
-
...props
|
|
960
|
-
}, ref) => {
|
|
961
|
-
const toNumber = (v) => {
|
|
962
|
-
if (v === "" || v === void 0 || v === null) return 0;
|
|
963
|
-
const n = Number(v);
|
|
964
|
-
return Number.isFinite(n) ? n : 0;
|
|
965
|
-
};
|
|
966
|
-
const format = import_react3.default.useCallback(
|
|
967
|
-
(n) => new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }).format(n),
|
|
968
|
-
[locale]
|
|
969
|
-
);
|
|
970
|
-
const parse = (s) => {
|
|
971
|
-
const digits = (s || "").replace(/\D+/g, "");
|
|
972
|
-
return digits ? Number(digits) : NaN;
|
|
973
|
-
};
|
|
974
|
-
const [displayValue, setDisplayValue] = import_react3.default.useState(
|
|
975
|
-
formatThousands ? value !== void 0 && value !== null && value !== "" ? format(toNumber(value)) : "" : String(value ?? "")
|
|
976
|
-
);
|
|
977
|
-
const currentValue = toNumber(value);
|
|
978
|
-
import_react3.default.useEffect(() => {
|
|
979
|
-
if (formatThousands) {
|
|
980
|
-
const next = value === "" || value === void 0 || value === null ? "" : format(toNumber(value));
|
|
981
|
-
setDisplayValue((prev) => prev === next ? prev : next);
|
|
982
|
-
} else {
|
|
983
|
-
const next = String(value ?? "");
|
|
984
|
-
setDisplayValue((prev) => prev === next ? prev : next);
|
|
985
|
-
}
|
|
986
|
-
}, [value, formatThousands, locale, format]);
|
|
987
|
-
const handleIncrement = () => {
|
|
988
|
-
if (onIncrement) {
|
|
989
|
-
onIncrement();
|
|
990
|
-
} else if (onChange) {
|
|
991
|
-
const newValue = Math.min(currentValue + step, max ?? Infinity);
|
|
992
|
-
if (formatThousands) setDisplayValue(format(newValue));
|
|
993
|
-
onChange({ target: { value: newValue.toString() } });
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
|
-
const handleDecrement = () => {
|
|
997
|
-
if (onDecrement) {
|
|
998
|
-
onDecrement();
|
|
999
|
-
} else if (onChange) {
|
|
1000
|
-
const newValue = Math.max(currentValue - step, min ?? -Infinity);
|
|
1001
|
-
if (formatThousands) setDisplayValue(format(newValue));
|
|
1002
|
-
onChange({ target: { value: newValue.toString() } });
|
|
1003
|
-
}
|
|
1004
|
-
};
|
|
1005
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative", children: [
|
|
1006
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
898
|
+
var SearchInput = (0, import_react3.forwardRef)(
|
|
899
|
+
({ onSearch, searchDelay = 300, placeholder = "Search...", ...props }, ref) => {
|
|
900
|
+
const [searchValue, setSearchValue] = (0, import_react3.useState)(props.value || "");
|
|
901
|
+
import_react3.default.useEffect(() => {
|
|
902
|
+
if (!onSearch) return;
|
|
903
|
+
const timer = setTimeout(() => {
|
|
904
|
+
onSearch(searchValue);
|
|
905
|
+
}, searchDelay);
|
|
906
|
+
return () => clearTimeout(timer);
|
|
907
|
+
}, [searchValue, onSearch, searchDelay]);
|
|
908
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1007
909
|
Input,
|
|
1008
910
|
{
|
|
1009
911
|
ref,
|
|
1010
|
-
type:
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
912
|
+
type: "search",
|
|
913
|
+
leftIcon: import_lucide_react3.Search,
|
|
914
|
+
placeholder,
|
|
915
|
+
clearable: true,
|
|
916
|
+
value: searchValue,
|
|
917
|
+
onChange: (e) => setSearchValue(e.target.value),
|
|
918
|
+
onClear: () => setSearchValue(""),
|
|
919
|
+
...props
|
|
920
|
+
}
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
);
|
|
924
|
+
SearchInput.displayName = "SearchInput";
|
|
925
|
+
var PasswordInput = (0, import_react3.forwardRef)(
|
|
926
|
+
({ showStrength = false, strengthLabels = ["Weak", "Fair", "Good", "Strong"], ...props }, ref) => {
|
|
927
|
+
const getPasswordStrength = (password) => {
|
|
928
|
+
let score = 0;
|
|
929
|
+
if (password.length >= 8) score++;
|
|
930
|
+
if (/[a-z]/.test(password)) score++;
|
|
931
|
+
if (/[A-Z]/.test(password)) score++;
|
|
932
|
+
if (/[0-9]/.test(password)) score++;
|
|
933
|
+
if (/[^A-Za-z0-9]/.test(password)) score++;
|
|
934
|
+
return Math.min(score, 4);
|
|
935
|
+
};
|
|
936
|
+
const strength = showStrength && typeof props.value === "string" ? getPasswordStrength(props.value) : 0;
|
|
937
|
+
const strengthColors = ["bg-destructive", "bg-warning", "bg-warning", "bg-success"];
|
|
938
|
+
const strengthLabel = strengthLabels[strength - 1];
|
|
939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-2", children: [
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Input, { ref, type: "password", ...props }),
|
|
941
|
+
showStrength && props.value && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-1", children: [
|
|
942
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((level) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
943
|
+
"div",
|
|
944
|
+
{
|
|
945
|
+
className: cn(
|
|
946
|
+
"h-1 flex-1 rounded-full transition-colors duration-300",
|
|
947
|
+
level <= strength ? strengthColors[strength - 1] : "bg-muted"
|
|
948
|
+
)
|
|
949
|
+
},
|
|
950
|
+
level
|
|
951
|
+
)) }),
|
|
952
|
+
strengthLabel && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
953
|
+
"p",
|
|
954
|
+
{
|
|
955
|
+
className: cn(
|
|
956
|
+
"text-xs font-medium",
|
|
957
|
+
strength <= 1 ? "text-destructive" : strength <= 2 ? "text-warning" : strength <= 3 ? "text-warning" : "text-success"
|
|
958
|
+
),
|
|
959
|
+
children: strengthLabel
|
|
1028
960
|
}
|
|
1029
|
-
},
|
|
1030
|
-
...props,
|
|
1031
|
-
className: cn(
|
|
1032
|
-
showSteppers && [
|
|
1033
|
-
"pr-12",
|
|
1034
|
-
// Hide native browser steppers
|
|
1035
|
-
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
1036
|
-
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
1037
|
-
"[&::-webkit-inner-spin-button]:m-0",
|
|
1038
|
-
"appearance-none"
|
|
1039
|
-
],
|
|
1040
|
-
props.className
|
|
1041
961
|
)
|
|
962
|
+
] })
|
|
963
|
+
] });
|
|
964
|
+
}
|
|
965
|
+
);
|
|
966
|
+
PasswordInput.displayName = "PasswordInput";
|
|
967
|
+
var NumberInput = (0, import_react3.forwardRef)(
|
|
968
|
+
({ min, max, step = 1, showSteppers = true, onIncrement, onDecrement, formatThousands = false, locale = "vi-VN", value, onChange, ...props }, ref) => {
|
|
969
|
+
const toNumber = (v) => {
|
|
970
|
+
if (v === "" || v === void 0 || v === null) return 0;
|
|
971
|
+
const n = Number(v);
|
|
972
|
+
return Number.isFinite(n) ? n : 0;
|
|
973
|
+
};
|
|
974
|
+
const format = import_react3.default.useCallback((n) => new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }).format(n), [locale]);
|
|
975
|
+
const parse = (s) => {
|
|
976
|
+
const digits = (s || "").replace(/\D+/g, "");
|
|
977
|
+
return digits ? Number(digits) : NaN;
|
|
978
|
+
};
|
|
979
|
+
const [displayValue, setDisplayValue] = import_react3.default.useState(
|
|
980
|
+
formatThousands ? value !== void 0 && value !== null && value !== "" ? format(toNumber(value)) : "" : String(value ?? "")
|
|
981
|
+
);
|
|
982
|
+
const currentValue = toNumber(value);
|
|
983
|
+
import_react3.default.useEffect(() => {
|
|
984
|
+
if (formatThousands) {
|
|
985
|
+
const next = value === "" || value === void 0 || value === null ? "" : format(toNumber(value));
|
|
986
|
+
setDisplayValue((prev) => prev === next ? prev : next);
|
|
987
|
+
} else {
|
|
988
|
+
const next = String(value ?? "");
|
|
989
|
+
setDisplayValue((prev) => prev === next ? prev : next);
|
|
990
|
+
}
|
|
991
|
+
}, [value, formatThousands, locale, format]);
|
|
992
|
+
const handleIncrement = () => {
|
|
993
|
+
if (onIncrement) {
|
|
994
|
+
onIncrement();
|
|
995
|
+
} else if (onChange) {
|
|
996
|
+
const newValue = Math.min(currentValue + step, max ?? Infinity);
|
|
997
|
+
if (formatThousands) setDisplayValue(format(newValue));
|
|
998
|
+
onChange({ target: { value: newValue.toString() } });
|
|
1042
999
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1000
|
+
};
|
|
1001
|
+
const handleDecrement = () => {
|
|
1002
|
+
if (onDecrement) {
|
|
1003
|
+
onDecrement();
|
|
1004
|
+
} else if (onChange) {
|
|
1005
|
+
const newValue = Math.max(currentValue - step, min ?? -Infinity);
|
|
1006
|
+
if (formatThousands) setDisplayValue(format(newValue));
|
|
1007
|
+
onChange({ target: { value: newValue.toString() } });
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative", children: [
|
|
1045
1011
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1046
|
-
|
|
1012
|
+
Input,
|
|
1047
1013
|
{
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
{
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
"path",
|
|
1068
|
-
{
|
|
1069
|
-
d: "M4 2L6 6H2L4 2Z",
|
|
1070
|
-
fill: "currentColor"
|
|
1071
|
-
}
|
|
1072
|
-
)
|
|
1014
|
+
ref,
|
|
1015
|
+
type: formatThousands ? "text" : "number",
|
|
1016
|
+
min,
|
|
1017
|
+
max,
|
|
1018
|
+
step,
|
|
1019
|
+
rightIcon: showSteppers ? void 0 : props.rightIcon,
|
|
1020
|
+
value: displayValue,
|
|
1021
|
+
onChange: (e) => {
|
|
1022
|
+
if (!onChange) return;
|
|
1023
|
+
if (!formatThousands) return onChange(e);
|
|
1024
|
+
const raw = e.target.value;
|
|
1025
|
+
const parsed = parse(raw);
|
|
1026
|
+
if (Number.isNaN(parsed)) {
|
|
1027
|
+
setDisplayValue("");
|
|
1028
|
+
onChange({ target: { value: "" } });
|
|
1029
|
+
} else {
|
|
1030
|
+
const bounded = Math.min(Math.max(parsed, min ?? -Infinity), max ?? Infinity);
|
|
1031
|
+
setDisplayValue(format(bounded));
|
|
1032
|
+
onChange({ target: { value: bounded.toString() } });
|
|
1073
1033
|
}
|
|
1034
|
+
},
|
|
1035
|
+
...props,
|
|
1036
|
+
className: cn(
|
|
1037
|
+
showSteppers && [
|
|
1038
|
+
"pr-12",
|
|
1039
|
+
// Hide native browser steppers
|
|
1040
|
+
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
1041
|
+
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
1042
|
+
"[&::-webkit-inner-spin-button]:m-0",
|
|
1043
|
+
"appearance-none"
|
|
1044
|
+
],
|
|
1045
|
+
props.className
|
|
1074
1046
|
)
|
|
1075
1047
|
}
|
|
1076
1048
|
),
|
|
1049
|
+
showSteppers && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "absolute right-2 top-1/2 -translate-y-1/2 flex flex-col gap-0.5", children: [
|
|
1050
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1051
|
+
"button",
|
|
1052
|
+
{
|
|
1053
|
+
type: "button",
|
|
1054
|
+
onClick: handleIncrement,
|
|
1055
|
+
disabled: max !== void 0 && currentValue >= max,
|
|
1056
|
+
className: cn(
|
|
1057
|
+
"flex items-center justify-center w-4 h-4 rounded-sm transition-colors",
|
|
1058
|
+
"hover:bg-accent focus:outline-none focus:bg-accent",
|
|
1059
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
1060
|
+
"text-muted-foreground hover:text-foreground"
|
|
1061
|
+
),
|
|
1062
|
+
"aria-label": "Increase value",
|
|
1063
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M4 2L6 6H2L4 2Z", fill: "currentColor" }) })
|
|
1064
|
+
}
|
|
1065
|
+
),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1067
|
+
"button",
|
|
1068
|
+
{
|
|
1069
|
+
type: "button",
|
|
1070
|
+
onClick: handleDecrement,
|
|
1071
|
+
disabled: min !== void 0 && currentValue <= min,
|
|
1072
|
+
className: cn(
|
|
1073
|
+
"flex items-center justify-center w-4 h-4 rounded-sm transition-colors",
|
|
1074
|
+
"hover:bg-accent focus:outline-none focus:bg-accent",
|
|
1075
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
1076
|
+
"text-muted-foreground hover:text-foreground"
|
|
1077
|
+
),
|
|
1078
|
+
"aria-label": "Decrease value",
|
|
1079
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M4 6L2 2H6L4 6Z", fill: "currentColor" }) })
|
|
1080
|
+
}
|
|
1081
|
+
)
|
|
1082
|
+
] })
|
|
1083
|
+
] });
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
NumberInput.displayName = "NumberInput";
|
|
1087
|
+
var Textarea = (0, import_react3.forwardRef)(
|
|
1088
|
+
({ label, error, description, variant = "default", resize = "vertical", counter = false, className, required, value, maxLength, ...props }, ref) => {
|
|
1089
|
+
const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
|
|
1090
|
+
const charCount = typeof value === "string" ? value.length : 0;
|
|
1091
|
+
const variantStyles6 = {
|
|
1092
|
+
default: "bg-background border border-input hover:border-accent-foreground/20 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1093
|
+
filled: "bg-muted/50 border border-transparent hover:bg-muted/70 focus-visible:outline-none focus-visible:bg-background focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1094
|
+
outlined: "bg-transparent border border-border hover:border-accent-foreground/30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1095
|
+
minimal: "bg-transparent border-0 border-b border-border hover:border-accent-foreground/30 focus-visible:outline-none focus-visible:border-ring focus-visible:ring-0 rounded-none"
|
|
1096
|
+
};
|
|
1097
|
+
const resizeClasses = {
|
|
1098
|
+
none: "resize-none",
|
|
1099
|
+
vertical: "resize-y",
|
|
1100
|
+
horizontal: "resize-x",
|
|
1101
|
+
both: "resize"
|
|
1102
|
+
};
|
|
1103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-full space-y-2", children: [
|
|
1104
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1105
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1106
|
+
"label",
|
|
1107
|
+
{
|
|
1108
|
+
className: cn(
|
|
1109
|
+
"text-sm font-medium transition-colors duration-200",
|
|
1110
|
+
isFocused ? "text-primary" : "text-foreground",
|
|
1111
|
+
error && "text-destructive"
|
|
1112
|
+
),
|
|
1113
|
+
children: [
|
|
1114
|
+
label,
|
|
1115
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-destructive ml-1", children: "*" })
|
|
1116
|
+
]
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
counter && maxLength && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1120
|
+
"span",
|
|
1121
|
+
{
|
|
1122
|
+
className: cn(
|
|
1123
|
+
"text-xs transition-colors duration-200",
|
|
1124
|
+
charCount > maxLength * 0.9 ? "text-warning" : "text-muted-foreground",
|
|
1125
|
+
charCount >= maxLength && "text-destructive"
|
|
1126
|
+
),
|
|
1127
|
+
children: [
|
|
1128
|
+
charCount,
|
|
1129
|
+
"/",
|
|
1130
|
+
maxLength
|
|
1131
|
+
]
|
|
1132
|
+
}
|
|
1133
|
+
)
|
|
1134
|
+
] }),
|
|
1077
1135
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1078
|
-
"
|
|
1136
|
+
"textarea",
|
|
1079
1137
|
{
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1138
|
+
ref,
|
|
1139
|
+
value,
|
|
1140
|
+
maxLength,
|
|
1141
|
+
required,
|
|
1142
|
+
onFocus: () => setIsFocused(true),
|
|
1143
|
+
onBlur: () => setIsFocused(false),
|
|
1083
1144
|
className: cn(
|
|
1084
|
-
"
|
|
1085
|
-
"
|
|
1086
|
-
"disabled:
|
|
1087
|
-
|
|
1145
|
+
"w-full rounded-lg px-4 py-3 text-sm text-foreground transition-all duration-200",
|
|
1146
|
+
"placeholder:text-muted-foreground focus:outline-none min-h-[80px]",
|
|
1147
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1148
|
+
variantStyles6[variant],
|
|
1149
|
+
// DÒNG NÀY ĐÃ ĐƯỢC CẬP NHẬT:
|
|
1150
|
+
error && "border-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-destructive focus-visible:ring-offset-1 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1151
|
+
resizeClasses[resize],
|
|
1152
|
+
isFocused && "shadow-md",
|
|
1153
|
+
variant !== "minimal" && "shadow-sm",
|
|
1154
|
+
className
|
|
1088
1155
|
),
|
|
1089
|
-
|
|
1090
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1091
|
-
"svg",
|
|
1092
|
-
{
|
|
1093
|
-
width: "8",
|
|
1094
|
-
height: "8",
|
|
1095
|
-
viewBox: "0 0 8 8",
|
|
1096
|
-
fill: "none",
|
|
1097
|
-
className: "flex-shrink-0",
|
|
1098
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1099
|
-
"path",
|
|
1100
|
-
{
|
|
1101
|
-
d: "M4 6L2 2H6L4 6Z",
|
|
1102
|
-
fill: "currentColor"
|
|
1103
|
-
}
|
|
1104
|
-
)
|
|
1105
|
-
}
|
|
1106
|
-
)
|
|
1156
|
+
...props
|
|
1107
1157
|
}
|
|
1108
|
-
)
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
})
|
|
1112
|
-
NumberInput.displayName = "NumberInput";
|
|
1113
|
-
var Textarea = (0, import_react3.forwardRef)(({
|
|
1114
|
-
label,
|
|
1115
|
-
error,
|
|
1116
|
-
description,
|
|
1117
|
-
variant = "default",
|
|
1118
|
-
resize = "vertical",
|
|
1119
|
-
counter = false,
|
|
1120
|
-
className,
|
|
1121
|
-
required,
|
|
1122
|
-
value,
|
|
1123
|
-
maxLength,
|
|
1124
|
-
...props
|
|
1125
|
-
}, ref) => {
|
|
1126
|
-
const [isFocused, setIsFocused] = (0, import_react3.useState)(false);
|
|
1127
|
-
const charCount = typeof value === "string" ? value.length : 0;
|
|
1128
|
-
const variantStyles6 = {
|
|
1129
|
-
default: "bg-background border border-input hover:border-accent-foreground/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1130
|
-
filled: "bg-muted/50 border border-transparent hover:bg-muted/70 focus-visible:outline-none focus-visible:bg-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1131
|
-
outlined: "bg-transparent border border-border hover:border-accent-foreground/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1132
|
-
minimal: "bg-transparent border-0 border-b border-border hover:border-accent-foreground/30 focus-visible:outline-none focus-visible:border-ring focus-visible:ring-0 rounded-none"
|
|
1133
|
-
};
|
|
1134
|
-
const resizeClasses = {
|
|
1135
|
-
none: "resize-none",
|
|
1136
|
-
vertical: "resize-y",
|
|
1137
|
-
horizontal: "resize-x",
|
|
1138
|
-
both: "resize"
|
|
1139
|
-
};
|
|
1140
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-full space-y-2", children: [
|
|
1141
|
-
label && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1142
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: cn(
|
|
1143
|
-
"text-sm font-medium transition-colors duration-200",
|
|
1144
|
-
isFocused ? "text-primary" : "text-foreground",
|
|
1145
|
-
error && "text-destructive"
|
|
1146
|
-
), children: [
|
|
1147
|
-
label,
|
|
1148
|
-
required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-destructive ml-1", children: "*" })
|
|
1158
|
+
),
|
|
1159
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
1160
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
1161
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: error })
|
|
1149
1162
|
] }),
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
), children: [
|
|
1155
|
-
charCount,
|
|
1156
|
-
"/",
|
|
1157
|
-
maxLength
|
|
1158
|
-
] })
|
|
1159
|
-
] }),
|
|
1160
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1161
|
-
"textarea",
|
|
1162
|
-
{
|
|
1163
|
-
ref,
|
|
1164
|
-
value,
|
|
1165
|
-
maxLength,
|
|
1166
|
-
required,
|
|
1167
|
-
onFocus: () => setIsFocused(true),
|
|
1168
|
-
onBlur: () => setIsFocused(false),
|
|
1169
|
-
className: cn(
|
|
1170
|
-
"w-full rounded-lg px-4 py-3 text-sm text-foreground transition-all duration-200",
|
|
1171
|
-
"placeholder:text-muted-foreground focus:outline-none min-h-[80px]",
|
|
1172
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1173
|
-
variantStyles6[variant],
|
|
1174
|
-
error && "border-destructive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-destructive focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:border-transparent",
|
|
1175
|
-
resizeClasses[resize],
|
|
1176
|
-
isFocused && "shadow-md",
|
|
1177
|
-
variant !== "minimal" && "shadow-sm",
|
|
1178
|
-
className
|
|
1179
|
-
),
|
|
1180
|
-
...props
|
|
1181
|
-
}
|
|
1182
|
-
),
|
|
1183
|
-
error && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
1184
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
1185
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: error })
|
|
1186
|
-
] }),
|
|
1187
|
-
description && !error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: cn(
|
|
1188
|
-
"text-xs transition-colors duration-200",
|
|
1189
|
-
isFocused ? "text-primary/70" : "text-muted-foreground"
|
|
1190
|
-
), children: description })
|
|
1191
|
-
] });
|
|
1192
|
-
});
|
|
1163
|
+
description && !error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: cn("text-xs transition-colors duration-200", isFocused ? "text-primary/70" : "text-muted-foreground"), children: description })
|
|
1164
|
+
] });
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1193
1167
|
Textarea.displayName = "Textarea";
|
|
1194
1168
|
var Input_default = Input;
|
|
1195
1169
|
|
|
@@ -6182,8 +6156,10 @@ function OverlayControls({
|
|
|
6182
6156
|
break;
|
|
6183
6157
|
case "f":
|
|
6184
6158
|
case "F":
|
|
6185
|
-
e.
|
|
6186
|
-
|
|
6159
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
6160
|
+
e.preventDefault();
|
|
6161
|
+
onToggleFullscreen?.();
|
|
6162
|
+
}
|
|
6187
6163
|
break;
|
|
6188
6164
|
case "m":
|
|
6189
6165
|
case "M":
|