@proyecto-viviana/solid-stately 0.2.7 → 0.4.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.
Files changed (254) hide show
  1. package/README.md +30 -283
  2. package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
  3. package/dist/autocomplete/index.d.ts +1 -1
  4. package/dist/calendar/IncompleteDate.d.ts +61 -0
  5. package/dist/calendar/createCalendarState.d.ts +41 -16
  6. package/dist/calendar/createDateFieldState.d.ts +39 -9
  7. package/dist/calendar/createDatePickerState.d.ts +60 -0
  8. package/dist/calendar/createRangeCalendarState.d.ts +37 -10
  9. package/dist/calendar/createTimeFieldState.d.ts +34 -7
  10. package/dist/calendar/index.d.ts +7 -6
  11. package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
  12. package/dist/checkbox/index.d.ts +1 -1
  13. package/dist/collections/ListCollection.d.ts +1 -1
  14. package/dist/collections/createListState.d.ts +11 -8
  15. package/dist/collections/createMenuState.d.ts +7 -7
  16. package/dist/collections/createSelectionState.d.ts +22 -10
  17. package/dist/collections/getChildNodes.d.ts +11 -0
  18. package/dist/collections/index.d.ts +6 -5
  19. package/dist/collections/types.d.ts +12 -12
  20. package/dist/color/Color.d.ts +1 -1
  21. package/dist/color/createColorAreaState.d.ts +4 -2
  22. package/dist/color/createColorFieldState.d.ts +38 -12
  23. package/dist/color/createColorSliderState.d.ts +9 -2
  24. package/dist/color/createColorWheelState.d.ts +13 -2
  25. package/dist/color/getColorChannels.d.ts +1 -1
  26. package/dist/color/index.d.ts +6 -6
  27. package/dist/color/types.d.ts +4 -4
  28. package/dist/combobox/createComboBoxState.d.ts +34 -13
  29. package/dist/combobox/index.d.ts +1 -1
  30. package/dist/data/createAsyncList.d.ts +4 -4
  31. package/dist/data/createListData.d.ts +2 -2
  32. package/dist/data/createTreeData.d.ts +6 -2
  33. package/dist/data/index.d.ts +3 -3
  34. package/dist/datepicker/index.d.ts +2 -4
  35. package/dist/disclosure/createDisclosureState.d.ts +2 -2
  36. package/dist/disclosure/index.d.ts +1 -1
  37. package/dist/dnd/createDragState.d.ts +2 -2
  38. package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
  39. package/dist/dnd/createDropState.d.ts +2 -2
  40. package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
  41. package/dist/dnd/index.d.ts +5 -5
  42. package/dist/dnd/types.d.ts +19 -17
  43. package/dist/form/createFormValidationState.d.ts +3 -3
  44. package/dist/form/index.d.ts +1 -1
  45. package/dist/grid/createGridState.d.ts +2 -2
  46. package/dist/grid/index.d.ts +2 -2
  47. package/dist/grid/types.d.ts +27 -11
  48. package/dist/index.d.ts +31 -30
  49. package/dist/index.js +9077 -7246
  50. package/dist/index.js.map +1 -1
  51. package/dist/menu/index.d.ts +2 -2
  52. package/dist/numberfield/createNumberFieldState.d.ts +2 -2
  53. package/dist/numberfield/index.d.ts +1 -1
  54. package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
  55. package/dist/overlays/index.d.ts +1 -1
  56. package/dist/radio/createRadioGroupState.d.ts +7 -7
  57. package/dist/radio/index.d.ts +1 -1
  58. package/dist/searchfield/createSearchFieldState.d.ts +2 -2
  59. package/dist/searchfield/index.d.ts +2 -2
  60. package/dist/select/createSelectState.d.ts +14 -8
  61. package/dist/select/index.d.ts +1 -1
  62. package/dist/selection/Selection.d.ts +16 -0
  63. package/dist/selection/SelectionManager.d.ts +110 -0
  64. package/dist/selection/createMultipleSelectionState.d.ts +59 -0
  65. package/dist/selection/index.d.ts +6 -5
  66. package/dist/slider/createSliderState.d.ts +3 -3
  67. package/dist/slider/index.d.ts +2 -2
  68. package/dist/steplist/createStepListState.d.ts +48 -0
  69. package/dist/steplist/index.d.ts +1 -0
  70. package/dist/table/TableCollection.d.ts +33 -3
  71. package/dist/table/createTableColumnResizeState.d.ts +54 -0
  72. package/dist/table/createTableState.d.ts +2 -2
  73. package/dist/table/createTreeGridState.d.ts +15 -0
  74. package/dist/table/index.d.ts +6 -3
  75. package/dist/table/types.d.ts +68 -10
  76. package/dist/tabs/createTabListState.d.ts +5 -5
  77. package/dist/tabs/index.d.ts +1 -1
  78. package/dist/textfield/createTextFieldState.d.ts +2 -2
  79. package/dist/textfield/index.d.ts +1 -1
  80. package/dist/toast/createToastState.d.ts +24 -2
  81. package/dist/toast/index.d.ts +1 -1
  82. package/dist/toggle/createToggleGroupState.d.ts +4 -4
  83. package/dist/toggle/createToggleState.d.ts +2 -2
  84. package/dist/toggle/index.d.ts +2 -2
  85. package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
  86. package/dist/tooltip/index.d.ts +1 -1
  87. package/dist/tree/TreeCollection.d.ts +2 -2
  88. package/dist/tree/createTreeState.d.ts +2 -2
  89. package/dist/tree/index.d.ts +3 -3
  90. package/dist/tree/types.d.ts +27 -13
  91. package/dist/utils/index.d.ts +1 -1
  92. package/dist/utils/reactivity.d.ts +1 -1
  93. package/package.json +29 -30
  94. package/src/autocomplete/createAutocompleteState.d.ts +19 -17
  95. package/src/autocomplete/createAutocompleteState.ts +24 -30
  96. package/src/autocomplete/index.d.ts +6 -2
  97. package/src/autocomplete/index.ts +1 -1
  98. package/src/calendar/IncompleteDate.ts +430 -0
  99. package/src/calendar/createCalendarState.d.ts +139 -117
  100. package/src/calendar/createCalendarState.ts +308 -94
  101. package/src/calendar/createDateFieldState.d.ts +130 -95
  102. package/src/calendar/createDateFieldState.ts +447 -334
  103. package/src/calendar/createDatePickerState.ts +347 -0
  104. package/src/calendar/createRangeCalendarState.d.ts +154 -129
  105. package/src/calendar/createRangeCalendarState.ts +411 -85
  106. package/src/calendar/createTimeFieldState.d.ts +81 -79
  107. package/src/calendar/createTimeFieldState.ts +187 -108
  108. package/src/calendar/index.d.ts +80 -7
  109. package/src/calendar/index.ts +23 -12
  110. package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
  111. package/src/checkbox/createCheckboxGroupState.ts +20 -29
  112. package/src/checkbox/index.d.ts +6 -2
  113. package/src/checkbox/index.ts +1 -1
  114. package/src/collections/ListCollection.d.ts +25 -22
  115. package/src/collections/ListCollection.ts +6 -6
  116. package/src/collections/createListState.d.ts +73 -58
  117. package/src/collections/createListState.ts +67 -35
  118. package/src/collections/createMenuState.d.ts +36 -31
  119. package/src/collections/createMenuState.ts +21 -12
  120. package/src/collections/createSelectionState.d.ts +78 -65
  121. package/src/collections/createSelectionState.ts +66 -52
  122. package/src/collections/getChildNodes.ts +98 -0
  123. package/src/collections/index.d.ts +38 -6
  124. package/src/collections/index.ts +14 -10
  125. package/src/collections/types.d.ts +101 -101
  126. package/src/collections/types.ts +12 -12
  127. package/src/color/Color.d.ts +20 -5
  128. package/src/color/Color.ts +357 -254
  129. package/src/color/createColorAreaState.d.ts +67 -65
  130. package/src/color/createColorAreaState.ts +21 -13
  131. package/src/color/createColorFieldState.d.ts +46 -44
  132. package/src/color/createColorFieldState.ts +285 -149
  133. package/src/color/createColorSliderState.d.ts +58 -56
  134. package/src/color/createColorSliderState.ts +45 -14
  135. package/src/color/createColorWheelState.d.ts +42 -40
  136. package/src/color/createColorWheelState.ts +101 -64
  137. package/src/color/getColorChannels.ts +7 -5
  138. package/src/color/index.d.ts +36 -7
  139. package/src/color/index.ts +6 -9
  140. package/src/color/types.d.ts +68 -63
  141. package/src/color/types.ts +11 -19
  142. package/src/combobox/createComboBoxState.d.ts +107 -105
  143. package/src/combobox/createComboBoxState.ts +184 -89
  144. package/src/combobox/index.d.ts +10 -2
  145. package/src/combobox/index.ts +1 -1
  146. package/src/data/createAsyncList.ts +89 -63
  147. package/src/data/createListData.ts +67 -67
  148. package/src/data/createTreeData.ts +304 -91
  149. package/src/data/index.ts +3 -3
  150. package/src/datepicker/index.ts +6 -10
  151. package/src/disclosure/createDisclosureState.d.ts +47 -43
  152. package/src/disclosure/createDisclosureState.ts +7 -21
  153. package/src/disclosure/index.d.ts +10 -2
  154. package/src/disclosure/index.ts +1 -1
  155. package/src/dnd/createDragState.d.ts +51 -42
  156. package/src/dnd/createDragState.ts +12 -14
  157. package/src/dnd/createDraggableCollectionState.d.ts +52 -41
  158. package/src/dnd/createDraggableCollectionState.ts +14 -21
  159. package/src/dnd/createDropState.d.ts +64 -45
  160. package/src/dnd/createDropState.ts +22 -32
  161. package/src/dnd/createDroppableCollectionState.d.ts +88 -59
  162. package/src/dnd/createDroppableCollectionState.ts +40 -43
  163. package/src/dnd/index.d.ts +55 -6
  164. package/src/dnd/index.ts +6 -18
  165. package/src/dnd/types.d.ts +170 -157
  166. package/src/dnd/types.ts +24 -22
  167. package/src/form/createFormValidationState.d.ts +47 -45
  168. package/src/form/createFormValidationState.ts +30 -63
  169. package/src/form/index.d.ts +16 -2
  170. package/src/form/index.ts +1 -1
  171. package/src/grid/createGridState.d.ts +7 -4
  172. package/src/grid/createGridState.ts +78 -51
  173. package/src/grid/index.d.ts +3 -3
  174. package/src/grid/index.ts +2 -8
  175. package/src/grid/types.d.ts +136 -129
  176. package/src/grid/types.ts +34 -18
  177. package/src/index.d.ts +311 -26
  178. package/src/index.ts +59 -89
  179. package/src/menu/index.ts +2 -4
  180. package/src/numberfield/createNumberFieldState.d.ts +56 -54
  181. package/src/numberfield/createNumberFieldState.ts +26 -31
  182. package/src/numberfield/index.d.ts +6 -2
  183. package/src/numberfield/index.ts +1 -1
  184. package/src/overlays/createOverlayTriggerState.d.ts +22 -20
  185. package/src/overlays/createOverlayTriggerState.ts +3 -4
  186. package/src/overlays/index.d.ts +6 -2
  187. package/src/overlays/index.ts +1 -1
  188. package/src/radio/createRadioGroupState.d.ts +54 -52
  189. package/src/radio/createRadioGroupState.ts +27 -29
  190. package/src/radio/index.d.ts +7 -2
  191. package/src/radio/index.ts +1 -1
  192. package/src/searchfield/createSearchFieldState.d.ts +16 -14
  193. package/src/searchfield/createSearchFieldState.ts +5 -11
  194. package/src/searchfield/index.d.ts +3 -3
  195. package/src/searchfield/index.ts +2 -5
  196. package/src/select/createSelectState.d.ts +63 -61
  197. package/src/select/createSelectState.ts +42 -29
  198. package/src/select/index.d.ts +2 -2
  199. package/src/select/index.ts +1 -5
  200. package/src/selection/Selection.ts +28 -0
  201. package/src/selection/SelectionManager.ts +526 -0
  202. package/src/selection/createMultipleSelectionState.ts +247 -0
  203. package/src/selection/index.ts +12 -7
  204. package/src/slider/createSliderState.d.ts +60 -60
  205. package/src/slider/createSliderState.ts +55 -33
  206. package/src/slider/index.d.ts +3 -3
  207. package/src/slider/index.ts +2 -6
  208. package/src/ssr/index.d.ts +1 -1
  209. package/src/ssr/index.ts +2 -2
  210. package/src/steplist/createStepListState.ts +191 -0
  211. package/src/steplist/index.ts +5 -0
  212. package/src/table/TableCollection.d.ts +42 -40
  213. package/src/table/TableCollection.ts +266 -99
  214. package/src/table/createTableColumnResizeState.ts +242 -0
  215. package/src/table/createTableState.d.ts +7 -4
  216. package/src/table/createTableState.ts +30 -7
  217. package/src/table/createTreeGridState.ts +146 -0
  218. package/src/table/index.d.ts +14 -4
  219. package/src/table/index.ts +13 -3
  220. package/src/table/types.d.ts +98 -92
  221. package/src/table/types.ts +83 -13
  222. package/src/tabs/createTabListState.d.ts +59 -57
  223. package/src/tabs/createTabListState.ts +34 -39
  224. package/src/tabs/index.d.ts +8 -2
  225. package/src/tabs/index.ts +1 -1
  226. package/src/textfield/createTextFieldState.d.ts +16 -14
  227. package/src/textfield/createTextFieldState.ts +7 -18
  228. package/src/textfield/index.d.ts +6 -2
  229. package/src/textfield/index.ts +1 -1
  230. package/src/toast/createToastState.d.ts +92 -86
  231. package/src/toast/createToastState.ts +90 -52
  232. package/src/toast/index.d.ts +12 -2
  233. package/src/toast/index.ts +2 -1
  234. package/src/toggle/createToggleGroupState.ts +11 -21
  235. package/src/toggle/createToggleState.d.ts +19 -19
  236. package/src/toggle/createToggleState.ts +2 -16
  237. package/src/toggle/index.d.ts +2 -2
  238. package/src/toggle/index.ts +2 -6
  239. package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
  240. package/src/tooltip/createTooltipTriggerState.ts +12 -11
  241. package/src/tooltip/index.d.ts +7 -2
  242. package/src/tooltip/index.ts +1 -1
  243. package/src/tree/TreeCollection.d.ts +26 -23
  244. package/src/tree/TreeCollection.ts +29 -22
  245. package/src/tree/createTreeState.d.ts +7 -4
  246. package/src/tree/createTreeState.ts +70 -41
  247. package/src/tree/index.d.ts +10 -4
  248. package/src/tree/index.ts +3 -3
  249. package/src/tree/types.d.ts +131 -126
  250. package/src/tree/types.ts +30 -13
  251. package/src/utils/index.d.ts +2 -2
  252. package/src/utils/index.ts +1 -1
  253. package/src/utils/reactivity.d.ts +2 -2
  254. package/src/utils/reactivity.ts +3 -3
