@wpmoo/ui 1.0.0-rc.8 → 1.0.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/ASSET_LICENSE.md +1 -1
- package/LICENSE +28 -0
- package/README.md +34 -6
- package/certification.json +3 -2
- package/dist/assets/css/moo-ui.css +634 -33
- package/dist/assets/css/moo-ui.min.css +10 -31
- package/dist/assets/css/moo.css +624 -30
- package/dist/assets/css/moo.min.css +10 -32
- package/dist/js/chart.js +23 -21
- package/dist/js/chart.min.js +5 -5
- package/dist/js/combobox.js +18 -65
- package/dist/js/context-menu.js +16 -68
- package/dist/js/datatable.js +39 -22
- package/dist/js/datepicker.js +4 -3
- package/dist/js/datepicker.min.js +4 -3
- package/dist/js/moo-ui.js +160 -15402
- package/dist/js/moo-ui.min.js +3 -23
- package/dist/js/sheet.js +46 -0
- package/dist/js/sidebar.js +5 -4
- package/dist/js/slider.js +12 -47
- package/dist/js/state.js +76 -0
- package/dist/release-manifest.json +6 -6
- package/package.json +5 -3
- package/scss/_components.scss +2 -2
- package/scss/_config.scss +15 -21
- package/scss/_settings.scss +0 -3
- package/scss/components/_accordion.scss +1 -12
- package/scss/components/_alert.scss +1 -12
- package/scss/components/_avatar.scss +1 -16
- package/scss/components/_badge.scss +1 -0
- package/scss/components/_breadcrumb.scss +1 -12
- package/scss/components/_button.scss +1 -29
- package/scss/components/_button_group.scss +1 -25
- package/scss/components/_card.scss +1 -29
- package/scss/components/_checkbox.scss +1 -38
- package/scss/components/_close_button.scss +1 -9
- package/scss/components/_collapsible.scss +1 -0
- package/scss/components/_combobox.scss +1 -6
- package/scss/components/_context-menu.scss +1 -11
- package/scss/components/_datatable.scss +46 -85
- package/scss/components/_datepicker.scss +1 -0
- package/scss/components/_dialog.scss +1 -14
- package/scss/components/_dropdown.scss +5 -51
- package/scss/components/_field.scss +6 -37
- package/scss/components/_input.scss +1 -5
- package/scss/components/_input_group.scss +1 -51
- package/scss/components/_kbd.scss +1 -0
- package/scss/components/_menubar.scss +1 -0
- package/scss/components/_navigation.scss +76 -6
- package/scss/components/_pagination.scss +1 -13
- package/scss/components/_popover.scss +1 -22
- package/scss/components/_progress.scss +1 -3
- package/scss/components/_radio_group.scss +105 -6
- package/scss/components/_search_trigger.scss +1 -2
- package/scss/components/_select.scss +1 -0
- package/scss/components/_separator.scss +1 -6
- package/scss/components/_sheet.scss +1 -17
- package/scss/components/_sidebar.scss +1 -2
- package/scss/components/_skeleton.scss +1 -8
- package/scss/components/_slider.scss +1 -2
- package/scss/components/_spinner.scss +1 -7
- package/scss/components/_switch.scss +1 -4
- package/scss/components/_table.scss +1 -2
- package/scss/components/_tabs.scss +1 -24
- package/scss/components/_textarea.scss +1 -0
- package/scss/components/_toast.scss +1 -4
- package/scss/components/_toggle_group.scss +1 -3
- package/scss/components/_tooltip.scss +1 -20
- package/scss/components/sidebar/_base.scss +0 -7
- package/scss/components/sidebar/_collapsed.scss +0 -6
- package/scss/components/sidebar/_identity.scss +0 -1
- package/scss/components/sidebar/_menus.scss +0 -17
- package/scss/foundations/_backdrop.scss +0 -26
- package/scss/foundations/_focus.scss +0 -56
- package/scss/foundations/_scope.scss +19 -0
- package/scss/layouts/_app.scss +5 -7
- package/scss/layouts/_page_grid.scss +27 -0
- package/scss/layouts/app/sidebar/_base.scss +0 -33
- package/scss/layouts/app/sidebar/_default.scss +2 -8
- package/scss/layouts/app/sidebar/_floating.scss +1 -9
- package/scss/layouts/app/sidebar/_inset.scss +5 -2
- package/scss/mixins/_banner.scss +9 -3
- package/scss/moo-core.scss +3 -6
- package/scss/moo-ui.scss +9 -5
- package/scss/settings/_bootstrap_overrides.scss +0 -8
- package/scss/settings/_component_variables.scss +4 -266
- package/scss/settings/_forms.scss +1 -38
- package/scss/settings/_palette.scss +0 -42
- package/scss/themes/_root.scss +4 -0
- package/scss/themes/_standalone.scss +1 -3
- package/scss/themes/_theme.scss +4 -0
- package/scss/utilities/_background_color.scss +18 -0
- package/scss/utilities/_border.scss +4 -0
- package/dist/js/theme-prepaint.js +0 -91
package/dist/js/combobox.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI combobox.js v1.0.0
|
|
3
|
-
* Copyright 2026 WPMoo
|
|
2
|
+
* Moo UI combobox.js v1.0.0 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
4
|
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
5
|
*/
|
|
6
|
-
const instances = new WeakMap();
|
|
7
|
-
const normalize = (value) => value.trim().toLowerCase();
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
const normalize = (value) => value.trim().toLowerCase();
|
|
9
|
+
class Combobox {
|
|
10
10
|
static getInstance(element) {
|
|
11
11
|
return element?.nodeType === 1 ? instances.get(element) || null : null;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
13
|
static getOrCreateInstance(element, config = {}) {
|
|
15
14
|
return Combobox.getInstance(element) || new Combobox(element, config);
|
|
16
15
|
}
|
|
17
|
-
|
|
18
16
|
constructor(element, config = {}) {
|
|
19
17
|
if (element?.nodeType !== 1 || !element.matches(".combobox")) {
|
|
20
18
|
throw new TypeError("Combobox requires a .combobox root element.");
|
|
@@ -23,7 +21,6 @@ export default class Combobox {
|
|
|
23
21
|
if (existing) {
|
|
24
22
|
return existing;
|
|
25
23
|
}
|
|
26
|
-
|
|
27
24
|
this._element = element;
|
|
28
25
|
this._document = element.ownerDocument;
|
|
29
26
|
this._window = this._document.defaultView;
|
|
@@ -34,7 +31,6 @@ export default class Combobox {
|
|
|
34
31
|
if (!this._input || !this._menu || this._options.length === 0) {
|
|
35
32
|
throw new TypeError("Combobox requires an input, menu, and at least one option.");
|
|
36
33
|
}
|
|
37
|
-
|
|
38
34
|
this._isMultiple = element.dataset.comboboxMultiple === "true";
|
|
39
35
|
this._hidden = this._isMultiple ? null : element.querySelector('input[type="hidden"]');
|
|
40
36
|
this._chipValue = element.querySelector(".combobox-value");
|
|
@@ -47,7 +43,6 @@ export default class Combobox {
|
|
|
47
43
|
this._listeners = [];
|
|
48
44
|
this._generated = [];
|
|
49
45
|
this._startsOpen = this._menu.classList.contains("show");
|
|
50
|
-
|
|
51
46
|
this._empty = this._menu.querySelector("[data-combobox-empty]");
|
|
52
47
|
if (!this._empty) {
|
|
53
48
|
this._empty = this._document.createElement("li");
|
|
@@ -58,7 +53,6 @@ export default class Combobox {
|
|
|
58
53
|
this._menu.appendChild(this._empty);
|
|
59
54
|
this._generated.push(this._empty);
|
|
60
55
|
}
|
|
61
|
-
|
|
62
56
|
this._liveRegion = element.querySelector("[data-combobox-live]");
|
|
63
57
|
if (!this._liveRegion) {
|
|
64
58
|
this._liveRegion = this._document.createElement("span");
|
|
@@ -68,12 +62,10 @@ export default class Combobox {
|
|
|
68
62
|
element.appendChild(this._liveRegion);
|
|
69
63
|
this._generated.push(this._liveRegion);
|
|
70
64
|
}
|
|
71
|
-
|
|
72
65
|
instances.set(element, this);
|
|
73
66
|
this._bindEvents();
|
|
74
67
|
this._toggleClear(
|
|
75
|
-
this._input.dataset.comboboxSelected === "true" ||
|
|
76
|
-
element.dataset.comboboxSelected === "true"
|
|
68
|
+
this._input.dataset.comboboxSelected === "true" || element.dataset.comboboxSelected === "true"
|
|
77
69
|
);
|
|
78
70
|
this._syncMultipleValue();
|
|
79
71
|
this._filterOptions({ open: this._startsOpen, activate: this._startsOpen });
|
|
@@ -81,7 +73,6 @@ export default class Combobox {
|
|
|
81
73
|
this._closeMenu(false);
|
|
82
74
|
}
|
|
83
75
|
}
|
|
84
|
-
|
|
85
76
|
dispose() {
|
|
86
77
|
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
87
78
|
target.removeEventListener(type, handler, options);
|
|
@@ -94,34 +85,29 @@ export default class Combobox {
|
|
|
94
85
|
this._valueStore?.querySelectorAll("[data-combobox-generated]").forEach((item) => item.remove());
|
|
95
86
|
instances.delete(this._element);
|
|
96
87
|
}
|
|
97
|
-
|
|
98
88
|
_listen(target, type, handler, options) {
|
|
99
89
|
target?.addEventListener(type, handler, options);
|
|
100
90
|
if (target) {
|
|
101
91
|
this._listeners.push({ target, type, handler, options });
|
|
102
92
|
}
|
|
103
93
|
}
|
|
104
|
-
|
|
105
94
|
_trigger(name, detail = {}) {
|
|
106
95
|
return this._element.dispatchEvent(
|
|
107
96
|
new this._window.CustomEvent(`${name}.moo.combobox`, {
|
|
108
97
|
bubbles: true,
|
|
109
98
|
cancelable: name === "show" || name === "hide",
|
|
110
|
-
detail
|
|
99
|
+
detail
|
|
111
100
|
})
|
|
112
101
|
);
|
|
113
102
|
}
|
|
114
|
-
|
|
115
103
|
_optionGroupIsHidden(option) {
|
|
116
104
|
return option.closest("[data-combobox-group]")?.hidden || false;
|
|
117
105
|
}
|
|
118
|
-
|
|
119
106
|
_visibleOptions() {
|
|
120
107
|
return this._options.filter(
|
|
121
108
|
(option) => !option.hidden && !option.disabled && !this._optionGroupIsHidden(option)
|
|
122
109
|
);
|
|
123
110
|
}
|
|
124
|
-
|
|
125
111
|
_syncGroupVisibility() {
|
|
126
112
|
if (this._groups.length === 0) {
|
|
127
113
|
return;
|
|
@@ -138,7 +124,6 @@ export default class Combobox {
|
|
|
138
124
|
separator.hidden = visibleGroupCount < 2;
|
|
139
125
|
});
|
|
140
126
|
}
|
|
141
|
-
|
|
142
127
|
_openMenu(emit = true) {
|
|
143
128
|
if (this._menu.classList.contains("show")) {
|
|
144
129
|
this._input.setAttribute("aria-expanded", "true");
|
|
@@ -153,7 +138,6 @@ export default class Combobox {
|
|
|
153
138
|
this._trigger("shown");
|
|
154
139
|
}
|
|
155
140
|
}
|
|
156
|
-
|
|
157
141
|
_closeMenu(emit = true) {
|
|
158
142
|
const wasOpen = this._menu.classList.contains("show");
|
|
159
143
|
if (wasOpen && emit && !this._trigger("hide")) {
|
|
@@ -170,15 +154,12 @@ export default class Combobox {
|
|
|
170
154
|
this._trigger("hidden");
|
|
171
155
|
}
|
|
172
156
|
}
|
|
173
|
-
|
|
174
157
|
_optionLabel(option) {
|
|
175
158
|
return option?.querySelector(".combobox-option__label")?.textContent?.trim() || "";
|
|
176
159
|
}
|
|
177
|
-
|
|
178
160
|
_selectedOptions() {
|
|
179
161
|
return this._options.filter((option) => option.getAttribute("aria-selected") === "true");
|
|
180
162
|
}
|
|
181
|
-
|
|
182
163
|
_toggleClear(selected) {
|
|
183
164
|
const value = selected ? "true" : "false";
|
|
184
165
|
this._element.dataset.comboboxSelected = value;
|
|
@@ -190,7 +171,6 @@ export default class Combobox {
|
|
|
190
171
|
}
|
|
191
172
|
}
|
|
192
173
|
}
|
|
193
|
-
|
|
194
174
|
_createChip(option) {
|
|
195
175
|
const value = option.dataset.value || "";
|
|
196
176
|
const label = this._optionLabel(option);
|
|
@@ -212,7 +192,6 @@ export default class Combobox {
|
|
|
212
192
|
chip.appendChild(remove);
|
|
213
193
|
return chip;
|
|
214
194
|
}
|
|
215
|
-
|
|
216
195
|
_syncMultipleValue() {
|
|
217
196
|
if (!this._isMultiple) {
|
|
218
197
|
return;
|
|
@@ -235,14 +214,12 @@ export default class Combobox {
|
|
|
235
214
|
}
|
|
236
215
|
this._toggleClear(selected.length > 0);
|
|
237
216
|
}
|
|
238
|
-
|
|
239
217
|
_emitChange() {
|
|
240
218
|
const selected = this._selectedOptions();
|
|
241
219
|
this._trigger("change", {
|
|
242
|
-
values: selected.map((option) => option.dataset.value || "")
|
|
220
|
+
values: selected.map((option) => option.dataset.value || "")
|
|
243
221
|
});
|
|
244
222
|
}
|
|
245
|
-
|
|
246
223
|
_removeChip(value) {
|
|
247
224
|
const option = this._options.find((candidate) => candidate.dataset.value === value);
|
|
248
225
|
if (!option) {
|
|
@@ -252,7 +229,6 @@ export default class Combobox {
|
|
|
252
229
|
this._syncMultipleValue();
|
|
253
230
|
this._emitChange();
|
|
254
231
|
}
|
|
255
|
-
|
|
256
232
|
_clearSelection(emit = true) {
|
|
257
233
|
if (this._hidden) {
|
|
258
234
|
this._hidden.value = "";
|
|
@@ -267,7 +243,6 @@ export default class Combobox {
|
|
|
267
243
|
this._emitChange();
|
|
268
244
|
}
|
|
269
245
|
}
|
|
270
|
-
|
|
271
246
|
_clearStaleSelection() {
|
|
272
247
|
if (this._isMultiple) {
|
|
273
248
|
this._input.value = "";
|
|
@@ -280,13 +255,12 @@ export default class Combobox {
|
|
|
280
255
|
}
|
|
281
256
|
const selected = this._selectedOptions()[0] || null;
|
|
282
257
|
const label = this._optionLabel(selected);
|
|
283
|
-
if (!this._input.value ||
|
|
258
|
+
if (!this._input.value || label && normalize(this._input.value) === normalize(label)) {
|
|
284
259
|
return;
|
|
285
260
|
}
|
|
286
261
|
this._clearSelection();
|
|
287
262
|
this._input.value = "";
|
|
288
263
|
}
|
|
289
|
-
|
|
290
264
|
_activeOption() {
|
|
291
265
|
const activeId = this._input.getAttribute("aria-activedescendant");
|
|
292
266
|
if (!activeId) {
|
|
@@ -294,28 +268,22 @@ export default class Combobox {
|
|
|
294
268
|
}
|
|
295
269
|
return this._visibleOptions().find((option) => option.id === activeId) || null;
|
|
296
270
|
}
|
|
297
|
-
|
|
298
271
|
_selectedOption() {
|
|
299
272
|
if (this._isMultiple) {
|
|
300
273
|
return null;
|
|
301
274
|
}
|
|
302
|
-
return (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
) || null
|
|
306
|
-
);
|
|
275
|
+
return this._visibleOptions().find(
|
|
276
|
+
(option) => option.getAttribute("aria-selected") === "true"
|
|
277
|
+
) || null;
|
|
307
278
|
}
|
|
308
|
-
|
|
309
279
|
_preferredActiveOption() {
|
|
310
280
|
return this._selectedOption() || this._visibleOptions()[0] || null;
|
|
311
281
|
}
|
|
312
|
-
|
|
313
282
|
_activatePreferredOptionIfNeeded() {
|
|
314
283
|
if (!this._activeOption()) {
|
|
315
284
|
this._setActiveOption(this._preferredActiveOption());
|
|
316
285
|
}
|
|
317
286
|
}
|
|
318
|
-
|
|
319
287
|
_setActiveOption(option, { scroll = true } = {}) {
|
|
320
288
|
this._options.forEach((candidate) => {
|
|
321
289
|
const active = candidate === option;
|
|
@@ -341,19 +309,12 @@ export default class Combobox {
|
|
|
341
309
|
}
|
|
342
310
|
}
|
|
343
311
|
}
|
|
344
|
-
|
|
345
312
|
_activateOptionFromPointer(option, event) {
|
|
346
|
-
if (
|
|
347
|
-
event.pointerType === "touch" ||
|
|
348
|
-
option.disabled ||
|
|
349
|
-
option.getAttribute("aria-disabled") === "true" ||
|
|
350
|
-
option === this._activeOption()
|
|
351
|
-
) {
|
|
313
|
+
if (event.pointerType === "touch" || option.disabled || option.getAttribute("aria-disabled") === "true" || option === this._activeOption()) {
|
|
352
314
|
return;
|
|
353
315
|
}
|
|
354
316
|
this._setActiveOption(option, { scroll: false });
|
|
355
317
|
}
|
|
356
|
-
|
|
357
318
|
_restoreSelectedActiveOption() {
|
|
358
319
|
if (!this._menu.classList.contains("show")) {
|
|
359
320
|
return;
|
|
@@ -363,7 +324,6 @@ export default class Combobox {
|
|
|
363
324
|
this._setActiveOption(selected, { scroll: false });
|
|
364
325
|
}
|
|
365
326
|
}
|
|
366
|
-
|
|
367
327
|
_chooseOption(option) {
|
|
368
328
|
if (!option || option.disabled) {
|
|
369
329
|
return;
|
|
@@ -391,7 +351,6 @@ export default class Combobox {
|
|
|
391
351
|
this._setActiveOption(option);
|
|
392
352
|
this._emitChange();
|
|
393
353
|
}
|
|
394
|
-
|
|
395
354
|
_filterOptions({ open = true, activate = true, resetActive = false } = {}) {
|
|
396
355
|
if (open) {
|
|
397
356
|
this._openMenu();
|
|
@@ -413,7 +372,6 @@ export default class Combobox {
|
|
|
413
372
|
this._activatePreferredOptionIfNeeded();
|
|
414
373
|
}
|
|
415
374
|
}
|
|
416
|
-
|
|
417
375
|
_bindEvents() {
|
|
418
376
|
this._listen(this._input, "focus", () => {
|
|
419
377
|
this._openMenu();
|
|
@@ -469,25 +427,17 @@ export default class Combobox {
|
|
|
469
427
|
}
|
|
470
428
|
});
|
|
471
429
|
}
|
|
472
|
-
|
|
473
430
|
_handleRootClick(event) {
|
|
474
431
|
const target = event.target;
|
|
475
|
-
const trigger = target instanceof this._window.Element
|
|
476
|
-
? target.closest("[data-combobox-chip-remove]")
|
|
477
|
-
: null;
|
|
432
|
+
const trigger = target instanceof this._window.Element ? target.closest("[data-combobox-chip-remove]") : null;
|
|
478
433
|
if (trigger) {
|
|
479
434
|
event.preventDefault();
|
|
480
435
|
this._removeChip(trigger.dataset.value || "");
|
|
481
436
|
this._input.focus();
|
|
482
|
-
} else if (
|
|
483
|
-
this._isMultiple &&
|
|
484
|
-
target instanceof this._window.Element &&
|
|
485
|
-
target.closest(".combobox-chips")
|
|
486
|
-
) {
|
|
437
|
+
} else if (this._isMultiple && target instanceof this._window.Element && target.closest(".combobox-chips")) {
|
|
487
438
|
this._input.focus();
|
|
488
439
|
}
|
|
489
440
|
}
|
|
490
|
-
|
|
491
441
|
_handleKeydown(event) {
|
|
492
442
|
const available = this._visibleOptions();
|
|
493
443
|
const current = available.findIndex(
|
|
@@ -528,3 +478,6 @@ export default class Combobox {
|
|
|
528
478
|
}
|
|
529
479
|
}
|
|
530
480
|
}
|
|
481
|
+
export {
|
|
482
|
+
Combobox as default
|
|
483
|
+
};
|
package/dist/js/context-menu.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI context-menu.js v1.0.0
|
|
3
|
-
* Copyright 2026 WPMoo
|
|
2
|
+
* Moo UI context-menu.js v1.0.0 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
4
|
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
5
|
*/
|
|
6
|
-
const instances = new WeakMap();
|
|
7
6
|
|
|
7
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
8
8
|
function pointerRect(event) {
|
|
9
9
|
const { clientX: x, clientY: y } = event;
|
|
10
10
|
return { top: y, left: x, right: x, bottom: y, width: 0, height: 0, x, y };
|
|
11
11
|
}
|
|
12
|
-
|
|
13
12
|
function isDisabledTrigger(trigger) {
|
|
14
13
|
return Boolean(trigger?.disabled || trigger?.getAttribute?.("aria-disabled") === "true");
|
|
15
14
|
}
|
|
16
|
-
|
|
17
15
|
function menuItemFromEvent(event) {
|
|
18
16
|
const target = event.target;
|
|
19
17
|
const item = target.closest?.(".dropdown-item");
|
|
@@ -22,16 +20,13 @@ function menuItemFromEvent(event) {
|
|
|
22
20
|
}
|
|
23
21
|
return target.closest?.("li")?.querySelector(":scope > .dropdown-item") || null;
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
export default class ContextMenu {
|
|
23
|
+
class ContextMenu {
|
|
27
24
|
static getInstance(element) {
|
|
28
25
|
return element?.nodeType === 1 ? instances.get(element) || null : null;
|
|
29
26
|
}
|
|
30
|
-
|
|
31
27
|
static getOrCreateInstance(element, config = {}) {
|
|
32
28
|
return ContextMenu.getInstance(element) || new ContextMenu(element, config);
|
|
33
29
|
}
|
|
34
|
-
|
|
35
30
|
constructor(element, config = {}) {
|
|
36
31
|
if (element?.nodeType !== 1 || !element.matches(".context-menu")) {
|
|
37
32
|
throw new TypeError("ContextMenu requires a .context-menu root element.");
|
|
@@ -40,7 +35,6 @@ export default class ContextMenu {
|
|
|
40
35
|
if (existing) {
|
|
41
36
|
return existing;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
38
|
this._element = element;
|
|
45
39
|
this._document = element.ownerDocument;
|
|
46
40
|
this._window = this._document.defaultView;
|
|
@@ -52,12 +46,10 @@ export default class ContextMenu {
|
|
|
52
46
|
if (!this._fallback) {
|
|
53
47
|
throw new TypeError("ContextMenu requires an explicit fallback trigger.");
|
|
54
48
|
}
|
|
55
|
-
|
|
56
49
|
const Dropdown = this._bootstrap("Dropdown");
|
|
57
50
|
if (!Dropdown) {
|
|
58
51
|
throw new TypeError("ContextMenu requires bootstrap.Dropdown (bootstrap.bundle.js).");
|
|
59
52
|
}
|
|
60
|
-
|
|
61
53
|
this._config = { ...config };
|
|
62
54
|
this._listeners = [];
|
|
63
55
|
this._invoker = null;
|
|
@@ -65,17 +57,15 @@ export default class ContextMenu {
|
|
|
65
57
|
this._openMethod = null;
|
|
66
58
|
this._reference = {
|
|
67
59
|
getBoundingClientRect: () => this._currentRect(),
|
|
68
|
-
contextElement: this._fallback
|
|
60
|
+
contextElement: this._fallback
|
|
69
61
|
};
|
|
70
62
|
this._dropdown = new Dropdown(this._fallback, {
|
|
71
63
|
reference: this._reference,
|
|
72
|
-
autoClose: "outside"
|
|
64
|
+
autoClose: "outside"
|
|
73
65
|
});
|
|
74
|
-
|
|
75
66
|
instances.set(element, this);
|
|
76
67
|
this._bindEvents();
|
|
77
68
|
}
|
|
78
|
-
|
|
79
69
|
dispose() {
|
|
80
70
|
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
81
71
|
target.removeEventListener(type, handler, options);
|
|
@@ -87,56 +77,46 @@ export default class ContextMenu {
|
|
|
87
77
|
this._pointerRect = null;
|
|
88
78
|
instances.delete(this._element);
|
|
89
79
|
}
|
|
90
|
-
|
|
91
80
|
show(anchor = this._fallback) {
|
|
92
81
|
this._open(anchor, null);
|
|
93
82
|
}
|
|
94
|
-
|
|
95
83
|
hide() {
|
|
96
84
|
this._dropdown.hide();
|
|
97
85
|
}
|
|
98
|
-
|
|
99
86
|
_listen(target, type, handler, options) {
|
|
100
87
|
target.addEventListener(type, handler, options);
|
|
101
88
|
this._listeners.push({ target, type, handler, options });
|
|
102
89
|
}
|
|
103
|
-
|
|
104
90
|
_bootstrap(name) {
|
|
105
91
|
return this._window.bootstrap?.[name] || null;
|
|
106
92
|
}
|
|
107
|
-
|
|
108
93
|
_currentRect() {
|
|
109
94
|
if (this._pointerRect) {
|
|
110
95
|
return this._pointerRect;
|
|
111
96
|
}
|
|
112
97
|
return (this._invoker || this._fallback).getBoundingClientRect();
|
|
113
98
|
}
|
|
114
|
-
|
|
115
99
|
_trigger(name, cancelable = false) {
|
|
116
100
|
return this._element.dispatchEvent(
|
|
117
101
|
new this._window.CustomEvent(`${name}.moo.context-menu`, {
|
|
118
102
|
bubbles: true,
|
|
119
|
-
cancelable
|
|
103
|
+
cancelable
|
|
120
104
|
})
|
|
121
105
|
);
|
|
122
106
|
}
|
|
123
|
-
|
|
124
107
|
_triggers() {
|
|
125
108
|
return Array.from(this._element.querySelectorAll(":scope > .context-menu-trigger"));
|
|
126
109
|
}
|
|
127
|
-
|
|
128
110
|
_syncExpanded(expanded) {
|
|
129
111
|
this._triggers().forEach((trigger) => {
|
|
130
112
|
trigger.setAttribute("aria-expanded", String(expanded));
|
|
131
113
|
});
|
|
132
114
|
}
|
|
133
|
-
|
|
134
115
|
_menuItems() {
|
|
135
116
|
return Array.from(this._menu.querySelectorAll(".dropdown-item")).filter(
|
|
136
117
|
(item) => !isDisabledTrigger(item) && !item.classList.contains("disabled")
|
|
137
118
|
);
|
|
138
119
|
}
|
|
139
|
-
|
|
140
120
|
_open(trigger, pointerEvent, openMethod) {
|
|
141
121
|
if (isDisabledTrigger(trigger)) {
|
|
142
122
|
return;
|
|
@@ -149,7 +129,6 @@ export default class ContextMenu {
|
|
|
149
129
|
this._openMethod = openMethod;
|
|
150
130
|
this._dropdown.show();
|
|
151
131
|
}
|
|
152
|
-
|
|
153
132
|
_handleContextMenu(event) {
|
|
154
133
|
const trigger = event.target.closest(".context-menu-trigger");
|
|
155
134
|
if (!trigger || !this._element.contains(trigger)) {
|
|
@@ -158,7 +137,6 @@ export default class ContextMenu {
|
|
|
158
137
|
event.preventDefault();
|
|
159
138
|
this._open(trigger, event, "pointer");
|
|
160
139
|
}
|
|
161
|
-
|
|
162
140
|
_handleKeydown(event) {
|
|
163
141
|
const trigger = event.target.closest(".context-menu-trigger");
|
|
164
142
|
if (!trigger || !this._element.contains(trigger)) {
|
|
@@ -172,13 +150,11 @@ export default class ContextMenu {
|
|
|
172
150
|
event.preventDefault();
|
|
173
151
|
this._open(trigger, null, "keyboard");
|
|
174
152
|
}
|
|
175
|
-
|
|
176
153
|
_handleFallbackClick() {
|
|
177
154
|
this._invoker = this._fallback;
|
|
178
155
|
this._pointerRect = null;
|
|
179
156
|
this._openMethod = "pointer";
|
|
180
157
|
}
|
|
181
|
-
|
|
182
158
|
_handleFallbackKeydown(event) {
|
|
183
159
|
if (event.key !== " " && event.key !== "Spacebar") {
|
|
184
160
|
return;
|
|
@@ -191,38 +167,28 @@ export default class ContextMenu {
|
|
|
191
167
|
}
|
|
192
168
|
this._open(this._fallback, null, "fallback-keyboard");
|
|
193
169
|
}
|
|
194
|
-
|
|
195
170
|
_handleMenuClick(event) {
|
|
196
171
|
const item = menuItemFromEvent(event);
|
|
197
172
|
if (!item) {
|
|
198
173
|
return;
|
|
199
174
|
}
|
|
200
175
|
const persistentItem = item.matches(".dropdown-item-check, [data-context-menu-persistent]");
|
|
201
|
-
const disabledItem =
|
|
202
|
-
item.matches('.dropdown-item.disabled, .dropdown-item[aria-disabled="true"], .dropdown-item:disabled') ||
|
|
203
|
-
isDisabledTrigger(item);
|
|
176
|
+
const disabledItem = item.matches('.dropdown-item.disabled, .dropdown-item[aria-disabled="true"], .dropdown-item:disabled') || isDisabledTrigger(item);
|
|
204
177
|
if (disabledItem) {
|
|
205
178
|
event.preventDefault();
|
|
206
179
|
event.stopPropagation();
|
|
207
180
|
return;
|
|
208
181
|
}
|
|
209
|
-
// Radio-grouped toggle buttons reuse the dropdown_toggle_item() button
|
|
210
|
-
// Bootstrap's own roving arrow-key focus already picks up (it only ever
|
|
211
|
-
// matches real .dropdown-item elements); Moo adds only the missing
|
|
212
|
-
// mutual-exclusivity Bootstrap's independent button-toggle plugin does
|
|
213
|
-
// not provide.
|
|
214
182
|
const radioButton = item.matches("[data-context-menu-radio-group]") ? item : null;
|
|
215
183
|
if (radioButton) {
|
|
216
184
|
event.preventDefault();
|
|
217
185
|
event.stopPropagation();
|
|
218
186
|
const group = radioButton.dataset.contextMenuRadioGroup;
|
|
219
|
-
this._menu
|
|
220
|
-
|
|
221
|
-
.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
button.setAttribute("aria-pressed", String(checked));
|
|
225
|
-
});
|
|
187
|
+
this._menu.querySelectorAll(`[data-context-menu-radio-group="${group}"]`).forEach((button) => {
|
|
188
|
+
const checked = button === radioButton;
|
|
189
|
+
button.classList.toggle("active", checked);
|
|
190
|
+
button.setAttribute("aria-pressed", String(checked));
|
|
191
|
+
});
|
|
226
192
|
return;
|
|
227
193
|
}
|
|
228
194
|
if (persistentItem) {
|
|
@@ -230,7 +196,6 @@ export default class ContextMenu {
|
|
|
230
196
|
}
|
|
231
197
|
this._dropdown.hide();
|
|
232
198
|
}
|
|
233
|
-
|
|
234
199
|
_bindEvents() {
|
|
235
200
|
this._listen(this._element, "contextmenu", (event) => this._handleContextMenu(event));
|
|
236
201
|
this._listen(this._element, "keydown", (event) => this._handleKeydown(event));
|
|
@@ -244,12 +209,6 @@ export default class ContextMenu {
|
|
|
244
209
|
});
|
|
245
210
|
this._listen(this._fallback, "shown.bs.dropdown", () => {
|
|
246
211
|
this._syncExpanded(true);
|
|
247
|
-
// Bootstrap's own show() never focuses an item either -- only its
|
|
248
|
-
// Up/Down-key handler does, since a pointer-driven open (right-click,
|
|
249
|
-
// or a click on the fallback trigger) should let :hover carry the
|
|
250
|
-
// highlight instead of a focus ring competing with it. A keyboard-
|
|
251
|
-
// driven open (Shift+F10/ContextMenu key) still lands on the first
|
|
252
|
-
// item, matching that same native arrow-key-open convention.
|
|
253
212
|
if (this._openMethod === "fallback-keyboard") {
|
|
254
213
|
this._fallback.focus();
|
|
255
214
|
} else if (this._openMethod === "keyboard") {
|
|
@@ -269,22 +228,8 @@ export default class ContextMenu {
|
|
|
269
228
|
const invoker = this._invoker;
|
|
270
229
|
this._pointerRect = null;
|
|
271
230
|
this._invoker = null;
|
|
272
|
-
// A native Bootstrap click on the fallback trigger bypasses _open()
|
|
273
|
-
// entirely (Bootstrap's own capture-phase click-to-toggle handler
|
|
274
|
-
// calls show() directly), so _handleFallbackClick's bubble-phase
|
|
275
|
-
// "click" listener always runs too late to set _openMethod before
|
|
276
|
-
// shown.bs.dropdown reads it. Resetting it here, back to the neutral
|
|
277
|
-
// pointer default, is what keeps that path correct rather than
|
|
278
|
-
// inheriting whatever a prior keyboard-triggered open last set.
|
|
279
231
|
this._openMethod = null;
|
|
280
232
|
this._trigger("hidden");
|
|
281
|
-
// Bootstrap's own dataApiKeydownHandler (js/src/dropdown.js) intercepts
|
|
282
|
-
// Escape in the document capture phase and hardcodes
|
|
283
|
-
// `getToggleButton.focus()` (our fallback trigger) after hide().
|
|
284
|
-
// Nothing downstream of a capture-phase stopPropagation() can observe
|
|
285
|
-
// that keydown to redirect focus, so correct it in a microtask, which
|
|
286
|
-
// still runs before the next paint: if focus landed on the fallback
|
|
287
|
-
// but a different trigger invoked this menu, return focus there.
|
|
288
233
|
queueMicrotask(() => {
|
|
289
234
|
if (invoker && invoker !== this._fallback && this._document.activeElement === this._fallback) {
|
|
290
235
|
invoker.focus();
|
|
@@ -293,3 +238,6 @@ export default class ContextMenu {
|
|
|
293
238
|
});
|
|
294
239
|
}
|
|
295
240
|
}
|
|
241
|
+
export {
|
|
242
|
+
ContextMenu as default
|
|
243
|
+
};
|
package/dist/js/datatable.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Moo UI datatable.js v1.0.0
|
|
3
|
-
* Copyright 2026 WPMoo
|
|
2
|
+
* Moo UI datatable.js v1.0.0 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
4
|
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
5
|
*/
|
|
6
|
+
|
|
6
7
|
var __defProp = Object.defineProperty;
|
|
7
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
9
|
|
|
9
10
|
// src/js/theme-owner.js
|
|
10
11
|
var OWNER_SELECTOR = '.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
|
|
11
|
-
var PORTAL_SELECTOR = "[data-moo-overlay-
|
|
12
|
+
var PORTAL_SELECTOR = "[data-moo-overlay-host]";
|
|
12
13
|
function documentFor(node) {
|
|
13
14
|
if (node?.nodeType === 9) return node;
|
|
14
15
|
if (node?.ownerDocument) return node.ownerDocument;
|
|
@@ -137,12 +138,12 @@ var _DataTable = class _DataTable {
|
|
|
137
138
|
// That keeps the menu out of the scroll wrapper's layout math, so Safari
|
|
138
139
|
// cannot clip it at the rounded frame edge or shift the visible table
|
|
139
140
|
// slice while Popper positions it against the viewport.
|
|
140
|
-
//
|
|
141
|
-
// .
|
|
142
|
-
//
|
|
141
|
+
// Public Dropdown compositions need the same behavior as the built-in
|
|
142
|
+
// row actions. Scope by row/card ownership so toolbar and sort menus keep
|
|
143
|
+
// their own lifecycle.
|
|
143
144
|
_rowActionTriggers() {
|
|
144
145
|
return this._element.querySelectorAll(
|
|
145
|
-
'
|
|
146
|
+
'tr[data-datatable-row] .dropdown [data-bs-toggle="dropdown"], [data-datatable-card] .dropdown [data-bs-toggle="dropdown"]'
|
|
146
147
|
);
|
|
147
148
|
}
|
|
148
149
|
_initRowActionDropdowns() {
|
|
@@ -226,7 +227,7 @@ var _DataTable = class _DataTable {
|
|
|
226
227
|
});
|
|
227
228
|
}
|
|
228
229
|
_rowActionMenuForTrigger(trigger) {
|
|
229
|
-
if (!trigger?.closest?.("
|
|
230
|
+
if (!trigger?.closest?.("tr[data-datatable-row], [data-datatable-card]")) {
|
|
230
231
|
return null;
|
|
231
232
|
}
|
|
232
233
|
return trigger.closest(".dropdown")?.querySelector(":scope > .dropdown-menu") || null;
|
|
@@ -391,38 +392,54 @@ var _DataTable = class _DataTable {
|
|
|
391
392
|
active.blur();
|
|
392
393
|
}
|
|
393
394
|
}
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
// developer-chosen breakpoint), so the choice belongs to the reader across
|
|
397
|
-
// visits, not just the current render.
|
|
395
|
+
// Auto mode remembers independent narrow/wide choices; toggle mode keeps
|
|
396
|
+
// its existing viewport-independent preference.
|
|
398
397
|
_initViewToggle() {
|
|
399
398
|
const toggles = Array.from(this._element.querySelectorAll(".datatable-view-toggle"));
|
|
400
399
|
if (!toggles.length) {
|
|
401
400
|
return;
|
|
402
401
|
}
|
|
403
402
|
const inputs = toggles.flatMap((toggle) => Array.from(toggle.querySelectorAll("input")));
|
|
404
|
-
const
|
|
403
|
+
const breakpoint = this._window.getComputedStyle(this._element).getPropertyValue("--moo-datatable-responsive-breakpoint").trim();
|
|
404
|
+
const auto = this._element.hasAttribute("data-datatable-responsive-breakpoint");
|
|
405
|
+
const media = auto && breakpoint ? this._window.matchMedia(`(min-width: ${breakpoint})`) : null;
|
|
406
|
+
const preferences = /* @__PURE__ */ new Map();
|
|
407
|
+
const storageKey = /* @__PURE__ */ __name(() => `moo-datatable-view:${this._element.id}${media ? media.matches ? ":wide" : ":narrow" : ""}`, "storageKey");
|
|
405
408
|
const setView = /* @__PURE__ */ __name((value, { persist = false } = {}) => {
|
|
409
|
+
if (this._element.dataset.datatableView !== value) {
|
|
410
|
+
const Dropdown = this._bootstrap("Dropdown");
|
|
411
|
+
[...this._rowActionTriggers(), ...this._sortTriggers()].forEach((trigger) => {
|
|
412
|
+
Dropdown?.getInstance(trigger)?.hide();
|
|
413
|
+
});
|
|
414
|
+
}
|
|
406
415
|
this._element.dataset.datatableView = value;
|
|
407
416
|
inputs.forEach((input) => {
|
|
408
417
|
input.checked = input.value === value;
|
|
409
418
|
});
|
|
410
419
|
if (persist) {
|
|
420
|
+
preferences.set(storageKey(), value);
|
|
411
421
|
try {
|
|
412
|
-
this._window.localStorage.setItem(storageKey, value);
|
|
422
|
+
this._window.localStorage.setItem(storageKey(), value);
|
|
413
423
|
} catch {
|
|
414
424
|
}
|
|
415
425
|
}
|
|
416
426
|
}, "setView");
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
427
|
+
const restoreView = /* @__PURE__ */ __name(() => {
|
|
428
|
+
let stored = preferences.get(storageKey());
|
|
429
|
+
try {
|
|
430
|
+
stored ?? (stored = this._window.localStorage.getItem(storageKey()));
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
setView(stored === "table" || stored === "cards" ? stored : media ? media.matches ? "table" : "cards" : this._element.dataset.datatableView);
|
|
434
|
+
}, "restoreView");
|
|
435
|
+
if (!auto || media) {
|
|
436
|
+
restoreView();
|
|
437
|
+
} else {
|
|
438
|
+
inputs.forEach((input) => {
|
|
439
|
+
input.checked = false;
|
|
440
|
+
});
|
|
425
441
|
}
|
|
442
|
+
if (media) this._listen(media, "change", restoreView);
|
|
426
443
|
toggles.forEach((toggle) => {
|
|
427
444
|
this._listen(toggle, "change", (event) => {
|
|
428
445
|
const value = event.target.value;
|