@underverse-ui/underverse 0.1.30 → 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.js
CHANGED
|
@@ -469,583 +469,557 @@ import React4, { forwardRef as forwardRef3, useId, useState as useState4 } from
|
|
|
469
469
|
import { useTranslations } from "next-intl";
|
|
470
470
|
import { Eye, EyeOff, Search, X, AlertCircle, CheckCircle, Loader2 } from "lucide-react";
|
|
471
471
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
472
|
-
var Input = forwardRef3(
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
/* @__PURE__ */ jsxs5(
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
"text-foreground group-focus-within:text-primary",
|
|
603
|
-
|
|
472
|
+
var Input = forwardRef3(
|
|
473
|
+
({
|
|
474
|
+
label,
|
|
475
|
+
error,
|
|
476
|
+
description,
|
|
477
|
+
className,
|
|
478
|
+
required,
|
|
479
|
+
variant = "default",
|
|
480
|
+
size = "md",
|
|
481
|
+
leftIcon: LeftIcon,
|
|
482
|
+
rightIcon: RightIcon,
|
|
483
|
+
clearable = false,
|
|
484
|
+
loading: loading2 = false,
|
|
485
|
+
success = false,
|
|
486
|
+
onClear,
|
|
487
|
+
hint,
|
|
488
|
+
counter = false,
|
|
489
|
+
type = "text",
|
|
490
|
+
value,
|
|
491
|
+
maxLength,
|
|
492
|
+
...rest
|
|
493
|
+
}, ref) => {
|
|
494
|
+
const [localError, setLocalError] = useState4(error);
|
|
495
|
+
const [showPassword, setShowPassword] = useState4(false);
|
|
496
|
+
const [isFocused, setIsFocused] = useState4(false);
|
|
497
|
+
const tv = useTranslations("ValidationInput");
|
|
498
|
+
const autoId = useId();
|
|
499
|
+
const needsId = !!(label || description || hint || error);
|
|
500
|
+
const resolvedId = rest.id || (needsId ? `input-${autoId}` : void 0);
|
|
501
|
+
const errMsg = error || localError;
|
|
502
|
+
const hasValue = value !== void 0 && value !== null && value !== "";
|
|
503
|
+
const charCount = typeof value === "string" ? value.length : 0;
|
|
504
|
+
const errorId = errMsg && resolvedId ? `${resolvedId}-error` : void 0;
|
|
505
|
+
const descId = !errMsg && (description || hint) && resolvedId ? `${resolvedId}-desc` : void 0;
|
|
506
|
+
const containerSpacing = size === "sm" ? "space-y-1.5" : "space-y-2";
|
|
507
|
+
const variantStyles6 = {
|
|
508
|
+
default: {
|
|
509
|
+
container: "bg-background border border-input hover:border-accent-foreground/20",
|
|
510
|
+
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",
|
|
511
|
+
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"
|
|
512
|
+
},
|
|
513
|
+
filled: {
|
|
514
|
+
container: "bg-muted/50 border border-transparent hover:bg-muted/70",
|
|
515
|
+
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",
|
|
516
|
+
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"
|
|
517
|
+
},
|
|
518
|
+
outlined: {
|
|
519
|
+
container: "bg-transparent border border-border hover:border-accent-foreground/30",
|
|
520
|
+
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",
|
|
521
|
+
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"
|
|
522
|
+
},
|
|
523
|
+
minimal: {
|
|
524
|
+
container: "bg-transparent border-0 border-b border-border hover:border-accent-foreground/30",
|
|
525
|
+
focus: "focus-visible:outline-none focus-visible:border-ring focus-visible:ring-0 rounded-none",
|
|
526
|
+
error: "border-destructive focus-visible:outline-none focus-visible:border-destructive"
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
const sizeStyles8 = {
|
|
530
|
+
sm: {
|
|
531
|
+
input: "px-3 py-1.5 text-sm h-8 md:h-7 md:py-1 md:text-xs",
|
|
532
|
+
icon: "w-4 h-4",
|
|
533
|
+
button: "h-7 w-7"
|
|
534
|
+
},
|
|
535
|
+
md: {
|
|
536
|
+
input: "px-4 py-2 text-sm h-10",
|
|
537
|
+
icon: "w-5 h-5",
|
|
538
|
+
button: "h-8 w-8"
|
|
539
|
+
},
|
|
540
|
+
lg: {
|
|
541
|
+
input: "px-5 py-4 text-base h-12",
|
|
542
|
+
icon: "w-6 h-6",
|
|
543
|
+
button: "h-10 w-10"
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
const getErrorKey = (v) => {
|
|
547
|
+
if (v.valueMissing) return "required";
|
|
548
|
+
if (v.typeMismatch) return "typeMismatch";
|
|
549
|
+
if (v.patternMismatch) return "pattern";
|
|
550
|
+
if (v.tooShort) return "tooShort";
|
|
551
|
+
if (v.tooLong) return "tooLong";
|
|
552
|
+
if (v.rangeUnderflow) return "rangeUnderflow";
|
|
553
|
+
if (v.rangeOverflow) return "rangeOverflow";
|
|
554
|
+
if (v.stepMismatch) return "stepMismatch";
|
|
555
|
+
if (v.badInput) return "badInput";
|
|
556
|
+
return "invalid";
|
|
557
|
+
};
|
|
558
|
+
const getStatusIcon = () => {
|
|
559
|
+
if (loading2) return /* @__PURE__ */ jsx5(Loader2, { className: cn("animate-spin text-muted-foreground", sizeStyles8[size].icon) });
|
|
560
|
+
if (success) return /* @__PURE__ */ jsx5(CheckCircle, { className: cn("text-success", sizeStyles8[size].icon) });
|
|
561
|
+
if (errMsg) return /* @__PURE__ */ jsx5(AlertCircle, { className: cn("text-destructive", sizeStyles8[size].icon) });
|
|
562
|
+
return null;
|
|
563
|
+
};
|
|
564
|
+
const showPasswordToggle = type === "password";
|
|
565
|
+
const actualType = showPasswordToggle && showPassword ? "text" : type;
|
|
566
|
+
const { onFocus: onFocusProp, onBlur: onBlurProp, disabled } = rest;
|
|
567
|
+
const {
|
|
568
|
+
label: _label,
|
|
569
|
+
error: _error,
|
|
570
|
+
description: _description,
|
|
571
|
+
variant: _variant,
|
|
572
|
+
size: _size,
|
|
573
|
+
leftIcon: _leftIcon,
|
|
574
|
+
rightIcon: _rightIcon,
|
|
575
|
+
clearable: _clearable,
|
|
576
|
+
loading: _loading,
|
|
577
|
+
success: _success,
|
|
578
|
+
onClear: _onClear,
|
|
579
|
+
hint: _hint,
|
|
580
|
+
counter: _counter,
|
|
581
|
+
...restInput
|
|
582
|
+
} = rest;
|
|
583
|
+
const handleFocus = (e) => {
|
|
584
|
+
setIsFocused(true);
|
|
585
|
+
onFocusProp?.(e);
|
|
586
|
+
};
|
|
587
|
+
const handleBlur = (e) => {
|
|
588
|
+
setIsFocused(false);
|
|
589
|
+
onBlurProp?.(e);
|
|
590
|
+
};
|
|
591
|
+
const radiusClass = size === "sm" ? "rounded-md" : "rounded-lg";
|
|
592
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn("w-full group", containerSpacing), children: [
|
|
593
|
+
label && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
594
|
+
/* @__PURE__ */ jsxs5(
|
|
595
|
+
"label",
|
|
596
|
+
{
|
|
597
|
+
className: cn(
|
|
598
|
+
// Label size follows input size
|
|
599
|
+
size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm",
|
|
600
|
+
"font-medium transition-colors duration-200",
|
|
601
|
+
// default color and highlight while any descendant focused
|
|
602
|
+
disabled ? "text-muted-foreground" : cn("text-foreground group-focus-within:text-primary", success && "text-primary"),
|
|
603
|
+
errMsg && "text-destructive"
|
|
604
604
|
),
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
/* @__PURE__ */ jsx5(
|
|
631
|
-
"input",
|
|
632
|
-
{
|
|
633
|
-
ref,
|
|
634
|
-
type: actualType,
|
|
635
|
-
...value !== void 0 ? { value } : {},
|
|
636
|
-
maxLength,
|
|
637
|
-
required,
|
|
638
|
-
id: resolvedId,
|
|
639
|
-
autoComplete: type === "password" ? "current-password" : type === "email" ? "email" : rest.autoComplete,
|
|
640
|
-
onFocus: handleFocus,
|
|
641
|
-
onBlur: handleBlur,
|
|
642
|
-
onInvalid: (e) => {
|
|
643
|
-
e.preventDefault();
|
|
644
|
-
const key = getErrorKey(e.currentTarget.validity);
|
|
645
|
-
setLocalError(tv(key));
|
|
646
|
-
},
|
|
647
|
-
onInput: () => setLocalError(void 0),
|
|
648
|
-
"aria-invalid": !!errMsg,
|
|
649
|
-
"aria-describedby": [errorId, descId].filter(Boolean).join(" ") || void 0,
|
|
650
|
-
className: cn(
|
|
651
|
-
"w-full text-foreground transition-all duration-200 ease-out",
|
|
652
|
-
"placeholder:text-muted-foreground focus:outline-none focus-visible:outline-none",
|
|
653
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
654
|
-
// Size styles
|
|
655
|
-
sizeStyles8[size].input,
|
|
656
|
-
radiusClass,
|
|
657
|
-
// Icon padding adjustments
|
|
658
|
-
LeftIcon && "pl-10",
|
|
659
|
-
(RightIcon || showPasswordToggle || clearable || loading2 || success || errMsg) && "pr-10",
|
|
660
|
-
// Variant styles
|
|
661
|
-
variantStyles6[variant].container,
|
|
662
|
-
errMsg ? variantStyles6[variant].error : variantStyles6[variant].focus,
|
|
663
|
-
// Reduce visual weight for sm: no default drop shadows
|
|
664
|
-
size !== "sm" && variant !== "minimal" && "shadow-sm",
|
|
665
|
-
size !== "sm" && isFocused && "shadow-md",
|
|
666
|
-
className
|
|
667
|
-
),
|
|
668
|
-
disabled,
|
|
669
|
-
...restInput
|
|
670
|
-
}
|
|
671
|
-
),
|
|
672
|
-
/* @__PURE__ */ jsxs5("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [
|
|
673
|
-
getStatusIcon(),
|
|
674
|
-
RightIcon && !loading2 && !success && !errMsg && /* @__PURE__ */ jsx5(RightIcon, { className: cn("text-muted-foreground", sizeStyles8[size].icon) }),
|
|
675
|
-
clearable && hasValue && !loading2 && /* @__PURE__ */ jsx5(
|
|
676
|
-
"button",
|
|
605
|
+
children: [
|
|
606
|
+
label,
|
|
607
|
+
required && /* @__PURE__ */ jsx5("span", { className: "text-destructive ml-1", children: "*" })
|
|
608
|
+
]
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
counter && maxLength && /* @__PURE__ */ jsxs5(
|
|
612
|
+
"span",
|
|
613
|
+
{
|
|
614
|
+
className: cn(
|
|
615
|
+
"text-xs transition-colors duration-200",
|
|
616
|
+
charCount > maxLength * 0.9 ? "text-warning" : "text-muted-foreground",
|
|
617
|
+
charCount >= maxLength && "text-destructive"
|
|
618
|
+
),
|
|
619
|
+
children: [
|
|
620
|
+
charCount,
|
|
621
|
+
"/",
|
|
622
|
+
maxLength
|
|
623
|
+
]
|
|
624
|
+
}
|
|
625
|
+
)
|
|
626
|
+
] }),
|
|
627
|
+
/* @__PURE__ */ jsxs5("div", { className: "relative group", children: [
|
|
628
|
+
LeftIcon && /* @__PURE__ */ jsx5(
|
|
629
|
+
"div",
|
|
677
630
|
{
|
|
678
|
-
type: "button",
|
|
679
|
-
onClick: () => {
|
|
680
|
-
if (onClear) return onClear();
|
|
681
|
-
rest.onChange?.({ target: { value: "" } });
|
|
682
|
-
},
|
|
683
631
|
className: cn(
|
|
684
|
-
"
|
|
685
|
-
"
|
|
686
|
-
|
|
687
|
-
|
|
632
|
+
"absolute left-3 top-1/2 -translate-y-1/2 z-10",
|
|
633
|
+
"text-muted-foreground transition-colors duration-200",
|
|
634
|
+
// highlight icon when input (or controls) focused
|
|
635
|
+
"group-focus-within:text-primary"
|
|
688
636
|
),
|
|
689
|
-
|
|
690
|
-
"aria-label": "Clear input",
|
|
691
|
-
children: /* @__PURE__ */ jsx5(X, { className: sizeStyles8[size].icon })
|
|
637
|
+
children: /* @__PURE__ */ jsx5(LeftIcon, { className: sizeStyles8[size].icon })
|
|
692
638
|
}
|
|
693
639
|
),
|
|
694
|
-
|
|
695
|
-
"
|
|
640
|
+
/* @__PURE__ */ jsx5(
|
|
641
|
+
"input",
|
|
696
642
|
{
|
|
697
|
-
|
|
698
|
-
|
|
643
|
+
ref,
|
|
644
|
+
type: actualType,
|
|
645
|
+
...value !== void 0 ? { value } : {},
|
|
646
|
+
maxLength,
|
|
647
|
+
required,
|
|
648
|
+
id: resolvedId,
|
|
649
|
+
autoComplete: type === "password" ? "current-password" : type === "email" ? "email" : rest.autoComplete,
|
|
650
|
+
onFocus: handleFocus,
|
|
651
|
+
onBlur: handleBlur,
|
|
652
|
+
onInvalid: (e) => {
|
|
653
|
+
e.preventDefault();
|
|
654
|
+
const key = getErrorKey(e.currentTarget.validity);
|
|
655
|
+
setLocalError(tv(key));
|
|
656
|
+
},
|
|
657
|
+
onInput: () => setLocalError(void 0),
|
|
658
|
+
"aria-invalid": !!errMsg,
|
|
659
|
+
"aria-describedby": [errorId, descId].filter(Boolean).join(" ") || void 0,
|
|
699
660
|
className: cn(
|
|
700
|
-
"
|
|
701
|
-
"
|
|
702
|
-
"
|
|
703
|
-
|
|
661
|
+
"w-full text-foreground transition-all duration-200 ease-out",
|
|
662
|
+
"placeholder:text-muted-foreground focus:outline-none focus-visible:outline-none",
|
|
663
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
664
|
+
// Size styles
|
|
665
|
+
sizeStyles8[size].input,
|
|
666
|
+
radiusClass,
|
|
667
|
+
// Icon padding adjustments
|
|
668
|
+
LeftIcon && "pl-10",
|
|
669
|
+
(RightIcon || showPasswordToggle || clearable || loading2 || success || errMsg) && "pr-10",
|
|
670
|
+
// Variant styles
|
|
671
|
+
variantStyles6[variant].container,
|
|
672
|
+
errMsg ? variantStyles6[variant].error : variantStyles6[variant].focus,
|
|
673
|
+
// Reduce visual weight for sm: no default drop shadows
|
|
674
|
+
size !== "sm" && variant !== "minimal" && "shadow-sm",
|
|
675
|
+
size !== "sm" && isFocused && "shadow-md",
|
|
676
|
+
className
|
|
704
677
|
),
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
678
|
+
disabled,
|
|
679
|
+
...restInput
|
|
680
|
+
}
|
|
681
|
+
),
|
|
682
|
+
/* @__PURE__ */ jsxs5("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [
|
|
683
|
+
getStatusIcon(),
|
|
684
|
+
RightIcon && !loading2 && !success && !errMsg && /* @__PURE__ */ jsx5(RightIcon, { className: cn("text-muted-foreground", sizeStyles8[size].icon) }),
|
|
685
|
+
clearable && hasValue && !loading2 && /* @__PURE__ */ jsx5(
|
|
686
|
+
"button",
|
|
687
|
+
{
|
|
688
|
+
type: "button",
|
|
689
|
+
onClick: () => {
|
|
690
|
+
if (onClear) return onClear();
|
|
691
|
+
rest.onChange?.({ target: { value: "" } });
|
|
692
|
+
},
|
|
693
|
+
className: cn(
|
|
694
|
+
"flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors rounded-sm",
|
|
695
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
|
|
696
|
+
"active:bg-accent active:text-accent-foreground",
|
|
697
|
+
sizeStyles8[size].button
|
|
698
|
+
),
|
|
699
|
+
tabIndex: 0,
|
|
700
|
+
"aria-label": "Clear input",
|
|
701
|
+
children: /* @__PURE__ */ jsx5(X, { className: sizeStyles8[size].icon })
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
showPasswordToggle && /* @__PURE__ */ jsx5(
|
|
705
|
+
"button",
|
|
706
|
+
{
|
|
707
|
+
type: "button",
|
|
708
|
+
onClick: () => setShowPassword(!showPassword),
|
|
709
|
+
className: cn(
|
|
710
|
+
"flex items-center justify-center text-muted-foreground hover:text-foreground transition-colors rounded-full",
|
|
711
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-ring/40",
|
|
712
|
+
"active:bg-accent/50 active:text-accent-foreground",
|
|
713
|
+
sizeStyles8[size].button
|
|
714
|
+
),
|
|
715
|
+
tabIndex: 0,
|
|
716
|
+
"aria-label": showPassword ? "Hide password" : "Show password",
|
|
717
|
+
children: showPassword ? /* @__PURE__ */ jsx5(EyeOff, { className: sizeStyles8[size].icon }) : /* @__PURE__ */ jsx5(Eye, { className: sizeStyles8[size].icon })
|
|
718
|
+
}
|
|
719
|
+
)
|
|
720
|
+
] }),
|
|
721
|
+
variant === "minimal" && /* @__PURE__ */ jsx5(
|
|
722
|
+
"div",
|
|
723
|
+
{
|
|
724
|
+
className: cn(
|
|
725
|
+
"absolute bottom-0 left-0 h-0.5 bg-gradient-to-r from-primary to-primary/60 transition-all duration-300",
|
|
726
|
+
// default hidden
|
|
727
|
+
"w-0 opacity-0",
|
|
728
|
+
// expand underline when focused within input container
|
|
729
|
+
"group-focus-within:w-full group-focus-within:opacity-100"
|
|
730
|
+
)
|
|
708
731
|
}
|
|
709
732
|
)
|
|
710
733
|
] }),
|
|
711
|
-
|
|
712
|
-
"
|
|
734
|
+
errMsg && /* @__PURE__ */ jsxs5("div", { id: errorId, className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
735
|
+
/* @__PURE__ */ jsx5(AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
736
|
+
/* @__PURE__ */ jsx5("span", { children: errMsg })
|
|
737
|
+
] }),
|
|
738
|
+
(description || hint) && !errMsg && /* @__PURE__ */ jsx5(
|
|
739
|
+
"p",
|
|
713
740
|
{
|
|
741
|
+
id: descId,
|
|
714
742
|
className: cn(
|
|
715
|
-
"
|
|
716
|
-
//
|
|
717
|
-
"
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
)
|
|
743
|
+
"text-xs transition-colors duration-200",
|
|
744
|
+
// follow focus state of the whole field area
|
|
745
|
+
"text-muted-foreground group-focus-within:text-primary/70"
|
|
746
|
+
),
|
|
747
|
+
children: hint || description
|
|
721
748
|
}
|
|
722
749
|
)
|
|
723
|
-
] })
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
/* @__PURE__ */ jsx5("span", { children: errMsg })
|
|
727
|
-
] }),
|
|
728
|
-
(description || hint) && !errMsg && /* @__PURE__ */ jsx5("p", { id: descId, className: cn(
|
|
729
|
-
"text-xs transition-colors duration-200",
|
|
730
|
-
// follow focus state of the whole field area
|
|
731
|
-
"text-muted-foreground group-focus-within:text-primary/70"
|
|
732
|
-
), children: hint || description })
|
|
733
|
-
] });
|
|
734
|
-
});
|
|
750
|
+
] });
|
|
751
|
+
}
|
|
752
|
+
);
|
|
735
753
|
Input.displayName = "Input";
|
|
736
|
-
var SearchInput = forwardRef3(
|
|
737
|
-
onSearch,
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}, searchDelay);
|
|
748
|
-
return () => clearTimeout(timer);
|
|
749
|
-
}, [searchValue, onSearch, searchDelay]);
|
|
750
|
-
return /* @__PURE__ */ jsx5(
|
|
751
|
-
Input,
|
|
752
|
-
{
|
|
753
|
-
ref,
|
|
754
|
-
type: "search",
|
|
755
|
-
leftIcon: Search,
|
|
756
|
-
placeholder,
|
|
757
|
-
clearable: true,
|
|
758
|
-
value: searchValue,
|
|
759
|
-
onChange: (e) => setSearchValue(e.target.value),
|
|
760
|
-
onClear: () => setSearchValue(""),
|
|
761
|
-
...props
|
|
762
|
-
}
|
|
763
|
-
);
|
|
764
|
-
});
|
|
765
|
-
SearchInput.displayName = "SearchInput";
|
|
766
|
-
var PasswordInput = forwardRef3(({
|
|
767
|
-
showStrength = false,
|
|
768
|
-
strengthLabels = ["Weak", "Fair", "Good", "Strong"],
|
|
769
|
-
...props
|
|
770
|
-
}, ref) => {
|
|
771
|
-
const getPasswordStrength = (password) => {
|
|
772
|
-
let score = 0;
|
|
773
|
-
if (password.length >= 8) score++;
|
|
774
|
-
if (/[a-z]/.test(password)) score++;
|
|
775
|
-
if (/[A-Z]/.test(password)) score++;
|
|
776
|
-
if (/[0-9]/.test(password)) score++;
|
|
777
|
-
if (/[^A-Za-z0-9]/.test(password)) score++;
|
|
778
|
-
return Math.min(score, 4);
|
|
779
|
-
};
|
|
780
|
-
const strength = showStrength && typeof props.value === "string" ? getPasswordStrength(props.value) : 0;
|
|
781
|
-
const strengthColors = ["bg-destructive", "bg-warning", "bg-warning", "bg-success"];
|
|
782
|
-
const strengthLabel = strengthLabels[strength - 1];
|
|
783
|
-
return /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
784
|
-
/* @__PURE__ */ jsx5(Input, { ref, type: "password", ...props }),
|
|
785
|
-
showStrength && props.value && /* @__PURE__ */ jsxs5("div", { className: "space-y-1", children: [
|
|
786
|
-
/* @__PURE__ */ jsx5("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((level) => /* @__PURE__ */ jsx5(
|
|
787
|
-
"div",
|
|
788
|
-
{
|
|
789
|
-
className: cn(
|
|
790
|
-
"h-1 flex-1 rounded-full transition-colors duration-300",
|
|
791
|
-
level <= strength ? strengthColors[strength - 1] : "bg-muted"
|
|
792
|
-
)
|
|
793
|
-
},
|
|
794
|
-
level
|
|
795
|
-
)) }),
|
|
796
|
-
strengthLabel && /* @__PURE__ */ jsx5("p", { className: cn(
|
|
797
|
-
"text-xs font-medium",
|
|
798
|
-
strength <= 1 ? "text-destructive" : strength <= 2 ? "text-warning" : strength <= 3 ? "text-warning" : "text-success"
|
|
799
|
-
), children: strengthLabel })
|
|
800
|
-
] })
|
|
801
|
-
] });
|
|
802
|
-
});
|
|
803
|
-
PasswordInput.displayName = "PasswordInput";
|
|
804
|
-
var NumberInput = forwardRef3(({
|
|
805
|
-
min,
|
|
806
|
-
max,
|
|
807
|
-
step = 1,
|
|
808
|
-
showSteppers = true,
|
|
809
|
-
onIncrement,
|
|
810
|
-
onDecrement,
|
|
811
|
-
formatThousands = false,
|
|
812
|
-
locale = "vi-VN",
|
|
813
|
-
value,
|
|
814
|
-
onChange,
|
|
815
|
-
...props
|
|
816
|
-
}, ref) => {
|
|
817
|
-
const toNumber = (v) => {
|
|
818
|
-
if (v === "" || v === void 0 || v === null) return 0;
|
|
819
|
-
const n = Number(v);
|
|
820
|
-
return Number.isFinite(n) ? n : 0;
|
|
821
|
-
};
|
|
822
|
-
const format = React4.useCallback(
|
|
823
|
-
(n) => new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }).format(n),
|
|
824
|
-
[locale]
|
|
825
|
-
);
|
|
826
|
-
const parse = (s) => {
|
|
827
|
-
const digits = (s || "").replace(/\D+/g, "");
|
|
828
|
-
return digits ? Number(digits) : NaN;
|
|
829
|
-
};
|
|
830
|
-
const [displayValue, setDisplayValue] = React4.useState(
|
|
831
|
-
formatThousands ? value !== void 0 && value !== null && value !== "" ? format(toNumber(value)) : "" : String(value ?? "")
|
|
832
|
-
);
|
|
833
|
-
const currentValue = toNumber(value);
|
|
834
|
-
React4.useEffect(() => {
|
|
835
|
-
if (formatThousands) {
|
|
836
|
-
const next = value === "" || value === void 0 || value === null ? "" : format(toNumber(value));
|
|
837
|
-
setDisplayValue((prev) => prev === next ? prev : next);
|
|
838
|
-
} else {
|
|
839
|
-
const next = String(value ?? "");
|
|
840
|
-
setDisplayValue((prev) => prev === next ? prev : next);
|
|
841
|
-
}
|
|
842
|
-
}, [value, formatThousands, locale, format]);
|
|
843
|
-
const handleIncrement = () => {
|
|
844
|
-
if (onIncrement) {
|
|
845
|
-
onIncrement();
|
|
846
|
-
} else if (onChange) {
|
|
847
|
-
const newValue = Math.min(currentValue + step, max ?? Infinity);
|
|
848
|
-
if (formatThousands) setDisplayValue(format(newValue));
|
|
849
|
-
onChange({ target: { value: newValue.toString() } });
|
|
850
|
-
}
|
|
851
|
-
};
|
|
852
|
-
const handleDecrement = () => {
|
|
853
|
-
if (onDecrement) {
|
|
854
|
-
onDecrement();
|
|
855
|
-
} else if (onChange) {
|
|
856
|
-
const newValue = Math.max(currentValue - step, min ?? -Infinity);
|
|
857
|
-
if (formatThousands) setDisplayValue(format(newValue));
|
|
858
|
-
onChange({ target: { value: newValue.toString() } });
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
return /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
|
|
862
|
-
/* @__PURE__ */ jsx5(
|
|
754
|
+
var SearchInput = forwardRef3(
|
|
755
|
+
({ onSearch, searchDelay = 300, placeholder = "Search...", ...props }, ref) => {
|
|
756
|
+
const [searchValue, setSearchValue] = useState4(props.value || "");
|
|
757
|
+
React4.useEffect(() => {
|
|
758
|
+
if (!onSearch) return;
|
|
759
|
+
const timer = setTimeout(() => {
|
|
760
|
+
onSearch(searchValue);
|
|
761
|
+
}, searchDelay);
|
|
762
|
+
return () => clearTimeout(timer);
|
|
763
|
+
}, [searchValue, onSearch, searchDelay]);
|
|
764
|
+
return /* @__PURE__ */ jsx5(
|
|
863
765
|
Input,
|
|
864
766
|
{
|
|
865
767
|
ref,
|
|
866
|
-
type:
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
768
|
+
type: "search",
|
|
769
|
+
leftIcon: Search,
|
|
770
|
+
placeholder,
|
|
771
|
+
clearable: true,
|
|
772
|
+
value: searchValue,
|
|
773
|
+
onChange: (e) => setSearchValue(e.target.value),
|
|
774
|
+
onClear: () => setSearchValue(""),
|
|
775
|
+
...props
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
SearchInput.displayName = "SearchInput";
|
|
781
|
+
var PasswordInput = forwardRef3(
|
|
782
|
+
({ showStrength = false, strengthLabels = ["Weak", "Fair", "Good", "Strong"], ...props }, ref) => {
|
|
783
|
+
const getPasswordStrength = (password) => {
|
|
784
|
+
let score = 0;
|
|
785
|
+
if (password.length >= 8) score++;
|
|
786
|
+
if (/[a-z]/.test(password)) score++;
|
|
787
|
+
if (/[A-Z]/.test(password)) score++;
|
|
788
|
+
if (/[0-9]/.test(password)) score++;
|
|
789
|
+
if (/[^A-Za-z0-9]/.test(password)) score++;
|
|
790
|
+
return Math.min(score, 4);
|
|
791
|
+
};
|
|
792
|
+
const strength = showStrength && typeof props.value === "string" ? getPasswordStrength(props.value) : 0;
|
|
793
|
+
const strengthColors = ["bg-destructive", "bg-warning", "bg-warning", "bg-success"];
|
|
794
|
+
const strengthLabel = strengthLabels[strength - 1];
|
|
795
|
+
return /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
796
|
+
/* @__PURE__ */ jsx5(Input, { ref, type: "password", ...props }),
|
|
797
|
+
showStrength && props.value && /* @__PURE__ */ jsxs5("div", { className: "space-y-1", children: [
|
|
798
|
+
/* @__PURE__ */ jsx5("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((level) => /* @__PURE__ */ jsx5(
|
|
799
|
+
"div",
|
|
800
|
+
{
|
|
801
|
+
className: cn(
|
|
802
|
+
"h-1 flex-1 rounded-full transition-colors duration-300",
|
|
803
|
+
level <= strength ? strengthColors[strength - 1] : "bg-muted"
|
|
804
|
+
)
|
|
805
|
+
},
|
|
806
|
+
level
|
|
807
|
+
)) }),
|
|
808
|
+
strengthLabel && /* @__PURE__ */ jsx5(
|
|
809
|
+
"p",
|
|
810
|
+
{
|
|
811
|
+
className: cn(
|
|
812
|
+
"text-xs font-medium",
|
|
813
|
+
strength <= 1 ? "text-destructive" : strength <= 2 ? "text-warning" : strength <= 3 ? "text-warning" : "text-success"
|
|
814
|
+
),
|
|
815
|
+
children: strengthLabel
|
|
884
816
|
}
|
|
885
|
-
},
|
|
886
|
-
...props,
|
|
887
|
-
className: cn(
|
|
888
|
-
showSteppers && [
|
|
889
|
-
"pr-12",
|
|
890
|
-
// Hide native browser steppers
|
|
891
|
-
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
892
|
-
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
893
|
-
"[&::-webkit-inner-spin-button]:m-0",
|
|
894
|
-
"appearance-none"
|
|
895
|
-
],
|
|
896
|
-
props.className
|
|
897
817
|
)
|
|
818
|
+
] })
|
|
819
|
+
] });
|
|
820
|
+
}
|
|
821
|
+
);
|
|
822
|
+
PasswordInput.displayName = "PasswordInput";
|
|
823
|
+
var NumberInput = forwardRef3(
|
|
824
|
+
({ min, max, step = 1, showSteppers = true, onIncrement, onDecrement, formatThousands = false, locale = "vi-VN", value, onChange, ...props }, ref) => {
|
|
825
|
+
const toNumber = (v) => {
|
|
826
|
+
if (v === "" || v === void 0 || v === null) return 0;
|
|
827
|
+
const n = Number(v);
|
|
828
|
+
return Number.isFinite(n) ? n : 0;
|
|
829
|
+
};
|
|
830
|
+
const format = React4.useCallback((n) => new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }).format(n), [locale]);
|
|
831
|
+
const parse = (s) => {
|
|
832
|
+
const digits = (s || "").replace(/\D+/g, "");
|
|
833
|
+
return digits ? Number(digits) : NaN;
|
|
834
|
+
};
|
|
835
|
+
const [displayValue, setDisplayValue] = React4.useState(
|
|
836
|
+
formatThousands ? value !== void 0 && value !== null && value !== "" ? format(toNumber(value)) : "" : String(value ?? "")
|
|
837
|
+
);
|
|
838
|
+
const currentValue = toNumber(value);
|
|
839
|
+
React4.useEffect(() => {
|
|
840
|
+
if (formatThousands) {
|
|
841
|
+
const next = value === "" || value === void 0 || value === null ? "" : format(toNumber(value));
|
|
842
|
+
setDisplayValue((prev) => prev === next ? prev : next);
|
|
843
|
+
} else {
|
|
844
|
+
const next = String(value ?? "");
|
|
845
|
+
setDisplayValue((prev) => prev === next ? prev : next);
|
|
846
|
+
}
|
|
847
|
+
}, [value, formatThousands, locale, format]);
|
|
848
|
+
const handleIncrement = () => {
|
|
849
|
+
if (onIncrement) {
|
|
850
|
+
onIncrement();
|
|
851
|
+
} else if (onChange) {
|
|
852
|
+
const newValue = Math.min(currentValue + step, max ?? Infinity);
|
|
853
|
+
if (formatThousands) setDisplayValue(format(newValue));
|
|
854
|
+
onChange({ target: { value: newValue.toString() } });
|
|
898
855
|
}
|
|
899
|
-
|
|
900
|
-
|
|
856
|
+
};
|
|
857
|
+
const handleDecrement = () => {
|
|
858
|
+
if (onDecrement) {
|
|
859
|
+
onDecrement();
|
|
860
|
+
} else if (onChange) {
|
|
861
|
+
const newValue = Math.max(currentValue - step, min ?? -Infinity);
|
|
862
|
+
if (formatThousands) setDisplayValue(format(newValue));
|
|
863
|
+
onChange({ target: { value: newValue.toString() } });
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
return /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
|
|
901
867
|
/* @__PURE__ */ jsx5(
|
|
902
|
-
|
|
868
|
+
Input,
|
|
903
869
|
{
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
{
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
"path",
|
|
924
|
-
{
|
|
925
|
-
d: "M4 2L6 6H2L4 2Z",
|
|
926
|
-
fill: "currentColor"
|
|
927
|
-
}
|
|
928
|
-
)
|
|
870
|
+
ref,
|
|
871
|
+
type: formatThousands ? "text" : "number",
|
|
872
|
+
min,
|
|
873
|
+
max,
|
|
874
|
+
step,
|
|
875
|
+
rightIcon: showSteppers ? void 0 : props.rightIcon,
|
|
876
|
+
value: displayValue,
|
|
877
|
+
onChange: (e) => {
|
|
878
|
+
if (!onChange) return;
|
|
879
|
+
if (!formatThousands) return onChange(e);
|
|
880
|
+
const raw = e.target.value;
|
|
881
|
+
const parsed = parse(raw);
|
|
882
|
+
if (Number.isNaN(parsed)) {
|
|
883
|
+
setDisplayValue("");
|
|
884
|
+
onChange({ target: { value: "" } });
|
|
885
|
+
} else {
|
|
886
|
+
const bounded = Math.min(Math.max(parsed, min ?? -Infinity), max ?? Infinity);
|
|
887
|
+
setDisplayValue(format(bounded));
|
|
888
|
+
onChange({ target: { value: bounded.toString() } });
|
|
929
889
|
}
|
|
890
|
+
},
|
|
891
|
+
...props,
|
|
892
|
+
className: cn(
|
|
893
|
+
showSteppers && [
|
|
894
|
+
"pr-12",
|
|
895
|
+
// Hide native browser steppers
|
|
896
|
+
"[&::-webkit-outer-spin-button]:appearance-none",
|
|
897
|
+
"[&::-webkit-inner-spin-button]:appearance-none",
|
|
898
|
+
"[&::-webkit-inner-spin-button]:m-0",
|
|
899
|
+
"appearance-none"
|
|
900
|
+
],
|
|
901
|
+
props.className
|
|
930
902
|
)
|
|
931
903
|
}
|
|
932
904
|
),
|
|
905
|
+
showSteppers && /* @__PURE__ */ jsxs5("div", { className: "absolute right-2 top-1/2 -translate-y-1/2 flex flex-col gap-0.5", children: [
|
|
906
|
+
/* @__PURE__ */ jsx5(
|
|
907
|
+
"button",
|
|
908
|
+
{
|
|
909
|
+
type: "button",
|
|
910
|
+
onClick: handleIncrement,
|
|
911
|
+
disabled: max !== void 0 && currentValue >= max,
|
|
912
|
+
className: cn(
|
|
913
|
+
"flex items-center justify-center w-4 h-4 rounded-sm transition-colors",
|
|
914
|
+
"hover:bg-accent focus:outline-none focus:bg-accent",
|
|
915
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
916
|
+
"text-muted-foreground hover:text-foreground"
|
|
917
|
+
),
|
|
918
|
+
"aria-label": "Increase value",
|
|
919
|
+
children: /* @__PURE__ */ jsx5("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", className: "flex-shrink-0", children: /* @__PURE__ */ jsx5("path", { d: "M4 2L6 6H2L4 2Z", fill: "currentColor" }) })
|
|
920
|
+
}
|
|
921
|
+
),
|
|
922
|
+
/* @__PURE__ */ jsx5(
|
|
923
|
+
"button",
|
|
924
|
+
{
|
|
925
|
+
type: "button",
|
|
926
|
+
onClick: handleDecrement,
|
|
927
|
+
disabled: min !== void 0 && currentValue <= min,
|
|
928
|
+
className: cn(
|
|
929
|
+
"flex items-center justify-center w-4 h-4 rounded-sm transition-colors",
|
|
930
|
+
"hover:bg-accent focus:outline-none focus:bg-accent",
|
|
931
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
932
|
+
"text-muted-foreground hover:text-foreground"
|
|
933
|
+
),
|
|
934
|
+
"aria-label": "Decrease value",
|
|
935
|
+
children: /* @__PURE__ */ jsx5("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", className: "flex-shrink-0", children: /* @__PURE__ */ jsx5("path", { d: "M4 6L2 2H6L4 6Z", fill: "currentColor" }) })
|
|
936
|
+
}
|
|
937
|
+
)
|
|
938
|
+
] })
|
|
939
|
+
] });
|
|
940
|
+
}
|
|
941
|
+
);
|
|
942
|
+
NumberInput.displayName = "NumberInput";
|
|
943
|
+
var Textarea = forwardRef3(
|
|
944
|
+
({ label, error, description, variant = "default", resize = "vertical", counter = false, className, required, value, maxLength, ...props }, ref) => {
|
|
945
|
+
const [isFocused, setIsFocused] = useState4(false);
|
|
946
|
+
const charCount = typeof value === "string" ? value.length : 0;
|
|
947
|
+
const variantStyles6 = {
|
|
948
|
+
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",
|
|
949
|
+
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",
|
|
950
|
+
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",
|
|
951
|
+
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"
|
|
952
|
+
};
|
|
953
|
+
const resizeClasses = {
|
|
954
|
+
none: "resize-none",
|
|
955
|
+
vertical: "resize-y",
|
|
956
|
+
horizontal: "resize-x",
|
|
957
|
+
both: "resize"
|
|
958
|
+
};
|
|
959
|
+
return /* @__PURE__ */ jsxs5("div", { className: "w-full space-y-2", children: [
|
|
960
|
+
label && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
961
|
+
/* @__PURE__ */ jsxs5(
|
|
962
|
+
"label",
|
|
963
|
+
{
|
|
964
|
+
className: cn(
|
|
965
|
+
"text-sm font-medium transition-colors duration-200",
|
|
966
|
+
isFocused ? "text-primary" : "text-foreground",
|
|
967
|
+
error && "text-destructive"
|
|
968
|
+
),
|
|
969
|
+
children: [
|
|
970
|
+
label,
|
|
971
|
+
required && /* @__PURE__ */ jsx5("span", { className: "text-destructive ml-1", children: "*" })
|
|
972
|
+
]
|
|
973
|
+
}
|
|
974
|
+
),
|
|
975
|
+
counter && maxLength && /* @__PURE__ */ jsxs5(
|
|
976
|
+
"span",
|
|
977
|
+
{
|
|
978
|
+
className: cn(
|
|
979
|
+
"text-xs transition-colors duration-200",
|
|
980
|
+
charCount > maxLength * 0.9 ? "text-warning" : "text-muted-foreground",
|
|
981
|
+
charCount >= maxLength && "text-destructive"
|
|
982
|
+
),
|
|
983
|
+
children: [
|
|
984
|
+
charCount,
|
|
985
|
+
"/",
|
|
986
|
+
maxLength
|
|
987
|
+
]
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
] }),
|
|
933
991
|
/* @__PURE__ */ jsx5(
|
|
934
|
-
"
|
|
992
|
+
"textarea",
|
|
935
993
|
{
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
994
|
+
ref,
|
|
995
|
+
value,
|
|
996
|
+
maxLength,
|
|
997
|
+
required,
|
|
998
|
+
onFocus: () => setIsFocused(true),
|
|
999
|
+
onBlur: () => setIsFocused(false),
|
|
939
1000
|
className: cn(
|
|
940
|
-
"
|
|
941
|
-
"
|
|
942
|
-
"disabled:
|
|
943
|
-
|
|
1001
|
+
"w-full rounded-lg px-4 py-3 text-sm text-foreground transition-all duration-200",
|
|
1002
|
+
"placeholder:text-muted-foreground focus:outline-none min-h-[80px]",
|
|
1003
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1004
|
+
variantStyles6[variant],
|
|
1005
|
+
// DÒNG NÀY ĐÃ ĐƯỢC CẬP NHẬT:
|
|
1006
|
+
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",
|
|
1007
|
+
resizeClasses[resize],
|
|
1008
|
+
isFocused && "shadow-md",
|
|
1009
|
+
variant !== "minimal" && "shadow-sm",
|
|
1010
|
+
className
|
|
944
1011
|
),
|
|
945
|
-
|
|
946
|
-
children: /* @__PURE__ */ jsx5(
|
|
947
|
-
"svg",
|
|
948
|
-
{
|
|
949
|
-
width: "8",
|
|
950
|
-
height: "8",
|
|
951
|
-
viewBox: "0 0 8 8",
|
|
952
|
-
fill: "none",
|
|
953
|
-
className: "flex-shrink-0",
|
|
954
|
-
children: /* @__PURE__ */ jsx5(
|
|
955
|
-
"path",
|
|
956
|
-
{
|
|
957
|
-
d: "M4 6L2 2H6L4 6Z",
|
|
958
|
-
fill: "currentColor"
|
|
959
|
-
}
|
|
960
|
-
)
|
|
961
|
-
}
|
|
962
|
-
)
|
|
1012
|
+
...props
|
|
963
1013
|
}
|
|
964
|
-
)
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
})
|
|
968
|
-
NumberInput.displayName = "NumberInput";
|
|
969
|
-
var Textarea = forwardRef3(({
|
|
970
|
-
label,
|
|
971
|
-
error,
|
|
972
|
-
description,
|
|
973
|
-
variant = "default",
|
|
974
|
-
resize = "vertical",
|
|
975
|
-
counter = false,
|
|
976
|
-
className,
|
|
977
|
-
required,
|
|
978
|
-
value,
|
|
979
|
-
maxLength,
|
|
980
|
-
...props
|
|
981
|
-
}, ref) => {
|
|
982
|
-
const [isFocused, setIsFocused] = useState4(false);
|
|
983
|
-
const charCount = typeof value === "string" ? value.length : 0;
|
|
984
|
-
const variantStyles6 = {
|
|
985
|
-
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",
|
|
986
|
-
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",
|
|
987
|
-
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",
|
|
988
|
-
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"
|
|
989
|
-
};
|
|
990
|
-
const resizeClasses = {
|
|
991
|
-
none: "resize-none",
|
|
992
|
-
vertical: "resize-y",
|
|
993
|
-
horizontal: "resize-x",
|
|
994
|
-
both: "resize"
|
|
995
|
-
};
|
|
996
|
-
return /* @__PURE__ */ jsxs5("div", { className: "w-full space-y-2", children: [
|
|
997
|
-
label && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
998
|
-
/* @__PURE__ */ jsxs5("label", { className: cn(
|
|
999
|
-
"text-sm font-medium transition-colors duration-200",
|
|
1000
|
-
isFocused ? "text-primary" : "text-foreground",
|
|
1001
|
-
error && "text-destructive"
|
|
1002
|
-
), children: [
|
|
1003
|
-
label,
|
|
1004
|
-
required && /* @__PURE__ */ jsx5("span", { className: "text-destructive ml-1", children: "*" })
|
|
1014
|
+
),
|
|
1015
|
+
error && /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
1016
|
+
/* @__PURE__ */ jsx5(AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
1017
|
+
/* @__PURE__ */ jsx5("span", { children: error })
|
|
1005
1018
|
] }),
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
), children: [
|
|
1011
|
-
charCount,
|
|
1012
|
-
"/",
|
|
1013
|
-
maxLength
|
|
1014
|
-
] })
|
|
1015
|
-
] }),
|
|
1016
|
-
/* @__PURE__ */ jsx5(
|
|
1017
|
-
"textarea",
|
|
1018
|
-
{
|
|
1019
|
-
ref,
|
|
1020
|
-
value,
|
|
1021
|
-
maxLength,
|
|
1022
|
-
required,
|
|
1023
|
-
onFocus: () => setIsFocused(true),
|
|
1024
|
-
onBlur: () => setIsFocused(false),
|
|
1025
|
-
className: cn(
|
|
1026
|
-
"w-full rounded-lg px-4 py-3 text-sm text-foreground transition-all duration-200",
|
|
1027
|
-
"placeholder:text-muted-foreground focus:outline-none min-h-[80px]",
|
|
1028
|
-
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
1029
|
-
variantStyles6[variant],
|
|
1030
|
-
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",
|
|
1031
|
-
resizeClasses[resize],
|
|
1032
|
-
isFocused && "shadow-md",
|
|
1033
|
-
variant !== "minimal" && "shadow-sm",
|
|
1034
|
-
className
|
|
1035
|
-
),
|
|
1036
|
-
...props
|
|
1037
|
-
}
|
|
1038
|
-
),
|
|
1039
|
-
error && /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-sm text-destructive animate-in slide-in-from-top-1 duration-200", children: [
|
|
1040
|
-
/* @__PURE__ */ jsx5(AlertCircle, { className: "w-4 h-4 flex-shrink-0" }),
|
|
1041
|
-
/* @__PURE__ */ jsx5("span", { children: error })
|
|
1042
|
-
] }),
|
|
1043
|
-
description && !error && /* @__PURE__ */ jsx5("p", { className: cn(
|
|
1044
|
-
"text-xs transition-colors duration-200",
|
|
1045
|
-
isFocused ? "text-primary/70" : "text-muted-foreground"
|
|
1046
|
-
), children: description })
|
|
1047
|
-
] });
|
|
1048
|
-
});
|
|
1019
|
+
description && !error && /* @__PURE__ */ jsx5("p", { className: cn("text-xs transition-colors duration-200", isFocused ? "text-primary/70" : "text-muted-foreground"), children: description })
|
|
1020
|
+
] });
|
|
1021
|
+
}
|
|
1022
|
+
);
|
|
1049
1023
|
Textarea.displayName = "Textarea";
|
|
1050
1024
|
var Input_default = Input;
|
|
1051
1025
|
|
|
@@ -6038,8 +6012,10 @@ function OverlayControls({
|
|
|
6038
6012
|
break;
|
|
6039
6013
|
case "f":
|
|
6040
6014
|
case "F":
|
|
6041
|
-
e.
|
|
6042
|
-
|
|
6015
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
6016
|
+
e.preventDefault();
|
|
6017
|
+
onToggleFullscreen?.();
|
|
6018
|
+
}
|
|
6043
6019
|
break;
|
|
6044
6020
|
case "m":
|
|
6045
6021
|
case "M":
|