@memelabui/ui 0.5.0 → 0.6.0

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.js CHANGED
@@ -701,7 +701,7 @@ var Slider = forwardRef(function Slider2({
701
701
  if (numMax === numMin) return 0;
702
702
  return Math.max(0, Math.min(100, (numericValue - numMin) / (numMax - numMin) * 100));
703
703
  }, [numericValue, min, max]);
704
- const trackGradient = `linear-gradient(to right, var(--ml-primary, #8b5cf6) ${percentage}%, rgba(255,255,255,0.1) ${percentage}%)`;
704
+ const trackGradient = `linear-gradient(to right, rgb(var(--ml-primary, 139 92 246)) ${percentage}%, rgba(255,255,255,0.1) ${percentage}%)`;
705
705
  const displayValue = formatValue ? formatValue(numericValue) : String(numericValue);
706
706
  const handleChange = (e) => {
707
707
  onChange?.(Number(e.target.value));
@@ -733,7 +733,7 @@ var Slider = forwardRef(function Slider2({
733
733
  "[&::-webkit-slider-thumb]:rounded-full",
734
734
  "[&::-webkit-slider-thumb]:bg-white",
735
735
  "[&::-webkit-slider-thumb]:border-2",
736
- "[&::-webkit-slider-thumb]:border-[var(--ml-primary,#8b5cf6)]",
736
+ "[&::-webkit-slider-thumb]:border-[rgb(var(--ml-primary,139_92_246))]",
737
737
  "[&::-webkit-slider-thumb]:transition-shadow",
738
738
  "[&::-webkit-slider-thumb]:duration-150",
739
739
  // Moz thumb
@@ -742,7 +742,7 @@ var Slider = forwardRef(function Slider2({
742
742
  "[&::-moz-range-thumb]:rounded-full",
743
743
  "[&::-moz-range-thumb]:bg-white",
744
744
  "[&::-moz-range-thumb]:border-2",
745
- "[&::-moz-range-thumb]:border-[var(--ml-primary,#8b5cf6)]",
745
+ "[&::-moz-range-thumb]:border-[rgb(var(--ml-primary,139_92_246))]",
746
746
  "[&::-moz-range-thumb]:transition-shadow",
747
747
  "[&::-moz-range-thumb]:duration-150",
748
748
  // Moz track — transparent so the gradient on the element shows through
@@ -2452,7 +2452,7 @@ function StageProgress({ stages, activeStage, className }) {
2452
2452
  ) })
2453
2453
  ) : isActive ? (
2454
2454
  // Active: primary dot with ring
2455
- /* @__PURE__ */ jsx("span", { className: "flex h-3.5 w-3.5 items-center justify-center rounded-full ring-2 ring-[var(--ml-primary)] ring-offset-1 ring-offset-black/50", children: /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-[var(--ml-primary)]" }) })
2455
+ /* @__PURE__ */ jsx("span", { className: "flex h-3.5 w-3.5 items-center justify-center rounded-full ring-2 ring-[rgb(var(--ml-primary))] ring-offset-1 ring-offset-black/50", children: /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-[rgb(var(--ml-primary))]" }) })
2456
2456
  ) : (
2457
2457
  // Upcoming: hollow dim dot
2458
2458
  /* @__PURE__ */ jsx("span", { className: "h-3.5 w-3.5 rounded-full border border-white/20 bg-transparent" })
@@ -2475,7 +2475,7 @@ function StageProgress({ stages, activeStage, className }) {
2475
2475
  children: /* @__PURE__ */ jsx(
2476
2476
  "div",
2477
2477
  {
2478
- className: "h-full rounded-full bg-gradient-to-r from-[var(--ml-accent)] to-[var(--ml-primary)] transition-[width] duration-500 ease-in-out",
2478
+ className: "h-full rounded-full bg-gradient-to-r from-[rgb(var(--ml-accent))] to-[rgb(var(--ml-primary))] transition-[width] duration-500 ease-in-out",
2479
2479
  style: { width: `${fillPercent}%` },
2480
2480
  children: isAnimating && /* @__PURE__ */ jsx(
2481
2481
  "span",
@@ -3470,5 +3470,793 @@ var NotificationBell = forwardRef(
3470
3470
  );
3471
3471
  }
3472
3472
  );
3473
+ var sizeClass7 = {
3474
+ xs: "text-sm font-semibold",
3475
+ sm: "text-base font-semibold",
3476
+ md: "text-lg font-bold",
3477
+ lg: "text-xl font-bold",
3478
+ xl: "text-2xl font-bold tracking-tight",
3479
+ "2xl": "text-3xl font-bold tracking-tight sm:text-4xl"
3480
+ };
3481
+ var levelToSize = {
3482
+ 1: "2xl",
3483
+ 2: "xl",
3484
+ 3: "lg",
3485
+ 4: "md",
3486
+ 5: "sm",
3487
+ 6: "xs"
3488
+ };
3489
+ var colorClass = {
3490
+ default: "text-white",
3491
+ muted: "text-white/70",
3492
+ gradient: "text-gradient"
3493
+ };
3494
+ var Heading = forwardRef(function Heading2({ level = 2, size, color = "default", className, ...props }, ref) {
3495
+ const Tag = `h${level}`;
3496
+ const effectiveSize = size ?? levelToSize[level];
3497
+ return /* @__PURE__ */ jsx(
3498
+ Tag,
3499
+ {
3500
+ ref,
3501
+ ...props,
3502
+ className: cn(sizeClass7[effectiveSize], colorClass[color], className)
3503
+ }
3504
+ );
3505
+ });
3506
+ var sizeMap2 = {
3507
+ xs: "text-xs",
3508
+ sm: "text-sm",
3509
+ md: "text-base",
3510
+ lg: "text-lg"
3511
+ };
3512
+ var colorMap = {
3513
+ default: "text-white/90",
3514
+ muted: "text-white/70",
3515
+ dimmed: "text-white/50",
3516
+ primary: "text-primary-light",
3517
+ success: "text-emerald-400",
3518
+ warning: "text-amber-400",
3519
+ danger: "text-rose-400"
3520
+ };
3521
+ var weightMap = {
3522
+ normal: "font-normal",
3523
+ medium: "font-medium",
3524
+ semibold: "font-semibold",
3525
+ bold: "font-bold"
3526
+ };
3527
+ var Text = forwardRef(function Text2({ size = "md", color = "default", weight = "normal", inline = false, truncate = false, className, ...props }, ref) {
3528
+ const Tag = inline ? "span" : "p";
3529
+ return /* @__PURE__ */ jsx(
3530
+ Tag,
3531
+ {
3532
+ ref,
3533
+ ...props,
3534
+ className: cn(
3535
+ sizeMap2[size],
3536
+ colorMap[color],
3537
+ weightMap[weight],
3538
+ truncate && "truncate",
3539
+ className
3540
+ )
3541
+ }
3542
+ );
3543
+ });
3544
+ var gapClass = {
3545
+ 0: "gap-0",
3546
+ 1: "gap-1",
3547
+ 2: "gap-2",
3548
+ 3: "gap-3",
3549
+ 4: "gap-4",
3550
+ 5: "gap-5",
3551
+ 6: "gap-6",
3552
+ 8: "gap-8",
3553
+ 10: "gap-10",
3554
+ 12: "gap-12"
3555
+ };
3556
+ var alignClass2 = {
3557
+ start: "items-start",
3558
+ center: "items-center",
3559
+ end: "items-end",
3560
+ stretch: "items-stretch",
3561
+ baseline: "items-baseline"
3562
+ };
3563
+ var justifyClass = {
3564
+ start: "justify-start",
3565
+ center: "justify-center",
3566
+ end: "justify-end",
3567
+ between: "justify-between",
3568
+ around: "justify-around",
3569
+ evenly: "justify-evenly"
3570
+ };
3571
+ var Stack = forwardRef(function Stack2({ children, direction = "vertical", gap = 4, align = "stretch", justify = "start", wrap = false, className, ...props }, ref) {
3572
+ return /* @__PURE__ */ jsx(
3573
+ "div",
3574
+ {
3575
+ ref,
3576
+ ...props,
3577
+ className: cn(
3578
+ "flex",
3579
+ direction === "vertical" ? "flex-col" : "flex-row",
3580
+ gapClass[gap],
3581
+ alignClass2[align],
3582
+ justifyClass[justify],
3583
+ wrap && "flex-wrap",
3584
+ className
3585
+ ),
3586
+ children
3587
+ }
3588
+ );
3589
+ });
3590
+ var ScrollArea = forwardRef(function ScrollArea2({ children, maxHeight, hideScrollbar = false, orientation = "vertical", className, style, ...props }, ref) {
3591
+ const overflowClass = {
3592
+ vertical: "overflow-y-auto overflow-x-hidden",
3593
+ horizontal: "overflow-x-auto overflow-y-hidden",
3594
+ both: "overflow-auto"
3595
+ }[orientation];
3596
+ return /* @__PURE__ */ jsx(
3597
+ "div",
3598
+ {
3599
+ ref,
3600
+ ...props,
3601
+ tabIndex: 0,
3602
+ className: cn(
3603
+ overflowClass,
3604
+ // Custom dark scrollbar styling
3605
+ "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
3606
+ "[&::-webkit-scrollbar-track]:bg-transparent",
3607
+ "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-white/15 hover:[&::-webkit-scrollbar-thumb]:bg-white/25",
3608
+ hideScrollbar && "no-scrollbar",
3609
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/30 focus-visible:ring-inset",
3610
+ className
3611
+ ),
3612
+ style: {
3613
+ maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight,
3614
+ ...style
3615
+ },
3616
+ children
3617
+ }
3618
+ );
3619
+ });
3620
+ var defaultSeparator = /* @__PURE__ */ jsx(
3621
+ "svg",
3622
+ {
3623
+ className: "w-3.5 h-3.5 text-white/30 flex-shrink-0",
3624
+ viewBox: "0 0 16 16",
3625
+ fill: "none",
3626
+ stroke: "currentColor",
3627
+ strokeWidth: "2",
3628
+ strokeLinecap: "round",
3629
+ "aria-hidden": "true",
3630
+ children: /* @__PURE__ */ jsx("path", { d: "M6 4l4 4-4 4" })
3631
+ }
3632
+ );
3633
+ function Breadcrumbs({ items, separator = defaultSeparator, className }) {
3634
+ if (items.length === 0) return null;
3635
+ return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: cn("flex items-center", className), children: /* @__PURE__ */ jsx("ol", { className: "flex items-center gap-1.5 text-sm", children: items.map((item, index) => {
3636
+ const isLast = index === items.length - 1;
3637
+ return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1.5", children: [
3638
+ index > 0 && separator,
3639
+ isLast ? /* @__PURE__ */ jsx("span", { className: "text-white font-medium", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx(
3640
+ "a",
3641
+ {
3642
+ href: item.href,
3643
+ onClick: item.onClick,
3644
+ className: "text-white/50 hover:text-white/80 transition-colors",
3645
+ children: item.label
3646
+ }
3647
+ ) : item.onClick ? /* @__PURE__ */ jsx(
3648
+ "button",
3649
+ {
3650
+ type: "button",
3651
+ onClick: item.onClick,
3652
+ className: "text-white/50 hover:text-white/80 transition-colors",
3653
+ children: item.label
3654
+ }
3655
+ ) : /* @__PURE__ */ jsx("span", { className: "text-white/50", children: item.label })
3656
+ ] }, index);
3657
+ }) }) });
3658
+ }
3659
+ function Popover({
3660
+ content,
3661
+ children,
3662
+ placement = "bottom",
3663
+ closeOnClickOutside = true,
3664
+ closeOnEsc = true,
3665
+ open: controlledOpen,
3666
+ onOpenChange,
3667
+ offset = 8,
3668
+ className
3669
+ }) {
3670
+ const popoverId = useId();
3671
+ const anchorRef = useRef(null);
3672
+ const popoverRef = useRef(null);
3673
+ const [internalOpen, setInternalOpen] = useState(false);
3674
+ const [pos, setPos] = useState(null);
3675
+ const isControlled = controlledOpen !== void 0;
3676
+ const isOpen = isControlled ? controlledOpen : internalOpen;
3677
+ const setOpen = useCallback(
3678
+ (value) => {
3679
+ if (!isControlled) setInternalOpen(value);
3680
+ onOpenChange?.(value);
3681
+ },
3682
+ [isControlled, onOpenChange]
3683
+ );
3684
+ const toggle = useCallback(() => setOpen(!isOpen), [isOpen, setOpen]);
3685
+ const close = useCallback(() => setOpen(false), [setOpen]);
3686
+ const updatePosition = useCallback(() => {
3687
+ const el = anchorRef.current;
3688
+ if (!el) return;
3689
+ const r = el.getBoundingClientRect();
3690
+ let left;
3691
+ let top;
3692
+ let effPlacement = placement;
3693
+ if (placement === "bottom" || placement === "top") {
3694
+ left = r.left + r.width / 2;
3695
+ if (placement === "bottom") {
3696
+ top = r.bottom + offset;
3697
+ if (top + 200 > window.innerHeight && r.top - offset > 200) {
3698
+ effPlacement = "top";
3699
+ top = r.top - offset;
3700
+ }
3701
+ } else {
3702
+ top = r.top - offset;
3703
+ if (top < 8) {
3704
+ effPlacement = "bottom";
3705
+ top = r.bottom + offset;
3706
+ }
3707
+ }
3708
+ } else {
3709
+ top = r.top + r.height / 2;
3710
+ if (placement === "right") {
3711
+ left = r.right + offset;
3712
+ } else {
3713
+ left = r.left - offset;
3714
+ }
3715
+ }
3716
+ setPos({ left: Math.round(left), top: Math.round(top), placement: effPlacement });
3717
+ }, [placement, offset]);
3718
+ useEffect(() => {
3719
+ if (!isOpen) return;
3720
+ updatePosition();
3721
+ window.addEventListener("scroll", updatePosition, true);
3722
+ window.addEventListener("resize", updatePosition);
3723
+ return () => {
3724
+ window.removeEventListener("scroll", updatePosition, true);
3725
+ window.removeEventListener("resize", updatePosition);
3726
+ };
3727
+ }, [isOpen, updatePosition]);
3728
+ useEffect(() => {
3729
+ if (!isOpen || !closeOnClickOutside) return;
3730
+ const handleClick = (e) => {
3731
+ const target = e.target;
3732
+ if (anchorRef.current?.contains(target) || popoverRef.current?.contains(target)) {
3733
+ return;
3734
+ }
3735
+ close();
3736
+ };
3737
+ document.addEventListener("mousedown", handleClick);
3738
+ return () => document.removeEventListener("mousedown", handleClick);
3739
+ }, [isOpen, closeOnClickOutside, close]);
3740
+ useEffect(() => {
3741
+ if (!isOpen || !closeOnEsc) return;
3742
+ const handleKey = (e) => {
3743
+ if (e.key === "Escape") {
3744
+ e.preventDefault();
3745
+ close();
3746
+ anchorRef.current?.focus();
3747
+ }
3748
+ };
3749
+ document.addEventListener("keydown", handleKey);
3750
+ return () => document.removeEventListener("keydown", handleKey);
3751
+ }, [isOpen, closeOnEsc, close]);
3752
+ if (!isValidElement(children)) return children;
3753
+ const child = cloneElement(children, {
3754
+ ref: (node) => {
3755
+ anchorRef.current = node;
3756
+ const childProps = children.props;
3757
+ const prevRef = childProps.ref;
3758
+ if (typeof prevRef === "function") prevRef(node);
3759
+ else if (prevRef && typeof prevRef === "object") prevRef.current = node;
3760
+ },
3761
+ onClick: (e) => {
3762
+ const childProps = children.props;
3763
+ if (typeof childProps.onClick === "function") childProps.onClick(e);
3764
+ toggle();
3765
+ },
3766
+ "aria-expanded": isOpen,
3767
+ "aria-haspopup": "dialog",
3768
+ "aria-controls": isOpen ? popoverId : void 0
3769
+ });
3770
+ const getTransform = () => {
3771
+ if (!pos) return "translate(-9999px, -9999px)";
3772
+ switch (pos.placement) {
3773
+ case "top":
3774
+ return "translate(-50%, -100%)";
3775
+ case "bottom":
3776
+ return "translate(-50%, 0%)";
3777
+ case "left":
3778
+ return "translate(-100%, -50%)";
3779
+ case "right":
3780
+ return "translate(0%, -50%)";
3781
+ }
3782
+ };
3783
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3784
+ child,
3785
+ isOpen && typeof document !== "undefined" ? createPortal(
3786
+ /* @__PURE__ */ jsx(
3787
+ "div",
3788
+ {
3789
+ ref: popoverRef,
3790
+ id: popoverId,
3791
+ role: "dialog",
3792
+ className: cn(
3793
+ "fixed z-[9999] rounded-xl shadow-xl ring-1 ring-white/10 bg-surface-50 backdrop-blur-md p-4",
3794
+ className
3795
+ ),
3796
+ style: {
3797
+ left: pos?.left ?? 0,
3798
+ top: pos?.top ?? 0,
3799
+ transform: getTransform()
3800
+ },
3801
+ children: content
3802
+ }
3803
+ ),
3804
+ document.body
3805
+ ) : null
3806
+ ] });
3807
+ }
3808
+ var scrollLockCount2 = 0;
3809
+ var savedOverflow2 = "";
3810
+ function lockScroll2() {
3811
+ if (typeof document === "undefined") return;
3812
+ if (scrollLockCount2 === 0) {
3813
+ savedOverflow2 = document.body.style.overflow;
3814
+ document.body.style.overflow = "hidden";
3815
+ }
3816
+ scrollLockCount2++;
3817
+ }
3818
+ function unlockScroll2() {
3819
+ if (typeof document === "undefined") return;
3820
+ scrollLockCount2 = Math.max(0, scrollLockCount2 - 1);
3821
+ if (scrollLockCount2 === 0) {
3822
+ document.body.style.overflow = savedOverflow2;
3823
+ }
3824
+ }
3825
+ var sizeClass8 = {
3826
+ left: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
3827
+ right: { sm: "w-64", md: "w-80", lg: "w-96", full: "w-screen" },
3828
+ bottom: { sm: "h-1/4", md: "h-1/3", lg: "h-1/2", full: "h-screen" }
3829
+ };
3830
+ var positionClass2 = {
3831
+ left: "inset-y-0 left-0",
3832
+ right: "inset-y-0 right-0",
3833
+ bottom: "inset-x-0 bottom-0"
3834
+ };
3835
+ var slideIn = {
3836
+ left: "translate-x-0",
3837
+ right: "translate-x-0",
3838
+ bottom: "translate-y-0"
3839
+ };
3840
+ var slideOut = {
3841
+ left: "-translate-x-full",
3842
+ right: "translate-x-full",
3843
+ bottom: "translate-y-full"
3844
+ };
3845
+ function Drawer({
3846
+ isOpen,
3847
+ onClose,
3848
+ children,
3849
+ side = "right",
3850
+ size = "md",
3851
+ ariaLabel,
3852
+ closeOnBackdrop = true,
3853
+ closeOnEsc = true,
3854
+ className
3855
+ }) {
3856
+ const panelRef = useRef(null);
3857
+ const lastActiveRef = useRef(null);
3858
+ useEffect(() => {
3859
+ if (!isOpen) return;
3860
+ lastActiveRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
3861
+ const raf = requestAnimationFrame(() => {
3862
+ const el = panelRef.current;
3863
+ if (!el) return;
3864
+ const focusables = getFocusableElements(el);
3865
+ focusSafely(focusables[0] ?? el);
3866
+ });
3867
+ return () => {
3868
+ cancelAnimationFrame(raf);
3869
+ const lastActive = lastActiveRef.current;
3870
+ lastActiveRef.current = null;
3871
+ if (lastActive?.isConnected) focusSafely(lastActive);
3872
+ };
3873
+ }, [isOpen]);
3874
+ useEffect(() => {
3875
+ if (!isOpen) return;
3876
+ lockScroll2();
3877
+ return () => unlockScroll2();
3878
+ }, [isOpen]);
3879
+ if (!isOpen) return null;
3880
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", role: "presentation", children: [
3881
+ /* @__PURE__ */ jsx(
3882
+ "div",
3883
+ {
3884
+ className: "absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity duration-200",
3885
+ "aria-hidden": "true",
3886
+ onClick: closeOnBackdrop ? onClose : void 0
3887
+ }
3888
+ ),
3889
+ /* @__PURE__ */ jsx(
3890
+ "div",
3891
+ {
3892
+ ref: panelRef,
3893
+ role: "dialog",
3894
+ "aria-modal": "true",
3895
+ "aria-label": ariaLabel,
3896
+ tabIndex: -1,
3897
+ className: cn(
3898
+ "fixed flex flex-col bg-surface-50 shadow-2xl ring-1 ring-white/10 transition-transform duration-300 ease-out focus:outline-none",
3899
+ positionClass2[side],
3900
+ sizeClass8[side][size],
3901
+ isOpen ? slideIn[side] : slideOut[side],
3902
+ className
3903
+ ),
3904
+ onKeyDownCapture: (e) => {
3905
+ if (closeOnEsc && e.key === "Escape") {
3906
+ e.preventDefault();
3907
+ e.stopPropagation();
3908
+ onClose();
3909
+ return;
3910
+ }
3911
+ if (e.key !== "Tab") return;
3912
+ const el = panelRef.current;
3913
+ if (!el) return;
3914
+ const focusables = getFocusableElements(el);
3915
+ if (focusables.length === 0) {
3916
+ e.preventDefault();
3917
+ focusSafely(el);
3918
+ return;
3919
+ }
3920
+ const active = document.activeElement;
3921
+ const first = focusables[0];
3922
+ const last = focusables[focusables.length - 1];
3923
+ if (e.shiftKey) {
3924
+ if (active === first) {
3925
+ e.preventDefault();
3926
+ focusSafely(last);
3927
+ }
3928
+ } else {
3929
+ if (active === last) {
3930
+ e.preventDefault();
3931
+ focusSafely(first);
3932
+ }
3933
+ }
3934
+ },
3935
+ children
3936
+ }
3937
+ )
3938
+ ] });
3939
+ }
3940
+ var defaultFilter = (opt, q) => opt.label.toLowerCase().includes(q.toLowerCase());
3941
+ var Combobox = forwardRef(function Combobox2({
3942
+ options,
3943
+ value,
3944
+ onChange,
3945
+ placeholder,
3946
+ label,
3947
+ error,
3948
+ allowCustom = false,
3949
+ filterFn = defaultFilter,
3950
+ emptyContent,
3951
+ disabled,
3952
+ className,
3953
+ id: externalId
3954
+ }, ref) {
3955
+ const generatedId = useId();
3956
+ const inputId = externalId || generatedId;
3957
+ const listboxId = `${inputId}-listbox`;
3958
+ const [query, setQuery] = useState("");
3959
+ const [open, setOpen] = useState(false);
3960
+ const [activeIndex, setActiveIndex] = useState(-1);
3961
+ const inputRef = useRef(null);
3962
+ const listRef = useRef(null);
3963
+ const containerRef = useRef(null);
3964
+ const [listPos, setListPos] = useState(null);
3965
+ useEffect(() => {
3966
+ if (value !== void 0) {
3967
+ const opt = options.find((o) => o.value === value);
3968
+ setQuery(opt ? opt.label : value);
3969
+ }
3970
+ }, [value, options]);
3971
+ const filtered = useMemo(() => {
3972
+ if (!query) return options;
3973
+ return options.filter((opt) => filterFn(opt, query));
3974
+ }, [options, query, filterFn]);
3975
+ const enabledFiltered = useMemo(
3976
+ () => filtered.filter((o) => !o.disabled),
3977
+ [filtered]
3978
+ );
3979
+ const updatePosition = useCallback(() => {
3980
+ const el = containerRef.current;
3981
+ if (!el) return;
3982
+ const r = el.getBoundingClientRect();
3983
+ setListPos({
3984
+ left: r.left,
3985
+ top: r.bottom + 4,
3986
+ width: r.width
3987
+ });
3988
+ }, []);
3989
+ useEffect(() => {
3990
+ if (!open) return;
3991
+ updatePosition();
3992
+ window.addEventListener("scroll", updatePosition, true);
3993
+ window.addEventListener("resize", updatePosition);
3994
+ return () => {
3995
+ window.removeEventListener("scroll", updatePosition, true);
3996
+ window.removeEventListener("resize", updatePosition);
3997
+ };
3998
+ }, [open, updatePosition]);
3999
+ useEffect(() => {
4000
+ if (!open) return;
4001
+ const handleClick = (e) => {
4002
+ const target = e.target;
4003
+ if (containerRef.current?.contains(target) || listRef.current?.contains(target)) return;
4004
+ setOpen(false);
4005
+ };
4006
+ document.addEventListener("mousedown", handleClick);
4007
+ return () => document.removeEventListener("mousedown", handleClick);
4008
+ }, [open]);
4009
+ const selectOption = useCallback(
4010
+ (opt) => {
4011
+ setQuery(opt.label);
4012
+ onChange?.(opt.value);
4013
+ setOpen(false);
4014
+ setActiveIndex(-1);
4015
+ inputRef.current?.focus();
4016
+ },
4017
+ [onChange]
4018
+ );
4019
+ const handleInputChange = (e) => {
4020
+ const v = e.target.value;
4021
+ setQuery(v);
4022
+ setOpen(true);
4023
+ setActiveIndex(-1);
4024
+ if (allowCustom) onChange?.(v);
4025
+ };
4026
+ const handleKeyDown = (e) => {
4027
+ if (e.key === "ArrowDown") {
4028
+ e.preventDefault();
4029
+ if (!open) {
4030
+ setOpen(true);
4031
+ return;
4032
+ }
4033
+ setActiveIndex((i) => {
4034
+ const next = i + 1;
4035
+ return next >= enabledFiltered.length ? 0 : next;
4036
+ });
4037
+ } else if (e.key === "ArrowUp") {
4038
+ e.preventDefault();
4039
+ if (!open) {
4040
+ setOpen(true);
4041
+ return;
4042
+ }
4043
+ setActiveIndex((i) => {
4044
+ const prev = i - 1;
4045
+ return prev < 0 ? enabledFiltered.length - 1 : prev;
4046
+ });
4047
+ } else if (e.key === "Enter") {
4048
+ e.preventDefault();
4049
+ if (open && activeIndex >= 0 && enabledFiltered[activeIndex]) {
4050
+ selectOption(enabledFiltered[activeIndex]);
4051
+ }
4052
+ } else if (e.key === "Escape") {
4053
+ if (open) {
4054
+ e.preventDefault();
4055
+ setOpen(false);
4056
+ setActiveIndex(-1);
4057
+ }
4058
+ }
4059
+ };
4060
+ useEffect(() => {
4061
+ if (activeIndex < 0 || !listRef.current) return;
4062
+ const items = listRef.current.querySelectorAll('[role="option"]:not([aria-disabled="true"])');
4063
+ const item = items[activeIndex];
4064
+ if (item && typeof item.scrollIntoView === "function") {
4065
+ item.scrollIntoView({ block: "nearest" });
4066
+ }
4067
+ }, [activeIndex]);
4068
+ const activeOptionId = activeIndex >= 0 ? `${inputId}-opt-${activeIndex}` : void 0;
4069
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
4070
+ label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "block text-sm text-white/70 mb-1.5", children: label }),
4071
+ /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
4072
+ /* @__PURE__ */ jsx(
4073
+ "input",
4074
+ {
4075
+ ref: (node) => {
4076
+ inputRef.current = node;
4077
+ if (typeof ref === "function") ref(node);
4078
+ else if (ref) ref.current = node;
4079
+ },
4080
+ id: inputId,
4081
+ type: "text",
4082
+ role: "combobox",
4083
+ "aria-expanded": open,
4084
+ "aria-controls": listboxId,
4085
+ "aria-activedescendant": activeOptionId,
4086
+ "aria-autocomplete": "list",
4087
+ autoComplete: "off",
4088
+ disabled,
4089
+ placeholder,
4090
+ value: query,
4091
+ onChange: handleInputChange,
4092
+ onFocus: () => setOpen(true),
4093
+ onKeyDown: handleKeyDown,
4094
+ className: cn(
4095
+ "w-full rounded-xl px-3 py-2.5 text-sm bg-white/10 text-white shadow-sm outline-none placeholder-white/30 focus-visible:ring-2 focus-visible:ring-primary/40 transition-shadow",
4096
+ error && "ring-1 ring-rose-500/50",
4097
+ disabled && "opacity-50 cursor-not-allowed"
4098
+ )
4099
+ }
4100
+ ),
4101
+ /* @__PURE__ */ jsx(
4102
+ "button",
4103
+ {
4104
+ type: "button",
4105
+ tabIndex: -1,
4106
+ "aria-hidden": "true",
4107
+ onClick: () => {
4108
+ if (!disabled) {
4109
+ setOpen(!open);
4110
+ inputRef.current?.focus();
4111
+ }
4112
+ },
4113
+ className: "absolute right-2 top-1/2 -translate-y-1/2 text-white/40 hover:text-white/60 p-1",
4114
+ children: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsx("path", { d: "M4 6l4 4 4-4" }) })
4115
+ }
4116
+ )
4117
+ ] }),
4118
+ error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-rose-400", children: error }),
4119
+ open && typeof document !== "undefined" ? createPortal(
4120
+ /* @__PURE__ */ jsx(
4121
+ "ul",
4122
+ {
4123
+ ref: listRef,
4124
+ id: listboxId,
4125
+ role: "listbox",
4126
+ className: "fixed z-[9999] max-h-60 overflow-auto rounded-xl bg-surface-50 shadow-xl ring-1 ring-white/10 py-1 backdrop-blur-md",
4127
+ style: {
4128
+ left: listPos?.left ?? 0,
4129
+ top: listPos?.top ?? 0,
4130
+ width: listPos?.width ?? 0
4131
+ },
4132
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-white/40", children: emptyContent ?? "No results" }) : filtered.map((opt) => {
4133
+ const enabledIndex = enabledFiltered.indexOf(opt);
4134
+ const isActive = enabledIndex === activeIndex;
4135
+ return /* @__PURE__ */ jsx(
4136
+ "li",
4137
+ {
4138
+ id: isActive ? activeOptionId : void 0,
4139
+ role: "option",
4140
+ "aria-selected": opt.value === value,
4141
+ "aria-disabled": opt.disabled || void 0,
4142
+ className: cn(
4143
+ "px-3 py-2 text-sm cursor-pointer transition-colors",
4144
+ isActive && "bg-white/10",
4145
+ opt.value === value && "text-primary-light",
4146
+ opt.disabled && "opacity-40 cursor-not-allowed",
4147
+ !opt.disabled && !isActive && "text-white hover:bg-white/5"
4148
+ ),
4149
+ onMouseDown: (e) => {
4150
+ e.preventDefault();
4151
+ if (!opt.disabled) selectOption(opt);
4152
+ },
4153
+ onMouseEnter: () => {
4154
+ if (!opt.disabled) setActiveIndex(enabledIndex);
4155
+ },
4156
+ children: opt.label
4157
+ },
4158
+ opt.value
4159
+ );
4160
+ })
4161
+ }
4162
+ ),
4163
+ document.body
4164
+ ) : null
4165
+ ] });
4166
+ });
4167
+ var presetStyles = {
4168
+ fade: {
4169
+ enter: "opacity-100",
4170
+ exit: "opacity-0"
4171
+ },
4172
+ "fade-up": {
4173
+ enter: "opacity-100 translate-y-0",
4174
+ exit: "opacity-0 translate-y-2"
4175
+ },
4176
+ "fade-down": {
4177
+ enter: "opacity-100 translate-y-0",
4178
+ exit: "opacity-0 -translate-y-2"
4179
+ },
4180
+ scale: {
4181
+ enter: "opacity-100 scale-100",
4182
+ exit: "opacity-0 scale-95"
4183
+ },
4184
+ "slide-right": {
4185
+ enter: "translate-x-0",
4186
+ exit: "translate-x-full"
4187
+ },
4188
+ "slide-left": {
4189
+ enter: "translate-x-0",
4190
+ exit: "-translate-x-full"
4191
+ }
4192
+ };
4193
+ function Transition({
4194
+ show,
4195
+ children,
4196
+ preset = "fade",
4197
+ duration = 200,
4198
+ unmountOnHide = true,
4199
+ className
4200
+ }) {
4201
+ const [mounted, setMounted] = useState(show);
4202
+ const [entering, setEntering] = useState(false);
4203
+ const timerRef = useRef(null);
4204
+ useEffect(() => {
4205
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4206
+ if (show) {
4207
+ setMounted(true);
4208
+ requestAnimationFrame(() => {
4209
+ requestAnimationFrame(() => setEntering(true));
4210
+ });
4211
+ } else {
4212
+ setEntering(false);
4213
+ if (unmountOnHide) {
4214
+ timerRef.current = setTimeout(() => {
4215
+ setMounted(false);
4216
+ timerRef.current = null;
4217
+ }, duration);
4218
+ }
4219
+ }
4220
+ return () => {
4221
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
4222
+ };
4223
+ }, [show, duration, unmountOnHide]);
4224
+ if (!mounted) return null;
4225
+ const styles = presetStyles[preset];
4226
+ return /* @__PURE__ */ jsx(
4227
+ "div",
4228
+ {
4229
+ className: cn(
4230
+ "transition-all",
4231
+ entering ? styles.enter : styles.exit,
4232
+ className
4233
+ ),
4234
+ style: { transitionDuration: `${duration}ms` },
4235
+ "aria-hidden": !show,
4236
+ children
4237
+ }
4238
+ );
4239
+ }
4240
+ function VisuallyHidden({ children, as: Tag = "span", style, ...props }) {
4241
+ return /* @__PURE__ */ jsx(
4242
+ Tag,
4243
+ {
4244
+ ...props,
4245
+ style: {
4246
+ position: "absolute",
4247
+ width: 1,
4248
+ height: 1,
4249
+ padding: 0,
4250
+ margin: -1,
4251
+ overflow: "hidden",
4252
+ clip: "rect(0,0,0,0)",
4253
+ whiteSpace: "nowrap",
4254
+ borderWidth: 0,
4255
+ ...style
4256
+ },
4257
+ children
4258
+ }
4259
+ );
4260
+ }
3473
4261
 
