@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.
@@ -1,5 +1,5 @@
1
1
  import { options, Fragment } from "preact";
2
- import { createContext, useContext, useId, forwardRef, useState, useCallback, useMemo, useEffect, createElement, useRef, isValidElement, Children, createPortal as createPortal$1 } from "react";
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
- if (providedId) {
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-rev dark:active:text-main",
3211
- filled: "bg-btn-secondary hover:bg-btn-secondary-hover active:bg-btn-active active:text-rev dark:active:text-main",
3212
- solid: "bg-btn-tertiary text-alt hover:bg-btn-tertiary-hover active:bg-btn-active dark:active:text-main",
3213
- outline: "bg-btn-primary hover:bg-btn-primary-hover active:bg-btn-active active:text-rev dark:active:text-main border border-bdr-strong"
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$7 = [
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$7);
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: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
3392
- const Check = createLucideIcon("check", __iconNode$6);
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
- return /* @__PURE__ */ u("button", { ref, type: "button", onClick: handleClick, ...props, children });
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 listboxId = usePrefixedId();
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
- registerItem,
4275
- unregisterItem,
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 { active, disabled, getItems, setActive, toggleValue, listboxId } = useListbox();
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
- [getItems, active, setActive]
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(`#${listboxId}-option-${active}`);
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, listboxId]);
4688
+ }, [active, baseId]);
4353
4689
  return /* @__PURE__ */ u(
4354
4690
  "div",
4355
4691
  {
4356
4692
  ref: useComposedRefs(ref, innerRef),
4357
- id: listboxId,
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-activedescendant": active ? `${listboxId}-option-${active}` : void 0,
4369
- tabIndex: disabled ? -1 : 0,
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, registerItem, unregisterItem, listboxId } = ctx;
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: `${listboxId}-option-${value}`,
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 SearchInput = forwardRef(
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: "absolute left-4.5 top-1/2 -translate-y-1/2 mt-0.25 text-subtle pointer-events-none",
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 pr-1 px-12 border-0",
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: "absolute right-3 top-1/2 -translate-y-1/2 w-8 h-8 text-subtle",
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
  };