flexdesk 0.4.5 → 0.4.6
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/css/base.css +57 -12
- package/css/flexdesk.css +75 -18
- package/css/overrides.css +15 -5
- package/dist/charts.js +2 -2
- package/dist/{chunk-BIAOGX6K.js → chunk-CYTWMP2Y.js} +569 -43
- package/dist/chunk-CYTWMP2Y.js.map +7 -0
- package/dist/{chunk-SWB5ARKN.js → chunk-HWCX5I4R.js} +2 -2
- package/dist/{chunk-QNQHQ24V.js → chunk-NLTOY57M.js} +72 -1
- package/dist/chunk-NLTOY57M.js.map +7 -0
- package/dist/editor.js +1 -1
- package/dist/flexdesk.css +75 -18
- package/dist/tiles.js +2 -2
- package/dist/widgets.js +1 -1
- package/dist/wm.js +1 -1
- package/package.json +1 -1
- package/src/editor/notebook_tab_bar.js +89 -0
- package/src/ui/components/data_table.js +635 -48
- package/dist/chunk-BIAOGX6K.js.map +0 -7
- package/dist/chunk-QNQHQ24V.js.map +0 -7
- /package/dist/{chunk-SWB5ARKN.js.map → chunk-HWCX5I4R.js.map} +0 -0
|
@@ -16,6 +16,7 @@ function createRafResizeObserver(callback) {
|
|
|
16
16
|
// src/ui/components/data_table.js
|
|
17
17
|
var DEFAULT_PAGE_SIZE = 100;
|
|
18
18
|
var AUTOSIZE_MAX_PX = 520;
|
|
19
|
+
var VIRTUAL_FALLBACK_VIEWPORT_PX = 1e3;
|
|
19
20
|
var DataTable = class {
|
|
20
21
|
/**
|
|
21
22
|
* Create a DataTable instance
|
|
@@ -71,8 +72,14 @@ var DataTable = class {
|
|
|
71
72
|
// promise that adding the key changed no existing layout by a
|
|
72
73
|
// pixel. Only a consumer that asks for 'content' sees anything new.
|
|
73
74
|
columnFit: "container",
|
|
75
|
+
// C10. Off unless asked for — see the typedef.
|
|
76
|
+
virtualize: false,
|
|
77
|
+
virtualRowHeight: null,
|
|
78
|
+
onRowsRendered: null,
|
|
74
79
|
...config
|
|
75
80
|
};
|
|
81
|
+
this._virt = null;
|
|
82
|
+
this._virtBase = null;
|
|
76
83
|
if (this.config.persistKey && !this.config.stateStore) {
|
|
77
84
|
throw new Error(`DataTable: persistKey "${this.config.persistKey}" requires { stateStore }`);
|
|
78
85
|
}
|
|
@@ -304,8 +311,11 @@ var DataTable = class {
|
|
|
304
311
|
const activeFilterColIdx = this._getActiveFilterColIdx();
|
|
305
312
|
const doc = this.container.ownerDocument;
|
|
306
313
|
const hadTableFocus = !!this._tableEl && doc?.activeElement === this._tableEl;
|
|
314
|
+
const prevWrap = this._tableWrapEl;
|
|
315
|
+
const prevScroll = prevWrap?.isConnected && this._renderedOffset === this._state.offset ? { top: prevWrap.scrollTop, left: prevWrap.scrollLeft } : null;
|
|
307
316
|
this._cleanup();
|
|
308
317
|
this.container.innerHTML = "";
|
|
318
|
+
this._virt = null;
|
|
309
319
|
const { rows, pagination, emptyMessage } = this.config;
|
|
310
320
|
const colSig = this._colSig();
|
|
311
321
|
if (this._colWidthsSig !== colSig) {
|
|
@@ -392,6 +402,18 @@ var DataTable = class {
|
|
|
392
402
|
}
|
|
393
403
|
this._tableWrapEl = tableWrap;
|
|
394
404
|
this.container.appendChild(this._wrapperEl);
|
|
405
|
+
if (prevScroll && this._state.offset === this._renderedOffset) {
|
|
406
|
+
tableWrap.scrollTop = prevScroll.top;
|
|
407
|
+
tableWrap.scrollLeft = prevScroll.left;
|
|
408
|
+
}
|
|
409
|
+
this._renderedOffset = this._state.offset;
|
|
410
|
+
if (this._virt) {
|
|
411
|
+
const onScroll = () => this._virtUpdate();
|
|
412
|
+
tableWrap.addEventListener("scroll", onScroll, { passive: true });
|
|
413
|
+
this._disposers.push(() => tableWrap.removeEventListener("scroll", onScroll));
|
|
414
|
+
this._virtMeasure();
|
|
415
|
+
this._virtUpdate({ force: true, notify: false });
|
|
416
|
+
}
|
|
395
417
|
if (this._headerTableEl && this._tableEl) {
|
|
396
418
|
requestAnimationFrame(() => this._syncHeaderWidths());
|
|
397
419
|
if (this._resizeObserver) {
|
|
@@ -401,9 +423,10 @@ var DataTable = class {
|
|
|
401
423
|
}
|
|
402
424
|
}
|
|
403
425
|
if (typeof ResizeObserver !== "undefined") {
|
|
404
|
-
this._resizeObserver = createRafResizeObserver(
|
|
405
|
-
|
|
406
|
-
|
|
426
|
+
this._resizeObserver = createRafResizeObserver(() => {
|
|
427
|
+
this._syncHeaderWidths();
|
|
428
|
+
this._virtUpdate();
|
|
429
|
+
});
|
|
407
430
|
this._resizeObserver.observe(this._wrapperEl);
|
|
408
431
|
}
|
|
409
432
|
this._installHeaderScrollSync();
|
|
@@ -534,6 +557,8 @@ var DataTable = class {
|
|
|
534
557
|
this._tableWrapEl = null;
|
|
535
558
|
this._processedRows = null;
|
|
536
559
|
this._processedIndexMap = null;
|
|
560
|
+
this._virt = null;
|
|
561
|
+
this._colgroupEl = null;
|
|
537
562
|
}
|
|
538
563
|
// ─────────────────────────────────────────────────────────────────
|
|
539
564
|
// Processed rows pipeline (filter → sort → cache)
|
|
@@ -1136,7 +1161,7 @@ var DataTable = class {
|
|
|
1136
1161
|
}
|
|
1137
1162
|
_createTable() {
|
|
1138
1163
|
const { headers, rows, pageSize, sortable, filterable, showRowNumbers, readonly, getHeaderIcon, onPageChange, offset: configOffset } = this.config;
|
|
1139
|
-
const {
|
|
1164
|
+
const { sortColumn, sortAscending } = this._state;
|
|
1140
1165
|
const isServerSide = typeof onPageChange === "function";
|
|
1141
1166
|
const processedRows = isServerSide ? rows : this._getProcessedRows();
|
|
1142
1167
|
const offset = isServerSide ? configOffset || 0 : this._state.offset;
|
|
@@ -1192,33 +1217,455 @@ var DataTable = class {
|
|
|
1192
1217
|
const filterRow = this._createFilterRow(headers);
|
|
1193
1218
|
thead.appendChild(filterRow);
|
|
1194
1219
|
}
|
|
1220
|
+
const virtual = this._isVirtual();
|
|
1221
|
+
if (virtual) {
|
|
1222
|
+
table.classList.add("twm-dt--virtual");
|
|
1223
|
+
table.appendChild(this._createColgroup());
|
|
1224
|
+
}
|
|
1195
1225
|
table.appendChild(thead);
|
|
1196
|
-
const
|
|
1197
|
-
pageRows.forEach((row, localIdx) => {
|
|
1226
|
+
const globalOf = (localIdx) => {
|
|
1198
1227
|
const processedIdx = offset + localIdx;
|
|
1199
|
-
|
|
1228
|
+
return isServerSide ? (configOffset || 0) + localIdx : this._processedIndexMap?.[processedIdx] ?? processedIdx;
|
|
1229
|
+
};
|
|
1230
|
+
const tbody = document.createElement("tbody");
|
|
1231
|
+
table.appendChild(tbody);
|
|
1232
|
+
this._tbodyEl = tbody;
|
|
1233
|
+
if (virtual) {
|
|
1234
|
+
this._virtInit(tbody, pageRows, globalOf, {
|
|
1235
|
+
serverSide: isServerSide,
|
|
1236
|
+
offset,
|
|
1237
|
+
configOffset: configOffset || 0
|
|
1238
|
+
});
|
|
1239
|
+
} else {
|
|
1240
|
+
pageRows.forEach((row, localIdx) => {
|
|
1241
|
+
tbody.appendChild(this._buildBodyRow(row, globalOf(localIdx), localIdx));
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
return table;
|
|
1245
|
+
}
|
|
1246
|
+
/** One body row, fully built. The ONE place a `<tr>` for a row is made, so
|
|
1247
|
+
* the full render and the virtual window cannot drift apart: a row drawn
|
|
1248
|
+
* on scroll is the same row a render would have drawn. */
|
|
1249
|
+
_buildBodyRow(row, globalIdx, localIdx) {
|
|
1250
|
+
const { showRowNumbers } = this.config;
|
|
1251
|
+
const tr = document.createElement("tr");
|
|
1252
|
+
tr.__rowIndex = globalIdx;
|
|
1253
|
+
tr.className = "data-preview-row";
|
|
1254
|
+
if (this._state.selected.has(globalIdx)) {
|
|
1255
|
+
tr.classList.add("selected");
|
|
1256
|
+
}
|
|
1257
|
+
if (this._virt) tr.classList.toggle("twm-dt-row--alt", localIdx % 2 === 1);
|
|
1258
|
+
this._fillRowCells(tr, row, globalIdx, showRowNumbers);
|
|
1259
|
+
if (this.config.onRowClick) {
|
|
1260
|
+
tr.addEventListener("click", (ev) => {
|
|
1261
|
+
this.config.onRowClick(globalIdx, row, ev);
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
if (this.config.onRowContextMenu) {
|
|
1265
|
+
tr.addEventListener("contextmenu", (ev) => {
|
|
1266
|
+
this.config.onRowContextMenu(globalIdx, row, ev);
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
return tr;
|
|
1270
|
+
}
|
|
1271
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1272
|
+
// C10 — the virtual window
|
|
1273
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1274
|
+
//
|
|
1275
|
+
// ══ WHAT IT IS ═══════════════════════════════════════════════════
|
|
1276
|
+
//
|
|
1277
|
+
// Only the rows near the viewport are in the DOM: a TOP SPACER row whose
|
|
1278
|
+
// height is every row above the window, the window's rows, and a BOTTOM
|
|
1279
|
+
// SPACER for everything below it. The scrollbar is therefore sized to the
|
|
1280
|
+
// whole table and every row is reachable, while the DOM stays bounded by
|
|
1281
|
+
// the viewport — a thousand-row page (or fifty of them) costs the same
|
|
1282
|
+
// few dozen `<tr>`s as one screenful.
|
|
1283
|
+
//
|
|
1284
|
+
// ══ WHAT IT PROMISES ═════════════════════════════════════════════
|
|
1285
|
+
//
|
|
1286
|
+
// - A row that stays in the window is NEVER MOVED OR REBUILT when the
|
|
1287
|
+
// window slides. Rows leave from the edges and arrive at the edges; the
|
|
1288
|
+
// ones in the middle are the same nodes. An open editor, a focused
|
|
1289
|
+
// cell, a hover — anything living in a row that is still on screen —
|
|
1290
|
+
// survives a scroll. (Moving a node that holds focus blurs it, which in
|
|
1291
|
+
// an editable grid is a commit nobody asked for.)
|
|
1292
|
+
// - Column widths come from a `<colgroup>`, so the first row changing
|
|
1293
|
+
// moves nothing (see `_createTable`).
|
|
1294
|
+
// - A spacer is in the DOM only while it has a height. A table that fits
|
|
1295
|
+
// in its window has exactly the DOM it had without `virtualize`.
|
|
1296
|
+
// - `onRowsRendered` hears about every row added after `render()`, and
|
|
1297
|
+
// every row taken out.
|
|
1298
|
+
//
|
|
1299
|
+
// ══ WHAT IT ASSUMES ══════════════════════════════════════════════
|
|
1300
|
+
//
|
|
1301
|
+
// That a row's height is knowable without drawing it: `baseHeight`,
|
|
1302
|
+
// measured from a real row, or what `virtualRowHeight` says. A row that
|
|
1303
|
+
// wraps to two lines is drawn correctly and is simply mis-counted in the
|
|
1304
|
+
// spacer arithmetic by the difference — the overscan absorbs that.
|
|
1305
|
+
_isVirtual() {
|
|
1306
|
+
return !!this.config.virtualize;
|
|
1307
|
+
}
|
|
1308
|
+
_virtOptions() {
|
|
1309
|
+
const v = this.config.virtualize;
|
|
1310
|
+
const o = v && typeof v === "object" ? v : {};
|
|
1311
|
+
return {
|
|
1312
|
+
overscan: Number.isFinite(o.overscan) && o.overscan >= 0 ? Math.floor(o.overscan) : 20,
|
|
1313
|
+
rowHeight: Number.isFinite(o.rowHeight) && o.rowHeight > 0 ? o.rowHeight : 24
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
/** One `<col>` per DOM column (the row-number column included), carrying
|
|
1317
|
+
* any width already pinned for it, so a re-render starts at the widths it
|
|
1318
|
+
* had rather than at a content-sized frame that then snaps. */
|
|
1319
|
+
_createColgroup() {
|
|
1320
|
+
const n = (this.config.headers?.length || 0) + (this.config.showRowNumbers ? 1 : 0);
|
|
1321
|
+
const colgroup = document.createElement("colgroup");
|
|
1322
|
+
for (let i = 0; i < n; i++) {
|
|
1323
|
+
const col = document.createElement("col");
|
|
1324
|
+
const w = this._colWidths?.[i];
|
|
1325
|
+
if (w != null && this._colWidthsSig === this._colSig()) col.style.width = `${w}px`;
|
|
1326
|
+
colgroup.appendChild(col);
|
|
1327
|
+
}
|
|
1328
|
+
this._colgroupEl = colgroup;
|
|
1329
|
+
return colgroup;
|
|
1330
|
+
}
|
|
1331
|
+
/** The `<col>`s, when this table has them. */
|
|
1332
|
+
_cols() {
|
|
1333
|
+
return this._virt && this._colgroupEl ? this._colgroupEl.children : null;
|
|
1334
|
+
}
|
|
1335
|
+
_virtInit(tbody, rows, globalOf, { serverSide, offset, configOffset }) {
|
|
1336
|
+
const { overscan, rowHeight } = this._virtOptions();
|
|
1337
|
+
const spacer = (edge) => {
|
|
1200
1338
|
const tr = document.createElement("tr");
|
|
1201
|
-
tr.
|
|
1202
|
-
tr.
|
|
1203
|
-
|
|
1204
|
-
|
|
1339
|
+
tr.className = `twm-dt-spacer twm-dt-spacer--${edge}`;
|
|
1340
|
+
tr.setAttribute("aria-hidden", "true");
|
|
1341
|
+
const td = document.createElement("td");
|
|
1342
|
+
td.colSpan = Math.max(1, (this.config.headers?.length || 0) + (this.config.showRowNumbers ? 1 : 0));
|
|
1343
|
+
td.style.cssText = "padding:0;border:0;height:0;min-width:0;max-width:none;";
|
|
1344
|
+
tr.appendChild(td);
|
|
1345
|
+
return tr;
|
|
1346
|
+
};
|
|
1347
|
+
const filler = document.createElement("tr");
|
|
1348
|
+
filler.className = "twm-dt-spacer twm-dt-spacer--parity";
|
|
1349
|
+
filler.setAttribute("aria-hidden", "true");
|
|
1350
|
+
filler.hidden = true;
|
|
1351
|
+
this._virt = {
|
|
1352
|
+
tbody,
|
|
1353
|
+
rows,
|
|
1354
|
+
globalOf,
|
|
1355
|
+
serverSide,
|
|
1356
|
+
offset,
|
|
1357
|
+
configOffset,
|
|
1358
|
+
overscan,
|
|
1359
|
+
base: this._virtBase || rowHeight,
|
|
1360
|
+
heights: null,
|
|
1361
|
+
offsets: null,
|
|
1362
|
+
start: 0,
|
|
1363
|
+
end: 0,
|
|
1364
|
+
map: /* @__PURE__ */ new Map(),
|
|
1365
|
+
top: spacer("top"),
|
|
1366
|
+
bottom: spacer("bottom"),
|
|
1367
|
+
filler
|
|
1368
|
+
};
|
|
1369
|
+
this._virtComputeOffsets();
|
|
1370
|
+
const guess = this.container?.clientHeight || 0;
|
|
1371
|
+
this._virtUpdate({ force: true, notify: false, viewport: guess || void 0, top: 0 });
|
|
1372
|
+
}
|
|
1373
|
+
/** Heights and their running sum. `offsets[i]` is where row `i` starts;
|
|
1374
|
+
* `offsets[n]` is the whole table. O(n), and rerun only when a height can
|
|
1375
|
+
* have changed — a new measurement, or `refreshVirtualLayout()`. */
|
|
1376
|
+
_virtComputeOffsets() {
|
|
1377
|
+
const v = this._virt;
|
|
1378
|
+
if (!v) return;
|
|
1379
|
+
const n = v.rows.length;
|
|
1380
|
+
const heights = new Float64Array(n);
|
|
1381
|
+
const offsets = new Float64Array(n + 1);
|
|
1382
|
+
const fn = typeof this.config.virtualRowHeight === "function" ? this.config.virtualRowHeight : null;
|
|
1383
|
+
let y = 0;
|
|
1384
|
+
for (let i = 0; i < n; i++) {
|
|
1385
|
+
let h = v.base;
|
|
1386
|
+
if (fn) {
|
|
1387
|
+
const asked = fn(v.globalOf(i), v.base);
|
|
1388
|
+
h = Number.isFinite(asked) && asked > 0 ? asked : 0;
|
|
1205
1389
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1390
|
+
heights[i] = h;
|
|
1391
|
+
offsets[i] = y;
|
|
1392
|
+
y += h;
|
|
1393
|
+
}
|
|
1394
|
+
offsets[n] = y;
|
|
1395
|
+
v.heights = heights;
|
|
1396
|
+
v.offsets = offsets;
|
|
1397
|
+
}
|
|
1398
|
+
/** The row whose span contains `y`: the LAST `i` with `offsets[i] <= y`,
|
|
1399
|
+
* which steps over rows of zero height at the same offset. */
|
|
1400
|
+
_virtIndexAt(y) {
|
|
1401
|
+
const v = this._virt;
|
|
1402
|
+
const n = v.rows.length;
|
|
1403
|
+
if (n === 0) return 0;
|
|
1404
|
+
let lo = 0;
|
|
1405
|
+
let hi = n - 1;
|
|
1406
|
+
while (lo < hi) {
|
|
1407
|
+
const mid = lo + hi + 1 >> 1;
|
|
1408
|
+
if (v.offsets[mid] <= y) lo = mid;
|
|
1409
|
+
else hi = mid - 1;
|
|
1410
|
+
}
|
|
1411
|
+
return lo;
|
|
1412
|
+
}
|
|
1413
|
+
/** Measure one real row. Client pixels, so the zoom is divided back out —
|
|
1414
|
+
* `_measureNaturalWidths` explains why — and NOT `offsetHeight`, which is
|
|
1415
|
+
* rounded: 21 for a 21.19px row is a 1,100px error over 6,000 rows. */
|
|
1416
|
+
_virtMeasure() {
|
|
1417
|
+
const v = this._virt;
|
|
1418
|
+
if (!v) return false;
|
|
1419
|
+
for (const tr of v.map.values()) {
|
|
1420
|
+
if (!tr.isConnected || tr.hidden) continue;
|
|
1421
|
+
const zoom = this._tableEl?.currentCSSZoom || 1;
|
|
1422
|
+
const h = tr.getBoundingClientRect().height / zoom;
|
|
1423
|
+
if (!(h > 0)) return false;
|
|
1424
|
+
if (Math.abs(h - v.base) > 0.01) {
|
|
1425
|
+
v.base = h;
|
|
1426
|
+
this._virtBase = h;
|
|
1427
|
+
this._virtComputeOffsets();
|
|
1428
|
+
return true;
|
|
1211
1429
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Slide the window to wherever the scroll box now is.
|
|
1436
|
+
*
|
|
1437
|
+
* HYSTERESIS, so a scroll of one pixel does not rebuild anything: the
|
|
1438
|
+
* window is redrawn only when the visible rows come within a quarter of the
|
|
1439
|
+
* overscan of either end of what is drawn, and it is then redrawn with the
|
|
1440
|
+
* full overscan on both sides.
|
|
1441
|
+
*
|
|
1442
|
+
* @param {object} [o]
|
|
1443
|
+
* @param {boolean} [o.force] redraw even inside the hysteresis band.
|
|
1444
|
+
* @param {boolean} [o.notify] report added and removed rows to `onRowsRendered`.
|
|
1445
|
+
* @param {number} [o.top] the scroll offset to draw for, instead of
|
|
1446
|
+
* reading one back — `scrollToRow` passes the offset it has just set,
|
|
1447
|
+
* which a DOM with no layout would read back as 0.
|
|
1448
|
+
* @param {number} [o.viewport] the viewport height, likewise.
|
|
1449
|
+
* @returns {HTMLElement[]} the rows added.
|
|
1450
|
+
*/
|
|
1451
|
+
_virtUpdate({ force = false, notify = true, top = null, viewport = null } = {}) {
|
|
1452
|
+
const v = this._virt;
|
|
1453
|
+
if (!v || !v.offsets) return [];
|
|
1454
|
+
const n = v.rows.length;
|
|
1455
|
+
const wrap = this._tableWrapEl;
|
|
1456
|
+
const view = viewport || wrap?.clientHeight || VIRTUAL_FALLBACK_VIEWPORT_PX;
|
|
1457
|
+
const scrollTop = top ?? (wrap?.scrollTop || 0);
|
|
1458
|
+
const vs = this._virtIndexAt(scrollTop);
|
|
1459
|
+
const ve = Math.min(n, this._virtIndexAt(scrollTop + view) + 1);
|
|
1460
|
+
if (!force && v.end > v.start) {
|
|
1461
|
+
const margin = Math.max(1, Math.floor(v.overscan / 4));
|
|
1462
|
+
const lowOk = v.start === 0 || vs >= v.start + margin;
|
|
1463
|
+
const highOk = v.end === n || ve <= v.end - margin;
|
|
1464
|
+
if (lowOk && highOk) return [];
|
|
1465
|
+
}
|
|
1466
|
+
const start = Math.max(0, vs - v.overscan);
|
|
1467
|
+
const end = Math.min(n, ve + v.overscan);
|
|
1468
|
+
const removed = [];
|
|
1469
|
+
const added = this._virtRender(start, end, removed);
|
|
1470
|
+
if (added.length || removed.length) {
|
|
1471
|
+
if (added[0]?.isConnected && this._tableEl?.style.tableLayout === "fixed") {
|
|
1472
|
+
this._updateCellTooltips(added);
|
|
1216
1473
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1474
|
+
if (notify) {
|
|
1475
|
+
try {
|
|
1476
|
+
this.config.onRowsRendered?.({ rows: added, removed });
|
|
1477
|
+
} catch (err) {
|
|
1478
|
+
console.error("[DataTable] onRowsRendered threw", err);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return added;
|
|
1483
|
+
}
|
|
1484
|
+
/** Make the DOM hold exactly the drawable rows of `[start, end)`, without
|
|
1485
|
+
* moving a row that is already there. See the promises above. */
|
|
1486
|
+
_virtRender(start, end, removed = null) {
|
|
1487
|
+
const v = this._virt;
|
|
1488
|
+
const tbody = v.tbody;
|
|
1489
|
+
const want = [];
|
|
1490
|
+
for (let i = start; i < end; i++) if (v.heights[i] > 0) want.push(i);
|
|
1491
|
+
const wanted = new Set(want);
|
|
1492
|
+
for (const [i, tr] of v.map) {
|
|
1493
|
+
if (!wanted.has(i)) {
|
|
1494
|
+
tr.remove();
|
|
1495
|
+
v.map.delete(i);
|
|
1496
|
+
removed?.push(tr);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
v.start = start;
|
|
1500
|
+
v.end = end;
|
|
1501
|
+
this._virtSpacers();
|
|
1502
|
+
const added = [];
|
|
1503
|
+
let ref = v.bottom.isConnected ? v.bottom : null;
|
|
1504
|
+
for (let k = want.length - 1; k >= 0; k--) {
|
|
1505
|
+
const i = want[k];
|
|
1506
|
+
const have = v.map.get(i);
|
|
1507
|
+
if (have) {
|
|
1508
|
+
ref = have;
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
const tr = this._buildBodyRow(v.rows[i], v.globalOf(i), i);
|
|
1512
|
+
v.map.set(i, tr);
|
|
1513
|
+
tbody.insertBefore(tr, ref);
|
|
1514
|
+
ref = tr;
|
|
1515
|
+
added.push(tr);
|
|
1516
|
+
}
|
|
1517
|
+
added.reverse();
|
|
1518
|
+
return added;
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Size the two spacers, and put each in the DOM only while it has a height.
|
|
1522
|
+
*
|
|
1523
|
+
* AND KEEP THE STRIPE. `tr:nth-child(even)` stripes by POSITION, and in a
|
|
1524
|
+
* full render row `i` is child `i + 1`. Here the first drawn row is preceded
|
|
1525
|
+
* by the top spacer, so its position is off by one — and by a different one
|
|
1526
|
+
* each time the window's start changes parity, which strobes every stripe
|
|
1527
|
+
* on the screen as you scroll. A hidden filler row after the spacer, present
|
|
1528
|
+
* exactly when `start` is even, puts the parity back: a `display: none` row
|
|
1529
|
+
* is still a child, and so still counted by `nth-child`.
|
|
1530
|
+
*/
|
|
1531
|
+
_virtSpacers() {
|
|
1532
|
+
const v = this._virt;
|
|
1533
|
+
const n = v.rows.length;
|
|
1534
|
+
const above = v.offsets[v.start];
|
|
1535
|
+
const below = v.offsets[n] - v.offsets[v.end];
|
|
1536
|
+
const tbody = v.tbody;
|
|
1537
|
+
if (above > 0) {
|
|
1538
|
+
v.top.firstChild.style.height = `${above}px`;
|
|
1539
|
+
if (tbody.firstChild !== v.top) tbody.insertBefore(v.top, tbody.firstChild);
|
|
1540
|
+
const needFiller = v.start % 2 === 0;
|
|
1541
|
+
if (needFiller && v.top.nextSibling !== v.filler) v.top.after(v.filler);
|
|
1542
|
+
else if (!needFiller) v.filler.remove();
|
|
1543
|
+
} else {
|
|
1544
|
+
v.top.remove();
|
|
1545
|
+
v.filler.remove();
|
|
1546
|
+
}
|
|
1547
|
+
if (below > 0) {
|
|
1548
|
+
v.bottom.firstChild.style.height = `${below}px`;
|
|
1549
|
+
if (tbody.lastChild !== v.bottom) tbody.appendChild(v.bottom);
|
|
1550
|
+
} else {
|
|
1551
|
+
v.bottom.remove();
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Recompute every row's height and redraw the window. For an embedder whose
|
|
1556
|
+
* `virtualRowHeight` answer has changed — a group folded, a header added.
|
|
1557
|
+
* A no-op on a table that is not virtual.
|
|
1558
|
+
*/
|
|
1559
|
+
refreshVirtualLayout() {
|
|
1560
|
+
if (!this._virt) return;
|
|
1561
|
+
this._virtMeasure();
|
|
1562
|
+
this._virtComputeOffsets();
|
|
1563
|
+
this._virtUpdate({ force: true });
|
|
1564
|
+
}
|
|
1565
|
+
/** The element that scrolls the body. Read-only; the table owns it. */
|
|
1566
|
+
get scrollElement() {
|
|
1567
|
+
return this._tableWrapEl || null;
|
|
1568
|
+
}
|
|
1569
|
+
/** The `<tr>` drawing row `rowIndex` (the `__rowIndex` space), or null when
|
|
1570
|
+
* it is not in the DOM — outside the window, on another page, or hidden. */
|
|
1571
|
+
getRowElement(rowIndex) {
|
|
1572
|
+
const v = this._virt;
|
|
1573
|
+
if (v) {
|
|
1574
|
+
const local = this._virtLocalOf(rowIndex);
|
|
1575
|
+
return local < 0 ? null : v.map.get(local) || null;
|
|
1576
|
+
}
|
|
1577
|
+
const tbody = this._tbodyEl;
|
|
1578
|
+
if (!tbody) return null;
|
|
1579
|
+
for (const tr of tbody.children) if (tr.__rowIndex === rowIndex) return tr;
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
/** Keep the window's row list and `config.rows` in step for one row. They
|
|
1583
|
+
* are the same array in server-side mode; in client mode the window reads
|
|
1584
|
+
* the filtered-and-sorted copy, which holds its own reference. */
|
|
1585
|
+
_virtSetRow(rowIndex, row) {
|
|
1586
|
+
const v = this._virt;
|
|
1587
|
+
if (!v) return;
|
|
1588
|
+
if (Array.isArray(this.config.rows) && rowIndex >= 0 && rowIndex < this.config.rows.length) {
|
|
1589
|
+
this.config.rows[rowIndex] = row;
|
|
1590
|
+
}
|
|
1591
|
+
const local = this._virtLocalOf(rowIndex);
|
|
1592
|
+
if (local >= 0) v.rows[local] = row;
|
|
1593
|
+
}
|
|
1594
|
+
/** `__rowIndex` → position in the window's row list, or -1. */
|
|
1595
|
+
_virtLocalOf(rowIndex) {
|
|
1596
|
+
const v = this._virt;
|
|
1597
|
+
if (!v) return -1;
|
|
1598
|
+
let local;
|
|
1599
|
+
if (v.serverSide) local = rowIndex - v.configOffset;
|
|
1600
|
+
else {
|
|
1601
|
+
const map = this._processedIndexMap;
|
|
1602
|
+
const processed = map ? map.indexOf(rowIndex) : rowIndex;
|
|
1603
|
+
local = processed < 0 ? -1 : processed - v.offset;
|
|
1604
|
+
}
|
|
1605
|
+
return local >= 0 && local < v.rows.length ? local : -1;
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Scroll the body so row `rowIndex` is on screen, drawing it if it was not,
|
|
1609
|
+
* and return its `<tr>` (null when it cannot be shown: another page, or a
|
|
1610
|
+
* row the embedder hides). Works with or without `virtualize`.
|
|
1611
|
+
*
|
|
1612
|
+
* @param {number} rowIndex in the `__rowIndex` space.
|
|
1613
|
+
* @param {{block?: 'nearest'|'center'|'start'}} [opts]
|
|
1614
|
+
*/
|
|
1615
|
+
scrollToRow(rowIndex, { block = "nearest" } = {}) {
|
|
1616
|
+
const wrap = this._tableWrapEl;
|
|
1617
|
+
const v = this._virt;
|
|
1618
|
+
if (!v) {
|
|
1619
|
+
const tr = this.getRowElement(rowIndex);
|
|
1620
|
+
if (!tr || !wrap) return tr;
|
|
1621
|
+
const zoom = this._tableEl?.currentCSSZoom || 1;
|
|
1622
|
+
const box = wrap.getBoundingClientRect();
|
|
1623
|
+
const r = tr.getBoundingClientRect();
|
|
1624
|
+
const rowTop = (r.top - box.top) / zoom + wrap.scrollTop;
|
|
1625
|
+
const rowH = r.height / zoom;
|
|
1626
|
+
const target2 = this._scrollTarget(
|
|
1627
|
+
rowTop,
|
|
1628
|
+
rowH,
|
|
1629
|
+
wrap.scrollTop,
|
|
1630
|
+
wrap.clientHeight,
|
|
1631
|
+
block
|
|
1632
|
+
);
|
|
1633
|
+
if (target2 !== wrap.scrollTop) wrap.scrollTop = target2;
|
|
1634
|
+
return tr;
|
|
1635
|
+
}
|
|
1636
|
+
const local = this._virtLocalOf(rowIndex);
|
|
1637
|
+
if (local < 0 || !(v.heights[local] > 0)) return null;
|
|
1638
|
+
const view = wrap?.clientHeight || VIRTUAL_FALLBACK_VIEWPORT_PX;
|
|
1639
|
+
const current = wrap?.scrollTop || 0;
|
|
1640
|
+
let target = this._scrollTarget(
|
|
1641
|
+
v.offsets[local],
|
|
1642
|
+
v.heights[local],
|
|
1643
|
+
current,
|
|
1644
|
+
view,
|
|
1645
|
+
block
|
|
1646
|
+
);
|
|
1647
|
+
target = Math.max(0, Math.min(target, Math.max(0, v.offsets[v.rows.length] - view)));
|
|
1648
|
+
if (wrap && Math.abs(target - current) >= 1) wrap.scrollTop = target;
|
|
1649
|
+
this._virtUpdate({ top: Math.abs(target - current) >= 1 ? target : current });
|
|
1650
|
+
return v.map.get(local) || null;
|
|
1651
|
+
}
|
|
1652
|
+
_scrollTarget(rowTop, rowH, current, view, block) {
|
|
1653
|
+
if (block === "start") return rowTop;
|
|
1654
|
+
if (block === "center") return rowTop - Math.max(0, (view - rowH) / 2);
|
|
1655
|
+
if (rowTop < current) return rowTop;
|
|
1656
|
+
if (rowTop + rowH > current + view) return rowTop + rowH - view;
|
|
1657
|
+
return current;
|
|
1658
|
+
}
|
|
1659
|
+
/** The first body row that draws a row of data — never a spacer, and never
|
|
1660
|
+
* a row an embedder injected (a group header spans every column with ONE
|
|
1661
|
+
* cell, so measuring it would report one column). */
|
|
1662
|
+
_firstBodyRow() {
|
|
1663
|
+
const tbody = this._tableEl?.querySelector?.("tbody");
|
|
1664
|
+
if (!tbody) return null;
|
|
1665
|
+
for (const tr of tbody.children) {
|
|
1666
|
+
if (tr.__rowIndex !== void 0) return tr;
|
|
1667
|
+
}
|
|
1668
|
+
return null;
|
|
1222
1669
|
}
|
|
1223
1670
|
/** Build (or rebuild) one row's cells in place.
|
|
1224
1671
|
*
|
|
@@ -1298,10 +1745,14 @@ var DataTable = class {
|
|
|
1298
1745
|
break;
|
|
1299
1746
|
}
|
|
1300
1747
|
}
|
|
1301
|
-
if (!tr)
|
|
1748
|
+
if (!tr) {
|
|
1749
|
+
if (this._virt) this._virtSetRow(index, row);
|
|
1750
|
+
return false;
|
|
1751
|
+
}
|
|
1302
1752
|
if (Array.isArray(this.config.rows) && this.config.rows[index]) {
|
|
1303
1753
|
this.config.rows[index] = row;
|
|
1304
1754
|
}
|
|
1755
|
+
if (this._virt) this._virtSetRow(index, row);
|
|
1305
1756
|
const active = document.activeElement;
|
|
1306
1757
|
let focusedCol = -1;
|
|
1307
1758
|
if (active && tr.contains(active)) {
|
|
@@ -1350,14 +1801,23 @@ var DataTable = class {
|
|
|
1350
1801
|
* never visible. Clearing inline widths first stops the last sync's forced
|
|
1351
1802
|
* widths from constraining the measure.
|
|
1352
1803
|
*
|
|
1804
|
+
* C10 — UNDER `virtualize` ONLY THE WINDOW IS IN THE DOM, so `max-content`
|
|
1805
|
+
* spans the eighty-odd rows drawn rather than every row loaded. The fit
|
|
1806
|
+
* pass that runs on every render is content with that (it is fitting what
|
|
1807
|
+
* is on screen). An EXPLICIT auto-size is not: it is a promise to fit the
|
|
1808
|
+
* column's content, and the longest value in a 6,000-row table is usually
|
|
1809
|
+
* not in the window. So `sample` adds, for the length of this one reflow,
|
|
1810
|
+
* the row holding each column's longest value — see `_virtSampleRows`.
|
|
1811
|
+
*
|
|
1812
|
+
* @param {{sample?: boolean}} [o]
|
|
1353
1813
|
* @returns {number[]|null} width per DOM column index, or null when there
|
|
1354
1814
|
* is nothing laid out to measure.
|
|
1355
1815
|
*/
|
|
1356
|
-
_measureNaturalWidths() {
|
|
1816
|
+
_measureNaturalWidths({ sample = false } = {}) {
|
|
1357
1817
|
const headerTable = this._headerTableEl;
|
|
1358
1818
|
const bodyTable = this._tableEl;
|
|
1359
1819
|
if (!headerTable || !bodyTable) return null;
|
|
1360
|
-
const firstRow =
|
|
1820
|
+
const firstRow = this._firstBodyRow();
|
|
1361
1821
|
if (!firstRow) return null;
|
|
1362
1822
|
const bodyCells = firstRow.children;
|
|
1363
1823
|
if (!bodyCells.length) return null;
|
|
@@ -1365,10 +1825,13 @@ var DataTable = class {
|
|
|
1365
1825
|
for (const th of tr.children) this._clearCellWidth(th);
|
|
1366
1826
|
});
|
|
1367
1827
|
for (const td of bodyCells) this._clearCellWidth(td);
|
|
1828
|
+
for (const col of this._cols() || []) col.style.width = "";
|
|
1368
1829
|
headerTable.style.width = "";
|
|
1369
1830
|
bodyTable.style.width = "";
|
|
1370
1831
|
headerTable.classList.add("twm-dt-measuring");
|
|
1371
1832
|
bodyTable.classList.add("twm-dt-measuring");
|
|
1833
|
+
const sampled = sample ? this._virtSampleRows() : [];
|
|
1834
|
+
for (const tr of sampled) this._virt.tbody.appendChild(tr);
|
|
1372
1835
|
bodyTable.offsetWidth;
|
|
1373
1836
|
const labelRow = headerTable.querySelector("thead > tr");
|
|
1374
1837
|
const cols = bodyCells.length;
|
|
@@ -1379,12 +1842,61 @@ var DataTable = class {
|
|
|
1379
1842
|
const head = labelRow && labelRow.children[i] ? labelRow.children[i].getBoundingClientRect().width / zoom : 0;
|
|
1380
1843
|
natural[i] = Math.max(body, head);
|
|
1381
1844
|
}
|
|
1845
|
+
for (const tr of sampled) tr.remove();
|
|
1382
1846
|
headerTable.classList.remove("twm-dt-measuring");
|
|
1383
1847
|
bodyTable.classList.remove("twm-dt-measuring");
|
|
1384
1848
|
return natural;
|
|
1385
1849
|
}
|
|
1386
1850
|
/**
|
|
1387
|
-
*
|
|
1851
|
+
* The rows an explicit auto-size must measure that the window has not
|
|
1852
|
+
* drawn: for each column, the drawable row whose value is LONGEST as a
|
|
1853
|
+
* string, plus the last row (the widest row number). Built with
|
|
1854
|
+
* `_buildBodyRow` — so a consumer's `renderCell` draws them exactly as it
|
|
1855
|
+
* would on screen — and handed back detached, for `_measureNaturalWidths`
|
|
1856
|
+
* to put in and take out around its one reflow.
|
|
1857
|
+
*
|
|
1858
|
+
* The string length is a proxy, not a measurement: in a proportional font
|
|
1859
|
+
* forty `i`s are narrower than thirty `W`s. It is the proxy a spreadsheet's
|
|
1860
|
+
* own autofit falls short by, and it is right about the case that matters
|
|
1861
|
+
* here — a value five times longer than anything in the window. One pass
|
|
1862
|
+
* over the loaded rows, on a click and never on a scroll or a resize.
|
|
1863
|
+
*
|
|
1864
|
+
* @returns {HTMLTableRowElement[]}
|
|
1865
|
+
*/
|
|
1866
|
+
_virtSampleRows() {
|
|
1867
|
+
const v = this._virt;
|
|
1868
|
+
if (!v || !v.rows.length) return [];
|
|
1869
|
+
const longest = [];
|
|
1870
|
+
const at = [];
|
|
1871
|
+
let last = -1;
|
|
1872
|
+
for (let i = 0; i < v.rows.length; i++) {
|
|
1873
|
+
if (!(v.heights[i] > 0)) continue;
|
|
1874
|
+
last = i;
|
|
1875
|
+
const row = v.rows[i];
|
|
1876
|
+
if (!Array.isArray(row)) continue;
|
|
1877
|
+
for (let c = 0; c < row.length; c++) {
|
|
1878
|
+
const value = row[c];
|
|
1879
|
+
const len = value == null ? 0 : String(value).length;
|
|
1880
|
+
if (len > (longest[c] ?? -1)) {
|
|
1881
|
+
longest[c] = len;
|
|
1882
|
+
at[c] = i;
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
const want = new Set(at.filter((i) => i !== void 0));
|
|
1887
|
+
if (last >= 0) want.add(last);
|
|
1888
|
+
const rows = [];
|
|
1889
|
+
for (const i of want) {
|
|
1890
|
+
if (v.map.has(i)) continue;
|
|
1891
|
+
const tr = this._buildBodyRow(v.rows[i], v.globalOf(i), i);
|
|
1892
|
+
tr.setAttribute("aria-hidden", "true");
|
|
1893
|
+
rows.push(tr);
|
|
1894
|
+
}
|
|
1895
|
+
return rows;
|
|
1896
|
+
}
|
|
1897
|
+
/**
|
|
1898
|
+
* Fit one column to its widest value and pin it there — every loaded
|
|
1899
|
+
* row's, under `virtualize` (see `_virtSampleRows`), not only the window's.
|
|
1388
1900
|
*
|
|
1389
1901
|
* The gesture is a double-click on the column's resize grip, which is where
|
|
1390
1902
|
* every spreadsheet has put it — and the grip is this component's, which is
|
|
@@ -1399,7 +1911,7 @@ var DataTable = class {
|
|
|
1399
1911
|
* @returns {boolean} whether it had a layout to measure.
|
|
1400
1912
|
*/
|
|
1401
1913
|
autoSizeColumn(domIdx, opts = {}) {
|
|
1402
|
-
const natural = this._measureNaturalWidths();
|
|
1914
|
+
const natural = this._measureNaturalWidths({ sample: true });
|
|
1403
1915
|
if (!natural || domIdx < 0 || domIdx >= natural.length) return false;
|
|
1404
1916
|
this._colWidths[domIdx] = this._autoWidth(natural[domIdx], opts);
|
|
1405
1917
|
this._colWidthsSig = this._colSig();
|
|
@@ -1423,7 +1935,7 @@ var DataTable = class {
|
|
|
1423
1935
|
* already, so a button that did that would be a button that does nothing.
|
|
1424
1936
|
*/
|
|
1425
1937
|
autoSizeColumns(opts = {}) {
|
|
1426
|
-
const natural = this._measureNaturalWidths();
|
|
1938
|
+
const natural = this._measureNaturalWidths({ sample: true });
|
|
1427
1939
|
if (!natural) return false;
|
|
1428
1940
|
this._colWidths = {};
|
|
1429
1941
|
for (let i = 0; i < natural.length; i++) {
|
|
@@ -1472,17 +1984,18 @@ var DataTable = class {
|
|
|
1472
1984
|
const headerTable = this._headerTableEl;
|
|
1473
1985
|
const bodyTable = this._tableEl;
|
|
1474
1986
|
if (!headerTable || !bodyTable) return;
|
|
1475
|
-
const
|
|
1476
|
-
|
|
1477
|
-
const bodyCells = firstRow
|
|
1478
|
-
|
|
1987
|
+
const cols = this._cols();
|
|
1988
|
+
const firstRow = this._firstBodyRow();
|
|
1989
|
+
const bodyCells = cols ? [] : firstRow?.children;
|
|
1990
|
+
const n = cols ? cols.length : bodyCells?.length || 0;
|
|
1991
|
+
if (!n) return;
|
|
1479
1992
|
const headerRows = headerTable.querySelectorAll("thead > tr");
|
|
1480
|
-
const natural = this._allColumnsPinned(
|
|
1993
|
+
const natural = this._allColumnsPinned(n) ? new Array(n).fill(0) : this._measureNaturalWidths();
|
|
1481
1994
|
if (!natural) return;
|
|
1482
1995
|
const wrap = this._tableWrapEl;
|
|
1483
1996
|
const headerWrap = this._headerWrapEl;
|
|
1484
1997
|
const avail = wrap ? wrap.clientWidth : 0;
|
|
1485
|
-
const firstIdx = this.config.showRowNumbers &&
|
|
1998
|
+
const firstIdx = this.config.showRowNumbers && n > 1 ? 1 : 0;
|
|
1486
1999
|
const widths = this._fitColumnWidths(natural, avail, {
|
|
1487
2000
|
firstIdx,
|
|
1488
2001
|
overrides: this._colWidths,
|
|
@@ -1494,8 +2007,14 @@ var DataTable = class {
|
|
|
1494
2007
|
this._setCellWidth(tr.children[i], widths[i]);
|
|
1495
2008
|
}
|
|
1496
2009
|
});
|
|
1497
|
-
|
|
1498
|
-
|
|
2010
|
+
if (cols) {
|
|
2011
|
+
for (let i = 0; i < cols.length && i < widths.length; i++) {
|
|
2012
|
+
cols[i].style.width = `${widths[i]}px`;
|
|
2013
|
+
}
|
|
2014
|
+
} else {
|
|
2015
|
+
for (let i = 0; i < bodyCells.length && i < widths.length; i++) {
|
|
2016
|
+
this._setCellWidth(bodyCells[i], widths[i]);
|
|
2017
|
+
}
|
|
1499
2018
|
}
|
|
1500
2019
|
headerTable.style.tableLayout = "fixed";
|
|
1501
2020
|
bodyTable.style.tableLayout = "fixed";
|
|
@@ -1793,7 +2312,12 @@ var DataTable = class {
|
|
|
1793
2312
|
headerTable.querySelectorAll("thead > tr").forEach((tr) => {
|
|
1794
2313
|
if (tr.children[i]) this._setCellWidth(tr.children[i], w);
|
|
1795
2314
|
});
|
|
1796
|
-
const
|
|
2315
|
+
const cols = this._cols();
|
|
2316
|
+
if (cols) {
|
|
2317
|
+
if (cols[i]) cols[i].style.width = `${w}px`;
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
const bodyRow = bodyTable && this._firstBodyRow();
|
|
1797
2321
|
if (bodyRow && bodyRow.children[i]) this._setCellWidth(bodyRow.children[i], w);
|
|
1798
2322
|
}
|
|
1799
2323
|
/** Size both tables so a widened column grows the table (→ horizontal
|
|
@@ -1842,10 +2366,12 @@ var DataTable = class {
|
|
|
1842
2366
|
/** Add a native `title` tooltip to any body cell whose text is
|
|
1843
2367
|
* clipped by its column width; remove ours once it fits again.
|
|
1844
2368
|
* Leaves caller-supplied titles (e.g. from renderCell) untouched. */
|
|
1845
|
-
_updateCellTooltips() {
|
|
2369
|
+
_updateCellTooltips(rows = null) {
|
|
1846
2370
|
const bodyTable = this._tableEl;
|
|
1847
2371
|
if (!bodyTable) return;
|
|
1848
|
-
bodyTable.querySelectorAll("tbody td")
|
|
2372
|
+
const cells = rows ? rows.flatMap((tr) => [...tr.children]) : bodyTable.querySelectorAll("tbody td");
|
|
2373
|
+
cells.forEach((td) => {
|
|
2374
|
+
if (td.parentElement?.classList.contains("twm-dt-spacer")) return;
|
|
1849
2375
|
const clipped = td.scrollWidth > td.clientWidth + 1;
|
|
1850
2376
|
if (clipped) {
|
|
1851
2377
|
if (!td.title) td.title = td.textContent;
|
|
@@ -2093,4 +2619,4 @@ export {
|
|
|
2093
2619
|
createRafResizeObserver,
|
|
2094
2620
|
DataTable
|
|
2095
2621
|
};
|
|
2096
|
-
//# sourceMappingURL=chunk-
|
|
2622
|
+
//# sourceMappingURL=chunk-CYTWMP2Y.js.map
|