@wpmoo/ui 0.9.0 → 1.0.0-rc.10
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 +14005 -12457
- package/dist/assets/css/moo-ui.min.css +10 -28
- package/dist/assets/css/moo.css +2606 -902
- 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 +36 -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 +26 -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/_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/datatable.js
CHANGED
|
@@ -1,18 +1,61 @@
|
|
|
1
|
-
|
|
1
|
+
/*!
|
|
2
|
+
* Moo UI datatable.js v1.0.0-rc.10 (https://wpmoo.org/)
|
|
3
|
+
* Copyright 2026 WPMoo Authors
|
|
4
|
+
* Licensed under MIT (https://github.com/wpmoo-org/ui/blob/main/LICENSE)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
9
|
+
|
|
10
|
+
// src/js/theme-owner.js
|
|
11
|
+
var OWNER_SELECTOR = '.moo-ui[data-bs-theme="light"], .moo-ui[data-bs-theme="dark"]';
|
|
12
|
+
var PORTAL_SELECTOR = "[data-moo-overlay-host]";
|
|
13
|
+
function documentFor(node) {
|
|
14
|
+
if (node?.nodeType === 9) return node;
|
|
15
|
+
if (node?.ownerDocument) return node.ownerDocument;
|
|
16
|
+
return typeof document === "undefined" ? null : document;
|
|
17
|
+
}
|
|
18
|
+
__name(documentFor, "documentFor");
|
|
19
|
+
function isResolvedOwner(node) {
|
|
20
|
+
return Boolean(node?.matches?.(OWNER_SELECTOR));
|
|
21
|
+
}
|
|
22
|
+
__name(isResolvedOwner, "isResolvedOwner");
|
|
23
|
+
function ownerRootFor(node) {
|
|
24
|
+
const ownerDocument = documentFor(node);
|
|
25
|
+
if (node?.nodeType === 9 || node === ownerDocument?.body) {
|
|
26
|
+
return ownerDocument?.body?.firstElementChild || null;
|
|
27
|
+
}
|
|
28
|
+
return node;
|
|
29
|
+
}
|
|
30
|
+
__name(ownerRootFor, "ownerRootFor");
|
|
31
|
+
function findThemeOwner(node = typeof document === "undefined" ? null : document) {
|
|
32
|
+
const start = ownerRootFor(node);
|
|
33
|
+
if (!start) return null;
|
|
34
|
+
if (isResolvedOwner(start)) return start;
|
|
35
|
+
return start.closest?.(OWNER_SELECTOR) || null;
|
|
36
|
+
}
|
|
37
|
+
__name(findThemeOwner, "findThemeOwner");
|
|
38
|
+
function ownerPortalRoot(owner) {
|
|
39
|
+
if (!owner) return null;
|
|
40
|
+
return Array.from(owner.children || []).find(
|
|
41
|
+
(child) => child.matches?.(PORTAL_SELECTOR)
|
|
42
|
+
) || owner;
|
|
43
|
+
}
|
|
44
|
+
__name(ownerPortalRoot, "ownerPortalRoot");
|
|
2
45
|
|
|
46
|
+
// src/js/components/datatable.js
|
|
47
|
+
var instances = /* @__PURE__ */ new WeakMap();
|
|
3
48
|
function normalize(value) {
|
|
4
49
|
return (value ?? "").toString().trim().toLowerCase();
|
|
5
50
|
}
|
|
6
|
-
|
|
7
|
-
|
|
51
|
+
__name(normalize, "normalize");
|
|
52
|
+
var _DataTable = class _DataTable {
|
|
8
53
|
static getInstance(element) {
|
|
9
54
|
return element?.nodeType === 1 ? instances.get(element) || null : null;
|
|
10
55
|
}
|
|
11
|
-
|
|
12
56
|
static getOrCreateInstance(element, config = {}) {
|
|
13
|
-
return
|
|
57
|
+
return _DataTable.getInstance(element) || new _DataTable(element, config);
|
|
14
58
|
}
|
|
15
|
-
|
|
16
59
|
constructor(element, config = {}) {
|
|
17
60
|
if (element?.nodeType !== 1 || !element.matches(".datatable")) {
|
|
18
61
|
throw new TypeError("DataTable requires a .datatable root element.");
|
|
@@ -21,7 +64,6 @@ export default class DataTable {
|
|
|
21
64
|
if (existing) {
|
|
22
65
|
return existing;
|
|
23
66
|
}
|
|
24
|
-
|
|
25
67
|
this._element = element;
|
|
26
68
|
this._document = element.ownerDocument;
|
|
27
69
|
this._window = this._document.defaultView;
|
|
@@ -34,38 +76,44 @@ export default class DataTable {
|
|
|
34
76
|
const cardById = new Map(
|
|
35
77
|
Array.from(element.querySelectorAll("[data-datatable-card]")).map((card) => [card.dataset.datatableCardFor, card])
|
|
36
78
|
);
|
|
37
|
-
|
|
38
79
|
this._config = { ...config };
|
|
39
80
|
this._listeners = [];
|
|
40
81
|
this._pageSize = Number(element.dataset.datatablePageSize) || 10;
|
|
41
82
|
this._currentPage = 1;
|
|
42
83
|
this._sortKey = null;
|
|
43
84
|
this._sortDirection = null;
|
|
44
|
-
this._selectedIds = new Set();
|
|
45
|
-
this._facetSelections = new Map();
|
|
46
|
-
this._visibleFacets = new Map();
|
|
85
|
+
this._selectedIds = /* @__PURE__ */ new Set();
|
|
86
|
+
this._facetSelections = /* @__PURE__ */ new Map();
|
|
87
|
+
this._visibleFacets = /* @__PURE__ */ new Map();
|
|
47
88
|
this._searchTerm = "";
|
|
48
89
|
this._tooltips = [];
|
|
49
90
|
this._filterMode = element.dataset.datatableFilterMode || "inline";
|
|
50
|
-
|
|
91
|
+
this._reparentedRowMenus = /* @__PURE__ */ new Map();
|
|
92
|
+
this._reparentedRowMenuByTrigger = /* @__PURE__ */ new WeakMap();
|
|
93
|
+
this._reparentedSortMenus = /* @__PURE__ */ new Map();
|
|
94
|
+
this._reparentedSortMenuByTrigger = /* @__PURE__ */ new WeakMap();
|
|
51
95
|
this._rows = Array.from(this._tbody.querySelectorAll(":scope > tr[data-datatable-row]")).map((tr, index) => ({
|
|
52
96
|
element: tr,
|
|
53
97
|
cardElement: cardById.get(tr.id) || null,
|
|
54
98
|
index,
|
|
55
99
|
search: normalize(tr.dataset.datatableSearch),
|
|
56
|
-
facets: this._readFacets(tr)
|
|
100
|
+
facets: this._readFacets(tr)
|
|
57
101
|
}));
|
|
58
102
|
this._readInitialFacetVisibility();
|
|
59
103
|
this._readInitialSort();
|
|
60
|
-
|
|
61
104
|
instances.set(element, this);
|
|
62
105
|
this._bindEvents();
|
|
63
106
|
this._render();
|
|
64
107
|
this._initBulkTooltips();
|
|
65
108
|
this._initViewToggle();
|
|
109
|
+
this._initRowActionDropdowns();
|
|
110
|
+
this._initSortDropdowns();
|
|
66
111
|
}
|
|
67
|
-
|
|
68
112
|
dispose() {
|
|
113
|
+
this._disposeRowActionDropdowns();
|
|
114
|
+
this._disposeSortDropdowns();
|
|
115
|
+
this._restoreRowActionMenus();
|
|
116
|
+
this._restoreSortMenus();
|
|
69
117
|
this._listeners.forEach(({ target, type, handler, options }) => {
|
|
70
118
|
target.removeEventListener(type, handler, options);
|
|
71
119
|
});
|
|
@@ -74,11 +122,9 @@ export default class DataTable {
|
|
|
74
122
|
this._tooltips = [];
|
|
75
123
|
instances.delete(this._element);
|
|
76
124
|
}
|
|
77
|
-
|
|
78
125
|
getSelectedIds() {
|
|
79
126
|
return Array.from(this._selectedIds);
|
|
80
127
|
}
|
|
81
|
-
|
|
82
128
|
_listen(target, type, handler, options) {
|
|
83
129
|
if (!target) {
|
|
84
130
|
return;
|
|
@@ -86,11 +132,231 @@ export default class DataTable {
|
|
|
86
132
|
target.addEventListener(type, handler, options);
|
|
87
133
|
this._listeners.push({ target, type, handler, options });
|
|
88
134
|
}
|
|
89
|
-
|
|
135
|
+
// Row menus are authored next to their trigger so the public HTML stays
|
|
136
|
+
// inspectable, but when opened they are temporarily moved to their nearest
|
|
137
|
+
// Moo owner portal.
|
|
138
|
+
// That keeps the menu out of the scroll wrapper's layout math, so Safari
|
|
139
|
+
// cannot clip it at the rounded frame edge or shift the visible table
|
|
140
|
+
// slice while Popper positions it against the viewport.
|
|
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.
|
|
144
|
+
_rowActionTriggers() {
|
|
145
|
+
return this._element.querySelectorAll(
|
|
146
|
+
'tr[data-datatable-row] .dropdown [data-bs-toggle="dropdown"], [data-datatable-card] .dropdown [data-bs-toggle="dropdown"]'
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
_initRowActionDropdowns() {
|
|
150
|
+
const Dropdown = this._bootstrap("Dropdown");
|
|
151
|
+
if (!Dropdown) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this._rowActionTriggers().forEach((trigger) => {
|
|
155
|
+
Dropdown.getOrCreateInstance(trigger, {
|
|
156
|
+
popperConfig: /* @__PURE__ */ __name((defaultConfig) => this._clippingSafePopperConfig(defaultConfig), "popperConfig")
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
_sortTriggers() {
|
|
161
|
+
return this._element.querySelectorAll(
|
|
162
|
+
'.datatable-sort [data-bs-toggle="dropdown"]'
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
_initSortDropdowns() {
|
|
166
|
+
const Dropdown = this._bootstrap("Dropdown");
|
|
167
|
+
if (!Dropdown) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this._sortTriggers().forEach((trigger) => {
|
|
171
|
+
Dropdown.getOrCreateInstance(trigger, {
|
|
172
|
+
popperConfig: /* @__PURE__ */ __name((defaultConfig) => this._clippingSafePopperConfig(defaultConfig), "popperConfig")
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
_clippingSafePopperConfig(defaultConfig) {
|
|
177
|
+
return {
|
|
178
|
+
...defaultConfig,
|
|
179
|
+
strategy: "fixed",
|
|
180
|
+
modifiers: [
|
|
181
|
+
...defaultConfig.modifiers || [],
|
|
182
|
+
{
|
|
183
|
+
name: "flip",
|
|
184
|
+
options: {
|
|
185
|
+
fallbackPlacements: ["top-end", "top-start", "bottom-end", "bottom-start"]
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "preventOverflow",
|
|
190
|
+
options: {
|
|
191
|
+
boundary: "viewport",
|
|
192
|
+
padding: 8
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
// dispose() alone leaves any open row-action Dropdown's show/aria-expanded/
|
|
199
|
+
// data-bs-popper state and Bootstrap instance behind; hide and dispose each
|
|
200
|
+
// one first so a subsequent re-init starts from a clean state.
|
|
201
|
+
_disposeRowActionDropdowns() {
|
|
202
|
+
const Dropdown = this._bootstrap("Dropdown");
|
|
203
|
+
if (!Dropdown) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
this._rowActionTriggers().forEach((trigger) => {
|
|
207
|
+
const instance = Dropdown.getInstance(trigger);
|
|
208
|
+
if (!instance) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
instance.hide();
|
|
212
|
+
instance.dispose();
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
_disposeSortDropdowns() {
|
|
216
|
+
const Dropdown = this._bootstrap("Dropdown");
|
|
217
|
+
if (!Dropdown) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this._sortTriggers().forEach((trigger) => {
|
|
221
|
+
const instance = Dropdown.getInstance(trigger);
|
|
222
|
+
if (!instance) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
instance.hide();
|
|
226
|
+
instance.dispose();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
_rowActionMenuForTrigger(trigger) {
|
|
230
|
+
if (!trigger?.closest?.("tr[data-datatable-row], [data-datatable-card]")) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return trigger.closest(".dropdown")?.querySelector(":scope > .dropdown-menu") || null;
|
|
234
|
+
}
|
|
235
|
+
_rowActionOwnerIdForTrigger(trigger) {
|
|
236
|
+
const rowId = trigger?.closest?.("tr[data-datatable-row]")?.id;
|
|
237
|
+
if (rowId) {
|
|
238
|
+
return rowId;
|
|
239
|
+
}
|
|
240
|
+
return trigger?.closest?.("[data-datatable-card]")?.getAttribute("data-datatable-card-for") || "";
|
|
241
|
+
}
|
|
242
|
+
_reparentRowActionMenu(trigger) {
|
|
243
|
+
const menu = this._rowActionMenuForTrigger(trigger);
|
|
244
|
+
if (!menu || this._reparentedRowMenus.has(menu)) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const ownerId = this._rowActionOwnerIdForTrigger(trigger);
|
|
248
|
+
if (ownerId) {
|
|
249
|
+
menu.setAttribute("data-datatable-row-action-owner", ownerId);
|
|
250
|
+
}
|
|
251
|
+
if (trigger.id) {
|
|
252
|
+
menu.setAttribute("data-datatable-row-action-trigger", trigger.id);
|
|
253
|
+
}
|
|
254
|
+
this._reparentedRowMenus.set(menu, {
|
|
255
|
+
parent: menu.parentNode,
|
|
256
|
+
nextSibling: menu.nextSibling,
|
|
257
|
+
trigger,
|
|
258
|
+
owner: findThemeOwner(trigger)
|
|
259
|
+
});
|
|
260
|
+
this._reparentedRowMenuByTrigger.set(trigger, menu);
|
|
261
|
+
this._portalRoot(trigger).appendChild(menu);
|
|
262
|
+
}
|
|
263
|
+
_restoreRowActionMenuForTrigger(trigger) {
|
|
264
|
+
const menu = this._reparentedRowMenuByTrigger.get(trigger) || this._rowActionMenuForTrigger(trigger);
|
|
265
|
+
if (menu) {
|
|
266
|
+
this._restoreRowActionMenu(menu);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
_restoreRowActionMenu(menu) {
|
|
270
|
+
const original = this._reparentedRowMenus.get(menu);
|
|
271
|
+
if (!original) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const { parent, nextSibling, trigger, owner } = original;
|
|
275
|
+
if (owner?.isConnected === false) {
|
|
276
|
+
menu.remove();
|
|
277
|
+
} else if (parent?.isConnected) {
|
|
278
|
+
if (nextSibling?.parentNode === parent) {
|
|
279
|
+
parent.insertBefore(menu, nextSibling);
|
|
280
|
+
} else {
|
|
281
|
+
parent.appendChild(menu);
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
menu.remove();
|
|
285
|
+
}
|
|
286
|
+
if (trigger) {
|
|
287
|
+
this._reparentedRowMenuByTrigger.delete(trigger);
|
|
288
|
+
}
|
|
289
|
+
menu.removeAttribute("data-datatable-row-action-owner");
|
|
290
|
+
menu.removeAttribute("data-datatable-row-action-trigger");
|
|
291
|
+
this._reparentedRowMenus.delete(menu);
|
|
292
|
+
}
|
|
293
|
+
_restoreRowActionMenus() {
|
|
294
|
+
Array.from(this._reparentedRowMenus.keys()).forEach((menu) => {
|
|
295
|
+
this._restoreRowActionMenu(menu);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
_sortMenuForTrigger(trigger) {
|
|
299
|
+
if (!trigger?.closest?.(".datatable-sort")) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return this._reparentedSortMenuByTrigger.get(trigger) || trigger?.closest?.(".dropdown")?.querySelector(":scope > .dropdown-menu") || null;
|
|
303
|
+
}
|
|
304
|
+
// Sort menus are authored inside their table header, but must leave the
|
|
305
|
+
// overflow frame while open just like row-action menus do.
|
|
306
|
+
_reparentSortMenu(trigger) {
|
|
307
|
+
const menu = this._sortMenuForTrigger(trigger);
|
|
308
|
+
if (!menu || this._reparentedSortMenus.has(menu)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
this._reparentedSortMenus.set(menu, {
|
|
312
|
+
parent: menu.parentNode,
|
|
313
|
+
nextSibling: menu.nextSibling,
|
|
314
|
+
trigger,
|
|
315
|
+
owner: trigger.closest(".datatable-sort"),
|
|
316
|
+
themeOwner: findThemeOwner(trigger)
|
|
317
|
+
});
|
|
318
|
+
this._reparentedSortMenuByTrigger.set(trigger, menu);
|
|
319
|
+
this._portalRoot(trigger).appendChild(menu);
|
|
320
|
+
}
|
|
321
|
+
_restoreSortMenuForTrigger(trigger) {
|
|
322
|
+
const menu = this._reparentedSortMenuByTrigger.get(trigger) || this._sortMenuForTrigger(trigger);
|
|
323
|
+
if (menu) {
|
|
324
|
+
this._restoreSortMenu(menu);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
_restoreSortMenu(menu) {
|
|
328
|
+
const original = this._reparentedSortMenus.get(menu);
|
|
329
|
+
if (!original) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const { parent, nextSibling, trigger, themeOwner } = original;
|
|
333
|
+
if (themeOwner?.isConnected === false) {
|
|
334
|
+
menu.remove();
|
|
335
|
+
} else if (parent?.isConnected) {
|
|
336
|
+
if (nextSibling?.parentNode === parent) {
|
|
337
|
+
parent.insertBefore(menu, nextSibling);
|
|
338
|
+
} else {
|
|
339
|
+
parent.appendChild(menu);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
menu.remove();
|
|
343
|
+
}
|
|
344
|
+
if (trigger) {
|
|
345
|
+
this._reparentedSortMenuByTrigger.delete(trigger);
|
|
346
|
+
}
|
|
347
|
+
this._reparentedSortMenus.delete(menu);
|
|
348
|
+
}
|
|
349
|
+
_restoreSortMenus() {
|
|
350
|
+
Array.from(this._reparentedSortMenus.keys()).forEach((menu) => {
|
|
351
|
+
this._restoreSortMenu(menu);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
90
354
|
_bootstrap(name) {
|
|
91
355
|
return this._window.bootstrap?.[name] || null;
|
|
92
356
|
}
|
|
93
|
-
|
|
357
|
+
_portalRoot(trigger = this._element) {
|
|
358
|
+
return ownerPortalRoot(findThemeOwner(trigger)) || this._document.body || this._document.documentElement;
|
|
359
|
+
}
|
|
94
360
|
// The bulk-actions bar's icon-only buttons carry data-bs-title instead of
|
|
95
361
|
// a bare title attribute so Bootstrap's own Tooltip owns the hover/focus
|
|
96
362
|
// affordance (matching the rest of Moo's icon-button contracts) rather
|
|
@@ -107,69 +373,73 @@ export default class DataTable {
|
|
|
107
373
|
if (!Tooltip) {
|
|
108
374
|
return;
|
|
109
375
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// aria-label already gives it an accessible name without the hover
|
|
117
|
-
// affordance.
|
|
118
|
-
this._element
|
|
119
|
-
.querySelectorAll("[data-datatable-bulk-actions] [data-bs-title]:not([data-bs-toggle=\"dropdown\"])")
|
|
120
|
-
.forEach((trigger) => {
|
|
121
|
-
this._tooltips.push(Tooltip.getOrCreateInstance(trigger, { animation: false }));
|
|
122
|
-
});
|
|
376
|
+
this._element.querySelectorAll('[data-datatable-bulk-actions] [data-bs-title]:not([data-bs-toggle="dropdown"])').forEach((trigger) => {
|
|
377
|
+
this._tooltips.push(Tooltip.getOrCreateInstance(trigger, {
|
|
378
|
+
animation: false,
|
|
379
|
+
container: this._portalRoot(trigger)
|
|
380
|
+
}));
|
|
381
|
+
});
|
|
123
382
|
}
|
|
124
|
-
|
|
125
383
|
_hideBulkTooltips({ blur = false } = {}) {
|
|
126
384
|
this._tooltips.forEach((tooltip) => {
|
|
127
385
|
tooltip.hide();
|
|
128
386
|
});
|
|
129
|
-
|
|
130
387
|
if (!blur) {
|
|
131
388
|
return;
|
|
132
389
|
}
|
|
133
|
-
|
|
134
390
|
const active = this._document.activeElement;
|
|
135
391
|
if (active?.closest?.("[data-datatable-bulk-actions]")) {
|
|
136
392
|
active.blur();
|
|
137
393
|
}
|
|
138
394
|
}
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
// lets the reader pick between them (Odoo's List/Kanban switcher, not a
|
|
142
|
-
// developer-chosen breakpoint), so the choice belongs to the reader across
|
|
143
|
-
// visits, not just the current render.
|
|
395
|
+
// Auto mode remembers independent narrow/wide choices; toggle mode keeps
|
|
396
|
+
// its existing viewport-independent preference.
|
|
144
397
|
_initViewToggle() {
|
|
145
398
|
const toggles = Array.from(this._element.querySelectorAll(".datatable-view-toggle"));
|
|
146
399
|
if (!toggles.length) {
|
|
147
400
|
return;
|
|
148
401
|
}
|
|
149
402
|
const inputs = toggles.flatMap((toggle) => Array.from(toggle.querySelectorAll("input")));
|
|
150
|
-
const
|
|
151
|
-
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");
|
|
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
|
+
}
|
|
152
415
|
this._element.dataset.datatableView = value;
|
|
153
416
|
inputs.forEach((input) => {
|
|
154
417
|
input.checked = input.value === value;
|
|
155
418
|
});
|
|
156
419
|
if (persist) {
|
|
420
|
+
preferences.set(storageKey(), value);
|
|
157
421
|
try {
|
|
158
|
-
this._window.localStorage.setItem(storageKey, value);
|
|
422
|
+
this._window.localStorage.setItem(storageKey(), value);
|
|
159
423
|
} catch {
|
|
160
|
-
// Storage may be unavailable (private browsing); the toggle still works for this session.
|
|
161
424
|
}
|
|
162
425
|
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
426
|
+
}, "setView");
|
|
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
|
+
});
|
|
172
441
|
}
|
|
442
|
+
if (media) this._listen(media, "change", restoreView);
|
|
173
443
|
toggles.forEach((toggle) => {
|
|
174
444
|
this._listen(toggle, "change", (event) => {
|
|
175
445
|
const value = event.target.value;
|
|
@@ -180,7 +450,6 @@ export default class DataTable {
|
|
|
180
450
|
});
|
|
181
451
|
});
|
|
182
452
|
}
|
|
183
|
-
|
|
184
453
|
_readFacets(tr) {
|
|
185
454
|
const facets = {};
|
|
186
455
|
Array.from(tr.attributes).forEach((attr) => {
|
|
@@ -191,7 +460,6 @@ export default class DataTable {
|
|
|
191
460
|
});
|
|
192
461
|
return facets;
|
|
193
462
|
}
|
|
194
|
-
|
|
195
463
|
_readInitialFacetVisibility() {
|
|
196
464
|
this._element.querySelectorAll("[data-datatable-facet]").forEach((facetRoot) => {
|
|
197
465
|
const key = facetRoot.dataset.datatableFacet;
|
|
@@ -212,7 +480,6 @@ export default class DataTable {
|
|
|
212
480
|
}
|
|
213
481
|
});
|
|
214
482
|
}
|
|
215
|
-
|
|
216
483
|
_readInitialSort() {
|
|
217
484
|
const th = this._element.querySelector("th[data-datatable-initial-sort][data-datatable-column]");
|
|
218
485
|
const direction = th?.dataset.datatableInitialSort;
|
|
@@ -222,16 +489,14 @@ export default class DataTable {
|
|
|
222
489
|
this._sortKey = th.dataset.datatableColumn || null;
|
|
223
490
|
this._sortDirection = this._sortKey ? direction : null;
|
|
224
491
|
}
|
|
225
|
-
|
|
226
492
|
_trigger(name, detail = {}) {
|
|
227
493
|
this._element.dispatchEvent(
|
|
228
494
|
new this._window.CustomEvent(`${name}.moo.datatable`, {
|
|
229
495
|
bubbles: true,
|
|
230
|
-
detail
|
|
496
|
+
detail
|
|
231
497
|
})
|
|
232
498
|
);
|
|
233
499
|
}
|
|
234
|
-
|
|
235
500
|
_sortValue(row, key) {
|
|
236
501
|
const cell = row.element.querySelector(`:scope > [data-datatable-column="${key}"]`);
|
|
237
502
|
if (!cell) {
|
|
@@ -239,7 +504,6 @@ export default class DataTable {
|
|
|
239
504
|
}
|
|
240
505
|
return normalize(cell.dataset.datatableSortValue ?? cell.textContent);
|
|
241
506
|
}
|
|
242
|
-
|
|
243
507
|
_matchesFilters(row) {
|
|
244
508
|
if (this._searchTerm && !row.search.includes(this._searchTerm)) {
|
|
245
509
|
return false;
|
|
@@ -254,7 +518,6 @@ export default class DataTable {
|
|
|
254
518
|
}
|
|
255
519
|
return true;
|
|
256
520
|
}
|
|
257
|
-
|
|
258
521
|
_visibleRows() {
|
|
259
522
|
const filtered = this._rows.filter((row) => this._matchesFilters(row));
|
|
260
523
|
if (this._sortKey) {
|
|
@@ -272,11 +535,9 @@ export default class DataTable {
|
|
|
272
535
|
}
|
|
273
536
|
return filtered;
|
|
274
537
|
}
|
|
275
|
-
|
|
276
538
|
_pageCount(rowCount) {
|
|
277
539
|
return Math.max(1, Math.ceil(rowCount / this._pageSize));
|
|
278
540
|
}
|
|
279
|
-
|
|
280
541
|
_render() {
|
|
281
542
|
const filtered = this._visibleRows();
|
|
282
543
|
const pageCount = this._pageCount(filtered.length);
|
|
@@ -284,7 +545,6 @@ export default class DataTable {
|
|
|
284
545
|
const start = (this._currentPage - 1) * this._pageSize;
|
|
285
546
|
const pageRows = filtered.slice(start, start + this._pageSize);
|
|
286
547
|
const pageRowIds = new Set(pageRows.map((row) => row.element.id));
|
|
287
|
-
|
|
288
548
|
this._rows.forEach((row) => {
|
|
289
549
|
const isVisible = pageRowIds.has(row.element.id);
|
|
290
550
|
row.element.hidden = !isVisible;
|
|
@@ -292,19 +552,9 @@ export default class DataTable {
|
|
|
292
552
|
row.cardElement.hidden = !isVisible;
|
|
293
553
|
}
|
|
294
554
|
});
|
|
295
|
-
|
|
296
|
-
// appendChild() always detaches-and-reinserts, which blurs a focused
|
|
297
|
-
// descendant (e.g. a row checkbox) even when the node is only moved to
|
|
298
|
-
// the position it already occupies. Selecting/deselecting a row never
|
|
299
|
-
// changes row order, so skip the reorder entirely unless the DOM order
|
|
300
|
-
// actually disagrees with the desired page order.
|
|
301
|
-
const currentOrder = Array.from(this._tbody.children)
|
|
302
|
-
.filter((element) => pageRowIds.has(element.id))
|
|
303
|
-
.map((element) => element.id);
|
|
555
|
+
const currentOrder = Array.from(this._tbody.children).filter((element) => pageRowIds.has(element.id)).map((element) => element.id);
|
|
304
556
|
const desiredOrder = pageRows.map((row) => row.element.id);
|
|
305
|
-
const orderChanged =
|
|
306
|
-
currentOrder.length !== desiredOrder.length ||
|
|
307
|
-
currentOrder.some((id, index) => id !== desiredOrder[index]);
|
|
557
|
+
const orderChanged = currentOrder.length !== desiredOrder.length || currentOrder.some((id, index) => id !== desiredOrder[index]);
|
|
308
558
|
if (orderChanged) {
|
|
309
559
|
pageRows.forEach((row) => {
|
|
310
560
|
this._tbody.appendChild(row.element);
|
|
@@ -313,7 +563,6 @@ export default class DataTable {
|
|
|
313
563
|
}
|
|
314
564
|
});
|
|
315
565
|
}
|
|
316
|
-
|
|
317
566
|
this._renderToolbarState();
|
|
318
567
|
this._renderSortHeaders();
|
|
319
568
|
this._renderSelection(pageRows);
|
|
@@ -323,7 +572,6 @@ export default class DataTable {
|
|
|
323
572
|
this._renderPageSize();
|
|
324
573
|
this._renderBulkActions();
|
|
325
574
|
}
|
|
326
|
-
|
|
327
575
|
_renderToolbarState() {
|
|
328
576
|
const hasSearch = Boolean(this._searchTerm);
|
|
329
577
|
const hasFacets = Array.from(this._facetSelections.values()).some((set) => set.size > 0);
|
|
@@ -331,10 +579,9 @@ export default class DataTable {
|
|
|
331
579
|
if (resetButton) {
|
|
332
580
|
resetButton.hidden = !(hasSearch || hasFacets);
|
|
333
581
|
}
|
|
334
|
-
|
|
335
582
|
this._element.querySelectorAll("[data-datatable-facet]").forEach((facetRoot) => {
|
|
336
583
|
const key = facetRoot.dataset.datatableFacet;
|
|
337
|
-
const selected = this._facetSelections.get(key) || new Set();
|
|
584
|
+
const selected = this._facetSelections.get(key) || /* @__PURE__ */ new Set();
|
|
338
585
|
if (this._visibleFacets.has(key)) {
|
|
339
586
|
facetRoot.hidden = !this._visibleFacets.get(key);
|
|
340
587
|
}
|
|
@@ -360,36 +607,31 @@ export default class DataTable {
|
|
|
360
607
|
option.setAttribute("aria-pressed", String(isSelected));
|
|
361
608
|
});
|
|
362
609
|
});
|
|
363
|
-
|
|
364
610
|
this._element.querySelectorAll("[data-datatable-filter-toggle]").forEach((toggle) => {
|
|
365
611
|
const key = toggle.dataset.datatableFilterToggle;
|
|
366
612
|
const visible = this._visibleFacets.get(key) ?? true;
|
|
367
613
|
toggle.classList.toggle("active", visible);
|
|
368
614
|
toggle.setAttribute("aria-pressed", visible ? "true" : "false");
|
|
369
615
|
});
|
|
370
|
-
|
|
371
616
|
this._element.querySelectorAll("[data-datatable-filter-option]").forEach((option) => {
|
|
372
617
|
const key = option.dataset.datatableFilterOptionKey;
|
|
373
|
-
const selected = this._facetSelections.get(key) || new Set();
|
|
618
|
+
const selected = this._facetSelections.get(key) || /* @__PURE__ */ new Set();
|
|
374
619
|
const active = selected.has(option.dataset.datatableFilterOption);
|
|
375
620
|
option.classList.toggle("active", active);
|
|
376
621
|
option.setAttribute("aria-pressed", active ? "true" : "false");
|
|
377
622
|
});
|
|
378
|
-
|
|
379
623
|
this._element.querySelectorAll("[data-datatable-filter-group-summary]").forEach((summary) => {
|
|
380
|
-
const selected = this._facetSelections.get(summary.dataset.datatableFilterGroupSummary) || new Set();
|
|
624
|
+
const selected = this._facetSelections.get(summary.dataset.datatableFilterGroupSummary) || /* @__PURE__ */ new Set();
|
|
381
625
|
summary.hidden = selected.size === 0;
|
|
382
626
|
summary.textContent = selected.size > 0 ? `${selected.size} selected` : "";
|
|
383
627
|
});
|
|
384
628
|
}
|
|
385
|
-
|
|
386
629
|
_createFacetBadge(text) {
|
|
387
630
|
const badge = this._document.createElement("span");
|
|
388
631
|
badge.className = "datatable-facet-badge";
|
|
389
632
|
badge.textContent = text;
|
|
390
633
|
return badge;
|
|
391
634
|
}
|
|
392
|
-
|
|
393
635
|
_renderSelection(pageRows) {
|
|
394
636
|
const selectAllControls = Array.from(this._element.querySelectorAll("[data-datatable-select-all]"));
|
|
395
637
|
pageRows.forEach((row) => {
|
|
@@ -408,7 +650,6 @@ export default class DataTable {
|
|
|
408
650
|
control.indeterminate = checked > 0 && checked < total;
|
|
409
651
|
});
|
|
410
652
|
}
|
|
411
|
-
|
|
412
653
|
_renderBulkActions() {
|
|
413
654
|
const bar = this._element.querySelector("[data-datatable-bulk-actions]");
|
|
414
655
|
if (!bar) {
|
|
@@ -420,14 +661,12 @@ export default class DataTable {
|
|
|
420
661
|
count.textContent = String(this._selectedIds.size);
|
|
421
662
|
}
|
|
422
663
|
}
|
|
423
|
-
|
|
424
664
|
_renderFrameState(totalRows) {
|
|
425
665
|
const isEmpty = totalRows === 0;
|
|
426
666
|
this._element.querySelectorAll(".datatable-frame, .datatable-card-frame").forEach((frame) => {
|
|
427
667
|
frame.hidden = isEmpty;
|
|
428
668
|
});
|
|
429
669
|
}
|
|
430
|
-
|
|
431
670
|
_renderEmptyState(totalRows) {
|
|
432
671
|
const empty = this._element.querySelector("[data-datatable-empty]");
|
|
433
672
|
if (!empty) {
|
|
@@ -447,7 +686,6 @@ export default class DataTable {
|
|
|
447
686
|
reset.hidden = !hasRows;
|
|
448
687
|
}
|
|
449
688
|
}
|
|
450
|
-
|
|
451
689
|
_sortAriaValue(direction) {
|
|
452
690
|
if (direction === "asc") {
|
|
453
691
|
return "ascending";
|
|
@@ -457,7 +695,6 @@ export default class DataTable {
|
|
|
457
695
|
}
|
|
458
696
|
return "none";
|
|
459
697
|
}
|
|
460
|
-
|
|
461
698
|
_renderSortHeaders() {
|
|
462
699
|
this._element.querySelectorAll("th[data-datatable-column]").forEach((th) => {
|
|
463
700
|
const trigger = th.querySelector("[data-datatable-sort-key]");
|
|
@@ -478,7 +715,6 @@ export default class DataTable {
|
|
|
478
715
|
}
|
|
479
716
|
});
|
|
480
717
|
}
|
|
481
|
-
|
|
482
718
|
_renderPagination(totalRows, pageCount) {
|
|
483
719
|
const first = this._element.querySelector("[data-datatable-page-first]");
|
|
484
720
|
const prev = this._element.querySelector("[data-datatable-page-prev]");
|
|
@@ -488,7 +724,6 @@ export default class DataTable {
|
|
|
488
724
|
const atEnd = this._currentPage >= pageCount;
|
|
489
725
|
[first, prev].forEach((button) => this._setPageItemDisabled(button, atStart));
|
|
490
726
|
[next, last].forEach((button) => this._setPageItemDisabled(button, atEnd));
|
|
491
|
-
|
|
492
727
|
const summary = this._element.querySelector("[data-datatable-results-summary]");
|
|
493
728
|
if (summary) {
|
|
494
729
|
if (totalRows === 0) {
|
|
@@ -499,42 +734,53 @@ export default class DataTable {
|
|
|
499
734
|
summary.textContent = `Showing ${start}-${end} of ${totalRows}`;
|
|
500
735
|
}
|
|
501
736
|
}
|
|
502
|
-
|
|
503
737
|
const template = this._element.querySelector("template[data-datatable-page-numbers]");
|
|
504
738
|
if (!template) {
|
|
505
739
|
return;
|
|
506
740
|
}
|
|
507
|
-
template.parentNode
|
|
508
|
-
.querySelectorAll("[data-datatable-page-number], [data-datatable-page-ellipsis]")
|
|
509
|
-
.forEach((node) => node.remove());
|
|
510
|
-
|
|
511
741
|
const pages = this._pageWindow(this._currentPage, pageCount);
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
742
|
+
const existing = Array.from(
|
|
743
|
+
template.parentNode.querySelectorAll(
|
|
744
|
+
"[data-datatable-page-number], [data-datatable-page-ellipsis]"
|
|
745
|
+
)
|
|
746
|
+
);
|
|
747
|
+
const existingWindow = existing.map(
|
|
748
|
+
(node) => node.dataset.datatablePageEllipsis === void 0 ? Number(node.dataset.datatablePageNumber) : "\u2026"
|
|
749
|
+
);
|
|
750
|
+
const windowChanged = existingWindow.length !== pages.length || existingWindow.some((page, index) => page !== pages[index]);
|
|
751
|
+
if (windowChanged) {
|
|
752
|
+
existing.forEach((node) => node.remove());
|
|
753
|
+
const fragment = this._document.createDocumentFragment();
|
|
754
|
+
pages.forEach((page) => {
|
|
755
|
+
const li = this._document.createElement("li");
|
|
756
|
+
li.className = "page-item";
|
|
757
|
+
if (page === "\u2026") {
|
|
758
|
+
li.dataset.datatablePageEllipsis = "";
|
|
759
|
+
li.innerHTML = '<span class="page-link">\u2026</span>';
|
|
760
|
+
} else {
|
|
761
|
+
li.dataset.datatablePageNumber = String(page);
|
|
762
|
+
const button = this._document.createElement("button");
|
|
763
|
+
button.type = "button";
|
|
764
|
+
button.className = "page-link";
|
|
765
|
+
button.textContent = String(page);
|
|
766
|
+
button.setAttribute("aria-label", `Go to page ${page}`);
|
|
767
|
+
button.dataset.datatablePageGo = String(page);
|
|
768
|
+
li.appendChild(button);
|
|
524
769
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
770
|
+
fragment.appendChild(li);
|
|
771
|
+
});
|
|
772
|
+
template.parentNode.insertBefore(fragment, template);
|
|
773
|
+
}
|
|
774
|
+
template.parentNode.querySelectorAll("[data-datatable-page-number]").forEach((li) => {
|
|
775
|
+
const isActive = Number(li.dataset.datatablePageNumber) === this._currentPage;
|
|
776
|
+
li.classList.toggle("active", isActive);
|
|
777
|
+
if (isActive) {
|
|
778
|
+
li.setAttribute("aria-current", "page");
|
|
779
|
+
} else {
|
|
780
|
+
li.removeAttribute("aria-current");
|
|
532
781
|
}
|
|
533
|
-
fragment.appendChild(li);
|
|
534
782
|
});
|
|
535
|
-
template.parentNode.insertBefore(fragment, template);
|
|
536
783
|
}
|
|
537
|
-
|
|
538
784
|
_renderPageSize() {
|
|
539
785
|
const currentSize = String(this._pageSize);
|
|
540
786
|
const value = this._element.querySelector("[data-datatable-page-size-value]");
|
|
@@ -551,7 +797,6 @@ export default class DataTable {
|
|
|
551
797
|
option.setAttribute("aria-pressed", active ? "true" : "false");
|
|
552
798
|
});
|
|
553
799
|
}
|
|
554
|
-
|
|
555
800
|
_setPageItemDisabled(button, disabled) {
|
|
556
801
|
if (!button) {
|
|
557
802
|
return;
|
|
@@ -559,31 +804,24 @@ export default class DataTable {
|
|
|
559
804
|
button.disabled = disabled;
|
|
560
805
|
button.closest(".page-item")?.classList.toggle("disabled", disabled);
|
|
561
806
|
}
|
|
562
|
-
|
|
563
807
|
_pageWindow(current, total) {
|
|
564
808
|
if (total <= 7) {
|
|
565
809
|
return Array.from({ length: total }, (_, index) => index + 1);
|
|
566
810
|
}
|
|
567
|
-
const pages = new Set([1, total, current - 1, current, current + 1]);
|
|
568
|
-
const sorted = Array.from(pages)
|
|
569
|
-
.filter((page) => page >= 1 && page <= total)
|
|
570
|
-
.sort((a, b) => a - b);
|
|
811
|
+
const pages = /* @__PURE__ */ new Set([1, total, current - 1, current, current + 1]);
|
|
812
|
+
const sorted = Array.from(pages).filter((page) => page >= 1 && page <= total).sort((a, b) => a - b);
|
|
571
813
|
const withEllipsis = [];
|
|
572
814
|
sorted.forEach((page, index) => {
|
|
573
815
|
if (index > 0 && page - sorted[index - 1] > 1) {
|
|
574
|
-
withEllipsis.push("
|
|
816
|
+
withEllipsis.push("\u2026");
|
|
575
817
|
}
|
|
576
818
|
withEllipsis.push(page);
|
|
577
819
|
});
|
|
578
820
|
return withEllipsis;
|
|
579
821
|
}
|
|
580
|
-
|
|
581
822
|
_isColumnFixed(key) {
|
|
582
|
-
return this._element
|
|
583
|
-
.querySelector(`th[data-datatable-column="${key}"]`)
|
|
584
|
-
?.hasAttribute("data-datatable-column-fixed") || false;
|
|
823
|
+
return this._element.querySelector(`th[data-datatable-column="${key}"]`)?.hasAttribute("data-datatable-column-fixed") || false;
|
|
585
824
|
}
|
|
586
|
-
|
|
587
825
|
_setColumnVisible(key, visible, { syncViewToggle = true } = {}) {
|
|
588
826
|
if (!visible && this._isColumnFixed(key)) {
|
|
589
827
|
return;
|
|
@@ -598,7 +836,6 @@ export default class DataTable {
|
|
|
598
836
|
});
|
|
599
837
|
}
|
|
600
838
|
}
|
|
601
|
-
|
|
602
839
|
_setSort(key, direction) {
|
|
603
840
|
const validDirection = direction === "asc" || direction === "desc" ? direction : null;
|
|
604
841
|
this._sortKey = validDirection ? key : null;
|
|
@@ -606,16 +843,17 @@ export default class DataTable {
|
|
|
606
843
|
this._render();
|
|
607
844
|
this._trigger("sort", { key: this._sortKey, direction: this._sortDirection });
|
|
608
845
|
}
|
|
609
|
-
|
|
610
846
|
_handleSortAction(event) {
|
|
611
847
|
const trigger = event.target.closest("[data-datatable-sort-action]");
|
|
612
848
|
if (!trigger) {
|
|
613
849
|
return;
|
|
614
850
|
}
|
|
615
851
|
const menu = trigger.closest(".dropdown-menu");
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
852
|
+
const sort = menu?.closest(".datatable-sort") || this._reparentedSortMenus.get(menu)?.owner;
|
|
853
|
+
if (!sort || !this._element.contains(sort)) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const key = sort.querySelector("[data-datatable-sort-key]")?.dataset.datatableSortKey;
|
|
619
857
|
if (!key) {
|
|
620
858
|
return;
|
|
621
859
|
}
|
|
@@ -626,7 +864,6 @@ export default class DataTable {
|
|
|
626
864
|
}
|
|
627
865
|
this._setSort(key, action);
|
|
628
866
|
}
|
|
629
|
-
|
|
630
867
|
_handleFacetClick(event) {
|
|
631
868
|
const option = event.target.closest("[data-datatable-facet-option]");
|
|
632
869
|
const clear = event.target.closest("[data-datatable-facet-clear]");
|
|
@@ -638,7 +875,7 @@ export default class DataTable {
|
|
|
638
875
|
if (!key) {
|
|
639
876
|
return;
|
|
640
877
|
}
|
|
641
|
-
const selected = this._facetSelections.get(key) || new Set();
|
|
878
|
+
const selected = this._facetSelections.get(key) || /* @__PURE__ */ new Set();
|
|
642
879
|
if (clear) {
|
|
643
880
|
selected.clear();
|
|
644
881
|
} else {
|
|
@@ -657,7 +894,6 @@ export default class DataTable {
|
|
|
657
894
|
this._render();
|
|
658
895
|
this._trigger("filter");
|
|
659
896
|
}
|
|
660
|
-
|
|
661
897
|
_handleFacetSearch(event) {
|
|
662
898
|
const search = event.target.closest("[data-datatable-facet-search]");
|
|
663
899
|
if (!search) {
|
|
@@ -670,7 +906,6 @@ export default class DataTable {
|
|
|
670
906
|
option.closest("li")?.classList.toggle("datatable-facet-search-hidden", query.length > 0 && !label.includes(query));
|
|
671
907
|
});
|
|
672
908
|
}
|
|
673
|
-
|
|
674
909
|
// Bootstrap's own dropdown keyboard navigation moves focus between
|
|
675
910
|
// .dropdown-item siblings, but it deliberately leaves focus alone while an
|
|
676
911
|
// <input> has it (arrow keys inside a text field move the caret, not a
|
|
@@ -694,7 +929,6 @@ export default class DataTable {
|
|
|
694
929
|
firstVisible.focus();
|
|
695
930
|
}
|
|
696
931
|
}
|
|
697
|
-
|
|
698
932
|
_handleFilterOptionClick(event) {
|
|
699
933
|
const option = event.target.closest("[data-datatable-filter-option]");
|
|
700
934
|
if (!option) {
|
|
@@ -706,7 +940,7 @@ export default class DataTable {
|
|
|
706
940
|
return;
|
|
707
941
|
}
|
|
708
942
|
this._visibleFacets.set(key, true);
|
|
709
|
-
const selected = this._facetSelections.get(key) || new Set();
|
|
943
|
+
const selected = this._facetSelections.get(key) || /* @__PURE__ */ new Set();
|
|
710
944
|
if (selected.has(value)) {
|
|
711
945
|
selected.delete(value);
|
|
712
946
|
} else {
|
|
@@ -720,18 +954,14 @@ export default class DataTable {
|
|
|
720
954
|
this._render();
|
|
721
955
|
this._trigger("filter");
|
|
722
956
|
}
|
|
723
|
-
|
|
724
957
|
_showFilterPickerPanel(key = null) {
|
|
725
958
|
this._element.querySelectorAll("[data-datatable-filter-panel]").forEach((panel) => {
|
|
726
959
|
const isFacetList = panel.dataset.datatableFilterPanel === "facets";
|
|
727
960
|
const isRequestedOptions = panel.dataset.datatableFilterPanelKey === key;
|
|
728
961
|
panel.hidden = key ? !isRequestedOptions : !isFacetList;
|
|
729
962
|
});
|
|
730
|
-
this._element
|
|
731
|
-
.querySelector(".datatable-search-filter-menu")
|
|
732
|
-
?.setAttribute("data-datatable-filter-menu-panel", key ? "options" : "facets");
|
|
963
|
+
this._element.querySelector(".datatable-search-filter-menu")?.setAttribute("data-datatable-filter-menu-panel", key ? "options" : "facets");
|
|
733
964
|
}
|
|
734
|
-
|
|
735
965
|
_handleFilterPickerClick(event) {
|
|
736
966
|
const group = event.target.closest("[data-datatable-filter-group]");
|
|
737
967
|
if (group) {
|
|
@@ -746,7 +976,6 @@ export default class DataTable {
|
|
|
746
976
|
this._showFilterPickerPanel();
|
|
747
977
|
}
|
|
748
978
|
}
|
|
749
|
-
|
|
750
979
|
_handleFilterOptionSearch(event) {
|
|
751
980
|
const search = event.target.closest("[data-datatable-filter-option-search]");
|
|
752
981
|
if (!search) {
|
|
@@ -759,7 +988,6 @@ export default class DataTable {
|
|
|
759
988
|
option.closest("li")?.classList.toggle("datatable-filter-option-search-hidden", query.length > 0 && !label.includes(query));
|
|
760
989
|
});
|
|
761
990
|
}
|
|
762
|
-
|
|
763
991
|
// Same bridge as _handleFacetSearchKeydown, for the filter-picker's
|
|
764
992
|
// drill-down option search box.
|
|
765
993
|
_handleFilterOptionSearchKeydown(event) {
|
|
@@ -782,7 +1010,6 @@ export default class DataTable {
|
|
|
782
1010
|
firstVisible.focus();
|
|
783
1011
|
}
|
|
784
1012
|
}
|
|
785
|
-
|
|
786
1013
|
_handleFilterToggleClick(event) {
|
|
787
1014
|
const toggle = event.target.closest("[data-datatable-filter-toggle]");
|
|
788
1015
|
if (!toggle) {
|
|
@@ -801,7 +1028,6 @@ export default class DataTable {
|
|
|
801
1028
|
}
|
|
802
1029
|
this._render();
|
|
803
1030
|
}
|
|
804
|
-
|
|
805
1031
|
_handleColumnToggleClick(event) {
|
|
806
1032
|
const toggle = event.target.closest("[data-datatable-column-toggle]");
|
|
807
1033
|
if (!toggle) {
|
|
@@ -811,7 +1037,6 @@ export default class DataTable {
|
|
|
811
1037
|
const visible = toggle.getAttribute("aria-pressed") === "true";
|
|
812
1038
|
this._setColumnVisible(key, visible, { syncViewToggle: false });
|
|
813
1039
|
}
|
|
814
|
-
|
|
815
1040
|
_handleReset() {
|
|
816
1041
|
const search = this._element.querySelector("[data-datatable-search]");
|
|
817
1042
|
if (search) {
|
|
@@ -828,7 +1053,6 @@ export default class DataTable {
|
|
|
828
1053
|
this._render();
|
|
829
1054
|
this._trigger("filter");
|
|
830
1055
|
}
|
|
831
|
-
|
|
832
1056
|
_handleSelectAll(event) {
|
|
833
1057
|
const checked = event.target.checked;
|
|
834
1058
|
const filtered = this._visibleRows();
|
|
@@ -844,7 +1068,6 @@ export default class DataTable {
|
|
|
844
1068
|
this._render();
|
|
845
1069
|
this._trigger("select");
|
|
846
1070
|
}
|
|
847
|
-
|
|
848
1071
|
_handleSelectRow(event) {
|
|
849
1072
|
const checkbox = event.target.closest("[data-datatable-select-row]");
|
|
850
1073
|
if (!checkbox) {
|
|
@@ -864,7 +1087,6 @@ export default class DataTable {
|
|
|
864
1087
|
this._render();
|
|
865
1088
|
this._trigger("select");
|
|
866
1089
|
}
|
|
867
|
-
|
|
868
1090
|
_handleBulkClick(event) {
|
|
869
1091
|
if (event.target.closest("[data-datatable-bulk-clear]")) {
|
|
870
1092
|
this._clearSelection();
|
|
@@ -887,14 +1109,12 @@ export default class DataTable {
|
|
|
887
1109
|
this._trigger(`bulk-${action}`, { ids: this.getSelectedIds() });
|
|
888
1110
|
}
|
|
889
1111
|
}
|
|
890
|
-
|
|
891
1112
|
_clearSelection() {
|
|
892
1113
|
this._hideBulkTooltips({ blur: true });
|
|
893
1114
|
this._selectedIds.clear();
|
|
894
1115
|
this._render();
|
|
895
1116
|
this._trigger("select");
|
|
896
1117
|
}
|
|
897
|
-
|
|
898
1118
|
_applyBulkUpdate(key, value, trigger) {
|
|
899
1119
|
const icon = trigger.querySelector("svg")?.cloneNode(true);
|
|
900
1120
|
const label = trigger.querySelector("span")?.textContent?.trim() ?? value;
|
|
@@ -927,7 +1147,6 @@ export default class DataTable {
|
|
|
927
1147
|
this._render();
|
|
928
1148
|
this._trigger("bulk-update", { key, value, ids: this.getSelectedIds() });
|
|
929
1149
|
}
|
|
930
|
-
|
|
931
1150
|
_applyBulkDelete() {
|
|
932
1151
|
const ids = this.getSelectedIds();
|
|
933
1152
|
ids.forEach((id) => {
|
|
@@ -942,7 +1161,6 @@ export default class DataTable {
|
|
|
942
1161
|
this._render();
|
|
943
1162
|
this._trigger("bulk-delete", { ids });
|
|
944
1163
|
}
|
|
945
|
-
|
|
946
1164
|
_handlePageClick(event) {
|
|
947
1165
|
const goButton = event.target.closest("[data-datatable-page-go]");
|
|
948
1166
|
if (goButton) {
|
|
@@ -970,7 +1188,6 @@ export default class DataTable {
|
|
|
970
1188
|
this._render();
|
|
971
1189
|
}
|
|
972
1190
|
}
|
|
973
|
-
|
|
974
1191
|
_setPageSize(value) {
|
|
975
1192
|
const nextPageSize = Number(value);
|
|
976
1193
|
if (!Number.isFinite(nextPageSize) || nextPageSize <= 0) {
|
|
@@ -981,7 +1198,6 @@ export default class DataTable {
|
|
|
981
1198
|
this._currentPage = 1;
|
|
982
1199
|
this._render();
|
|
983
1200
|
}
|
|
984
|
-
|
|
985
1201
|
_bindEvents() {
|
|
986
1202
|
const search = this._element.querySelector("[data-datatable-search]");
|
|
987
1203
|
this._listen(search, "input", (event) => {
|
|
@@ -1001,9 +1217,7 @@ export default class DataTable {
|
|
|
1001
1217
|
Dropdown.getOrCreateInstance(trigger).hide();
|
|
1002
1218
|
}
|
|
1003
1219
|
});
|
|
1004
|
-
|
|
1005
1220
|
this._listen(this._element, "click", (event) => {
|
|
1006
|
-
this._handleSortAction(event);
|
|
1007
1221
|
this._handleFilterPickerClick(event);
|
|
1008
1222
|
this._handleFilterOptionClick(event);
|
|
1009
1223
|
this._handleFilterToggleClick(event);
|
|
@@ -1018,63 +1232,51 @@ export default class DataTable {
|
|
|
1018
1232
|
this._handleSelectRow(event);
|
|
1019
1233
|
}
|
|
1020
1234
|
});
|
|
1021
|
-
|
|
1022
|
-
// Bound to the document, not this._element: a checkbox click does not
|
|
1023
|
-
// reliably move focus into the table (WebKit never focuses form
|
|
1024
|
-
// controls on click, and even where it does the bulk-actions bar itself
|
|
1025
|
-
// sits outside the table's DOM flow), so a listener scoped to the table
|
|
1026
|
-
// would miss Escape whenever focus is elsewhere on the page.
|
|
1235
|
+
this._listen(this._document, "click", (event) => this._handleSortAction(event));
|
|
1027
1236
|
this._listen(this._document, "keydown", (event) => {
|
|
1028
1237
|
if (event.key === "Escape" && this._selectedIds.size > 0) {
|
|
1029
1238
|
this._clearSelection();
|
|
1030
1239
|
}
|
|
1031
1240
|
});
|
|
1032
|
-
|
|
1033
|
-
// A hover-triggered tooltip does not hide itself when a click opens the
|
|
1034
|
-
// same button's dropdown (Bootstrap Tooltip only listens for
|
|
1035
|
-
// mouseleave/blur), so it would otherwise sit on top of the open menu.
|
|
1036
|
-
// hide() alone is not enough: opening the dropdown shifts layout under
|
|
1037
|
-
// the still-hovered cursor, which makes the browser refire mouseenter
|
|
1038
|
-
// and the tooltip re-show a moment later. disable()/enable() -- not
|
|
1039
|
-
// hide() -- is Bootstrap's own way to suppress that: it makes show() a
|
|
1040
|
-
// no-op for as long as the dropdown stays open, however it gets
|
|
1041
|
-
// re-triggered.
|
|
1042
1241
|
this._listen(this._element, "show.bs.dropdown", (event) => {
|
|
1242
|
+
this._reparentRowActionMenu(event.target);
|
|
1243
|
+
this._reparentSortMenu(event.target);
|
|
1043
1244
|
const tooltip = this._bootstrap("Tooltip")?.getInstance(event.target);
|
|
1044
1245
|
tooltip?.hide();
|
|
1045
1246
|
tooltip?.disable();
|
|
1046
1247
|
});
|
|
1047
1248
|
this._listen(this._element, "hidden.bs.dropdown", (event) => {
|
|
1249
|
+
this._restoreRowActionMenuForTrigger(event.target);
|
|
1250
|
+
this._restoreSortMenuForTrigger(event.target);
|
|
1048
1251
|
this._bootstrap("Tooltip")?.getInstance(event.target)?.enable();
|
|
1049
1252
|
if (event.target.matches("[data-datatable-filter-menu-trigger]")) {
|
|
1050
1253
|
this._showFilterPickerPanel();
|
|
1051
1254
|
}
|
|
1052
1255
|
});
|
|
1053
|
-
|
|
1054
1256
|
this._element.querySelectorAll("[data-datatable-filter-option-search]").forEach((searchInput) => {
|
|
1055
1257
|
this._listen(searchInput, "input", (event) => this._handleFilterOptionSearch(event));
|
|
1056
1258
|
this._listen(searchInput, "keydown", (event) => this._handleFilterOptionSearchKeydown(event));
|
|
1057
1259
|
});
|
|
1058
|
-
|
|
1059
1260
|
this._element.querySelectorAll("[data-datatable-facet-search]").forEach((searchInput) => {
|
|
1060
1261
|
this._listen(searchInput, "input", (event) => this._handleFacetSearch(event));
|
|
1061
1262
|
this._listen(searchInput, "keydown", (event) => this._handleFacetSearchKeydown(event));
|
|
1062
1263
|
});
|
|
1063
|
-
|
|
1064
1264
|
const resetButton = this._element.querySelector("[data-datatable-reset]");
|
|
1065
1265
|
this._listen(resetButton, "click", () => this._handleReset());
|
|
1066
|
-
|
|
1067
1266
|
this._element.querySelectorAll("[data-datatable-select-all]").forEach((selectAll) => {
|
|
1068
1267
|
this._listen(selectAll, "change", (event) => this._handleSelectAll(event));
|
|
1069
1268
|
});
|
|
1070
|
-
|
|
1071
1269
|
const pageSizeSelect = this._element.querySelector("[data-datatable-page-size-select]");
|
|
1072
1270
|
this._listen(pageSizeSelect, "change", (event) => this._setPageSize(event.target.value));
|
|
1073
|
-
|
|
1074
1271
|
this._element.querySelectorAll("[data-datatable-page-size-option]").forEach((option) => {
|
|
1075
1272
|
this._listen(option, "click", () => {
|
|
1076
1273
|
this._setPageSize(option.dataset.datatablePageSizeOption);
|
|
1077
1274
|
});
|
|
1078
1275
|
});
|
|
1079
1276
|
}
|
|
1080
|
-
}
|
|
1277
|
+
};
|
|
1278
|
+
__name(_DataTable, "DataTable");
|
|
1279
|
+
var DataTable = _DataTable;
|
|
1280
|
+
export {
|
|
1281
|
+
DataTable as default
|
|
1282
|
+
};
|