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