@wpmoo/ui 1.0.0-rc.2 → 1.0.0-rc.4
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 +3 -1
- package/README.md +50 -107
- package/certification.json +20 -3
- package/dist/assets/css/moo-ui.css +8254 -7629
- package/dist/assets/css/moo-ui.min.css +3 -3
- package/dist/assets/css/moo.css +595 -71
- package/dist/assets/css/moo.min.css +3 -3
- package/dist/js/chart.js +15258 -0
- package/dist/js/chart.min.js +27 -0
- package/dist/js/combobox.js +98 -40
- package/dist/js/context-menu.js +5 -0
- package/dist/js/datatable.js +5 -0
- package/dist/js/datepicker.js +1154 -0
- package/dist/js/datepicker.min.js +6 -0
- package/dist/js/moo-ui.js +18950 -0
- package/dist/js/moo-ui.min.js +27 -0
- package/dist/js/sidebar.js +67 -25
- package/dist/js/slider.js +324 -0
- package/package.json +24 -4
- package/scss/_components.scss +73 -0
- package/scss/_config.scss +24 -0
- package/scss/_settings.scss +8 -0
- package/scss/components/_accordion.scss +59 -0
- package/scss/components/_alert.scss +94 -0
- package/scss/components/_avatar.scss +154 -0
- package/scss/components/_badge.scss +33 -0
- package/scss/components/_breadcrumb.scss +130 -0
- package/scss/components/_button.scss +228 -0
- package/scss/components/_button_group.scss +51 -0
- package/scss/components/_card.scss +142 -0
- package/scss/components/_checkbox.scss +115 -0
- package/scss/components/_close_button.scss +59 -0
- package/scss/components/_collapsible.scss +43 -0
- package/scss/components/_combobox.scss +258 -0
- package/scss/components/_context-menu.scss +28 -0
- package/scss/components/_datatable.scss +1082 -0
- package/scss/components/_datepicker.scss +236 -0
- package/scss/components/_dialog.scss +66 -0
- package/scss/components/_dropdown.scss +202 -0
- package/scss/components/_field.scss +125 -0
- package/scss/components/_input.scss +35 -0
- package/scss/components/_input_group.scss +201 -0
- package/scss/components/_kbd.scss +10 -0
- package/scss/components/_menubar.scss +16 -0
- package/scss/components/_navigation.scss +41 -0
- package/scss/components/_pagination.scss +70 -0
- package/scss/components/_popover.scss +47 -0
- package/scss/components/_progress.scss +7 -0
- package/scss/components/_radio_group.scss +9 -0
- package/scss/components/_select.scss +7 -0
- package/scss/components/_separator.scss +15 -0
- package/scss/components/_sheet.scss +32 -0
- package/scss/components/_sidebar.scss +7 -0
- package/scss/components/_skeleton.scss +17 -0
- package/scss/components/_slider.scss +214 -0
- package/scss/components/_spinner.scss +17 -0
- package/scss/components/_switch.scss +25 -0
- package/scss/components/_table.scss +7 -0
- package/scss/components/_tabs.scss +139 -0
- package/scss/components/_textarea.scss +7 -0
- package/scss/components/_toast.scss +119 -0
- package/scss/components/_toggle_group.scss +19 -0
- package/scss/components/_tooltip.scss +83 -0
- package/scss/components/sidebar/_collapsed.scss +187 -0
- package/scss/components/sidebar/_identity.scss +72 -0
- package/scss/components/sidebar/_inset.scss +59 -0
- package/scss/components/sidebar/_layout.scss +176 -0
- package/scss/components/sidebar/_menus.scss +236 -0
- package/scss/foundations/_core_global_primitives.scss +5 -0
- package/scss/foundations/_core_state_layer.scss +25 -0
- package/scss/foundations/_focus.scss +139 -0
- package/scss/foundations/_overlay_backdrop.scss +19 -0
- package/scss/moo-core.scss +47 -0
- package/scss/moo-ui.scss +50 -0
- package/scss/settings/_bootstrap_overrides.scss +11 -0
- package/scss/settings/_component_variables.scss +479 -0
- package/scss/settings/_forms.scss +65 -0
- package/scss/settings/_palette.scss +132 -0
- package/scss/themes/_scoped_core.scss +239 -0
- package/scss/themes/_standalone_root.scss +117 -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,3 +1,8 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI combobox.js v1.0.0-rc.4 (https://ui.wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo (https://wpmoo.org)
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
1
6
|
const instances = new WeakMap();
|
|
2
7
|
const normalize = (value) => value.trim().toLowerCase();
|
|
3
8
|
|
|
@@ -30,36 +35,36 @@ export default class Combobox {
|
|
|
30
35
|
throw new TypeError("Combobox requires an input, menu, and at least one option.");
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
this._isMultiple = element.dataset.
|
|
38
|
+
this._isMultiple = element.dataset.comboboxMultiple === "true";
|
|
34
39
|
this._hidden = this._isMultiple ? null : element.querySelector('input[type="hidden"]');
|
|
35
40
|
this._chipValue = element.querySelector(".combobox-value");
|
|
36
|
-
this._valueStore = element.querySelector("[data-
|
|
37
|
-
this._chipIconTemplate = element.querySelector("[data-
|
|
38
|
-
this._clearTrigger = element.querySelector("[data-
|
|
41
|
+
this._valueStore = element.querySelector("[data-combobox-value-store]");
|
|
42
|
+
this._chipIconTemplate = element.querySelector("[data-combobox-chip-icon]");
|
|
43
|
+
this._clearTrigger = element.querySelector("[data-combobox-clear]");
|
|
39
44
|
this._indicator = element.querySelector(".combobox-indicator");
|
|
40
|
-
this._groups = Array.from(element.querySelectorAll("[data-
|
|
41
|
-
this._separators = Array.from(element.querySelectorAll("[data-
|
|
45
|
+
this._groups = Array.from(element.querySelectorAll("[data-combobox-group]"));
|
|
46
|
+
this._separators = Array.from(element.querySelectorAll("[data-combobox-separator]"));
|
|
42
47
|
this._listeners = [];
|
|
43
48
|
this._generated = [];
|
|
44
49
|
this._startsOpen = this._menu.classList.contains("show");
|
|
45
50
|
|
|
46
|
-
this._empty = this._menu.querySelector("[data-
|
|
51
|
+
this._empty = this._menu.querySelector("[data-combobox-empty]");
|
|
47
52
|
if (!this._empty) {
|
|
48
53
|
this._empty = this._document.createElement("li");
|
|
49
54
|
this._empty.className = "combobox-empty";
|
|
50
55
|
this._empty.hidden = true;
|
|
51
|
-
this._empty.dataset.
|
|
56
|
+
this._empty.dataset.comboboxEmpty = "true";
|
|
52
57
|
this._empty.textContent = this._config.noResultsText;
|
|
53
58
|
this._menu.appendChild(this._empty);
|
|
54
59
|
this._generated.push(this._empty);
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
this._liveRegion = element.querySelector("[data-
|
|
62
|
+
this._liveRegion = element.querySelector("[data-combobox-live]");
|
|
58
63
|
if (!this._liveRegion) {
|
|
59
64
|
this._liveRegion = this._document.createElement("span");
|
|
60
65
|
this._liveRegion.className = "visually-hidden";
|
|
61
66
|
this._liveRegion.setAttribute("aria-live", "polite");
|
|
62
|
-
this._liveRegion.dataset.
|
|
67
|
+
this._liveRegion.dataset.comboboxLive = "true";
|
|
63
68
|
element.appendChild(this._liveRegion);
|
|
64
69
|
this._generated.push(this._liveRegion);
|
|
65
70
|
}
|
|
@@ -67,8 +72,8 @@ export default class Combobox {
|
|
|
67
72
|
instances.set(element, this);
|
|
68
73
|
this._bindEvents();
|
|
69
74
|
this._toggleClear(
|
|
70
|
-
this._input.dataset.
|
|
71
|
-
element.dataset.
|
|
75
|
+
this._input.dataset.comboboxSelected === "true" ||
|
|
76
|
+
element.dataset.comboboxSelected === "true"
|
|
72
77
|
);
|
|
73
78
|
this._syncMultipleValue();
|
|
74
79
|
this._filterOptions({ open: this._startsOpen, activate: this._startsOpen });
|
|
@@ -85,8 +90,8 @@ export default class Combobox {
|
|
|
85
90
|
this._closeMenu(false);
|
|
86
91
|
this._generated.forEach((element) => element.remove());
|
|
87
92
|
this._generated = [];
|
|
88
|
-
this._chipValue?.querySelectorAll("[data-
|
|
89
|
-
this._valueStore?.querySelectorAll("[data-
|
|
93
|
+
this._chipValue?.querySelectorAll("[data-combobox-generated]").forEach((item) => item.remove());
|
|
94
|
+
this._valueStore?.querySelectorAll("[data-combobox-generated]").forEach((item) => item.remove());
|
|
90
95
|
instances.delete(this._element);
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -108,7 +113,7 @@ export default class Combobox {
|
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
_optionGroupIsHidden(option) {
|
|
111
|
-
return option.closest("[data-
|
|
116
|
+
return option.closest("[data-combobox-group]")?.hidden || false;
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
_visibleOptions() {
|
|
@@ -157,7 +162,10 @@ export default class Combobox {
|
|
|
157
162
|
this._menu.classList.remove("show");
|
|
158
163
|
this._input.setAttribute("aria-expanded", "false");
|
|
159
164
|
this._input.removeAttribute("aria-activedescendant");
|
|
160
|
-
this._options.forEach((option) =>
|
|
165
|
+
this._options.forEach((option) => {
|
|
166
|
+
option.removeAttribute("aria-current");
|
|
167
|
+
option.classList.remove("active");
|
|
168
|
+
});
|
|
161
169
|
if (wasOpen && emit) {
|
|
162
170
|
this._trigger("hidden");
|
|
163
171
|
}
|
|
@@ -173,8 +181,8 @@ export default class Combobox {
|
|
|
173
181
|
|
|
174
182
|
_toggleClear(selected) {
|
|
175
183
|
const value = selected ? "true" : "false";
|
|
176
|
-
this._element.dataset.
|
|
177
|
-
this._input.dataset.
|
|
184
|
+
this._element.dataset.comboboxSelected = value;
|
|
185
|
+
this._input.dataset.comboboxSelected = value;
|
|
178
186
|
if (this._clearTrigger) {
|
|
179
187
|
this._clearTrigger.hidden = !selected;
|
|
180
188
|
if (this._indicator) {
|
|
@@ -189,7 +197,7 @@ export default class Combobox {
|
|
|
189
197
|
const chip = this._document.createElement("span");
|
|
190
198
|
chip.className = "badge text-bg-secondary combobox-chip";
|
|
191
199
|
chip.dataset.value = value;
|
|
192
|
-
chip.dataset.
|
|
200
|
+
chip.dataset.comboboxGenerated = "true";
|
|
193
201
|
const labelElement = this._document.createElement("span");
|
|
194
202
|
labelElement.className = "combobox-chip__label";
|
|
195
203
|
labelElement.textContent = label;
|
|
@@ -198,7 +206,7 @@ export default class Combobox {
|
|
|
198
206
|
remove.className = "combobox-chip__remove";
|
|
199
207
|
remove.type = "button";
|
|
200
208
|
remove.dataset.value = value;
|
|
201
|
-
remove.dataset.
|
|
209
|
+
remove.dataset.comboboxChipRemove = "true";
|
|
202
210
|
remove.setAttribute("aria-label", `Remove ${label}`);
|
|
203
211
|
remove.append(this._chipIconTemplate?.content.cloneNode(true) || "x");
|
|
204
212
|
chip.appendChild(remove);
|
|
@@ -215,13 +223,13 @@ export default class Combobox {
|
|
|
215
223
|
}
|
|
216
224
|
if (this._valueStore) {
|
|
217
225
|
this._valueStore.replaceChildren();
|
|
218
|
-
const name = this._valueStore.dataset.
|
|
226
|
+
const name = this._valueStore.dataset.comboboxName || "";
|
|
219
227
|
selected.forEach((option) => {
|
|
220
228
|
const field = this._document.createElement("input");
|
|
221
229
|
field.type = "hidden";
|
|
222
230
|
field.name = name;
|
|
223
231
|
field.value = option.dataset.value || "";
|
|
224
|
-
field.dataset.
|
|
232
|
+
field.dataset.comboboxGenerated = "true";
|
|
225
233
|
this._valueStore.appendChild(field);
|
|
226
234
|
});
|
|
227
235
|
}
|
|
@@ -287,16 +295,35 @@ export default class Combobox {
|
|
|
287
295
|
return this._visibleOptions().find((option) => option.id === activeId) || null;
|
|
288
296
|
}
|
|
289
297
|
|
|
290
|
-
|
|
298
|
+
_selectedOption() {
|
|
299
|
+
if (this._isMultiple) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return (
|
|
303
|
+
this._visibleOptions().find(
|
|
304
|
+
(option) => option.getAttribute("aria-selected") === "true"
|
|
305
|
+
) || null
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
_preferredActiveOption() {
|
|
310
|
+
return this._selectedOption() || this._visibleOptions()[0] || null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_activatePreferredOptionIfNeeded() {
|
|
291
314
|
if (!this._activeOption()) {
|
|
292
|
-
this._setActiveOption(this.
|
|
315
|
+
this._setActiveOption(this._preferredActiveOption());
|
|
293
316
|
}
|
|
294
317
|
}
|
|
295
318
|
|
|
296
|
-
_setActiveOption(option) {
|
|
319
|
+
_setActiveOption(option, { scroll = true } = {}) {
|
|
297
320
|
this._options.forEach((candidate) => {
|
|
298
321
|
const active = candidate === option;
|
|
299
|
-
|
|
322
|
+
if (active) {
|
|
323
|
+
candidate.setAttribute("aria-current", "true");
|
|
324
|
+
} else {
|
|
325
|
+
candidate.removeAttribute("aria-current");
|
|
326
|
+
}
|
|
300
327
|
candidate.classList.toggle("active", active);
|
|
301
328
|
});
|
|
302
329
|
if (!option) {
|
|
@@ -304,12 +331,36 @@ export default class Combobox {
|
|
|
304
331
|
return;
|
|
305
332
|
}
|
|
306
333
|
this._input.setAttribute("aria-activedescendant", option.id);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
334
|
+
if (scroll) {
|
|
335
|
+
const optionRect = option.getBoundingClientRect();
|
|
336
|
+
const menuRect = this._menu.getBoundingClientRect();
|
|
337
|
+
if (optionRect.top < menuRect.top) {
|
|
338
|
+
this._menu.scrollTop -= menuRect.top - optionRect.top;
|
|
339
|
+
} else if (optionRect.bottom > menuRect.bottom) {
|
|
340
|
+
this._menu.scrollTop += optionRect.bottom - menuRect.bottom;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
_activateOptionFromPointer(option, event) {
|
|
346
|
+
if (
|
|
347
|
+
event.pointerType === "touch" ||
|
|
348
|
+
option.disabled ||
|
|
349
|
+
option.getAttribute("aria-disabled") === "true" ||
|
|
350
|
+
option === this._activeOption()
|
|
351
|
+
) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
this._setActiveOption(option, { scroll: false });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
_restoreSelectedActiveOption() {
|
|
358
|
+
if (!this._menu.classList.contains("show")) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const selected = this._selectedOption();
|
|
362
|
+
if (selected) {
|
|
363
|
+
this._setActiveOption(selected, { scroll: false });
|
|
313
364
|
}
|
|
314
365
|
}
|
|
315
366
|
|
|
@@ -341,7 +392,7 @@ export default class Combobox {
|
|
|
341
392
|
this._emitChange();
|
|
342
393
|
}
|
|
343
394
|
|
|
344
|
-
_filterOptions({ open = true, activate = true } = {}) {
|
|
395
|
+
_filterOptions({ open = true, activate = true, resetActive = false } = {}) {
|
|
345
396
|
if (open) {
|
|
346
397
|
this._openMenu();
|
|
347
398
|
}
|
|
@@ -355,24 +406,27 @@ export default class Combobox {
|
|
|
355
406
|
this._syncGroupVisibility();
|
|
356
407
|
this._empty.hidden = count !== 0;
|
|
357
408
|
this._liveRegion.textContent = count === 0 ? "No results" : `${count} result${count === 1 ? "" : "s"}`;
|
|
409
|
+
if (resetActive) {
|
|
410
|
+
this._setActiveOption(null, { scroll: false });
|
|
411
|
+
}
|
|
358
412
|
if (activate) {
|
|
359
|
-
this.
|
|
413
|
+
this._activatePreferredOptionIfNeeded();
|
|
360
414
|
}
|
|
361
415
|
}
|
|
362
416
|
|
|
363
417
|
_bindEvents() {
|
|
364
418
|
this._listen(this._input, "focus", () => {
|
|
365
419
|
this._openMenu();
|
|
366
|
-
if (!this._isMultiple && this._input.dataset.
|
|
420
|
+
if (!this._isMultiple && this._input.dataset.comboboxSelected === "true") {
|
|
367
421
|
this._input.select();
|
|
368
422
|
}
|
|
369
|
-
this.
|
|
423
|
+
this._activatePreferredOptionIfNeeded();
|
|
370
424
|
});
|
|
371
425
|
this._listen(this._input, "input", () => {
|
|
372
426
|
if (!this._isMultiple) {
|
|
373
427
|
this._clearSelection();
|
|
374
428
|
}
|
|
375
|
-
this._filterOptions();
|
|
429
|
+
this._filterOptions({ resetActive: true });
|
|
376
430
|
});
|
|
377
431
|
this._listen(this._input, "blur", (event) => {
|
|
378
432
|
const nextTarget = event.relatedTarget;
|
|
@@ -383,11 +437,14 @@ export default class Combobox {
|
|
|
383
437
|
});
|
|
384
438
|
this._listen(this._input, "click", () => {
|
|
385
439
|
this._openMenu();
|
|
386
|
-
this._setActiveOption(this.
|
|
440
|
+
this._setActiveOption(this._preferredActiveOption());
|
|
387
441
|
});
|
|
388
442
|
this._listen(this._input, "keydown", (event) => this._handleKeydown(event));
|
|
389
443
|
this._options.forEach((option) => {
|
|
390
444
|
this._listen(option, "keydown", (event) => this._handleKeydown(event));
|
|
445
|
+
this._listen(option, "pointerenter", (event) => {
|
|
446
|
+
this._activateOptionFromPointer(option, event);
|
|
447
|
+
});
|
|
391
448
|
this._listen(option, "click", () => {
|
|
392
449
|
this._chooseOption(option);
|
|
393
450
|
if (this._isMultiple) {
|
|
@@ -402,8 +459,9 @@ export default class Combobox {
|
|
|
402
459
|
this._clearSelection();
|
|
403
460
|
this._input.value = "";
|
|
404
461
|
this._input.focus();
|
|
405
|
-
this._filterOptions();
|
|
462
|
+
this._filterOptions({ resetActive: true });
|
|
406
463
|
});
|
|
464
|
+
this._listen(this._menu, "pointerleave", () => this._restoreSelectedActiveOption());
|
|
407
465
|
this._listen(this._element, "click", (event) => this._handleRootClick(event));
|
|
408
466
|
this._listen(this._document, "click", (event) => {
|
|
409
467
|
if (event.target instanceof this._window.Node && !this._element.contains(event.target)) {
|
|
@@ -415,7 +473,7 @@ export default class Combobox {
|
|
|
415
473
|
_handleRootClick(event) {
|
|
416
474
|
const target = event.target;
|
|
417
475
|
const trigger = target instanceof this._window.Element
|
|
418
|
-
? target.closest("[data-
|
|
476
|
+
? target.closest("[data-combobox-chip-remove]")
|
|
419
477
|
: null;
|
|
420
478
|
if (trigger) {
|
|
421
479
|
event.preventDefault();
|
package/dist/js/context-menu.js
CHANGED
package/dist/js/datatable.js
CHANGED