@livenetworks/ashlar 2.1.9 → 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.
@@ -27,6 +27,37 @@ export function calculateVirtualWindow(scrollTop, viewportHeight, rowHeight, tot
27
27
  return { start, end, topPadding, bottomPadding };
28
28
  }
29
29
 
30
+ /**
31
+ * Computes the required row pool size based on viewport height, row height, and buffer.
32
+ * @param {number} viewportHeight
33
+ * @param {number} rowHeight
34
+ * @param {number} [bufferRows=15]
35
+ * @returns {number}
36
+ */
37
+ export function calculatePoolSize(viewportHeight, rowHeight, bufferRows = 15) {
38
+ if (rowHeight <= 0) return 0;
39
+ const safeViewportHeight = Math.max(0, viewportHeight || 0);
40
+ const visibleCount = Math.ceil(safeViewportHeight / rowHeight);
41
+ return visibleCount + (bufferRows * 2);
42
+ }
43
+
44
+ /**
45
+ * Parses raw JSON seed string for a table into an array of records.
46
+ * @param {string} raw
47
+ * @returns {Array<Record<string, any>> | null}
48
+ */
49
+ export function parseTableSeed(raw) {
50
+ if (!raw || typeof raw !== 'string') return null;
51
+ const trimmed = raw.trim();
52
+ if (!trimmed) return null;
53
+ try {
54
+ const parsed = JSON.parse(trimmed);
55
+ return Array.isArray(parsed) ? parsed : null;
56
+ } catch (_) {
57
+ return null;
58
+ }
59
+ }
60
+
30
61
  /**
31
62
  * Computes selection state metadata across a dataset.
32
63
  * @param {string[]|number[]} allIds
@@ -329,6 +329,10 @@
329
329
  "name": "data-ln-data-store-search-fields",
330
330
  "description": "Record fields to index for client-side search"
331
331
  },
332
+ {
333
+ "name": "data-ln-data-store-seed",
334
+ "description": "Selector or element ID of an external seed script block"
335
+ },
332
336
  {
333
337
  "name": "data-ln-data-store-stale",
334
338
  "description": "Cache staleness threshold in seconds, or -1/never"
@@ -1341,18 +1345,13 @@
1341
1345
  "description": "Mounts table coordinator mediating between table, search, filter, and pagination",
1342
1346
  "valueSet": "v"
1343
1347
  },
1344
- {
1345
- "name": "data-ln-table-count",
1346
- "description": "Total record count override for virtual scrollbar calculation"
1347
- },
1348
1348
  {
1349
1349
  "name": "data-ln-table-dict",
1350
1350
  "description": "data-ln-table-dict attribute"
1351
1351
  },
1352
1352
  {
1353
1353
  "name": "data-ln-table-empty",
1354
- "description": "Container for table empty state",
1355
- "valueSet": "v"
1354
+ "description": "data-ln-table-empty attribute"
1356
1355
  },
1357
1356
  {
1358
1357
  "name": "data-ln-table-empty-when",
@@ -1366,6 +1365,10 @@
1366
1365
  "name": "data-ln-table-filtered",
1367
1366
  "description": "data-ln-table-filtered attribute"
1368
1367
  },
1368
+ {
1369
+ "name": "data-ln-table-loading",
1370
+ "description": "data-ln-table-loading attribute"
1371
+ },
1369
1372
  {
1370
1373
  "name": "data-ln-table-row",
1371
1374
  "description": "Table row template element",
@@ -1410,15 +1413,7 @@
1410
1413
  },
1411
1414
  {
1412
1415
  "name": "data-ln-table-window",
1413
- "description": "Virtual scrolling window size in rows"
1414
- },
1415
- {
1416
- "name": "data-ln-table-window-page",
1417
- "description": "Virtual scrolling slice page size"
1418
- },
1419
- {
1420
- "name": "data-ln-table-window-threshold",
1421
- "description": "Scroll threshold margin in pixels to trigger page fetch"
1416
+ "description": "data-ln-table-window attribute"
1422
1417
  },
1423
1418
  {
1424
1419
  "name": "data-ln-tabs",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livenetworks/ashlar",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "LiveNetworks unified frontend library - CSS framework and vanilla JS components",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "author": "Live Networks",
@@ -61,4 +61,13 @@
61
61
  @include ln-table-placeholder-row;
62
62
  }
63
63
 
64
+ [data-ln-table-loading],
65
+ .table-loading {
66
+ margin-left: auto;
67
+ display: inline-flex;
68
+ align-items: center;
69
+ color: var(--fg-muted);
70
+ }
71
+
72
+
64
73
 
@@ -39,12 +39,9 @@
39
39
  // bind to the outer .app-main > section scroll surface (same doctrine
40
40
  // as @mixin section-card).
41
41
  overflow-x: clip;
42
+ overflow-anchor: none;
42
43
  max-width: 100%;
43
44
 
44
- colgroup {
45
- display: none !important;
46
- }
47
-
48
45
  // Table overrides inside component
49
46
  table {
50
47
  overflow: visible;
@@ -57,18 +54,24 @@
57
54
  }
58
55
 
59
56
  // Whole thead sticky as one unit (toolbar row + column-headers row).
60
- // Wrapper's `overflow: clip` + `rounded-lg` clip the top corners,
61
- // so per-th radius is unnecessary.
57
+ // Solid opaque background prevents underlying scrolled rows from bleeding through.
62
58
  thead {
63
59
  @include sticky-top;
64
- will-change: transform;
65
- transform: translateZ(0);
60
+ background: var(--color-bg);
61
+ isolation: isolate;
66
62
  }
67
63
 
68
64
  thead th {
69
65
  background: var(--bg-sunken);
70
66
  }
71
67
 
68
+ // Prevent height fluctuation on recycled virtual table rows
69
+ tbody tr[data-ln-table-row] td {
70
+ white-space: nowrap;
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ }
74
+
72
75
  // First td override -- remove global bold/bg
73
76
  table td:first-child {
74
77
  background: transparent;
@@ -200,8 +203,7 @@
200
203
  position: sticky;
201
204
  bottom: 0;
202
205
  z-index: var(--z-sticky);
203
- will-change: transform;
204
- transform: translateZ(0);
206
+ isolation: isolate;
205
207
 
206
208
  strong {
207
209
  color: var(--color-fg);
@@ -382,10 +384,13 @@
382
384
  // Virtual-scroll spacer rows (JS-injected) — neutralize cell chrome including bg.
383
385
 
384
386
  @mixin ln-table-spacer-row {
387
+ overflow-anchor: none;
388
+
385
389
  td {
386
390
  padding: 0 !important;
387
391
  border: none !important;
388
392
  background: transparent !important;
393
+ overflow-anchor: none;
389
394
  }
390
395
  }
391
396
 
@@ -393,10 +398,13 @@
393
398
  // only. No shimmer (window-cache.md §4); height stays JS-owned via inline
394
399
  // px, spacer geometry depends on the exact value so nothing here may touch it.
395
400
  @mixin ln-table-placeholder-row {
401
+ overflow-anchor: none;
402
+
396
403
  td {
397
404
  padding: 0 !important;
398
405
  border: none !important;
399
406
  background: var(--bg-sunken);
407
+ overflow-anchor: none;
400
408
  }
401
409
  }
402
410
 
@@ -73,11 +73,15 @@
73
73
  top: auto;
74
74
  z-index: auto;
75
75
  vertical-align: middle;
76
+ white-space: nowrap;
77
+ overflow: hidden;
78
+ text-overflow: ellipsis;
76
79
 
77
80
  // Shared header-button stack — keyed off the unprefixed structural filter button
78
81
  // class and the [data-ln-sort] JS-component selector, so table / ln-table /
79
82
  // ln-data-table all share one positioning rule.
80
83
  &:has([data-ln-sort]):has(.table-filter) {
84
+ min-width: var(--th-double-action-min-width, 8.5rem);
81
85
  padding-inline-end: calc(var(--padding-x, var(--size-md)) + 3.75rem);
82
86
 
83
87
  .table-filter {
@@ -97,6 +101,7 @@
97
101
  }
98
102
 
99
103
  &:has([data-ln-sort]):not(:has(.table-filter)) {
104
+ min-width: var(--th-single-action-min-width, 6rem);
100
105
  padding-inline-end: calc(var(--padding-x, var(--size-md)) + 2rem);
101
106
 
102
107
  [data-ln-sort] {
@@ -109,6 +114,7 @@
109
114
  }
110
115
 
111
116
  &:has(.table-filter):not(:has([data-ln-sort])) {
117
+ min-width: var(--th-single-action-min-width, 6rem);
112
118
  padding-inline-end: calc(var(--padding-x, var(--size-md)) + 2rem);
113
119
 
114
120
  .table-filter {
@@ -281,8 +287,8 @@
281
287
  // ─── Column sort & filter buttons ──────────────────────────────
282
288
 
283
289
  @mixin table-header-btn-base {
284
- width: 1.75rem;
285
- height: 1.75rem;
290
+ width: var(--table-header-btn-size, 1.75rem);
291
+ height: var(--table-header-btn-size, 1.75rem);
286
292
  display: inline-flex;
287
293
  align-items: center;
288
294
  justify-content: center;
@@ -296,6 +302,10 @@
296
302
  opacity: 0.45;
297
303
  transition: all 0.15s ease-in-out;
298
304
 
305
+ .ln-icon {
306
+ --icon-size: var(--table-header-icon-size, 1.125rem);
307
+ }
308
+
299
309
  &:hover {
300
310
  background-color: color-mix(in srgb, var(--color-accent) var(--tint-hover), transparent);
301
311
  color: var(--color-accent);
@@ -371,9 +381,12 @@
371
381
  }
372
382
 
373
383
  @mixin table-spacer-row {
384
+ overflow-anchor: none;
385
+
374
386
  td {
375
387
  padding: 0 !important;
376
388
  border: none !important;
389
+ overflow-anchor: none;
377
390
  }
378
391
  }
379
392
 
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@livenetworks/ashlar",
4
- "version": "2.1.9",
4
+ "version": "2.1.11",
5
5
  "framework": "html",
6
6
  "contributions": {
7
7
  "html": {
@@ -513,6 +513,13 @@
513
513
  "type": "string[]"
514
514
  }
515
515
  },
516
+ {
517
+ "name": "data-ln-data-store-seed",
518
+ "description": "Selector or element ID of an external seed script block",
519
+ "value": {
520
+ "type": "string"
521
+ }
522
+ },
516
523
  {
517
524
  "name": "data-ln-data-store-stale",
518
525
  "description": "Cache staleness threshold in seconds, or -1/never",
@@ -1739,13 +1746,6 @@
1739
1746
  "type": "boolean"
1740
1747
  }
1741
1748
  },
1742
- {
1743
- "name": "data-ln-table-count",
1744
- "description": "Total record count override for virtual scrollbar calculation",
1745
- "value": {
1746
- "type": "number"
1747
- }
1748
- },
1749
1749
  {
1750
1750
  "name": "data-ln-table-dict",
1751
1751
  "description": "data-ln-table-dict attribute",
@@ -1755,9 +1755,9 @@
1755
1755
  },
1756
1756
  {
1757
1757
  "name": "data-ln-table-empty",
1758
- "description": "Container for table empty state",
1758
+ "description": "data-ln-table-empty attribute",
1759
1759
  "value": {
1760
- "type": "boolean"
1760
+ "type": "string"
1761
1761
  }
1762
1762
  },
1763
1763
  {
@@ -1781,6 +1781,13 @@
1781
1781
  "type": "string"
1782
1782
  }
1783
1783
  },
1784
+ {
1785
+ "name": "data-ln-table-loading",
1786
+ "description": "data-ln-table-loading attribute",
1787
+ "value": {
1788
+ "type": "string"
1789
+ }
1790
+ },
1784
1791
  {
1785
1792
  "name": "data-ln-table-row",
1786
1793
  "description": "Table row template element",
@@ -1853,23 +1860,9 @@
1853
1860
  },
1854
1861
  {
1855
1862
  "name": "data-ln-table-window",
1856
- "description": "Virtual scrolling window size in rows",
1857
- "value": {
1858
- "type": "number"
1859
- }
1860
- },
1861
- {
1862
- "name": "data-ln-table-window-page",
1863
- "description": "Virtual scrolling slice page size",
1863
+ "description": "data-ln-table-window attribute",
1864
1864
  "value": {
1865
- "type": "number"
1866
- }
1867
- },
1868
- {
1869
- "name": "data-ln-table-window-threshold",
1870
- "description": "Scroll threshold margin in pixels to trigger page fetch",
1871
- "value": {
1872
- "type": "number"
1865
+ "type": "string"
1873
1866
  }
1874
1867
  },
1875
1868
  {