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