@livenetworks/ashlar 2.1.10 → 2.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/COMPONENTS.md +1 -1
- package/components/ln-data-store/README.md +19 -0
- package/components/ln-data-store/ln-data-store.js +1 -1
- package/components/ln-data-store/ln-data-store.schema.json +92 -84
- package/components/ln-data-store/src/data-store-model.js +17 -0
- package/components/ln-data-store/src/ln-data-store.js +77 -1
- package/components/ln-debug/ln-debug.js +1 -1
- package/components/ln-debug/src/generated-attributes.js +1 -3
- package/components/ln-table/README.md +69 -38
- package/components/ln-table/ln-table.js +1 -1
- package/components/ln-table/ln-table.schema.json +11 -35
- package/components/ln-table/src/ln-table.js +306 -414
- package/components/ln-table/src/table-model.js +31 -0
- package/ln-ashlar.html-data.json +7 -17
- package/package.json +1 -1
- package/theme/config/mixins/_ln-table.scss +18 -10
- package/theme/config/mixins/_table.scss +15 -2
- package/web-types.json +14 -28
|
@@ -1,60 +1,20 @@
|
|
|
1
|
-
import { cloneTemplateScoped,
|
|
2
|
-
import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, toggleSelectAll } from './table-model.js';
|
|
1
|
+
import { cloneTemplateScoped, dispatch, fill, fillTemplate, readValue, registerComponent, requestData, attrBool, attrStr, attrSpec, defineAttrs } from '../../ln-core';
|
|
2
|
+
import { calculateSelectionState, calculateVirtualWindow, calculatePoolSize, toggleRowSelection, toggleSelectAll } from './table-model.js';
|
|
3
3
|
|
|
4
4
|
(function () {
|
|
5
5
|
const DOM_SELECTOR = 'data-ln-table';
|
|
6
6
|
const DOM_ATTRIBUTE = 'lnTable';
|
|
7
|
-
const EMPTY_TEMPLATE = 'data-ln-table-empty';
|
|
8
7
|
// Tuning constant — duplicated in ln-data-table for component independence
|
|
9
8
|
const VIRTUAL_THRESHOLD = 200;
|
|
10
9
|
const BUFFER_ROWS = 15;
|
|
11
|
-
const WINDOW_DEFAULT = 1000;
|
|
12
|
-
const WINDOW_PAGE = 200;
|
|
13
|
-
const FETCH_DEBOUNCE = 120;
|
|
14
10
|
|
|
15
11
|
if (window[DOM_ATTRIBUTE] !== undefined) return;
|
|
16
12
|
|
|
17
13
|
// ─── Attribute Contract (SSOT) ──────────────────────────
|
|
18
|
-
function _applyWindow(inst, val) {
|
|
19
|
-
if (!inst || !inst.isDataDriven) return;
|
|
20
|
-
const present = inst.dom.hasAttribute('data-ln-table-window');
|
|
21
|
-
if (present && !inst._windowed) {
|
|
22
|
-
inst._enterWindowedMode();
|
|
23
|
-
inst._kickWindowInitial();
|
|
24
|
-
} else if (!present && inst._windowed) {
|
|
25
|
-
inst._exitWindowedMode();
|
|
26
|
-
} else if (present && inst._windowed) {
|
|
27
|
-
const v = parseInt(val, 10);
|
|
28
|
-
if (v > 0) inst._cache.configure({ windowSize: v });
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function _applyWindowPage(inst, val) {
|
|
33
|
-
if (!inst || !inst.isDataDriven || !inst._windowed || !inst._cache) return;
|
|
34
|
-
const v = parseInt(val, 10);
|
|
35
|
-
if (v > 0) inst._cache.configure({ pageSize: v });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function _applyWindowThreshold(inst, val) {
|
|
39
|
-
if (!inst || !inst.isDataDriven || !inst._windowed || !inst._cache) return;
|
|
40
|
-
const v = parseInt(val, 10);
|
|
41
|
-
if (v >= 0) inst._cache.configure({ threshold: v });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function _applyCount(inst, val) {
|
|
45
|
-
if (!inst || !inst.isDataDriven || !inst._windowed || !inst._cache) return;
|
|
46
|
-
const v = parseInt(val, 10);
|
|
47
|
-
if (v >= 0) inst._cache.setGrandTotal(v);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
14
|
const ATTRIBUTES = {
|
|
51
15
|
'data-ln-table': { prop: 'name', type: 'string', read: attrStr, fallback: '', description: 'Table instance name or identifier' },
|
|
52
16
|
'data-ln-table-source': { prop: 'source', type: 'string', read: attrStr, fallback: '', description: 'Source store or coordinator addressing' },
|
|
53
17
|
'data-ln-table-selectable': { prop: '_selectable', type: 'boolean', read: attrBool, description: 'Enables row selection check controls' },
|
|
54
|
-
'data-ln-table-window': { type: 'integer', fallback: WINDOW_DEFAULT, min: 10, effect: _applyWindow, description: 'Virtual scrolling window size in rows' },
|
|
55
|
-
'data-ln-table-window-page': { type: 'integer', fallback: WINDOW_PAGE, min: 5, effect: _applyWindowPage, description: 'Virtual scrolling slice page size' },
|
|
56
|
-
'data-ln-table-window-threshold': { type: 'integer', fallback: 50, min: 0, effect: _applyWindowThreshold, description: 'Scroll threshold margin in pixels to trigger page fetch' },
|
|
57
|
-
'data-ln-table-count': { type: 'integer', min: 0, effect: _applyCount, description: 'Total record count override for virtual scrollbar calculation' },
|
|
58
18
|
'data-ln-table-row': { type: 'marker', description: 'Table row template element' },
|
|
59
19
|
'data-ln-table-row-id': { type: 'string', description: 'Record identifier on row element' },
|
|
60
20
|
'data-ln-table-row-action': { type: 'trigger', description: 'Action trigger inside a table row' },
|
|
@@ -62,8 +22,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
62
22
|
'data-ln-table-col': { type: 'string', description: 'Column header identifier or field mapping' },
|
|
63
23
|
'data-ln-table-col-select': { type: 'trigger', description: 'Header select-all checkbox trigger' },
|
|
64
24
|
'data-ln-table-cell-attr': { type: 'string', description: 'Cell attribute template mapping' },
|
|
65
|
-
'data-ln-table-empty': { type: 'marker', description: 'Container for table empty state' },
|
|
66
|
-
'data-ln-table-loading': { type: 'marker', description: 'Loading indicator element in footer or template' },
|
|
67
25
|
'data-ln-table-select-all-label': { type: 'string', description: 'Accessibility label for select-all header trigger' }
|
|
68
26
|
};
|
|
69
27
|
|
|
@@ -74,17 +32,30 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
74
32
|
? new Intl.Collator(document.documentElement.lang || undefined, { sensitivity: 'base' })
|
|
75
33
|
: null;
|
|
76
34
|
|
|
77
|
-
function
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
35
|
+
function _setNumberValue(el, val) {
|
|
36
|
+
if (!el) return;
|
|
37
|
+
if (val == null || isNaN(val)) {
|
|
38
|
+
el.removeAttribute('data-ln-value');
|
|
39
|
+
if ('value' in el && el.tagName === 'DATA') el.value = '';
|
|
40
|
+
if (el.lnNumber) {
|
|
41
|
+
el.lnNumber.value = NaN;
|
|
42
|
+
} else {
|
|
43
|
+
el.textContent = '';
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const s = String(val);
|
|
48
|
+
el.setAttribute('data-ln-value', s);
|
|
49
|
+
if ('value' in el && el.tagName === 'DATA') el.value = s;
|
|
50
|
+
if (el.lnNumber) {
|
|
51
|
+
el.lnNumber.value = val;
|
|
52
|
+
} else {
|
|
53
|
+
el.textContent = s;
|
|
83
54
|
}
|
|
84
55
|
}
|
|
85
56
|
|
|
86
|
-
function _findScrollContainer(el) {
|
|
87
|
-
let p =
|
|
57
|
+
function _findScrollContainer(el, table) {
|
|
58
|
+
let p = table ? table.parentElement : el;
|
|
88
59
|
while (p && p !== document.body && p !== document.documentElement) {
|
|
89
60
|
const cs = getComputedStyle(p);
|
|
90
61
|
const oy = cs.overflowY;
|
|
@@ -144,6 +115,11 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
144
115
|
this._scrollHandler = null;
|
|
145
116
|
this._scrollContainer = null;
|
|
146
117
|
this._colgroup = null;
|
|
118
|
+
this._rowPool = null;
|
|
119
|
+
this._topSpacer = null;
|
|
120
|
+
this._topTd = null;
|
|
121
|
+
this._bottomSpacer = null;
|
|
122
|
+
this._bottomTd = null;
|
|
147
123
|
|
|
148
124
|
const self = this;
|
|
149
125
|
|
|
@@ -222,11 +198,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
222
198
|
this._lastFiltered = 0;
|
|
223
199
|
this._hasInitialSeed = false;
|
|
224
200
|
|
|
225
|
-
// Windowed (sliding-window) server-side virtualization — opt-in
|
|
226
|
-
this._windowed = false;
|
|
227
|
-
this._cache = null;
|
|
228
|
-
if (this.isDataDriven && dom.hasAttribute('data-ln-table-window')) this._enterWindowedMode();
|
|
229
|
-
|
|
230
201
|
// --- Event listeners ---
|
|
231
202
|
this._onSetData = function (e) {
|
|
232
203
|
const detail = e.detail || {};
|
|
@@ -238,18 +209,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
238
209
|
return;
|
|
239
210
|
}
|
|
240
211
|
|
|
241
|
-
if (self._windowed) {
|
|
242
|
-
// Only an accepted, authoritative page ends the refresh. A declined
|
|
243
|
-
// one means the source has not resolved the new query yet; a
|
|
244
|
-
// provisional one is the store's own answer, rendered while the
|
|
245
|
-
// server query is still out.
|
|
246
|
-
if (self._cache.ingest(detail) && !detail.provisional) {
|
|
247
|
-
dom.classList.remove('ln-table--loading');
|
|
248
|
-
dom.setAttribute('aria-busy', 'false');
|
|
249
|
-
}
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
212
|
self._data = newData;
|
|
254
213
|
self._lastTotal = newTotal;
|
|
255
214
|
self._lastFiltered = detail.filtered != null ? detail.filtered : self._data.length;
|
|
@@ -295,33 +254,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
295
254
|
};
|
|
296
255
|
dom.addEventListener('ln-table:set-loading', this._onSetLoading);
|
|
297
256
|
|
|
298
|
-
this._onPageFailed = function (e) {
|
|
299
|
-
if (!self._windowed || !self._cache) return;
|
|
300
|
-
self._cache.release(e.detail && e.detail.offset);
|
|
301
|
-
};
|
|
302
|
-
dom.addEventListener('ln-table:page-failed', this._onPageFailed);
|
|
303
|
-
|
|
304
|
-
this._onRequestRevalidate = function () {
|
|
305
|
-
if (!self._windowed || !self._cache) return;
|
|
306
|
-
self._cache.revalidate();
|
|
307
|
-
};
|
|
308
|
-
dom.addEventListener('ln-table:request-revalidate', this._onRequestRevalidate);
|
|
309
|
-
|
|
310
|
-
// The source owns the query, so a query change reaches the view as a
|
|
311
|
-
// restart order, not as query state — the window drops to page 0 and
|
|
312
|
-
// the composed query is resolved at serve time.
|
|
313
|
-
this._onRequestInvalidate = function (e) {
|
|
314
|
-
if (!self._windowed || !self._cache) return;
|
|
315
|
-
if (e && e.detail && e.detail.query) {
|
|
316
|
-
const q = e.detail.query;
|
|
317
|
-
if (q.search !== undefined) self.currentSearch = q.search;
|
|
318
|
-
if (q.filters !== undefined) self.currentFilters = q.filters;
|
|
319
|
-
if (q.sort !== undefined) self.currentSort = q.sort;
|
|
320
|
-
}
|
|
321
|
-
self._requestData();
|
|
322
|
-
};
|
|
323
|
-
dom.addEventListener('ln-table:request-invalidate', this._onRequestInvalidate);
|
|
324
|
-
|
|
325
257
|
// --- Sort ---
|
|
326
258
|
this._onSort = function (e) {
|
|
327
259
|
if (!e.detail || e.detail.field == null) return;
|
|
@@ -331,11 +263,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
331
263
|
};
|
|
332
264
|
dom.addEventListener('ln-sort:change', this._onSort);
|
|
333
265
|
|
|
334
|
-
// D4 — windowed tables cannot select rows they have never fetched
|
|
335
|
-
if (this._windowed && this._selectable && this._selectAllCheckbox) {
|
|
336
|
-
this._selectAllCheckbox.classList.add('hidden');
|
|
337
|
-
}
|
|
338
|
-
|
|
339
266
|
// --- Row Click & Actions ---
|
|
340
267
|
this._onRowClick = function (e) {
|
|
341
268
|
if (e.target.closest('[data-ln-table-row-select]')) return;
|
|
@@ -377,8 +304,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
377
304
|
};
|
|
378
305
|
if (this.tbody) this.tbody.addEventListener('click', this._onRowAction);
|
|
379
306
|
|
|
380
|
-
|
|
381
|
-
|
|
382
307
|
// Local hydration of initial SSR rows or initial loading state
|
|
383
308
|
if (this.tbody && this.tbody.rows.length > 0) {
|
|
384
309
|
this._parseRows();
|
|
@@ -390,16 +315,12 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
390
315
|
// Initial request-data deferred so coordinators have attached listeners
|
|
391
316
|
setTimeout(function () {
|
|
392
317
|
if (!self.dom || !document.contains(self.dom)) return;
|
|
393
|
-
|
|
394
|
-
self.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
filters: self.currentFilters,
|
|
400
|
-
search: self.currentSearch
|
|
401
|
-
});
|
|
402
|
-
}
|
|
318
|
+
dispatch(dom, 'ln-table:request-data', {
|
|
319
|
+
table: self.name,
|
|
320
|
+
sort: self.currentSort,
|
|
321
|
+
filters: self.currentFilters,
|
|
322
|
+
search: self.currentSearch
|
|
323
|
+
});
|
|
403
324
|
}, 0);
|
|
404
325
|
|
|
405
326
|
} else {
|
|
@@ -577,7 +498,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
577
498
|
if (!this.tbody) return;
|
|
578
499
|
|
|
579
500
|
if (this.isDataDriven) {
|
|
580
|
-
if (this._windowed) { this._renderWindowed(); return; }
|
|
581
501
|
const total = this._lastTotal;
|
|
582
502
|
const filtered = this.visibleCount;
|
|
583
503
|
|
|
@@ -625,8 +545,15 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
625
545
|
};
|
|
626
546
|
|
|
627
547
|
_component.prototype._renderAll = function () {
|
|
628
|
-
|
|
629
|
-
|
|
548
|
+
this._rowPool = null;
|
|
549
|
+
this._topSpacer = null;
|
|
550
|
+
this._topTd = null;
|
|
551
|
+
this._bottomSpacer = null;
|
|
552
|
+
this._bottomTd = null;
|
|
553
|
+
|
|
554
|
+
const data = this._filteredData;
|
|
555
|
+
|
|
556
|
+
if (this.isDataDriven || (data.length > 0 && !data[0].html)) {
|
|
630
557
|
const frag = document.createDocumentFragment();
|
|
631
558
|
|
|
632
559
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -640,7 +567,6 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
640
567
|
if (this._selectable) this._updateSelectAll();
|
|
641
568
|
} else {
|
|
642
569
|
const html = [];
|
|
643
|
-
const data = this._filteredData;
|
|
644
570
|
for (let i = 0; i < data.length; i++) html.push(data[i].html);
|
|
645
571
|
|
|
646
572
|
this.tbody.innerHTML = html.join('');
|
|
@@ -663,10 +589,7 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
663
589
|
this._rowHeight = this.tbody.rows[0].offsetHeight || 40;
|
|
664
590
|
} else {
|
|
665
591
|
let tempRow = null;
|
|
666
|
-
if (this.
|
|
667
|
-
const sample = this._cache ? this._cache.peek() : null;
|
|
668
|
-
tempRow = sample ? this._buildRow(sample) : this._buildPlaceholderRow();
|
|
669
|
-
} else if (this.isDataDriven && this._data.length > 0) {
|
|
592
|
+
if (this.isDataDriven && this._data.length > 0) {
|
|
670
593
|
tempRow = this._buildRow(this._data[0]);
|
|
671
594
|
}
|
|
672
595
|
if (tempRow && this.tbody) {
|
|
@@ -677,18 +600,21 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
677
600
|
}
|
|
678
601
|
}
|
|
679
602
|
|
|
680
|
-
|
|
681
|
-
this._scrollContainer = _findScrollContainer(this.dom);
|
|
682
|
-
} else {
|
|
683
|
-
this._scrollContainer = null;
|
|
684
|
-
}
|
|
603
|
+
this._scrollContainer = _findScrollContainer(this.dom, this.table);
|
|
685
604
|
const scrollTarget = this._scrollContainer || window;
|
|
686
605
|
|
|
606
|
+
// Disable browser scroll anchoring on table, tbody, and scroll container
|
|
607
|
+
// to prevent runaway scroll feedback loops when spacer heights dynamically resize.
|
|
608
|
+
if (this.dom) this.dom.style.overflowAnchor = 'none';
|
|
609
|
+
if (this.table) this.table.style.overflowAnchor = 'none';
|
|
610
|
+
if (this.tbody) this.tbody.style.overflowAnchor = 'none';
|
|
611
|
+
if (this._scrollContainer) this._scrollContainer.style.overflowAnchor = 'none';
|
|
612
|
+
|
|
687
613
|
this._scrollHandler = function () {
|
|
688
614
|
if (self._rafId) return;
|
|
689
615
|
self._rafId = requestAnimationFrame(function () {
|
|
690
616
|
self._rafId = null;
|
|
691
|
-
self.
|
|
617
|
+
self._renderVirtual();
|
|
692
618
|
});
|
|
693
619
|
};
|
|
694
620
|
|
|
@@ -713,123 +639,205 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
713
639
|
}
|
|
714
640
|
this._vStart = -1;
|
|
715
641
|
this._vEnd = -1;
|
|
642
|
+
this._rowPool = null;
|
|
643
|
+
this._topSpacer = null;
|
|
644
|
+
this._topTd = null;
|
|
645
|
+
this._bottomSpacer = null;
|
|
646
|
+
this._bottomTd = null;
|
|
716
647
|
};
|
|
717
648
|
|
|
718
|
-
|
|
719
|
-
const data = this._filteredData;
|
|
720
|
-
const total = data.length;
|
|
721
|
-
const rowH = this._rowHeight;
|
|
722
|
-
if (!rowH || !total) return;
|
|
649
|
+
// ─── Row Pool (Cheetah / Glide style DOM Recycling) ────────
|
|
723
650
|
|
|
724
|
-
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
651
|
+
_component.prototype._compileRowBindings = function (tr) {
|
|
652
|
+
const bindings = {
|
|
653
|
+
textNodes: [],
|
|
654
|
+
attrs: [],
|
|
655
|
+
cellAttrs: []
|
|
656
|
+
};
|
|
728
657
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
658
|
+
const walker = document.createTreeWalker(tr, NodeFilter.SHOW_TEXT);
|
|
659
|
+
while (walker.nextNode()) {
|
|
660
|
+
const node = walker.currentNode;
|
|
661
|
+
if (node.textContent.indexOf('{{') !== -1) {
|
|
662
|
+
const parent = node.parentElement;
|
|
663
|
+
bindings.textNodes.push({
|
|
664
|
+
node: node,
|
|
665
|
+
raw: node.textContent,
|
|
666
|
+
el: parent,
|
|
667
|
+
isLeaf: !!(parent && parent.children.length === 0)
|
|
668
|
+
});
|
|
669
|
+
}
|
|
741
670
|
}
|
|
742
671
|
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
672
|
+
const elements = Array.from(tr.querySelectorAll('*'));
|
|
673
|
+
if (tr.nodeType === 1) elements.push(tr);
|
|
674
|
+
|
|
675
|
+
for (let i = 0; i < elements.length; i++) {
|
|
676
|
+
const el = elements[i];
|
|
677
|
+
if (el.hasAttribute('data-ln-table-cell-attr')) {
|
|
678
|
+
const pairs = el.getAttribute('data-ln-table-cell-attr').split(',');
|
|
679
|
+
for (let j = 0; j < pairs.length; j++) {
|
|
680
|
+
const parts = pairs[j].trim().split(':');
|
|
681
|
+
if (parts.length === 2) {
|
|
682
|
+
bindings.cellAttrs.push({
|
|
683
|
+
el: el,
|
|
684
|
+
field: parts[0].trim(),
|
|
685
|
+
attr: parts[1].trim()
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
750
690
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
691
|
+
const attrs = el.attributes;
|
|
692
|
+
for (let j = 0; j < attrs.length; j++) {
|
|
693
|
+
const a = attrs[j];
|
|
694
|
+
if (a.value.indexOf('{{') !== -1) {
|
|
695
|
+
bindings.attrs.push({
|
|
696
|
+
el: el,
|
|
697
|
+
name: a.name,
|
|
698
|
+
raw: a.value
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
754
703
|
|
|
755
|
-
|
|
756
|
-
|
|
704
|
+
tr._lnBindings = bindings;
|
|
705
|
+
return bindings;
|
|
706
|
+
};
|
|
757
707
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
topTd.style.height = topH + 'px';
|
|
765
|
-
topSpacer.appendChild(topTd);
|
|
766
|
-
frag.appendChild(topSpacer);
|
|
708
|
+
_component.prototype._createTemplateRow = function () {
|
|
709
|
+
let clone = cloneTemplateScoped(this.dom, this.name + '-row', 'ln-table');
|
|
710
|
+
if (!clone) {
|
|
711
|
+
const genericTpl = this.dom.querySelector('template[data-ln-table-row]');
|
|
712
|
+
if (genericTpl) {
|
|
713
|
+
clone = document.importNode(genericTpl.content, true);
|
|
767
714
|
}
|
|
715
|
+
}
|
|
768
716
|
|
|
769
|
-
|
|
770
|
-
const tr = this._buildRow(data[i]);
|
|
771
|
-
if (tr) frag.appendChild(tr);
|
|
772
|
-
}
|
|
717
|
+
let tr = clone ? (clone.querySelector('[data-ln-table-row]') || clone.firstElementChild) : null;
|
|
773
718
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
bottomSpacer.setAttribute('aria-hidden', 'true');
|
|
778
|
-
const bottomTd = document.createElement('td');
|
|
779
|
-
bottomTd.setAttribute('colspan', colSpan);
|
|
780
|
-
bottomTd.style.height = bottomH + 'px';
|
|
781
|
-
bottomSpacer.appendChild(bottomTd);
|
|
782
|
-
frag.appendChild(bottomSpacer);
|
|
719
|
+
if (!tr) {
|
|
720
|
+
if (window.lnDebug || (typeof document !== 'undefined' && document.body && document.body.hasAttribute('data-ln-debug'))) {
|
|
721
|
+
console.warn(`[ln-table] Missing row template: <template data-ln-template="${this.name}-row">`);
|
|
783
722
|
}
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
784
725
|
|
|
785
|
-
|
|
726
|
+
this._compileRowBindings(tr);
|
|
727
|
+
return tr;
|
|
728
|
+
};
|
|
786
729
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
730
|
+
_component.prototype._updateRow = function (tr, record) {
|
|
731
|
+
if (!record) return;
|
|
732
|
+
tr._lnRecord = record;
|
|
733
|
+
if (record.id != null) {
|
|
734
|
+
tr.setAttribute('data-ln-table-row-id', record.id);
|
|
735
|
+
}
|
|
790
736
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
737
|
+
const bindings = tr._lnBindings || this._compileRowBindings(tr);
|
|
738
|
+
|
|
739
|
+
const textNodes = bindings.textNodes;
|
|
740
|
+
for (let i = 0; i < textNodes.length; i++) {
|
|
741
|
+
const item = textNodes[i];
|
|
742
|
+
const val = item.raw.replace(
|
|
743
|
+
/\{\{\s*(\w+)\s*\}\}/g,
|
|
744
|
+
function (_, key) { return record[key] !== undefined && record[key] !== null ? record[key] : ''; }
|
|
745
|
+
);
|
|
746
|
+
if (item.isLeaf && item.el) {
|
|
747
|
+
item.el.textContent = val;
|
|
748
|
+
} else if (item.node && item.node.parentNode) {
|
|
749
|
+
item.node.textContent = val;
|
|
750
|
+
} else if (item.el) {
|
|
751
|
+
item.el.textContent = val;
|
|
799
752
|
}
|
|
753
|
+
}
|
|
800
754
|
|
|
801
|
-
|
|
755
|
+
const attrs = bindings.attrs;
|
|
756
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
757
|
+
const item = attrs[i];
|
|
758
|
+
item.el.setAttribute(
|
|
759
|
+
item.name,
|
|
760
|
+
item.raw.replace(
|
|
761
|
+
/\{\{\s*(\w+)\s*\}\}/g,
|
|
762
|
+
function (_, key) { return record[key] !== undefined && record[key] !== null ? record[key] : ''; }
|
|
763
|
+
)
|
|
764
|
+
);
|
|
765
|
+
}
|
|
802
766
|
|
|
803
|
-
|
|
767
|
+
const cellAttrs = bindings.cellAttrs;
|
|
768
|
+
for (let i = 0; i < cellAttrs.length; i++) {
|
|
769
|
+
const item = cellAttrs[i];
|
|
770
|
+
const val = record[item.field];
|
|
771
|
+
if (val != null) {
|
|
772
|
+
item.el.setAttribute(item.attr, val);
|
|
773
|
+
}
|
|
804
774
|
}
|
|
805
|
-
};
|
|
806
775
|
|
|
807
|
-
|
|
776
|
+
if (bindings.textNodes.length === 0 && bindings.attrs.length === 0 && this.ths.length > 0) {
|
|
777
|
+
for (let j = 0; j < this.ths.length; j++) {
|
|
778
|
+
const colName = this.ths[j].getAttribute('data-ln-table-col');
|
|
779
|
+
if (colName && j < tr.cells.length && record[colName] != null) {
|
|
780
|
+
tr.cells[j].textContent = String(record[colName]);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
808
784
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
tr.appendChild(td);
|
|
817
|
-
return tr;
|
|
785
|
+
if (this._selectable) {
|
|
786
|
+
const id = record.id != null ? String(record.id) : null;
|
|
787
|
+
const isSelected = id != null && this.selectedIds.has(id);
|
|
788
|
+
tr.classList.toggle('ln-row-selected', isSelected);
|
|
789
|
+
const rowCb = tr.querySelector('[data-ln-table-row-select]');
|
|
790
|
+
if (rowCb) rowCb.checked = isSelected;
|
|
791
|
+
}
|
|
818
792
|
};
|
|
819
793
|
|
|
820
|
-
_component.prototype.
|
|
821
|
-
if (this.
|
|
822
|
-
this.
|
|
823
|
-
this.
|
|
824
|
-
|
|
794
|
+
_component.prototype._ensureRowPool = function (requiredCount, colSpan) {
|
|
795
|
+
if (!this._rowPool) {
|
|
796
|
+
this._rowPool = [];
|
|
797
|
+
this._topSpacer = document.createElement('tr');
|
|
798
|
+
this._topSpacer.className = 'ln-table__spacer';
|
|
799
|
+
this._topSpacer.setAttribute('aria-hidden', 'true');
|
|
800
|
+
this._topSpacer.style.overflowAnchor = 'none';
|
|
801
|
+
this._topTd = document.createElement('td');
|
|
802
|
+
this._topTd.setAttribute('colspan', colSpan);
|
|
803
|
+
this._topTd.style.padding = '0';
|
|
804
|
+
this._topTd.style.border = 'none';
|
|
805
|
+
this._topTd.style.overflowAnchor = 'none';
|
|
806
|
+
this._topSpacer.appendChild(this._topTd);
|
|
807
|
+
|
|
808
|
+
this._bottomSpacer = document.createElement('tr');
|
|
809
|
+
this._bottomSpacer.className = 'ln-table__spacer';
|
|
810
|
+
this._bottomSpacer.setAttribute('aria-hidden', 'true');
|
|
811
|
+
this._bottomSpacer.style.overflowAnchor = 'none';
|
|
812
|
+
this._bottomTd = document.createElement('td');
|
|
813
|
+
this._bottomTd.setAttribute('colspan', colSpan);
|
|
814
|
+
this._bottomTd.style.padding = '0';
|
|
815
|
+
this._bottomTd.style.border = 'none';
|
|
816
|
+
this._bottomTd.style.overflowAnchor = 'none';
|
|
817
|
+
this._bottomSpacer.appendChild(this._bottomTd);
|
|
818
|
+
|
|
819
|
+
this.tbody.replaceChildren(this._topSpacer, this._bottomSpacer);
|
|
820
|
+
} else {
|
|
821
|
+
if (this._topTd && this._topTd.getAttribute('colspan') !== String(colSpan)) {
|
|
822
|
+
this._topTd.setAttribute('colspan', colSpan);
|
|
823
|
+
this._bottomTd.setAttribute('colspan', colSpan);
|
|
824
|
+
}
|
|
825
825
|
}
|
|
826
826
|
|
|
827
|
-
|
|
827
|
+
while (this._rowPool.length < requiredCount) {
|
|
828
|
+
const tr = this._createTemplateRow();
|
|
829
|
+
if (!tr) break;
|
|
830
|
+
this._rowPool.push(tr);
|
|
831
|
+
this.tbody.insertBefore(tr, this._bottomSpacer);
|
|
832
|
+
}
|
|
833
|
+
};
|
|
828
834
|
|
|
835
|
+
_component.prototype._renderVirtual = function () {
|
|
836
|
+
const data = this._filteredData;
|
|
837
|
+
const total = data.length;
|
|
829
838
|
const rowH = this._rowHeight;
|
|
830
|
-
if (!rowH) return;
|
|
839
|
+
if (!rowH || !total) return;
|
|
831
840
|
|
|
832
|
-
const total = this._cache.logicalTotal;
|
|
833
841
|
const theadH = this.thead ? this.thead.offsetHeight : 0;
|
|
834
842
|
const sc = this._scrollContainer;
|
|
835
843
|
let scrollIntoData;
|
|
@@ -854,60 +862,72 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
854
862
|
const endRow = vw.end;
|
|
855
863
|
|
|
856
864
|
if (startRow === this._vStart && endRow === this._vEnd) return;
|
|
865
|
+
this._vStart = startRow;
|
|
866
|
+
this._vEnd = endRow;
|
|
857
867
|
|
|
858
868
|
const colSpan = this.ths.length || 1;
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
topTd.setAttribute('colspan', colSpan);
|
|
870
|
-
topTd.style.height = topH + 'px';
|
|
871
|
-
topSpacer.appendChild(topTd);
|
|
872
|
-
frag.appendChild(topSpacer);
|
|
869
|
+
const neededRows = endRow - startRow;
|
|
870
|
+
|
|
871
|
+
this._ensureRowPool(neededRows, colSpan);
|
|
872
|
+
|
|
873
|
+
if (vw.topPadding > 0) {
|
|
874
|
+
this._topTd.style.height = vw.topPadding + 'px';
|
|
875
|
+
this._topSpacer.style.display = '';
|
|
876
|
+
} else {
|
|
877
|
+
this._topTd.style.height = '0px';
|
|
878
|
+
this._topSpacer.style.display = 'none';
|
|
873
879
|
}
|
|
874
880
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
881
|
+
if (vw.bottomPadding > 0) {
|
|
882
|
+
this._bottomTd.style.height = vw.bottomPadding + 'px';
|
|
883
|
+
this._bottomSpacer.style.display = '';
|
|
884
|
+
} else {
|
|
885
|
+
this._bottomTd.style.height = '0px';
|
|
886
|
+
this._bottomSpacer.style.display = 'none';
|
|
882
887
|
}
|
|
883
888
|
|
|
884
|
-
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
889
|
+
for (let k = 0; k < this._rowPool.length; k++) {
|
|
890
|
+
const rowEl = this._rowPool[k];
|
|
891
|
+
const dataIndex = startRow + k;
|
|
892
|
+
if (k < neededRows && dataIndex < total) {
|
|
893
|
+
rowEl.style.display = '';
|
|
894
|
+
const rec = data[dataIndex];
|
|
895
|
+
if (rec && rec.html) {
|
|
896
|
+
const temp = document.createElement('tbody');
|
|
897
|
+
temp.innerHTML = rec.html;
|
|
898
|
+
const parsedTr = temp.firstElementChild;
|
|
899
|
+
if (parsedTr) {
|
|
900
|
+
rowEl.innerHTML = parsedTr.innerHTML;
|
|
901
|
+
rowEl.className = parsedTr.className;
|
|
902
|
+
rowEl._lnRecord = rec;
|
|
903
|
+
if (rec.id != null) rowEl.setAttribute('data-ln-table-row-id', rec.id);
|
|
904
|
+
}
|
|
905
|
+
} else {
|
|
906
|
+
this._updateRow(rowEl, rec);
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
rowEl.style.display = 'none';
|
|
910
|
+
}
|
|
893
911
|
}
|
|
894
912
|
|
|
895
|
-
this.
|
|
913
|
+
if (this._selectable) this._updateSelectAll();
|
|
914
|
+
};
|
|
896
915
|
|
|
897
|
-
// Select-all is disabled in windowed mode (D4) — no _updateSelectAll() call.
|
|
898
|
-
this._vStart = startRow;
|
|
899
|
-
this._vEnd = endRow;
|
|
900
916
|
|
|
901
|
-
this._cache.ensure(startRow, endRow);
|
|
902
|
-
};
|
|
903
917
|
|
|
904
918
|
// ─── Loading state ──────────────────────────────────────────
|
|
905
919
|
|
|
906
920
|
_component.prototype._showLoadingState = function () {
|
|
921
|
+
this._rowPool = null;
|
|
922
|
+
this._topSpacer = null;
|
|
923
|
+
this._topTd = null;
|
|
924
|
+
this._bottomSpacer = null;
|
|
925
|
+
this._bottomTd = null;
|
|
907
926
|
const colSpan = this.ths.length || 1;
|
|
908
927
|
let loadingEl = null;
|
|
909
928
|
|
|
910
|
-
let clone = cloneTemplateScoped(this.dom, this.name + '-loading', 'ln-table')
|
|
929
|
+
let clone = cloneTemplateScoped(this.dom, this.name + '-loading', 'ln-table')
|
|
930
|
+
|| cloneTemplateScoped(this.dom, 'table-loading', 'ln-table');
|
|
911
931
|
|
|
912
932
|
if (!clone) {
|
|
913
933
|
const genericTpl = this.dom.querySelector('template[data-ln-table-loading]');
|
|
@@ -940,9 +960,13 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
940
960
|
// ─── Empty state ───────────────────────────────────────────
|
|
941
961
|
|
|
942
962
|
_component.prototype._showEmptyState = function () {
|
|
963
|
+
this._rowPool = null;
|
|
964
|
+
this._topSpacer = null;
|
|
965
|
+
this._topTd = null;
|
|
966
|
+
this._bottomSpacer = null;
|
|
967
|
+
this._bottomTd = null;
|
|
943
968
|
const colSpan = this.ths.length || 1;
|
|
944
969
|
let emptyEl = null;
|
|
945
|
-
|
|
946
970
|
let clone = null;
|
|
947
971
|
|
|
948
972
|
if (this.isDataDriven) {
|
|
@@ -954,7 +978,8 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
954
978
|
const isFiltered = filtered === 0 && total > 0;
|
|
955
979
|
|
|
956
980
|
const templateName = isFiltered ? (this.name + '-empty-filtered') : (this.name + '-empty');
|
|
957
|
-
clone = cloneTemplateScoped(this.dom, templateName, 'ln-table')
|
|
981
|
+
clone = cloneTemplateScoped(this.dom, templateName, 'ln-table')
|
|
982
|
+
|| cloneTemplateScoped(this.dom, isFiltered ? 'table-empty-filtered' : 'table-empty', 'ln-table');
|
|
958
983
|
|
|
959
984
|
if (!clone) {
|
|
960
985
|
const genericTpl = this.dom.querySelector('template[data-ln-table-empty]');
|
|
@@ -967,30 +992,29 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
967
992
|
}
|
|
968
993
|
}
|
|
969
994
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
td.appendChild(clone);
|
|
978
|
-
const tr = document.createElement('tr');
|
|
979
|
-
tr.className = 'ln-table__empty';
|
|
980
|
-
tr.appendChild(td);
|
|
981
|
-
emptyEl = tr;
|
|
995
|
+
} else {
|
|
996
|
+
clone = cloneTemplateScoped(this.dom, this.name + '-empty', 'ln-table')
|
|
997
|
+
|| cloneTemplateScoped(this.dom, 'table-empty', 'ln-table');
|
|
998
|
+
if (!clone) {
|
|
999
|
+
const genericTpl = this.dom.querySelector('template[data-ln-table-empty]');
|
|
1000
|
+
if (genericTpl) {
|
|
1001
|
+
clone = document.importNode(genericTpl.content, true);
|
|
982
1002
|
}
|
|
983
1003
|
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (clone) {
|
|
1007
|
+
if (clone.tagName === 'TR') {
|
|
1008
|
+
emptyEl = clone;
|
|
1009
|
+
} else {
|
|
1010
|
+
const td = document.createElement('td');
|
|
1011
|
+
td.setAttribute('colspan', String(colSpan));
|
|
1012
|
+
td.appendChild(clone);
|
|
1013
|
+
const tr = document.createElement('tr');
|
|
1014
|
+
tr.className = 'ln-table__empty';
|
|
1015
|
+
tr.appendChild(td);
|
|
1016
|
+
emptyEl = tr;
|
|
1017
|
+
}
|
|
994
1018
|
}
|
|
995
1019
|
|
|
996
1020
|
if (emptyEl) {
|
|
@@ -1043,28 +1067,10 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
1043
1067
|
temp.innerHTML = record.html;
|
|
1044
1068
|
tr = temp.firstElementChild;
|
|
1045
1069
|
} else {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const ths = this.ths;
|
|
1049
|
-
|
|
1050
|
-
for (let j = 0; j < ths.length; j++) {
|
|
1051
|
-
const isSelectCol = ths[j].hasAttribute('data-ln-table-col-select');
|
|
1052
|
-
const td = document.createElement('td');
|
|
1053
|
-
|
|
1054
|
-
if (isSelectCol) {
|
|
1055
|
-
const cb = document.createElement('input');
|
|
1056
|
-
cb.type = 'checkbox';
|
|
1057
|
-
cb.setAttribute('data-ln-table-row-select', '');
|
|
1058
|
-
cb.setAttribute('aria-label', 'Select row');
|
|
1059
|
-
td.appendChild(cb);
|
|
1060
|
-
} else {
|
|
1061
|
-
const colName = ths[j].getAttribute('data-ln-table-col');
|
|
1062
|
-
if (colName && record[colName] != null) {
|
|
1063
|
-
td.textContent = String(record[colName]);
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
tr.appendChild(td);
|
|
1070
|
+
if (window.lnDebug || (typeof document !== 'undefined' && document.body && document.body.hasAttribute('data-ln-debug'))) {
|
|
1071
|
+
console.warn(`[ln-table] Missing row template: <template data-ln-template="${this.name}-row">`);
|
|
1067
1072
|
}
|
|
1073
|
+
return null;
|
|
1068
1074
|
}
|
|
1069
1075
|
} else {
|
|
1070
1076
|
this._fillRow(tr, record);
|
|
@@ -1085,119 +1091,9 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
1085
1091
|
};
|
|
1086
1092
|
|
|
1087
1093
|
_component.prototype._requestData = function () {
|
|
1088
|
-
if (this._windowed) {
|
|
1089
|
-
this.dom.classList.add('ln-table--loading');
|
|
1090
|
-
this._cache.invalidate({
|
|
1091
|
-
sort: this.currentSort,
|
|
1092
|
-
filters: this.currentFilters,
|
|
1093
|
-
search: this.currentSearch
|
|
1094
|
-
});
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
1094
|
requestData(this, 'ln-table:request-data', 'table');
|
|
1098
1095
|
};
|
|
1099
1096
|
|
|
1100
|
-
// ─── Windowed Mode — enter/exit/seed (live toggle) ──────────
|
|
1101
|
-
|
|
1102
|
-
_component.prototype._enterWindowedMode = function () {
|
|
1103
|
-
const self = this;
|
|
1104
|
-
const dom = this.dom;
|
|
1105
|
-
const winAttr = parseInt(dom.getAttribute('data-ln-table-window'), 10);
|
|
1106
|
-
const pageAttr = parseInt(dom.getAttribute('data-ln-table-window-page'), 10);
|
|
1107
|
-
const threshAttr = parseInt(dom.getAttribute('data-ln-table-window-threshold'), 10);
|
|
1108
|
-
|
|
1109
|
-
// Cache change → sync totals off the cache, re-render (pull), notify.
|
|
1110
|
-
this._onCacheChange = function () {
|
|
1111
|
-
if (!self._windowed || !self._cache) return;
|
|
1112
|
-
self.totalCount = self._cache.grandTotal;
|
|
1113
|
-
self.visibleCount = self._cache.logicalTotal;
|
|
1114
|
-
self._lastTotal = self._cache.grandTotal;
|
|
1115
|
-
self.isLoaded = true;
|
|
1116
|
-
self._vStart = -1;
|
|
1117
|
-
self._vEnd = -1;
|
|
1118
|
-
self._render();
|
|
1119
|
-
self._updateFooter();
|
|
1120
|
-
dispatch(dom, 'ln-table:rendered', {
|
|
1121
|
-
table: self.name,
|
|
1122
|
-
total: self.totalCount,
|
|
1123
|
-
visible: self.visibleCount
|
|
1124
|
-
});
|
|
1125
|
-
};
|
|
1126
|
-
this._renderBatch = createBatcher(this._onCacheChange);
|
|
1127
|
-
|
|
1128
|
-
this._cache = createWindowCache({
|
|
1129
|
-
windowSize: winAttr > 0 ? winAttr : WINDOW_DEFAULT,
|
|
1130
|
-
pageSize: pageAttr > 0 ? pageAttr : WINDOW_PAGE,
|
|
1131
|
-
threshold: threshAttr >= 0 ? threshAttr : 25,
|
|
1132
|
-
fetchDebounce: FETCH_DEBOUNCE,
|
|
1133
|
-
requestPage: function (query, offset, limit) {
|
|
1134
|
-
dispatch(dom, 'ln-table:request-data', {
|
|
1135
|
-
table: self.name,
|
|
1136
|
-
sort: query.sort,
|
|
1137
|
-
filters: query.filters,
|
|
1138
|
-
search: query.search,
|
|
1139
|
-
offset: offset,
|
|
1140
|
-
limit: limit,
|
|
1141
|
-
queryGen: self._cache.queryGen
|
|
1142
|
-
});
|
|
1143
|
-
},
|
|
1144
|
-
onChange: this._renderBatch
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
this._windowed = true;
|
|
1148
|
-
|
|
1149
|
-
// D4 — windowed tables cannot select rows they have never fetched
|
|
1150
|
-
if (this._selectable && this._selectAllCheckbox) {
|
|
1151
|
-
this._selectAllCheckbox.classList.add('hidden');
|
|
1152
|
-
}
|
|
1153
|
-
};
|
|
1154
|
-
|
|
1155
|
-
_component.prototype._kickWindowInitial = function () {
|
|
1156
|
-
if (this._data.length > 0) {
|
|
1157
|
-
// SSR-seeded / warm-seeded: page 0 is already resident, the grand
|
|
1158
|
-
// total is declared in markup — no initial fetch needed. No
|
|
1159
|
-
// queryGen on the seed — it must never be dropped as stale.
|
|
1160
|
-
let declaredTotal = parseInt(this.dom.getAttribute('data-ln-table-count'), 10);
|
|
1161
|
-
if (isNaN(declaredTotal) && this._totalSpan) {
|
|
1162
|
-
const spanText = this._totalSpan.textContent.replace(/[^\d]/g, '');
|
|
1163
|
-
if (spanText) declaredTotal = parseInt(spanText, 10);
|
|
1164
|
-
}
|
|
1165
|
-
const seedTotal = (declaredTotal > 0) ? declaredTotal : this._data.length;
|
|
1166
|
-
this._cache.ingest({
|
|
1167
|
-
data: this._data,
|
|
1168
|
-
offset: 0,
|
|
1169
|
-
total: seedTotal,
|
|
1170
|
-
filtered: seedTotal
|
|
1171
|
-
});
|
|
1172
|
-
} else {
|
|
1173
|
-
this.dom.classList.add('ln-table--loading');
|
|
1174
|
-
this._cache.requestInitial({
|
|
1175
|
-
sort: this.currentSort,
|
|
1176
|
-
filters: this.currentFilters,
|
|
1177
|
-
search: this.currentSearch
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
};
|
|
1181
|
-
|
|
1182
|
-
_component.prototype._exitWindowedMode = function () {
|
|
1183
|
-
this._disableVirtualScroll();
|
|
1184
|
-
if (this._cache) this._cache.destroy();
|
|
1185
|
-
this._cache = null;
|
|
1186
|
-
this._windowed = false;
|
|
1187
|
-
this._renderBatch = null;
|
|
1188
|
-
this._onCacheChange = null;
|
|
1189
|
-
if (this._selectAllCheckbox) {
|
|
1190
|
-
this._selectAllCheckbox.classList.remove('hidden');
|
|
1191
|
-
}
|
|
1192
|
-
this._rowHeight = 0;
|
|
1193
|
-
this._vStart = -1;
|
|
1194
|
-
this._vEnd = -1;
|
|
1195
|
-
this._data = [];
|
|
1196
|
-
this._filteredData = [];
|
|
1197
|
-
this.dom.classList.add('ln-table--loading');
|
|
1198
|
-
this._requestData();
|
|
1199
|
-
};
|
|
1200
|
-
|
|
1201
1097
|
// ─── Selection Helpers ─────────────────────────────────────
|
|
1202
1098
|
|
|
1203
1099
|
_component.prototype._updateSelectAll = function () {
|
|
@@ -1370,11 +1266,11 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
1370
1266
|
const isFiltered = filtered < total;
|
|
1371
1267
|
|
|
1372
1268
|
if (this._totalSpan) {
|
|
1373
|
-
this._totalSpan
|
|
1269
|
+
_setNumberValue(this._totalSpan, total);
|
|
1374
1270
|
}
|
|
1375
1271
|
|
|
1376
1272
|
if (this._filteredSpan) {
|
|
1377
|
-
this._filteredSpan
|
|
1273
|
+
_setNumberValue(this._filteredSpan, isFiltered ? filtered : null);
|
|
1378
1274
|
}
|
|
1379
1275
|
|
|
1380
1276
|
if (this._filteredWrap) {
|
|
@@ -1383,7 +1279,7 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
1383
1279
|
|
|
1384
1280
|
if (this._selectedSpan) {
|
|
1385
1281
|
const count = this.selectedIds ? this.selectedIds.size : 0;
|
|
1386
|
-
this._selectedSpan
|
|
1282
|
+
_setNumberValue(this._selectedSpan, count > 0 ? count : null);
|
|
1387
1283
|
if (this._selectedWrap) {
|
|
1388
1284
|
this._selectedWrap.classList.toggle('hidden', count === 0);
|
|
1389
1285
|
}
|
|
@@ -1404,15 +1300,11 @@ import { calculateSelectionState, calculateVirtualWindow, toggleRowSelection, to
|
|
|
1404
1300
|
if (this.isDataDriven) {
|
|
1405
1301
|
this.dom.removeEventListener('ln-table:set-data', this._onSetData);
|
|
1406
1302
|
this.dom.removeEventListener('ln-table:set-loading', this._onSetLoading);
|
|
1407
|
-
this.dom.removeEventListener('ln-table:page-failed', this._onPageFailed);
|
|
1408
|
-
this.dom.removeEventListener('ln-table:request-revalidate', this._onRequestRevalidate);
|
|
1409
|
-
this.dom.removeEventListener('ln-table:request-invalidate', this._onRequestInvalidate);
|
|
1410
1303
|
this.dom.removeEventListener('ln-sort:change', this._onSort);
|
|
1411
1304
|
if (this.tbody) {
|
|
1412
1305
|
this.tbody.removeEventListener('click', this._onRowClick);
|
|
1413
1306
|
this.tbody.removeEventListener('click', this._onRowAction);
|
|
1414
1307
|
}
|
|
1415
|
-
if (this._cache) this._cache.destroy();
|
|
1416
1308
|
} else {
|
|
1417
1309
|
if (this._emptyTbodyObserver) {
|
|
1418
1310
|
this._emptyTbodyObserver.disconnect();
|