@enonic/ui 0.12.0 → 0.12.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.
@@ -1,6 +1,8 @@
1
1
  import { options, Fragment } from "preact";
2
- import { forwardRef, createContext, useId, useContext, createElement, useState, isValidElement, Children, useRef, useEffect, useCallback, createPortal, useMemo } from "react";
3
- import { Root } from "@radix-ui/react-slot";
2
+ import { createContext, useContext, useId, forwardRef, useState, useCallback, useMemo, useEffect, createElement, useRef, isValidElement, Children, createPortal as createPortal$1 } from "react";
3
+ import { Slot, Root } from "@radix-ui/react-slot";
4
+ import { FocusTrap } from "focus-trap-react";
5
+ import { createPortal } from "react-dom";
4
6
  import { useState as useState$1 } from "preact/hooks";
5
7
  var f = 0;
6
8
  function u(e, t, n, o, i, u2) {
@@ -11,6 +13,56 @@ function u(e, t, n, o, i, u2) {
11
13
  if ("function" == typeof e && (a = e.defaultProps)) for (c in a) void 0 === p[c] && (p[c] = a[c]);
12
14
  return options.vnode && options.vnode(l), l;
13
15
  }
16
+ const AvatarContext = createContext(null);
17
+ const AvatarProvider = ({ value, children }) => {
18
+ return /* @__PURE__ */ u(AvatarContext.Provider, { value, children });
19
+ };
20
+ AvatarProvider.displayName = "AvatarProvider";
21
+ const useAvatar = () => {
22
+ const ctx = useContext(AvatarContext);
23
+ if (!ctx) {
24
+ throw new Error("useAvatar must be used within an Avatar");
25
+ }
26
+ return ctx;
27
+ };
28
+ const DialogContext = createContext(void 0);
29
+ const DialogProvider = ({ value, children }) => {
30
+ return /* @__PURE__ */ u(DialogContext.Provider, { value, children });
31
+ };
32
+ DialogProvider.displayName = "DialogProvider";
33
+ const useDialog = () => {
34
+ const context = useContext(DialogContext);
35
+ if (!context) {
36
+ throw new Error("useDialog must be used within a DialogProvider");
37
+ }
38
+ return context;
39
+ };
40
+ const IdContext = createContext(void 0);
41
+ const IdProvider = ({ children, prefix }) => {
42
+ return /* @__PURE__ */ u(IdContext.Provider, { value: { prefix }, children });
43
+ };
44
+ IdProvider.displayName = "IdProvider";
45
+ const usePrefixedId = (providedId) => {
46
+ const baseId = useId();
47
+ const context = useContext(IdContext);
48
+ if (providedId) {
49
+ return providedId;
50
+ }
51
+ const id = context?.prefix ? `${context.prefix}-${baseId}` : baseId;
52
+ return id;
53
+ };
54
+ const ListboxContext = createContext(null);
55
+ const ListboxProvider = ({ value, children }) => {
56
+ return /* @__PURE__ */ u(ListboxContext.Provider, { value, children });
57
+ };
58
+ ListboxProvider.displayName = "ListboxProvider";
59
+ const useListbox = () => {
60
+ const ctx = useContext(ListboxContext);
61
+ if (!ctx) {
62
+ throw new Error("useListbox must be used within a Listbox");
63
+ }
64
+ return ctx;
65
+ };
14
66
  function r(e) {
15
67
  var t, f2, n = "";
16
68
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -3024,6 +3076,125 @@ const cva = (base, config) => (props) => {
3024
3076
  }, []);
3025
3077
  return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
3026
3078
  };
