@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.
- package/README.md +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
- package/dist/autocomplete/index.d.ts +1 -1
- package/dist/calendar/createCalendarState.d.ts +22 -7
- package/dist/calendar/createDateFieldState.d.ts +30 -7
- package/dist/calendar/createDatePickerState.d.ts +60 -0
- package/dist/calendar/createRangeCalendarState.d.ts +27 -8
- package/dist/calendar/createTimeFieldState.d.ts +34 -7
- package/dist/calendar/index.d.ts +7 -6
- package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
- package/dist/checkbox/index.d.ts +1 -1
- package/dist/collections/ListCollection.d.ts +1 -1
- package/dist/collections/createListState.d.ts +8 -8
- package/dist/collections/createMenuState.d.ts +7 -7
- package/dist/collections/createSelectionState.d.ts +5 -5
- package/dist/collections/index.d.ts +5 -5
- package/dist/collections/types.d.ts +12 -12
- package/dist/color/Color.d.ts +1 -1
- package/dist/color/createColorAreaState.d.ts +4 -2
- package/dist/color/createColorFieldState.d.ts +38 -12
- package/dist/color/createColorSliderState.d.ts +9 -2
- package/dist/color/createColorWheelState.d.ts +13 -2
- package/dist/color/getColorChannels.d.ts +1 -1
- package/dist/color/index.d.ts +6 -6
- package/dist/color/types.d.ts +4 -4
- package/dist/combobox/createComboBoxState.d.ts +27 -13
- package/dist/combobox/index.d.ts +1 -1
- package/dist/data/createAsyncList.d.ts +4 -4
- package/dist/data/createListData.d.ts +2 -2
- package/dist/data/createTreeData.d.ts +6 -2
- package/dist/data/index.d.ts +3 -3
- package/dist/datepicker/index.d.ts +2 -4
- package/dist/disclosure/createDisclosureState.d.ts +2 -2
- package/dist/disclosure/index.d.ts +1 -1
- package/dist/dnd/createDragState.d.ts +2 -2
- package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
- package/dist/dnd/createDropState.d.ts +2 -2
- package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
- package/dist/dnd/index.d.ts +5 -5
- package/dist/dnd/types.d.ts +16 -16
- package/dist/form/createFormValidationState.d.ts +3 -3
- package/dist/form/index.d.ts +1 -1
- package/dist/grid/createGridState.d.ts +2 -2
- package/dist/grid/index.d.ts +2 -2
- package/dist/grid/types.d.ts +13 -11
- package/dist/index.d.ts +31 -30
- package/dist/index.js +7924 -7238
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +2 -2
- package/dist/numberfield/createNumberFieldState.d.ts +2 -2
- package/dist/numberfield/index.d.ts +1 -1
- package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
- package/dist/overlays/index.d.ts +1 -1
- package/dist/radio/createRadioGroupState.d.ts +7 -7
- package/dist/radio/index.d.ts +1 -1
- package/dist/searchfield/createSearchFieldState.d.ts +2 -2
- package/dist/searchfield/index.d.ts +2 -2
- package/dist/select/createSelectState.d.ts +8 -8
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/index.d.ts +5 -5
- package/dist/slider/createSliderState.d.ts +3 -3
- package/dist/slider/index.d.ts +2 -2
- package/dist/steplist/createStepListState.d.ts +48 -0
- package/dist/steplist/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +3 -3
- package/dist/table/createTableColumnResizeState.d.ts +54 -0
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/index.d.ts +5 -3
- package/dist/table/types.d.ts +10 -10
- package/dist/tabs/createTabListState.d.ts +5 -5
- package/dist/tabs/index.d.ts +1 -1
- package/dist/textfield/createTextFieldState.d.ts +2 -2
- package/dist/textfield/index.d.ts +1 -1
- package/dist/toast/createToastState.d.ts +8 -2
- package/dist/toast/index.d.ts +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +4 -4
- package/dist/toggle/createToggleState.d.ts +2 -2
- package/dist/toggle/index.d.ts +2 -2
- package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tree/TreeCollection.d.ts +2 -2
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/index.d.ts +3 -3
- package/dist/tree/types.d.ts +19 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/reactivity.d.ts +1 -1
- package/package.json +29 -30
- package/src/autocomplete/createAutocompleteState.d.ts +19 -17
- package/src/autocomplete/createAutocompleteState.ts +24 -30
- package/src/autocomplete/index.d.ts +6 -2
- package/src/autocomplete/index.ts +1 -1
- package/src/calendar/createCalendarState.d.ts +139 -117
- package/src/calendar/createCalendarState.ts +221 -74
- package/src/calendar/createDateFieldState.d.ts +130 -95
- package/src/calendar/createDateFieldState.ts +263 -151
- package/src/calendar/createDatePickerState.ts +347 -0
- package/src/calendar/createRangeCalendarState.d.ts +154 -129
- package/src/calendar/createRangeCalendarState.ts +293 -79
- package/src/calendar/createTimeFieldState.d.ts +81 -79
- package/src/calendar/createTimeFieldState.ts +187 -108
- package/src/calendar/index.d.ts +80 -7
- package/src/calendar/index.ts +21 -12
- package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
- package/src/checkbox/createCheckboxGroupState.ts +20 -29
- package/src/checkbox/index.d.ts +6 -2
- package/src/checkbox/index.ts +1 -1
- package/src/collections/ListCollection.d.ts +25 -22
- package/src/collections/ListCollection.ts +6 -6
- package/src/collections/createListState.d.ts +73 -58
- package/src/collections/createListState.ts +19 -18
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +18 -12
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +36 -32
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +5 -10
- package/src/collections/types.d.ts +101 -101
- package/src/collections/types.ts +12 -12
- package/src/color/Color.d.ts +20 -5
- package/src/color/Color.ts +357 -254
- package/src/color/createColorAreaState.d.ts +67 -65
- package/src/color/createColorAreaState.ts +21 -13
- package/src/color/createColorFieldState.d.ts +46 -44
- package/src/color/createColorFieldState.ts +285 -149
- package/src/color/createColorSliderState.d.ts +58 -56
- package/src/color/createColorSliderState.ts +45 -14
- package/src/color/createColorWheelState.d.ts +42 -40
- package/src/color/createColorWheelState.ts +101 -64
- package/src/color/getColorChannels.ts +7 -5
- package/src/color/index.d.ts +36 -7
- package/src/color/index.ts +6 -9
- package/src/color/types.d.ts +68 -63
- package/src/color/types.ts +11 -19
- package/src/combobox/createComboBoxState.d.ts +107 -105
- package/src/combobox/createComboBoxState.ts +176 -89
- package/src/combobox/index.d.ts +10 -2
- package/src/combobox/index.ts +1 -1
- package/src/data/createAsyncList.ts +89 -63
- package/src/data/createListData.ts +67 -67
- package/src/data/createTreeData.ts +304 -91
- package/src/data/index.ts +3 -3
- package/src/datepicker/index.ts +6 -10
- package/src/disclosure/createDisclosureState.d.ts +47 -43
- package/src/disclosure/createDisclosureState.ts +7 -21
- package/src/disclosure/index.d.ts +10 -2
- package/src/disclosure/index.ts +1 -1
- package/src/dnd/createDragState.d.ts +51 -42
- package/src/dnd/createDragState.ts +12 -14
- package/src/dnd/createDraggableCollectionState.d.ts +52 -41
- package/src/dnd/createDraggableCollectionState.ts +14 -21
- package/src/dnd/createDropState.d.ts +64 -45
- package/src/dnd/createDropState.ts +22 -32
- package/src/dnd/createDroppableCollectionState.d.ts +88 -59
- package/src/dnd/createDroppableCollectionState.ts +40 -43
- package/src/dnd/index.d.ts +54 -6
- package/src/dnd/index.ts +5 -18
- package/src/dnd/types.d.ts +167 -156
- package/src/dnd/types.ts +20 -21
- package/src/form/createFormValidationState.d.ts +47 -45
- package/src/form/createFormValidationState.ts +30 -63
- package/src/form/index.d.ts +16 -2
- package/src/form/index.ts +1 -1
- package/src/grid/createGridState.d.ts +7 -4
- package/src/grid/createGridState.ts +44 -51
- package/src/grid/index.d.ts +3 -3
- package/src/grid/index.ts +2 -8
- package/src/grid/types.d.ts +136 -129
- package/src/grid/types.ts +20 -18
- package/src/index.d.ts +310 -26
- package/src/index.ts +48 -89
- package/src/menu/index.ts +2 -4
- package/src/numberfield/createNumberFieldState.d.ts +56 -54
- package/src/numberfield/createNumberFieldState.ts +26 -31
- package/src/numberfield/index.d.ts +6 -2
- package/src/numberfield/index.ts +1 -1
- package/src/overlays/createOverlayTriggerState.d.ts +22 -20
- package/src/overlays/createOverlayTriggerState.ts +3 -4
- package/src/overlays/index.d.ts +6 -2
- package/src/overlays/index.ts +1 -1
- package/src/radio/createRadioGroupState.d.ts +54 -52
- package/src/radio/createRadioGroupState.ts +27 -29
- package/src/radio/index.d.ts +7 -2
- package/src/radio/index.ts +1 -1
- package/src/searchfield/createSearchFieldState.d.ts +16 -14
- package/src/searchfield/createSearchFieldState.ts +5 -11
- package/src/searchfield/index.d.ts +3 -3
- package/src/searchfield/index.ts +2 -5
- package/src/select/createSelectState.d.ts +63 -61
- package/src/select/createSelectState.ts +27 -27
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/index.ts +6 -7
- package/src/slider/createSliderState.d.ts +60 -60
- package/src/slider/createSliderState.ts +55 -33
- package/src/slider/index.d.ts +3 -3
- package/src/slider/index.ts +2 -6
- package/src/ssr/index.d.ts +1 -1
- package/src/ssr/index.ts +2 -2
- package/src/steplist/createStepListState.ts +191 -0
- package/src/steplist/index.ts +5 -0
- package/src/table/TableCollection.d.ts +42 -40
- package/src/table/TableCollection.ts +61 -64
- package/src/table/createTableColumnResizeState.ts +242 -0
- package/src/table/createTableState.d.ts +7 -4
- package/src/table/createTableState.ts +10 -7
- package/src/table/index.d.ts +14 -4
- package/src/table/index.ts +10 -3
- package/src/table/types.d.ts +98 -92
- package/src/table/types.ts +17 -13
- package/src/tabs/createTabListState.d.ts +59 -57
- package/src/tabs/createTabListState.ts +24 -31
- package/src/tabs/index.d.ts +8 -2
- package/src/tabs/index.ts +1 -1
- package/src/textfield/createTextFieldState.d.ts +16 -14
- package/src/textfield/createTextFieldState.ts +7 -18
- package/src/textfield/index.d.ts +6 -2
- package/src/textfield/index.ts +1 -1
- package/src/toast/createToastState.d.ts +92 -86
- package/src/toast/createToastState.ts +48 -43
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +1 -1
- package/src/toggle/createToggleGroupState.ts +11 -21
- package/src/toggle/createToggleState.d.ts +19 -19
- package/src/toggle/createToggleState.ts +2 -16
- package/src/toggle/index.d.ts +2 -2
- package/src/toggle/index.ts +2 -6
- package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
- package/src/tooltip/createTooltipTriggerState.ts +12 -11
- package/src/tooltip/index.d.ts +7 -2
- package/src/tooltip/index.ts +1 -1
- package/src/tree/TreeCollection.d.ts +26 -23
- package/src/tree/TreeCollection.ts +29 -22
- package/src/tree/createTreeState.d.ts +7 -4
- package/src/tree/createTreeState.ts +39 -41
- package/src/tree/index.d.ts +10 -4
- package/src/tree/index.ts +3 -3
- package/src/tree/types.d.ts +131 -126
- package/src/tree/types.ts +22 -13
- package/src/utils/index.d.ts +2 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/reactivity.d.ts +2 -2
- 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
|
|
10
|
-
import { access, type MaybeAccessor } from
|
|
11
|
-
import { createListState } from
|
|
12
|
-
import { createOverlayTriggerState } from
|
|
13
|
-
import type { Key, CollectionNode, Collection, FocusStrategy } from
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
119
|
-
readonly selectionMode: Accessor<
|
|
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 ??
|
|
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 =
|
|
169
|
+
let menuOpenTrigger: MenuTriggerAction = "focus";
|
|
168
170
|
|
|
169
|
-
// ----
|
|
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 ??
|
|
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
|
|
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 ===
|
|
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 ===
|
|
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 =
|
|
331
|
-
|
|
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 ??
|
|
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 ===
|
|
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 =
|
|
352
|
-
|
|
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 ??
|
|
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() ===
|
|
435
|
-
open(null,
|
|
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() !==
|
|
524
|
+
menuTrigger() !== "manual"
|
|
460
525
|
) {
|
|
461
|
-
open(null,
|
|
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 ===
|
|
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
|
-
|
|
516
|
-
|
|
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<
|
|
520
|
-
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
716
|
+
if (item.type === "item") {
|
|
630
717
|
count++;
|
|
631
718
|
} else if (item.childNodes) {
|
|
632
|
-
count += Array.from(item.childNodes).filter(c => c.type ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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]();
|
package/src/combobox/index.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ComboBox state management for Solid Stately.
|
|
3
3
|
*/
|
|
4
|
-
export {
|
|
5
|
-
|
|
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
|
package/src/combobox/index.ts
CHANGED