3474
- export { ActiveFilterPills, Alert, Avatar, Badge, Button, Card, Checkbox, CollapsibleSection, ColorInput, ConfirmDialog, CooldownRing, CopyField, DashboardLayout, Divider, DotIndicator, DropZone, Dropdown, DropdownItem, DropdownMenu, DropdownSeparator, DropdownTrigger, EmptyState, FormField, IconButton, Input, Modal, MutationOverlay, Navbar, NotificationBell, PageShell, Pagination, Pill, ProgressBar, ProgressButton, RadioGroup, RadioItem, SearchInput, SectionCard, Select, Sidebar, Skeleton, Slider, Spinner, StageProgress, StatCard, Stepper, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TagInput, Textarea, ToastProvider, Toggle, Tooltip, cn, colors, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useSharedNow, useToast };
4262
+ export { ActiveFilterPills, Alert, Avatar, Badge, Breadcrumbs, Button, Card, Checkbox, CollapsibleSection, ColorInput, Combobox, ConfirmDialog, CooldownRing, CopyField, DashboardLayout, Divider, DotIndicator, Drawer, DropZone, Dropdown, DropdownItem, DropdownMenu, DropdownSeparator, DropdownTrigger, EmptyState, FormField, Heading, IconButton, Input, Modal, MutationOverlay, Navbar, NotificationBell, PageShell, Pagination, Pill, Popover, ProgressBar, ProgressButton, RadioGroup, RadioItem, ScrollArea, SearchInput, SectionCard, Select, Sidebar, Skeleton, Slider, Spinner, Stack, StageProgress, StatCard, Stepper, Tab, TabList, TabPanel, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TagInput, Text, Textarea, ToastProvider, Toggle, Tooltip, Transition, VisuallyHidden, cn, colors, focusSafely, getFocusableElements, useClipboard, useDebounce, useDisclosure, useHotkeys, useIntersectionObserver, useMediaQuery, useSharedNow, useToast };