@toolbox-web/grid 0.2.6 → 0.2.8
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/all.d.ts +434 -61
- package/all.js +844 -541
- package/all.js.map +1 -1
- package/index-YjW60MHD.js +3235 -0
- package/index-YjW60MHD.js.map +1 -0
- package/index.d.ts +210 -6
- package/index.js +25 -3194
- package/index.js.map +1 -1
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.js +183 -148
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js +116 -82
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +139 -81
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +17 -17
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +369 -337
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/reorder/index.js +264 -91
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/index.js +180 -169
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +21 -21
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +12 -12
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +1 -1
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +1 -1
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js +1 -1
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/multi-sort.umd.js +1 -1
- package/umd/plugins/multi-sort.umd.js.map +1 -1
- package/umd/plugins/pivot.umd.js +1 -1
- package/umd/plugins/pivot.umd.js.map +1 -1
- package/umd/plugins/reorder.umd.js +1 -1
- package/umd/plugins/reorder.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
function
|
|
2
|
-
const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan:
|
|
3
|
-
let
|
|
4
|
-
|
|
5
|
-
let
|
|
6
|
-
return
|
|
7
|
-
start:
|
|
8
|
-
end:
|
|
9
|
-
offsetY:
|
|
1
|
+
function P(p) {
|
|
2
|
+
const { totalRows: e, viewportHeight: t, scrollTop: r, rowHeight: l, overscan: n } = p, o = Math.ceil(t / l);
|
|
3
|
+
let i = Math.floor(r / l) - n;
|
|
4
|
+
i < 0 && (i = 0);
|
|
5
|
+
let a = i + o + n * 2;
|
|
6
|
+
return a > e && (a = e), a === e && i > 0 && (i = Math.max(0, a - o - n * 2)), {
|
|
7
|
+
start: i,
|
|
8
|
+
end: a,
|
|
9
|
+
offsetY: i * l,
|
|
10
10
|
totalHeight: e * l
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
function
|
|
14
|
-
return
|
|
13
|
+
function M(p, e) {
|
|
14
|
+
return p <= e;
|
|
15
15
|
}
|
|
16
|
-
const
|
|
16
|
+
const V = {
|
|
17
17
|
expand: "▶",
|
|
18
18
|
collapse: "▼",
|
|
19
19
|
sortAsc: "▲",
|
|
@@ -23,7 +23,7 @@ const _ = {
|
|
|
23
23
|
dragHandle: "⋮⋮",
|
|
24
24
|
toolPanel: "☰"
|
|
25
25
|
};
|
|
26
|
-
class
|
|
26
|
+
class _ {
|
|
27
27
|
/** Plugin version - override in subclass if needed */
|
|
28
28
|
version = "1.0.0";
|
|
29
29
|
/** CSS styles to inject into the grid's shadow DOM */
|
|
@@ -148,7 +148,7 @@ class V {
|
|
|
148
148
|
*/
|
|
149
149
|
get gridIcons() {
|
|
150
150
|
const e = this.grid?.gridConfig?.icons ?? {};
|
|
151
|
-
return { ...
|
|
151
|
+
return { ...V, ...e };
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
154
|
* Resolve an icon value to string or HTMLElement.
|
|
@@ -179,28 +179,28 @@ class V {
|
|
|
179
179
|
}
|
|
180
180
|
// #endregion
|
|
181
181
|
}
|
|
182
|
-
function q(
|
|
183
|
-
const r =
|
|
182
|
+
function q(p, e, t = !1) {
|
|
183
|
+
const r = p[e.field];
|
|
184
184
|
if (e.operator === "blank")
|
|
185
185
|
return r == null || r === "";
|
|
186
186
|
if (e.operator === "notBlank")
|
|
187
187
|
return r != null && r !== "";
|
|
188
188
|
if (r == null) return !1;
|
|
189
|
-
const l = String(r),
|
|
189
|
+
const l = String(r), n = t ? l : l.toLowerCase(), o = t ? String(e.value) : String(e.value).toLowerCase();
|
|
190
190
|
switch (e.operator) {
|
|
191
191
|
// Text operators
|
|
192
192
|
case "contains":
|
|
193
|
-
return
|
|
193
|
+
return n.includes(o);
|
|
194
194
|
case "notContains":
|
|
195
|
-
return !
|
|
195
|
+
return !n.includes(o);
|
|
196
196
|
case "equals":
|
|
197
|
-
return
|
|
197
|
+
return n === o;
|
|
198
198
|
case "notEquals":
|
|
199
|
-
return
|
|
199
|
+
return n !== o;
|
|
200
200
|
case "startsWith":
|
|
201
|
-
return
|
|
201
|
+
return n.startsWith(o);
|
|
202
202
|
case "endsWith":
|
|
203
|
-
return
|
|
203
|
+
return n.endsWith(o);
|
|
204
204
|
// Number/Date operators (use raw numeric values)
|
|
205
205
|
case "lessThan":
|
|
206
206
|
return Number(r) < Number(e.value);
|
|
@@ -221,12 +221,12 @@ function q(f, e, t = !1) {
|
|
|
221
221
|
return !0;
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
-
function B(
|
|
225
|
-
return e.length ?
|
|
224
|
+
function B(p, e, t = !1) {
|
|
225
|
+
return e.length ? p.filter((r) => e.every((l) => q(r, l, t))) : p;
|
|
226
226
|
}
|
|
227
|
-
function z(
|
|
227
|
+
function z(p) {
|
|
228
228
|
return JSON.stringify(
|
|
229
|
-
|
|
229
|
+
p.map((e) => ({
|
|
230
230
|
field: e.field,
|
|
231
231
|
operator: e.operator,
|
|
232
232
|
value: e.value,
|
|
@@ -234,16 +234,16 @@ function z(f) {
|
|
|
234
234
|
}))
|
|
235
235
|
);
|
|
236
236
|
}
|
|
237
|
-
function
|
|
237
|
+
function N(p, e) {
|
|
238
238
|
const t = /* @__PURE__ */ new Set();
|
|
239
|
-
for (const r of
|
|
239
|
+
for (const r of p) {
|
|
240
240
|
const l = r[e];
|
|
241
241
|
l != null && t.add(l);
|
|
242
242
|
}
|
|
243
243
|
return [...t].sort((r, l) => typeof r == "number" && typeof l == "number" ? r - l : String(r).localeCompare(String(l)));
|
|
244
244
|
}
|
|
245
|
-
const
|
|
246
|
-
class x extends
|
|
245
|
+
const $ = '.header-cell.filtered:before{content:"";position:absolute;top:4px;right:4px;width:6px;height:6px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}.tbw-filter-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:4px;opacity:.4;transition:opacity .15s;color:inherit;vertical-align:middle}.tbw-filter-btn:hover,.tbw-filter-btn.active{opacity:1}.tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}', K = ".tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:12px;z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, 13px)}.tbw-filter-search{margin-bottom:8px}.tbw-filter-search-input{width:100%;padding:6px 10px;background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, 4px);font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:4px 2px;margin-bottom:8px;border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:12px;padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:8px;max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer;border-radius:3px}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:8px 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:6px 12px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:6px 12px;background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}";
|
|
246
|
+
class x extends _ {
|
|
247
247
|
name = "filtering";
|
|
248
248
|
version = "1.0.0";
|
|
249
249
|
get defaultConfig() {
|
|
@@ -283,6 +283,8 @@ class x extends V {
|
|
|
283
283
|
processRows(e) {
|
|
284
284
|
const t = [...this.filters.values()];
|
|
285
285
|
if (!t.length) return [...e];
|
|
286
|
+
if (this.config.filterHandler)
|
|
287
|
+
return this.cachedResult ? this.cachedResult : [...e];
|
|
286
288
|
const r = z(t);
|
|
287
289
|
if (this.cacheKey === r && this.cachedResult)
|
|
288
290
|
return this.cachedResult;
|
|
@@ -295,14 +297,19 @@ class x extends V {
|
|
|
295
297
|
e.querySelectorAll('[part~="header-cell"]').forEach((r) => {
|
|
296
298
|
const l = r.getAttribute("data-col");
|
|
297
299
|
if (l === null) return;
|
|
298
|
-
const
|
|
299
|
-
if (!
|
|
300
|
-
const
|
|
301
|
-
if (!
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
300
|
+
const n = this.visibleColumns[parseInt(l, 10)];
|
|
301
|
+
if (!n || n.filterable === !1) return;
|
|
302
|
+
const o = n.field;
|
|
303
|
+
if (!o) return;
|
|
304
|
+
const i = this.filters.has(o);
|
|
305
|
+
let a = r.querySelector(".tbw-filter-btn");
|
|
306
|
+
if (a) {
|
|
307
|
+
a.classList.toggle("active", i), r.classList.toggle("filtered", i);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
a = document.createElement("button"), a.className = "tbw-filter-btn", a.setAttribute("aria-label", `Filter ${n.header ?? o}`), a.innerHTML = '<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>', i && (a.classList.add("active"), r.classList.add("filtered")), a.addEventListener("click", (f) => {
|
|
311
|
+
f.stopPropagation(), this.toggleFilterPanel(o, n, a);
|
|
312
|
+
}), r.appendChild(a);
|
|
306
313
|
});
|
|
307
314
|
}
|
|
308
315
|
// #endregion
|
|
@@ -312,7 +319,7 @@ class x extends V {
|
|
|
312
319
|
* Pass null to remove the filter.
|
|
313
320
|
*/
|
|
314
321
|
setFilter(e, t) {
|
|
315
|
-
t === null ? this.filters.delete(e) : this.filters.set(e, { ...t, field: e }), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
|
|
322
|
+
t === null ? (this.filters.delete(e), this.excludedValues.delete(e)) : (this.filters.set(e, { ...t, field: e }), t.type === "set" && t.operator === "notIn" && Array.isArray(t.value) ? this.excludedValues.set(e, new Set(t.value)) : t.type === "set" && this.excludedValues.delete(e)), this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
|
|
316
323
|
filters: [...this.filters.values()],
|
|
317
324
|
filteredRowCount: 0
|
|
318
325
|
// Will be accurate after processRows
|
|
@@ -340,9 +347,9 @@ class x extends V {
|
|
|
340
347
|
* Set filters from an array (replaces all existing filters).
|
|
341
348
|
*/
|
|
342
349
|
setFilterModel(e) {
|
|
343
|
-
this.filters.clear();
|
|
350
|
+
this.filters.clear(), this.excludedValues.clear();
|
|
344
351
|
for (const t of e)
|
|
345
|
-
this.filters.set(t.field, t);
|
|
352
|
+
this.filters.set(t.field, t), t.type === "set" && t.operator === "notIn" && Array.isArray(t.value) && this.excludedValues.set(t.field, new Set(t.value));
|
|
346
353
|
this.cachedResult = null, this.cacheKey = null, this.emit("filter-change", {
|
|
347
354
|
filters: [...this.filters.values()],
|
|
348
355
|
filteredRowCount: 0
|
|
@@ -352,19 +359,13 @@ class x extends V {
|
|
|
352
359
|
* Clear all filters.
|
|
353
360
|
*/
|
|
354
361
|
clearAllFilters() {
|
|
355
|
-
this.filters.clear(), this.excludedValues.clear(), this.searchText.clear(), this.
|
|
356
|
-
filters: [],
|
|
357
|
-
filteredRowCount: this.rows.length
|
|
358
|
-
}), this.requestRender();
|
|
362
|
+
this.filters.clear(), this.excludedValues.clear(), this.searchText.clear(), this.applyFiltersInternal();
|
|
359
363
|
}
|
|
360
364
|
/**
|
|
361
365
|
* Clear filter for a specific field.
|
|
362
366
|
*/
|
|
363
367
|
clearFieldFilter(e) {
|
|
364
|
-
this.filters.delete(e), this.excludedValues.delete(e), this.searchText.delete(e), this.
|
|
365
|
-
filters: [...this.filters.values()],
|
|
366
|
-
filteredRowCount: 0
|
|
367
|
-
}), this.requestRender();
|
|
368
|
+
this.filters.delete(e), this.excludedValues.delete(e), this.searchText.delete(e), this.applyFiltersInternal();
|
|
368
369
|
}
|
|
369
370
|
/**
|
|
370
371
|
* Check if a field has an active filter.
|
|
@@ -389,7 +390,7 @@ class x extends V {
|
|
|
389
390
|
* Uses sourceRows to include all values regardless of current filter.
|
|
390
391
|
*/
|
|
391
392
|
getUniqueValues(e) {
|
|
392
|
-
return
|
|
393
|
+
return N(this.sourceRows, e);
|
|
393
394
|
}
|
|
394
395
|
// #endregion
|
|
395
396
|
// #region Private Methods
|
|
@@ -403,7 +404,7 @@ class x extends V {
|
|
|
403
404
|
return;
|
|
404
405
|
}
|
|
405
406
|
const e = document.createElement("style");
|
|
406
|
-
e.id = "tbw-filter-panel-styles", e.textContent =
|
|
407
|
+
e.id = "tbw-filter-panel-styles", e.textContent = K, document.head.appendChild(e), this.globalStylesInjected = !0;
|
|
407
408
|
}
|
|
408
409
|
/**
|
|
409
410
|
* Toggle the filter panel for a field
|
|
@@ -415,33 +416,50 @@ class x extends V {
|
|
|
415
416
|
}
|
|
416
417
|
this.closeFilterPanel();
|
|
417
418
|
const l = document.createElement("div");
|
|
418
|
-
l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
419
|
+
if (l.className = "tbw-filter-panel", this.panelElement = l, this.openPanelField = e, this.config.valuesHandler) {
|
|
420
|
+
l.innerHTML = '<div class="tbw-filter-loading">Loading...</div>', document.body.appendChild(l), this.positionPanel(l, r), this.setupPanelCloseHandler(l, r), this.config.valuesHandler(e, t).then((o) => {
|
|
421
|
+
this.openPanelField !== e || !this.panelElement || (l.innerHTML = "", this.renderPanelContent(e, t, l, o));
|
|
422
|
+
});
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const n = N(this.sourceRows, e);
|
|
426
|
+
this.renderPanelContent(e, t, l, n), document.body.appendChild(l), this.positionPanel(l, r), this.setupPanelCloseHandler(l, r);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Render filter panel content with given values
|
|
430
|
+
*/
|
|
431
|
+
renderPanelContent(e, t, r, l) {
|
|
432
|
+
let n = this.excludedValues.get(e);
|
|
433
|
+
n || (n = /* @__PURE__ */ new Set(), this.excludedValues.set(e, n));
|
|
434
|
+
const o = this.searchText.get(e) ?? "", i = {
|
|
423
435
|
field: e,
|
|
424
436
|
column: t,
|
|
425
|
-
uniqueValues:
|
|
426
|
-
excludedValues:
|
|
427
|
-
searchText:
|
|
428
|
-
applySetFilter: (
|
|
429
|
-
this.applySetFilter(e,
|
|
437
|
+
uniqueValues: l,
|
|
438
|
+
excludedValues: n,
|
|
439
|
+
searchText: o,
|
|
440
|
+
applySetFilter: (f) => {
|
|
441
|
+
this.applySetFilter(e, f), this.closeFilterPanel();
|
|
430
442
|
},
|
|
431
|
-
applyTextFilter: (
|
|
432
|
-
this.applyTextFilter(e,
|
|
443
|
+
applyTextFilter: (f, g, C) => {
|
|
444
|
+
this.applyTextFilter(e, f, g, C), this.closeFilterPanel();
|
|
433
445
|
},
|
|
434
446
|
clearFilter: () => {
|
|
435
447
|
this.clearFieldFilter(e), this.closeFilterPanel();
|
|
436
448
|
},
|
|
437
449
|
closePanel: () => this.closeFilterPanel()
|
|
438
450
|
};
|
|
439
|
-
let
|
|
440
|
-
this.config.filterPanelRenderer && (this.config.filterPanelRenderer(
|
|
451
|
+
let a = !1;
|
|
452
|
+
this.config.filterPanelRenderer && (this.config.filterPanelRenderer(r, i), a = r.children.length > 0), a || this.renderDefaultFilterPanel(r, i, l, n);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Setup click-outside handler to close the panel
|
|
456
|
+
*/
|
|
457
|
+
setupPanelCloseHandler(e, t) {
|
|
458
|
+
this.panelAbortController = new AbortController(), setTimeout(() => {
|
|
441
459
|
document.addEventListener(
|
|
442
460
|
"click",
|
|
443
|
-
(
|
|
444
|
-
!
|
|
461
|
+
(r) => {
|
|
462
|
+
!e.contains(r.target) && r.target !== t && this.closeFilterPanel();
|
|
445
463
|
},
|
|
446
464
|
{ signal: this.panelAbortController?.signal }
|
|
447
465
|
);
|
|
@@ -467,112 +485,112 @@ class x extends V {
|
|
|
467
485
|
* Render the default filter panel content
|
|
468
486
|
*/
|
|
469
487
|
renderDefaultFilterPanel(e, t, r, l) {
|
|
470
|
-
const { field:
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
const s = [...
|
|
484
|
-
|
|
488
|
+
const { field: n } = t, o = document.createElement("div");
|
|
489
|
+
o.className = "tbw-filter-search";
|
|
490
|
+
const i = document.createElement("input");
|
|
491
|
+
i.type = "text", i.placeholder = "Search...", i.className = "tbw-filter-search-input", i.value = this.searchText.get(n) ?? "", o.appendChild(i), e.appendChild(o);
|
|
492
|
+
const a = document.createElement("div");
|
|
493
|
+
a.className = "tbw-filter-actions";
|
|
494
|
+
const f = document.createElement("label");
|
|
495
|
+
f.className = "tbw-filter-value-item", f.style.padding = "0", f.style.margin = "0";
|
|
496
|
+
const g = document.createElement("input");
|
|
497
|
+
g.type = "checkbox", g.className = "tbw-filter-checkbox";
|
|
498
|
+
const C = document.createElement("span");
|
|
499
|
+
C.textContent = "Select All", f.appendChild(g), f.appendChild(C), a.appendChild(f);
|
|
500
|
+
const k = () => {
|
|
501
|
+
const s = [...m.values()], u = s.every((d) => d), h = s.every((d) => !d);
|
|
502
|
+
g.checked = u, g.indeterminate = !u && !h;
|
|
485
503
|
};
|
|
486
|
-
|
|
487
|
-
const s =
|
|
488
|
-
for (const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}), e.appendChild(
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
const
|
|
495
|
-
|
|
504
|
+
g.addEventListener("change", () => {
|
|
505
|
+
const s = g.checked;
|
|
506
|
+
for (const u of m.keys())
|
|
507
|
+
m.set(u, s);
|
|
508
|
+
k(), F();
|
|
509
|
+
}), e.appendChild(a);
|
|
510
|
+
const v = document.createElement("div");
|
|
511
|
+
v.className = "tbw-filter-values";
|
|
512
|
+
const S = document.createElement("div");
|
|
513
|
+
S.className = "tbw-filter-values-spacer", v.appendChild(S);
|
|
496
514
|
const b = document.createElement("div");
|
|
497
|
-
b.className = "tbw-filter-values-content",
|
|
498
|
-
const
|
|
515
|
+
b.className = "tbw-filter-values-content", v.appendChild(b);
|
|
516
|
+
const m = /* @__PURE__ */ new Map();
|
|
499
517
|
r.forEach((s) => {
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
}),
|
|
518
|
+
const u = s == null ? "__null__" : String(s);
|
|
519
|
+
m.set(u, !l.has(s));
|
|
520
|
+
}), k();
|
|
503
521
|
let y = [];
|
|
504
|
-
const I = (s,
|
|
505
|
-
const
|
|
506
|
-
|
|
522
|
+
const I = (s, u) => {
|
|
523
|
+
const h = s == null ? "(Blank)" : String(s), d = s == null ? "__null__" : String(s), c = document.createElement("label");
|
|
524
|
+
c.className = "tbw-filter-value-item", c.style.position = "absolute", c.style.top = `${u * x.LIST_ITEM_HEIGHT}px`, c.style.left = "0", c.style.right = "0", c.style.height = `${x.LIST_ITEM_HEIGHT}px`, c.style.boxSizing = "border-box";
|
|
507
525
|
const w = document.createElement("input");
|
|
508
|
-
w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked =
|
|
509
|
-
|
|
526
|
+
w.type = "checkbox", w.className = "tbw-filter-checkbox", w.checked = m.get(d) ?? !0, w.dataset.value = d, w.addEventListener("change", () => {
|
|
527
|
+
m.set(d, w.checked), k();
|
|
510
528
|
});
|
|
511
|
-
const
|
|
512
|
-
return
|
|
529
|
+
const H = document.createElement("span");
|
|
530
|
+
return H.textContent = h, c.appendChild(w), c.appendChild(H), c;
|
|
513
531
|
}, F = () => {
|
|
514
|
-
const s = y.length,
|
|
515
|
-
if (
|
|
516
|
-
b.innerHTML = "", b.style.transform = "translateY(0px)", y.forEach((
|
|
517
|
-
b.appendChild(I(
|
|
532
|
+
const s = y.length, u = v.clientHeight, h = v.scrollTop;
|
|
533
|
+
if (S.style.height = `${s * x.LIST_ITEM_HEIGHT}px`, M(s, x.LIST_BYPASS_THRESHOLD / 3)) {
|
|
534
|
+
b.innerHTML = "", b.style.transform = "translateY(0px)", y.forEach((c, w) => {
|
|
535
|
+
b.appendChild(I(c, w));
|
|
518
536
|
});
|
|
519
537
|
return;
|
|
520
538
|
}
|
|
521
|
-
const
|
|
539
|
+
const d = P({
|
|
522
540
|
totalRows: s,
|
|
523
|
-
viewportHeight:
|
|
524
|
-
scrollTop:
|
|
541
|
+
viewportHeight: u,
|
|
542
|
+
scrollTop: h,
|
|
525
543
|
rowHeight: x.LIST_ITEM_HEIGHT,
|
|
526
544
|
overscan: x.LIST_OVERSCAN
|
|
527
545
|
});
|
|
528
|
-
b.style.transform = `translateY(${
|
|
529
|
-
for (let
|
|
530
|
-
b.appendChild(I(y[
|
|
546
|
+
b.style.transform = `translateY(${d.offsetY}px)`, b.innerHTML = "";
|
|
547
|
+
for (let c = d.start; c < d.end; c++)
|
|
548
|
+
b.appendChild(I(y[c], c - d.start));
|
|
531
549
|
}, L = (s) => {
|
|
532
|
-
const
|
|
533
|
-
if (y = r.filter((
|
|
534
|
-
const
|
|
535
|
-
return !s ||
|
|
550
|
+
const u = s.toLowerCase();
|
|
551
|
+
if (y = r.filter((h) => {
|
|
552
|
+
const d = h == null ? "(Blank)" : String(h);
|
|
553
|
+
return !s || d.toLowerCase().includes(u);
|
|
536
554
|
}), y.length === 0) {
|
|
537
|
-
|
|
538
|
-
const
|
|
539
|
-
|
|
555
|
+
S.style.height = "0px", b.innerHTML = "";
|
|
556
|
+
const h = document.createElement("div");
|
|
557
|
+
h.className = "tbw-filter-no-match", h.textContent = "No matching values", b.appendChild(h);
|
|
540
558
|
return;
|
|
541
559
|
}
|
|
542
560
|
F();
|
|
543
561
|
};
|
|
544
|
-
|
|
562
|
+
v.addEventListener(
|
|
545
563
|
"scroll",
|
|
546
564
|
() => {
|
|
547
565
|
y.length > 0 && F();
|
|
548
566
|
},
|
|
549
567
|
{ passive: !0 }
|
|
550
|
-
), L(
|
|
551
|
-
let
|
|
552
|
-
|
|
553
|
-
clearTimeout(
|
|
554
|
-
this.searchText.set(
|
|
568
|
+
), L(i.value), e.appendChild(v);
|
|
569
|
+
let A;
|
|
570
|
+
i.addEventListener("input", () => {
|
|
571
|
+
clearTimeout(A), A = setTimeout(() => {
|
|
572
|
+
this.searchText.set(n, i.value), L(i.value);
|
|
555
573
|
}, this.config.debounceMs ?? 150);
|
|
556
574
|
});
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
const
|
|
560
|
-
|
|
575
|
+
const T = document.createElement("div");
|
|
576
|
+
T.className = "tbw-filter-buttons";
|
|
577
|
+
const E = document.createElement("button");
|
|
578
|
+
E.className = "tbw-filter-apply-btn", E.textContent = "Apply", E.addEventListener("click", () => {
|
|
561
579
|
const s = [];
|
|
562
|
-
for (const [
|
|
563
|
-
if (!
|
|
564
|
-
if (
|
|
580
|
+
for (const [u, h] of m)
|
|
581
|
+
if (!h)
|
|
582
|
+
if (u === "__null__")
|
|
565
583
|
s.push(null);
|
|
566
584
|
else {
|
|
567
|
-
const
|
|
568
|
-
s.push(
|
|
585
|
+
const d = r.find((c) => String(c) === u);
|
|
586
|
+
s.push(d !== void 0 ? d : u);
|
|
569
587
|
}
|
|
570
588
|
t.applySetFilter(s);
|
|
571
|
-
}),
|
|
572
|
-
const
|
|
573
|
-
|
|
589
|
+
}), T.appendChild(E);
|
|
590
|
+
const R = document.createElement("button");
|
|
591
|
+
R.className = "tbw-filter-clear-btn", R.textContent = "Clear Filter", R.addEventListener("click", () => {
|
|
574
592
|
t.clearFilter();
|
|
575
|
-
}),
|
|
593
|
+
}), T.appendChild(R), e.appendChild(T);
|
|
576
594
|
}
|
|
577
595
|
/**
|
|
578
596
|
* Apply a set filter (exclude values)
|
|
@@ -583,10 +601,7 @@ class x extends V {
|
|
|
583
601
|
type: "set",
|
|
584
602
|
operator: "notIn",
|
|
585
603
|
value: t
|
|
586
|
-
}), this.
|
|
587
|
-
filters: [...this.filters.values()],
|
|
588
|
-
filteredRowCount: 0
|
|
589
|
-
}), this.requestRender();
|
|
604
|
+
}), this.applyFiltersInternal();
|
|
590
605
|
}
|
|
591
606
|
/**
|
|
592
607
|
* Apply a text filter
|
|
@@ -598,8 +613,28 @@ class x extends V {
|
|
|
598
613
|
operator: t,
|
|
599
614
|
value: r,
|
|
600
615
|
valueTo: l
|
|
601
|
-
}), this.
|
|
602
|
-
|
|
616
|
+
}), this.applyFiltersInternal();
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Internal method to apply filters (sync or async based on config)
|
|
620
|
+
*/
|
|
621
|
+
applyFiltersInternal() {
|
|
622
|
+
this.cachedResult = null, this.cacheKey = null;
|
|
623
|
+
const e = [...this.filters.values()];
|
|
624
|
+
if (this.config.filterHandler) {
|
|
625
|
+
const t = this.grid;
|
|
626
|
+
t.setAttribute("aria-busy", "true");
|
|
627
|
+
const r = this.config.filterHandler(e, this.sourceRows), l = (n) => {
|
|
628
|
+
t.removeAttribute("aria-busy"), this.cachedResult = n, this.grid.rows = n, this.emit("filter-change", {
|
|
629
|
+
filters: e,
|
|
630
|
+
filteredRowCount: n.length
|
|
631
|
+
}), this.requestRender();
|
|
632
|
+
};
|
|
633
|
+
r && typeof r.then == "function" ? r.then(l) : l(r);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
this.emit("filter-change", {
|
|
637
|
+
filters: e,
|
|
603
638
|
filteredRowCount: 0
|
|
604
639
|
}), this.requestRender();
|
|
605
640
|
}
|
|
@@ -639,7 +674,7 @@ class x extends V {
|
|
|
639
674
|
}
|
|
640
675
|
// #endregion
|
|
641
676
|
// #region Styles
|
|
642
|
-
styles =
|
|
677
|
+
styles = $;
|
|
643
678
|
// #endregion
|
|
644
679
|
}
|
|
645
680
|
export {
|