flexdesk 0.10.0 → 0.11.1
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/README.md +190 -0
- package/css/base.css +2 -2
- package/css/flexdesk.css +48 -2
- package/css/tokens.css +4 -0
- package/dist/charts.js +2 -2
- package/dist/{chunk-H4LT43RS.js → chunk-5QPGYHBN.js} +2 -2
- package/dist/{chunk-JLCJWCRS.js → chunk-6CGH73BC.js} +451 -32
- package/dist/chunk-6CGH73BC.js.map +7 -0
- package/dist/{chunk-6NG7YVBG.js → chunk-6EZPPN4E.js} +134 -13
- package/dist/chunk-6EZPPN4E.js.map +7 -0
- package/dist/{chunk-WKTYKXWF.js → chunk-EHHTKD7B.js} +2 -2
- package/dist/flexdesk.css +48 -2
- package/dist/flow.js +2 -2
- package/dist/tiles.js +2 -2
- package/dist/tokens.css +4 -0
- package/dist/widgets.js +50 -5
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +2 -2
- package/package.json +1 -1
- package/src/ui/components/data_table.js +521 -31
- package/src/ui/components/modal.js +191 -12
- package/src/ui/notification_center.js +48 -1
- package/dist/chunk-6NG7YVBG.js.map +0 -7
- package/dist/chunk-JLCJWCRS.js.map +0 -7
- /package/dist/{chunk-H4LT43RS.js.map → chunk-5QPGYHBN.js.map} +0 -0
- /package/dist/{chunk-WKTYKXWF.js.map → chunk-EHHTKD7B.js.map} +0 -0
|
@@ -629,6 +629,16 @@ function compareSortKeys(a, b, asc) {
|
|
|
629
629
|
}
|
|
630
630
|
var AUTOSIZE_MAX_PX = 520;
|
|
631
631
|
var VIRTUAL_FALLBACK_VIEWPORT_PX = 1e3;
|
|
632
|
+
var COPY_MENU_HTML = `
|
|
633
|
+
<div class="twm-context-menu-item" data-action="copy-tsv">
|
|
634
|
+
<span class="material-symbols-outlined">content_copy</span>
|
|
635
|
+
<span class="label">Copy Row(s)</span>
|
|
636
|
+
</div>
|
|
637
|
+
<div class="twm-context-menu-item" data-action="copy-csv">
|
|
638
|
+
<span class="material-symbols-outlined">table</span>
|
|
639
|
+
<span class="label">Copy as CSV</span>
|
|
640
|
+
</div>
|
|
641
|
+
`;
|
|
632
642
|
var DataTable = class {
|
|
633
643
|
/**
|
|
634
644
|
* Create a DataTable instance
|
|
@@ -741,6 +751,18 @@ var DataTable = class {
|
|
|
741
751
|
keepWhileDetached: false,
|
|
742
752
|
filterKinds: null,
|
|
743
753
|
onFilterChange: null,
|
|
754
|
+
// STORY-0010. Off — or exactly the 0.9.1 behaviour — until set. The
|
|
755
|
+
// typedef says what each does.
|
|
756
|
+
contextMenuItems: null,
|
|
757
|
+
onContextMenuAction: null,
|
|
758
|
+
onRender: null,
|
|
759
|
+
keyboardNav: false,
|
|
760
|
+
onRowKeyDown: null,
|
|
761
|
+
persistPage: false,
|
|
762
|
+
// STORY-0012. Off — every column drawn, as in 0.9.1 — until set.
|
|
763
|
+
hiddenColumns: null,
|
|
764
|
+
columnChooser: false,
|
|
765
|
+
onColumnsChange: null,
|
|
744
766
|
...config
|
|
745
767
|
};
|
|
746
768
|
this._expanded = new Set((this.config.expandedRows || []).map((k) => String(k)));
|
|
@@ -752,6 +774,8 @@ var DataTable = class {
|
|
|
752
774
|
this._filterControls = /* @__PURE__ */ new Map();
|
|
753
775
|
this._filterCounts = /* @__PURE__ */ new Map();
|
|
754
776
|
this._pendingFilter = null;
|
|
777
|
+
this._hidden = /* @__PURE__ */ new Set();
|
|
778
|
+
this._setHidden(this.config.hiddenColumns);
|
|
755
779
|
this._activeKey = this.config.activeRow == null ? null : String(this.config.activeRow);
|
|
756
780
|
this._error = null;
|
|
757
781
|
this._virt = null;
|
|
@@ -776,6 +800,8 @@ var DataTable = class {
|
|
|
776
800
|
this._tableEl = null;
|
|
777
801
|
this._tbodyEl = null;
|
|
778
802
|
this._contextMenuEl = null;
|
|
803
|
+
this._contextMenuCtx = null;
|
|
804
|
+
this._navCursor = null;
|
|
779
805
|
this._filterDropdownEl = null;
|
|
780
806
|
this._filterDropdownCleanup = null;
|
|
781
807
|
this._colWidths = {};
|
|
@@ -847,6 +873,10 @@ var DataTable = class {
|
|
|
847
873
|
this._state.filters = new Map(blob.filters);
|
|
848
874
|
applied = true;
|
|
849
875
|
}
|
|
876
|
+
if (Array.isArray(blob.hidden)) {
|
|
877
|
+
this._setHidden(blob.hidden);
|
|
878
|
+
applied = true;
|
|
879
|
+
}
|
|
850
880
|
if (blob.colWidths && typeof blob.colWidths === "object") {
|
|
851
881
|
this._colWidths = {};
|
|
852
882
|
for (const [k, v] of Object.entries(blob.colWidths)) {
|
|
@@ -855,12 +885,17 @@ var DataTable = class {
|
|
|
855
885
|
this._colWidthsSig = this._colSig();
|
|
856
886
|
applied = true;
|
|
857
887
|
}
|
|
888
|
+
if (this.config.persistPage && Number.isInteger(blob.offset) && blob.offset >= 0) {
|
|
889
|
+
this._state.offset = blob.offset;
|
|
890
|
+
this._clampRestoredPage = true;
|
|
891
|
+
applied = true;
|
|
892
|
+
}
|
|
858
893
|
return applied;
|
|
859
894
|
}
|
|
860
895
|
/** Identity of the current column set — restored widths/sort are
|
|
861
896
|
* keyed by position, so a schema change invalidates them. */
|
|
862
897
|
_colSig() {
|
|
863
|
-
return (this.config.headers || []).join("") + (this.config.showRowNumbers ? "|#" : "");
|
|
898
|
+
return (this.config.headers || []).join("") + (this.config.showRowNumbers ? "|#" : "") + (this._hidden?.size ? `|-${this.getHiddenColumns().join(",")}` : "");
|
|
864
899
|
}
|
|
865
900
|
/** Snapshot the persistable slice of state to the project store.
|
|
866
901
|
* No-op unless `persistKey` is set. Debounced inside the store. */
|
|
@@ -870,9 +905,135 @@ var DataTable = class {
|
|
|
870
905
|
sortColumn: this._state.sortColumn,
|
|
871
906
|
sortAscending: this._state.sortAscending,
|
|
872
907
|
filters: [...this._state.filters.entries()],
|
|
873
|
-
colWidths: { ...this._colWidths }
|
|
908
|
+
colWidths: { ...this._colWidths },
|
|
909
|
+
// STORY-0010. Only for a table that asked: one that did not writes
|
|
910
|
+
// the 0.9.1 blob, key for key.
|
|
911
|
+
...this.config.persistPage ? { offset: this._state.offset } : {},
|
|
912
|
+
// STORY-0012. Only for a table that hides columns or lets them be
|
|
913
|
+
// hidden.
|
|
914
|
+
...this._columnsOn() ? { hidden: this.getHiddenColumns() } : {}
|
|
874
915
|
});
|
|
875
916
|
}
|
|
917
|
+
// ─────────────────────────────────────────────────────────────────
|
|
918
|
+
// STORY-0012 — columns shown and hidden
|
|
919
|
+
// ─────────────────────────────────────────────────────────────────
|
|
920
|
+
/** Whether this table hides columns at all (and so saves the set). */
|
|
921
|
+
_columnsOn() {
|
|
922
|
+
return Array.isArray(this.config.hiddenColumns) || !!this.config.columnChooser || !!this._columnsTouched;
|
|
923
|
+
}
|
|
924
|
+
/** Replace the hidden set: whole indices of existing columns, and never
|
|
925
|
+
* every column — the first one stays when nothing else would. A table
|
|
926
|
+
* with no headers yet keeps the set for the headers `setData` brings. */
|
|
927
|
+
_setHidden(list) {
|
|
928
|
+
const n = (this.config.headers || []).length;
|
|
929
|
+
this._hidden = new Set((Array.isArray(list) ? list : []).map(Number).filter((c) => Number.isInteger(c) && c >= 0 && (!n || c < n)));
|
|
930
|
+
if (n && this._hidden.size >= n) this._hidden.delete(0);
|
|
931
|
+
}
|
|
932
|
+
_isHidden(colIdx) {
|
|
933
|
+
return this._hidden.has(colIdx);
|
|
934
|
+
}
|
|
935
|
+
/** The cells a drawn row has: the shown columns, and the row number. */
|
|
936
|
+
_domColCount() {
|
|
937
|
+
const n = Math.max(0, (this.config.headers?.length || 0) - this._hidden.size);
|
|
938
|
+
return n + (this.config.showRowNumbers ? 1 : 0);
|
|
939
|
+
}
|
|
940
|
+
/** A data column's drawn index (row number included), or -1 when hidden. */
|
|
941
|
+
_domIdxOf(colIdx) {
|
|
942
|
+
if (this._isHidden(colIdx)) return -1;
|
|
943
|
+
let dom = this.config.showRowNumbers ? 1 : 0;
|
|
944
|
+
for (const h of this._hidden) if (h < colIdx) dom--;
|
|
945
|
+
return dom + colIdx;
|
|
946
|
+
}
|
|
947
|
+
/** A drawn cell's data column, or null for the row number. */
|
|
948
|
+
_dataIdxOfDom(domIdx) {
|
|
949
|
+
let i = domIdx - (this.config.showRowNumbers ? 1 : 0);
|
|
950
|
+
if (i < 0) return null;
|
|
951
|
+
const n = this.config.headers?.length || 0;
|
|
952
|
+
for (let c = 0; c < n; c++) {
|
|
953
|
+
if (this._isHidden(c)) continue;
|
|
954
|
+
if (i === 0) return c;
|
|
955
|
+
i--;
|
|
956
|
+
}
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
/** The columns not drawn, by data index, ascending. */
|
|
960
|
+
getHiddenColumns() {
|
|
961
|
+
return [...this._hidden || []].sort((a, b) => a - b);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Show (`on` true) or hide a column. The last column shown is not hidden.
|
|
965
|
+
* The widths of the other columns stay as they were; a column shown again
|
|
966
|
+
* is measured. Saved under `persistKey`, and `onColumnsChange` is told.
|
|
967
|
+
*
|
|
968
|
+
* @param {number} colIdx
|
|
969
|
+
* @param {boolean} [on=true]
|
|
970
|
+
* @returns {boolean} whether the set changed
|
|
971
|
+
*/
|
|
972
|
+
setColumnVisible(colIdx, on = true) {
|
|
973
|
+
const n = this.config.headers?.length || 0;
|
|
974
|
+
if (!Number.isInteger(colIdx) || colIdx < 0 || colIdx >= n) return false;
|
|
975
|
+
if (!!on === !this._isHidden(colIdx)) return false;
|
|
976
|
+
if (!on && n - this._hidden.size <= 1) return false;
|
|
977
|
+
this._columnsTouched = true;
|
|
978
|
+
const next = new Set(this._hidden);
|
|
979
|
+
if (on) next.delete(colIdx);
|
|
980
|
+
else next.add(colIdx);
|
|
981
|
+
this._applyHidden(next);
|
|
982
|
+
return true;
|
|
983
|
+
}
|
|
984
|
+
/** Swap in a new hidden set, carrying the drawn widths of the columns
|
|
985
|
+
* that stay drawn to their new places, and tell whoever listens. */
|
|
986
|
+
_applyHidden(next) {
|
|
987
|
+
const before = this.getHiddenColumns();
|
|
988
|
+
const n = this.config.headers?.length || 0;
|
|
989
|
+
const old = this._colWidths;
|
|
990
|
+
const kept = this._colWidthsSig === this._colSig();
|
|
991
|
+
const domBefore = [];
|
|
992
|
+
for (let c = 0; c < n; c++) domBefore[c] = this._domIdxOf(c);
|
|
993
|
+
this._setHidden([...next]);
|
|
994
|
+
if (kept) {
|
|
995
|
+
const widths = {};
|
|
996
|
+
if (this.config.showRowNumbers && old[0] != null) widths[0] = old[0];
|
|
997
|
+
for (let c = 0; c < n; c++) {
|
|
998
|
+
const from = domBefore[c];
|
|
999
|
+
const to = this._domIdxOf(c);
|
|
1000
|
+
if (from >= 0 && to >= 0 && old[from] != null) widths[to] = old[from];
|
|
1001
|
+
}
|
|
1002
|
+
this._colWidths = widths;
|
|
1003
|
+
this._colWidthsSig = this._colSig();
|
|
1004
|
+
}
|
|
1005
|
+
const after = this.getHiddenColumns();
|
|
1006
|
+
if (before.join() === after.join()) return;
|
|
1007
|
+
this._savePersisted();
|
|
1008
|
+
if (this._wrapperEl) this.render();
|
|
1009
|
+
try {
|
|
1010
|
+
this.config.onColumnsChange?.(after);
|
|
1011
|
+
} catch (err) {
|
|
1012
|
+
console.error("[DataTable] onColumnsChange threw", err);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
/** `columnChooser`: the header's right-click menu, one checkable item a
|
|
1016
|
+
* column. */
|
|
1017
|
+
_showColumnChooser(clientX, clientY) {
|
|
1018
|
+
const headers = this.config.headers || [];
|
|
1019
|
+
const lastShown = headers.length - this._hidden.size <= 1;
|
|
1020
|
+
const items = headers.map((h, c) => {
|
|
1021
|
+
const shown = !this._isHidden(c);
|
|
1022
|
+
return {
|
|
1023
|
+
label: String(h ?? ""),
|
|
1024
|
+
icon: shown ? "check" : "",
|
|
1025
|
+
checked: shown,
|
|
1026
|
+
disabled: shown && lastShown,
|
|
1027
|
+
title: shown && lastShown ? "The last column shown cannot be hidden" : "",
|
|
1028
|
+
onChoose: () => this.setColumnVisible(c, !shown)
|
|
1029
|
+
};
|
|
1030
|
+
});
|
|
1031
|
+
this._showContextMenu(clientX, clientY, { ctx: null, items, copy: false });
|
|
1032
|
+
}
|
|
1033
|
+
/** STORY-0010 `persistPage`: the client page changed, so save it. */
|
|
1034
|
+
_savePage() {
|
|
1035
|
+
if (this.config.persistPage) this._savePersisted();
|
|
1036
|
+
}
|
|
876
1037
|
/**
|
|
877
1038
|
* Update data and re-render
|
|
878
1039
|
* @param {Object} updates - Partial config updates (headers, rows, etc.)
|
|
@@ -897,6 +1058,20 @@ var DataTable = class {
|
|
|
897
1058
|
this._destroyFilterControls();
|
|
898
1059
|
}
|
|
899
1060
|
Object.assign(this.config, updates);
|
|
1061
|
+
if (Object.prototype.hasOwnProperty.call(updates, "hiddenColumns")) {
|
|
1062
|
+
const before = this.getHiddenColumns().join();
|
|
1063
|
+
this._setHidden(updates.hiddenColumns);
|
|
1064
|
+
if (this.getHiddenColumns().join() !== before) {
|
|
1065
|
+
this._savePersisted();
|
|
1066
|
+
try {
|
|
1067
|
+
this.config.onColumnsChange?.(this.getHiddenColumns());
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
console.error("[DataTable] onColumnsChange threw", err);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
} else if (updates.headers && this._hidden.size) {
|
|
1073
|
+
this._setHidden([...this._hidden]);
|
|
1074
|
+
}
|
|
900
1075
|
if (updates.rows) {
|
|
901
1076
|
this._state.selected.clear();
|
|
902
1077
|
this._state.anchorIndex = null;
|
|
@@ -1060,6 +1235,7 @@ var DataTable = class {
|
|
|
1060
1235
|
const rowCount = this._getProcessedRows().length;
|
|
1061
1236
|
const maxPage = Math.ceil(rowCount / this.config.pageSize) - 1;
|
|
1062
1237
|
this._state.offset = Math.max(0, Math.min(page, maxPage)) * this.config.pageSize;
|
|
1238
|
+
this._savePage();
|
|
1063
1239
|
this.render();
|
|
1064
1240
|
}
|
|
1065
1241
|
/**
|
|
@@ -1074,6 +1250,7 @@ var DataTable = class {
|
|
|
1074
1250
|
const rowCount = this._getProcessedRows().length;
|
|
1075
1251
|
const idx = Math.max(0, Math.min(rowIndex, rowCount - 1));
|
|
1076
1252
|
this._state.offset = Math.floor(idx / this.config.pageSize) * this.config.pageSize;
|
|
1253
|
+
this._savePage();
|
|
1077
1254
|
this.render();
|
|
1078
1255
|
}
|
|
1079
1256
|
/**
|
|
@@ -1136,6 +1313,41 @@ var DataTable = class {
|
|
|
1136
1313
|
getFilteredRowCount() {
|
|
1137
1314
|
return this._getProcessedRows().length;
|
|
1138
1315
|
}
|
|
1316
|
+
/**
|
|
1317
|
+
* STORY-0010. Give the table the keyboard focus (the body, where the arrow
|
|
1318
|
+
* keys and `onRowKeyDown` listen) without scrolling the page. False when
|
|
1319
|
+
* there is nothing drawn to focus.
|
|
1320
|
+
* @returns {boolean}
|
|
1321
|
+
*/
|
|
1322
|
+
focus() {
|
|
1323
|
+
if (!this._tableEl) return false;
|
|
1324
|
+
try {
|
|
1325
|
+
this._tableEl.focus({ preventScroll: true });
|
|
1326
|
+
} catch (_) {
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
return true;
|
|
1330
|
+
}
|
|
1331
|
+
/** STORY-0010 `keyboardNav`: the original indices of the page shown, in
|
|
1332
|
+
* the order shown — every row held, when the page is a server's or there
|
|
1333
|
+
* are no pages. */
|
|
1334
|
+
_pageOrder() {
|
|
1335
|
+
this._getProcessedRows();
|
|
1336
|
+
const order = this._processedIndexMap || [];
|
|
1337
|
+
if (this._isServerSide() || !this.config.pagination || this._pagingConfig()) return order;
|
|
1338
|
+
return order.slice(this._state.offset, this._state.offset + this.config.pageSize);
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* STORY-0010. The rows the table shows, as row arrays, in the order shown:
|
|
1342
|
+
* filtered and sorted, across EVERY page — not only the one on screen. With
|
|
1343
|
+
* server-side paging (`onPageChange`, `dataSource`) they are the page the
|
|
1344
|
+
* consumer or the source gave, which is all the table holds. A copy:
|
|
1345
|
+
* changing it changes nothing in the table.
|
|
1346
|
+
* @returns {Array<Array>}
|
|
1347
|
+
*/
|
|
1348
|
+
getDisplayedRows() {
|
|
1349
|
+
return this._getProcessedRows().slice();
|
|
1350
|
+
}
|
|
1139
1351
|
/**
|
|
1140
1352
|
* Render the table
|
|
1141
1353
|
*/
|
|
@@ -1160,6 +1372,14 @@ var DataTable = class {
|
|
|
1160
1372
|
if (this._columnTypes.length === 0) {
|
|
1161
1373
|
this._columnTypes = this._detectColumnTypes();
|
|
1162
1374
|
}
|
|
1375
|
+
if (this._clampRestoredPage && rows.length > 0) {
|
|
1376
|
+
this._clampRestoredPage = false;
|
|
1377
|
+
if (!this._isServerSide() && !this._pagingConfig()) {
|
|
1378
|
+
const size = this.config.pageSize;
|
|
1379
|
+
const last = Math.max(0, Math.ceil(this._getProcessedRows().length / size) - 1) * size;
|
|
1380
|
+
this._state.offset = Math.min(Math.floor(this._state.offset / size) * size, last);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1163
1383
|
this._wrapperEl = document.createElement("div");
|
|
1164
1384
|
this._wrapperEl.className = "twm-data-table-component" + (this.config.mode === "compact" ? " twm-data-table-component--compact" : "");
|
|
1165
1385
|
const fit = this._fitsContent();
|
|
@@ -1201,6 +1421,7 @@ var DataTable = class {
|
|
|
1201
1421
|
tr.appendChild(th);
|
|
1202
1422
|
}
|
|
1203
1423
|
headers.forEach((h, colIdx) => {
|
|
1424
|
+
if (this._isHidden(colIdx)) return;
|
|
1204
1425
|
const th = document.createElement("th");
|
|
1205
1426
|
th.className = this._columnTypes[colIdx] || "text";
|
|
1206
1427
|
th.textContent = h;
|
|
@@ -1215,7 +1436,7 @@ var DataTable = class {
|
|
|
1215
1436
|
const bodyTable = document.createElement("table");
|
|
1216
1437
|
bodyTable.className = headerTable.className;
|
|
1217
1438
|
const tbody = document.createElement("tbody");
|
|
1218
|
-
tbody.appendChild(this._buildEmptyRow(
|
|
1439
|
+
tbody.appendChild(this._buildEmptyRow(this._domColCount(), emptyMessage));
|
|
1219
1440
|
bodyTable.appendChild(tbody);
|
|
1220
1441
|
tableWrap.appendChild(bodyTable);
|
|
1221
1442
|
this._wrapperEl.appendChild(tableWrap);
|
|
@@ -1240,13 +1461,22 @@ var DataTable = class {
|
|
|
1240
1461
|
this._headerTableEl = null;
|
|
1241
1462
|
}
|
|
1242
1463
|
if (keepFilterRow && this._tbodyEl) {
|
|
1243
|
-
|
|
1244
|
-
this._tbodyEl.appendChild(this._buildEmptyRow(n, emptyMessage));
|
|
1464
|
+
this._tbodyEl.appendChild(this._buildEmptyRow(this._domColCount(), emptyMessage));
|
|
1245
1465
|
}
|
|
1246
1466
|
tableWrap.appendChild(fullTable);
|
|
1247
1467
|
this._wrapperEl.appendChild(tableWrap);
|
|
1248
1468
|
}
|
|
1249
1469
|
this._tableWrapEl = tableWrap;
|
|
1470
|
+
if (this.config.columnChooser && this._headerTableEl) {
|
|
1471
|
+
const head = this._headerTableEl;
|
|
1472
|
+
const onHeadMenu = (ev) => {
|
|
1473
|
+
ev.preventDefault();
|
|
1474
|
+
ev.stopPropagation();
|
|
1475
|
+
setTimeout(() => this._showColumnChooser(ev.clientX, ev.clientY), 0);
|
|
1476
|
+
};
|
|
1477
|
+
head.addEventListener("contextmenu", onHeadMenu);
|
|
1478
|
+
this._disposers.push(() => head.removeEventListener("contextmenu", onHeadMenu));
|
|
1479
|
+
}
|
|
1250
1480
|
if (this._paginationEl && this.config.pagerPosition === "bottom") {
|
|
1251
1481
|
this._paginationEl.classList.add("twm-pagination-controls--bottom");
|
|
1252
1482
|
this._paginationEl.style.borderBottom = "0";
|
|
@@ -1304,7 +1534,7 @@ var DataTable = class {
|
|
|
1304
1534
|
this._installHeaderScrollSync();
|
|
1305
1535
|
if (rows.length > 0) this._installColumnResizers();
|
|
1306
1536
|
}
|
|
1307
|
-
if (rows.length > 0 && (this.config.selectable || this.config.copyable || !this.config.readonly || typeof this.config.onRowActivate === "function" || typeof this.config.rowDetail === "function")) {
|
|
1537
|
+
if (rows.length > 0 && (this.config.selectable || this.config.copyable || !this.config.readonly || typeof this.config.onRowActivate === "function" || typeof this.config.rowDetail === "function" || typeof this.config.onRowKeyDown === "function")) {
|
|
1308
1538
|
this._installInteractions();
|
|
1309
1539
|
}
|
|
1310
1540
|
if (activeFilterColIdx !== null) {
|
|
@@ -1327,6 +1557,13 @@ var DataTable = class {
|
|
|
1327
1557
|
}
|
|
1328
1558
|
if (this._following && !this._followTimer) this._armFollow();
|
|
1329
1559
|
}
|
|
1560
|
+
if (typeof this.config.onRender === "function") {
|
|
1561
|
+
try {
|
|
1562
|
+
this.config.onRender(this);
|
|
1563
|
+
} catch (err) {
|
|
1564
|
+
console.error("[DataTable] onRender threw", err);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1330
1567
|
}
|
|
1331
1568
|
/**
|
|
1332
1569
|
* Copy selected rows to clipboard
|
|
@@ -1339,11 +1576,12 @@ var DataTable = class {
|
|
|
1339
1576
|
return;
|
|
1340
1577
|
}
|
|
1341
1578
|
const { headers, rows } = this.config;
|
|
1342
|
-
const
|
|
1579
|
+
const shown = (_, colIdx) => !this._isHidden(colIdx);
|
|
1580
|
+
const matrix = [headers.filter(shown)];
|
|
1343
1581
|
for (const idx of indices) {
|
|
1344
1582
|
const row = rows[idx];
|
|
1345
1583
|
if (row) {
|
|
1346
|
-
matrix.push(row.map((val, colIdx) => this._formatValue(val, colIdx)));
|
|
1584
|
+
matrix.push(row.map((val, colIdx) => this._formatValue(val, colIdx)).filter(shown));
|
|
1347
1585
|
}
|
|
1348
1586
|
}
|
|
1349
1587
|
const separator = format === "csv" ? "," : " ";
|
|
@@ -1374,11 +1612,12 @@ var DataTable = class {
|
|
|
1374
1612
|
this._notify("Download", "No data to download.", "warn");
|
|
1375
1613
|
return;
|
|
1376
1614
|
}
|
|
1377
|
-
const
|
|
1615
|
+
const shown = (_, colIdx) => !this._isHidden(colIdx);
|
|
1616
|
+
const lines = [headers.filter(shown).join(",")];
|
|
1378
1617
|
for (const row of rows) {
|
|
1379
1618
|
lines.push(row.map(
|
|
1380
1619
|
(val, colIdx) => this._csvEscape(this._formatValue(val, colIdx))
|
|
1381
|
-
).join(","));
|
|
1620
|
+
).filter(shown).join(","));
|
|
1382
1621
|
}
|
|
1383
1622
|
const csv = lines.join("\n");
|
|
1384
1623
|
const dialogs = this.config.host?.dialogs;
|
|
@@ -1689,6 +1928,7 @@ var DataTable = class {
|
|
|
1689
1928
|
tr.appendChild(th);
|
|
1690
1929
|
}
|
|
1691
1930
|
headers.forEach((header, colIdx) => {
|
|
1931
|
+
if (this._isHidden(colIdx)) return;
|
|
1692
1932
|
const th = document.createElement("th");
|
|
1693
1933
|
th.className = "data-table__filter-cell";
|
|
1694
1934
|
const control = this._filterControlFor(colIdx, header);
|
|
@@ -2008,7 +2248,7 @@ var DataTable = class {
|
|
|
2008
2248
|
* rebuilt its box: the words, and the debounce that will apply them. */
|
|
2009
2249
|
_resumePendingFilter({ colIdx, value }) {
|
|
2010
2250
|
const filterRow = this._wrapperEl?.querySelector(".twm-data-table__filter-row");
|
|
2011
|
-
const cell = filterRow?.children[this.
|
|
2251
|
+
const cell = filterRow?.children[this._domIdxOf(colIdx)];
|
|
2012
2252
|
const input = cell?.querySelector(".twm-data-table__filter-input");
|
|
2013
2253
|
if (input && input.value !== value) {
|
|
2014
2254
|
const focused = input.ownerDocument?.activeElement === input;
|
|
@@ -2028,12 +2268,12 @@ var DataTable = class {
|
|
|
2028
2268
|
if (!row) return null;
|
|
2029
2269
|
const cells = Array.from(row.children);
|
|
2030
2270
|
const idx = cells.indexOf(cell);
|
|
2031
|
-
return this.config.showRowNumbers ? idx - 1 : idx;
|
|
2271
|
+
return this._hidden.size ? this._dataIdxOfDom(idx) : this.config.showRowNumbers ? idx - 1 : idx;
|
|
2032
2272
|
}
|
|
2033
2273
|
_restoreFilterFocus(colIdx) {
|
|
2034
2274
|
const filterRow = this._wrapperEl?.querySelector(".twm-data-table__filter-row");
|
|
2035
2275
|
if (!filterRow) return;
|
|
2036
|
-
const cellIdx = this.
|
|
2276
|
+
const cellIdx = this._domIdxOf(colIdx);
|
|
2037
2277
|
const cell = filterRow.children[cellIdx];
|
|
2038
2278
|
const input = cell?.querySelector(".twm-data-table__filter-input");
|
|
2039
2279
|
if (input) {
|
|
@@ -2146,6 +2386,7 @@ var DataTable = class {
|
|
|
2146
2386
|
this._awaitWithSpinner(onPageChange(newOffset, pageSize));
|
|
2147
2387
|
} else {
|
|
2148
2388
|
this._state.offset = newOffset;
|
|
2389
|
+
this._savePage();
|
|
2149
2390
|
this.render();
|
|
2150
2391
|
}
|
|
2151
2392
|
};
|
|
@@ -2506,6 +2747,7 @@ var DataTable = class {
|
|
|
2506
2747
|
headerRow.appendChild(th);
|
|
2507
2748
|
}
|
|
2508
2749
|
headers.forEach((header, colIdx) => {
|
|
2750
|
+
if (this._isHidden(colIdx)) return;
|
|
2509
2751
|
const th = document.createElement("th");
|
|
2510
2752
|
th.className = this._columnTypes[colIdx] || "text";
|
|
2511
2753
|
if (sortable) {
|
|
@@ -2652,7 +2894,7 @@ var DataTable = class {
|
|
|
2652
2894
|
* any width already pinned for it, so a re-render starts at the widths it
|
|
2653
2895
|
* had rather than at a content-sized frame that then snaps. */
|
|
2654
2896
|
_createColgroup() {
|
|
2655
|
-
const n =
|
|
2897
|
+
const n = this._domColCount();
|
|
2656
2898
|
const colgroup = document.createElement("colgroup");
|
|
2657
2899
|
for (let i = 0; i < n; i++) {
|
|
2658
2900
|
const col = document.createElement("col");
|
|
@@ -2674,7 +2916,7 @@ var DataTable = class {
|
|
|
2674
2916
|
tr.className = `twm-dt-spacer twm-dt-spacer--${edge}`;
|
|
2675
2917
|
tr.setAttribute("aria-hidden", "true");
|
|
2676
2918
|
const td = document.createElement("td");
|
|
2677
|
-
td.colSpan = Math.max(1,
|
|
2919
|
+
td.colSpan = Math.max(1, this._domColCount());
|
|
2678
2920
|
td.style.cssText = "padding:0;border:0;height:0;min-width:0;max-width:none;";
|
|
2679
2921
|
tr.appendChild(td);
|
|
2680
2922
|
return tr;
|
|
@@ -3076,6 +3318,7 @@ var DataTable = class {
|
|
|
3076
3318
|
const gap = diff?.status === "gap";
|
|
3077
3319
|
const changedCells = diff?.cells ? diff.cells instanceof Set ? diff.cells : new Set(diff.cells) : null;
|
|
3078
3320
|
for (let colIdx = 0; colIdx < row.length; colIdx++) {
|
|
3321
|
+
if (this._isHidden(colIdx)) continue;
|
|
3079
3322
|
const td = document.createElement("td");
|
|
3080
3323
|
td.className = this._columnTypes[colIdx] || "text";
|
|
3081
3324
|
const value = row[colIdx];
|
|
@@ -3263,7 +3506,7 @@ var DataTable = class {
|
|
|
3263
3506
|
for (const p of pieces) parent.insertBefore(p, ref);
|
|
3264
3507
|
}
|
|
3265
3508
|
_buildDetailPieces(tr, key) {
|
|
3266
|
-
const n =
|
|
3509
|
+
const n = this._domColCount();
|
|
3267
3510
|
const detailTr = document.createElement("tr");
|
|
3268
3511
|
detailTr.className = "twm-dt-detail-row";
|
|
3269
3512
|
const td = document.createElement("td");
|
|
@@ -3997,6 +4240,7 @@ var DataTable = class {
|
|
|
3997
4240
|
return;
|
|
3998
4241
|
}
|
|
3999
4242
|
this._state.offset = newOffset;
|
|
4243
|
+
this._savePage();
|
|
4000
4244
|
this.render();
|
|
4001
4245
|
};
|
|
4002
4246
|
const el = document.createElement("div");
|
|
@@ -4202,7 +4446,7 @@ var DataTable = class {
|
|
|
4202
4446
|
const v = textBox && value != null && typeof value !== "string" ? String(value) : value;
|
|
4203
4447
|
if (textBox) {
|
|
4204
4448
|
const filterRow = this._wrapperEl?.querySelector(".twm-data-table__filter-row");
|
|
4205
|
-
const cell = filterRow?.children[this.
|
|
4449
|
+
const cell = filterRow?.children[this._domIdxOf(colIdx)];
|
|
4206
4450
|
const input = cell?.querySelector(".twm-data-table__filter-input");
|
|
4207
4451
|
if (input) input.value = v || "";
|
|
4208
4452
|
}
|
|
@@ -5249,6 +5493,7 @@ var DataTable = class {
|
|
|
5249
5493
|
_installInteractions() {
|
|
5250
5494
|
if (!this._tbodyEl || !this._tableEl) return;
|
|
5251
5495
|
const { selectable, copyable } = this.config;
|
|
5496
|
+
const customMenu = this.config.contextMenuItems != null;
|
|
5252
5497
|
const tbody = this._tbodyEl;
|
|
5253
5498
|
const table = this._tableEl;
|
|
5254
5499
|
if (selectable) {
|
|
@@ -5287,6 +5532,58 @@ var DataTable = class {
|
|
|
5287
5532
|
tbody.addEventListener("click", handleRowClick);
|
|
5288
5533
|
this._disposers.push(() => tbody.removeEventListener("click", handleRowClick));
|
|
5289
5534
|
}
|
|
5535
|
+
if (typeof this.config.onRowKeyDown === "function") {
|
|
5536
|
+
const onRowKey = (ev) => {
|
|
5537
|
+
if (ev.isComposing || isRowControl(ev.target, table)) return;
|
|
5538
|
+
const tr = this._currentRowEl();
|
|
5539
|
+
let handled = false;
|
|
5540
|
+
try {
|
|
5541
|
+
handled = this.config.onRowKeyDown(tr ? tr.__rowIndex : null, tr ? tr.__row : null, ev) === true;
|
|
5542
|
+
} catch (err) {
|
|
5543
|
+
console.error("[DataTable] onRowKeyDown threw", err);
|
|
5544
|
+
}
|
|
5545
|
+
if (handled) {
|
|
5546
|
+
ev.preventDefault();
|
|
5547
|
+
ev.stopImmediatePropagation();
|
|
5548
|
+
}
|
|
5549
|
+
};
|
|
5550
|
+
table.addEventListener("keydown", onRowKey);
|
|
5551
|
+
this._disposers.push(() => table.removeEventListener("keydown", onRowKey));
|
|
5552
|
+
}
|
|
5553
|
+
if (this.config.keyboardNav && selectable) {
|
|
5554
|
+
const NAV_KEYS = ["ArrowDown", "ArrowUp", "Home", "End"];
|
|
5555
|
+
const onNavKey = (ev) => {
|
|
5556
|
+
if (!NAV_KEYS.includes(ev.key) || ev.ctrlKey || ev.metaKey || ev.altKey) return;
|
|
5557
|
+
if (isRowControl(ev.target, table)) return;
|
|
5558
|
+
const order = this._pageOrder();
|
|
5559
|
+
if (!order.length) return;
|
|
5560
|
+
ev.preventDefault();
|
|
5561
|
+
const selected = this._state.selected;
|
|
5562
|
+
const cur = this._navCursor != null && selected.has(this._navCursor) ? this._navCursor : this._state.anchorIndex;
|
|
5563
|
+
let pos = order.indexOf(cur);
|
|
5564
|
+
if (ev.key === "Home") pos = 0;
|
|
5565
|
+
else if (ev.key === "End") pos = order.length - 1;
|
|
5566
|
+
else if (pos === -1) pos = ev.key === "ArrowDown" ? 0 : order.length - 1;
|
|
5567
|
+
else pos = Math.max(0, Math.min(order.length - 1, pos + (ev.key === "ArrowDown" ? 1 : -1)));
|
|
5568
|
+
const idx = order[pos];
|
|
5569
|
+
this._navCursor = idx;
|
|
5570
|
+
const anchorPos = order.indexOf(this._state.anchorIndex);
|
|
5571
|
+
if (ev.shiftKey && selectable !== "single" && anchorPos !== -1) {
|
|
5572
|
+
const [a, b] = anchorPos <= pos ? [anchorPos, pos] : [pos, anchorPos];
|
|
5573
|
+
selected.clear();
|
|
5574
|
+
for (let i = a; i <= b; i++) selected.add(order[i]);
|
|
5575
|
+
} else {
|
|
5576
|
+
selected.clear();
|
|
5577
|
+
selected.add(idx);
|
|
5578
|
+
this._state.anchorIndex = idx;
|
|
5579
|
+
}
|
|
5580
|
+
this._updateRowSelection();
|
|
5581
|
+
this._notifySelectionChange();
|
|
5582
|
+
this.scrollToRow(idx);
|
|
5583
|
+
};
|
|
5584
|
+
table.addEventListener("keydown", onNavKey);
|
|
5585
|
+
this._disposers.push(() => table.removeEventListener("keydown", onNavKey));
|
|
5586
|
+
}
|
|
5290
5587
|
if (typeof this.config.onRowActivate === "function") {
|
|
5291
5588
|
this._installActivation(tbody, table);
|
|
5292
5589
|
}
|
|
@@ -5304,7 +5601,7 @@ var DataTable = class {
|
|
|
5304
5601
|
table.addEventListener("keydown", onDetailKey);
|
|
5305
5602
|
this._disposers.push(() => table.removeEventListener("keydown", onDetailKey));
|
|
5306
5603
|
}
|
|
5307
|
-
if (copyable) {
|
|
5604
|
+
if (copyable || customMenu) {
|
|
5308
5605
|
const handleContextMenu = (event) => {
|
|
5309
5606
|
const rowEl = event.target.closest("tr");
|
|
5310
5607
|
if (!rowEl) {
|
|
@@ -5315,9 +5612,19 @@ var DataTable = class {
|
|
|
5315
5612
|
this._hideContextMenu();
|
|
5316
5613
|
return;
|
|
5317
5614
|
}
|
|
5615
|
+
const idx = rowEl.__rowIndex;
|
|
5616
|
+
let custom = null;
|
|
5617
|
+
if (customMenu) {
|
|
5618
|
+
const selection = idx !== void 0 && !this._state.selected.has(idx) ? [idx] : this.getSelection();
|
|
5619
|
+
const ctx = this._contextMenuCtxFor(rowEl, event.target, selection);
|
|
5620
|
+
custom = { ctx, items: this._customMenuItems(ctx) };
|
|
5621
|
+
}
|
|
5622
|
+
if (!copyable && !custom.items.length) {
|
|
5623
|
+
this._hideContextMenu();
|
|
5624
|
+
return;
|
|
5625
|
+
}
|
|
5318
5626
|
event.preventDefault();
|
|
5319
5627
|
event.stopPropagation();
|
|
5320
|
-
const idx = rowEl.__rowIndex;
|
|
5321
5628
|
if (idx !== void 0 && !this._state.selected.has(idx)) {
|
|
5322
5629
|
this._state.selected.clear();
|
|
5323
5630
|
this._state.selected.add(idx);
|
|
@@ -5329,10 +5636,12 @@ var DataTable = class {
|
|
|
5329
5636
|
table.focus({ preventScroll: true });
|
|
5330
5637
|
} catch (_) {
|
|
5331
5638
|
}
|
|
5332
|
-
setTimeout(() => this._showContextMenu(event.clientX, event.clientY), 0);
|
|
5639
|
+
setTimeout(() => this._showContextMenu(event.clientX, event.clientY, custom), 0);
|
|
5333
5640
|
};
|
|
5334
5641
|
tbody.addEventListener("contextmenu", handleContextMenu);
|
|
5335
5642
|
this._disposers.push(() => tbody.removeEventListener("contextmenu", handleContextMenu));
|
|
5643
|
+
}
|
|
5644
|
+
if (copyable) {
|
|
5336
5645
|
const handleKeyDown = (event) => {
|
|
5337
5646
|
const ctrlLike = event.ctrlKey || event.metaKey;
|
|
5338
5647
|
if (ctrlLike && !event.altKey) {
|
|
@@ -5389,20 +5698,28 @@ var DataTable = class {
|
|
|
5389
5698
|
menu.style.display = "none";
|
|
5390
5699
|
menu.style.position = "fixed";
|
|
5391
5700
|
menu.style.zIndex = "10001";
|
|
5392
|
-
menu.innerHTML =
|
|
5393
|
-
<div class="twm-context-menu-item" data-action="copy-tsv">
|
|
5394
|
-
<span class="material-symbols-outlined">content_copy</span>
|
|
5395
|
-
<span class="label">Copy Row(s)</span>
|
|
5396
|
-
</div>
|
|
5397
|
-
<div class="twm-context-menu-item" data-action="copy-csv">
|
|
5398
|
-
<span class="material-symbols-outlined">table</span>
|
|
5399
|
-
<span class="label">Copy as CSV</span>
|
|
5400
|
-
</div>
|
|
5401
|
-
`;
|
|
5701
|
+
menu.innerHTML = COPY_MENU_HTML;
|
|
5402
5702
|
document.body.appendChild(menu);
|
|
5403
5703
|
menu.addEventListener("click", (event) => {
|
|
5404
5704
|
const item = event.target.closest(".twm-context-menu-item");
|
|
5405
5705
|
if (!item || item.classList.contains("disabled")) return;
|
|
5706
|
+
if (item.__cmItem) {
|
|
5707
|
+
event.stopPropagation();
|
|
5708
|
+
event.preventDefault();
|
|
5709
|
+
const ctx = this._contextMenuCtx;
|
|
5710
|
+
this._hideContextMenu();
|
|
5711
|
+
if (typeof item.__cmItem.onChoose === "function") {
|
|
5712
|
+
item.__cmItem.onChoose();
|
|
5713
|
+
return;
|
|
5714
|
+
}
|
|
5715
|
+
if (!ctx || typeof this.config.onContextMenuAction !== "function") return;
|
|
5716
|
+
try {
|
|
5717
|
+
this.config.onContextMenuAction(item.__cmItem.action, ctx.rowIdx, ctx.row, ctx);
|
|
5718
|
+
} catch (err) {
|
|
5719
|
+
console.error("[DataTable] onContextMenuAction threw", err);
|
|
5720
|
+
}
|
|
5721
|
+
return;
|
|
5722
|
+
}
|
|
5406
5723
|
const action = item.getAttribute("data-action");
|
|
5407
5724
|
if (action === "copy-tsv") this.copyToClipboard("tsv");
|
|
5408
5725
|
else if (action === "copy-csv") this.copyToClipboard("csv");
|
|
@@ -5460,11 +5777,13 @@ var DataTable = class {
|
|
|
5460
5777
|
}
|
|
5461
5778
|
this._disarmContextMenuGlobals();
|
|
5462
5779
|
}
|
|
5463
|
-
_showContextMenu(clientX, clientY) {
|
|
5780
|
+
_showContextMenu(clientX, clientY, custom = null) {
|
|
5464
5781
|
const menu = this._ensureContextMenu();
|
|
5782
|
+
this._drawContextMenuItems(menu, custom);
|
|
5465
5783
|
this._armContextMenuGlobals();
|
|
5466
5784
|
const hasSelection = this._state.selected.size > 0;
|
|
5467
5785
|
menu.querySelectorAll(".twm-context-menu-item").forEach((item) => {
|
|
5786
|
+
if (item.__cmItem) return;
|
|
5468
5787
|
item.classList.toggle("disabled", !hasSelection);
|
|
5469
5788
|
item.style.pointerEvents = hasSelection ? "" : "none";
|
|
5470
5789
|
});
|
|
@@ -5481,6 +5800,106 @@ var DataTable = class {
|
|
|
5481
5800
|
menu.style.left = `${left}px`;
|
|
5482
5801
|
menu.style.top = `${top}px`;
|
|
5483
5802
|
}
|
|
5803
|
+
/**
|
|
5804
|
+
* STORY-0010. The `ctx` handed to `contextMenuItems` and, later, to
|
|
5805
|
+
* `onContextMenuAction`: the right-clicked row and cell, and `selection`
|
|
5806
|
+
* (original indices, ascending) with its rows 1:1.
|
|
5807
|
+
*/
|
|
5808
|
+
_contextMenuCtxFor(rowEl, target, selection) {
|
|
5809
|
+
const rows = this.config.rows || [];
|
|
5810
|
+
const rowIdx = rowEl.__rowIndex === void 0 ? null : rowEl.__rowIndex;
|
|
5811
|
+
const row = rowIdx === null ? null : rows[rowIdx] ?? null;
|
|
5812
|
+
let colIdx = null;
|
|
5813
|
+
const cellEl = target?.closest?.("td");
|
|
5814
|
+
if (cellEl && cellEl.parentNode === rowEl) {
|
|
5815
|
+
const domIdx = Array.prototype.indexOf.call(rowEl.children, cellEl);
|
|
5816
|
+
const dataIdx = this._dataIdxOfDom(domIdx);
|
|
5817
|
+
if (dataIdx !== null) colIdx = dataIdx;
|
|
5818
|
+
}
|
|
5819
|
+
const sel = [...selection].sort((a, b) => a - b);
|
|
5820
|
+
return {
|
|
5821
|
+
rowIdx,
|
|
5822
|
+
row,
|
|
5823
|
+
colIdx,
|
|
5824
|
+
value: colIdx !== null && row ? row[colIdx] : null,
|
|
5825
|
+
selection: sel,
|
|
5826
|
+
selectedRows: sel.map((i) => rows[i])
|
|
5827
|
+
};
|
|
5828
|
+
}
|
|
5829
|
+
/** STORY-0010. `contextMenuItems` for one right-click: none off a data row
|
|
5830
|
+
* (the empty row), and none from a function that threw. */
|
|
5831
|
+
_customMenuItems(ctx) {
|
|
5832
|
+
if (ctx.rowIdx === null) return [];
|
|
5833
|
+
const spec = this.config.contextMenuItems;
|
|
5834
|
+
let items = spec;
|
|
5835
|
+
if (typeof spec === "function") {
|
|
5836
|
+
try {
|
|
5837
|
+
items = spec(ctx.row, ctx.rowIdx, ctx);
|
|
5838
|
+
} catch (err) {
|
|
5839
|
+
console.error("[DataTable] contextMenuItems threw", err);
|
|
5840
|
+
items = null;
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
return Array.isArray(items) ? items.filter(Boolean) : [];
|
|
5844
|
+
}
|
|
5845
|
+
/**
|
|
5846
|
+
* STORY-0010. Fill the menu for this right-click: the consumer's items,
|
|
5847
|
+
* then (with `copyable`) a separator and the copy items. Without custom
|
|
5848
|
+
* items the menu is the 0.9.1 copy menu, and is only rebuilt when the last
|
|
5849
|
+
* right-click drew custom ones into it. Built with DOM calls, not markup:
|
|
5850
|
+
* the labels are the consumer's text.
|
|
5851
|
+
*/
|
|
5852
|
+
_drawContextMenuItems(menu, custom) {
|
|
5853
|
+
this._contextMenuCtx = custom ? custom.ctx : null;
|
|
5854
|
+
if (!custom) {
|
|
5855
|
+
if (this._contextMenuCustom) {
|
|
5856
|
+
menu.innerHTML = COPY_MENU_HTML;
|
|
5857
|
+
this._contextMenuCustom = false;
|
|
5858
|
+
}
|
|
5859
|
+
return;
|
|
5860
|
+
}
|
|
5861
|
+
this._contextMenuCustom = true;
|
|
5862
|
+
menu.textContent = "";
|
|
5863
|
+
for (const item of custom.items) {
|
|
5864
|
+
if (item.separator) {
|
|
5865
|
+
const sep = document.createElement("div");
|
|
5866
|
+
sep.className = "twm-context-menu__separator";
|
|
5867
|
+
menu.appendChild(sep);
|
|
5868
|
+
continue;
|
|
5869
|
+
}
|
|
5870
|
+
const el = document.createElement("div");
|
|
5871
|
+
el.className = "twm-context-menu-item";
|
|
5872
|
+
if (item.action != null) el.setAttribute("data-action", String(item.action));
|
|
5873
|
+
if (item.danger) el.classList.add("twm-delete-node");
|
|
5874
|
+
if (item.disabled) {
|
|
5875
|
+
el.classList.add("disabled");
|
|
5876
|
+
el.style.pointerEvents = "none";
|
|
5877
|
+
el.setAttribute("aria-disabled", "true");
|
|
5878
|
+
}
|
|
5879
|
+
if (item.title) el.title = String(item.title);
|
|
5880
|
+
if (typeof item.checked === "boolean") {
|
|
5881
|
+
el.setAttribute("role", "menuitemcheckbox");
|
|
5882
|
+
el.setAttribute("aria-checked", String(item.checked));
|
|
5883
|
+
}
|
|
5884
|
+
const icon = document.createElement("span");
|
|
5885
|
+
icon.className = "material-symbols-outlined";
|
|
5886
|
+
icon.textContent = item.icon == null ? "" : String(item.icon);
|
|
5887
|
+
const label = document.createElement("span");
|
|
5888
|
+
label.className = "label";
|
|
5889
|
+
label.textContent = item.label == null ? "" : String(item.label);
|
|
5890
|
+
el.append(icon, label);
|
|
5891
|
+
el.__cmItem = item;
|
|
5892
|
+
menu.appendChild(el);
|
|
5893
|
+
}
|
|
5894
|
+
if (this.config.copyable && custom.copy !== false) {
|
|
5895
|
+
if (custom.items.length) {
|
|
5896
|
+
const sep = document.createElement("div");
|
|
5897
|
+
sep.className = "twm-context-menu__separator";
|
|
5898
|
+
menu.appendChild(sep);
|
|
5899
|
+
}
|
|
5900
|
+
menu.insertAdjacentHTML("beforeend", COPY_MENU_HTML);
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5484
5903
|
_teardownContextMenu() {
|
|
5485
5904
|
this._contextMenuGlobals.forEach((dispose) => {
|
|
5486
5905
|
try {
|
|
@@ -5556,4 +5975,4 @@ export {
|
|
|
5556
5975
|
resolvePaging,
|
|
5557
5976
|
DataTable
|
|
5558
5977
|
};
|
|
5559
|
-
//# sourceMappingURL=chunk-
|
|
5978
|
+
//# sourceMappingURL=chunk-6CGH73BC.js.map
|