@wpmoo/ui 1.0.0-rc.1 → 1.0.0-rc.11
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/ASSET_LICENSE.md +4 -2
- package/LICENSE +28 -0
- package/README.md +105 -100
- package/certification.json +25 -3
- package/dist/assets/css/moo-ui.css +14068 -12458
- package/dist/assets/css/moo-ui.min.css +10 -28
- package/dist/assets/css/moo.css +3694 -1928
- package/dist/assets/css/moo.min.css +10 -29
- package/dist/js/chart.js +15312 -0
- package/dist/js/chart.min.js +27 -0
- package/dist/js/combobox.js +129 -85
- package/dist/js/context-menu.js +35 -66
- package/dist/js/datatable.js +390 -188
- package/dist/js/datepicker.js +1197 -0
- package/dist/js/datepicker.min.js +7 -0
- package/dist/js/moo-ui.js +3947 -0
- package/dist/js/moo-ui.min.js +7 -0
- package/dist/js/sheet.js +46 -0
- package/dist/js/sidebar.js +190 -172
- package/dist/js/slider.js +289 -0
- package/dist/js/state.js +76 -0
- package/dist/release-manifest.json +24 -0
- package/package.json +39 -9
- package/scss/_components.scss +70 -0
- package/scss/_config.scss +18 -0
- package/scss/_settings.scss +5 -0
- package/scss/components/_accordion.scss +54 -0
- package/scss/components/_alert.scss +82 -0
- package/scss/components/_avatar.scss +139 -0
- package/scss/components/_badge.scss +34 -0
- package/scss/components/_breadcrumb.scss +119 -0
- package/scss/components/_button.scss +200 -0
- package/scss/components/_button_group.scss +57 -0
- package/scss/components/_card.scss +114 -0
- package/scss/components/_checkbox.scss +78 -0
- package/scss/components/_close_button.scss +56 -0
- package/scss/components/_collapsible.scss +44 -0
- package/scss/components/_combobox.scss +253 -0
- package/scss/components/_context-menu.scss +18 -0
- package/scss/components/_datatable.scss +1067 -0
- package/scss/components/_datepicker.scss +237 -0
- package/scss/components/_dialog.scss +69 -0
- package/scss/components/_dropdown.scss +201 -0
- package/scss/components/_field.scss +94 -0
- package/scss/components/_input.scss +31 -0
- package/scss/components/_input_group.scss +151 -0
- package/scss/components/_kbd.scss +11 -0
- package/scss/components/_menubar.scss +17 -0
- package/scss/components/_navigation.scss +111 -0
- package/scss/components/_pagination.scss +58 -0
- package/scss/components/_popover.scss +26 -0
- package/scss/components/_progress.scss +5 -0
- package/scss/components/_radio_group.scss +108 -0
- package/scss/components/_search_trigger.scss +61 -0
- package/scss/components/_select.scss +8 -0
- package/scss/components/_separator.scss +10 -0
- package/scss/components/_sheet.scss +16 -0
- package/scss/components/_sidebar.scss +5 -0
- package/scss/components/_skeleton.scss +10 -0
- package/scss/components/_slider.scss +213 -0
- package/scss/components/_spinner.scss +11 -0
- package/scss/components/_switch.scss +30 -0
- package/scss/components/_table.scss +12 -0
- package/scss/components/_tabs.scss +116 -0
- package/scss/components/_textarea.scss +8 -0
- package/scss/components/_toast.scss +116 -0
- package/scss/components/_toggle_group.scss +17 -0
- package/scss/components/_tooltip.scss +64 -0
- package/scss/components/sidebar/_base.scss +86 -0
- package/scss/components/sidebar/_collapsed.scss +112 -0
- package/scss/components/sidebar/_identity.scss +71 -0
- package/scss/components/sidebar/_menus.scss +219 -0
- package/scss/foundations/_backdrop.scss +102 -0
- package/scss/foundations/_focus.scss +83 -0
- package/scss/foundations/_globals.scss +5 -0
- package/scss/foundations/_scope.scss +27 -0
- package/scss/layouts/_app.scss +75 -0
- package/scss/layouts/_page_grid.scss +27 -0
- package/scss/layouts/app/sidebar/_base.scss +84 -0
- package/scss/layouts/app/sidebar/_default.scss +20 -0
- package/scss/layouts/app/sidebar/_floating.scss +36 -0
- package/scss/layouts/app/sidebar/_inset.scss +32 -0
- package/scss/mixins/_banner.scss +9 -0
- package/scss/moo-core.scss +16 -0
- package/scss/moo-ui.scss +54 -0
- package/scss/settings/_bootstrap_overrides.scss +7 -0
- package/scss/settings/_component_variables.scss +244 -0
- package/scss/settings/_forms.scss +28 -0
- package/scss/settings/_options.scss +2 -0
- package/scss/settings/_palette.scss +90 -0
- package/scss/themes/_forms.scss +15 -0
- package/scss/themes/_root.scss +20 -0
- package/scss/themes/_standalone.scss +3 -0
- package/scss/themes/_theme.scss +244 -0
- package/scss/utilities/_background_color.scss +18 -0
- package/scss/utilities/_border.scss +4 -0
- package/scss/utilities/_scroll_fade.scss +193 -0
- package/scss/utilities/_scroll_fade_primitives.scss +43 -0
- package/scss/_facade-settings.scss +0 -51
- package/scss/settings/_facade_public.scss +0 -23
package/dist/js/combobox.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI combobox.js v1.0.0-rc.11 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
const normalize = (value) => value.trim().toLowerCase();
|
|
9
|
+
class Combobox {
|
|
5
10
|
static getInstance(element) {
|
|
6
11
|
return element?.nodeType === 1 ? instances.get(element) || null : null;
|
|
7
12
|
}
|
|
8
|
-
|
|
9
13
|
static getOrCreateInstance(element, config = {}) {
|
|
10
14
|
return Combobox.getInstance(element) || new Combobox(element, config);
|
|
11
15
|
}
|
|
12
|
-
|
|
13
16
|
constructor(element, config = {}) {
|
|
14
17
|
if (element?.nodeType !== 1 || !element.matches(".combobox")) {
|
|
15
18
|
throw new TypeError("Combobox requires a .combobox root element.");
|
|
@@ -18,7 +21,6 @@ export default class Combobox {
|
|
|
18
21
|
if (existing) {
|
|
19
22
|
return existing;
|
|
20
23
|
}
|
|
21
|
-
|
|
22
24
|
this._element = element;
|
|
23
25
|
this._document = element.ownerDocument;
|
|
24
26
|
this._window = this._document.defaultView;
|
|
@@ -29,46 +31,41 @@ export default class Combobox {
|
|
|
29
31
|
if (!this._input || !this._menu || this._options.length === 0) {
|
|
30
32
|
throw new TypeError("Combobox requires an input, menu, and at least one option.");
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
this._isMultiple = element.dataset.mooComboboxMultiple === "true";
|
|
34
|
+
this._isMultiple = element.dataset.comboboxMultiple === "true";
|
|
34
35
|
this._hidden = this._isMultiple ? null : element.querySelector('input[type="hidden"]');
|
|
35
36
|
this._chipValue = element.querySelector(".combobox-value");
|
|
36
|
-
this._valueStore = element.querySelector("[data-
|
|
37
|
-
this._chipIconTemplate = element.querySelector("[data-
|
|
38
|
-
this._clearTrigger = element.querySelector("[data-
|
|
37
|
+
this._valueStore = element.querySelector("[data-combobox-value-store]");
|
|
38
|
+
this._chipIconTemplate = element.querySelector("[data-combobox-chip-icon]");
|
|
39
|
+
this._clearTrigger = element.querySelector("[data-combobox-clear]");
|
|
39
40
|
this._indicator = element.querySelector(".combobox-indicator");
|
|
40
|
-
this._groups = Array.from(element.querySelectorAll("[data-
|
|
41
|
-
this._separators = Array.from(element.querySelectorAll("[data-
|
|
41
|
+
this._groups = Array.from(element.querySelectorAll("[data-combobox-group]"));
|
|
42
|
+
this._separators = Array.from(element.querySelectorAll("[data-combobox-separator]"));
|
|
42
43
|
this._listeners = [];
|
|
43
44
|
this._generated = [];
|
|
44
45
|
this._startsOpen = this._menu.classList.contains("show");
|
|
45
|
-
|
|
46
|
-
this._empty = this._menu.querySelector("[data-moo-combobox-empty]");
|
|
46
|
+
this._empty = this._menu.querySelector("[data-combobox-empty]");
|
|
47
47
|
if (!this._empty) {
|
|
48
48
|
this._empty = this._document.createElement("li");
|
|
49
49
|
this._empty.className = "combobox-empty";
|
|
50
50
|
this._empty.hidden = true;
|
|
51
|
-
this._empty.dataset.
|
|
51
|
+
this._empty.dataset.comboboxEmpty = "true";
|
|
52
52
|
this._empty.textContent = this._config.noResultsText;
|
|
53
53
|
this._menu.appendChild(this._empty);
|
|
54
54
|
this._generated.push(this._empty);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
this._liveRegion = element.querySelector("[data-moo-combobox-live]");
|
|
56
|
+
this._liveRegion = element.querySelector("[data-combobox-live]");
|
|
58
57
|
if (!this._liveRegion) {
|
|
59
58
|
this._liveRegion = this._document.createElement("span");
|
|
60
59
|
this._liveRegion.className = "visually-hidden";
|
|
61
60
|
this._liveRegion.setAttribute("aria-live", "polite");
|
|
62
|
-
this._liveRegion.dataset.
|
|
61
|
+
this._liveRegion.dataset.comboboxLive = "true";
|
|
63
62
|
element.appendChild(this._liveRegion);
|
|
64
63
|
this._generated.push(this._liveRegion);
|
|
65
64
|
}
|
|
66
|
-
|
|
67
65
|
instances.set(element, this);
|
|
68
66
|
this._bindEvents();
|
|
69
67
|
this._toggleClear(
|
|
70
|
-
this._input.dataset.
|
|
71
|
-
element.dataset.mooComboboxSelected === "true"
|
|
68
|
+
this._input.dataset.comboboxSelected === "true" || element.dataset.comboboxSelected === "true"
|
|
72
69
|
);
|
|
73
70
|
this._syncMultipleValue();
|
|
74
71
|
this._filterOptions({ open: this._startsOpen, activate: this._startsOpen });
|
|
@@ -76,7 +73,6 @@ export default class Combobox {
|
|
|
76
73
|
this._closeMenu(false);
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
|
-
|
|
80
76
|
dispose() {
|
|
81
77
|
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
82
78
|
target.removeEventListener(type, handler, options);
|
|
@@ -85,38 +81,33 @@ export default class Combobox {
|
|
|
85
81
|
this._closeMenu(false);
|
|
86
82
|
this._generated.forEach((element) => element.remove());
|
|
87
83
|
this._generated = [];
|
|
88
|
-
this._chipValue?.querySelectorAll("[data-
|
|
89
|
-
this._valueStore?.querySelectorAll("[data-
|
|
84
|
+
this._chipValue?.querySelectorAll("[data-combobox-generated]").forEach((item) => item.remove());
|
|
85
|
+
this._valueStore?.querySelectorAll("[data-combobox-generated]").forEach((item) => item.remove());
|
|
90
86
|
instances.delete(this._element);
|
|
91
87
|
}
|
|
92
|
-
|
|
93
88
|
_listen(target, type, handler, options) {
|
|
94
89
|
target?.addEventListener(type, handler, options);
|
|
95
90
|
if (target) {
|
|
96
91
|
this._listeners.push({ target, type, handler, options });
|
|
97
92
|
}
|
|
98
93
|
}
|
|
99
|
-
|
|
100
94
|
_trigger(name, detail = {}) {
|
|
101
95
|
return this._element.dispatchEvent(
|
|
102
96
|
new this._window.CustomEvent(`${name}.moo.combobox`, {
|
|
103
97
|
bubbles: true,
|
|
104
98
|
cancelable: name === "show" || name === "hide",
|
|
105
|
-
detail
|
|
99
|
+
detail
|
|
106
100
|
})
|
|
107
101
|
);
|
|
108
102
|
}
|
|
109
|
-
|
|
110
103
|
_optionGroupIsHidden(option) {
|
|
111
|
-
return option.closest("[data-
|
|
104
|
+
return option.closest("[data-combobox-group]")?.hidden || false;
|
|
112
105
|
}
|
|
113
|
-
|
|
114
106
|
_visibleOptions() {
|
|
115
107
|
return this._options.filter(
|
|
116
108
|
(option) => !option.hidden && !option.disabled && !this._optionGroupIsHidden(option)
|
|
117
109
|
);
|
|
118
110
|
}
|
|
119
|
-
|
|
120
111
|
_syncGroupVisibility() {
|
|
121
112
|
if (this._groups.length === 0) {
|
|
122
113
|
return;
|
|
@@ -133,7 +124,6 @@ export default class Combobox {
|
|
|
133
124
|
separator.hidden = visibleGroupCount < 2;
|
|
134
125
|
});
|
|
135
126
|
}
|
|
136
|
-
|
|
137
127
|
_openMenu(emit = true) {
|
|
138
128
|
if (this._menu.classList.contains("show")) {
|
|
139
129
|
this._input.setAttribute("aria-expanded", "true");
|
|
@@ -148,7 +138,6 @@ export default class Combobox {
|
|
|
148
138
|
this._trigger("shown");
|
|
149
139
|
}
|
|
150
140
|
}
|
|
151
|
-
|
|
152
141
|
_closeMenu(emit = true) {
|
|
153
142
|
const wasOpen = this._menu.classList.contains("show");
|
|
154
143
|
if (wasOpen && emit && !this._trigger("hide")) {
|
|
@@ -157,24 +146,24 @@ export default class Combobox {
|
|
|
157
146
|
this._menu.classList.remove("show");
|
|
158
147
|
this._input.setAttribute("aria-expanded", "false");
|
|
159
148
|
this._input.removeAttribute("aria-activedescendant");
|
|
160
|
-
this._options.forEach((option) =>
|
|
149
|
+
this._options.forEach((option) => {
|
|
150
|
+
option.removeAttribute("aria-current");
|
|
151
|
+
option.classList.remove("active");
|
|
152
|
+
});
|
|
161
153
|
if (wasOpen && emit) {
|
|
162
154
|
this._trigger("hidden");
|
|
163
155
|
}
|
|
164
156
|
}
|
|
165
|
-
|
|
166
157
|
_optionLabel(option) {
|
|
167
158
|
return option?.querySelector(".combobox-option__label")?.textContent?.trim() || "";
|
|
168
159
|
}
|
|
169
|
-
|
|
170
160
|
_selectedOptions() {
|
|
171
161
|
return this._options.filter((option) => option.getAttribute("aria-selected") === "true");
|
|
172
162
|
}
|
|
173
|
-
|
|
174
163
|
_toggleClear(selected) {
|
|
175
164
|
const value = selected ? "true" : "false";
|
|
176
|
-
this._element.dataset.
|
|
177
|
-
this._input.dataset.
|
|
165
|
+
this._element.dataset.comboboxSelected = value;
|
|
166
|
+
this._input.dataset.comboboxSelected = value;
|
|
178
167
|
if (this._clearTrigger) {
|
|
179
168
|
this._clearTrigger.hidden = !selected;
|
|
180
169
|
if (this._indicator) {
|
|
@@ -182,14 +171,13 @@ export default class Combobox {
|
|
|
182
171
|
}
|
|
183
172
|
}
|
|
184
173
|
}
|
|
185
|
-
|
|
186
174
|
_createChip(option) {
|
|
187
175
|
const value = option.dataset.value || "";
|
|
188
176
|
const label = this._optionLabel(option);
|
|
189
177
|
const chip = this._document.createElement("span");
|
|
190
178
|
chip.className = "badge text-bg-secondary combobox-chip";
|
|
191
179
|
chip.dataset.value = value;
|
|
192
|
-
chip.dataset.
|
|
180
|
+
chip.dataset.comboboxGenerated = "true";
|
|
193
181
|
const labelElement = this._document.createElement("span");
|
|
194
182
|
labelElement.className = "combobox-chip__label";
|
|
195
183
|
labelElement.textContent = label;
|
|
@@ -198,13 +186,12 @@ export default class Combobox {
|
|
|
198
186
|
remove.className = "combobox-chip__remove";
|
|
199
187
|
remove.type = "button";
|
|
200
188
|
remove.dataset.value = value;
|
|
201
|
-
remove.dataset.
|
|
189
|
+
remove.dataset.comboboxChipRemove = "true";
|
|
202
190
|
remove.setAttribute("aria-label", `Remove ${label}`);
|
|
203
191
|
remove.append(this._chipIconTemplate?.content.cloneNode(true) || "x");
|
|
204
192
|
chip.appendChild(remove);
|
|
205
193
|
return chip;
|
|
206
194
|
}
|
|
207
|
-
|
|
208
195
|
_syncMultipleValue() {
|
|
209
196
|
if (!this._isMultiple) {
|
|
210
197
|
return;
|
|
@@ -215,26 +202,24 @@ export default class Combobox {
|
|
|
215
202
|
}
|
|
216
203
|
if (this._valueStore) {
|
|
217
204
|
this._valueStore.replaceChildren();
|
|
218
|
-
const name = this._valueStore.dataset.
|
|
205
|
+
const name = this._valueStore.dataset.comboboxName || "";
|
|
219
206
|
selected.forEach((option) => {
|
|
220
207
|
const field = this._document.createElement("input");
|
|
221
208
|
field.type = "hidden";
|
|
222
209
|
field.name = name;
|
|
223
210
|
field.value = option.dataset.value || "";
|
|
224
|
-
field.dataset.
|
|
211
|
+
field.dataset.comboboxGenerated = "true";
|
|
225
212
|
this._valueStore.appendChild(field);
|
|
226
213
|
});
|
|
227
214
|
}
|
|
228
215
|
this._toggleClear(selected.length > 0);
|
|
229
216
|
}
|
|
230
|
-
|
|
231
217
|
_emitChange() {
|
|
232
218
|
const selected = this._selectedOptions();
|
|
233
219
|
this._trigger("change", {
|
|
234
|
-
values: selected.map((option) => option.dataset.value || "")
|
|
220
|
+
values: selected.map((option) => option.dataset.value || "")
|
|
235
221
|
});
|
|
236
222
|
}
|
|
237
|
-
|
|
238
223
|
_removeChip(value) {
|
|
239
224
|
const option = this._options.find((candidate) => candidate.dataset.value === value);
|
|
240
225
|
if (!option) {
|
|
@@ -244,7 +229,6 @@ export default class Combobox {
|
|
|
244
229
|
this._syncMultipleValue();
|
|
245
230
|
this._emitChange();
|
|
246
231
|
}
|
|
247
|
-
|
|
248
232
|
_clearSelection(emit = true) {
|
|
249
233
|
if (this._hidden) {
|
|
250
234
|
this._hidden.value = "";
|
|
@@ -259,7 +243,6 @@ export default class Combobox {
|
|
|
259
243
|
this._emitChange();
|
|
260
244
|
}
|
|
261
245
|
}
|
|
262
|
-
|
|
263
246
|
_clearStaleSelection() {
|
|
264
247
|
if (this._isMultiple) {
|
|
265
248
|
this._input.value = "";
|
|
@@ -272,29 +255,75 @@ export default class Combobox {
|
|
|
272
255
|
}
|
|
273
256
|
const selected = this._selectedOptions()[0] || null;
|
|
274
257
|
const label = this._optionLabel(selected);
|
|
275
|
-
if (!this._input.value ||
|
|
258
|
+
if (!this._input.value || label && normalize(this._input.value) === normalize(label)) {
|
|
276
259
|
return;
|
|
277
260
|
}
|
|
278
261
|
this._clearSelection();
|
|
279
262
|
this._input.value = "";
|
|
280
263
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
264
|
+
_activeOption() {
|
|
265
|
+
const activeId = this._input.getAttribute("aria-activedescendant");
|
|
266
|
+
if (!activeId) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
return this._visibleOptions().find((option) => option.id === activeId) || null;
|
|
270
|
+
}
|
|
271
|
+
_selectedOption() {
|
|
272
|
+
if (this._isMultiple) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
return this._visibleOptions().find(
|
|
276
|
+
(option) => option.getAttribute("aria-selected") === "true"
|
|
277
|
+
) || null;
|
|
278
|
+
}
|
|
279
|
+
_preferredActiveOption() {
|
|
280
|
+
return this._selectedOption() || this._visibleOptions()[0] || null;
|
|
281
|
+
}
|
|
282
|
+
_activatePreferredOptionIfNeeded() {
|
|
283
|
+
if (!this._activeOption()) {
|
|
284
|
+
this._setActiveOption(this._preferredActiveOption());
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
_setActiveOption(option, { scroll = true } = {}) {
|
|
288
|
+
this._options.forEach((candidate) => {
|
|
289
|
+
const active = candidate === option;
|
|
290
|
+
if (active) {
|
|
291
|
+
candidate.setAttribute("aria-current", "true");
|
|
292
|
+
} else {
|
|
293
|
+
candidate.removeAttribute("aria-current");
|
|
294
|
+
}
|
|
295
|
+
candidate.classList.toggle("active", active);
|
|
296
|
+
});
|
|
284
297
|
if (!option) {
|
|
285
298
|
this._input.removeAttribute("aria-activedescendant");
|
|
286
299
|
return;
|
|
287
300
|
}
|
|
288
301
|
this._input.setAttribute("aria-activedescendant", option.id);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
if (scroll) {
|
|
303
|
+
const optionRect = option.getBoundingClientRect();
|
|
304
|
+
const menuRect = this._menu.getBoundingClientRect();
|
|
305
|
+
if (optionRect.top < menuRect.top) {
|
|
306
|
+
this._menu.scrollTop -= menuRect.top - optionRect.top;
|
|
307
|
+
} else if (optionRect.bottom > menuRect.bottom) {
|
|
308
|
+
this._menu.scrollTop += optionRect.bottom - menuRect.bottom;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
_activateOptionFromPointer(option, event) {
|
|
313
|
+
if (event.pointerType === "touch" || option.disabled || option.getAttribute("aria-disabled") === "true" || option === this._activeOption()) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
this._setActiveOption(option, { scroll: false });
|
|
317
|
+
}
|
|
318
|
+
_restoreSelectedActiveOption() {
|
|
319
|
+
if (!this._menu.classList.contains("show")) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const selected = this._selectedOption();
|
|
323
|
+
if (selected) {
|
|
324
|
+
this._setActiveOption(selected, { scroll: false });
|
|
295
325
|
}
|
|
296
326
|
}
|
|
297
|
-
|
|
298
327
|
_chooseOption(option) {
|
|
299
328
|
if (!option || option.disabled) {
|
|
300
329
|
return;
|
|
@@ -322,8 +351,7 @@ export default class Combobox {
|
|
|
322
351
|
this._setActiveOption(option);
|
|
323
352
|
this._emitChange();
|
|
324
353
|
}
|
|
325
|
-
|
|
326
|
-
_filterOptions({ open = true, activate = true } = {}) {
|
|
354
|
+
_filterOptions({ open = true, activate = true, resetActive = false } = {}) {
|
|
327
355
|
if (open) {
|
|
328
356
|
this._openMenu();
|
|
329
357
|
}
|
|
@@ -337,29 +365,44 @@ export default class Combobox {
|
|
|
337
365
|
this._syncGroupVisibility();
|
|
338
366
|
this._empty.hidden = count !== 0;
|
|
339
367
|
this._liveRegion.textContent = count === 0 ? "No results" : `${count} result${count === 1 ? "" : "s"}`;
|
|
368
|
+
if (resetActive) {
|
|
369
|
+
this._setActiveOption(null, { scroll: false });
|
|
370
|
+
}
|
|
340
371
|
if (activate) {
|
|
341
|
-
this.
|
|
372
|
+
this._activatePreferredOptionIfNeeded();
|
|
342
373
|
}
|
|
343
374
|
}
|
|
344
|
-
|
|
345
375
|
_bindEvents() {
|
|
346
376
|
this._listen(this._input, "focus", () => {
|
|
347
377
|
this._openMenu();
|
|
348
|
-
if (!this._isMultiple && this._input.dataset.
|
|
378
|
+
if (!this._isMultiple && this._input.dataset.comboboxSelected === "true") {
|
|
349
379
|
this._input.select();
|
|
350
380
|
}
|
|
351
|
-
this.
|
|
381
|
+
this._activatePreferredOptionIfNeeded();
|
|
352
382
|
});
|
|
353
383
|
this._listen(this._input, "input", () => {
|
|
354
384
|
if (!this._isMultiple) {
|
|
355
385
|
this._clearSelection();
|
|
356
386
|
}
|
|
357
|
-
this._filterOptions();
|
|
387
|
+
this._filterOptions({ resetActive: true });
|
|
388
|
+
});
|
|
389
|
+
this._listen(this._input, "blur", (event) => {
|
|
390
|
+
const nextTarget = event.relatedTarget;
|
|
391
|
+
if (nextTarget instanceof this._window.Node && this._element.contains(nextTarget)) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this._clearStaleSelection();
|
|
395
|
+
});
|
|
396
|
+
this._listen(this._input, "click", () => {
|
|
397
|
+
this._openMenu();
|
|
398
|
+
this._setActiveOption(this._preferredActiveOption());
|
|
358
399
|
});
|
|
359
|
-
this._listen(this._input, "blur", () => this._clearStaleSelection());
|
|
360
|
-
this._listen(this._input, "click", () => this._openMenu());
|
|
361
400
|
this._listen(this._input, "keydown", (event) => this._handleKeydown(event));
|
|
362
401
|
this._options.forEach((option) => {
|
|
402
|
+
this._listen(option, "keydown", (event) => this._handleKeydown(event));
|
|
403
|
+
this._listen(option, "pointerenter", (event) => {
|
|
404
|
+
this._activateOptionFromPointer(option, event);
|
|
405
|
+
});
|
|
363
406
|
this._listen(option, "click", () => {
|
|
364
407
|
this._chooseOption(option);
|
|
365
408
|
if (this._isMultiple) {
|
|
@@ -374,8 +417,9 @@ export default class Combobox {
|
|
|
374
417
|
this._clearSelection();
|
|
375
418
|
this._input.value = "";
|
|
376
419
|
this._input.focus();
|
|
377
|
-
this._filterOptions();
|
|
420
|
+
this._filterOptions({ resetActive: true });
|
|
378
421
|
});
|
|
422
|
+
this._listen(this._menu, "pointerleave", () => this._restoreSelectedActiveOption());
|
|
379
423
|
this._listen(this._element, "click", (event) => this._handleRootClick(event));
|
|
380
424
|
this._listen(this._document, "click", (event) => {
|
|
381
425
|
if (event.target instanceof this._window.Node && !this._element.contains(event.target)) {
|
|
@@ -383,30 +427,24 @@ export default class Combobox {
|
|
|
383
427
|
}
|
|
384
428
|
});
|
|
385
429
|
}
|
|
386
|
-
|
|
387
430
|
_handleRootClick(event) {
|
|
388
431
|
const target = event.target;
|
|
389
|
-
const trigger = target instanceof this._window.Element
|
|
390
|
-
? target.closest("[data-moo-combobox-chip-remove]")
|
|
391
|
-
: null;
|
|
432
|
+
const trigger = target instanceof this._window.Element ? target.closest("[data-combobox-chip-remove]") : null;
|
|
392
433
|
if (trigger) {
|
|
393
434
|
event.preventDefault();
|
|
394
435
|
this._removeChip(trigger.dataset.value || "");
|
|
395
436
|
this._input.focus();
|
|
396
|
-
} else if (
|
|
397
|
-
this._isMultiple &&
|
|
398
|
-
target instanceof this._window.Element &&
|
|
399
|
-
target.closest(".combobox-chips")
|
|
400
|
-
) {
|
|
437
|
+
} else if (this._isMultiple && target instanceof this._window.Element && target.closest(".combobox-chips")) {
|
|
401
438
|
this._input.focus();
|
|
402
439
|
}
|
|
403
440
|
}
|
|
404
|
-
|
|
405
441
|
_handleKeydown(event) {
|
|
406
442
|
const available = this._visibleOptions();
|
|
407
443
|
const current = available.findIndex(
|
|
408
444
|
(option) => option.id === this._input.getAttribute("aria-activedescendant")
|
|
409
445
|
);
|
|
446
|
+
const isNextKey = event.key === "ArrowDown" || event.key === "Down";
|
|
447
|
+
const isPreviousKey = event.key === "ArrowUp" || event.key === "Up";
|
|
410
448
|
if (this._isMultiple && event.key === "Backspace" && this._input.value === "") {
|
|
411
449
|
const selected = this._selectedOptions();
|
|
412
450
|
const last = selected[selected.length - 1];
|
|
@@ -414,12 +452,15 @@ export default class Combobox {
|
|
|
414
452
|
event.preventDefault();
|
|
415
453
|
this._removeChip(last.dataset.value || "");
|
|
416
454
|
}
|
|
417
|
-
} else if ((
|
|
455
|
+
} else if ((isNextKey || isPreviousKey) && available.length) {
|
|
418
456
|
event.preventDefault();
|
|
419
457
|
this._openMenu();
|
|
420
|
-
const offset =
|
|
458
|
+
const offset = isNextKey ? 1 : -1;
|
|
421
459
|
const next = current === -1 ? 0 : (current + offset + available.length) % available.length;
|
|
422
460
|
this._setActiveOption(available[next]);
|
|
461
|
+
if (event.target !== this._input) {
|
|
462
|
+
this._input.focus({ preventScroll: true });
|
|
463
|
+
}
|
|
423
464
|
} else if (event.key === "Enter") {
|
|
424
465
|
const option = available[current];
|
|
425
466
|
if (option) {
|
|
@@ -431,9 +472,12 @@ export default class Combobox {
|
|
|
431
472
|
}
|
|
432
473
|
} else if (event.key === "Escape") {
|
|
433
474
|
this._closeMenu();
|
|
434
|
-
this._input.
|
|
475
|
+
this._input.focus();
|
|
435
476
|
} else if (event.key === "Tab") {
|
|
436
477
|
this._closeMenu();
|
|
437
478
|
}
|
|
438
479
|
}
|
|
439
480
|
}
|
|
481
|
+
export {
|
|
482
|
+
Combobox as default
|
|
483
|
+
};
|