@@ -6,26 +6,22 @@
6
6
  * either type to filter options or select from a list.
7
7
  */
8
8
 
9
- import { createSignal, createMemo, createEffect, type Accessor } from 'solid-js';
10
- import { access, type MaybeAccessor } from '../utils';
11
- import { createListState } from '../collections/createListState';
12
- import { createOverlayTriggerState } from '../overlays';
13
- import type { Key, CollectionNode, Collection, FocusStrategy } from '../collections/types';
9
+ import { createSignal, createMemo, createEffect, type Accessor } from "solid-js";
10
+ import { access, type MaybeAccessor } from "../utils";
11
+ import { createListState } from "../collections/createListState";
12
+ import { createOverlayTriggerState } from "../overlays";
13
+ import type { Key, CollectionNode, Collection, FocusStrategy } from "../collections/types";
14
+ import type { SelectionManager } from "../selection/SelectionManager";
14
15
 
15
- // ============================================
16
- // TYPES
17
- // ============================================
16
+ export type MenuTriggerAction = "focus" | "input" | "manual";
18
17
 
19
- export type MenuTriggerAction = 'focus' | 'input' | 'manual';
20
-
21
- // Re-export FocusStrategy for convenience
22
- export type { FocusStrategy } from '../collections/types';
18
+ export type { FocusStrategy } from "../collections/types";
23
19
 