3079
+ const avatarVariants = cva("relative flex shrink-0 overflow-hidden", {
3080
+ variants: {
3081
+ size: {
3082
+ sm: "w-6 h-6 text-xs",
3083
+ md: "w-8 h-8 text-sm",
3084
+ lg: "w-12 h-12 text-base"
3085
+ },
3086
+ shape: {
3087
+ circle: "rounded-full",
3088
+ square: "rounded-md"
3089
+ }
3090
+ },
3091
+ defaultVariants: {
3092
+ size: "md",
3093
+ shape: "circle"
3094
+ }
3095
+ });
3096
+ const AvatarRoot = forwardRef(
3097
+ ({ children, className, size, shape, ...props }, ref) => {
3098
+ const [imageLoadingStatus, setImageLoadingStatus] = useState("idle");
3099
+ const handleImageLoadingStatusChange = useCallback((status) => {
3100
+ setImageLoadingStatus(status);
3101
+ }, []);
3102
+ const contextValue = useMemo(
3103
+ () => ({
3104
+ imageLoadingStatus,
3105
+ onImageLoadingStatusChange: handleImageLoadingStatusChange
3106
+ }),
3107
+ [imageLoadingStatus, handleImageLoadingStatusChange]
3108
+ );
3109
+ return /* @__PURE__ */ u(AvatarProvider, { value: contextValue, children: /* @__PURE__ */ u("span", { ref, className: cn(avatarVariants({ size, shape }), className), ...props, children }) });
3110
+ }
3111
+ );
3112
+ AvatarRoot.displayName = "AvatarRoot";
3113
+ const AvatarImage = forwardRef(
3114
+ ({ src, alt, className, onLoadingStatusChange, ...props }, ref) => {
3115
+ const { imageLoadingStatus, onImageLoadingStatusChange } = useAvatar();
3116
+ const [hasError, setHasError] = useState(false);
3117
+ useEffect(() => {
3118
+ setHasError(false);
3119
+ if (!src) {
3120
+ return;
3121
+ }
3122
+ onImageLoadingStatusChange("loading");
3123
+ onLoadingStatusChange?.("loading");
3124
+ const img = new Image();
3125
+ const handleLoad = () => {
3126
+ onImageLoadingStatusChange("loaded");
3127
+ onLoadingStatusChange?.("loaded");
3128
+ };
3129
+ const handleError = () => {
3130
+ setHasError(true);
3131
+ onImageLoadingStatusChange("error");
3132
+ onLoadingStatusChange?.("error");
3133
+ };
3134
+ img.addEventListener("load", handleLoad);
3135
+ img.addEventListener("error", handleError);
3136
+ img.src = src;
3137
+ return () => {
3138
+ img.removeEventListener("load", handleLoad);
3139
+ img.removeEventListener("error", handleError);
3140
+ };
3141
+ }, [src, onImageLoadingStatusChange, onLoadingStatusChange]);
3142
+ if (!src || hasError || imageLoadingStatus === "error") {
3143
+ return null;
3144
+ }
3145
+ return /* @__PURE__ */ u(
3146
+ "img",
3147
+ {
3148
+ ref,
3149
+ src,
3150
+ alt,
3151
+ className: cn("aspect-square h-full w-full object-cover", className),
3152
+ ...props
3153
+ }
3154
+ );
3155
+ }
3156
+ );
3157
+ AvatarImage.displayName = "AvatarImage";
3158
+ const AvatarFallback = forwardRef(
3159
+ ({ children, className, delayMs = 0, ...props }, ref) => {
3160
+ const { imageLoadingStatus } = useAvatar();
3161
+ const [canRender, setCanRender] = useState(delayMs === 0);
3162
+ useEffect(() => {
3163
+ if (delayMs === 0) {
3164
+ return;
3165
+ }
3166
+ const timerId = setTimeout(() => {
3167
+ setCanRender(true);
3168
+ }, delayMs);
3169
+ return () => {
3170
+ clearTimeout(timerId);
3171
+ };
3172
+ }, [delayMs]);
3173
+ const shouldRender = canRender && (imageLoadingStatus === "idle" || imageLoadingStatus === "error");
3174
+ if (!shouldRender) {
3175
+ return null;
3176
+ }
3177
+ return /* @__PURE__ */ u(
3178
+ "span",
3179
+ {
3180
+ ref,
3181
+ className: cn(
3182
+ "flex h-full w-full items-center justify-center",
3183
+ "bg-surface-secondary text-alt font-medium uppercase cursor-default",
3184
+ className
3185
+ ),
3186
+ ...props,
3187
+ children
3188
+ }
3189
+ );
3190
+ }
3191
+ );
3192
+ AvatarFallback.displayName = "AvatarFallback";
3193
+ const Avatar = Object.assign(AvatarRoot, {
3194
+ Root: AvatarRoot,
3195
+ Image: AvatarImage,
3196
+ Fallback: AvatarFallback
3197
+ });
3027
3198
  const buttonVariants = cva(
3028
3199
  [
3029
3200
  "inline-flex items-center justify-center",
@@ -3101,20 +3272,6 @@ const Button = forwardRef(
3101
3272
  }
3102
3273
  );
3103
3274
  Button.displayName = "Button";
3104
- const IdContext = createContext(void 0);
3105
- const IdProvider = ({ children, prefix }) => {
3106
- return /* @__PURE__ */ u(IdContext.Provider, { value: { prefix }, children });
3107
- };
3108
- IdProvider.displayName = "IdProvider";
3109
- const usePrefixedId = (providedId) => {
3110
- const baseId = useId();
3111
- const context = useContext(IdContext);
3112
- if (providedId) {
3113
- return providedId;
3114
- }
3115
- const id = context?.prefix ? `${context.prefix}-${baseId}` : baseId;
3116
- return id;
3117
- };
3118
3275
  /**
3119
3276
  * @license lucide-react v0.545.0 - ISC
3120
3277
  *
@@ -3487,6 +3644,324 @@ const IconButton = forwardRef(
3487
3644
  }
3488
3645
  );
3489
3646
  IconButton.displayName = "IconButton";
3647
+ const useScrollLock = (lock, element) => {
3648
+ useEffect(() => {
3649
+ if (!lock) {
3650
+ return;
3651
+ }
3652
+ const target = element ?? document.body;
3653
+ const originalOverflow = target.style.overflow;
3654
+ target.style.overflow = "hidden";
3655
+ return () => {
3656
+ target.style.overflow = originalOverflow;
3657
+ };
3658
+ }, [lock, element]);
3659
+ };
3660
+ const DialogRoot = ({
3661
+ open: controlledOpen,
3662
+ defaultOpen = false,
3663
+ onOpenChange,
3664
+ children
3665
+ }) => {
3666
+ const isControlled = controlledOpen !== void 0;
3667
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
3668
+ const open = isControlled ? controlledOpen : uncontrolledOpen;
3669
+ const setOpen = useCallback(
3670
+ (next) => {
3671
+ if (!isControlled) {
3672
+ setUncontrolledOpen(next);
3673
+ }
3674
+ onOpenChange?.(next);
3675
+ },
3676
+ [isControlled, onOpenChange]
3677
+ );
3678
+ const value = useMemo(() => ({ open, setOpen }), [open, setOpen]);
3679
+ return /* @__PURE__ */ u(DialogProvider, { value, children });
3680
+ };
3681
+ DialogRoot.displayName = "Dialog.Root";
3682
+ const DialogTrigger = forwardRef(
3683
+ ({ children, onClick, ...props }, ref) => {
3684
+ const { setOpen } = useDialog();
3685
+ const handleClick = (e) => {
3686
+ onClick?.(e);
3687
+ setOpen(true);
3688
+ };
3689
+ return /* @__PURE__ */ u("button", { ref, type: "button", onClick: handleClick, ...props, children });
3690
+ }
3691
+ );
3692
+ DialogTrigger.displayName = "Dialog.Trigger";
3693
+ const DialogPortal = ({ children, container, forceMount }) => {
3694
+ const { open } = useDialog();
3695
+ const [mounted, setMounted] = useState(false);
3696
+ useEffect(() => {
3697
+ setMounted(true);
3698
+ }, []);
3699
+ if (!mounted || !forceMount && !open) {
3700
+ return null;
3701
+ }
3702
+ return createPortal(children, container ?? document.body);
3703
+ };
3704
+ DialogPortal.displayName = "Dialog.Portal";
3705
+ const DialogOverlay = forwardRef(
3706
+ ({ className, forceMount, ...props }, ref) => {
3707
+ const { open } = useDialog();
3708
+ if (!forceMount && !open) {
3709
+ return null;
3710
+ }
3711
+ return /* @__PURE__ */ u(
3712
+ "div",
3713
+ {
3714
+ ref,
3715
+ "data-state": open ? "open" : "closed",
3716
+ className: cn(
3717
+ "fixed inset-0 z-30 bg-overlay backdrop-blur-xs",
3718
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
3719
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
3720
+ className
3721
+ ),
3722
+ ...props
3723
+ }
3724
+ );
3725
+ }
3726
+ );
3727
+ DialogOverlay.displayName = "Dialog.Overlay";
3728
+ const DialogContent = forwardRef(
3729
+ ({
3730
+ children,
3731
+ className,
3732
+ forceMount,
3733
+ onEscapeKeyDown,
3734
+ onPointerDownOutside,
3735
+ onInteractOutside,
3736
+ onOpenAutoFocus,
3737
+ onCloseAutoFocus,
3738
+ ...props
3739
+ }, ref) => {
3740
+ const { open, setOpen } = useDialog();
3741
+ const contentRef = useRef(null);
3742
+ const titleId = useId();
3743
+ const descriptionId = useId();
3744
+ useEffect(() => {
3745
+ if (typeof ref === "function") {
3746
+ ref(contentRef.current);
3747
+ } else if (ref) {
3748
+ ref.current = contentRef.current;
3749
+ }
3750
+ }, [ref]);
3751
+ useScrollLock(open);
3752
+ const handleEscapeKey = useCallback(
3753
+ (e) => {
3754
+ if (e.key === "Escape") {
3755
+ onEscapeKeyDown?.(e);
3756
+ if (!e.defaultPrevented) {
3757
+ setOpen(false);
3758
+ }
3759
+ }
3760
+ },
3761
+ [onEscapeKeyDown, setOpen]
3762
+ );
3763
+ useEffect(() => {
3764
+ if (!open) {
3765
+ return;
3766
+ }
3767
+ document.addEventListener("keydown", handleEscapeKey);
3768
+ return () => document.removeEventListener("keydown", handleEscapeKey);
3769
+ }, [open, handleEscapeKey]);
3770
+ const handlePointerDownOutside = useCallback(
3771
+ (e) => {
3772
+ const target = e.target;
3773
+ if (contentRef.current && !contentRef.current.contains(target)) {
3774
+ onPointerDownOutside?.(e);
3775
+ onInteractOutside?.(e);
3776
+ if (!e.defaultPrevented) {
3777
+ setOpen(false);
3778
+ }
3779
+ }
3780
+ },
3781
+ [onPointerDownOutside, onInteractOutside, setOpen]
3782
+ );
3783
+ useEffect(() => {
3784
+ if (!open) {
3785
+ return;
3786
+ }
3787
+ document.addEventListener("pointerdown", handlePointerDownOutside);
3788
+ return () => document.removeEventListener("pointerdown", handlePointerDownOutside);
3789
+ }, [open, handlePointerDownOutside]);
3790
+ if (!forceMount && !open) {
3791
+ return null;
3792
+ }
3793
+ return /* @__PURE__ */ u(
3794
+ FocusTrap,
3795
+ {
3796
+ active: open,
3797
+ focusTrapOptions: {
3798
+ initialFocus: false,
3799
+ fallbackFocus: () => contentRef.current ?? document.body,
3800
+ escapeDeactivates: false,
3801
+ clickOutsideDeactivates: false,
3802
+ returnFocusOnDeactivate: true,
3803
+ allowOutsideClick: true,
3804
+ preventScroll: false,
3805
+ onActivate: () => {
3806
+ requestAnimationFrame(() => {
3807
+ const event = new Event("openautofocus");
3808
+ onOpenAutoFocus?.(event);
3809
+ if (!event.defaultPrevented && contentRef.current) {
3810
+ contentRef.current.focus();
3811
+ }
3812
+ });
3813
+ },
3814
+ onDeactivate: () => {
3815
+ requestAnimationFrame(() => {
3816
+ const event = new Event("closeautofocus");
3817
+ onCloseAutoFocus?.(event);
3818
+ });
3819
+ }
3820
+ },
3821
+ children: /* @__PURE__ */ u("div", { className: "fixed inset-0 z-40 flex items-center justify-center p-4", children: /* @__PURE__ */ u(
3822
+ "div",
3823
+ {
3824
+ ref: contentRef,
3825
+ role: "dialog",
3826
+ "aria-modal": "true",
3827
+ "aria-labelledby": titleId,
3828
+ "aria-describedby": descriptionId,
3829
+ "data-state": open ? "open" : "closed",
3830
+ tabIndex: -1,
3831
+ className: cn(
3832
+ "relative rounded-lg shadow-xl bg-surface-neutral",
3833
+ "flex flex-col max-w-lg w-full max-h-[90vh] gap-10 p-10",
3834
+ "border border-bdr-subtle outline-none overflow-hidden",
3835
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
3836
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
3837
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
3838
+ className
3839
+ ),
3840
+ ...props,
3841
+ children
3842
+ }
3843
+ ) })
3844
+ }
3845
+ );
3846
+ }
3847
+ );
3848
+ DialogContent.displayName = "Dialog.Content";
3849
+ const DialogHeader = forwardRef(
3850
+ ({ className, children, ...props }, ref) => {
3851
+ return /* @__PURE__ */ u("header", { ref, className: cn("relative grid gap-2.5 self-stretch", className), ...props, children });
3852
+ }
3853
+ );
3854
+ DialogHeader.displayName = "Dialog.Header";
3855
+ const DialogClose = forwardRef(
3856
+ ({ children, asChild, onClick, ...props }, ref) => {
3857
+ const { setOpen } = useDialog();
3858
+ const handleClick = useCallback(
3859
+ (e) => {
3860
+ onClick?.(e);
3861
+ if (!e.defaultPrevented) {
3862
+ setOpen(false);
3863
+ }
3864
+ },
3865
+ [onClick, setOpen]
3866
+ );
3867
+ const Comp = asChild ? Slot : "button";
3868
+ return /* @__PURE__ */ u(
3869
+ Comp,
3870
+ {
3871
+ ref,
3872
+ type: asChild ? void 0 : "button",
3873
+ onClick: handleClick,
3874
+ ...props,
3875
+ children
3876
+ }
3877
+ );
3878
+ }
3879
+ );
3880
+ DialogClose.displayName = "Dialog.Close";
3881
+ const DialogTitle = forwardRef(
3882
+ ({ children, className, asChild, ...props }, ref) => {
3883
+ const Comp = asChild ? Slot : "h2";
3884
+ return /* @__PURE__ */ u(
3885
+ Comp,
3886
+ {
3887
+ ref,
3888
+ className: cn("text-2xl font-semibold", className),
3889
+ ...props,
3890
+ children
3891
+ }
3892
+ );
3893
+ }
3894
+ );
3895
+ DialogTitle.displayName = "Dialog.Title";
3896
+ const DialogDescription = forwardRef(
3897
+ ({ children, className, asChild, ...props }, ref) => {
3898
+ const Comp = asChild ? Slot : "p";
3899
+ return /* @__PURE__ */ u(
3900
+ Comp,
3901
+ {
3902
+ ref,
3903
+ className: cn("", className),
3904
+ ...props,
3905
+ children
3906
+ }
3907
+ );
3908
+ }
3909
+ );
3910
+ DialogDescription.displayName = "Dialog.Description";
3911
+ const DialogBody = forwardRef(
3912
+ ({ className, children, ...props }, ref) => {
3913
+ return /* @__PURE__ */ u("div", { ref, className: cn("min-h-0 flex-1 overflow-y-auto", className), ...props, children });
3914
+ }
3915
+ );
3916
+ DialogBody.displayName = "Dialog.Body";
3917
+ const DialogFooter = forwardRef(
3918
+ ({ className, children, ...props }, ref) => {
3919
+ return /* @__PURE__ */ u("footer", { ref, className: cn("flex justify-end gap-2.5", className), ...props, children });
3920
+ }
3921
+ );
3922
+ DialogFooter.displayName = "Dialog.Footer";
3923
+ const DialogDefaultClose = forwardRef((props, ref) => {
3924
+ return /* @__PURE__ */ u(DialogClose, { asChild: true, ref, ...props, children: /* @__PURE__ */ u(
3925
+ IconButton,
3926
+ {
3927
+ "aria-label": "Close",
3928
+ "data-area": "close",
3929
+ icon: X,
3930
+ size: "lg",
3931
+ iconSize: 36,
3932
+ iconStrokeWidth: 1,
3933
+ shape: "round",
3934
+ variant: "filled"
3935
+ }
3936
+ ) });
3937
+ });
3938
+ DialogDefaultClose.displayName = "Dialog.DefaultClose";
3939
+ const DialogDefaultHeader = forwardRef(
3940
+ ({ title, description, withClose, className, children, ...props }, ref) => {
3941
+ return /* @__PURE__ */ u(DialogHeader, { ref, className: cn(withClose && "grid-cols-[minmax(0,1fr)_auto]", className), ...props, children: [
3942
+ /* @__PURE__ */ u(DialogTitle, { className: cn(withClose && "col-start-1 row-start-1 min-w-0"), children: title }),
3943
+ withClose && /* @__PURE__ */ u(DialogDefaultClose, { className: "col-start-2 row-start-1 row-span-2 self-start justify-self-end" }),
3944
+ description && /* @__PURE__ */ u(DialogDescription, { className: cn(withClose && "row-start-2"), children: description }),
3945
+ children
3946
+ ] });
3947
+ }
3948
+ );
3949
+ DialogDefaultHeader.displayName = "Dialog.DefaultHeader";
3950
+ const Dialog = Object.assign(DialogRoot, {
3951
+ Root: DialogRoot,
3952
+ Trigger: DialogTrigger,
3953
+ Portal: DialogPortal,
3954
+ Overlay: DialogOverlay,
3955
+ Content: DialogContent,
3956
+ Header: DialogHeader,
3957
+ Close: DialogClose,
3958
+ Title: DialogTitle,
3959
+ Description: DialogDescription,
3960
+ Body: DialogBody,
3961
+ Footer: DialogFooter,
3962
+ DefaultClose: DialogDefaultClose,
3963
+ DefaultHeader: DialogDefaultHeader
3964
+ });
3490
3965
  const inputContainerVariants = cva(
3491
3966
  [
3492
3967
  "relative flex rounded-sm overflow-hidden",
@@ -3686,49 +4161,420 @@ const ListItem = Object.assign(ListItemRoot, {
3686
4161
  DefaultContent: ListItemDefaultContent,
3687
4162
  Right: ListItemRight
3688
4163
  });
3689
- const SelectableListItem = ({
3690
- className,
3691
- selected,
3692
- children,
3693
- label,
3694
- description,
3695
- metadata,
3696
- icon,
3697
- readOnly,
3698
- checked,
3699
- defaultChecked,
3700
- onCheckedChange,
3701
- ...props
3702
- }) => {
3703
- return /* @__PURE__ */ u(
3704
- ListItem,
3705
- {
3706
- className: cn(!selected && "hover:bg-surface-primary-hover", className),
3707
- selected,
3708
- role: "row",
3709
- "aria-selected": selected,
3710
- ...props,
3711
- children: [
3712
- /* @__PURE__ */ u(ListItem.Left, { children: /* @__PURE__ */ u(
3713
- Checkbox,
3714
- {
3715
- checked,
3716
- defaultChecked,
3717
- onCheckedChange,
3718
- readOnly
3719
- }
3720
- ) }),
3721
- /* @__PURE__ */ u(ListItem.DefaultContent, { label, description, metadata, icon }),
3722
- /* @__PURE__ */ u(ListItem.Right, { children })
3723
- ]
4164
+ function setRef(ref, value) {
4165
+ if (!ref) return;
4166
+ if (typeof ref === "function") {
4167
+ ref(value);
4168
+ } else {
4169
+ try {
4170
+ ref.current = value;
4171
+ } catch {
3724
4172
  }
3725
- );
3726
- };
3727
- SelectableListItem.displayName = "SelectableListItem";
3728
- const oppositeSide = {
3729
- top: "bottom",
3730
- bottom: "top",
3731
- left: "right",
4173
+ }
4174
+ }
4175
+ function useComposedRefs(...refs) {
4176
+ return useCallback((node) => {
4177
+ refs.forEach((ref) => setRef(ref, node));
4178
+ }, refs);
4179
+ }
4180
+ const EMPTY_SELECTION = [];
4181
+ const listboxItemVariants = cva("flex w-full items-center px-4.5 py-1 gap-x-2.5 cursor-pointer", {
4182
+ variants: {
4183
+ selected: {
4184
+ true: "bg-surface-primary-selected text-alt hover:bg-surface-primary-selected-hover",
4185
+ false: "hover:bg-surface-primary-hover"
4186
+ },
4187
+ active: {
4188
+ true: "bg-surface-primary-hover",
4189
+ false: ""
4190
+ }
4191
+ },
4192
+ compoundVariants: [
4193
+ {
4194
+ selected: true,
4195
+ active: true,
4196
+ class: "bg-surface-primary-selected-hover"
4197
+ }
4198
+ ],
4199
+ defaultVariants: {
4200
+ selected: false,
4201
+ active: false
4202
+ }
4203
+ });
4204
+ const ListboxRoot = ({
4205
+ selection: controlledSelection,
4206
+ defaultSelection = EMPTY_SELECTION,
4207
+ onSelectionChange,
4208
+ active: controlledActive,
4209
+ defaultActive,
4210
+ setActive,
4211
+ selectionMode = "single",
4212
+ disabled,
4213
+ children
4214
+ }) => {
4215
+ const listboxId = usePrefixedId();
4216
+ const [uncontrolledSelection, setUncontrolledSelection] = useState(() => new Set(defaultSelection));
4217
+ const isSelectionControlled = controlledSelection !== void 0;
4218
+ const selectionSet = useMemo(
4219
+ () => isSelectionControlled ? new Set(controlledSelection) : uncontrolledSelection,
4220
+ [isSelectionControlled, controlledSelection, uncontrolledSelection]
4221
+ );
4222
+ const [uncontrolledActive, setUncontrolledActive] = useState(defaultActive);
4223
+ const isActiveControlled = controlledActive !== void 0;
4224
+ const active = isActiveControlled ? controlledActive : uncontrolledActive;
4225
+ const updateActive = useCallback(
4226
+ (id) => {
4227
+ if (!isActiveControlled) {
4228
+ setUncontrolledActive(id);
4229
+ }
4230
+ setActive?.(id);
4231
+ },
4232
+ [isActiveControlled, setActive]
4233
+ );
4234
+ const itemsRef = useRef(/* @__PURE__ */ new Set());
4235
+ const registerItem = useCallback((value) => {
4236
+ itemsRef.current.add(value);
4237
+ }, []);
4238
+ const unregisterItem = useCallback((value) => {
4239
+ itemsRef.current.delete(value);
4240
+ }, []);
4241
+ const getItems = useCallback(() => Array.from(itemsRef.current), []);
4242
+ const toggleValue = useCallback(
4243
+ (value) => {
4244
+ const isSelected = selectionSet.has(value);
4245
+ const newSet = new Set(selectionSet);
4246
+ if (selectionMode === "single") {
4247
+ newSet.clear();
4248
+ if (!isSelected) {
4249
+ newSet.add(value);
4250
+ }
4251
+ } else {
4252
+ if (isSelected) {
4253
+ newSet.delete(value);
4254
+ } else {
4255
+ newSet.add(value);
4256
+ }
4257
+ }
4258
+ if (!isSelectionControlled) {
4259
+ setUncontrolledSelection(newSet);
4260
+ }
4261
+ onSelectionChange?.(Array.from(newSet));
4262
+ updateActive(value);
4263
+ },
4264
+ [selectionSet, selectionMode, isSelectionControlled, onSelectionChange, updateActive]
4265
+ );
4266
+ const contextValue = useMemo(
4267
+ () => ({
4268
+ active,
4269
+ selection: selectionSet,
4270
+ selectionMode,
4271
+ disabled,
4272
+ setActive: updateActive,
4273
+ toggleValue,
4274
+ registerItem,
4275
+ unregisterItem,
4276
+ getItems,
4277
+ listboxId
4278
+ }),
4279
+ [
4280
+ active,
4281
+ selectionSet,
4282
+ selectionMode,
4283
+ disabled,
4284
+ updateActive,
4285
+ toggleValue,
4286
+ registerItem,
4287
+ unregisterItem,
4288
+ getItems,
4289
+ listboxId
4290
+ ]
4291
+ );
4292
+ return /* @__PURE__ */ u(ListboxProvider, { value: contextValue, children });
4293
+ };
4294
+ ListboxRoot.displayName = "ListboxRoot";
4295
+ const ListboxContent = forwardRef(
4296
+ ({ className, label, children, ...props }, ref) => {
4297
+ const { active, disabled, getItems, setActive, toggleValue, listboxId } = useListbox();
4298
+ const innerRef = useRef(null);
4299
+ const moveActive = useCallback(
4300
+ (delta) => {
4301
+ const items = getItems();
4302
+ if (!items.length) {
4303
+ return;
4304
+ }
4305
+ const currentIndex = active ? items.indexOf(active) : -1;
4306
+ const newIndex = Math.max(0, Math.min(items.length - 1, currentIndex + delta));
4307
+ setActive(items[newIndex]);
4308
+ },
4309
+ [getItems, active, setActive]
4310
+ );
4311
+ const handleKeyDown = useCallback(
4312
+ (e) => {
4313
+ if (disabled) {
4314
+ return;
4315
+ }
4316
+ const items = getItems();
4317
+ if (!items.length) {
4318
+ return;
4319
+ }
4320
+ if (e.key === "ArrowDown") {
4321
+ e.preventDefault();
4322
+ moveActive(1);
4323
+ } else if (e.key === "ArrowUp") {
4324
+ e.preventDefault();
4325
+ moveActive(-1);
4326
+ } else if (e.key === "Home") {
4327
+ e.preventDefault();
4328
+ setActive(items[0]);
4329
+ } else if (e.key === "End") {
4330
+ e.preventDefault();
4331
+ setActive(items[items.length - 1]);
4332
+ } else if (e.key === " " || e.key === "Enter") {
4333
+ e.preventDefault();
4334
+ if (active) {
4335
+ toggleValue(active);
4336
+ }
4337
+ }
4338
+ },
4339
+ [disabled, getItems, active, moveActive, toggleValue, setActive]
4340
+ );
4341
+ useEffect(() => {
4342
+ if (!active || !innerRef.current) {
4343
+ return;
4344
+ }
4345
+ const el = innerRef.current.querySelector(`#${listboxId}-option-${active}`);
4346
+ if (el) {
4347
+ el.scrollIntoView({
4348
+ block: "nearest",
4349
+ behavior: "auto"
4350
+ });
4351
+ }
4352
+ }, [active, listboxId]);
4353
+ return /* @__PURE__ */ u(
4354
+ "div",
4355
+ {
4356
+ ref: useComposedRefs(ref, innerRef),
4357
+ id: listboxId,
4358
+ className: cn(
4359
+ "flex flex-col items-start grow shrink-0 basis-0",
4360
+ "max-h-100 overflow-y-auto",
4361
+ "focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50",
4362
+ disabled && "pointer-events-none select-none opacity-30",
4363
+ className
4364
+ ),
4365
+ role: "listbox",
4366
+ "aria-disabled": disabled,
4367
+ "aria-label": label,
4368
+ "aria-activedescendant": active ? `${listboxId}-option-${active}` : void 0,
4369
+ tabIndex: disabled ? -1 : 0,
4370
+ onKeyDown: handleKeyDown,
4371
+ ...props,
4372
+ children
4373
+ }
4374
+ );
4375
+ }
4376
+ );
4377
+ ListboxContent.displayName = "ListboxContent";
4378
+ const ListboxItem = ({ value, children, className, ...props }) => {
4379
+ const ctx = useListbox();
4380
+ const { disabled, toggleValue, registerItem, unregisterItem, listboxId } = ctx;
4381
+ const isSelected = ctx.selection.has(value);
4382
+ const isActive = ctx.active === value;
4383
+ useEffect(() => {
4384
+ registerItem(value);
4385
+ return () => {
4386
+ unregisterItem(value);
4387
+ };
4388
+ }, [value, registerItem, unregisterItem]);
4389
+ const handleClick = useCallback(() => {
4390
+ if (!disabled) {
4391
+ toggleValue(value);
4392
+ }
4393
+ }, [disabled, toggleValue, value]);
4394
+ return (
4395
+ // ARIA listbox pattern: options are not individually focusable
4396
+ // Parent listbox handles all keyboard interactions via aria-activedescendant
4397
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
4398
+ /* @__PURE__ */ u(
4399
+ "div",
4400
+ {
4401
+ id: `${listboxId}-option-${value}`,
4402
+ className: cn(listboxItemVariants({ selected: isSelected, active: isActive }), className),
4403
+ role: "option",
4404
+ "aria-selected": isSelected,
4405
+ "data-value": value,
4406
+ "data-active": isActive || void 0,
4407
+ onClick: handleClick,
4408
+ ...props,
4409
+ children
4410
+ }
4411
+ )
4412
+ );
4413
+ };
4414
+ ListboxItem.displayName = "ListboxItem";
4415
+ const Listbox = Object.assign(ListboxRoot, {
4416
+ Root: ListboxRoot,
4417
+ Content: ListboxContent,
4418
+ Item: ListboxItem
4419
+ });
4420
+ const SearchInput = forwardRef(
4421
+ ({
4422
+ className,
4423
+ id,
4424
+ value,
4425
+ defaultValue = "",
4426
+ placeholder = "Search",
4427
+ clearLabel = "Clear",
4428
+ onChange,
4429
+ disabled,
4430
+ readOnly,
4431
+ ...props
4432
+ }, ref) => {
4433
+ const inputId = usePrefixedId(unwrap(id));
4434
+ const [uncontrolledValue, setUncontrolledValue] = useState$1(defaultValue);
4435
+ const isControlled = value !== void 0;
4436
+ const inputValue = isControlled ? value : uncontrolledValue;
4437
+ const isValueSet = inputValue.length > 0;
4438
+ const canClear = isValueSet && !disabled && !readOnly;
4439
+ const handleChange = (e) => {
4440
+ const newValue = e.currentTarget.value;
4441
+ if (!isControlled) {
4442
+ setUncontrolledValue(newValue);
4443
+ }
4444
+ onChange?.(newValue);
4445
+ };
4446
+ const inputRef = useRef(null);
4447
+ const handleClear = () => {
4448
+ const newValue = "";
4449
+ if (!isControlled) {
4450
+ setUncontrolledValue(newValue);
4451
+ }
4452
+ onChange?.(newValue);
4453
+ inputRef.current?.focus();
4454
+ };
4455
+ return /* @__PURE__ */ u(
4456
+ "div",
4457
+ {
4458
+ className: cn(
4459
+ "relative flex rounded-sm overflow-hidden",
4460
+ "h-12 border border-bdr-subtle focus-within:border-bdr-strong",
4461
+ "focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
4462
+ "transition-highlight",
4463
+ disabled && "pointer-events-none select-none opacity-30",
4464
+ className
4465
+ ),
4466
+ children: [
4467
+ /* @__PURE__ */ u(
4468
+ Search,
4469
+ {
4470
+ className: "absolute left-4.5 top-1/2 -translate-y-1/2 mt-0.25 text-subtle pointer-events-none",
4471
+ size: 20,
4472
+ strokeWidth: 1.5
4473
+ }
4474
+ ),
4475
+ /* @__PURE__ */ u(
4476
+ "input",
4477
+ {
4478
+ ref: useComposedRefs(ref, inputRef),
4479
+ id: inputId,
4480
+ className: cn(
4481
+ "w-full text-base pr-1 px-12 border-0",
4482
+ "text-main bg-surface-neutral",
4483
+ "placeholder:text-subtle",
4484
+ "focus:outline-none",
4485
+ "read-only:bg-surface-primary"
4486
+ ),
4487
+ value: inputValue,
4488
+ onChange: handleChange,
4489
+ readOnly,
4490
+ disabled,
4491
+ placeholder,
4492
+ "aria-label": "Search",
4493
+ "aria-disabled": disabled,
4494
+ ...props
4495
+ }
4496
+ ),
4497
+ canClear && /* @__PURE__ */ u(
4498
+ IconButton,
4499
+ {
4500
+ className: "absolute right-3 top-1/2 -translate-y-1/2 w-8 h-8 text-subtle",
4501
+ size: "lg",
4502
+ icon: X,
4503
+ title: clearLabel,
4504
+ onClick: handleClear,
4505
+ disabled
4506
+ }
4507
+ )
4508
+ ]
4509
+ }
4510
+ );
4511
+ }
4512
+ );
4513
+ SearchInput.displayName = "SearchInput";
4514
+ const SelectableListItem = ({
4515
+ className,
4516
+ selected,
4517
+ children,
4518
+ label,
4519
+ description,
4520
+ metadata,
4521
+ icon,
4522
+ readOnly,
4523
+ checked,
4524
+ defaultChecked,
4525
+ onCheckedChange,
4526
+ ...props
4527
+ }) => {
4528
+ return /* @__PURE__ */ u(
4529
+ ListItem,
4530
+ {
4531
+ className: cn(!selected && "hover:bg-surface-primary-hover", className),
4532
+ selected,
4533
+ role: "row",
4534
+ "aria-selected": selected,
4535
+ ...props,
4536
+ children: [
4537
+ /* @__PURE__ */ u(ListItem.Left, { children: /* @__PURE__ */ u(
4538
+ Checkbox,
4539
+ {
4540
+ checked,
4541
+ defaultChecked,
4542
+ onCheckedChange,
4543
+ readOnly
4544
+ }
4545
+ ) }),
4546
+ /* @__PURE__ */ u(ListItem.DefaultContent, { label, description, metadata, icon }),
4547
+ /* @__PURE__ */ u(ListItem.Right, { children })
4548
+ ]
4549
+ }
4550
+ );
4551
+ };
4552
+ SelectableListItem.displayName = "SelectableListItem";
4553
+ const Separator = ({ className, label, decorative = false, ...props }) => {
4554
+ const ariaHidden = decorative ? "true" : void 0;
4555
+ if (!label) {
4556
+ return /* @__PURE__ */ u("hr", { "aria-hidden": ariaHidden, className: cn("w-full border-bdr-subtle", className) });
4557
+ }
4558
+ return /* @__PURE__ */ u(
4559
+ "div",
4560
+ {
4561
+ role: "separator",
4562
+ "aria-orientation": "horizontal",
4563
+ "aria-hidden": ariaHidden,
4564
+ className: cn("inline-flex w-full gap-2.5", className),
4565
+ ...props,
4566
+ children: [
4567
+ /* @__PURE__ */ u("span", { className: "min-w-0 truncate text-subtle uppercase", children: label }),
4568
+ /* @__PURE__ */ u("span", { className: "min-w-6 flex-1 border-b-1 border-bdr-subtle" })
4569
+ ]
4570
+ }
4571
+ );
4572
+ };
4573
+ Separator.displayName = "Separator";
4574
+ const oppositeSide = {
4575
+ top: "bottom",
4576
+ bottom: "top",
4577
+ left: "right",
3732
4578
  right: "left"
3733
4579
  };
3734
4580
  function useTooltipPosition(isOpen, side, triggerRef, tooltipRef) {
@@ -3924,7 +4770,7 @@ function Tooltip({
3924
4770
  children
3925
4771
  }
3926
4772
  ),
3927
- isOpen && hasContent && createPortal(
4773
+ isOpen && hasContent && createPortal$1(
3928
4774
  /* @__PURE__ */ u(
3929
4775
  TooltipContent,
3930
4776
  {
@@ -3939,299 +4785,12 @@ function Tooltip({
3939
4785
  )
3940
4786
  ] });
3941
4787
  }
3942
- const DialogContext = createContext(void 0);
3943
- const DialogProvider = ({ value, children }) => {
3944
- return /* @__PURE__ */ u(DialogContext.Provider, { value, children });
3945
- };
3946
- DialogProvider.displayName = "DialogProvider";
3947
- const useDialog = () => {
3948
- const context = useContext(DialogContext);
3949
- if (!context) {
3950
- throw new Error("useDialog must be used within a DialogProvider");
3951
- }
3952
- return context;
3953
- };
3954
- function setRef(ref, value) {
3955
- if (!ref) return;
3956
- if (typeof ref === "function") {
3957
- ref(value);
3958
- } else {
3959
- try {
3960
- ref.current = value;
3961
- } catch {
3962
- }
3963
- }
3964
- }
3965
- function useComposedRefs(...refs) {
3966
- return useCallback((node) => {
3967
- refs.forEach((ref) => setRef(ref, node));
3968
- }, refs);
3969
- }
3970
- const SearchInput = forwardRef(
3971
- ({
3972
- className,
3973
- id,
3974
- value,
3975
- defaultValue = "",
3976
- placeholder = "Search",
3977
- clearLabel = "Clear",
3978
- onChange,
3979
- disabled,
3980
- readOnly,
3981
- ...props
3982
- }, ref) => {
3983
- const inputId = usePrefixedId(unwrap(id));
3984
- const [uncontrolledValue, setUncontrolledValue] = useState$1(defaultValue);
3985
- const isControlled = value !== void 0;
3986
- const inputValue = isControlled ? value : uncontrolledValue;
3987
- const isValueSet = inputValue.length > 0;
3988
- const canClear = isValueSet && !disabled && !readOnly;
3989
- const handleChange = (e) => {
3990
- const newValue = e.currentTarget.value;
3991
- if (!isControlled) {
3992
- setUncontrolledValue(newValue);
3993
- }
3994
- onChange?.(newValue);
3995
- };
3996
- const inputRef = useRef(null);
3997
- const handleClear = () => {
3998
- const newValue = "";
3999
- if (!isControlled) {
4000
- setUncontrolledValue(newValue);
4001
- }
4002
- onChange?.(newValue);
4003
- inputRef.current?.focus();
4004
- };
4005
- return /* @__PURE__ */ u(
4006
- "div",
4007
- {
4008
- className: cn(
4009
- "relative flex rounded-sm overflow-hidden",
4010
- "h-12 border border-bdr-subtle focus-within:border-bdr-strong",
4011
- "focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
4012
- "transition-highlight",
4013
- disabled && "pointer-events-none select-none opacity-30",
4014
- className
4015
- ),
4016
- children: [
4017
- /* @__PURE__ */ u(
4018
- Search,
4019
- {
4020
- className: "absolute left-4.5 top-1/2 -translate-y-1/2 mt-0.25 text-subtle pointer-events-none",
4021
- size: 20,
4022
- strokeWidth: 1.5
4023
- }
4024
- ),
4025
- /* @__PURE__ */ u(
4026
- "input",
4027
- {
4028
- ref: useComposedRefs(ref, inputRef),
4029
- id: inputId,
4030
- className: cn(
4031
- "w-full text-base pr-1 px-12 border-0",
4032
- "text-main bg-surface-neutral",
4033
- "placeholder:text-subtle",
4034
- "focus:outline-none",
4035
- "read-only:bg-surface-primary"
4036
- ),
4037
- value: inputValue,
4038
- onChange: handleChange,
4039
- readOnly,
4040
- disabled,
4041
- placeholder,
4042
- "aria-label": "Search",
4043
- "aria-disabled": disabled,
4044
- ...props
4045
- }
4046
- ),
4047
- canClear && /* @__PURE__ */ u(
4048
- IconButton,
4049
- {
4050
- className: "absolute right-3 top-1/2 -translate-y-1/2 w-8 h-8 text-subtle",
4051
- size: "lg",
4052
- icon: X,
4053
- title: clearLabel,
4054
- onClick: handleClear,
4055
- disabled
4056
- }
4057
- )
4058
- ]
4059
- }
4060
- );
4061
- }
4062
- );
4063
- SearchInput.displayName = "SearchInput";
4064
- function ListboxImpl({
4065
- className,
4066
- selection: controlledSelection,
4067
- selectionMode = "single",
4068
- disabled = false,
4069
- defaultSelection = /* @__PURE__ */ new Set(),
4070
- setSelection,
4071
- active: controlledActive,
4072
- defaultActive,
4073
- onActiveChange,
4074
- items,
4075
- renderItem,
4076
- getValue,
4077
- ...props
4078
- }, ref) {
4079
- const listboxId = usePrefixedId();
4080
- const innerRef = useRef(null);
4081
- const [uncontrolledSelection, setUncontrolledSelection] = useState(defaultSelection);
4082
- const isSelectionControlled = controlledSelection !== void 0;
4083
- const selection = isSelectionControlled ? controlledSelection : uncontrolledSelection;
4084
- const [uncontrolledActive, setUncontrolledActive] = useState(
4085
- defaultActive ?? (items[0] ? getValue(items[0]) : void 0)
4086
- );
4087
- const isActiveControlled = controlledActive !== void 0;
4088
- const active = isActiveControlled ? controlledActive : uncontrolledActive;
4089
- const valueToIndexMap = useMemo(() => {
4090
- return new Map(items.map((item, idx) => [getValue(item), idx]));
4091
- }, [items, getValue]);
4092
- const updateActive = useCallback(
4093
- (newActive) => {
4094
- if (!isActiveControlled) {
4095
- setUncontrolledActive(newActive);
4096
- }
4097
- onActiveChange?.(newActive);
4098
- },
4099
- [isActiveControlled, onActiveChange]
4100
- );
4101
- useEffect(() => {
4102
- if (!active || !items.some((item) => getValue(item) === active)) {
4103
- updateActive(items[0] ? getValue(items[0]) : void 0);
4104
- }
4105
- }, [items, getValue, active, updateActive]);
4106
- useEffect(() => {
4107
- if (!active || !innerRef.current) return;
4108
- const el = innerRef.current.querySelector(`#${listboxId}-option-${active}`);
4109
- el?.scrollIntoView({ block: "nearest", behavior: "auto" });
4110
- }, [active, listboxId]);
4111
- const handleToggleValue = (value) => {
4112
- const isAlreadySelected = selection.has(value);
4113
- let newSelection;
4114
- if (selectionMode === "single") {
4115
- newSelection = isAlreadySelected ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([value]);
4116
- } else {
4117
- newSelection = new Set(selection);
4118
- if (isAlreadySelected) {
4119
- newSelection.delete(value);
4120
- } else {
4121
- newSelection.add(value);
4122
- }
4123
- }
4124
- if (!isSelectionControlled) {
4125
- setUncontrolledSelection(newSelection);
4126
- }
4127
- setSelection?.(newSelection);
4128
- updateActive(value);
4129
- };
4130
- const handleClickItem = ({ target }) => {
4131
- const li = target instanceof Element ? target.closest("li") : null;
4132
- const value = li?.getAttribute("data-value");
4133
- if (value) {
4134
- handleToggleValue(value);
4135
- }
4136
- };
4137
- const moveActive = (delta) => {
4138
- const activeIndex = active ? valueToIndexMap.get(active) ?? 0 : 0;
4139
- const newIndex = Math.max(0, Math.min(items.length - 1, activeIndex + delta));
4140
- updateActive(getValue(items[newIndex]));
4141
- };
4142
- const handleKeyDown = (e) => {
4143
- if (disabled) {
4144
- return;
4145
- }
4146
- if (e.key === "ArrowDown") {
4147
- e.preventDefault();
4148
- moveActive(1);
4149
- } else if (e.key === "ArrowUp") {
4150
- e.preventDefault();
4151
- moveActive(-1);
4152
- } else if (e.key === "Home") {
4153
- e.preventDefault();
4154
- const firstItem = items[0];
4155
- updateActive(firstItem && getValue(firstItem));
4156
- } else if (e.key === "End") {
4157
- e.preventDefault();
4158
- const lastItem = items.at(-1);
4159
- updateActive(lastItem && getValue(lastItem));
4160
- } else if (e.key === " " || e.key === "Enter") {
4161
- e.preventDefault();
4162
- if (active) {
4163
- handleToggleValue(active);
4164
- }
4165
- }
4166
- };
4167
- return /* @__PURE__ */ u(
4168
- "ul",
4169
- {
4170
- id: listboxId,
4171
- ref: useComposedRefs(ref, innerRef),
4172
- tabIndex: disabled ? -1 : 0,
4173
- className: cn(
4174
- "flex flex-col items-start grow-1 shrink-0 basis-0",
4175
- "max-h-100 px-0 overflow-y-auto",
4176
- "focus-within:border-bdr-solid focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
4177
- "focus-within:[&>li[data-active=true]]:bg-surface-primary-hover",
4178
- "focus-within:[&>li[data-active=true][aria-selected=true]]:bg-surface-primary-selected-hover",
4179
- "transition-highlight",
4180
- disabled && "pointer-events-none select-none opacity-30",
4181
- className
4182
- ),
4183
- role: "listbox",
4184
- "aria-activedescendant": active ? `${listboxId}-option-${active}` : void 0,
4185
- "aria-disabled": disabled,
4186
- "aria-multiselectable": selectionMode === "multiple",
4187
- "aria-label": props.label ?? void 0,
4188
- "aria-orientation": "vertical",
4189
- onKeyDown: handleKeyDown,
4190
- onClick: handleClickItem,
4191
- children: items.map((item) => {
4192
- const value = getValue(item);
4193
- const selected = selection.has(value);
4194
- const isActive = value === active;
4195
- return /* @__PURE__ */ u(
4196
- "li",
4197
- {
4198
- id: `${listboxId}-option-${value}`,
4199
- "data-value": value,
4200
- "data-active": isActive ? "true" : "false",
4201
- tabIndex: -1,
4202
- className: cn(
4203
- "flex items-center w-full px-4.5 py-1 gap-x-2.5",
4204
- !disabled && "cursor-pointer",
4205
- selected ? "bg-surface-primary-selected text-alt hover:bg-surface-primary-selected-hover" : "hover:bg-surface-primary-hover"
4206
- ),
4207
- role: "option",
4208
- "aria-selected": selected,
4209
- children: renderItem(item, selected, isActive)
4210
- },
4211
- value
4212
- );
4213
- })
4214
- }
4215
- );
4216
- }
4217
- const Listbox = forwardRef(ListboxImpl);
4218
- Listbox.displayName = "Listbox";
4219
- const useScrollLock = (lock, element) => {
4220
- useEffect(() => {
4221
- if (!lock) {
4222
- return;
4223
- }
4224
- const target = element ?? document.body;
4225
- const originalOverflow = target.style.overflow;
4226
- target.style.overflow = "hidden";
4227
- return () => {
4228
- target.style.overflow = originalOverflow;
4229
- };
4230
- }, [lock, element]);
4231
- };
4232
4788
  export {
4789
+ Avatar,
4790
+ AvatarProvider,
4233
4791
  Button,
4234
4792
  Checkbox,
4793
+ Dialog,
4235
4794
  DialogProvider,
4236
4795
  IconButton,
4237
4796
  IdProvider,
@@ -4239,12 +4798,16 @@ export {
4239
4798
  Link,
4240
4799
  ListItem,
4241
4800
  Listbox,
4801
+ ListboxProvider,
4242
4802
  SearchInput,
4243
4803
  SelectableListItem,
4804
+ Separator,
4244
4805
  Tooltip,
4245
4806
  cn,
4246
4807
  unwrap,
4808
+ useAvatar,
4247
4809
  useDialog,
4810
+ useListbox,
4248
4811
  usePrefixedId,
4249
4812
  useScrollLock
4250
4813
  };