@memelabui/ui 0.5.1 → 0.6.1

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