@enonic/ui 0.12.0 → 0.13.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/enonic-ui.cjs +5 -4
- package/dist/enonic-ui.es.js +1824 -455
- package/dist/styles/preset.css +1 -1
- package/dist/styles/style.css +1 -1
- package/dist/styles/tokens.css +1 -1
- package/dist/types/components/avatar/avatar.d.ts +36 -0
- package/dist/types/components/avatar/index.d.ts +1 -0
- package/dist/types/components/combobox/combobox.d.ts +60 -0
- package/dist/types/components/combobox/index.d.ts +1 -0
- package/dist/types/components/dialog/dialog.d.ts +1 -0
- package/dist/types/components/dialog/index.d.ts +1 -3
- package/dist/types/components/icon-button/icon-button.d.ts +1 -1
- package/dist/types/components/index.d.ts +7 -2
- package/dist/types/components/listbox/index.d.ts +1 -1
- package/dist/types/components/listbox/listbox.d.ts +37 -29
- package/dist/types/components/menu/index.d.ts +2 -0
- package/dist/types/components/menu/menu.d.ts +71 -0
- package/dist/types/components/search-input/search-input.d.ts +3 -1
- package/dist/types/components/separator/index.d.ts +1 -2
- package/dist/types/providers/avatar-provider.d.ts +15 -0
- package/dist/types/providers/combobox-provider.d.ts +23 -0
- package/dist/types/providers/id-provider.d.ts +7 -1
- package/dist/types/providers/index.d.ts +4 -0
- package/dist/types/providers/listbox-provider.d.ts +21 -0
- package/dist/types/providers/menu-provider.d.ts +20 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/ref.d.ts +1 -1
- package/package.json +1 -1
package/dist/enonic-ui.es.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { options, Fragment } from "preact";
|
|
2
|
-
import {
|
|
3
|
-
import { Root } from "@radix-ui/react-slot";
|
|
2
|
+
import { createContext, useContext, useId, useCallback, forwardRef, useState, 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,75 @@ 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 ComboboxContext = createContext(null);
|
|
29
|
+
const ComboboxProvider = ({ value, children }) => {
|
|
30
|
+
return /* @__PURE__ */ u(ComboboxContext.Provider, { value, children });
|
|
31
|
+
};
|
|
32
|
+
ComboboxProvider.displayName = "ComboboxProvider";
|
|
33
|
+
const useCombobox = () => {
|
|
34
|
+
const ctx = useContext(ComboboxContext);
|
|
35
|
+
if (!ctx) {
|
|
36
|
+
throw new Error("useCombobox must be used within a ComboboxProvider");
|
|
37
|
+
}
|
|
38
|
+
return ctx;
|
|
39
|
+
};
|
|
40
|
+
const DialogContext = createContext(void 0);
|
|
41
|
+
const DialogProvider = ({ value, children }) => {
|
|
42
|
+
return /* @__PURE__ */ u(DialogContext.Provider, { value, children });
|
|
43
|
+
};
|
|
44
|
+
DialogProvider.displayName = "DialogProvider";
|
|
45
|
+
const useDialog = () => {
|
|
46
|
+
const context = useContext(DialogContext);
|
|
47
|
+
if (!context) {
|
|
48
|
+
throw new Error("useDialog must be used within a DialogProvider");
|
|
49
|
+
}
|
|
50
|
+
return context;
|
|
51
|
+
};
|
|
52
|
+
const IdContext = createContext(void 0);
|
|
53
|
+
const IdProvider = ({ children, prefix }) => {
|
|
54
|
+
return /* @__PURE__ */ u(IdContext.Provider, { value: { prefix }, children });
|
|
55
|
+
};
|
|
56
|
+
IdProvider.displayName = "IdProvider";
|
|
57
|
+
const usePrefixedId = (providedId, prefix) => {
|
|
58
|
+
const baseId = providedId ?? useId();
|
|
59
|
+
const context = useContext(IdContext);
|
|
60
|
+
return [context?.prefix, prefix, baseId].filter(Boolean).join("-");
|
|
61
|
+
};
|
|
62
|
+
const ListboxContext = createContext(null);
|
|
63
|
+
const ListboxProvider = ({ value, children }) => {
|
|
64
|
+
return /* @__PURE__ */ u(ListboxContext.Provider, { value, children });
|
|
65
|
+
};
|
|
66
|
+
ListboxProvider.displayName = "ListboxProvider";
|
|
67
|
+
const useListbox = () => {
|
|
68
|
+
const ctx = useContext(ListboxContext);
|
|
69
|
+
if (!ctx) {
|
|
70
|
+
throw new Error("useListbox must be used within a Listbox");
|
|
71
|
+
}
|
|
72
|
+
return ctx;
|
|
73
|
+
};
|
|
74
|
+
const MenuContext = createContext(void 0);
|
|
75
|
+
const MenuProvider = ({ value, children }) => {
|
|
76
|
+
return /* @__PURE__ */ u(MenuContext.Provider, { value, children });
|
|
77
|
+
};
|
|
78
|
+
const useMenu = () => {
|
|
79
|
+
const context = useContext(MenuContext);
|
|
80
|
+
if (!context) {
|
|
81
|
+
throw new Error("useMenu must be used within a MenuProvider");
|
|
82
|
+
}
|
|
83
|
+
return context;
|
|
84
|
+
};
|
|
14
85
|
function r(e) {
|
|
15
86
|
var t, f2, n = "";
|
|
16
87
|
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
@@ -2978,6 +3049,22 @@ const twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
|
|
|
2978
3049
|
function cn(...inputs) {
|
|
2979
3050
|
return twMerge(clsx(inputs));
|
|
2980
3051
|
}
|
|
3052
|
+
function setRef(ref, value) {
|
|
3053
|
+
if (!ref) return;
|
|
3054
|
+
if (typeof ref === "function") {
|
|
3055
|
+
ref(value);
|
|
3056
|
+
} else {
|
|
3057
|
+
try {
|
|
3058
|
+
ref.current = value;
|
|
3059
|
+
} catch {
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
function useComposedRefs(...refs) {
|
|
3064
|
+
return useCallback((node) => {
|
|
3065
|
+
refs.forEach((ref) => setRef(ref, node));
|
|
3066
|
+
}, refs);
|
|
3067
|
+
}
|
|
2981
3068
|
function isSignal(v) {
|
|
2982
3069
|
return !!(v && typeof v === "object" && "value" in v && "peek" in v && typeof v.peek === "function" && "subscribe" in v && typeof v.subscribe === "function");
|
|
2983
3070
|
}
|
|
@@ -3024,6 +3111,125 @@ const cva = (base, config) => (props) => {
|
|
|
3024
3111
|
}, []);
|
|
3025
3112
|
return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
|
|
3026
3113
|
};
|
|
3114
|
+
const avatarVariants = cva("relative flex shrink-0 overflow-hidden", {
|
|
3115
|
+
variants: {
|
|
3116
|
+
size: {
|
|
3117
|
+
sm: "w-6 h-6 text-xs",
|
|
3118
|
+
md: "w-8 h-8 text-sm",
|
|
3119
|
+
lg: "w-12 h-12 text-base"
|
|
3120
|
+
},
|
|
3121
|
+
shape: {
|
|
3122
|
+
circle: "rounded-full",
|
|
3123
|
+
square: "rounded-md"
|
|
3124
|
+
}
|
|
3125
|
+
},
|
|
3126
|
+
defaultVariants: {
|
|
3127
|
+
size: "md",
|
|
3128
|
+
shape: "circle"
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3131
|
+
const AvatarRoot = forwardRef(
|
|
3132
|
+
({ children, className, size, shape, ...props }, ref) => {
|
|
3133
|
+
const [imageLoadingStatus, setImageLoadingStatus] = useState("idle");
|
|
3134
|
+
const handleImageLoadingStatusChange = useCallback((status) => {
|
|
3135
|
+
setImageLoadingStatus(status);
|
|
3136
|
+
}, []);
|
|
3137
|
+
const contextValue = useMemo(
|
|
3138
|
+
() => ({
|
|
3139
|
+
imageLoadingStatus,
|
|
3140
|
+
onImageLoadingStatusChange: handleImageLoadingStatusChange
|
|
3141
|
+
}),
|
|
3142
|
+
[imageLoadingStatus, handleImageLoadingStatusChange]
|
|
3143
|
+
);
|
|
3144
|
+
return /* @__PURE__ */ u(AvatarProvider, { value: contextValue, children: /* @__PURE__ */ u("span", { ref, className: cn(avatarVariants({ size, shape }), className), ...props, children }) });
|
|
3145
|
+
}
|
|
3146
|
+
);
|
|
3147
|
+
AvatarRoot.displayName = "AvatarRoot";
|
|
3148
|
+
const AvatarImage = forwardRef(
|
|
3149
|
+
({ src, alt, className, onLoadingStatusChange, ...props }, ref) => {
|
|
3150
|
+
const { imageLoadingStatus, onImageLoadingStatusChange } = useAvatar();
|
|
3151
|
+
const [hasError, setHasError] = useState(false);
|
|
3152
|
+
useEffect(() => {
|
|
3153
|
+
setHasError(false);
|
|
3154
|
+
if (!src) {
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
onImageLoadingStatusChange("loading");
|
|
3158
|
+
onLoadingStatusChange?.("loading");
|
|
3159
|
+
const img = new Image();
|
|
3160
|
+
const handleLoad = () => {
|
|
3161
|
+
onImageLoadingStatusChange("loaded");
|
|
3162
|
+
onLoadingStatusChange?.("loaded");
|
|
3163
|
+
};
|
|
3164
|
+
const handleError = () => {
|
|
3165
|
+
setHasError(true);
|
|
3166
|
+
onImageLoadingStatusChange("error");
|
|
3167
|
+
onLoadingStatusChange?.("error");
|
|
3168
|
+
};
|
|
3169
|
+
img.addEventListener("load", handleLoad);
|
|
3170
|
+
img.addEventListener("error", handleError);
|
|
3171
|
+
img.src = src;
|
|
3172
|
+
return () => {
|
|
3173
|
+
img.removeEventListener("load", handleLoad);
|
|
3174
|
+
img.removeEventListener("error", handleError);
|
|
3175
|
+
};
|
|
3176
|
+
}, [src, onImageLoadingStatusChange, onLoadingStatusChange]);
|
|
3177
|
+
if (!src || hasError || imageLoadingStatus === "error") {
|
|
3178
|
+
return null;
|
|
3179
|
+
}
|
|
3180
|
+
return /* @__PURE__ */ u(
|
|
3181
|
+
"img",
|
|
3182
|
+
{
|
|
3183
|
+
ref,
|
|
3184
|
+
src,
|
|
3185
|
+
alt,
|
|
3186
|
+
className: cn("aspect-square h-full w-full object-cover", className),
|
|
3187
|
+
...props
|
|
3188
|
+
}
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
);
|
|
3192
|
+
AvatarImage.displayName = "AvatarImage";
|
|
3193
|
+
const AvatarFallback = forwardRef(
|
|
3194
|
+
({ children, className, delayMs = 0, ...props }, ref) => {
|
|
3195
|
+
const { imageLoadingStatus } = useAvatar();
|
|
3196
|
+
const [canRender, setCanRender] = useState(delayMs === 0);
|
|
3197
|
+
useEffect(() => {
|
|
3198
|
+
if (delayMs === 0) {
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
const timerId = setTimeout(() => {
|
|
3202
|
+
setCanRender(true);
|
|
3203
|
+
}, delayMs);
|
|
3204
|
+
return () => {
|
|
3205
|
+
clearTimeout(timerId);
|
|
3206
|
+
};
|
|
3207
|
+
}, [delayMs]);
|
|
3208
|
+
const shouldRender = canRender && (imageLoadingStatus === "idle" || imageLoadingStatus === "error");
|
|
3209
|
+
if (!shouldRender) {
|
|
3210
|
+
return null;
|
|
3211
|
+
}
|
|
3212
|
+
return /* @__PURE__ */ u(
|
|
3213
|
+
"span",
|
|
3214
|
+
{
|
|
3215
|
+
ref,
|
|
3216
|
+
className: cn(
|
|
3217
|
+
"flex h-full w-full items-center justify-center",
|
|
3218
|
+
"bg-surface-secondary text-alt font-medium uppercase cursor-default",
|
|
3219
|
+
className
|
|
3220
|
+
),
|
|
3221
|
+
...props,
|
|
3222
|
+
children
|
|
3223
|
+
}
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
);
|
|
3227
|
+
AvatarFallback.displayName = "AvatarFallback";
|
|
3228
|
+
const Avatar = Object.assign(AvatarRoot, {
|
|
3229
|
+
Root: AvatarRoot,
|
|
3230
|
+
Image: AvatarImage,
|
|
3231
|
+
Fallback: AvatarFallback
|
|
3232
|
+
});
|
|
3027
3233
|
const buttonVariants = cva(
|
|
3028
3234
|
[
|
|
3029
3235
|
"inline-flex items-center justify-center",
|
|
@@ -3036,10 +3242,10 @@ const buttonVariants = cva(
|
|
|
3036
3242
|
{
|
|
3037
3243
|
variants: {
|
|
3038
3244
|
variant: {
|
|
3039
|
-
text: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-
|
|
3040
|
-
filled: "bg-btn-secondary hover:bg-btn-secondary-hover active:bg-btn-active active:text-
|
|
3041
|
-
solid: "bg-btn-tertiary text-alt hover:bg-btn-tertiary-hover active:bg-btn-active
|
|
3042
|
-
outline: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-
|
|
3245
|
+
text: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-alt",
|
|
3246
|
+
filled: "bg-btn-secondary hover:bg-btn-secondary-hover active:bg-btn-active active:text-alt",
|
|
3247
|
+
solid: "bg-btn-tertiary text-alt hover:bg-btn-tertiary-hover active:bg-btn-active",
|
|
3248
|
+
outline: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-alt border border-bdr-strong"
|
|
3043
3249
|
},
|
|
3044
3250
|
size: {
|
|
3045
3251
|
sm: "h-9 px-3.5 gap-2 text-sm",
|
|
@@ -3101,20 +3307,6 @@ const Button = forwardRef(
|
|
|
3101
3307
|
}
|
|
3102
3308
|
);
|
|
3103
3309
|
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
3310
|
/**
|
|
3119
3311
|
* @license lucide-react v0.545.0 - ISC
|
|
3120
3312
|
*
|
|
@@ -3220,19 +3412,27 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
3220
3412
|
* This source code is licensed under the ISC license.
|
|
3221
3413
|
* See the LICENSE file in the root directory of this source tree.
|
|
3222
3414
|
*/
|
|
3223
|
-
const __iconNode$
|
|
3415
|
+
const __iconNode$8 = [
|
|
3224
3416
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
3225
3417
|
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
3226
3418
|
];
|
|
3227
|
-
const ArrowRight = createLucideIcon("arrow-right", __iconNode$
|
|
3419
|
+
const ArrowRight = createLucideIcon("arrow-right", __iconNode$8);
|
|
3420
|
+
/**
|
|
3421
|
+
* @license lucide-react v0.545.0 - ISC
|
|
3422
|
+
*
|
|
3423
|
+
* This source code is licensed under the ISC license.
|
|
3424
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
3425
|
+
*/
|
|
3426
|
+
const __iconNode$7 = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
|
|
3427
|
+
const Check = createLucideIcon("check", __iconNode$7);
|
|
3228
3428
|
/**
|
|
3229
3429
|
* @license lucide-react v0.545.0 - ISC
|
|
3230
3430
|
*
|
|
3231
3431
|
* This source code is licensed under the ISC license.
|
|
3232
3432
|
* See the LICENSE file in the root directory of this source tree.
|
|
3233
3433
|
*/
|
|
3234
|
-
const __iconNode$6 = [["path", { d: "
|
|
3235
|
-
const
|
|
3434
|
+
const __iconNode$6 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
3435
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$6);
|
|
3236
3436
|
/**
|
|
3237
3437
|
* @license lucide-react v0.545.0 - ISC
|
|
3238
3438
|
*
|
|
@@ -3487,96 +3687,746 @@ const IconButton = forwardRef(
|
|
|
3487
3687
|
}
|
|
3488
3688
|
);
|
|
3489
3689
|
IconButton.displayName = "IconButton";
|
|
3490
|
-
const
|
|
3690
|
+
const ComboboxRoot = ({
|
|
3691
|
+
children,
|
|
3692
|
+
open: controlledOpen,
|
|
3693
|
+
onOpenChange,
|
|
3694
|
+
closeOnBlur = true,
|
|
3695
|
+
value,
|
|
3696
|
+
onChange,
|
|
3697
|
+
disabled = false,
|
|
3698
|
+
error = false,
|
|
3699
|
+
selectionMode = "single",
|
|
3700
|
+
selection: controlledSelection,
|
|
3701
|
+
onSelectionChange
|
|
3702
|
+
}) => {
|
|
3703
|
+
const baseId = usePrefixedId();
|
|
3704
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
|
|
3705
|
+
const isOpenControlled = controlledOpen !== void 0;
|
|
3706
|
+
const open = isOpenControlled ? controlledOpen : uncontrolledOpen;
|
|
3707
|
+
const setOpen = useCallback(
|
|
3708
|
+
(next) => {
|
|
3709
|
+
if (!isOpenControlled) {
|
|
3710
|
+
setUncontrolledOpen(next);
|
|
3711
|
+
}
|
|
3712
|
+
onOpenChange?.(next);
|
|
3713
|
+
},
|
|
3714
|
+
[isOpenControlled, onOpenChange]
|
|
3715
|
+
);
|
|
3716
|
+
const [uncontrolledSelection, setUncontrolledSelection] = useState([]);
|
|
3717
|
+
const isSelectionControlled = controlledSelection !== void 0;
|
|
3718
|
+
const selectedItems = isSelectionControlled ? controlledSelection : uncontrolledSelection;
|
|
3719
|
+
const onSelectionChangeInner = useCallback(
|
|
3720
|
+
(newSelection) => {
|
|
3721
|
+
if (!isSelectionControlled) {
|
|
3722
|
+
setUncontrolledSelection(newSelection);
|
|
3723
|
+
}
|
|
3724
|
+
onSelectionChange?.(newSelection);
|
|
3725
|
+
if (selectionMode === "single") {
|
|
3726
|
+
setOpen(false);
|
|
3727
|
+
}
|
|
3728
|
+
},
|
|
3729
|
+
[isSelectionControlled, selectionMode, setOpen, onSelectionChange, selectedItems]
|
|
3730
|
+
);
|
|
3731
|
+
const [uncontrolledInput, setUncontrolledInput] = useState("");
|
|
3732
|
+
const isInputControlled = value !== void 0;
|
|
3733
|
+
const inputValue = isInputControlled ? value : uncontrolledInput;
|
|
3734
|
+
const setInputValue = useCallback(
|
|
3735
|
+
(next) => {
|
|
3736
|
+
if (!isInputControlled) {
|
|
3737
|
+
setUncontrolledInput(next);
|
|
3738
|
+
}
|
|
3739
|
+
onChange?.(next);
|
|
3740
|
+
setOpen(true);
|
|
3741
|
+
},
|
|
3742
|
+
[isInputControlled, onChange, setOpen]
|
|
3743
|
+
);
|
|
3744
|
+
const toggleValueSelection = useCallback(
|
|
3745
|
+
(value2) => {
|
|
3746
|
+
let newSelection = [];
|
|
3747
|
+
if (selectionMode === "multiple") {
|
|
3748
|
+
if (selectedItems.includes(value2)) {
|
|
3749
|
+
newSelection = selectedItems.filter((item) => item !== value2);
|
|
3750
|
+
} else {
|
|
3751
|
+
newSelection = [...selectedItems, value2];
|
|
3752
|
+
}
|
|
3753
|
+
} else {
|
|
3754
|
+
newSelection = [value2];
|
|
3755
|
+
}
|
|
3756
|
+
onSelectionChangeInner(newSelection);
|
|
3757
|
+
},
|
|
3758
|
+
[selectionMode, selectedItems, onSelectionChangeInner]
|
|
3759
|
+
);
|
|
3760
|
+
const [active, setActive] = useState(void 0);
|
|
3761
|
+
const innerRef = useRef(null);
|
|
3762
|
+
const getItems = useCallback(() => {
|
|
3763
|
+
const container = innerRef.current;
|
|
3764
|
+
if (!container) {
|
|
3765
|
+
return [];
|
|
3766
|
+
}
|
|
3767
|
+
const optionNodes = container.querySelectorAll('[role="option"][data-value]');
|
|
3768
|
+
return Array.from(optionNodes).map((node) => node.dataset.value).filter((v) => v !== void 0);
|
|
3769
|
+
}, []);
|
|
3770
|
+
const moveActive = useCallback(
|
|
3771
|
+
(delta) => {
|
|
3772
|
+
const items = getItems();
|
|
3773
|
+
if (!items.length) {
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
const currentIndex = active ? items.indexOf(active) : -1;
|
|
3777
|
+
const newIndex = Math.max(0, Math.min(items.length - 1, currentIndex + delta));
|
|
3778
|
+
setActive(items[newIndex]);
|
|
3779
|
+
},
|
|
3780
|
+
[active, setActive, getItems]
|
|
3781
|
+
);
|
|
3782
|
+
const keyHandler = useCallback(
|
|
3783
|
+
(e) => {
|
|
3784
|
+
if (disabled) {
|
|
3785
|
+
return;
|
|
3786
|
+
}
|
|
3787
|
+
const items = getItems();
|
|
3788
|
+
if (e.key === "ArrowDown") {
|
|
3789
|
+
e.preventDefault();
|
|
3790
|
+
setOpen(true);
|
|
3791
|
+
moveActive(1);
|
|
3792
|
+
} else if (e.key === "ArrowUp") {
|
|
3793
|
+
setOpen(true);
|
|
3794
|
+
moveActive(-1);
|
|
3795
|
+
} else if (e.key === "Home") {
|
|
3796
|
+
e.preventDefault();
|
|
3797
|
+
setActive(items[0]);
|
|
3798
|
+
} else if (e.key === "End") {
|
|
3799
|
+
e.preventDefault();
|
|
3800
|
+
setActive(items[items.length - 1]);
|
|
3801
|
+
} else if (e.key === "Enter") {
|
|
3802
|
+
if (open) {
|
|
3803
|
+
e.preventDefault();
|
|
3804
|
+
if (active) {
|
|
3805
|
+
toggleValueSelection(active);
|
|
3806
|
+
}
|
|
3807
|
+
setOpen(false);
|
|
3808
|
+
setActive(items[0]);
|
|
3809
|
+
}
|
|
3810
|
+
} else if (e.key === "Escape") {
|
|
3811
|
+
setOpen(false);
|
|
3812
|
+
setActive(items[0]);
|
|
3813
|
+
}
|
|
3814
|
+
},
|
|
3815
|
+
[disabled, moveActive, setOpen, toggleValueSelection, open]
|
|
3816
|
+
);
|
|
3817
|
+
useEffect(() => {
|
|
3818
|
+
if (open && active === void 0) {
|
|
3819
|
+
const items = getItems();
|
|
3820
|
+
if (items.length > 0) {
|
|
3821
|
+
setActive(items[0]);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}, [open, active, getItems]);
|
|
3825
|
+
const context = useMemo(
|
|
3826
|
+
() => ({
|
|
3827
|
+
open,
|
|
3828
|
+
setOpen,
|
|
3829
|
+
inputValue,
|
|
3830
|
+
setInputValue,
|
|
3831
|
+
baseId,
|
|
3832
|
+
active,
|
|
3833
|
+
disabled,
|
|
3834
|
+
error,
|
|
3835
|
+
closeOnBlur,
|
|
3836
|
+
keyHandler,
|
|
3837
|
+
selection: selectedItems
|
|
3838
|
+
}),
|
|
3839
|
+
[open, setOpen, closeOnBlur, keyHandler, inputValue, setInputValue, active, baseId, disabled, error, selectedItems]
|
|
3840
|
+
);
|
|
3841
|
+
return /* @__PURE__ */ u("div", { ref: innerRef, children: /* @__PURE__ */ u(ComboboxProvider, { value: context, children: /* @__PURE__ */ u(
|
|
3842
|
+
Listbox.Root,
|
|
3843
|
+
{
|
|
3844
|
+
selectionMode,
|
|
3845
|
+
selection: selectedItems,
|
|
3846
|
+
onSelectionChange: onSelectionChangeInner,
|
|
3847
|
+
disabled,
|
|
3848
|
+
active,
|
|
3849
|
+
focusable: false,
|
|
3850
|
+
baseId,
|
|
3851
|
+
setActive,
|
|
3852
|
+
keyHandler,
|
|
3853
|
+
children
|
|
3854
|
+
}
|
|
3855
|
+
) }) });
|
|
3856
|
+
};
|
|
3857
|
+
ComboboxRoot.displayName = "Combobox.Root";
|
|
3858
|
+
const ComboboxContent = forwardRef(
|
|
3859
|
+
({ className, children }, ref) => {
|
|
3860
|
+
const { setOpen, baseId, closeOnBlur } = useCombobox();
|
|
3861
|
+
const innerRef = useRef(null);
|
|
3862
|
+
const handleFocusOut = closeOnBlur ? useCallback(
|
|
3863
|
+
(e) => {
|
|
3864
|
+
const nextTarget = e.relatedTarget;
|
|
3865
|
+
if (nextTarget && innerRef.current?.contains(nextTarget)) {
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3868
|
+
setOpen(false);
|
|
3869
|
+
},
|
|
3870
|
+
[baseId, setOpen]
|
|
3871
|
+
) : void 0;
|
|
3872
|
+
return (
|
|
3873
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
3874
|
+
/* @__PURE__ */ u("div", { onFocusOut: handleFocusOut, ref: useComposedRefs(ref, innerRef), className, children })
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
);
|
|
3878
|
+
const comboboxControlVariants = cva(
|
|
3491
3879
|
[
|
|
3492
|
-
"
|
|
3493
|
-
"h-12 border
|
|
3880
|
+
"flex items-center",
|
|
3881
|
+
"h-12 rounded-sm border bg-surface-neutral",
|
|
3494
3882
|
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
|
|
3495
3883
|
"transition-highlight"
|
|
3496
3884
|
],
|
|
3497
3885
|
{
|
|
3498
3886
|
variants: {
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3887
|
+
error: {
|
|
3888
|
+
true: "border-error focus-within:border-error focus-within:ring-error/50",
|
|
3889
|
+
false: "border-bdr-subtle focus-within:border-bdr-strong"
|
|
3890
|
+
},
|
|
3891
|
+
open: {
|
|
3892
|
+
true: "border-bdr-strong",
|
|
3893
|
+
false: null
|
|
3502
3894
|
},
|
|
3503
3895
|
disabled: {
|
|
3504
|
-
true: "select-none"
|
|
3896
|
+
true: "pointer-events-none select-none opacity-30",
|
|
3897
|
+
false: null
|
|
3505
3898
|
}
|
|
3506
3899
|
},
|
|
3507
3900
|
defaultVariants: {
|
|
3508
|
-
|
|
3901
|
+
error: false,
|
|
3902
|
+
open: false,
|
|
3509
3903
|
disabled: false
|
|
3510
3904
|
}
|
|
3511
3905
|
}
|
|
3512
3906
|
);
|
|
3513
|
-
const
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3907
|
+
const ComboboxControl = ({ children, className }) => {
|
|
3908
|
+
const { open, disabled, error } = useCombobox();
|
|
3909
|
+
return /* @__PURE__ */ u(
|
|
3910
|
+
"div",
|
|
3911
|
+
{
|
|
3912
|
+
className: cn(
|
|
3913
|
+
comboboxControlVariants({
|
|
3914
|
+
error,
|
|
3915
|
+
open,
|
|
3916
|
+
disabled
|
|
3917
|
+
}),
|
|
3918
|
+
className
|
|
3919
|
+
),
|
|
3920
|
+
"data-open": open ? "true" : void 0,
|
|
3921
|
+
children
|
|
3922
|
+
}
|
|
3923
|
+
);
|
|
3924
|
+
};
|
|
3925
|
+
ComboboxControl.displayName = "Combobox.Control";
|
|
3926
|
+
const ComboboxInput = forwardRef(
|
|
3927
|
+
({ className, placeholder, ...props }, ref) => {
|
|
3928
|
+
const { inputValue, setInputValue, open, keyHandler, selection, baseId, active, disabled } = useCombobox();
|
|
3929
|
+
const innerRef = useRef(null);
|
|
3930
|
+
useEffect(() => {
|
|
3931
|
+
if (open && !disabled) {
|
|
3932
|
+
innerRef.current?.focus();
|
|
3933
|
+
}
|
|
3934
|
+
}, [open, selection, disabled]);
|
|
3935
|
+
return /* @__PURE__ */ u(
|
|
3936
|
+
SearchInput,
|
|
3937
|
+
{
|
|
3938
|
+
ref: useComposedRefs(ref, innerRef),
|
|
3939
|
+
id: `${baseId}-input`,
|
|
3940
|
+
className: "border-none focus:outline-none focus-within:outline-none h-auto focus-within:ring-0 grow",
|
|
3941
|
+
value: inputValue,
|
|
3942
|
+
onChange: setInputValue,
|
|
3943
|
+
onKeyDown: keyHandler,
|
|
3944
|
+
placeholder,
|
|
3945
|
+
disabled,
|
|
3946
|
+
"aria-disabled": disabled,
|
|
3947
|
+
role: "combobox",
|
|
3948
|
+
"aria-autocomplete": "list",
|
|
3949
|
+
"aria-expanded": open,
|
|
3950
|
+
"aria-haspopup": "listbox",
|
|
3951
|
+
"aria-controls": `${baseId}-listbox`,
|
|
3952
|
+
"aria-activedescendant": active ? `${baseId}-listbox-option-${active}` : void 0,
|
|
3953
|
+
showClearButton: false,
|
|
3954
|
+
...props
|
|
3955
|
+
}
|
|
3956
|
+
);
|
|
3524
3957
|
}
|
|
3525
3958
|
);
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
startAddon && "rounded-l-none",
|
|
3559
|
-
endAddon && "rounded-r-none"
|
|
3560
|
-
),
|
|
3561
|
-
disabled,
|
|
3562
|
-
readOnly,
|
|
3563
|
-
...props
|
|
3564
|
-
}
|
|
3565
|
-
),
|
|
3566
|
-
endAddon && /* @__PURE__ */ u(Addon, { error: hasError, children: endAddon })
|
|
3567
|
-
] }),
|
|
3568
|
-
error && /* @__PURE__ */ u("div", { className: "flex items-center gap-2 mt-2 leading-5 text-error", children: [
|
|
3569
|
-
/* @__PURE__ */ u(OctagonAlert, { size: 16, strokeWidth: 2.5 }),
|
|
3570
|
-
error
|
|
3571
|
-
] })
|
|
3572
|
-
] });
|
|
3959
|
+
ComboboxInput.displayName = "Combobox.Input";
|
|
3960
|
+
const ComboboxToggle = ({ className }) => {
|
|
3961
|
+
const { open, setOpen, disabled } = useCombobox();
|
|
3962
|
+
return /* @__PURE__ */ u(
|
|
3963
|
+
IconButton,
|
|
3964
|
+
{
|
|
3965
|
+
type: "button",
|
|
3966
|
+
variant: "text",
|
|
3967
|
+
size: "md",
|
|
3968
|
+
icon: ChevronDown,
|
|
3969
|
+
"aria-label": "Toggle",
|
|
3970
|
+
onClick: () => {
|
|
3971
|
+
if (disabled) {
|
|
3972
|
+
return;
|
|
3973
|
+
}
|
|
3974
|
+
setOpen(!open);
|
|
3975
|
+
},
|
|
3976
|
+
disabled,
|
|
3977
|
+
tabIndex: -1,
|
|
3978
|
+
className: cn(
|
|
3979
|
+
"h-10 w-10 text-subtle transition-transform hover:bg-surface-primary-hover mr-1",
|
|
3980
|
+
open && "rotate-180",
|
|
3981
|
+
className
|
|
3982
|
+
)
|
|
3983
|
+
}
|
|
3984
|
+
);
|
|
3985
|
+
};
|
|
3986
|
+
ComboboxToggle.displayName = "Combobox.Toggle";
|
|
3987
|
+
const ComboboxPopup = ({ children, className }) => {
|
|
3988
|
+
const { open } = useCombobox();
|
|
3989
|
+
if (!open) {
|
|
3990
|
+
return null;
|
|
3573
3991
|
}
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3992
|
+
return /* @__PURE__ */ u(
|
|
3993
|
+
"div",
|
|
3994
|
+
{
|
|
3995
|
+
className: cn(
|
|
3996
|
+
"absolute left-0 right-0 z-50 mt-1 rounded-sm bg-surface-neutral shadow-lg ring-1 ring-bdr-subtle",
|
|
3997
|
+
className
|
|
3998
|
+
),
|
|
3999
|
+
children
|
|
4000
|
+
}
|
|
4001
|
+
);
|
|
4002
|
+
};
|
|
4003
|
+
ComboboxPopup.displayName = "Combobox.Popup";
|
|
4004
|
+
const Combobox = Object.assign(ComboboxRoot, {
|
|
4005
|
+
Root: ComboboxRoot,
|
|
4006
|
+
Content: ComboboxContent,
|
|
4007
|
+
Control: ComboboxControl,
|
|
4008
|
+
Input: ComboboxInput,
|
|
4009
|
+
Toggle: ComboboxToggle,
|
|
4010
|
+
Popup: ComboboxPopup
|
|
4011
|
+
});
|
|
4012
|
+
const useScrollLock = (lock, element) => {
|
|
4013
|
+
useEffect(() => {
|
|
4014
|
+
if (!lock) {
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
const target = element ?? document.body;
|
|
4018
|
+
const originalOverflow = target.style.overflow;
|
|
4019
|
+
target.style.overflow = "hidden";
|
|
4020
|
+
return () => {
|
|
4021
|
+
target.style.overflow = originalOverflow;
|
|
4022
|
+
};
|
|
4023
|
+
}, [lock, element]);
|
|
4024
|
+
};
|
|
4025
|
+
const DialogRoot = ({
|
|
4026
|
+
open: controlledOpen,
|
|
4027
|
+
defaultOpen = false,
|
|
4028
|
+
onOpenChange,
|
|
4029
|
+
children
|
|
4030
|
+
}) => {
|
|
4031
|
+
const isControlled = controlledOpen !== void 0;
|
|
4032
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
4033
|
+
const open = isControlled ? controlledOpen : uncontrolledOpen;
|
|
4034
|
+
const setOpen = useCallback(
|
|
4035
|
+
(next) => {
|
|
4036
|
+
if (!isControlled) {
|
|
4037
|
+
setUncontrolledOpen(next);
|
|
4038
|
+
}
|
|
4039
|
+
onOpenChange?.(next);
|
|
4040
|
+
},
|
|
4041
|
+
[isControlled, onOpenChange]
|
|
4042
|
+
);
|
|
4043
|
+
const value = useMemo(() => ({ open, setOpen }), [open, setOpen]);
|
|
4044
|
+
return /* @__PURE__ */ u(DialogProvider, { value, children });
|
|
4045
|
+
};
|
|
4046
|
+
DialogRoot.displayName = "Dialog.Root";
|
|
4047
|
+
const DialogTrigger = forwardRef(
|
|
4048
|
+
({ asChild, children, onClick, ...props }, ref) => {
|
|
4049
|
+
const { setOpen } = useDialog();
|
|
4050
|
+
const handleClick = (e) => {
|
|
4051
|
+
onClick?.(e);
|
|
4052
|
+
setOpen(true);
|
|
4053
|
+
};
|
|
4054
|
+
const Comp = asChild ? Slot : "button";
|
|
4055
|
+
return /* @__PURE__ */ u(
|
|
4056
|
+
Comp,
|
|
4057
|
+
{
|
|
4058
|
+
ref,
|
|
4059
|
+
type: asChild ? void 0 : "button",
|
|
4060
|
+
onClick: handleClick,
|
|
4061
|
+
...props,
|
|
4062
|
+
children
|
|
4063
|
+
}
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
);
|
|
4067
|
+
DialogTrigger.displayName = "Dialog.Trigger";
|
|
4068
|
+
const DialogPortal = ({ children, container, forceMount }) => {
|
|
4069
|
+
const { open } = useDialog();
|
|
4070
|
+
const [mounted, setMounted] = useState(false);
|
|
4071
|
+
useEffect(() => {
|
|
4072
|
+
setMounted(true);
|
|
4073
|
+
}, []);
|
|
4074
|
+
if (!mounted || !forceMount && !open) {
|
|
4075
|
+
return null;
|
|
4076
|
+
}
|
|
4077
|
+
return createPortal(children, container ?? document.body);
|
|
4078
|
+
};
|
|
4079
|
+
DialogPortal.displayName = "Dialog.Portal";
|
|
4080
|
+
const DialogOverlay = forwardRef(
|
|
4081
|
+
({ className, forceMount, ...props }, ref) => {
|
|
4082
|
+
const { open } = useDialog();
|
|
4083
|
+
if (!forceMount && !open) {
|
|
4084
|
+
return null;
|
|
4085
|
+
}
|
|
4086
|
+
return /* @__PURE__ */ u(
|
|
4087
|
+
"div",
|
|
4088
|
+
{
|
|
4089
|
+
ref,
|
|
4090
|
+
"data-state": open ? "open" : "closed",
|
|
4091
|
+
className: cn(
|
|
4092
|
+
"fixed inset-0 z-30 bg-overlay backdrop-blur-xs",
|
|
4093
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
4094
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
4095
|
+
className
|
|
4096
|
+
),
|
|
4097
|
+
...props
|
|
4098
|
+
}
|
|
4099
|
+
);
|
|
4100
|
+
}
|
|
4101
|
+
);
|
|
4102
|
+
DialogOverlay.displayName = "Dialog.Overlay";
|
|
4103
|
+
const DialogContent = forwardRef(
|
|
4104
|
+
({
|
|
4105
|
+
children,
|
|
4106
|
+
className,
|
|
4107
|
+
forceMount,
|
|
4108
|
+
onEscapeKeyDown,
|
|
4109
|
+
onPointerDownOutside,
|
|
4110
|
+
onInteractOutside,
|
|
4111
|
+
onOpenAutoFocus,
|
|
4112
|
+
onCloseAutoFocus,
|
|
4113
|
+
...props
|
|
4114
|
+
}, ref) => {
|
|
4115
|
+
const { open, setOpen } = useDialog();
|
|
4116
|
+
const contentRef = useRef(null);
|
|
4117
|
+
const titleId = useId();
|
|
4118
|
+
const descriptionId = useId();
|
|
4119
|
+
useEffect(() => {
|
|
4120
|
+
if (typeof ref === "function") {
|
|
4121
|
+
ref(contentRef.current);
|
|
4122
|
+
} else if (ref) {
|
|
4123
|
+
ref.current = contentRef.current;
|
|
4124
|
+
}
|
|
4125
|
+
}, [ref]);
|
|
4126
|
+
useScrollLock(open);
|
|
4127
|
+
const handleEscapeKey = useCallback(
|
|
4128
|
+
(e) => {
|
|
4129
|
+
if (e.key === "Escape") {
|
|
4130
|
+
onEscapeKeyDown?.(e);
|
|
4131
|
+
if (!e.defaultPrevented) {
|
|
4132
|
+
setOpen(false);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
},
|
|
4136
|
+
[onEscapeKeyDown, setOpen]
|
|
4137
|
+
);
|
|
4138
|
+
useEffect(() => {
|
|
4139
|
+
if (!open) {
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
document.addEventListener("keydown", handleEscapeKey);
|
|
4143
|
+
return () => document.removeEventListener("keydown", handleEscapeKey);
|
|
4144
|
+
}, [open, handleEscapeKey]);
|
|
4145
|
+
const handlePointerDownOutside = useCallback(
|
|
4146
|
+
(e) => {
|
|
4147
|
+
const target = e.target;
|
|
4148
|
+
if (contentRef.current && !contentRef.current.contains(target)) {
|
|
4149
|
+
onPointerDownOutside?.(e);
|
|
4150
|
+
onInteractOutside?.(e);
|
|
4151
|
+
if (!e.defaultPrevented) {
|
|
4152
|
+
setOpen(false);
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
},
|
|
4156
|
+
[onPointerDownOutside, onInteractOutside, setOpen]
|
|
4157
|
+
);
|
|
4158
|
+
useEffect(() => {
|
|
4159
|
+
if (!open) {
|
|
4160
|
+
return;
|
|
4161
|
+
}
|
|
4162
|
+
document.addEventListener("pointerdown", handlePointerDownOutside);
|
|
4163
|
+
return () => document.removeEventListener("pointerdown", handlePointerDownOutside);
|
|
4164
|
+
}, [open, handlePointerDownOutside]);
|
|
4165
|
+
if (!forceMount && !open) {
|
|
4166
|
+
return null;
|
|
4167
|
+
}
|
|
4168
|
+
return /* @__PURE__ */ u(
|
|
4169
|
+
FocusTrap,
|
|
4170
|
+
{
|
|
4171
|
+
active: open,
|
|
4172
|
+
focusTrapOptions: {
|
|
4173
|
+
initialFocus: false,
|
|
4174
|
+
fallbackFocus: () => contentRef.current ?? document.body,
|
|
4175
|
+
escapeDeactivates: false,
|
|
4176
|
+
clickOutsideDeactivates: false,
|
|
4177
|
+
returnFocusOnDeactivate: true,
|
|
4178
|
+
allowOutsideClick: true,
|
|
4179
|
+
preventScroll: false,
|
|
4180
|
+
onActivate: () => {
|
|
4181
|
+
requestAnimationFrame(() => {
|
|
4182
|
+
const event = new Event("openautofocus");
|
|
4183
|
+
onOpenAutoFocus?.(event);
|
|
4184
|
+
if (!event.defaultPrevented && contentRef.current) {
|
|
4185
|
+
contentRef.current.focus();
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
},
|
|
4189
|
+
onDeactivate: () => {
|
|
4190
|
+
requestAnimationFrame(() => {
|
|
4191
|
+
const event = new Event("closeautofocus");
|
|
4192
|
+
onCloseAutoFocus?.(event);
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
},
|
|
4196
|
+
children: /* @__PURE__ */ u("div", { className: "fixed inset-0 z-40 flex items-center justify-center p-4", children: /* @__PURE__ */ u(
|
|
4197
|
+
"div",
|
|
4198
|
+
{
|
|
4199
|
+
ref: contentRef,
|
|
4200
|
+
role: "dialog",
|
|
4201
|
+
"aria-modal": "true",
|
|
4202
|
+
"aria-labelledby": titleId,
|
|
4203
|
+
"aria-describedby": descriptionId,
|
|
4204
|
+
"data-state": open ? "open" : "closed",
|
|
4205
|
+
tabIndex: -1,
|
|
4206
|
+
className: cn(
|
|
4207
|
+
"relative rounded-lg shadow-xl bg-surface-neutral",
|
|
4208
|
+
"flex flex-col max-w-lg w-full max-h-[90vh] gap-10 p-10",
|
|
4209
|
+
"border border-bdr-subtle outline-none overflow-hidden",
|
|
4210
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
4211
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
4212
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
4213
|
+
className
|
|
4214
|
+
),
|
|
4215
|
+
...props,
|
|
4216
|
+
children
|
|
4217
|
+
}
|
|
4218
|
+
) })
|
|
4219
|
+
}
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
);
|
|
4223
|
+
DialogContent.displayName = "Dialog.Content";
|
|
4224
|
+
const DialogHeader = forwardRef(
|
|
4225
|
+
({ className, children, ...props }, ref) => {
|
|
4226
|
+
return /* @__PURE__ */ u("header", { ref, className: cn("relative grid gap-2.5 self-stretch", className), ...props, children });
|
|
4227
|
+
}
|
|
4228
|
+
);
|
|
4229
|
+
DialogHeader.displayName = "Dialog.Header";
|
|
4230
|
+
const DialogClose = forwardRef(
|
|
4231
|
+
({ children, asChild, onClick, ...props }, ref) => {
|
|
4232
|
+
const { setOpen } = useDialog();
|
|
4233
|
+
const handleClick = useCallback(
|
|
4234
|
+
(e) => {
|
|
4235
|
+
onClick?.(e);
|
|
4236
|
+
if (!e.defaultPrevented) {
|
|
4237
|
+
setOpen(false);
|
|
4238
|
+
}
|
|
4239
|
+
},
|
|
4240
|
+
[onClick, setOpen]
|
|
4241
|
+
);
|
|
4242
|
+
const Comp = asChild ? Slot : "button";
|
|
4243
|
+
return /* @__PURE__ */ u(
|
|
4244
|
+
Comp,
|
|
4245
|
+
{
|
|
4246
|
+
ref,
|
|
4247
|
+
type: asChild ? void 0 : "button",
|
|
4248
|
+
onClick: handleClick,
|
|
4249
|
+
...props,
|
|
4250
|
+
children
|
|
4251
|
+
}
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
4254
|
+
);
|
|
4255
|
+
DialogClose.displayName = "Dialog.Close";
|
|
4256
|
+
const DialogTitle = forwardRef(
|
|
4257
|
+
({ children, className, asChild, ...props }, ref) => {
|
|
4258
|
+
const Comp = asChild ? Slot : "h2";
|
|
4259
|
+
return /* @__PURE__ */ u(
|
|
4260
|
+
Comp,
|
|
4261
|
+
{
|
|
4262
|
+
ref,
|
|
4263
|
+
className: cn("text-2xl font-semibold", className),
|
|
4264
|
+
...props,
|
|
4265
|
+
children
|
|
4266
|
+
}
|
|
4267
|
+
);
|
|
4268
|
+
}
|
|
4269
|
+
);
|
|
4270
|
+
DialogTitle.displayName = "Dialog.Title";
|
|
4271
|
+
const DialogDescription = forwardRef(
|
|
4272
|
+
({ children, className, asChild, ...props }, ref) => {
|
|
4273
|
+
const Comp = asChild ? Slot : "p";
|
|
4274
|
+
return /* @__PURE__ */ u(
|
|
4275
|
+
Comp,
|
|
4276
|
+
{
|
|
4277
|
+
ref,
|
|
4278
|
+
className: cn("", className),
|
|
4279
|
+
...props,
|
|
4280
|
+
children
|
|
4281
|
+
}
|
|
4282
|
+
);
|
|
4283
|
+
}
|
|
4284
|
+
);
|
|
4285
|
+
DialogDescription.displayName = "Dialog.Description";
|
|
4286
|
+
const DialogBody = forwardRef(
|
|
4287
|
+
({ className, children, ...props }, ref) => {
|
|
4288
|
+
return /* @__PURE__ */ u("div", { ref, className: cn("min-h-0 flex-1 overflow-y-auto", className), ...props, children });
|
|
4289
|
+
}
|
|
4290
|
+
);
|
|
4291
|
+
DialogBody.displayName = "Dialog.Body";
|
|
4292
|
+
const DialogFooter = forwardRef(
|
|
4293
|
+
({ className, children, ...props }, ref) => {
|
|
4294
|
+
return /* @__PURE__ */ u("footer", { ref, className: cn("flex justify-end gap-2.5", className), ...props, children });
|
|
4295
|
+
}
|
|
4296
|
+
);
|
|
4297
|
+
DialogFooter.displayName = "Dialog.Footer";
|
|
4298
|
+
const DialogDefaultClose = forwardRef((props, ref) => {
|
|
4299
|
+
return /* @__PURE__ */ u(DialogClose, { asChild: true, ref, ...props, children: /* @__PURE__ */ u(
|
|
4300
|
+
IconButton,
|
|
4301
|
+
{
|
|
4302
|
+
"aria-label": "Close",
|
|
4303
|
+
"data-area": "close",
|
|
4304
|
+
icon: X,
|
|
4305
|
+
size: "lg",
|
|
4306
|
+
iconSize: 36,
|
|
4307
|
+
iconStrokeWidth: 1,
|
|
4308
|
+
shape: "round",
|
|
4309
|
+
variant: "filled"
|
|
4310
|
+
}
|
|
4311
|
+
) });
|
|
4312
|
+
});
|
|
4313
|
+
DialogDefaultClose.displayName = "Dialog.DefaultClose";
|
|
4314
|
+
const DialogDefaultHeader = forwardRef(
|
|
4315
|
+
({ title, description, withClose, className, children, ...props }, ref) => {
|
|
4316
|
+
return /* @__PURE__ */ u(DialogHeader, { ref, className: cn(withClose && "grid-cols-[minmax(0,1fr)_auto]", className), ...props, children: [
|
|
4317
|
+
/* @__PURE__ */ u(DialogTitle, { className: cn(withClose && "col-start-1 row-start-1 min-w-0"), children: title }),
|
|
4318
|
+
withClose && /* @__PURE__ */ u(DialogDefaultClose, { className: "col-start-2 row-start-1 row-span-2 self-start justify-self-end" }),
|
|
4319
|
+
description && /* @__PURE__ */ u(DialogDescription, { className: cn(withClose && "row-start-2"), children: description }),
|
|
4320
|
+
children
|
|
4321
|
+
] });
|
|
4322
|
+
}
|
|
4323
|
+
);
|
|
4324
|
+
DialogDefaultHeader.displayName = "Dialog.DefaultHeader";
|
|
4325
|
+
const Dialog = Object.assign(DialogRoot, {
|
|
4326
|
+
Root: DialogRoot,
|
|
4327
|
+
Trigger: DialogTrigger,
|
|
4328
|
+
Portal: DialogPortal,
|
|
4329
|
+
Overlay: DialogOverlay,
|
|
4330
|
+
Content: DialogContent,
|
|
4331
|
+
Header: DialogHeader,
|
|
4332
|
+
Close: DialogClose,
|
|
4333
|
+
Title: DialogTitle,
|
|
4334
|
+
Description: DialogDescription,
|
|
4335
|
+
Body: DialogBody,
|
|
4336
|
+
Footer: DialogFooter,
|
|
4337
|
+
DefaultClose: DialogDefaultClose,
|
|
4338
|
+
DefaultHeader: DialogDefaultHeader
|
|
4339
|
+
});
|
|
4340
|
+
const inputContainerVariants = cva(
|
|
4341
|
+
[
|
|
4342
|
+
"relative flex rounded-sm overflow-hidden",
|
|
4343
|
+
"h-12 border focus-within:border-bdr-solid",
|
|
4344
|
+
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
|
|
4345
|
+
"transition-highlight"
|
|
4346
|
+
],
|
|
4347
|
+
{
|
|
4348
|
+
variants: {
|
|
4349
|
+
state: {
|
|
4350
|
+
default: "border-bdr-subtle focus-within:border-bdr-strong",
|
|
4351
|
+
error: "border-error focus-within:border-error focus-within:ring-error/50"
|
|
4352
|
+
},
|
|
4353
|
+
disabled: {
|
|
4354
|
+
true: "select-none"
|
|
4355
|
+
}
|
|
4356
|
+
},
|
|
4357
|
+
defaultVariants: {
|
|
4358
|
+
state: "default",
|
|
4359
|
+
disabled: false
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
);
|
|
4363
|
+
const Addon = ({ children, error }) => /* @__PURE__ */ u(
|
|
4364
|
+
"div",
|
|
4365
|
+
{
|
|
4366
|
+
className: cn(
|
|
4367
|
+
"flex items-center justify-center shrink-0 min-w-12 px-4",
|
|
4368
|
+
"text-sm text-subtle bg-surface-primary",
|
|
4369
|
+
"first:rounded-l-sm first:border-r first:border-bdr-subtle",
|
|
4370
|
+
"last:rounded-r-sm last:border-l last:border-bdr-subtle",
|
|
4371
|
+
error && "first:border-error last:border-error"
|
|
4372
|
+
),
|
|
4373
|
+
children
|
|
4374
|
+
}
|
|
4375
|
+
);
|
|
4376
|
+
const Input = forwardRef(
|
|
4377
|
+
({ className, label, description, error, startAddon, endAddon, id, disabled, readOnly, ...props }, ref) => {
|
|
4378
|
+
const inputId = usePrefixedId(unwrap(id));
|
|
4379
|
+
const state = error ? "error" : "default";
|
|
4380
|
+
const hasError = state === "error";
|
|
4381
|
+
return /* @__PURE__ */ u("div", { className: cn("w-full", disabled && "opacity-30", className), children: [
|
|
4382
|
+
/* @__PURE__ */ u("div", { className: "mb-2", children: [
|
|
4383
|
+
label && /* @__PURE__ */ u(
|
|
4384
|
+
"label",
|
|
4385
|
+
{
|
|
4386
|
+
htmlFor: inputId,
|
|
4387
|
+
className: cn("block text-base font-semibold text-main", disabled && "opacity-30"),
|
|
4388
|
+
children: /* @__PURE__ */ u("div", { className: "flex items-center gap-2", children: [
|
|
4389
|
+
readOnly && /* @__PURE__ */ u(LockKeyhole, { size: 16, strokeWidth: 2.5 }),
|
|
4390
|
+
label
|
|
4391
|
+
] })
|
|
4392
|
+
}
|
|
4393
|
+
),
|
|
4394
|
+
description && /* @__PURE__ */ u("div", { className: cn("text-sm text-subtle", disabled && "opacity-30"), children: description })
|
|
4395
|
+
] }),
|
|
4396
|
+
/* @__PURE__ */ u("div", { className: cn(inputContainerVariants({ state, disabled })), children: [
|
|
4397
|
+
startAddon && /* @__PURE__ */ u(Addon, { error: hasError, children: startAddon }),
|
|
4398
|
+
/* @__PURE__ */ u(
|
|
4399
|
+
"input",
|
|
4400
|
+
{
|
|
4401
|
+
ref,
|
|
4402
|
+
id: inputId,
|
|
4403
|
+
className: cn(
|
|
4404
|
+
"flex-1 w-full px-4.5 text-base",
|
|
4405
|
+
"text-main bg-surface-neutral placeholder:text-subtle",
|
|
4406
|
+
"border-0 focus:outline-none",
|
|
4407
|
+
"disabled:select-none read-only:bg-surface-primary",
|
|
4408
|
+
startAddon && "rounded-l-none",
|
|
4409
|
+
endAddon && "rounded-r-none"
|
|
4410
|
+
),
|
|
4411
|
+
disabled,
|
|
4412
|
+
readOnly,
|
|
4413
|
+
...props
|
|
4414
|
+
}
|
|
4415
|
+
),
|
|
4416
|
+
endAddon && /* @__PURE__ */ u(Addon, { error: hasError, children: endAddon })
|
|
4417
|
+
] }),
|
|
4418
|
+
error && /* @__PURE__ */ u("div", { className: "flex items-center gap-2 mt-2 leading-5 text-error", children: [
|
|
4419
|
+
/* @__PURE__ */ u(OctagonAlert, { size: 16, strokeWidth: 2.5 }),
|
|
4420
|
+
error
|
|
4421
|
+
] })
|
|
4422
|
+
] });
|
|
4423
|
+
}
|
|
4424
|
+
);
|
|
4425
|
+
Input.displayName = "Input";
|
|
4426
|
+
function isExternalHref(href) {
|
|
4427
|
+
try {
|
|
4428
|
+
if (href.startsWith("mailto:") || href.startsWith("tel:")) return true;
|
|
4429
|
+
const location = typeof window !== "undefined" ? window.location : void 0;
|
|
3580
4430
|
if (!location) return true;
|
|
3581
4431
|
const url = new URL(href, location.origin);
|
|
3582
4432
|
return url.origin !== location.origin;
|
|
@@ -3618,74 +4468,847 @@ const Link = forwardRef(
|
|
|
3618
4468
|
);
|
|
3619
4469
|
}
|
|
3620
4470
|
);
|
|
3621
|
-
Link.displayName = "Link";
|
|
3622
|
-
function isOfType(node, Comp) {
|
|
3623
|
-
return isValidElement(node) && node.type === Comp;
|
|
3624
|
-
}
|
|
3625
|
-
function findComponentByType(children, Comp) {
|
|
3626
|
-
return Children.toArray(children).find((child) => isOfType(child, Comp));
|
|
3627
|
-
}
|
|
3628
|
-
const ListItemLeft = ({ children, className, ...props }) => /* @__PURE__ */ u("div", { className: cn("flex items-center gap-2.5 flex-shrink-0", className), ...props, children });
|
|
3629
|
-
const ListItemContent = ({
|
|
3630
|
-
className,
|
|
3631
|
-
children,
|
|
3632
|
-
...props
|
|
3633
|
-
}) => {
|
|
3634
|
-
return /* @__PURE__ */ u("div", { className: cn("flex-1 min-w-0", className), ...props, children });
|
|
3635
|
-
};
|
|
3636
|
-
ListItemContent.displayName = "ListItem.Content";
|
|
3637
|
-
const ListItemDefaultContent = ({
|
|
3638
|
-
className,
|
|
3639
|
-
label,
|
|
3640
|
-
description,
|
|
3641
|
-
metadata,
|
|
3642
|
-
icon
|
|
3643
|
-
}) => {
|
|
3644
|
-
return /* @__PURE__ */ u(ListItemContent, { className: cn(icon && "grid grid-cols-[auto_1fr] gap-2.5 items-center", className), children: [
|
|
3645
|
-
icon && /* @__PURE__ */ u("div", { className: "flex items-center justify-center flex-shrink-0 group-data-[tone=inverse]:text-alt", children: icon }),
|
|
3646
|
-
/* @__PURE__ */ u("div", { className: "min-w-0 text-left", children: [
|
|
3647
|
-
label && /* @__PURE__ */ u("h1", { className: "truncate font-semibold group-data-[tone=inverse]:text-alt", children: label }),
|
|
3648
|
-
description && /* @__PURE__ */ u("p", { className: "truncate text-sm text-subtle group-data-[tone=inverse]:text-alt", children: description }),
|
|
3649
|
-
metadata && /* @__PURE__ */ u("p", { className: "text-xs text-subtle group-data-[tone=inverse]:text-alt", children: metadata })
|
|
3650
|
-
] })
|
|
3651
|
-
] });
|
|
3652
|
-
};
|
|
3653
|
-
const ListItemRight = ({
|
|
3654
|
-
children,
|
|
3655
|
-
className,
|
|
3656
|
-
...props
|
|
3657
|
-
}) => /* @__PURE__ */ u("div", { className: cn("flex items-center gap-5 flex-shrink-0", className), ...props, children });
|
|
3658
|
-
ListItemRight.displayName = "ListItem.Right";
|
|
3659
|
-
const ListItemRoot = ({ children, className, selected, ...props }) => {
|
|
3660
|
-
const left = findComponentByType(children, ListItemLeft);
|
|
3661
|
-
const content = findComponentByType(children, ListItemContent) ?? findComponentByType(children, ListItemDefaultContent);
|
|
3662
|
-
const right = findComponentByType(children, ListItemRight);
|
|
3663
|
-
return /* @__PURE__ */ u(
|
|
3664
|
-
"div",
|
|
3665
|
-
{
|
|
3666
|
-
className: cn(
|
|
3667
|
-
"group flex items-center px-2.5 py-1 gap-2.5",
|
|
3668
|
-
selected && "bg-surface-primary-selected text-alt",
|
|
3669
|
-
className
|
|
3670
|
-
),
|
|
3671
|
-
"data-tone": selected && "inverse",
|
|
3672
|
-
role: "listitem",
|
|
3673
|
-
...props,
|
|
3674
|
-
children: [
|
|
3675
|
-
left,
|
|
3676
|
-
content,
|
|
3677
|
-
right
|
|
3678
|
-
]
|
|
3679
|
-
}
|
|
3680
|
-
);
|
|
3681
|
-
};
|
|
3682
|
-
ListItemRoot.displayName = "ListItem";
|
|
3683
|
-
const ListItem = Object.assign(ListItemRoot, {
|
|
3684
|
-
Left: ListItemLeft,
|
|
3685
|
-
Content: ListItemContent,
|
|
3686
|
-
DefaultContent: ListItemDefaultContent,
|
|
3687
|
-
Right: ListItemRight
|
|
3688
|
-
});
|
|
4471
|
+
Link.displayName = "Link";
|
|
4472
|
+
function isOfType(node, Comp) {
|
|
4473
|
+
return isValidElement(node) && node.type === Comp;
|
|
4474
|
+
}
|
|
4475
|
+
function findComponentByType(children, Comp) {
|
|
4476
|
+
return Children.toArray(children).find((child) => isOfType(child, Comp));
|
|
4477
|
+
}
|
|
4478
|
+
const ListItemLeft = ({ children, className, ...props }) => /* @__PURE__ */ u("div", { className: cn("flex items-center gap-2.5 flex-shrink-0", className), ...props, children });
|
|
4479
|
+
const ListItemContent = ({
|
|
4480
|
+
className,
|
|
4481
|
+
children,
|
|
4482
|
+
...props
|
|
4483
|
+
}) => {
|
|
4484
|
+
return /* @__PURE__ */ u("div", { className: cn("flex-1 min-w-0", className), ...props, children });
|
|
4485
|
+
};
|
|
4486
|
+
ListItemContent.displayName = "ListItem.Content";
|
|
4487
|
+
const ListItemDefaultContent = ({
|
|
4488
|
+
className,
|
|
4489
|
+
label,
|
|
4490
|
+
description,
|
|
4491
|
+
metadata,
|
|
4492
|
+
icon
|
|
4493
|
+
}) => {
|
|
4494
|
+
return /* @__PURE__ */ u(ListItemContent, { className: cn(icon && "grid grid-cols-[auto_1fr] gap-2.5 items-center", className), children: [
|
|
4495
|
+
icon && /* @__PURE__ */ u("div", { className: "flex items-center justify-center flex-shrink-0 group-data-[tone=inverse]:text-alt", children: icon }),
|
|
4496
|
+
/* @__PURE__ */ u("div", { className: "min-w-0 text-left", children: [
|
|
4497
|
+
label && /* @__PURE__ */ u("h1", { className: "truncate font-semibold group-data-[tone=inverse]:text-alt", children: label }),
|
|
4498
|
+
description && /* @__PURE__ */ u("p", { className: "truncate text-sm text-subtle group-data-[tone=inverse]:text-alt", children: description }),
|
|
4499
|
+
metadata && /* @__PURE__ */ u("p", { className: "text-xs text-subtle group-data-[tone=inverse]:text-alt", children: metadata })
|
|
4500
|
+
] })
|
|
4501
|
+
] });
|
|
4502
|
+
};
|
|
4503
|
+
const ListItemRight = ({
|
|
4504
|
+
children,
|
|
4505
|
+
className,
|
|
4506
|
+
...props
|
|
4507
|
+
}) => /* @__PURE__ */ u("div", { className: cn("flex items-center gap-5 flex-shrink-0", className), ...props, children });
|
|
4508
|
+
ListItemRight.displayName = "ListItem.Right";
|
|
4509
|
+
const ListItemRoot = ({ children, className, selected, ...props }) => {
|
|
4510
|
+
const left = findComponentByType(children, ListItemLeft);
|
|
4511
|
+
const content = findComponentByType(children, ListItemContent) ?? findComponentByType(children, ListItemDefaultContent);
|
|
4512
|
+
const right = findComponentByType(children, ListItemRight);
|
|
4513
|
+
return /* @__PURE__ */ u(
|
|
4514
|
+
"div",
|
|
4515
|
+
{
|
|
4516
|
+
className: cn(
|
|
4517
|
+
"group flex items-center px-2.5 py-1 gap-2.5",
|
|
4518
|
+
selected && "bg-surface-primary-selected text-alt",
|
|
4519
|
+
className
|
|
4520
|
+
),
|
|
4521
|
+
"data-tone": selected && "inverse",
|
|
4522
|
+
role: "listitem",
|
|
4523
|
+
...props,
|
|
4524
|
+
children: [
|
|
4525
|
+
left,
|
|
4526
|
+
content,
|
|
4527
|
+
right
|
|
4528
|
+
]
|
|
4529
|
+
}
|
|
4530
|
+
);
|
|
4531
|
+
};
|
|
4532
|
+
ListItemRoot.displayName = "ListItem";
|
|
4533
|
+
const ListItem = Object.assign(ListItemRoot, {
|
|
4534
|
+
Left: ListItemLeft,
|
|
4535
|
+
Content: ListItemContent,
|
|
4536
|
+
DefaultContent: ListItemDefaultContent,
|
|
4537
|
+
Right: ListItemRight
|
|
4538
|
+
});
|
|
4539
|
+
const EMPTY_SELECTION = [];
|
|
4540
|
+
const ListboxRoot = ({
|
|
4541
|
+
baseId,
|
|
4542
|
+
selection: controlledSelection,
|
|
4543
|
+
defaultSelection = EMPTY_SELECTION,
|
|
4544
|
+
onSelectionChange,
|
|
4545
|
+
active: controlledActive,
|
|
4546
|
+
defaultActive,
|
|
4547
|
+
setActive,
|
|
4548
|
+
selectionMode = "single",
|
|
4549
|
+
focusable = true,
|
|
4550
|
+
disabled,
|
|
4551
|
+
children,
|
|
4552
|
+
keyHandler
|
|
4553
|
+
}) => {
|
|
4554
|
+
const listboxBaseId = baseId ?? usePrefixedId();
|
|
4555
|
+
const [uncontrolledSelection, setUncontrolledSelection] = useState(() => new Set(defaultSelection));
|
|
4556
|
+
const isSelectionControlled = controlledSelection !== void 0;
|
|
4557
|
+
const selectionSet = useMemo(
|
|
4558
|
+
() => isSelectionControlled ? new Set(controlledSelection) : uncontrolledSelection,
|
|
4559
|
+
[isSelectionControlled, controlledSelection, uncontrolledSelection]
|
|
4560
|
+
);
|
|
4561
|
+
const [uncontrolledActive, setUncontrolledActive] = useState(defaultActive);
|
|
4562
|
+
const isActiveControlled = controlledActive !== void 0;
|
|
4563
|
+
const active = isActiveControlled ? controlledActive : uncontrolledActive;
|
|
4564
|
+
const updateActive = useCallback(
|
|
4565
|
+
(id) => {
|
|
4566
|
+
if (!isActiveControlled) {
|
|
4567
|
+
setUncontrolledActive(id);
|
|
4568
|
+
}
|
|
4569
|
+
setActive?.(id);
|
|
4570
|
+
},
|
|
4571
|
+
[isActiveControlled, setActive]
|
|
4572
|
+
);
|
|
4573
|
+
const toggleValue = useCallback(
|
|
4574
|
+
(value) => {
|
|
4575
|
+
const isSelected = selectionSet.has(value);
|
|
4576
|
+
const newSet = new Set(selectionSet);
|
|
4577
|
+
if (selectionMode === "single") {
|
|
4578
|
+
newSet.clear();
|
|
4579
|
+
if (!isSelected) {
|
|
4580
|
+
newSet.add(value);
|
|
4581
|
+
}
|
|
4582
|
+
} else {
|
|
4583
|
+
if (isSelected) {
|
|
4584
|
+
newSet.delete(value);
|
|
4585
|
+
} else {
|
|
4586
|
+
newSet.add(value);
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
if (!isSelectionControlled) {
|
|
4590
|
+
setUncontrolledSelection(newSet);
|
|
4591
|
+
}
|
|
4592
|
+
onSelectionChange?.(Array.from(newSet));
|
|
4593
|
+
updateActive(value);
|
|
4594
|
+
},
|
|
4595
|
+
[selectionSet, selectionMode, isSelectionControlled, onSelectionChange, updateActive]
|
|
4596
|
+
);
|
|
4597
|
+
const contextValue = useMemo(
|
|
4598
|
+
() => ({
|
|
4599
|
+
active,
|
|
4600
|
+
selection: selectionSet,
|
|
4601
|
+
selectionMode,
|
|
4602
|
+
focusable,
|
|
4603
|
+
disabled,
|
|
4604
|
+
setActive: updateActive,
|
|
4605
|
+
toggleValue,
|
|
4606
|
+
baseId: listboxBaseId,
|
|
4607
|
+
keyHandler
|
|
4608
|
+
}),
|
|
4609
|
+
[active, selectionSet, selectionMode, focusable, disabled, updateActive, toggleValue, listboxBaseId, keyHandler]
|
|
4610
|
+
);
|
|
4611
|
+
return /* @__PURE__ */ u(ListboxProvider, { value: contextValue, children });
|
|
4612
|
+
};
|
|
4613
|
+
ListboxRoot.displayName = "ListboxRoot";
|
|
4614
|
+
const ListboxContent = forwardRef(
|
|
4615
|
+
({ className, label, children, ...props }, ref) => {
|
|
4616
|
+
const {
|
|
4617
|
+
active,
|
|
4618
|
+
disabled,
|
|
4619
|
+
setActive,
|
|
4620
|
+
toggleValue,
|
|
4621
|
+
baseId,
|
|
4622
|
+
selectionMode,
|
|
4623
|
+
keyHandler,
|
|
4624
|
+
focusable = true
|
|
4625
|
+
} = useListbox();
|
|
4626
|
+
const innerRef = useRef(null);
|
|
4627
|
+
const getItems = useCallback(() => {
|
|
4628
|
+
const container = innerRef.current;
|
|
4629
|
+
if (!container) {
|
|
4630
|
+
return [];
|
|
4631
|
+
}
|
|
4632
|
+
const optionNodes = container.querySelectorAll('[role="option"][data-value]');
|
|
4633
|
+
return Array.from(optionNodes).map((node) => node.dataset.value).filter((v) => v !== void 0);
|
|
4634
|
+
}, []);
|
|
4635
|
+
const moveActive = useCallback(
|
|
4636
|
+
(delta) => {
|
|
4637
|
+
const items = getItems();
|
|
4638
|
+
if (!items.length) {
|
|
4639
|
+
return;
|
|
4640
|
+
}
|
|
4641
|
+
const currentIndex = active ? items.indexOf(active) : -1;
|
|
4642
|
+
const newIndex = Math.max(0, Math.min(items.length - 1, currentIndex + delta));
|
|
4643
|
+
setActive(items[newIndex]);
|
|
4644
|
+
},
|
|
4645
|
+
[active, setActive, getItems]
|
|
4646
|
+
);
|
|
4647
|
+
const handleKeyDown = keyHandler ?? useCallback(
|
|
4648
|
+
(e) => {
|
|
4649
|
+
if (disabled) {
|
|
4650
|
+
return;
|
|
4651
|
+
}
|
|
4652
|
+
const items = getItems();
|
|
4653
|
+
if (!items.length) {
|
|
4654
|
+
return;
|
|
4655
|
+
}
|
|
4656
|
+
if (e.key === "ArrowDown") {
|
|
4657
|
+
e.preventDefault();
|
|
4658
|
+
moveActive(1);
|
|
4659
|
+
} else if (e.key === "ArrowUp") {
|
|
4660
|
+
e.preventDefault();
|
|
4661
|
+
moveActive(-1);
|
|
4662
|
+
} else if (e.key === "Home") {
|
|
4663
|
+
e.preventDefault();
|
|
4664
|
+
setActive(items[0]);
|
|
4665
|
+
} else if (e.key === "End") {
|
|
4666
|
+
e.preventDefault();
|
|
4667
|
+
setActive(items[items.length - 1]);
|
|
4668
|
+
} else if (e.key === " " || e.key === "Enter") {
|
|
4669
|
+
e.preventDefault();
|
|
4670
|
+
if (active) {
|
|
4671
|
+
toggleValue(active);
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
},
|
|
4675
|
+
[disabled, getItems, active, moveActive, toggleValue, setActive]
|
|
4676
|
+
);
|
|
4677
|
+
useEffect(() => {
|
|
4678
|
+
if (!active || !innerRef.current) {
|
|
4679
|
+
return;
|
|
4680
|
+
}
|
|
4681
|
+
const el = innerRef.current.querySelector(`#${baseId}-listbox-option-${active}`);
|
|
4682
|
+
if (el) {
|
|
4683
|
+
el.scrollIntoView({
|
|
4684
|
+
block: "nearest",
|
|
4685
|
+
behavior: "auto"
|
|
4686
|
+
});
|
|
4687
|
+
}
|
|
4688
|
+
}, [active, baseId]);
|
|
4689
|
+
return /* @__PURE__ */ u(
|
|
4690
|
+
"div",
|
|
4691
|
+
{
|
|
4692
|
+
ref: useComposedRefs(ref, innerRef),
|
|
4693
|
+
id: `${baseId}-listbox`,
|
|
4694
|
+
className: cn(
|
|
4695
|
+
"flex flex-col items-start grow shrink-0 basis-0",
|
|
4696
|
+
"max-h-100 overflow-y-auto",
|
|
4697
|
+
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50",
|
|
4698
|
+
disabled && "pointer-events-none select-none opacity-30",
|
|
4699
|
+
className
|
|
4700
|
+
),
|
|
4701
|
+
role: "listbox",
|
|
4702
|
+
"aria-disabled": disabled,
|
|
4703
|
+
"aria-label": label,
|
|
4704
|
+
"aria-multiselectable": selectionMode === "multiple" ? true : void 0,
|
|
4705
|
+
"aria-activedescendant": active ? `${baseId}-listbox-option-${active}` : void 0,
|
|
4706
|
+
tabIndex: focusable && !disabled ? 0 : -1,
|
|
4707
|
+
onKeyDown: handleKeyDown,
|
|
4708
|
+
...props,
|
|
4709
|
+
children
|
|
4710
|
+
}
|
|
4711
|
+
);
|
|
4712
|
+
}
|
|
4713
|
+
);
|
|
4714
|
+
ListboxContent.displayName = "ListboxContent";
|
|
4715
|
+
const listboxItemVariants = cva("flex w-full items-center px-4.5 py-1 gap-x-2.5 cursor-pointer", {
|
|
4716
|
+
variants: {
|
|
4717
|
+
selected: {
|
|
4718
|
+
true: "bg-surface-primary-selected text-alt hover:bg-surface-primary-selected-hover",
|
|
4719
|
+
false: "hover:bg-surface-primary-hover"
|
|
4720
|
+
},
|
|
4721
|
+
active: {
|
|
4722
|
+
true: "bg-surface-primary-hover",
|
|
4723
|
+
false: ""
|
|
4724
|
+
}
|
|
4725
|
+
},
|
|
4726
|
+
compoundVariants: [
|
|
4727
|
+
{
|
|
4728
|
+
selected: true,
|
|
4729
|
+
active: true,
|
|
4730
|
+
class: "bg-surface-primary-selected-hover"
|
|
4731
|
+
}
|
|
4732
|
+
],
|
|
4733
|
+
defaultVariants: {
|
|
4734
|
+
selected: false,
|
|
4735
|
+
active: false
|
|
4736
|
+
}
|
|
4737
|
+
});
|
|
4738
|
+
const ListboxItem = ({ value, children, className, ...props }) => {
|
|
4739
|
+
const ctx = useListbox();
|
|
4740
|
+
const { disabled, toggleValue, baseId } = ctx;
|
|
4741
|
+
const isSelected = ctx.selection.has(value);
|
|
4742
|
+
const isActive = ctx.active === value;
|
|
4743
|
+
const handleClick = useCallback(() => {
|
|
4744
|
+
if (!disabled) {
|
|
4745
|
+
toggleValue(value);
|
|
4746
|
+
}
|
|
4747
|
+
}, [disabled, toggleValue, value]);
|
|
4748
|
+
return (
|
|
4749
|
+
// ARIA listbox pattern: options are not individually focusable
|
|
4750
|
+
// Parent listbox handles all keyboard interactions via aria-activedescendant
|
|
4751
|
+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
|
|
4752
|
+
/* @__PURE__ */ u(
|
|
4753
|
+
"div",
|
|
4754
|
+
{
|
|
4755
|
+
id: `${baseId}-listbox-option-${value}`,
|
|
4756
|
+
className: cn(listboxItemVariants({ selected: isSelected, active: isActive }), className),
|
|
4757
|
+
role: "option",
|
|
4758
|
+
"aria-selected": isSelected,
|
|
4759
|
+
"data-value": value,
|
|
4760
|
+
"data-active": isActive || void 0,
|
|
4761
|
+
onClick: handleClick,
|
|
4762
|
+
...props,
|
|
4763
|
+
children
|
|
4764
|
+
}
|
|
4765
|
+
)
|
|
4766
|
+
);
|
|
4767
|
+
};
|
|
4768
|
+
ListboxItem.displayName = "ListboxItem";
|
|
4769
|
+
const Listbox = Object.assign(ListboxRoot, {
|
|
4770
|
+
Root: ListboxRoot,
|
|
4771
|
+
Content: ListboxContent,
|
|
4772
|
+
Item: ListboxItem
|
|
4773
|
+
});
|
|
4774
|
+
const MenuRoot = ({
|
|
4775
|
+
open: controlledOpen,
|
|
4776
|
+
defaultOpen = false,
|
|
4777
|
+
onOpenChange,
|
|
4778
|
+
children
|
|
4779
|
+
}) => {
|
|
4780
|
+
const isControlled = controlledOpen !== void 0;
|
|
4781
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
|
|
4782
|
+
const open = isControlled ? controlledOpen : uncontrolledOpen;
|
|
4783
|
+
const setOpen = useCallback(
|
|
4784
|
+
(next) => {
|
|
4785
|
+
if (!isControlled) {
|
|
4786
|
+
setUncontrolledOpen(next);
|
|
4787
|
+
}
|
|
4788
|
+
onOpenChange?.(next);
|
|
4789
|
+
},
|
|
4790
|
+
[isControlled, onOpenChange]
|
|
4791
|
+
);
|
|
4792
|
+
const [active, setActive] = useState(void 0);
|
|
4793
|
+
const itemsRef = useRef(/* @__PURE__ */ new Map());
|
|
4794
|
+
const triggerRef = useRef(null);
|
|
4795
|
+
const triggerId = usePrefixedId(void 0, "menu-trigger");
|
|
4796
|
+
const menuId = usePrefixedId(void 0, "menu");
|
|
4797
|
+
const registerItem = useCallback((id, disabled = false) => {
|
|
4798
|
+
itemsRef.current.set(id, { disabled });
|
|
4799
|
+
}, []);
|
|
4800
|
+
const unregisterItem = useCallback((id) => {
|
|
4801
|
+
itemsRef.current.delete(id);
|
|
4802
|
+
}, []);
|
|
4803
|
+
const getItems = useCallback(() => {
|
|
4804
|
+
return Array.from(itemsRef.current.keys());
|
|
4805
|
+
}, []);
|
|
4806
|
+
const isItemDisabled = useCallback((id) => {
|
|
4807
|
+
return itemsRef.current.get(id)?.disabled ?? false;
|
|
4808
|
+
}, []);
|
|
4809
|
+
const value = useMemo(
|
|
4810
|
+
() => ({
|
|
4811
|
+
open,
|
|
4812
|
+
setOpen,
|
|
4813
|
+
active,
|
|
4814
|
+
setActive,
|
|
4815
|
+
registerItem,
|
|
4816
|
+
unregisterItem,
|
|
4817
|
+
getItems,
|
|
4818
|
+
isItemDisabled,
|
|
4819
|
+
triggerId,
|
|
4820
|
+
menuId,
|
|
4821
|
+
triggerRef
|
|
4822
|
+
}),
|
|
4823
|
+
[open, setOpen, active, setActive, registerItem, unregisterItem, getItems, isItemDisabled, triggerId, menuId]
|
|
4824
|
+
);
|
|
4825
|
+
useEffect(() => {
|
|
4826
|
+
if (!open && triggerRef.current) {
|
|
4827
|
+
triggerRef.current.focus();
|
|
4828
|
+
}
|
|
4829
|
+
}, [open]);
|
|
4830
|
+
return /* @__PURE__ */ u(MenuProvider, { value, children });
|
|
4831
|
+
};
|
|
4832
|
+
MenuRoot.displayName = "Menu.Root";
|
|
4833
|
+
const MenuTrigger = forwardRef(
|
|
4834
|
+
({ asChild, onClick, onKeyDown, className, children, ...props }, ref) => {
|
|
4835
|
+
const { open, setOpen, triggerId, menuId, triggerRef } = useMenu();
|
|
4836
|
+
const handleClick = (e) => {
|
|
4837
|
+
onClick?.(e);
|
|
4838
|
+
setOpen(!open);
|
|
4839
|
+
};
|
|
4840
|
+
const handleKeyDown = (e) => {
|
|
4841
|
+
onKeyDown?.(e);
|
|
4842
|
+
if (e.key === "ArrowDown" && !open) {
|
|
4843
|
+
e.preventDefault();
|
|
4844
|
+
setOpen(true);
|
|
4845
|
+
}
|
|
4846
|
+
};
|
|
4847
|
+
const Comp = asChild ? Slot : "button";
|
|
4848
|
+
return /* @__PURE__ */ u(
|
|
4849
|
+
Comp,
|
|
4850
|
+
{
|
|
4851
|
+
ref: useComposedRefs(ref, triggerRef),
|
|
4852
|
+
id: triggerId,
|
|
4853
|
+
type: asChild ? void 0 : "button",
|
|
4854
|
+
"aria-haspopup": "menu",
|
|
4855
|
+
"aria-expanded": open,
|
|
4856
|
+
"aria-controls": open ? menuId : void 0,
|
|
4857
|
+
onClick: handleClick,
|
|
4858
|
+
onKeyDown: handleKeyDown,
|
|
4859
|
+
className,
|
|
4860
|
+
...props,
|
|
4861
|
+
children
|
|
4862
|
+
}
|
|
4863
|
+
);
|
|
4864
|
+
}
|
|
4865
|
+
);
|
|
4866
|
+
MenuTrigger.displayName = "Menu.Trigger";
|
|
4867
|
+
const MenuPortal = ({ container, forceMount, children }) => {
|
|
4868
|
+
const { open } = useMenu();
|
|
4869
|
+
const [mounted, setMounted] = useState(false);
|
|
4870
|
+
useEffect(() => {
|
|
4871
|
+
setMounted(true);
|
|
4872
|
+
}, []);
|
|
4873
|
+
if (!mounted || !forceMount && !open) {
|
|
4874
|
+
return null;
|
|
4875
|
+
}
|
|
4876
|
+
return createPortal(children, container ?? document.body);
|
|
4877
|
+
};
|
|
4878
|
+
MenuPortal.displayName = "Menu.Portal";
|
|
4879
|
+
const VIEWPORT_PADDING = 10;
|
|
4880
|
+
const MenuContent = forwardRef(
|
|
4881
|
+
({
|
|
4882
|
+
forceMount,
|
|
4883
|
+
align = "start",
|
|
4884
|
+
loop = true,
|
|
4885
|
+
onEscapeKeyDown,
|
|
4886
|
+
onPointerDownOutside,
|
|
4887
|
+
onInteractOutside,
|
|
4888
|
+
className,
|
|
4889
|
+
children,
|
|
4890
|
+
...props
|
|
4891
|
+
}, ref) => {
|
|
4892
|
+
const { open, setOpen, active, setActive, getItems, isItemDisabled, menuId, triggerRef } = useMenu();
|
|
4893
|
+
const contentRef = useRef(null);
|
|
4894
|
+
const composedRefs = useComposedRefs(ref, contentRef);
|
|
4895
|
+
const [position, setPosition] = useState(
|
|
4896
|
+
null
|
|
4897
|
+
);
|
|
4898
|
+
useEffect(() => {
|
|
4899
|
+
if (!open || !triggerRef?.current || !contentRef.current) {
|
|
4900
|
+
return;
|
|
4901
|
+
}
|
|
4902
|
+
const updatePosition = () => {
|
|
4903
|
+
if (!triggerRef.current || !contentRef.current) return;
|
|
4904
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
4905
|
+
const contentRect = contentRef.current.getBoundingClientRect();
|
|
4906
|
+
const viewportWidth = window.innerWidth;
|
|
4907
|
+
const viewportHeight = window.innerHeight;
|
|
4908
|
+
let top = triggerRect.bottom;
|
|
4909
|
+
if (top + contentRect.height > viewportHeight) {
|
|
4910
|
+
top = triggerRect.top - contentRect.height;
|
|
4911
|
+
}
|
|
4912
|
+
if (top < 0) {
|
|
4913
|
+
top = VIEWPORT_PADDING;
|
|
4914
|
+
}
|
|
4915
|
+
if (align === "start") {
|
|
4916
|
+
let left = triggerRect.left;
|
|
4917
|
+
if (left + contentRect.width > viewportWidth) {
|
|
4918
|
+
left = triggerRect.right - contentRect.width;
|
|
4919
|
+
}
|
|
4920
|
+
if (left < 0) {
|
|
4921
|
+
left = VIEWPORT_PADDING;
|
|
4922
|
+
}
|
|
4923
|
+
setPosition({ top, left });
|
|
4924
|
+
} else {
|
|
4925
|
+
let right = viewportWidth - triggerRect.right;
|
|
4926
|
+
if (triggerRect.right - contentRect.width < 0) {
|
|
4927
|
+
right = viewportWidth - (triggerRect.left + contentRect.width);
|
|
4928
|
+
}
|
|
4929
|
+
if (right < 0) {
|
|
4930
|
+
right = VIEWPORT_PADDING;
|
|
4931
|
+
}
|
|
4932
|
+
setPosition({ top, right });
|
|
4933
|
+
}
|
|
4934
|
+
};
|
|
4935
|
+
updatePosition();
|
|
4936
|
+
window.addEventListener("resize", updatePosition);
|
|
4937
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
4938
|
+
return () => {
|
|
4939
|
+
window.removeEventListener("resize", updatePosition);
|
|
4940
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
4941
|
+
};
|
|
4942
|
+
}, [open, triggerRef, align]);
|
|
4943
|
+
useEffect(() => {
|
|
4944
|
+
if (open && contentRef.current) {
|
|
4945
|
+
contentRef.current.focus();
|
|
4946
|
+
const items = getItems();
|
|
4947
|
+
const firstSelectableItem = items.find((itemId) => !isItemDisabled(itemId));
|
|
4948
|
+
if (firstSelectableItem) {
|
|
4949
|
+
setActive(firstSelectableItem);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
}, [open, getItems, isItemDisabled, setActive]);
|
|
4953
|
+
const moveActive = useCallback(
|
|
4954
|
+
(delta) => {
|
|
4955
|
+
const items = getItems();
|
|
4956
|
+
if (!items.length) {
|
|
4957
|
+
return;
|
|
4958
|
+
}
|
|
4959
|
+
const currentIndex = active ? items.indexOf(active) : -1;
|
|
4960
|
+
let newIndex;
|
|
4961
|
+
let attempts = 0;
|
|
4962
|
+
const maxAttempts = items.length;
|
|
4963
|
+
if (currentIndex === -1) {
|
|
4964
|
+
newIndex = delta > 0 ? 0 : items.length - 1;
|
|
4965
|
+
} else {
|
|
4966
|
+
newIndex = currentIndex + delta;
|
|
4967
|
+
}
|
|
4968
|
+
while (attempts < maxAttempts) {
|
|
4969
|
+
if (loop) {
|
|
4970
|
+
if (newIndex < 0) {
|
|
4971
|
+
newIndex = items.length - 1;
|
|
4972
|
+
} else if (newIndex >= items.length) {
|
|
4973
|
+
newIndex = 0;
|
|
4974
|
+
}
|
|
4975
|
+
} else {
|
|
4976
|
+
if (newIndex < 0 || newIndex >= items.length) {
|
|
4977
|
+
return;
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
if (!isItemDisabled(items[newIndex])) {
|
|
4981
|
+
setActive(items[newIndex]);
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
4984
|
+
newIndex += delta;
|
|
4985
|
+
attempts++;
|
|
4986
|
+
}
|
|
4987
|
+
},
|
|
4988
|
+
[getItems, active, setActive, loop, isItemDisabled]
|
|
4989
|
+
);
|
|
4990
|
+
const handleKeyDown = useCallback(
|
|
4991
|
+
(e) => {
|
|
4992
|
+
const items = getItems();
|
|
4993
|
+
if (!items.length) {
|
|
4994
|
+
return;
|
|
4995
|
+
}
|
|
4996
|
+
switch (e.key) {
|
|
4997
|
+
case "ArrowDown":
|
|
4998
|
+
e.preventDefault();
|
|
4999
|
+
moveActive(1);
|
|
5000
|
+
break;
|
|
5001
|
+
case "ArrowUp":
|
|
5002
|
+
e.preventDefault();
|
|
5003
|
+
moveActive(-1);
|
|
5004
|
+
break;
|
|
5005
|
+
case "Home":
|
|
5006
|
+
e.preventDefault();
|
|
5007
|
+
{
|
|
5008
|
+
const firstEnabled = items.find((id) => !isItemDisabled(id));
|
|
5009
|
+
if (firstEnabled) {
|
|
5010
|
+
setActive(firstEnabled);
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
break;
|
|
5014
|
+
case "End":
|
|
5015
|
+
e.preventDefault();
|
|
5016
|
+
{
|
|
5017
|
+
const lastEnabled = [...items].reverse().find((id) => !isItemDisabled(id));
|
|
5018
|
+
if (lastEnabled) {
|
|
5019
|
+
setActive(lastEnabled);
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
5022
|
+
break;
|
|
5023
|
+
case "Enter":
|
|
5024
|
+
case " ":
|
|
5025
|
+
e.preventDefault();
|
|
5026
|
+
if (active && !isItemDisabled(active)) {
|
|
5027
|
+
const itemElement = document.getElementById(active);
|
|
5028
|
+
if (itemElement) {
|
|
5029
|
+
itemElement.click();
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
break;
|
|
5033
|
+
case "Escape":
|
|
5034
|
+
e.preventDefault();
|
|
5035
|
+
onEscapeKeyDown?.(e);
|
|
5036
|
+
setOpen(false);
|
|
5037
|
+
break;
|
|
5038
|
+
case "Tab":
|
|
5039
|
+
setOpen(false);
|
|
5040
|
+
break;
|
|
5041
|
+
}
|
|
5042
|
+
},
|
|
5043
|
+
[getItems, active, moveActive, setActive, isItemDisabled, onEscapeKeyDown, setOpen]
|
|
5044
|
+
);
|
|
5045
|
+
const handlePointerDownOutside = useCallback(
|
|
5046
|
+
(e) => {
|
|
5047
|
+
const target = e.target;
|
|
5048
|
+
if (triggerRef?.current?.contains(target)) {
|
|
5049
|
+
return;
|
|
5050
|
+
}
|
|
5051
|
+
if (contentRef.current && !contentRef.current.contains(target)) {
|
|
5052
|
+
onPointerDownOutside?.(e);
|
|
5053
|
+
onInteractOutside?.(e);
|
|
5054
|
+
if (!e.defaultPrevented) {
|
|
5055
|
+
setOpen(false);
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
},
|
|
5059
|
+
[onPointerDownOutside, onInteractOutside, setOpen, triggerRef]
|
|
5060
|
+
);
|
|
5061
|
+
useEffect(() => {
|
|
5062
|
+
if (!open) {
|
|
5063
|
+
return;
|
|
5064
|
+
}
|
|
5065
|
+
document.addEventListener("pointerdown", handlePointerDownOutside);
|
|
5066
|
+
return () => document.removeEventListener("pointerdown", handlePointerDownOutside);
|
|
5067
|
+
}, [open, handlePointerDownOutside]);
|
|
5068
|
+
useEffect(() => {
|
|
5069
|
+
if (!active || !contentRef.current) {
|
|
5070
|
+
return;
|
|
5071
|
+
}
|
|
5072
|
+
const el = contentRef.current.querySelector(`#${active}`);
|
|
5073
|
+
if (el) {
|
|
5074
|
+
el.scrollIntoView({
|
|
5075
|
+
block: "nearest",
|
|
5076
|
+
behavior: "auto"
|
|
5077
|
+
});
|
|
5078
|
+
}
|
|
5079
|
+
}, [active]);
|
|
5080
|
+
if (!forceMount && !open) {
|
|
5081
|
+
return null;
|
|
5082
|
+
}
|
|
5083
|
+
return /* @__PURE__ */ u(
|
|
5084
|
+
"div",
|
|
5085
|
+
{
|
|
5086
|
+
ref: composedRefs,
|
|
5087
|
+
id: menuId,
|
|
5088
|
+
role: "menu",
|
|
5089
|
+
"aria-orientation": "vertical",
|
|
5090
|
+
"aria-activedescendant": active,
|
|
5091
|
+
tabIndex: -1,
|
|
5092
|
+
"data-state": open ? "open" : "closed",
|
|
5093
|
+
className: cn(
|
|
5094
|
+
"fixed z-40 flex flex-col items-start w-fit py-2.5 mt-2.5 overflow-hidden",
|
|
5095
|
+
"rounded-sm border border-bdr-subtle bg-surface-neutral shadow-lg outline-none",
|
|
5096
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
5097
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
5098
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
5099
|
+
!position && "opacity-0 pointer-events-none",
|
|
5100
|
+
className
|
|
5101
|
+
),
|
|
5102
|
+
style: { ...position },
|
|
5103
|
+
onKeyDown: handleKeyDown,
|
|
5104
|
+
...props,
|
|
5105
|
+
children
|
|
5106
|
+
}
|
|
5107
|
+
);
|
|
5108
|
+
}
|
|
5109
|
+
);
|
|
5110
|
+
MenuContent.displayName = "Menu.Content";
|
|
5111
|
+
const menuItemVariants = cva("flex w-full items-center px-4.5 py-2.5 gap-x-1.25 cursor-pointer text-sm outline-none", {
|
|
5112
|
+
variants: {
|
|
5113
|
+
active: {
|
|
5114
|
+
true: "bg-surface-primary-selected",
|
|
5115
|
+
false: ""
|
|
5116
|
+
},
|
|
5117
|
+
disabled: {
|
|
5118
|
+
true: "hover:bg-transparent opacity-30 select-none pointer-events-none",
|
|
5119
|
+
false: ""
|
|
5120
|
+
}
|
|
5121
|
+
},
|
|
5122
|
+
compoundVariants: [
|
|
5123
|
+
{
|
|
5124
|
+
active: true,
|
|
5125
|
+
disabled: false,
|
|
5126
|
+
class: "text-alt outline-none ring-3 ring-inset ring-bdr-strong"
|
|
5127
|
+
}
|
|
5128
|
+
],
|
|
5129
|
+
defaultVariants: {
|
|
5130
|
+
active: false,
|
|
5131
|
+
disabled: false
|
|
5132
|
+
}
|
|
5133
|
+
});
|
|
5134
|
+
const MenuItem = ({
|
|
5135
|
+
id: providedId,
|
|
5136
|
+
asChild = false,
|
|
5137
|
+
disabled = false,
|
|
5138
|
+
onSelect,
|
|
5139
|
+
className,
|
|
5140
|
+
children,
|
|
5141
|
+
...props
|
|
5142
|
+
}) => {
|
|
5143
|
+
const id = usePrefixedId(providedId, providedId ? void 0 : "menu-item");
|
|
5144
|
+
const { active, setActive, setOpen, registerItem, unregisterItem } = useMenu();
|
|
5145
|
+
const isActive = active === id;
|
|
5146
|
+
const isDisabled = disabled;
|
|
5147
|
+
useEffect(() => {
|
|
5148
|
+
registerItem(id, disabled);
|
|
5149
|
+
return () => unregisterItem(id);
|
|
5150
|
+
}, [id, disabled, registerItem, unregisterItem]);
|
|
5151
|
+
const handleClick = useCallback(() => {
|
|
5152
|
+
if (isDisabled) {
|
|
5153
|
+
return;
|
|
5154
|
+
}
|
|
5155
|
+
const event = new Event("select", { bubbles: true, cancelable: true });
|
|
5156
|
+
onSelect?.(event);
|
|
5157
|
+
if (!event.defaultPrevented) {
|
|
5158
|
+
setOpen(false);
|
|
5159
|
+
}
|
|
5160
|
+
}, [isDisabled, onSelect, setOpen]);
|
|
5161
|
+
const handlePointerMove = useCallback(() => {
|
|
5162
|
+
if (!isActive) {
|
|
5163
|
+
setActive(id);
|
|
5164
|
+
}
|
|
5165
|
+
}, [isActive, setActive, id]);
|
|
5166
|
+
const handlePointerLeave = useCallback(() => {
|
|
5167
|
+
setActive(void 0);
|
|
5168
|
+
}, [setActive]);
|
|
5169
|
+
const Comp = asChild ? Slot : "div";
|
|
5170
|
+
return /* @__PURE__ */ u(
|
|
5171
|
+
Comp,
|
|
5172
|
+
{
|
|
5173
|
+
id,
|
|
5174
|
+
role: "menuitem",
|
|
5175
|
+
"aria-disabled": isDisabled,
|
|
5176
|
+
"data-active": isActive || void 0,
|
|
5177
|
+
"data-disabled": isDisabled || void 0,
|
|
5178
|
+
className: cn(menuItemVariants({ active: isActive, disabled: isDisabled }), className),
|
|
5179
|
+
onClick: handleClick,
|
|
5180
|
+
onPointerMove: handlePointerMove,
|
|
5181
|
+
onPointerLeave: handlePointerLeave,
|
|
5182
|
+
...props,
|
|
5183
|
+
children
|
|
5184
|
+
}
|
|
5185
|
+
);
|
|
5186
|
+
};
|
|
5187
|
+
MenuItem.displayName = "Menu.Item";
|
|
5188
|
+
const MenuLabel = forwardRef(({ className, children, ...props }, ref) => {
|
|
5189
|
+
return /* @__PURE__ */ u("div", { ref, className: cn("px-3 py-1.5 text-xs font-semibold text-subtle", className), ...props, children });
|
|
5190
|
+
});
|
|
5191
|
+
MenuLabel.displayName = "Menu.Label";
|
|
5192
|
+
const MenuSeparator = forwardRef(({ className, ...props }, ref) => {
|
|
5193
|
+
return /* @__PURE__ */ u(
|
|
5194
|
+
"div",
|
|
5195
|
+
{
|
|
5196
|
+
ref,
|
|
5197
|
+
role: "separator",
|
|
5198
|
+
"aria-orientation": "horizontal",
|
|
5199
|
+
className: cn("my-1 h-px bg-bdr-subtle", className),
|
|
5200
|
+
...props
|
|
5201
|
+
}
|
|
5202
|
+
);
|
|
5203
|
+
});
|
|
5204
|
+
MenuSeparator.displayName = "Menu.Separator";
|
|
5205
|
+
const Menu = Object.assign(MenuRoot, {
|
|
5206
|
+
Root: MenuRoot,
|
|
5207
|
+
Trigger: MenuTrigger,
|
|
5208
|
+
Portal: MenuPortal,
|
|
5209
|
+
Content: MenuContent,
|
|
5210
|
+
Item: MenuItem,
|
|
5211
|
+
Label: MenuLabel,
|
|
5212
|
+
Separator: MenuSeparator
|
|
5213
|
+
});
|
|
5214
|
+
const SearchInput = forwardRef(
|
|
5215
|
+
({
|
|
5216
|
+
id,
|
|
5217
|
+
value,
|
|
5218
|
+
defaultValue = "",
|
|
5219
|
+
placeholder = "Search",
|
|
5220
|
+
clearLabel = "Clear",
|
|
5221
|
+
onChange,
|
|
5222
|
+
disabled,
|
|
5223
|
+
readOnly,
|
|
5224
|
+
showSearchIcon = true,
|
|
5225
|
+
showClearButton = true,
|
|
5226
|
+
className,
|
|
5227
|
+
...props
|
|
5228
|
+
}, ref) => {
|
|
5229
|
+
const inputId = usePrefixedId(unwrap(id));
|
|
5230
|
+
const [uncontrolledValue, setUncontrolledValue] = useState$1(defaultValue);
|
|
5231
|
+
const isControlled = value !== void 0;
|
|
5232
|
+
const inputValue = isControlled ? value : uncontrolledValue;
|
|
5233
|
+
const isValueSet = inputValue.length > 0;
|
|
5234
|
+
const canClear = showClearButton && isValueSet && !disabled && !readOnly;
|
|
5235
|
+
const handleChange = (e) => {
|
|
5236
|
+
const newValue = e.currentTarget.value;
|
|
5237
|
+
if (!isControlled) {
|
|
5238
|
+
setUncontrolledValue(newValue);
|
|
5239
|
+
}
|
|
5240
|
+
onChange?.(newValue);
|
|
5241
|
+
};
|
|
5242
|
+
const inputRef = useRef(null);
|
|
5243
|
+
const handleClear = () => {
|
|
5244
|
+
const newValue = "";
|
|
5245
|
+
if (!isControlled) {
|
|
5246
|
+
setUncontrolledValue(newValue);
|
|
5247
|
+
}
|
|
5248
|
+
onChange?.(newValue);
|
|
5249
|
+
inputRef.current?.focus();
|
|
5250
|
+
};
|
|
5251
|
+
return /* @__PURE__ */ u(
|
|
5252
|
+
"div",
|
|
5253
|
+
{
|
|
5254
|
+
className: cn(
|
|
5255
|
+
"relative flex items-center rounded-sm overflow-hidden",
|
|
5256
|
+
"h-12 border border-bdr-subtle focus-within:border-bdr-strong",
|
|
5257
|
+
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
|
|
5258
|
+
"transition-highlight",
|
|
5259
|
+
disabled && "pointer-events-none select-none opacity-30",
|
|
5260
|
+
className
|
|
5261
|
+
),
|
|
5262
|
+
children: [
|
|
5263
|
+
showSearchIcon && /* @__PURE__ */ u(
|
|
5264
|
+
Search,
|
|
5265
|
+
{
|
|
5266
|
+
className: "flex items-center justify-center shrink-0 w-10 text-subtle pointer-events-none",
|
|
5267
|
+
size: 20,
|
|
5268
|
+
strokeWidth: 1.5
|
|
5269
|
+
}
|
|
5270
|
+
),
|
|
5271
|
+
/* @__PURE__ */ u(
|
|
5272
|
+
"input",
|
|
5273
|
+
{
|
|
5274
|
+
ref: useComposedRefs(ref, inputRef),
|
|
5275
|
+
id: inputId,
|
|
5276
|
+
className: cn(
|
|
5277
|
+
"w-full text-base border-0",
|
|
5278
|
+
"text-main bg-surface-neutral",
|
|
5279
|
+
"placeholder:text-subtle",
|
|
5280
|
+
"focus:outline-none",
|
|
5281
|
+
"read-only:bg-surface-primary",
|
|
5282
|
+
!showSearchIcon && "pl-4.5",
|
|
5283
|
+
"pr-4"
|
|
5284
|
+
),
|
|
5285
|
+
value: inputValue,
|
|
5286
|
+
onChange: handleChange,
|
|
5287
|
+
readOnly,
|
|
5288
|
+
disabled,
|
|
5289
|
+
placeholder,
|
|
5290
|
+
"aria-label": "Search",
|
|
5291
|
+
"aria-disabled": disabled,
|
|
5292
|
+
...props
|
|
5293
|
+
}
|
|
5294
|
+
),
|
|
5295
|
+
canClear && /* @__PURE__ */ u(
|
|
5296
|
+
IconButton,
|
|
5297
|
+
{
|
|
5298
|
+
className: "flex items-center justify-center shrink-0 h-10 w-10 mr-1 text-subtle",
|
|
5299
|
+
size: "lg",
|
|
5300
|
+
icon: X,
|
|
5301
|
+
title: clearLabel,
|
|
5302
|
+
onClick: handleClear,
|
|
5303
|
+
disabled
|
|
5304
|
+
}
|
|
5305
|
+
)
|
|
5306
|
+
]
|
|
5307
|
+
}
|
|
5308
|
+
);
|
|
5309
|
+
}
|
|
5310
|
+
);
|
|
5311
|
+
SearchInput.displayName = "SearchInput";
|
|
3689
5312
|
const SelectableListItem = ({
|
|
3690
5313
|
className,
|
|
3691
5314
|
selected,
|
|
@@ -3725,6 +5348,27 @@ const SelectableListItem = ({
|
|
|
3725
5348
|
);
|
|
3726
5349
|
};
|
|
3727
5350
|
SelectableListItem.displayName = "SelectableListItem";
|
|
5351
|
+
const Separator = ({ className, label, decorative = false, ...props }) => {
|
|
5352
|
+
const ariaHidden = decorative ? "true" : void 0;
|
|
5353
|
+
if (!label) {
|
|
5354
|
+
return /* @__PURE__ */ u("hr", { "aria-hidden": ariaHidden, className: cn("w-full border-bdr-subtle", className) });
|
|
5355
|
+
}
|
|
5356
|
+
return /* @__PURE__ */ u(
|
|
5357
|
+
"div",
|
|
5358
|
+
{
|
|
5359
|
+
role: "separator",
|
|
5360
|
+
"aria-orientation": "horizontal",
|
|
5361
|
+
"aria-hidden": ariaHidden,
|
|
5362
|
+
className: cn("inline-flex w-full gap-2.5", className),
|
|
5363
|
+
...props,
|
|
5364
|
+
children: [
|
|
5365
|
+
/* @__PURE__ */ u("span", { className: "min-w-0 truncate text-subtle uppercase", children: label }),
|
|
5366
|
+
/* @__PURE__ */ u("span", { className: "min-w-6 flex-1 border-b-1 border-bdr-subtle" })
|
|
5367
|
+
]
|
|
5368
|
+
}
|
|
5369
|
+
);
|
|
5370
|
+
};
|
|
5371
|
+
Separator.displayName = "Separator";
|
|
3728
5372
|
const oppositeSide = {
|
|
3729
5373
|
top: "bottom",
|
|
3730
5374
|
bottom: "top",
|
|
@@ -3924,7 +5568,7 @@ function Tooltip({
|
|
|
3924
5568
|
children
|
|
3925
5569
|
}
|
|
3926
5570
|
),
|
|
3927
|
-
isOpen && hasContent && createPortal(
|
|
5571
|
+
isOpen && hasContent && createPortal$1(
|
|
3928
5572
|
/* @__PURE__ */ u(
|
|
3929
5573
|
TooltipContent,
|
|
3930
5574
|
{
|
|
@@ -3939,299 +5583,14 @@ function Tooltip({
|
|
|
3939
5583
|
)
|
|
3940
5584
|
] });
|
|
3941
5585
|
}
|
|
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
5586
|
export {
|
|
5587
|
+
Avatar,
|
|
5588
|
+
AvatarProvider,
|
|
4233
5589
|
Button,
|
|
4234
5590
|
Checkbox,
|
|
5591
|
+
Combobox,
|
|
5592
|
+
ComboboxProvider,
|
|
5593
|
+
Dialog,
|
|
4235
5594
|
DialogProvider,
|
|
4236
5595
|
IconButton,
|
|
4237
5596
|
IdProvider,
|
|
@@ -4239,12 +5598,22 @@ export {
|
|
|
4239
5598
|
Link,
|
|
4240
5599
|
ListItem,
|
|
4241
5600
|
Listbox,
|
|
5601
|
+
ListboxProvider,
|
|
5602
|
+
Menu,
|
|
5603
|
+
MenuProvider,
|
|
4242
5604
|
SearchInput,
|
|
4243
5605
|
SelectableListItem,
|
|
5606
|
+
Separator,
|
|
4244
5607
|
Tooltip,
|
|
4245
5608
|
cn,
|
|
5609
|
+
setRef,
|
|
4246
5610
|
unwrap,
|
|
5611
|
+
useAvatar,
|
|
5612
|
+
useCombobox,
|
|
5613
|
+
useComposedRefs,
|
|
4247
5614
|
useDialog,
|
|
5615
|
+
useListbox,
|
|
5616
|
+
useMenu,
|
|
4248
5617
|
usePrefixedId,
|
|
4249
5618
|
useScrollLock
|
|
4250
5619
|
};
|