@oscarpalmer/oui 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/combobox.css +1 -1
- package/dist/css/listbox.css +152 -0
- package/dist/js/combobox/constants.combobox.d.mts +2 -2
- package/dist/js/combobox/constants.combobox.mjs +40 -33
- package/dist/js/combobox/dom.combobox.d.mts +8 -91
- package/dist/js/combobox/dom.combobox.mjs +204 -2
- package/dist/js/combobox/event.combobox.d.mts +14 -8
- package/dist/js/combobox/event.combobox.mjs +2 -2
- package/dist/js/combobox/index.d.mts +1 -1
- package/dist/js/combobox/index.mjs +20 -18
- package/dist/js/combobox/misc.combobox.d.mts +2 -2
- package/dist/js/combobox/misc.combobox.mjs +2 -2
- package/dist/js/combobox/models.combobox.d.mts +44 -2
- package/dist/js/combobox/state.combobox.d.mts +9 -10
- package/dist/js/combobox/state.combobox.mjs +2 -2
- package/dist/js/constants.combobox-DmRAMC64.d.mts +26 -0
- package/dist/js/constants.listbox-DRsXUxd3.d.mts +14 -0
- package/dist/js/index-EfkUSals.d.mts +139 -0
- package/dist/js/index.d.mts +4 -2
- package/dist/js/index.mjs +4 -2
- package/dist/js/internal/attributable.mjs +4 -3
- package/dist/js/internal/listbox/listbox.constants.d.mts +11 -0
- package/dist/js/internal/listbox/listbox.constants.mjs +23 -0
- package/dist/js/internal/listbox/listbox.data.d.mts +20 -0
- package/dist/js/internal/listbox/listbox.data.mjs +2 -0
- package/dist/js/internal/listbox/listbox.dom.d.mts +66 -0
- package/dist/js/internal/listbox/listbox.dom.mjs +2 -0
- package/dist/js/internal/listbox/listbox.event.d.mts +5 -0
- package/dist/js/internal/listbox/listbox.event.mjs +63 -0
- package/dist/js/internal/listbox/listbox.form.d.mts +6 -0
- package/dist/js/internal/listbox/listbox.form.mjs +52 -0
- package/dist/js/internal/listbox/listbox.misc.d.mts +12 -0
- package/dist/js/internal/listbox/listbox.misc.mjs +2 -0
- package/dist/js/internal/listbox/listbox.model.d.mts +2 -0
- package/dist/js/internal/listbox/listbox.model.mjs +1 -0
- package/dist/js/internal/listbox/listbox.state.d.mts +7 -0
- package/dist/js/internal/listbox/listbox.state.mjs +40 -0
- package/dist/js/internal/misc.d.mts +6 -0
- package/dist/js/internal/misc.mjs +13 -0
- package/dist/js/l10n-Bn-7RcwR.d.mts +20 -0
- package/dist/js/l10n.d.mts +2 -20
- package/dist/js/listbox/constants.listbox.d.mts +2 -0
- package/dist/js/listbox/constants.listbox.mjs +40 -0
- package/dist/js/listbox/event.listbox.d.mts +8 -0
- package/dist/js/listbox/event.listbox.mjs +2 -0
- package/dist/js/listbox/index.d.mts +116 -0
- package/dist/js/listbox/index.mjs +189 -0
- package/dist/js/listbox/misc.listbox.d.mts +2 -0
- package/dist/js/listbox/misc.listbox.mjs +2 -0
- package/dist/js/listbox/state.listbox.d.mts +11 -0
- package/dist/js/listbox/state.listbox.mjs +2 -0
- package/dist/js/listbox.data-BWd0PCy7.mjs +424 -0
- package/dist/js/listbox.model-CRReGOmu.d.mts +125 -0
- package/dist/js/misc.listbox-BI-XsTOf.mjs +197 -0
- package/dist/js/misc.listbox-gnoJhrTr.d.mts +20 -0
- package/dist/js/sortable/sortable.embedded.mjs +4 -2
- package/dist/js/state.combobox-4xq0FPCF.mjs +365 -0
- package/package.json +7 -3
- package/src/css/combobox.scss +1 -1
- package/src/css/listbox.scss +149 -0
- package/src/js/combobox/constants.combobox.ts +46 -25
- package/src/js/combobox/dom.combobox.ts +36 -454
- package/src/js/combobox/event.combobox.ts +129 -218
- package/src/js/combobox/index.ts +37 -30
- package/src/js/combobox/misc.combobox.ts +30 -114
- package/src/js/combobox/models.combobox.ts +7 -47
- package/src/js/combobox/state.combobox.ts +126 -127
- package/src/js/index.ts +1 -0
- package/src/js/internal/listbox/listbox.constants.ts +25 -0
- package/src/js/{combobox/data.combobox.ts → internal/listbox/listbox.data.ts} +94 -43
- package/src/js/internal/listbox/listbox.dom.ts +445 -0
- package/src/js/internal/listbox/listbox.event.ts +129 -0
- package/src/js/{combobox/form.combobox.ts → internal/listbox/listbox.form.ts} +12 -13
- package/src/js/internal/listbox/listbox.misc.ts +135 -0
- package/src/js/internal/listbox/listbox.model.ts +169 -0
- package/src/js/internal/listbox/listbox.state.ts +82 -0
- package/src/js/internal/misc.ts +27 -0
- package/src/js/l10n.ts +8 -8
- package/src/js/listbox/constants.listbox.ts +61 -0
- package/src/js/listbox/event.listbox.ts +106 -0
- package/src/js/listbox/index.ts +302 -0
- package/src/js/listbox/misc.listbox.ts +70 -0
- package/src/js/listbox/state.listbox.ts +196 -0
- package/dist/js/combobox/data.combobox.d.mts +0 -18
- package/dist/js/combobox/data.combobox.mjs +0 -2
- package/dist/js/combobox/form.combobox.d.mts +0 -6
- package/dist/js/combobox/form.combobox.mjs +0 -2
- package/dist/js/constants.combobox-h_kyDegw.d.mts +0 -248
- package/dist/js/data.combobox-B8HMkDOh.mjs +0 -1030
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
2
|
-
import {
|
|
2
|
+
import {findAncestor, getData} from '@oscarpalmer/toretto';
|
|
3
3
|
import {on} from '@oscarpalmer/toretto/event';
|
|
4
|
+
import {LISTBOX_KEYS} from '../internal/listbox/listbox.constants';
|
|
5
|
+
import {
|
|
6
|
+
applyListboxSelection,
|
|
7
|
+
filterListbox,
|
|
8
|
+
filterListboxOption,
|
|
9
|
+
filterListboxOptions,
|
|
10
|
+
toggleListboxOption,
|
|
11
|
+
} from '../internal/listbox/listbox.data';
|
|
12
|
+
import {updateListboxPreview} from '../internal/listbox/listbox.dom';
|
|
13
|
+
import {processListboxKeydown} from '../internal/listbox/listbox.event';
|
|
14
|
+
import type {OuiListboxPreview} from '../internal/listbox/listbox.model';
|
|
4
15
|
import {
|
|
5
16
|
COMBOBOX_ATTRIBUTE_SELECTION_ITEM,
|
|
6
|
-
|
|
7
|
-
COMBOBOX_KEYS_NAVIGATION,
|
|
8
|
-
COMBOBOX_KEYS_SELECTION,
|
|
9
|
-
COMBOBOX_SELECTOR_ANCHOR,
|
|
10
|
-
COMBOBOX_SELECTOR_OPTION,
|
|
11
|
-
COMBOBOX_SELECTOR_SELECTION_ITEM,
|
|
17
|
+
COMBOBOX_SELECTORS,
|
|
12
18
|
COMBOBOX_TAGNAME,
|
|
13
19
|
} from './constants.combobox';
|
|
14
20
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from './data.combobox';
|
|
20
|
-
import {updateComboboxPreview, updateComboboxTabindices} from './dom.combobox';
|
|
21
|
+
updateComboboxAutocomplete,
|
|
22
|
+
updateComboboxSelection,
|
|
23
|
+
updateComboboxTabindices,
|
|
24
|
+
} from './dom.combobox';
|
|
21
25
|
import type {OuiComboboxElement} from './index';
|
|
22
|
-
import {
|
|
23
|
-
import type {
|
|
26
|
+
import {hasTextSelection} from './misc.combobox';
|
|
27
|
+
import type {OuiComboboxState} from './models.combobox';
|
|
24
28
|
import {comboboxStates} from './state.combobox';
|
|
25
29
|
|
|
26
30
|
// #region Functions
|
|
27
31
|
|
|
28
|
-
export function
|
|
29
|
-
|
|
32
|
+
export function onComboboxActivation(state: OuiComboboxState): void {
|
|
33
|
+
state.floatable.show();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function onComboboxApply(
|
|
37
|
+
state: OuiComboboxState,
|
|
38
|
+
option: HTMLElement | undefined,
|
|
39
|
+
_: string[],
|
|
40
|
+
added: boolean,
|
|
41
|
+
selected?: HTMLElement[],
|
|
42
|
+
): void {
|
|
43
|
+
updateComboboxSelection(state, option, added, selected);
|
|
44
|
+
}
|
|
30
45
|
|
|
46
|
+
export function onComboboxClickContent(event: PointerEvent, state: OuiComboboxState): void {
|
|
31
47
|
if (state == null) {
|
|
32
48
|
return;
|
|
33
49
|
}
|
|
@@ -36,25 +52,31 @@ export function onComboboxClickContent(event: PointerEvent, content: HTMLElement
|
|
|
36
52
|
return;
|
|
37
53
|
}
|
|
38
54
|
|
|
39
|
-
const option = findAncestor(event,
|
|
55
|
+
const option = findAncestor(event, COMBOBOX_SELECTORS.option);
|
|
40
56
|
|
|
41
57
|
if (!(option instanceof HTMLElement)) {
|
|
42
58
|
state.filter.element.focus();
|
|
43
59
|
} else if (state.readonly) {
|
|
44
|
-
|
|
60
|
+
onComboboxPreview('set', state, option);
|
|
45
61
|
} else {
|
|
46
|
-
|
|
62
|
+
toggleListboxOption(state, option);
|
|
47
63
|
}
|
|
48
64
|
}
|
|
49
65
|
|
|
50
|
-
|
|
51
|
-
const combobox = findAncestor(event, COMBOBOX_TAGNAME)
|
|
66
|
+
function onComboboxClickDocument(event: PointerEvent): void {
|
|
67
|
+
const combobox = findAncestor(event, COMBOBOX_TAGNAME);
|
|
52
68
|
const state = comboboxStates.map.get(combobox!);
|
|
53
69
|
|
|
54
70
|
if (combobox == null || state == null) {
|
|
55
71
|
return;
|
|
56
72
|
}
|
|
57
73
|
|
|
74
|
+
if (findAncestor(event, COMBOBOX_SELECTORS.content) != null) {
|
|
75
|
+
onComboboxClickContent(event, state);
|
|
76
|
+
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
58
80
|
const label = findAncestor(event, 'label');
|
|
59
81
|
|
|
60
82
|
if (label != null) {
|
|
@@ -81,13 +103,13 @@ export function onComboboxClickDocument(event: PointerEvent): void {
|
|
|
81
103
|
} else if (state.readonly) {
|
|
82
104
|
updateComboboxTabindices(state, option);
|
|
83
105
|
} else {
|
|
84
|
-
|
|
106
|
+
toggleListboxOption(state, option);
|
|
85
107
|
}
|
|
86
108
|
|
|
87
109
|
return;
|
|
88
110
|
}
|
|
89
111
|
|
|
90
|
-
const anchor = findAncestor(event,
|
|
112
|
+
const anchor = findAncestor(event, COMBOBOX_SELECTORS.anchor);
|
|
91
113
|
|
|
92
114
|
if (anchor != null && !state.disabled) {
|
|
93
115
|
state.floatable.show();
|
|
@@ -102,9 +124,9 @@ export function onComboboxEnter(state: OuiComboboxState): void {
|
|
|
102
124
|
}
|
|
103
125
|
|
|
104
126
|
if (hasTextSelection(state.anchor.element)) {
|
|
105
|
-
onComboboxSelection(state);
|
|
127
|
+
onComboboxSelection(state, true);
|
|
106
128
|
} else {
|
|
107
|
-
|
|
129
|
+
toggleListboxOption(state, state.active);
|
|
108
130
|
}
|
|
109
131
|
}
|
|
110
132
|
|
|
@@ -113,66 +135,48 @@ export function onComboboxEscape(state: OuiComboboxState): void {
|
|
|
113
135
|
}
|
|
114
136
|
|
|
115
137
|
export function onComboboxFilter(
|
|
116
|
-
|
|
138
|
+
preview: OuiListboxPreview,
|
|
117
139
|
state: OuiComboboxState,
|
|
118
|
-
|
|
140
|
+
update: boolean,
|
|
141
|
+
active?: HTMLElement,
|
|
119
142
|
emit?: boolean,
|
|
120
143
|
): void {
|
|
121
|
-
if (state.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
state
|
|
144
|
+
if (state.autocomplete.none) {
|
|
145
|
+
applyListboxSelection({
|
|
146
|
+
state,
|
|
147
|
+
added: false,
|
|
148
|
+
emit: (emit ?? true) && update,
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
updateListboxPreview(preview, state, active);
|
|
129
152
|
}
|
|
153
|
+
}
|
|
130
154
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
let update: OuiComboboxPreview | undefined;
|
|
136
|
-
|
|
137
|
-
if (type.includes('delete')) {
|
|
138
|
-
update = 'none';
|
|
139
|
-
} else if (type.includes('insert')) {
|
|
140
|
-
update = 'typed';
|
|
141
|
-
}
|
|
155
|
+
export function onComboboxFocusout(event: FocusEvent): void {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
const combobox = findAncestor(event, COMBOBOX_TAGNAME);
|
|
158
|
+
const state = comboboxStates.map.get(combobox!);
|
|
142
159
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
if (
|
|
161
|
+
combobox == null ||
|
|
162
|
+
state == null ||
|
|
163
|
+
state.disabled ||
|
|
164
|
+
state.readonly ||
|
|
165
|
+
state.type === 'select' ||
|
|
166
|
+
state.host === document.activeElement ||
|
|
167
|
+
state.host.contains(document.activeElement)
|
|
168
|
+
) {
|
|
169
|
+
return;
|
|
148
170
|
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (diff) {
|
|
152
|
-
state.filter.value = state.filter.element.value;
|
|
153
|
-
|
|
154
|
-
dispatch(state.host, 'input');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
171
|
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
state.disabled ||
|
|
161
|
-
state.readonly ||
|
|
162
|
-
state.type === 'select' ||
|
|
163
|
-
state.host === document.activeElement ||
|
|
164
|
-
state.host.contains(document.activeElement)
|
|
165
|
-
) {
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
172
|
+
const option = filterListboxOption(state, state.filter.element.value, true);
|
|
168
173
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
selected: option == null ? [] : [option],
|
|
174
|
+
applyListboxSelection({
|
|
175
|
+
option,
|
|
176
|
+
state,
|
|
177
|
+
added: false,
|
|
178
|
+
selected: option == null ? [] : [option],
|
|
179
|
+
});
|
|
176
180
|
});
|
|
177
181
|
}
|
|
178
182
|
|
|
@@ -181,7 +185,7 @@ export function onComboboxInput(event: InputEvent): void {
|
|
|
181
185
|
const state = comboboxStates.map.get(combobox!);
|
|
182
186
|
|
|
183
187
|
if (combobox != null && state != null && event.target === state.filter.element) {
|
|
184
|
-
|
|
188
|
+
filterListbox(event.inputType, state);
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
|
|
@@ -193,84 +197,12 @@ export function onComboboxKeydown(event: KeyboardEvent): void {
|
|
|
193
197
|
return;
|
|
194
198
|
}
|
|
195
199
|
|
|
196
|
-
const item = findAncestor(event,
|
|
200
|
+
const item = findAncestor(event, COMBOBOX_SELECTORS.selectionItem);
|
|
197
201
|
|
|
198
202
|
if (item instanceof HTMLElement) {
|
|
199
203
|
onComboboxKeydownSelection(event, state, item);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
onComboboxKeydownFilter(event, state);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function onComboboxKeydownFilter(event: KeyboardEvent, state: OuiComboboxState): void {
|
|
208
|
-
const ignore = state.disabled || state.readonly;
|
|
209
|
-
const isSelect = state.type === 'select';
|
|
210
|
-
|
|
211
|
-
let preventDefault = ignore;
|
|
212
|
-
|
|
213
|
-
switch (true) {
|
|
214
|
-
case event.key === ' ': {
|
|
215
|
-
if (!state.disabled) {
|
|
216
|
-
state.floatable.show();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
case event.key === 'Enter': {
|
|
223
|
-
preventDefault = state.floatable.open;
|
|
224
|
-
|
|
225
|
-
if (preventDefault) {
|
|
226
|
-
onComboboxEnter(state);
|
|
227
|
-
} else if (!ignore && state.type === 'select') {
|
|
228
|
-
state.floatable.show();
|
|
229
|
-
} else if (!ignore) {
|
|
230
|
-
state.internals.form?.requestSubmit();
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
case event.key === 'Escape': {
|
|
237
|
-
preventDefault = false;
|
|
238
|
-
|
|
239
|
-
onComboboxEscape(state);
|
|
240
|
-
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
case !isSelect && COMBOBOX_KEYS_SELECTION.has(event.key): {
|
|
245
|
-
if (!ignore && hasTextSelection(state.anchor.element)) {
|
|
246
|
-
onComboboxSelection(state);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
case COMBOBOX_KEYS_NAVIGATION.has(event.key): {
|
|
253
|
-
if (!state.disabled) {
|
|
254
|
-
onComboboxNavigation(event, state);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
default: {
|
|
261
|
-
preventDefault = false;
|
|
262
|
-
|
|
263
|
-
if (event.key === 'Tab') {
|
|
264
|
-
onComboboxTab(state);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (preventDefault) {
|
|
272
|
-
event.preventDefault();
|
|
273
|
-
event.stopPropagation();
|
|
204
|
+
} else {
|
|
205
|
+
processListboxKeydown(event, state);
|
|
274
206
|
}
|
|
275
207
|
}
|
|
276
208
|
|
|
@@ -279,7 +211,7 @@ export function onComboboxKeydownSelection(
|
|
|
279
211
|
state: OuiComboboxState,
|
|
280
212
|
item: HTMLElement,
|
|
281
213
|
): void {
|
|
282
|
-
if (
|
|
214
|
+
if (LISTBOX_KEYS.activation.has(event.key)) {
|
|
283
215
|
event.preventDefault();
|
|
284
216
|
|
|
285
217
|
onComboboxRemoveSelection(state, item);
|
|
@@ -338,67 +270,12 @@ export function onComboboxKeydownSelection(
|
|
|
338
270
|
}
|
|
339
271
|
}
|
|
340
272
|
|
|
341
|
-
export function
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
state.floatable.show();
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const visible = getComboboxVisibleOptions(state);
|
|
351
|
-
|
|
352
|
-
if (visible.length === 0) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
let next: HTMLElement | undefined;
|
|
357
|
-
|
|
358
|
-
if (skip || state.active == null) {
|
|
359
|
-
next = state.active ?? visible.at(event.key === 'ArrowDown' ? 0 : -1);
|
|
360
|
-
} else {
|
|
361
|
-
const current = visible.findIndex(option => option.id === state.active?.id);
|
|
362
|
-
|
|
363
|
-
let index: number | undefined;
|
|
364
|
-
|
|
365
|
-
switch (event.key) {
|
|
366
|
-
case 'ArrowDown':
|
|
367
|
-
case 'ArrowUp':
|
|
368
|
-
index = clamp(current + (event.key === 'ArrowDown' ? 1 : -1), 0, visible.length - 1, true);
|
|
369
|
-
break;
|
|
370
|
-
|
|
371
|
-
case 'End':
|
|
372
|
-
index = visible.length - 1;
|
|
373
|
-
break;
|
|
374
|
-
|
|
375
|
-
case 'Home':
|
|
376
|
-
index = 0;
|
|
377
|
-
break;
|
|
378
|
-
|
|
379
|
-
case 'PageDown':
|
|
380
|
-
case 'PageUp':
|
|
381
|
-
index = clamp(current + (event.key === 'PageDown' ? 10 : -10), 0, visible.length - 1);
|
|
382
|
-
break;
|
|
383
|
-
|
|
384
|
-
default:
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (index != null) {
|
|
389
|
-
next = visible[index];
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
let preview: OuiComboboxPreview;
|
|
394
|
-
|
|
395
|
-
if (state.type === 'autocomplete' && (state.autocomplete.both || state.autocomplete.inline)) {
|
|
396
|
-
preview = 'set';
|
|
397
|
-
} else {
|
|
398
|
-
preview = 'none';
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
updateComboboxPreview(preview, state, next ?? state.active ?? visible[0]);
|
|
273
|
+
export function onComboboxPreview(
|
|
274
|
+
preview: OuiListboxPreview,
|
|
275
|
+
state: OuiComboboxState,
|
|
276
|
+
active?: HTMLElement,
|
|
277
|
+
): void {
|
|
278
|
+
updateComboboxAutocomplete(preview, state, active);
|
|
402
279
|
}
|
|
403
280
|
|
|
404
281
|
export function onComboboxRemoveSelection(state: OuiComboboxState, item: HTMLElement): void {
|
|
@@ -411,15 +288,27 @@ export function onComboboxRemoveSelection(state: OuiComboboxState, item: HTMLEle
|
|
|
411
288
|
);
|
|
412
289
|
|
|
413
290
|
if (option != null) {
|
|
414
|
-
|
|
291
|
+
toggleListboxOption(state, option);
|
|
415
292
|
}
|
|
416
293
|
}
|
|
417
294
|
|
|
418
|
-
export function
|
|
295
|
+
export function onComboboxNavigation(
|
|
296
|
+
preview: OuiListboxPreview,
|
|
297
|
+
state: OuiComboboxState,
|
|
298
|
+
active?: HTMLElement,
|
|
299
|
+
): void {
|
|
300
|
+
updateListboxPreview(preview, state, active);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function onComboboxSelection(state: OuiComboboxState, hasSelection?: boolean): void {
|
|
304
|
+
if (!(hasSelection ?? hasTextSelection(state.anchor.element))) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
419
308
|
state.filter.value =
|
|
420
309
|
state.anchor.element === state.filter.element ? state.filter.element.value : '';
|
|
421
310
|
|
|
422
|
-
|
|
311
|
+
toggleListboxOption(state, filterListboxOptions(state) ?? state.active);
|
|
423
312
|
}
|
|
424
313
|
|
|
425
314
|
export function onComboboxTab(state: OuiComboboxState): void {
|
|
@@ -437,12 +326,34 @@ export function onComboboxTab(state: OuiComboboxState): void {
|
|
|
437
326
|
});
|
|
438
327
|
}
|
|
439
328
|
|
|
329
|
+
export function onComboboxToggle(state: OuiComboboxState): void {
|
|
330
|
+
if (!state.multiple) {
|
|
331
|
+
state.floatable.hide();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function preventComboboxActivation(state: OuiComboboxState): boolean {
|
|
336
|
+
return state.floatable.open;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export function skipComboboxNavigation(state: OuiComboboxState): boolean {
|
|
340
|
+
if (state.floatable.open) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
state.floatable.show();
|
|
345
|
+
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
440
349
|
// #endregion
|
|
441
350
|
|
|
442
351
|
// #region Initialization
|
|
443
352
|
|
|
444
353
|
on(document, 'click', onComboboxClickDocument);
|
|
445
354
|
|
|
355
|
+
on(document, 'focusout', onComboboxFocusout);
|
|
356
|
+
|
|
446
357
|
on(document, 'input', onComboboxInput, {
|
|
447
358
|
passive: false,
|
|
448
359
|
});
|
package/src/js/combobox/index.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import {parse} from '@oscarpalmer/atoms/string';
|
|
2
2
|
import {getAttribute} from '@oscarpalmer/toretto/attribute';
|
|
3
|
+
import {setListboxValue, setListboxValues} from '../internal/listbox/listbox.data';
|
|
4
|
+
import {
|
|
5
|
+
setListboxDisabled,
|
|
6
|
+
setListboxFilter,
|
|
7
|
+
setListboxLimit,
|
|
8
|
+
setListboxReadonly,
|
|
9
|
+
setListboxRequired,
|
|
10
|
+
} from '../internal/listbox/listbox.misc';
|
|
11
|
+
import type {OuiListboxLocalizations, OuiListboxType} from '../internal/listbox/listbox.model';
|
|
12
|
+
import {attachListbox, detachListbox} from '../internal/listbox/listbox.state';
|
|
3
13
|
import {updateLocalizations} from '../l10n';
|
|
4
14
|
import {COMBOBOX_TAGNAME} from './constants.combobox';
|
|
5
|
-
import {
|
|
6
|
-
import {setComboboxDisabled, setComboboxReadonly, setComboboxRequired} from './dom.combobox';
|
|
7
|
-
import {setComboboxFilter, setComboboxLimit} from './misc.combobox';
|
|
8
|
-
import type {OuiComboboxLocalizations, OuiComboboxState, OuiComboboxType} from './models.combobox';
|
|
15
|
+
import type {OuiComboboxState} from './models.combobox';
|
|
9
16
|
import {
|
|
10
|
-
|
|
11
|
-
comboboxLocalizations,
|
|
17
|
+
comboboxStates,
|
|
12
18
|
createComboboxState,
|
|
13
|
-
|
|
19
|
+
onAttachCombobox,
|
|
20
|
+
onDetachCombobox,
|
|
14
21
|
} from './state.combobox';
|
|
15
22
|
|
|
16
23
|
// #region Types
|
|
@@ -44,7 +51,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
44
51
|
*/
|
|
45
52
|
set disabled(value: boolean) {
|
|
46
53
|
if (typeof value === 'boolean' && value !== this.disabled) {
|
|
47
|
-
|
|
54
|
+
setListboxDisabled(this.#state, value);
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
|
|
@@ -59,7 +66,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
59
66
|
* Set the filter value
|
|
60
67
|
*/
|
|
61
68
|
set filter(value: string) {
|
|
62
|
-
|
|
69
|
+
setListboxFilter(this.#state, value);
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
/**
|
|
@@ -73,7 +80,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
73
80
|
* Set the maximum number of selected options
|
|
74
81
|
*/
|
|
75
82
|
set max(value: number) {
|
|
76
|
-
|
|
83
|
+
setListboxLimit('max', this.#state, value);
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
/**
|
|
@@ -87,7 +94,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
87
94
|
* Set the minimum number of selected options
|
|
88
95
|
*/
|
|
89
96
|
set min(value: number) {
|
|
90
|
-
|
|
97
|
+
setListboxLimit('min', this.#state, value);
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
/**
|
|
@@ -116,7 +123,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
116
123
|
*/
|
|
117
124
|
set readonly(value: boolean) {
|
|
118
125
|
if (typeof value === 'boolean' && value !== this.readonly) {
|
|
119
|
-
|
|
126
|
+
setListboxReadonly(this.#state, value);
|
|
120
127
|
}
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -132,7 +139,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
132
139
|
*/
|
|
133
140
|
set required(value: boolean) {
|
|
134
141
|
if (typeof value === 'boolean' && value !== this.required) {
|
|
135
|
-
|
|
142
|
+
setListboxRequired(this.#state, value);
|
|
136
143
|
}
|
|
137
144
|
}
|
|
138
145
|
|
|
@@ -146,7 +153,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
146
153
|
/**
|
|
147
154
|
* Type of combobox
|
|
148
155
|
*/
|
|
149
|
-
get type():
|
|
156
|
+
get type(): OuiListboxType {
|
|
150
157
|
return this.#state.type;
|
|
151
158
|
}
|
|
152
159
|
|
|
@@ -161,7 +168,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
161
168
|
* Set the value
|
|
162
169
|
*/
|
|
163
170
|
set value(value: unknown) {
|
|
164
|
-
|
|
171
|
+
setListboxValue(this.#state, value);
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
/**
|
|
@@ -174,8 +181,8 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
174
181
|
/**
|
|
175
182
|
* Set the values
|
|
176
183
|
*/
|
|
177
|
-
set values(
|
|
178
|
-
|
|
184
|
+
set values(values: unknown[]) {
|
|
185
|
+
setListboxValues(this.#state, values);
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
constructor() {
|
|
@@ -196,7 +203,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
196
203
|
|
|
197
204
|
case 'max':
|
|
198
205
|
case 'min':
|
|
199
|
-
|
|
206
|
+
setListboxLimit(name, this.#state, value);
|
|
200
207
|
break;
|
|
201
208
|
|
|
202
209
|
case 'value':
|
|
@@ -212,20 +219,20 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
212
219
|
* Clear all selected options
|
|
213
220
|
*/
|
|
214
221
|
clear(): void {
|
|
215
|
-
|
|
222
|
+
setListboxValues(this.#state, []);
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
connectedCallback(): void {
|
|
219
226
|
if (this.#state == null) {
|
|
220
227
|
this.#initialize();
|
|
221
228
|
} else {
|
|
222
|
-
|
|
229
|
+
attachListbox(this.#state, comboboxStates, onAttachCombobox);
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
|
|
226
233
|
disconnectedCallback(): void {
|
|
227
234
|
if (this.#state != null) {
|
|
228
|
-
|
|
235
|
+
detachListbox(this.#state, comboboxStates, onDetachCombobox);
|
|
229
236
|
}
|
|
230
237
|
}
|
|
231
238
|
|
|
@@ -234,7 +241,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
234
241
|
}
|
|
235
242
|
|
|
236
243
|
formResetCallback(): void {
|
|
237
|
-
|
|
244
|
+
setListboxValues(this.#state, this.#state.values.initial);
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
formStateRestoreCallback(state: string | File | FormData, reason: unknown): void {
|
|
@@ -251,7 +258,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
251
258
|
if (this.#state == null) {
|
|
252
259
|
this.#restored = parsed;
|
|
253
260
|
} else {
|
|
254
|
-
|
|
261
|
+
setListboxValues(this.#state, parsed);
|
|
255
262
|
}
|
|
256
263
|
}
|
|
257
264
|
|
|
@@ -261,27 +268,27 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
261
268
|
* @param language Language code
|
|
262
269
|
* @param localizations Localizations for language
|
|
263
270
|
*/
|
|
264
|
-
setLocalizations(language: string, localizations: Partial<
|
|
271
|
+
setLocalizations(language: string, localizations: Partial<OuiListboxLocalizations>): void;
|
|
265
272
|
|
|
266
273
|
/**
|
|
267
274
|
* Set combobox localizations for one or more languages
|
|
268
275
|
*
|
|
269
276
|
* @param localizations Localizations for one or more languages
|
|
270
277
|
*/
|
|
271
|
-
setLocalizations(localizations: Record<string, Partial<
|
|
278
|
+
setLocalizations(localizations: Record<string, Partial<OuiListboxLocalizations>>): void;
|
|
272
279
|
|
|
273
280
|
setLocalizations(
|
|
274
|
-
first: string | Record<string, Partial<
|
|
275
|
-
second?: Partial<
|
|
281
|
+
first: string | Record<string, Partial<OuiListboxLocalizations>>,
|
|
282
|
+
second?: Partial<OuiListboxLocalizations>,
|
|
276
283
|
): void {
|
|
277
284
|
updateLocalizations(
|
|
278
|
-
|
|
285
|
+
this.#state.localizations.global,
|
|
279
286
|
typeof first === 'string'
|
|
280
287
|
? {
|
|
281
288
|
[first]: second!,
|
|
282
289
|
}
|
|
283
290
|
: first,
|
|
284
|
-
this.#state.localizations,
|
|
291
|
+
this.#state.localizations.component,
|
|
285
292
|
);
|
|
286
293
|
}
|
|
287
294
|
|
|
@@ -291,7 +298,7 @@ export class OuiComboboxElement extends HTMLElement {
|
|
|
291
298
|
this.#state = createComboboxState(this, this.#internals);
|
|
292
299
|
|
|
293
300
|
if (this.#restored != null) {
|
|
294
|
-
|
|
301
|
+
setListboxValues(this.#state, this.#restored);
|
|
295
302
|
|
|
296
303
|
this.#restored = undefined;
|
|
297
304
|
}
|