@enonic/ui 0.12.1 → 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 -5
- package/dist/enonic-ui.es.js +913 -107
- 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/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/index.d.ts +2 -0
- package/dist/types/components/listbox/listbox.d.ts +5 -2
- 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/providers/combobox-provider.d.ts +23 -0
- package/dist/types/providers/id-provider.d.ts +7 -1
- package/dist/types/providers/index.d.ts +2 -0
- package/dist/types/providers/listbox-provider.d.ts +3 -4
- 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,5 +1,5 @@
|
|
|
1
1
|
import { options, Fragment } from "preact";
|
|
2
|
-
import { createContext, useContext, useId, forwardRef, useState,
|
|
2
|
+
import { createContext, useContext, useId, useCallback, forwardRef, useState, useMemo, useEffect, createElement, useRef, isValidElement, Children, createPortal as createPortal$1 } from "react";
|
|
3
3
|
import { Slot, Root } from "@radix-ui/react-slot";
|
|
4
4
|
import { FocusTrap } from "focus-trap-react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
@@ -25,6 +25,18 @@ const useAvatar = () => {
|
|
|
25
25
|
}
|
|
26
26
|
return ctx;
|
|
27
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
|
+
};
|
|
28
40
|
const DialogContext = createContext(void 0);
|
|
29
41
|
const DialogProvider = ({ value, children }) => {
|
|
30
42
|
return /* @__PURE__ */ u(DialogContext.Provider, { value, children });
|
|
@@ -42,14 +54,10 @@ const IdProvider = ({ children, prefix }) => {
|
|
|
42
54
|
return /* @__PURE__ */ u(IdContext.Provider, { value: { prefix }, children });
|
|
43
55
|
};
|
|
44
56
|
IdProvider.displayName = "IdProvider";
|
|
45
|
-
const usePrefixedId = (providedId) => {
|
|
46
|
-
const baseId = useId();
|
|
57
|
+
const usePrefixedId = (providedId, prefix) => {
|
|
58
|
+
const baseId = providedId ?? useId();
|
|
47
59
|
const context = useContext(IdContext);
|
|
48
|
-
|
|
49
|
-
return providedId;
|
|
50
|
-
}
|
|
51
|
-
const id = context?.prefix ? `${context.prefix}-${baseId}` : baseId;
|
|
52
|
-
return id;
|
|
60
|
+
return [context?.prefix, prefix, baseId].filter(Boolean).join("-");
|
|
53
61
|
};
|
|
54
62
|
const ListboxContext = createContext(null);
|
|
55
63
|
const ListboxProvider = ({ value, children }) => {
|
|
@@ -63,6 +71,17 @@ const useListbox = () => {
|
|
|
63
71
|
}
|
|
64
72
|
return ctx;
|
|
65
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
|
+
};
|
|
66
85
|
function r(e) {
|
|
67
86
|
var t, f2, n = "";
|
|
68
87
|
if ("string" == typeof e || "number" == typeof e) n += e;
|
|
@@ -3030,6 +3049,22 @@ const twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
|
|
|
3030
3049
|
function cn(...inputs) {
|
|
3031
3050
|
return twMerge(clsx(inputs));
|
|
3032
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
|
+
}
|
|
3033
3068
|
function isSignal(v) {
|
|
3034
3069
|
return !!(v && typeof v === "object" && "value" in v && "peek" in v && typeof v.peek === "function" && "subscribe" in v && typeof v.subscribe === "function");
|
|
3035
3070
|
}
|
|
@@ -3207,10 +3242,10 @@ const buttonVariants = cva(
|
|
|
3207
3242
|
{
|
|
3208
3243
|
variants: {
|
|
3209
3244
|
variant: {
|
|
3210
|
-
text: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-
|
|
3211
|
-
filled: "bg-btn-secondary hover:bg-btn-secondary-hover active:bg-btn-active active:text-
|
|
3212
|
-
solid: "bg-btn-tertiary text-alt hover:bg-btn-tertiary-hover active:bg-btn-active
|
|
3213
|
-
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"
|
|
3214
3249
|
},
|
|
3215
3250
|
size: {
|
|
3216
3251
|
sm: "h-9 px-3.5 gap-2 text-sm",
|
|
@@ -3377,19 +3412,27 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
3377
3412
|
* This source code is licensed under the ISC license.
|
|
3378
3413
|
* See the LICENSE file in the root directory of this source tree.
|
|
3379
3414
|
*/
|
|
3380
|
-
const __iconNode$
|
|
3415
|
+
const __iconNode$8 = [
|
|
3381
3416
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
3382
3417
|
["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
|
|
3383
3418
|
];
|
|
3384
|
-
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);
|
|
3385
3428
|
/**
|
|
3386
3429
|
* @license lucide-react v0.545.0 - ISC
|
|
3387
3430
|
*
|
|
3388
3431
|
* This source code is licensed under the ISC license.
|
|
3389
3432
|
* See the LICENSE file in the root directory of this source tree.
|
|
3390
3433
|
*/
|
|
3391
|
-
const __iconNode$6 = [["path", { d: "
|
|
3392
|
-
const
|
|
3434
|
+
const __iconNode$6 = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
|
|
3435
|
+
const ChevronDown = createLucideIcon("chevron-down", __iconNode$6);
|
|
3393
3436
|
/**
|
|
3394
3437
|
* @license lucide-react v0.545.0 - ISC
|
|
3395
3438
|
*
|
|
@@ -3644,6 +3687,328 @@ const IconButton = forwardRef(
|
|
|
3644
3687
|
}
|
|
3645
3688
|
);
|
|
3646
3689
|
IconButton.displayName = "IconButton";
|
|
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(
|
|
3879
|
+
[
|
|
3880
|
+
"flex items-center",
|
|
3881
|
+
"h-12 rounded-sm border bg-surface-neutral",
|
|
3882
|
+
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
|
|
3883
|
+
"transition-highlight"
|
|
3884
|
+
],
|
|
3885
|
+
{
|
|
3886
|
+
variants: {
|
|
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
|
|
3894
|
+
},
|
|
3895
|
+
disabled: {
|
|
3896
|
+
true: "pointer-events-none select-none opacity-30",
|
|
3897
|
+
false: null
|
|
3898
|
+
}
|
|
3899
|
+
},
|
|
3900
|
+
defaultVariants: {
|
|
3901
|
+
error: false,
|
|
3902
|
+
open: false,
|
|
3903
|
+
disabled: false
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
);
|
|
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
|
+
);
|
|
3957
|
+
}
|
|
3958
|
+
);
|
|
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;
|
|
3991
|
+
}
|
|
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
|
+
});
|
|
3647
4012
|
const useScrollLock = (lock, element) => {
|
|
3648
4013
|
useEffect(() => {
|
|
3649
4014
|
if (!lock) {
|
|
@@ -3680,13 +4045,23 @@ const DialogRoot = ({
|
|
|
3680
4045
|
};
|
|
3681
4046
|
DialogRoot.displayName = "Dialog.Root";
|
|
3682
4047
|
const DialogTrigger = forwardRef(
|
|
3683
|
-
({ children, onClick, ...props }, ref) => {
|
|
4048
|
+
({ asChild, children, onClick, ...props }, ref) => {
|
|
3684
4049
|
const { setOpen } = useDialog();
|
|
3685
4050
|
const handleClick = (e) => {
|
|
3686
4051
|
onClick?.(e);
|
|
3687
4052
|
setOpen(true);
|
|
3688
4053
|
};
|
|
3689
|
-
|
|
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
|
+
);
|
|
3690
4065
|
}
|
|
3691
4066
|
);
|
|
3692
4067
|
DialogTrigger.displayName = "Dialog.Trigger";
|
|
@@ -4161,47 +4536,9 @@ const ListItem = Object.assign(ListItemRoot, {
|
|
|
4161
4536
|
DefaultContent: ListItemDefaultContent,
|
|
4162
4537
|
Right: ListItemRight
|
|
4163
4538
|
});
|
|
4164
|
-
function setRef(ref, value) {
|
|
4165
|
-
if (!ref) return;
|
|
4166
|
-
if (typeof ref === "function") {
|
|
4167
|
-
ref(value);
|
|
4168
|
-
} else {
|
|
4169
|
-
try {
|
|
4170
|
-
ref.current = value;
|
|
4171
|
-
} catch {
|
|
4172
|
-
}
|
|
4173
|
-
}
|
|
4174
|
-
}
|
|
4175
|
-
function useComposedRefs(...refs) {
|
|
4176
|
-
return useCallback((node) => {
|
|
4177
|
-
refs.forEach((ref) => setRef(ref, node));
|
|
4178
|
-
}, refs);
|
|
4179
|
-
}
|
|
4180
4539
|
const EMPTY_SELECTION = [];
|
|
4181
|
-
const listboxItemVariants = cva("flex w-full items-center px-4.5 py-1 gap-x-2.5 cursor-pointer", {
|
|
4182
|
-
variants: {
|
|
4183
|
-
selected: {
|
|
4184
|
-
true: "bg-surface-primary-selected text-alt hover:bg-surface-primary-selected-hover",
|
|
4185
|
-
false: "hover:bg-surface-primary-hover"
|
|
4186
|
-
},
|
|
4187
|
-
active: {
|
|
4188
|
-
true: "bg-surface-primary-hover",
|
|
4189
|
-
false: ""
|
|
4190
|
-
}
|
|
4191
|
-
},
|
|
4192
|
-
compoundVariants: [
|
|
4193
|
-
{
|
|
4194
|
-
selected: true,
|
|
4195
|
-
active: true,
|
|
4196
|
-
class: "bg-surface-primary-selected-hover"
|
|
4197
|
-
}
|
|
4198
|
-
],
|
|
4199
|
-
defaultVariants: {
|
|
4200
|
-
selected: false,
|
|
4201
|
-
active: false
|
|
4202
|
-
}
|
|
4203
|
-
});
|
|
4204
4540
|
const ListboxRoot = ({
|
|
4541
|
+
baseId,
|
|
4205
4542
|
selection: controlledSelection,
|
|
4206
4543
|
defaultSelection = EMPTY_SELECTION,
|
|
4207
4544
|
onSelectionChange,
|
|
@@ -4209,10 +4546,12 @@ const ListboxRoot = ({
|
|
|
4209
4546
|
defaultActive,
|
|
4210
4547
|
setActive,
|
|
4211
4548
|
selectionMode = "single",
|
|
4549
|
+
focusable = true,
|
|
4212
4550
|
disabled,
|
|
4213
|
-
children
|
|
4551
|
+
children,
|
|
4552
|
+
keyHandler
|
|
4214
4553
|
}) => {
|
|
4215
|
-
const
|
|
4554
|
+
const listboxBaseId = baseId ?? usePrefixedId();
|
|
4216
4555
|
const [uncontrolledSelection, setUncontrolledSelection] = useState(() => new Set(defaultSelection));
|
|
4217
4556
|
const isSelectionControlled = controlledSelection !== void 0;
|
|
4218
4557
|
const selectionSet = useMemo(
|
|
@@ -4231,14 +4570,6 @@ const ListboxRoot = ({
|
|
|
4231
4570
|
},
|
|
4232
4571
|
[isActiveControlled, setActive]
|
|
4233
4572
|
);
|
|
4234
|
-
const itemsRef = useRef(/* @__PURE__ */ new Set());
|
|
4235
|
-
const registerItem = useCallback((value) => {
|
|
4236
|
-
itemsRef.current.add(value);
|
|
4237
|
-
}, []);
|
|
4238
|
-
const unregisterItem = useCallback((value) => {
|
|
4239
|
-
itemsRef.current.delete(value);
|
|
4240
|
-
}, []);
|
|
4241
|
-
const getItems = useCallback(() => Array.from(itemsRef.current), []);
|
|
4242
4573
|
const toggleValue = useCallback(
|
|
4243
4574
|
(value) => {
|
|
4244
4575
|
const isSelected = selectionSet.has(value);
|
|
@@ -4268,34 +4599,39 @@ const ListboxRoot = ({
|
|
|
4268
4599
|
active,
|
|
4269
4600
|
selection: selectionSet,
|
|
4270
4601
|
selectionMode,
|
|
4602
|
+
focusable,
|
|
4271
4603
|
disabled,
|
|
4272
4604
|
setActive: updateActive,
|
|
4273
4605
|
toggleValue,
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
getItems,
|
|
4277
|
-
listboxId
|
|
4606
|
+
baseId: listboxBaseId,
|
|
4607
|
+
keyHandler
|
|
4278
4608
|
}),
|
|
4279
|
-
[
|
|
4280
|
-
active,
|
|
4281
|
-
selectionSet,
|
|
4282
|
-
selectionMode,
|
|
4283
|
-
disabled,
|
|
4284
|
-
updateActive,
|
|
4285
|
-
toggleValue,
|
|
4286
|
-
registerItem,
|
|
4287
|
-
unregisterItem,
|
|
4288
|
-
getItems,
|
|
4289
|
-
listboxId
|
|
4290
|
-
]
|
|
4609
|
+
[active, selectionSet, selectionMode, focusable, disabled, updateActive, toggleValue, listboxBaseId, keyHandler]
|
|
4291
4610
|
);
|
|
4292
4611
|
return /* @__PURE__ */ u(ListboxProvider, { value: contextValue, children });
|
|
4293
4612
|
};
|
|
4294
4613
|
ListboxRoot.displayName = "ListboxRoot";
|
|
4295
4614
|
const ListboxContent = forwardRef(
|
|
4296
4615
|
({ className, label, children, ...props }, ref) => {
|
|
4297
|
-
const {
|
|
4616
|
+
const {
|
|
4617
|
+
active,
|
|
4618
|
+
disabled,
|
|
4619
|
+
setActive,
|
|
4620
|
+
toggleValue,
|
|
4621
|
+
baseId,
|
|
4622
|
+
selectionMode,
|
|
4623
|
+
keyHandler,
|
|
4624
|
+
focusable = true
|
|
4625
|
+
} = useListbox();
|
|
4298
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
|
+
}, []);
|
|
4299
4635
|
const moveActive = useCallback(
|
|
4300
4636
|
(delta) => {
|
|
4301
4637
|
const items = getItems();
|
|
@@ -4306,9 +4642,9 @@ const ListboxContent = forwardRef(
|
|
|
4306
4642
|
const newIndex = Math.max(0, Math.min(items.length - 1, currentIndex + delta));
|
|
4307
4643
|
setActive(items[newIndex]);
|
|
4308
4644
|
},
|
|
4309
|
-
[
|
|
4645
|
+
[active, setActive, getItems]
|
|
4310
4646
|
);
|
|
4311
|
-
const handleKeyDown = useCallback(
|
|
4647
|
+
const handleKeyDown = keyHandler ?? useCallback(
|
|
4312
4648
|
(e) => {
|
|
4313
4649
|
if (disabled) {
|
|
4314
4650
|
return;
|
|
@@ -4342,19 +4678,19 @@ const ListboxContent = forwardRef(
|
|
|
4342
4678
|
if (!active || !innerRef.current) {
|
|
4343
4679
|
return;
|
|
4344
4680
|
}
|
|
4345
|
-
const el = innerRef.current.querySelector(`#${
|
|
4681
|
+
const el = innerRef.current.querySelector(`#${baseId}-listbox-option-${active}`);
|
|
4346
4682
|
if (el) {
|
|
4347
4683
|
el.scrollIntoView({
|
|
4348
4684
|
block: "nearest",
|
|
4349
4685
|
behavior: "auto"
|
|
4350
4686
|
});
|
|
4351
4687
|
}
|
|
4352
|
-
}, [active,
|
|
4688
|
+
}, [active, baseId]);
|
|
4353
4689
|
return /* @__PURE__ */ u(
|
|
4354
4690
|
"div",
|
|
4355
4691
|
{
|
|
4356
4692
|
ref: useComposedRefs(ref, innerRef),
|
|
4357
|
-
id:
|
|
4693
|
+
id: `${baseId}-listbox`,
|
|
4358
4694
|
className: cn(
|
|
4359
4695
|
"flex flex-col items-start grow shrink-0 basis-0",
|
|
4360
4696
|
"max-h-100 overflow-y-auto",
|
|
@@ -4365,8 +4701,9 @@ const ListboxContent = forwardRef(
|
|
|
4365
4701
|
role: "listbox",
|
|
4366
4702
|
"aria-disabled": disabled,
|
|
4367
4703
|
"aria-label": label,
|
|
4368
|
-
"aria-
|
|
4369
|
-
|
|
4704
|
+
"aria-multiselectable": selectionMode === "multiple" ? true : void 0,
|
|
4705
|
+
"aria-activedescendant": active ? `${baseId}-listbox-option-${active}` : void 0,
|
|
4706
|
+
tabIndex: focusable && !disabled ? 0 : -1,
|
|
4370
4707
|
onKeyDown: handleKeyDown,
|
|
4371
4708
|
...props,
|
|
4372
4709
|
children
|
|
@@ -4375,17 +4712,34 @@ const ListboxContent = forwardRef(
|
|
|
4375
4712
|
}
|
|
4376
4713
|
);
|
|
4377
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
|
+
});
|
|
4378
4738
|
const ListboxItem = ({ value, children, className, ...props }) => {
|
|
4379
4739
|
const ctx = useListbox();
|
|
4380
|
-
const { disabled, toggleValue,
|
|
4740
|
+
const { disabled, toggleValue, baseId } = ctx;
|
|
4381
4741
|
const isSelected = ctx.selection.has(value);
|
|
4382
4742
|
const isActive = ctx.active === value;
|
|
4383
|
-
useEffect(() => {
|
|
4384
|
-
registerItem(value);
|
|
4385
|
-
return () => {
|
|
4386
|
-
unregisterItem(value);
|
|
4387
|
-
};
|
|
4388
|
-
}, [value, registerItem, unregisterItem]);
|
|
4389
4743
|
const handleClick = useCallback(() => {
|
|
4390
4744
|
if (!disabled) {
|
|
4391
4745
|
toggleValue(value);
|
|
@@ -4398,7 +4752,7 @@ const ListboxItem = ({ value, children, className, ...props }) => {
|
|
|
4398
4752
|
/* @__PURE__ */ u(
|
|
4399
4753
|
"div",
|
|
4400
4754
|
{
|
|
4401
|
-
id: `${
|
|
4755
|
+
id: `${baseId}-listbox-option-${value}`,
|
|
4402
4756
|
className: cn(listboxItemVariants({ selected: isSelected, active: isActive }), className),
|
|
4403
4757
|
role: "option",
|
|
4404
4758
|
"aria-selected": isSelected,
|
|
@@ -4417,9 +4771,448 @@ const Listbox = Object.assign(ListboxRoot, {
|
|
|
4417
4771
|
Content: ListboxContent,
|
|
4418
4772
|
Item: ListboxItem
|
|
4419
4773
|
});
|
|
4420
|
-
const
|
|
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(
|
|
4421
4881
|
({
|
|
4882
|
+
forceMount,
|
|
4883
|
+
align = "start",
|
|
4884
|
+
loop = true,
|
|
4885
|
+
onEscapeKeyDown,
|
|
4886
|
+
onPointerDownOutside,
|
|
4887
|
+
onInteractOutside,
|
|
4422
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
|
+
({
|
|
4423
5216
|
id,
|
|
4424
5217
|
value,
|
|
4425
5218
|
defaultValue = "",
|
|
@@ -4428,6 +5221,9 @@ const SearchInput = forwardRef(
|
|
|
4428
5221
|
onChange,
|
|
4429
5222
|
disabled,
|
|
4430
5223
|
readOnly,
|
|
5224
|
+
showSearchIcon = true,
|
|
5225
|
+
showClearButton = true,
|
|
5226
|
+
className,
|
|
4431
5227
|
...props
|
|
4432
5228
|
}, ref) => {
|
|
4433
5229
|
const inputId = usePrefixedId(unwrap(id));
|
|
@@ -4435,7 +5231,7 @@ const SearchInput = forwardRef(
|
|
|
4435
5231
|
const isControlled = value !== void 0;
|
|
4436
5232
|
const inputValue = isControlled ? value : uncontrolledValue;
|
|
4437
5233
|
const isValueSet = inputValue.length > 0;
|
|
4438
|
-
const canClear = isValueSet && !disabled && !readOnly;
|
|
5234
|
+
const canClear = showClearButton && isValueSet && !disabled && !readOnly;
|
|
4439
5235
|
const handleChange = (e) => {
|
|
4440
5236
|
const newValue = e.currentTarget.value;
|
|
4441
5237
|
if (!isControlled) {
|
|
@@ -4456,7 +5252,7 @@ const SearchInput = forwardRef(
|
|
|
4456
5252
|
"div",
|
|
4457
5253
|
{
|
|
4458
5254
|
className: cn(
|
|
4459
|
-
"relative flex rounded-sm overflow-hidden",
|
|
5255
|
+
"relative flex items-center rounded-sm overflow-hidden",
|
|
4460
5256
|
"h-12 border border-bdr-subtle focus-within:border-bdr-strong",
|
|
4461
5257
|
"focus-within:outline-none focus-within:ring-3 focus-within:ring-ring/50 focus-within:ring-offset-0",
|
|
4462
5258
|
"transition-highlight",
|
|
@@ -4464,10 +5260,10 @@ const SearchInput = forwardRef(
|
|
|
4464
5260
|
className
|
|
4465
5261
|
),
|
|
4466
5262
|
children: [
|
|
4467
|
-
/* @__PURE__ */ u(
|
|
5263
|
+
showSearchIcon && /* @__PURE__ */ u(
|
|
4468
5264
|
Search,
|
|
4469
5265
|
{
|
|
4470
|
-
className: "
|
|
5266
|
+
className: "flex items-center justify-center shrink-0 w-10 text-subtle pointer-events-none",
|
|
4471
5267
|
size: 20,
|
|
4472
5268
|
strokeWidth: 1.5
|
|
4473
5269
|
}
|
|
@@ -4478,11 +5274,13 @@ const SearchInput = forwardRef(
|
|
|
4478
5274
|
ref: useComposedRefs(ref, inputRef),
|
|
4479
5275
|
id: inputId,
|
|
4480
5276
|
className: cn(
|
|
4481
|
-
"w-full text-base
|
|
5277
|
+
"w-full text-base border-0",
|
|
4482
5278
|
"text-main bg-surface-neutral",
|
|
4483
5279
|
"placeholder:text-subtle",
|
|
4484
5280
|
"focus:outline-none",
|
|
4485
|
-
"read-only:bg-surface-primary"
|
|
5281
|
+
"read-only:bg-surface-primary",
|
|
5282
|
+
!showSearchIcon && "pl-4.5",
|
|
5283
|
+
"pr-4"
|
|
4486
5284
|
),
|
|
4487
5285
|
value: inputValue,
|
|
4488
5286
|
onChange: handleChange,
|
|
@@ -4497,7 +5295,7 @@ const SearchInput = forwardRef(
|
|
|
4497
5295
|
canClear && /* @__PURE__ */ u(
|
|
4498
5296
|
IconButton,
|
|
4499
5297
|
{
|
|
4500
|
-
className: "
|
|
5298
|
+
className: "flex items-center justify-center shrink-0 h-10 w-10 mr-1 text-subtle",
|
|
4501
5299
|
size: "lg",
|
|
4502
5300
|
icon: X,
|
|
4503
5301
|
title: clearLabel,
|
|
@@ -4790,6 +5588,8 @@ export {
|
|
|
4790
5588
|
AvatarProvider,
|
|
4791
5589
|
Button,
|
|
4792
5590
|
Checkbox,
|
|
5591
|
+
Combobox,
|
|
5592
|
+
ComboboxProvider,
|
|
4793
5593
|
Dialog,
|
|
4794
5594
|
DialogProvider,
|
|
4795
5595
|
IconButton,
|
|
@@ -4799,15 +5599,21 @@ export {
|
|
|
4799
5599
|
ListItem,
|
|
4800
5600
|
Listbox,
|
|
4801
5601
|
ListboxProvider,
|
|
5602
|
+
Menu,
|
|
5603
|
+
MenuProvider,
|
|
4802
5604
|
SearchInput,
|
|
4803
5605
|
SelectableListItem,
|
|
4804
5606
|
Separator,
|
|
4805
5607
|
Tooltip,
|
|
4806
5608
|
cn,
|
|
5609
|
+
setRef,
|
|
4807
5610
|
unwrap,
|
|
4808
5611
|
useAvatar,
|
|
5612
|
+
useCombobox,
|
|
5613
|
+
useComposedRefs,
|
|
4809
5614
|
useDialog,
|
|
4810
5615
|
useListbox,
|
|
5616
|
+
useMenu,
|
|
4811
5617
|
usePrefixedId,
|
|
4812
5618
|
useScrollLock
|
|
4813
5619
|
};
|