24
20
  export type FilterFn = (textValue: string, inputValue: string) => boolean;
25
21
 
26
22
  export interface ComboBoxStateProps<T = unknown> {
27
23
  /** The items to display in the combobox dropdown. */
28
- items: T[];
24
+ items?: T[];
29
25
  /** Default items when uncontrolled. */
30
26
  defaultItems?: T[];
31
27
  /** Function to get the key for an item. */
@@ -36,12 +32,20 @@ export interface ComboBoxStateProps<T = unknown> {
36
32
  getDisabled?: (item: T) => boolean;
37
33
  /** Keys of disabled items. */
38
34
  disabledKeys?: Iterable<Key>;
39
- /** The currently selected key (controlled). */
35
+ /** The selection mode for the combobox. */
36
+ selectionMode?: "single" | "multiple";
37
+ /** The currently selected key (controlled, single mode). */
40
38
  selectedKey?: Key | null;
41
- /** The default selected key (uncontrolled). */
39
+ /** The default selected key (uncontrolled, single mode). */
42
40
  defaultSelectedKey?: Key | null;
43
- /** Handler called when the selection changes. */
41
+ /** The currently selected keys (controlled, multiple mode). */
42
+ selectedKeys?: Iterable<Key>;
43
+ /** The default selected keys (uncontrolled, multiple mode). */
44
+ defaultSelectedKeys?: Iterable<Key>;
45
+ /** Handler called when the selection changes (single mode). */
44
46
  onSelectionChange?: (key: Key | null) => void;
47
+ /** Handler called when the selection changes (multiple mode). */
48
+ onSelectionChangeMultiple?: (keys: Set<Key>) => void;
45
49
  /** The current input value (controlled). */
46
50
  inputValue?: string;
47
51
  /** The default input value (uncontrolled). */
@@ -75,6 +79,12 @@ export interface ComboBoxStateProps<T = unknown> {
75
79
  export interface ComboBoxState<T = unknown> {
76
80
  /** The collection of items (may be filtered). */
77
81
  readonly collection: Accessor<Collection<T>>;
82
+ /**
83
+ * The collection-aware selection manager. Mirrors upstream `useComboBoxState`,
84
+ * which returns the `useListState` selection manager bound to the unfiltered
85
+ * collection (selection spans all items; filtering is display-only).
86
+ */
87
+ readonly selectionManager: SelectionManager<T>;
78
88
  /** Whether the combobox dropdown is open. */
79
89
  readonly isOpen: Accessor<boolean>;
80
90
  /** Open the combobox dropdown. */
@@ -83,14 +93,20 @@ export interface ComboBoxState<T = unknown> {
83
93
  close(): void;
84
94
  /** Toggle the combobox dropdown. */
85
95
  toggle(focusStrategy?: FocusStrategy | null, trigger?: MenuTriggerAction): void;
86
- /** The currently selected key. */
96
+ /** The currently selected key (single mode). */
87
97
  readonly selectedKey: Accessor<Key | null>;
88
98
  /** The default selected key. */
89
99
  readonly defaultSelectedKey: Key | null;
90
- /** The currently selected item. */
100
+ /** The currently selected item (single mode). */
91
101
  readonly selectedItem: Accessor<CollectionNode<T> | null>;
92
102
  /** Set the selected key. */
93
103
  setSelectedKey(key: Key | null): void;
104
+ /** The currently selected keys (multiple mode). */
105
+ readonly selectedKeys: Accessor<Set<Key>>;
106
+ /** The currently selected items (multiple mode). */
107
+ readonly selectedItems: Accessor<CollectionNode<T>[]>;
108
+ /** Remove a selected key (multiple mode). */
109
+ removeSelectedKey(key: Key): void;
94
110
  /** The current input value. */
95
111
  readonly inputValue: Accessor<string>;
96
112
  /** The default input value. */
@@ -115,8 +131,8 @@ export interface ComboBoxState<T = unknown> {
115
131
  isKeyDisabled(key: Key): boolean;
116
132
  /** Select a key and close the menu (for ListState compatibility). */
117
133
  select(key: Key): void;
118
- /** The selection mode (always 'single' for combobox). */
119
- readonly selectionMode: Accessor<'single'>;
134
+ /** The selection mode. */
135
+ readonly selectionMode: Accessor<"single" | "multiple">;
120
136
  /** Check if a key is selected. */
121
137
  isSelected(key: Key): boolean;
122
138
  /** Whether the combobox is disabled. */
@@ -127,10 +143,6 @@ export interface ComboBoxState<T = unknown> {
127
143
  readonly isRequired: boolean;
128
144
  }
129
145
 
130
- // ============================================
131
- // DEFAULT FILTER
132
- // ============================================
133
-
134
146
  /**
135
147
  * Default filter function that does case-insensitive "contains" matching.
136
148
  */
@@ -138,24 +150,21 @@ export const defaultContainsFilter: FilterFn = (textValue, inputValue) => {
138
150
  return textValue.toLowerCase().includes(inputValue.toLowerCase());
139
151
  };
140
152
 
141
- // ============================================
142
- // IMPLEMENTATION
143
- // ============================================
144
-
145
153
  /**
146
154
  * Creates state for a combobox component.
147
155
  * Combines list state with input value management and filtering.
148
156
  */
149
157
  export function createComboBoxState<T = unknown>(
150
- props: MaybeAccessor<ComboBoxStateProps<T>>
158
+ props: MaybeAccessor<ComboBoxStateProps<T>>,
151
159
  ): ComboBoxState<T> {
152
160
  const getProps = () => access(props);
153
161
 
154
162
  // Extract options with defaults
155
- const menuTrigger = () => getProps().menuTrigger ?? 'input';
163
+ const menuTrigger = () => getProps().menuTrigger ?? "input";
156
164
  const allowsEmptyCollection = () => getProps().allowsEmptyCollection ?? false;
157
165
  const allowsCustomValue = () => getProps().allowsCustomValue ?? false;
158
166
  const shouldCloseOnBlur = () => getProps().shouldCloseOnBlur ?? true;
167
+ const isMultiple = () => getProps().selectionMode === "multiple";
159
168
 
160
169
  // Track focus strategy for list navigation
161
170
  const [focusStrategy, setFocusStrategy] = createSignal<FocusStrategy | null>(null);
@@ -164,13 +173,32 @@ export function createComboBoxState<T = unknown>(
164
173
  const [showAllItems, setShowAllItems] = createSignal(false);
165
174
 
166
175
  // Track the menu open trigger
167
- let menuOpenTrigger: MenuTriggerAction = 'focus';
176
+ let menuOpenTrigger: MenuTriggerAction = "focus";
168
177
 
169
- // ---- Selection State ----
178
+ // ---- Multi-select State ----
179
+ const isMultiSelectionControlled = () => getProps().selectedKeys !== undefined;
180
+ const [internalSelectedKeys, setInternalSelectedKeys] = createSignal<Set<Key>>(
181
+ new Set(getProps().defaultSelectedKeys ?? []),
182
+ );
183
+
184
+ const selectedKeys: Accessor<Set<Key>> = () => {
185
+ return isMultiSelectionControlled()
186
+ ? new Set(getProps().selectedKeys ?? [])
187
+ : internalSelectedKeys();
188
+ };
189
+
190
+ const setSelectedKeys = (keys: Set<Key>) => {
191
+ if (!isMultiSelectionControlled()) {
192
+ setInternalSelectedKeys(new Set(keys));
193
+ }
194
+ getProps().onSelectionChangeMultiple?.(keys);
195
+ };
196
+
197
+ // ---- Selection State (single mode) ----
170
198
  // Note: Selection state is initialized first because input value may depend on it
171
199
  const isSelectionControlled = () => getProps().selectedKey !== undefined;
172
200
  const [internalSelectedKey, setInternalSelectedKey] = createSignal<Key | null>(
173
- getProps().defaultSelectedKey ?? null
201
+ getProps().defaultSelectedKey ?? null,
174
202
  );
175
203
 
176
204
  // ---- Input Value State ----
@@ -179,13 +207,13 @@ export function createComboBoxState<T = unknown>(
179
207
 
180
208
  // We'll set the proper initial value after collection is created
181
209
  const [internalInputValue, setInternalInputValue] = createSignal(
182
- getProps().defaultInputValue ?? ''
210
+ getProps().defaultInputValue ?? "",
183
211
  );
184
212
  // Track if we've initialized input from selection
185
213
  let inputInitialized = false;
186
214
 
187
215
  const inputValue: Accessor<string> = () => {
188
- return isInputControlled() ? (getProps().inputValue ?? '') : internalInputValue();
216
+ return isInputControlled() ? (getProps().inputValue ?? "") : internalInputValue();
189
217
  };
190
218
 
191
219
  const setInputValue = (value: string) => {
@@ -240,14 +268,27 @@ export function createComboBoxState<T = unknown>(
240
268
  get disabledKeys() {
241
269
  return getProps().disabledKeys;
242
270
  },
243
- selectionMode: 'single',
271
+ get selectionMode() {
272
+ return isMultiple() ? ("multiple" as const) : ("single" as const);
273
+ },
244
274
  disallowEmptySelection: false,
245
275
  get selectedKeys() {
276
+ if (isMultiple()) {
277
+ return Array.from(selectedKeys());
278
+ }
246
279
  const key = selectedKey();
247
280
  return key != null ? [key] : [];
248
281
  },
249
282
  onSelectionChange(keys) {
250
- if (keys === 'all') return;
283
+ if (keys === "all") return;
284
+
285
+ if (isMultiple()) {
286
+ // In multiple mode, toggle selections
287
+ const newKeys = new Set(keys);
288
+ setSelectedKeys(newKeys);
289
+ return;
290
+ }
291
+
251
292
  const key = keys.size > 0 ? Array.from(keys)[0] : null;
252
293
 
253
294
  // If same key selected, just reset input and close
@@ -284,13 +325,24 @@ export function createComboBoxState<T = unknown>(
284
325
  return showAllItems() ? originalCollection() : filteredCollection();
285
326
  });
286
327
 
287
- // ---- Selected Item ----
328
+ // ---- Selected Item(s) ----
288
329
  const selectedItem: Accessor<CollectionNode<T> | null> = () => {
289
330
  const key = selectedKey();
290
331
  if (key == null) return null;
291
332
  return originalCollection().getItem(key);
292
333
  };
293
334
 
335
+ const selectedItems = createMemo<CollectionNode<T>[]>(() => {
336
+ const keys = selectedKeys();
337
+ const collection = originalCollection();
338
+ const items: CollectionNode<T>[] = [];
339
+ for (const key of keys) {
340
+ const item = collection.getItem(key);
341
+ if (item) items.push(item);
342
+ }
343
+ return items;
344
+ });
345
+
294
346
  // Initialize input value from selected item if not already set
295
347
  // This runs once on creation
296
348
  if (!inputInitialized && !isInputControlled()) {
@@ -309,7 +361,7 @@ export function createComboBoxState<T = unknown>(
309
361
  // ---- Helper Functions ----
310
362
  const resetInputValue = () => {
311
363
  const item = selectedItem();
312
- const textValue = item?.textValue ?? '';
364
+ const textValue = item?.textValue ?? "";
313
365
  setLastValue(textValue);
314
366
  setInputValue(textValue);
315
367
  };
@@ -322,14 +374,13 @@ export function createComboBoxState<T = unknown>(
322
374
 
323
375
  // ---- Open/Toggle Logic ----
324
376
  const open = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
325
- const displayAll = trigger === 'manual' || (trigger === 'focus' && menuTrigger() === 'focus');
377
+ const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
326
378
 
327
379
  // Check if we should open
328
380
  const filtered = filteredCollection();
329
381
  const original = originalCollection();
330
- const canOpen = allowsEmptyCollection() ||
331
- filtered.size > 0 ||
332
- (displayAll && original.size > 0);
382
+ const canOpen =
383
+ allowsEmptyCollection() || filtered.size > 0 || (displayAll && original.size > 0);
333
384
 
334
385
  if (!canOpen) return;
335
386
 
@@ -337,20 +388,32 @@ export function createComboBoxState<T = unknown>(
337
388
  setShowAllItems(true);
338
389
  }
339
390
 
340
- menuOpenTrigger = trigger ?? 'focus';
391
+ menuOpenTrigger = trigger ?? "focus";
341
392
  setFocusStrategy(strategy);
393
+ if (!overlayState.isOpen()) {
394
+ const key = selectedKey();
395
+ const nextFocusedKey =
396
+ key != null && !listState.isDisabled(key)
397
+ ? key
398
+ : strategy === "first"
399
+ ? displayedCollection().getFirstKey()
400
+ : strategy === "last"
401
+ ? displayedCollection().getLastKey()
402
+ : null;
403
+ listState.setFocused(true);
404
+ listState.setFocusedKey(nextFocusedKey);
405
+ }
342
406
  overlayState.open();
343
407
  };
344
408
 
345
409
  const toggle = (strategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
346
- const displayAll = trigger === 'manual' || (trigger === 'focus' && menuTrigger() === 'focus');
410
+ const displayAll = trigger === "manual" || (trigger === "focus" && menuTrigger() === "focus");
347
411
 
348
412
  // Check if we can open (if closed)
349
413
  const filtered = filteredCollection();
350
414
  const original = originalCollection();
351
- const canOpen = allowsEmptyCollection() ||
352
- filtered.size > 0 ||
353
- (displayAll && original.size > 0);
415
+ const canOpen =
416
+ allowsEmptyCollection() || filtered.size > 0 || (displayAll && original.size > 0);
354
417
 
355
418
  if (!canOpen && !overlayState.isOpen()) return;
356
419
 
@@ -359,7 +422,7 @@ export function createComboBoxState<T = unknown>(
359
422
  }
360
423
 
361
424
  if (!overlayState.isOpen()) {
362
- menuOpenTrigger = trigger ?? 'focus';
425
+ menuOpenTrigger = trigger ?? "focus";
363
426
  }
364
427
 
365
428
  setFocusStrategy(strategy);
@@ -377,7 +440,7 @@ export function createComboBoxState<T = unknown>(
377
440
  if (isSelectionControlled() && isInputControlled()) {
378
441
  getProps().onSelectionChange?.(selectedKey());
379
442
  const item = selectedItem();
380
- setLastValue(item?.textValue ?? '');
443
+ setLastValue(item?.textValue ?? "");
381
444
  closeMenu();
382
445
  } else {
383
446
  // Reset input to selected item's text
@@ -389,7 +452,7 @@ export function createComboBoxState<T = unknown>(
389
452
  const commitValue = () => {
390
453
  if (allowsCustomValue()) {
391
454
  const item = selectedItem();
392
- const itemText = item?.textValue ?? '';
455
+ const itemText = item?.textValue ?? "";
393
456
  if (inputValue() === itemText) {
394
457
  commitSelection();
395
458
  } else {
@@ -403,6 +466,14 @@ export function createComboBoxState<T = unknown>(
403
466
  const commit = () => {
404
467
  const focusedKey = listState.focusedKey();
405
468
 
469
+ if (isMultiple()) {
470
+ // In multiple mode, toggle the focused item without closing
471
+ if (overlayState.isOpen() && focusedKey != null) {
472
+ select(focusedKey);
473
+ }
474
+ return;
475
+ }
476
+
406
477
  if (overlayState.isOpen() && focusedKey != null) {
407
478
  // If focused key is already selected, just commit
408
479
  if (selectedKey() === focusedKey) {
@@ -410,6 +481,7 @@ export function createComboBoxState<T = unknown>(
410
481
  } else {
411
482
  // Select the focused item
412
483
  setSelectedKey(focusedKey);
484
+ closeMenu();
413
485
  }
414
486
  } else {
415
487
  commitValue();
@@ -426,13 +498,13 @@ export function createComboBoxState<T = unknown>(
426
498
 
427
499
  // ---- Focus Handling ----
428
500
  const [isFocused, setIsFocused] = createSignal(false);
429
- let valueOnFocus = '';
501
+ let valueOnFocus = "";
430
502
 
431
503
  const setFocused = (focused: boolean) => {
432
504
  if (focused) {
433
505
  valueOnFocus = inputValue();
434
- if (menuTrigger() === 'focus' && !getProps().isReadOnly) {
435
- open(null, 'focus');
506
+ if (menuTrigger() === "focus" && !getProps().isReadOnly) {
507
+ open(null, "focus");
436
508
  }
437
509
  } else {
438
510
  if (shouldCloseOnBlur()) {
@@ -456,18 +528,13 @@ export function createComboBoxState<T = unknown>(
456
528
  (filtered.size > 0 || allowsEmptyCollection()) &&
457
529
  !isOpen &&
458
530
  input !== last &&
459
- menuTrigger() !== 'manual'
531
+ menuTrigger() !== "manual"
460
532
  ) {
461
- open(null, 'input');
533
+ open(null, "input");
462
534
  }
463
535
 
464
536
  // Auto-close when empty (unless showing all)
465
- if (
466
- !showAllItems() &&
467
- !allowsEmptyCollection() &&
468
- isOpen &&
469
- filtered.size === 0
470
- ) {
537
+ if (!showAllItems() && !allowsEmptyCollection() && isOpen && filtered.size === 0) {
471
538
  closeMenu();
472
539
  }
473
540
 
@@ -477,7 +544,7 @@ export function createComboBoxState<T = unknown>(
477
544
  setShowAllItems(false);
478
545
 
479
546
  // Clear selection when input is cleared (if not fully controlled)
480
- if (input === '' && (!isInputControlled() || !isSelectionControlled())) {
547
+ if (input === "" && (!isInputControlled() || !isSelectionControlled())) {
481
548
  setSelectedKey(null);
482
549
  }
483
550
 
@@ -485,11 +552,12 @@ export function createComboBoxState<T = unknown>(
485
552
  }
486
553
  });
487
554
 
488
- // Update input when selection changes
555
+ // Update input when selection changes (only in single mode)
489
556
  createEffect(() => {
557
+ if (isMultiple()) return;
490
558
  const key = selectedKey();
491
559
  const item = key != null ? originalCollection().getItem(key) : null;
492
- const textValue = item?.textValue ?? '';
560
+ const textValue = item?.textValue ?? "";
493
561
 
494
562
  // Only update if selection changed and not fully controlled
495
563
  if (!isInputControlled() || !isSelectionControlled()) {
@@ -500,8 +568,9 @@ export function createComboBoxState<T = unknown>(
500
568
  }
501
569
  });
502
570
 
503
- // Close when selection changes
571
+ // Close when selection changes (only in single mode)
504
572
  createEffect((prevKey: Key | null | undefined) => {
573
+ if (isMultiple()) return undefined;
505
574
  const key = selectedKey();
506
575
  if (key != null && key !== prevKey) {
507
576
  closeMenu();
@@ -512,12 +581,38 @@ export function createComboBoxState<T = unknown>(
512
581
  // ---- Selection Methods for ListState compatibility ----
513
582
  // These methods allow createOption to work with ComboBoxState
514
583
  const select = (key: Key) => {
515
- setSelectedKey(key);
516
- closeMenu();
584
+ if (isMultiple()) {
585
+ // Toggle selection in multiple mode
586
+ const current = new Set(selectedKeys());
587
+ if (current.has(key)) {
588
+ current.delete(key);
589
+ } else {
590
+ current.add(key);
591
+ }
592
+ setSelectedKeys(current);
593
+ // Don't close menu and don't reset input in multi-select mode
594
+ } else {
595
+ setSelectedKey(key);
596
+ closeMenu();
597
+ }
598
+ };
599
+
600
+ const removeSelectedKey = (key: Key) => {
601
+ if (isMultiple()) {
602
+ const current = new Set(selectedKeys());
603
+ current.delete(key);
604
+ setSelectedKeys(current);
605
+ }
517
606
  };
518
607
 
519
- const selectionMode: Accessor<'single'> = () => 'single';
520
- const isSelected = (key: Key) => selectedKey() === key;
608
+ const selectionMode: Accessor<"single" | "multiple"> = () => getProps().selectionMode ?? "single";
609
+
610
+ const isSelected = (key: Key) => {
611
+ if (isMultiple()) {
612
+ return selectedKeys().has(key);
613
+ }
614
+ return selectedKey() === key;
615
+ };
521
616
 
522
617
  // ---- Return State ----
523
618
  return {
@@ -530,9 +625,13 @@ export function createComboBoxState<T = unknown>(
530
625
  defaultSelectedKey: getProps().defaultSelectedKey ?? null,
531
626
  selectedItem,
532
627
  setSelectedKey,
628
+ selectedKeys,
629
+ selectedItems,
630
+ removeSelectedKey,
533
631
  inputValue,
534
- defaultInputValue: getProps().defaultInputValue ?? '',
632
+ defaultInputValue: getProps().defaultInputValue ?? "",
535
633
  setInputValue,
634
+ selectionManager: listState.selectionManager,
536
635
  focusedKey: listState.focusedKey,
537
636
  setFocusedKey: listState.setFocusedKey,
538
637
  isFocused,
@@ -557,17 +656,13 @@ export function createComboBoxState<T = unknown>(
557
656
  };
558
657
  }
559
658
 
560
- // ============================================
561
- // COLLECTION FILTERING
562
- // ============================================
563
-
564
659
  /**
565
660
  * Filter a collection based on input value.
566
661
  */
567
662
  function filterCollection<T>(
568
663
  collection: Collection<T>,
569
664
  inputValue: string,
570
- filter: FilterFn
665
+ filter: FilterFn,
571
666
  ): Collection<T> {
572
667
  if (!inputValue) {
573
668
  return collection;
@@ -576,12 +671,12 @@ function filterCollection<T>(
576
671
  const filteredItems: CollectionNode<T>[] = [];
577
672
 
578
673
  for (const item of collection) {
579
- if (item.type === 'section') {
674
+ if (item.type === "section") {
580
675
  // Filter section children
581
676
  const filteredChildren: CollectionNode<T>[] = [];
582
677
  if (item.childNodes) {
583
678
  for (const child of item.childNodes) {
584
- if (child.type === 'item' && filter(child.textValue, inputValue)) {
679
+ if (child.type === "item" && filter(child.textValue, inputValue)) {
585
680
  filteredChildren.push(child);
586
681
  }
587
682
  }
@@ -593,7 +688,7 @@ function filterCollection<T>(
593
688
  childNodes: filteredChildren,
594
689
  });
595
690
  }
596
- } else if (item.type === 'item') {
691
+ } else if (item.type === "item") {
597
692
  if (filter(item.textValue, inputValue)) {
598
693
  filteredItems.push(item);
599
694
  }
@@ -609,7 +704,7 @@ function filterCollection<T>(
609
704
  */
610
705
  function createFilteredCollection<T>(
611
706
  items: CollectionNode<T>[],
612
- original: Collection<T>
707
+ original: Collection<T>,
613
708
  ): Collection<T> {
614
709
  const itemMap = new Map<Key, CollectionNode<T>>();
615
710
 
@@ -626,10 +721,10 @@ function createFilteredCollection<T>(
626
721
  get size() {
627
722
  let count = 0;
628
723
  for (const item of items) {
629
- if (item.type === 'item') {
724
+ if (item.type === "item") {
630
725
  count++;
631
726
  } else if (item.childNodes) {
632
- count += Array.from(item.childNodes).filter(c => c.type === 'item').length;
727
+ count += Array.from(item.childNodes).filter((c) => c.type === "item").length;
633
728
  }
634
729
  }
635
730
  return count;
@@ -642,10 +737,10 @@ function createFilteredCollection<T>(
642
737
  },
643
738
  getFirstKey() {
644
739
  for (const item of items) {
645
- if (item.type === 'item') return item.key;
740
+ if (item.type === "item") return item.key;
646
741
  if (item.childNodes) {
647
742
  for (const child of item.childNodes) {
648
- if (child.type === 'item') return child.key;
743
+ if (child.type === "item") return child.key;
649
744
  }
650
745
  }
651
746
  }
@@ -654,11 +749,11 @@ function createFilteredCollection<T>(
654
749
  getLastKey() {
655
750
  for (let i = items.length - 1; i >= 0; i--) {
656
751
  const item = items[i];
657
- if (item.type === 'item') return item.key;
752
+ if (item.type === "item") return item.key;
658
753
  if (item.childNodes) {
659
754
  const children = Array.from(item.childNodes);
660
755
  for (let j = children.length - 1; j >= 0; j--) {
661
- if (children[j].type === 'item') return children[j].key;
756
+ if (children[j].type === "item") return children[j].key;
662
757
  }
663
758
  }
664
759
  }
@@ -674,12 +769,12 @@ function createFilteredCollection<T>(
674
769
  // Flatten items for indexing
675
770
  let currentIndex = 0;
676
771
  for (const item of items) {
677
- if (item.type === 'item') {
772
+ if (item.type === "item") {
678
773
  if (currentIndex === index) return item;
679
774
  currentIndex++;
680
775
  } else if (item.childNodes) {
681
776
  for (const child of item.childNodes) {
682
- if (child.type === 'item') {
777
+ if (child.type === "item") {
683
778
  if (currentIndex === index) return child;
684
779
  currentIndex++;
685
780
  }
@@ -694,7 +789,7 @@ function createFilteredCollection<T>(
694
789
  },
695
790
  getTextValue(key: Key) {
696
791
  const item = itemMap.get(key);
697
- return item?.textValue ?? '';
792
+ return item?.textValue ?? "";
698
793
  },
699
794
  [Symbol.iterator]() {
700
795
  return items[Symbol.iterator]();
@@ -1,5 +1,13 @@
1
1
  /**
2
2
  * ComboBox state management for Solid Stately.
3
3
  */
4
- export { createComboBoxState, defaultContainsFilter, type ComboBoxState, type ComboBoxStateProps, type FilterFn, type MenuTriggerAction, type FocusStrategy, } from './createComboBoxState';
5
- //# sourceMappingURL=index.d.ts.map
4
+ export {
5
+ createComboBoxState,
6
+ defaultContainsFilter,
7
+ type ComboBoxState,
8
+ type ComboBoxStateProps,
9
+ type FilterFn,
10
+ type MenuTriggerAction,
11
+ type FocusStrategy,
12
+ } from "./createComboBoxState";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -10,4 +10,4 @@ export {
10
10
  type FilterFn,
11
11
  type MenuTriggerAction,
12
12
  type FocusStrategy,
13
- } from './createComboBoxState';
13
+ } from "./createComboBoxState";