@lilydesignsystem/angular-data-grid 0.9.0 → 0.10.0
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 +36 -0
- package/fesm2022/lilydesignsystem-angular-data-grid.mjs +683 -101
- package/fesm2022/lilydesignsystem-angular-data-grid.mjs.map +1 -1
- package/package.json +1 -1
- package/types/lilydesignsystem-angular-data-grid.d.ts +249 -4
- package/types/lilydesignsystem-angular-data-grid.d.ts.map +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
2
|
import { ChangeDetectionStrategy, Component, Directive, ElementRef, Injector, afterNextRender, computed, effect, inject, input, model, output, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { NgTemplateOutlet } from "@angular/common";
|
|
4
|
-
import { DataTable, DataTableBody, DataTableFoot, DataTableHead, DataTableRow, DataTableTD, DataTableTH } from "@lilydesignsystem/angular-headless";
|
|
4
|
+
import { DataTable, DataTableBody, DataTableFoot, DataTableHead, DataTableRow, DataTableTD, DataTableTH, PaginationNav, SearchInput } from "@lilydesignsystem/angular-headless";
|
|
5
5
|
function isExportFormula(value) {
|
|
6
6
|
return !!value && typeof value === "object" && !(value instanceof Date) && typeof value.formula === "string" && "result" in value;
|
|
7
7
|
}
|
|
@@ -213,6 +213,251 @@ function downloadBlob(blob, filename) {
|
|
|
213
213
|
link.remove();
|
|
214
214
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
215
215
|
}
|
|
216
|
+
function toClipboardText(table) {
|
|
217
|
+
return toTsv(table, { bom: false });
|
|
218
|
+
}
|
|
219
|
+
async function copyToClipboard(text) {
|
|
220
|
+
try {
|
|
221
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
222
|
+
await navigator.clipboard.writeText(text);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
} catch {}
|
|
226
|
+
try {
|
|
227
|
+
if (typeof document === "undefined") return false;
|
|
228
|
+
const area = document.createElement("textarea");
|
|
229
|
+
area.value = text;
|
|
230
|
+
area.setAttribute("readonly", "");
|
|
231
|
+
area.setAttribute("aria-hidden", "true");
|
|
232
|
+
area.style.position = "fixed";
|
|
233
|
+
area.style.opacity = "0";
|
|
234
|
+
document.body.appendChild(area);
|
|
235
|
+
area.select();
|
|
236
|
+
const ok = typeof document.execCommand === "function" && document.execCommand("copy");
|
|
237
|
+
area.remove();
|
|
238
|
+
return !!ok;
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function foldText(text) {
|
|
244
|
+
return text.normalize("NFD").replace(/\p{M}+/gu, "").toLowerCase();
|
|
245
|
+
}
|
|
246
|
+
function searchTerms(query) {
|
|
247
|
+
return foldText(query).split(/\s+/).filter(Boolean);
|
|
248
|
+
}
|
|
249
|
+
function matchesSearch(text, query) {
|
|
250
|
+
const terms = searchTerms(query);
|
|
251
|
+
if (terms.length === 0) return true;
|
|
252
|
+
const folded = foldText(text);
|
|
253
|
+
return terms.every((term) => folded.includes(term));
|
|
254
|
+
}
|
|
255
|
+
function highlightSegments(text, query) {
|
|
256
|
+
const terms = searchTerms(query);
|
|
257
|
+
if (terms.length === 0 || text === "") return [{
|
|
258
|
+
text,
|
|
259
|
+
match: false
|
|
260
|
+
}];
|
|
261
|
+
const chars = Array.from(text);
|
|
262
|
+
const folded = chars.map((c) => foldText(c));
|
|
263
|
+
if (folded.some((f) => f.length !== 1)) return [{
|
|
264
|
+
text,
|
|
265
|
+
match: false
|
|
266
|
+
}];
|
|
267
|
+
const flat = folded.join("");
|
|
268
|
+
const marked = new Array(chars.length).fill(false);
|
|
269
|
+
for (const term of terms) for (let at = flat.indexOf(term); at !== -1; at = flat.indexOf(term, at + 1)) for (let i = at; i < at + term.length; i++) marked[i] = true;
|
|
270
|
+
const out = [];
|
|
271
|
+
chars.forEach((c, i) => {
|
|
272
|
+
const last = out[out.length - 1];
|
|
273
|
+
if (last && last.match === marked[i]) last.text += c;
|
|
274
|
+
else out.push({
|
|
275
|
+
text: c,
|
|
276
|
+
match: marked[i]
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
return out;
|
|
280
|
+
}
|
|
281
|
+
function isRtl(element) {
|
|
282
|
+
if (!element) return false;
|
|
283
|
+
const attr = element.closest("[dir]")?.getAttribute("dir")?.toLowerCase();
|
|
284
|
+
if (attr === "rtl") return true;
|
|
285
|
+
if (attr === "ltr") return false;
|
|
286
|
+
if (typeof getComputedStyle === "function") return getComputedStyle(element).direction === "rtl";
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
function logicalKey(key, rtl) {
|
|
290
|
+
if (!rtl) return key;
|
|
291
|
+
if (key === "ArrowLeft") return "ArrowRight";
|
|
292
|
+
if (key === "ArrowRight") return "ArrowLeft";
|
|
293
|
+
return key;
|
|
294
|
+
}
|
|
295
|
+
const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
296
|
+
function gridMove(event, at, nav) {
|
|
297
|
+
const key = logicalKey(event.key, nav.rtl ?? false);
|
|
298
|
+
const corner = !!(event.ctrlKey || event.metaKey);
|
|
299
|
+
let row = at.row;
|
|
300
|
+
let col = at.col;
|
|
301
|
+
switch (key) {
|
|
302
|
+
case "ArrowRight":
|
|
303
|
+
col += 1;
|
|
304
|
+
break;
|
|
305
|
+
case "ArrowLeft":
|
|
306
|
+
col -= 1;
|
|
307
|
+
break;
|
|
308
|
+
case "ArrowDown":
|
|
309
|
+
row += 1;
|
|
310
|
+
break;
|
|
311
|
+
case "ArrowUp":
|
|
312
|
+
row -= 1;
|
|
313
|
+
break;
|
|
314
|
+
case "PageDown":
|
|
315
|
+
row += nav.pageStep;
|
|
316
|
+
break;
|
|
317
|
+
case "PageUp":
|
|
318
|
+
row -= nav.pageStep;
|
|
319
|
+
break;
|
|
320
|
+
case "Home":
|
|
321
|
+
if (corner) [row, col] = [nav.firstRow, 0];
|
|
322
|
+
else if (nav.homeEnd === "column") row = nav.firstRow;
|
|
323
|
+
else col = 0;
|
|
324
|
+
break;
|
|
325
|
+
case "End":
|
|
326
|
+
if (corner) [row, col] = [nav.lastRow, nav.lastCol];
|
|
327
|
+
else if (nav.homeEnd === "column") row = nav.lastRow;
|
|
328
|
+
else col = nav.lastCol;
|
|
329
|
+
break;
|
|
330
|
+
default: return null;
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
row: clamp(row, nav.firstRow, nav.lastRow),
|
|
334
|
+
col: clamp(col, 0, nav.lastCol)
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const CONTROL_LABELS = {
|
|
338
|
+
selection: [
|
|
339
|
+
"selectAll",
|
|
340
|
+
"selectionColumn",
|
|
341
|
+
"selectRow"
|
|
342
|
+
],
|
|
343
|
+
pagination: [
|
|
344
|
+
"previousPage",
|
|
345
|
+
"nextPage",
|
|
346
|
+
"pageStatus"
|
|
347
|
+
],
|
|
348
|
+
hidableColumns: ["columnVisibility", "columnVisibilityOption"],
|
|
349
|
+
resizableColumns: ["resizeHandle"],
|
|
350
|
+
editing: ["editCell", "cellEditor"],
|
|
351
|
+
search: ["search"],
|
|
352
|
+
exporting: ["export"]
|
|
353
|
+
};
|
|
354
|
+
const ANNOUNCEMENT_LABELS = {
|
|
355
|
+
sorting: ["sortAnnouncement"],
|
|
356
|
+
search: ["filterAnnouncement"],
|
|
357
|
+
selection: ["selectionAnnouncement"],
|
|
358
|
+
pagination: ["pageAnnouncement"],
|
|
359
|
+
editing: [
|
|
360
|
+
"editAnnouncement",
|
|
361
|
+
"editCancelAnnouncement",
|
|
362
|
+
"editError",
|
|
363
|
+
"undoAnnouncement",
|
|
364
|
+
"redoAnnouncement"
|
|
365
|
+
],
|
|
366
|
+
exporting: [
|
|
367
|
+
"exportAnnouncement",
|
|
368
|
+
"copyAnnouncement",
|
|
369
|
+
"copyFailed"
|
|
370
|
+
]
|
|
371
|
+
};
|
|
372
|
+
function requiredLabels(features) {
|
|
373
|
+
const out = [];
|
|
374
|
+
const add = (table, kind) => {
|
|
375
|
+
for (const feature of Object.keys(table)) {
|
|
376
|
+
if (!features[feature]) continue;
|
|
377
|
+
for (const key of table[feature]) if (!out.some((o) => o.key === key)) out.push({
|
|
378
|
+
key,
|
|
379
|
+
kind
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
add(CONTROL_LABELS, "control");
|
|
384
|
+
add(ANNOUNCEMENT_LABELS, "announcement");
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
function missingLabels(features, labels) {
|
|
388
|
+
return requiredLabels(features).filter(({ key }) => {
|
|
389
|
+
const value = labels?.[key];
|
|
390
|
+
return value === void 0 || value === null || value === "";
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
const EMPTY_HISTORY = {
|
|
394
|
+
undo: [],
|
|
395
|
+
redo: []
|
|
396
|
+
};
|
|
397
|
+
const HISTORY_LIMIT = 100;
|
|
398
|
+
function recordEdit(history, record, limit = 100) {
|
|
399
|
+
const undo = [...history.undo, record];
|
|
400
|
+
return {
|
|
401
|
+
undo: undo.length > limit ? undo.slice(undo.length - limit) : undo,
|
|
402
|
+
redo: []
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function undoEdit(history) {
|
|
406
|
+
const record = history.undo[history.undo.length - 1];
|
|
407
|
+
if (!record) return {
|
|
408
|
+
history,
|
|
409
|
+
record: void 0
|
|
410
|
+
};
|
|
411
|
+
return {
|
|
412
|
+
history: {
|
|
413
|
+
undo: history.undo.slice(0, -1),
|
|
414
|
+
redo: [...history.redo, record]
|
|
415
|
+
},
|
|
416
|
+
record
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function redoEdit(history) {
|
|
420
|
+
const record = history.redo[history.redo.length - 1];
|
|
421
|
+
if (!record) return {
|
|
422
|
+
history,
|
|
423
|
+
record: void 0
|
|
424
|
+
};
|
|
425
|
+
return {
|
|
426
|
+
history: {
|
|
427
|
+
undo: [...history.undo, record],
|
|
428
|
+
redo: history.redo.slice(0, -1)
|
|
429
|
+
},
|
|
430
|
+
record
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function historyKey(event) {
|
|
434
|
+
if (event.altKey || !(event.ctrlKey || event.metaKey)) return null;
|
|
435
|
+
const key = event.key.toLowerCase();
|
|
436
|
+
if (key === "z") return event.shiftKey ? "redo" : "undo";
|
|
437
|
+
if (key === "y" && !event.shiftKey) return "redo";
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
const TYPEAHEAD_RESET_MS = 700;
|
|
441
|
+
function isTypeaheadKey(event) {
|
|
442
|
+
return event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.altKey && !event.metaKey;
|
|
443
|
+
}
|
|
444
|
+
function nextTypeaheadState(previous, key, now, resetMs = 700) {
|
|
445
|
+
return {
|
|
446
|
+
prefix: (previous.prefix !== "" && now - previous.at <= resetMs ? previous.prefix : "") + key,
|
|
447
|
+
at: now
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function findTypeaheadMatch(labels, from, prefix) {
|
|
451
|
+
const folded = foldText(prefix);
|
|
452
|
+
if (folded === "" || labels.length === 0) return -1;
|
|
453
|
+
const needle = folded.length > 1 && [...folded].every((c) => c === folded[0]) ? folded[0] : folded;
|
|
454
|
+
const startOffset = needle.length > 1 ? 0 : 1;
|
|
455
|
+
for (let step = 0; step < labels.length; step++) {
|
|
456
|
+
const index = (from + startOffset + step + labels.length) % labels.length;
|
|
457
|
+
if (foldText(labels[index]).startsWith(needle)) return index;
|
|
458
|
+
}
|
|
459
|
+
return -1;
|
|
460
|
+
}
|
|
216
461
|
const SAFE_SCHEME = /^(https?:|mailto:|tel:)/i;
|
|
217
462
|
const RELATIVE = /^(\/(?!\/)|\.\.?\/|#|\?)/;
|
|
218
463
|
function safeHref(href) {
|
|
@@ -607,6 +852,7 @@ function resolveColumn(column, gridLocale, rows = []) {
|
|
|
607
852
|
}
|
|
608
853
|
return resolved;
|
|
609
854
|
}
|
|
855
|
+
const isMissing = (value) => value === null || value === void 0 || value === "";
|
|
610
856
|
const WIDGET_SELECTOR = "a[href], button, input, select, textarea, summary, [contenteditable=\"true\"]";
|
|
611
857
|
const MIN_COLUMN_WIDTH = 40;
|
|
612
858
|
const RESIZE_STEP = 16;
|
|
@@ -705,12 +951,20 @@ var DataGrid = class DataGrid {
|
|
|
705
951
|
}
|
|
706
952
|
return ctx;
|
|
707
953
|
}
|
|
954
|
+
isMovable(column) {
|
|
955
|
+
return column.movable ?? this.movableColumns();
|
|
956
|
+
}
|
|
708
957
|
constructor() {
|
|
709
958
|
this.label = input.required(...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
|
|
710
959
|
this.caption = input(void 0, ...ngDevMode ? [{ debugName: "caption" }] : /* istanbul ignore next */ []);
|
|
711
960
|
this.columns = input.required(...ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []);
|
|
712
961
|
this.rows = input.required(...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
|
|
713
962
|
this.locale = input(void 0, ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
|
|
963
|
+
this.emptyValue = input(void 0, ...ngDevMode ? [{ debugName: "emptyValue" }] : /* istanbul ignore next */ []);
|
|
964
|
+
this.warnMissingLabels = input(true, ...ngDevMode ? [{ debugName: "warnMissingLabels" }] : /* istanbul ignore next */ []);
|
|
965
|
+
this.nulls = input("last", ...ngDevMode ? [{ debugName: "nulls" }] : /* istanbul ignore next */ []);
|
|
966
|
+
this.movableColumns = input(false, ...ngDevMode ? [{ debugName: "movableColumns" }] : /* istanbul ignore next */ []);
|
|
967
|
+
this.rowFilter = input(void 0, ...ngDevMode ? [{ debugName: "rowFilter" }] : /* istanbul ignore next */ []);
|
|
714
968
|
this.rowId = input((_row, index) => String(index), ...ngDevMode ? [{ debugName: "rowId" }] : /* istanbul ignore next */ []);
|
|
715
969
|
this.selectionMode = input("none", ...ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []);
|
|
716
970
|
this.pageSize = input(void 0, ...ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []);
|
|
@@ -728,10 +982,13 @@ var DataGrid = class DataGrid {
|
|
|
728
982
|
this.exportScope = input("view", ...ngDevMode ? [{ debugName: "exportScope" }] : /* istanbul ignore next */ []);
|
|
729
983
|
this.exportFormulas = input(false, ...ngDevMode ? [{ debugName: "exportFormulas" }] : /* istanbul ignore next */ []);
|
|
730
984
|
this.exportHandler = input(void 0, ...ngDevMode ? [{ debugName: "exportHandler" }] : /* istanbul ignore next */ []);
|
|
985
|
+
this.copyHandler = input(void 0, ...ngDevMode ? [{ debugName: "copyHandler" }] : /* istanbul ignore next */ []);
|
|
731
986
|
this.virtualize = input(void 0, ...ngDevMode ? [{ debugName: "virtualize" }] : /* istanbul ignore next */ []);
|
|
732
987
|
this.className = input("", ...ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []);
|
|
733
988
|
this.selected = model([], ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
|
|
734
989
|
this.filter = model("", ...ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []);
|
|
990
|
+
this.filterField = model("", ...ngDevMode ? [{ debugName: "filterField" }] : /* istanbul ignore next */ []);
|
|
991
|
+
this.thenBy = model([], ...ngDevMode ? [{ debugName: "thenBy" }] : /* istanbul ignore next */ []);
|
|
735
992
|
this.sort = model({
|
|
736
993
|
columnId: "",
|
|
737
994
|
direction: "none"
|
|
@@ -743,6 +1000,7 @@ var DataGrid = class DataGrid {
|
|
|
743
1000
|
this.fileRemove = output();
|
|
744
1001
|
this.rowActivate = output();
|
|
745
1002
|
this.exportFile = output();
|
|
1003
|
+
this.copy = output();
|
|
746
1004
|
this.gridId = nextDataGridId();
|
|
747
1005
|
this.minColumnWidth = MIN_COLUMN_WIDTH;
|
|
748
1006
|
this.rootRef = viewChild.required("rootEl", ...ngDevMode ? [{ debugName: "rootRef" }] : /* istanbul ignore next */ []);
|
|
@@ -787,36 +1045,50 @@ var DataGrid = class DataGrid {
|
|
|
787
1045
|
this.resolvedColumns();
|
|
788
1046
|
return /* @__PURE__ */ new WeakMap();
|
|
789
1047
|
}, ...ngDevMode ? [{ debugName: "cellCache" }] : /* istanbul ignore next */ []);
|
|
790
|
-
this.
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}, ...ngDevMode ? [{ debugName: "searchIndex" }] : /* istanbul ignore next */ []);
|
|
1048
|
+
this.isMissing = isMissing;
|
|
1049
|
+
this.filterableColumns = computed(() => this.resolvedColumns().filter((c) => c.filterable !== false), ...ngDevMode ? [{ debugName: "filterableColumns" }] : /* istanbul ignore next */ []);
|
|
1050
|
+
this.searchIndex = computed(() => this.entries().map((entry) => this.filterableColumns().map((column) => foldText(column.filterText ? column.filterText(this.cellFor(column, entry.row).value, entry.row) : this.cellFor(column, entry.row).formatted))), ...ngDevMode ? [{ debugName: "searchIndex" }] : /* istanbul ignore next */ []);
|
|
794
1051
|
this.filteredEntries = computed(() => {
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1052
|
+
const terms = searchTerms(this.filter());
|
|
1053
|
+
const rowFilter = this.rowFilter();
|
|
1054
|
+
if (terms.length === 0 && !rowFilter) return this.entries();
|
|
1055
|
+
const index = terms.length > 0 ? this.searchIndex() : null;
|
|
1056
|
+
const field = this.filterField();
|
|
1057
|
+
const only = field ? this.filterableColumns().findIndex((c) => c.id === field) : -1;
|
|
1058
|
+
const rowText = (i) => only >= 0 ? index[i][only] : index[i].join(" ");
|
|
1059
|
+
return this.entries().filter((entry, i) => (!rowFilter || rowFilter(entry.row)) && (!index || terms.every((term) => rowText(i).includes(term))));
|
|
799
1060
|
}, ...ngDevMode ? [{ debugName: "filteredEntries" }] : /* istanbul ignore next */ []);
|
|
1061
|
+
this.sortKeys = computed(() => [this.sort(), ...this.thenBy()].map((k) => ({
|
|
1062
|
+
column: this.resolvedColumns().find((c) => c.id === k.columnId),
|
|
1063
|
+
dir: k.direction === "ascending" ? 1 : k.direction === "descending" ? -1 : 0
|
|
1064
|
+
})).filter((k) => !!k.column && k.dir !== 0), ...ngDevMode ? [{ debugName: "sortKeys" }] : /* istanbul ignore next */ []);
|
|
800
1065
|
this.sortedEntries = computed(() => {
|
|
801
|
-
const
|
|
1066
|
+
const keys = this.sortKeys();
|
|
802
1067
|
const filtered = this.filteredEntries();
|
|
803
|
-
if (
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1068
|
+
if (keys.length === 0) return filtered;
|
|
1069
|
+
const nulls = this.nulls();
|
|
1070
|
+
const collators = keys.map((k) => {
|
|
1071
|
+
const tag = k.column.locale ?? this.locale();
|
|
1072
|
+
return tag ? new Intl.Collator(tag, { numeric: true }) : null;
|
|
1073
|
+
});
|
|
808
1074
|
const keyed = filtered.map((entry) => ({
|
|
809
1075
|
entry,
|
|
810
|
-
|
|
1076
|
+
keys: keys.map((k) => this.cellFor(k.column, entry.row).value)
|
|
811
1077
|
}));
|
|
812
1078
|
keyed.sort((a, b) => {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1079
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1080
|
+
const { column, dir } = keys[i];
|
|
1081
|
+
const av = a.keys[i];
|
|
1082
|
+
const bv = b.keys[i];
|
|
1083
|
+
if (av === bv) continue;
|
|
1084
|
+
const aMissing = av === null || av === void 0;
|
|
1085
|
+
const bMissing = bv === null || bv === void 0;
|
|
1086
|
+
if (aMissing || bMissing) return aMissing === bMissing ? 0 : (aMissing ? 1 : -1) * (nulls === "first" ? -1 : 1);
|
|
1087
|
+
const collator = collators[i];
|
|
1088
|
+
const result = column.compare ? column.compare(av, bv) * dir : collator && typeof av === "string" && typeof bv === "string" ? collator.compare(av, bv) * dir : av > bv ? dir : -dir;
|
|
1089
|
+
if (result !== 0) return result;
|
|
1090
|
+
}
|
|
1091
|
+
return 0;
|
|
820
1092
|
});
|
|
821
1093
|
return keyed.map((k) => k.entry);
|
|
822
1094
|
}, ...ngDevMode ? [{ debugName: "sortedEntries" }] : /* istanbul ignore next */ []);
|
|
@@ -889,8 +1161,13 @@ var DataGrid = class DataGrid {
|
|
|
889
1161
|
this.exportEntries = computed(() => this.exportScope() === "selected" ? this.sortedEntries().filter((e) => this.selectedSet().has(e.id)) : this.sortedEntries(), ...ngDevMode ? [{ debugName: "exportEntries" }] : /* istanbul ignore next */ []);
|
|
890
1162
|
this.hasExportButtons = computed(() => {
|
|
891
1163
|
const l = this.labels();
|
|
892
|
-
return !!(l.exportCsv || l.exportTsv || l.exportExcel);
|
|
1164
|
+
return !!(l.exportCsv || l.exportTsv || l.exportExcel || l.copy);
|
|
893
1165
|
}, ...ngDevMode ? [{ debugName: "hasExportButtons" }] : /* istanbul ignore next */ []);
|
|
1166
|
+
this.history = EMPTY_HISTORY;
|
|
1167
|
+
this.typeahead = {
|
|
1168
|
+
prefix: "",
|
|
1169
|
+
at: 0
|
|
1170
|
+
};
|
|
894
1171
|
effect(() => {
|
|
895
1172
|
const root = this.rootRef().nativeElement;
|
|
896
1173
|
const grid = this.isGrid();
|
|
@@ -910,6 +1187,30 @@ var DataGrid = class DataGrid {
|
|
|
910
1187
|
else table.removeAttribute("aria-busy");
|
|
911
1188
|
}, { injector: this.injector });
|
|
912
1189
|
});
|
|
1190
|
+
const reportedLabels = /* @__PURE__ */ new Set();
|
|
1191
|
+
effect(() => {
|
|
1192
|
+
const labels = this.labels();
|
|
1193
|
+
const columns = this.resolvedColumns();
|
|
1194
|
+
const features = {
|
|
1195
|
+
selection: this.selectionMode() !== "none",
|
|
1196
|
+
pagination: !!this.pageSize(),
|
|
1197
|
+
hidableColumns: columns.some((c) => c.hidable),
|
|
1198
|
+
resizableColumns: columns.some((c) => c.resizable),
|
|
1199
|
+
editing: columns.some((c) => c.editable),
|
|
1200
|
+
sorting: columns.some((c) => c.sortable),
|
|
1201
|
+
search: !!labels.search,
|
|
1202
|
+
exporting: !!(labels.exportCsv || labels.exportTsv || labels.exportExcel || labels.copy)
|
|
1203
|
+
};
|
|
1204
|
+
const warn = this.warnMissingLabels();
|
|
1205
|
+
const env = globalThis.process?.env;
|
|
1206
|
+
if (!warn || env?.["NODE_ENV"] === "production") return;
|
|
1207
|
+
const fresh = missingLabels(features, labels).filter((m) => !reportedLabels.has(m.key));
|
|
1208
|
+
if (fresh.length === 0) return;
|
|
1209
|
+
for (const m of fresh) reportedLabels.add(m.key);
|
|
1210
|
+
const controls = fresh.filter((m) => m.kind === "control").map((m) => m.key);
|
|
1211
|
+
const announcements = fresh.filter((m) => m.kind === "announcement").map((m) => m.key);
|
|
1212
|
+
console.warn(`[data-grid] "${this.label()}": labels left out — ` + [controls.length ? `controls with no name: ${controls.join(", ")}` : "", announcements.length ? `silent announcements: ${announcements.join(", ")}` : ""].filter(Boolean).join("; "));
|
|
1213
|
+
});
|
|
913
1214
|
effect(() => {
|
|
914
1215
|
this.pageEntries();
|
|
915
1216
|
this.visibleColumns();
|
|
@@ -943,11 +1244,33 @@ var DataGrid = class DataGrid {
|
|
|
943
1244
|
}
|
|
944
1245
|
ariaSortFor(column) {
|
|
945
1246
|
if (!column.sortable) return null;
|
|
946
|
-
|
|
1247
|
+
if (this.sort().columnId === column.id) return this.sort().direction;
|
|
1248
|
+
return this.thenBy().find((k) => k.columnId === column.id)?.direction ?? "none";
|
|
1249
|
+
}
|
|
1250
|
+
sortOrderFor(column) {
|
|
1251
|
+
const keys = this.sortKeys();
|
|
1252
|
+
if (keys.length < 2) return null;
|
|
1253
|
+
const at = keys.findIndex((k) => k.column.id === column.id);
|
|
1254
|
+
return at === -1 ? null : at + 1;
|
|
947
1255
|
}
|
|
948
|
-
toggleSort(column) {
|
|
1256
|
+
toggleSort(column, additive = false) {
|
|
949
1257
|
if (!column.sortable) return;
|
|
950
1258
|
const current = this.sort();
|
|
1259
|
+
if (additive && current.direction !== "none" && current.columnId && current.columnId !== column.id) {
|
|
1260
|
+
const thenBy = this.thenBy();
|
|
1261
|
+
const existing = thenBy.find((k) => k.columnId === column.id);
|
|
1262
|
+
const nextThen = !existing ? [...thenBy, {
|
|
1263
|
+
columnId: column.id,
|
|
1264
|
+
direction: "ascending"
|
|
1265
|
+
}] : existing.direction === "ascending" ? thenBy.map((k) => k.columnId === column.id ? {
|
|
1266
|
+
columnId: column.id,
|
|
1267
|
+
direction: "descending"
|
|
1268
|
+
} : k) : thenBy.filter((k) => k.columnId !== column.id);
|
|
1269
|
+
this.thenBy.set(nextThen);
|
|
1270
|
+
this.announce(this.labels().sortAnnouncement?.(column.header, nextThen.find((k) => k.columnId === column.id)?.direction ?? "none"));
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
if (this.thenBy().length > 0) this.thenBy.set([]);
|
|
951
1274
|
const next = current.columnId !== column.id ? {
|
|
952
1275
|
columnId: column.id,
|
|
953
1276
|
direction: "ascending"
|
|
@@ -964,8 +1287,20 @@ var DataGrid = class DataGrid {
|
|
|
964
1287
|
this.sort.set(next);
|
|
965
1288
|
this.announce(this.labels().sortAnnouncement?.(column.header, next.direction));
|
|
966
1289
|
}
|
|
967
|
-
|
|
968
|
-
this.filter.set(
|
|
1290
|
+
setFilter(value) {
|
|
1291
|
+
this.filter.set(value);
|
|
1292
|
+
this.page.set(1);
|
|
1293
|
+
this.announce(this.labels().filterAnnouncement?.(this.filteredEntries().length, this.rows().length));
|
|
1294
|
+
}
|
|
1295
|
+
onFilterKeydown(event) {
|
|
1296
|
+
const e = event;
|
|
1297
|
+
if (e.key === "Escape" && this.filter() !== "") {
|
|
1298
|
+
e.preventDefault();
|
|
1299
|
+
this.setFilter("");
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
onFilterFieldChange(event) {
|
|
1303
|
+
this.filterField.set(event.target.value);
|
|
969
1304
|
this.page.set(1);
|
|
970
1305
|
this.announce(this.labels().filterAnnouncement?.(this.filteredEntries().length, this.rows().length));
|
|
971
1306
|
}
|
|
@@ -1125,29 +1460,44 @@ var DataGrid = class DataGrid {
|
|
|
1125
1460
|
hasUtcColumn(c) {
|
|
1126
1461
|
return c.type === "time-zone" && !!c.exportUtc && !!c.referenceDate && !!this.labels().exportUtcHeader;
|
|
1127
1462
|
}
|
|
1128
|
-
|
|
1463
|
+
buildExportTable(format) {
|
|
1129
1464
|
const live = (c) => format === "xlsx" && this.exportFormulas() && c.type === "formula" && !!c.source;
|
|
1130
1465
|
const headers = [];
|
|
1131
1466
|
for (const c of this.exportColumns()) {
|
|
1132
1467
|
headers.push(c.header);
|
|
1133
1468
|
if (this.hasUtcColumn(c)) headers.push(this.labels().exportUtcHeader(c.header));
|
|
1134
1469
|
}
|
|
1135
|
-
|
|
1136
|
-
const cells = [];
|
|
1137
|
-
for (const c of this.exportColumns()) {
|
|
1138
|
-
const expression = live(c) ? c.source(entry.row) : void 0;
|
|
1139
|
-
cells.push(typeof expression === "string" && expression !== "" && !c.errorOf?.(entry.row) ? {
|
|
1140
|
-
formula: expression,
|
|
1141
|
-
result: this.exportCell(c, entry.row)
|
|
1142
|
-
} : this.exportCell(c, entry.row));
|
|
1143
|
-
if (this.hasUtcColumn(c)) cells.push(zonedUtcTime(this.cellFor(c, entry.row).value, c.referenceDate));
|
|
1144
|
-
}
|
|
1145
|
-
return cells;
|
|
1146
|
-
});
|
|
1147
|
-
const table = {
|
|
1470
|
+
return {
|
|
1148
1471
|
headers,
|
|
1149
|
-
rows
|
|
1472
|
+
rows: this.exportEntries().map((entry) => {
|
|
1473
|
+
const cells = [];
|
|
1474
|
+
for (const c of this.exportColumns()) {
|
|
1475
|
+
const expression = live(c) ? c.source(entry.row) : void 0;
|
|
1476
|
+
cells.push(typeof expression === "string" && expression !== "" && !c.errorOf?.(entry.row) ? {
|
|
1477
|
+
formula: expression,
|
|
1478
|
+
result: this.exportCell(c, entry.row)
|
|
1479
|
+
} : this.exportCell(c, entry.row));
|
|
1480
|
+
if (this.hasUtcColumn(c)) cells.push(zonedUtcTime(this.cellFor(c, entry.row).value, c.referenceDate));
|
|
1481
|
+
}
|
|
1482
|
+
return cells;
|
|
1483
|
+
})
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
async runCopy() {
|
|
1487
|
+
const table = this.buildExportTable("tsv");
|
|
1488
|
+
const text = toClipboardText(table);
|
|
1489
|
+
const copied = {
|
|
1490
|
+
text,
|
|
1491
|
+
rowCount: table.rows.length
|
|
1150
1492
|
};
|
|
1493
|
+
this.copy.emit(copied);
|
|
1494
|
+
if (this.copyHandler()?.(copied) === false) return;
|
|
1495
|
+
const ok = await copyToClipboard(text);
|
|
1496
|
+
this.announce(ok ? this.labels().copyAnnouncement?.(table.rows.length) : this.labels().copyFailed);
|
|
1497
|
+
}
|
|
1498
|
+
runExport(format) {
|
|
1499
|
+
const table = this.buildExportTable(format);
|
|
1500
|
+
const rows = table.rows;
|
|
1151
1501
|
const name = this.exportFilename();
|
|
1152
1502
|
const filename = exportFilename(name, format);
|
|
1153
1503
|
const blob = exportBlob(table, format, { sheetName: name });
|
|
@@ -1235,6 +1585,26 @@ var DataGrid = class DataGrid {
|
|
|
1235
1585
|
if (column.editor === "date" || column.editor === "time" || column.editor === "select") return draft === "" ? null : draft;
|
|
1236
1586
|
return draft;
|
|
1237
1587
|
}
|
|
1588
|
+
stepHistory(direction) {
|
|
1589
|
+
const step = direction === "undo" ? undoEdit(this.history) : redoEdit(this.history);
|
|
1590
|
+
if (!step.record) return;
|
|
1591
|
+
this.history = step.history;
|
|
1592
|
+
const { ownerId, fieldId, value, previous } = step.record;
|
|
1593
|
+
const entry = this.entries().find((e) => e.id === ownerId);
|
|
1594
|
+
const column = this.resolvedColumns().find((c) => c.id === fieldId);
|
|
1595
|
+
if (!entry || !column) return;
|
|
1596
|
+
const restored = direction === "undo" ? previous : value;
|
|
1597
|
+
const replaced = direction === "undo" ? value : previous;
|
|
1598
|
+
this.cellEdit.emit({
|
|
1599
|
+
rowId: entry.id,
|
|
1600
|
+
row: entry.row,
|
|
1601
|
+
columnId: column.id,
|
|
1602
|
+
value: restored,
|
|
1603
|
+
previous: replaced
|
|
1604
|
+
});
|
|
1605
|
+
const text = column.format ? column.format(restored, entry.row) : String(restored ?? "");
|
|
1606
|
+
this.announce((direction === "undo" ? this.labels().undoAnnouncement : this.labels().redoAnnouncement)?.(column.header, text));
|
|
1607
|
+
}
|
|
1238
1608
|
isStructurallyEqual(a, b) {
|
|
1239
1609
|
return typeof a === "object" && typeof b === "object" && a !== null && b !== null && JSON.stringify(a) === JSON.stringify(b);
|
|
1240
1610
|
}
|
|
@@ -1255,6 +1625,12 @@ var DataGrid = class DataGrid {
|
|
|
1255
1625
|
const previous = column.type === "formula" && column.source ? column.source(entry.row) : defaultAccessor(column, entry.row);
|
|
1256
1626
|
this.editingState.set(null);
|
|
1257
1627
|
if (value !== previous && !this.isStructurallyEqual(value, previous)) {
|
|
1628
|
+
this.history = recordEdit(this.history, {
|
|
1629
|
+
ownerId: entry.id,
|
|
1630
|
+
fieldId: column.id,
|
|
1631
|
+
value,
|
|
1632
|
+
previous
|
|
1633
|
+
});
|
|
1258
1634
|
this.cellEdit.emit({
|
|
1259
1635
|
rowId: entry.id,
|
|
1260
1636
|
row: entry.row,
|
|
@@ -1423,51 +1799,51 @@ var DataGrid = class DataGrid {
|
|
|
1423
1799
|
const activeRow = this.activeRow();
|
|
1424
1800
|
const activeCol = this.activeCol();
|
|
1425
1801
|
const colOffset = this.colOffset();
|
|
1426
|
-
|
|
1802
|
+
const key = logicalKey(e.key, isRtl(cell));
|
|
1803
|
+
if (e.altKey && activeRow === -1 && (key === "ArrowRight" || key === "ArrowLeft")) {
|
|
1427
1804
|
const column = this.visibleColumns()[activeCol - colOffset];
|
|
1428
|
-
if (column
|
|
1805
|
+
if (column && this.isMovable(column)) {
|
|
1429
1806
|
e.preventDefault();
|
|
1430
|
-
const delta =
|
|
1807
|
+
const delta = key === "ArrowRight" ? 1 : -1;
|
|
1431
1808
|
if (this.moveColumn(column.id, activeCol - colOffset + delta)) this.moveFocus(-1, activeCol + delta);
|
|
1432
1809
|
return;
|
|
1433
1810
|
}
|
|
1434
1811
|
}
|
|
1435
|
-
const
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
break;
|
|
1812
|
+
const historyAction = target === cell && this.resolvedColumns().some((c) => c.editable) ? historyKey(e) : null;
|
|
1813
|
+
if (historyAction) {
|
|
1814
|
+
e.preventDefault();
|
|
1815
|
+
this.stepHistory(historyAction);
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
if (isTypeaheadKey(e) && target === cell && activeRow >= 0) {
|
|
1819
|
+
const column = this.visibleColumns()[activeCol - colOffset];
|
|
1820
|
+
if (column) {
|
|
1821
|
+
const state = nextTypeaheadState(this.typeahead, e.key, Date.now(), 700);
|
|
1822
|
+
const hit = findTypeaheadMatch(this.pageEntries().map((entry) => this.cellFor(column, entry.row).formatted), activeRow, state.prefix);
|
|
1823
|
+
if (hit !== -1) {
|
|
1824
|
+
this.typeahead = state;
|
|
1825
|
+
e.preventDefault();
|
|
1826
|
+
this.moveFocus(hit, activeCol);
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
const move = gridMove(e, {
|
|
1832
|
+
row: activeRow,
|
|
1833
|
+
col: activeCol
|
|
1834
|
+
}, {
|
|
1835
|
+
firstRow: -1,
|
|
1836
|
+
lastRow: this.pageEntries().length - 1,
|
|
1837
|
+
lastCol: this.lastCol(),
|
|
1838
|
+
pageStep: this.pageSize() ?? 10,
|
|
1839
|
+
rtl: isRtl(cell)
|
|
1840
|
+
});
|
|
1841
|
+
if (move) {
|
|
1842
|
+
e.preventDefault();
|
|
1843
|
+
this.moveFocus(move.row, move.col);
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1846
|
+
switch (key) {
|
|
1471
1847
|
case "F2": {
|
|
1472
1848
|
const entry = this.pageEntries()[Number(cell.dataset["row"])];
|
|
1473
1849
|
const column = this.visibleColumns()[Number(cell.dataset["col"]) - colOffset];
|
|
@@ -1604,6 +1980,41 @@ var DataGrid = class DataGrid {
|
|
|
1604
1980
|
isRequired: false,
|
|
1605
1981
|
transformFunction: null
|
|
1606
1982
|
},
|
|
1983
|
+
emptyValue: {
|
|
1984
|
+
classPropertyName: "emptyValue",
|
|
1985
|
+
publicName: "emptyValue",
|
|
1986
|
+
isSignal: true,
|
|
1987
|
+
isRequired: false,
|
|
1988
|
+
transformFunction: null
|
|
1989
|
+
},
|
|
1990
|
+
warnMissingLabels: {
|
|
1991
|
+
classPropertyName: "warnMissingLabels",
|
|
1992
|
+
publicName: "warnMissingLabels",
|
|
1993
|
+
isSignal: true,
|
|
1994
|
+
isRequired: false,
|
|
1995
|
+
transformFunction: null
|
|
1996
|
+
},
|
|
1997
|
+
nulls: {
|
|
1998
|
+
classPropertyName: "nulls",
|
|
1999
|
+
publicName: "nulls",
|
|
2000
|
+
isSignal: true,
|
|
2001
|
+
isRequired: false,
|
|
2002
|
+
transformFunction: null
|
|
2003
|
+
},
|
|
2004
|
+
movableColumns: {
|
|
2005
|
+
classPropertyName: "movableColumns",
|
|
2006
|
+
publicName: "movableColumns",
|
|
2007
|
+
isSignal: true,
|
|
2008
|
+
isRequired: false,
|
|
2009
|
+
transformFunction: null
|
|
2010
|
+
},
|
|
2011
|
+
rowFilter: {
|
|
2012
|
+
classPropertyName: "rowFilter",
|
|
2013
|
+
publicName: "rowFilter",
|
|
2014
|
+
isSignal: true,
|
|
2015
|
+
isRequired: false,
|
|
2016
|
+
transformFunction: null
|
|
2017
|
+
},
|
|
1607
2018
|
rowId: {
|
|
1608
2019
|
classPropertyName: "rowId",
|
|
1609
2020
|
publicName: "rowId",
|
|
@@ -1723,6 +2134,13 @@ var DataGrid = class DataGrid {
|
|
|
1723
2134
|
isRequired: false,
|
|
1724
2135
|
transformFunction: null
|
|
1725
2136
|
},
|
|
2137
|
+
copyHandler: {
|
|
2138
|
+
classPropertyName: "copyHandler",
|
|
2139
|
+
publicName: "copyHandler",
|
|
2140
|
+
isSignal: true,
|
|
2141
|
+
isRequired: false,
|
|
2142
|
+
transformFunction: null
|
|
2143
|
+
},
|
|
1726
2144
|
virtualize: {
|
|
1727
2145
|
classPropertyName: "virtualize",
|
|
1728
2146
|
publicName: "virtualize",
|
|
@@ -1751,6 +2169,20 @@ var DataGrid = class DataGrid {
|
|
|
1751
2169
|
isRequired: false,
|
|
1752
2170
|
transformFunction: null
|
|
1753
2171
|
},
|
|
2172
|
+
filterField: {
|
|
2173
|
+
classPropertyName: "filterField",
|
|
2174
|
+
publicName: "filterField",
|
|
2175
|
+
isSignal: true,
|
|
2176
|
+
isRequired: false,
|
|
2177
|
+
transformFunction: null
|
|
2178
|
+
},
|
|
2179
|
+
thenBy: {
|
|
2180
|
+
classPropertyName: "thenBy",
|
|
2181
|
+
publicName: "thenBy",
|
|
2182
|
+
isSignal: true,
|
|
2183
|
+
isRequired: false,
|
|
2184
|
+
transformFunction: null
|
|
2185
|
+
},
|
|
1754
2186
|
sort: {
|
|
1755
2187
|
classPropertyName: "sort",
|
|
1756
2188
|
publicName: "sort",
|
|
@@ -1776,6 +2208,8 @@ var DataGrid = class DataGrid {
|
|
|
1776
2208
|
outputs: {
|
|
1777
2209
|
selected: "selectedChange",
|
|
1778
2210
|
filter: "filterChange",
|
|
2211
|
+
filterField: "filterFieldChange",
|
|
2212
|
+
thenBy: "thenByChange",
|
|
1779
2213
|
sort: "sortChange",
|
|
1780
2214
|
columnOrder: "columnOrderChange",
|
|
1781
2215
|
page: "pageChange",
|
|
@@ -1783,7 +2217,8 @@ var DataGrid = class DataGrid {
|
|
|
1783
2217
|
filesSelected: "filesSelected",
|
|
1784
2218
|
fileRemove: "fileRemove",
|
|
1785
2219
|
rowActivate: "rowActivate",
|
|
1786
|
-
exportFile: "exportFile"
|
|
2220
|
+
exportFile: "exportFile",
|
|
2221
|
+
copy: "copy"
|
|
1787
2222
|
},
|
|
1788
2223
|
viewQueries: [{
|
|
1789
2224
|
propertyName: "rootRef",
|
|
@@ -1803,11 +2238,22 @@ var DataGrid = class DataGrid {
|
|
|
1803
2238
|
<div #rootEl class="data-grid {{ className() }}">
|
|
1804
2239
|
@if (labels().search || hidableColumns().length > 0 || hasExportButtons()) {
|
|
1805
2240
|
<div class="data-grid-toolbar">
|
|
2241
|
+
@if (labels().search && labels().searchField && labels().searchAllFields) {
|
|
2242
|
+
<select class="data-grid-search-field" [attr.aria-label]="labels().searchField" [value]="filterField()" (change)="onFilterFieldChange($event)">
|
|
2243
|
+
<option value="">{{ labels().searchAllFields }}</option>
|
|
2244
|
+
@for (c of filterableColumns(); track c.id) {
|
|
2245
|
+
<option [value]="c.id">{{ c.header }}</option>
|
|
2246
|
+
}
|
|
2247
|
+
</select>
|
|
2248
|
+
}
|
|
1806
2249
|
@if (labels().search; as searchLabel) {
|
|
1807
|
-
<input
|
|
2250
|
+
<lily-search-input className="data-grid-search" [label]="searchLabel" [value]="filter()" (valueChange)="setFilter($event)" (keydown)="onFilterKeydown($event)" />
|
|
1808
2251
|
}
|
|
1809
2252
|
@if (hasExportButtons()) {
|
|
1810
2253
|
<div class="data-grid-export" role="group" [attr.aria-label]="labels().export ?? null">
|
|
2254
|
+
@if (labels().copy; as copy) {
|
|
2255
|
+
<button type="button" class="data-grid-export-button" data-grid-export-format="copy" [disabled]="exportEntries().length === 0" (click)="runCopy()">{{ copy }}</button>
|
|
2256
|
+
}
|
|
1811
2257
|
@if (labels().exportCsv; as csv) {
|
|
1812
2258
|
<button type="button" class="data-grid-export-button" data-grid-export-format="csv" [disabled]="exportEntries().length === 0" (click)="runExport('csv')">{{ csv }}</button>
|
|
1813
2259
|
}
|
|
@@ -1851,16 +2297,26 @@ var DataGrid = class DataGrid {
|
|
|
1851
2297
|
}
|
|
1852
2298
|
|
|
1853
2299
|
@if (pageSize()) {
|
|
1854
|
-
|
|
1855
|
-
<
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
2300
|
+
@if (labels().pagination; as pagination) {
|
|
2301
|
+
<lily-pagination-nav className="data-grid-pagination" [label]="pagination">
|
|
2302
|
+
<ng-container *ngTemplateOutlet="pager" />
|
|
2303
|
+
</lily-pagination-nav>
|
|
2304
|
+
} @else {
|
|
2305
|
+
<div class="data-grid-pagination">
|
|
2306
|
+
<ng-container *ngTemplateOutlet="pager" />
|
|
2307
|
+
</div>
|
|
2308
|
+
}
|
|
1859
2309
|
}
|
|
1860
2310
|
|
|
1861
2311
|
<p class="data-grid-status" aria-live="polite">{{ statusMessage() }}</p>
|
|
1862
2312
|
</div>
|
|
1863
2313
|
|
|
2314
|
+
<ng-template #pager>
|
|
2315
|
+
<button type="button" class="data-grid-page-previous" [disabled]="clampedPage() <= 1" (click)="goToPage(clampedPage() - 1)">{{ labels().previousPage }}</button>
|
|
2316
|
+
<span class="data-grid-page-status">{{ labels().pageStatus?.(clampedPage(), pageCount(), sortedEntries().length) }}</span>
|
|
2317
|
+
<button type="button" class="data-grid-page-next" [disabled]="clampedPage() >= pageCount()" (click)="goToPage(clampedPage() + 1)">{{ labels().nextPage }}</button>
|
|
2318
|
+
</ng-template>
|
|
2319
|
+
|
|
1864
2320
|
<ng-template #tableTemplate>
|
|
1865
2321
|
<lily-data-table
|
|
1866
2322
|
[label]="label()"
|
|
@@ -1908,15 +2364,16 @@ var DataGrid = class DataGrid {
|
|
|
1908
2364
|
[attr.data-pinned]="column.pinned ?? null"
|
|
1909
2365
|
[attr.tabindex]="isGrid() ? (activeRow() === -1 && activeCol() === colOffset() + i ? 0 : -1) : null"
|
|
1910
2366
|
[attr.aria-sort]="ariaSortFor(column)"
|
|
2367
|
+
[attr.data-sort-order]="sortOrderFor(column)"
|
|
1911
2368
|
[attr.aria-describedby]="column.type === 'rollup' && labels().rollupDescription ? gridId + '-' + column.id + '-description' : null"
|
|
1912
2369
|
[attr.style]="cellStyle(column)"
|
|
1913
|
-
[attr.draggable]="column
|
|
2370
|
+
[attr.draggable]="isMovable(column) ? 'true' : null"
|
|
1914
2371
|
(dragstart)="onHeaderDragStart(column, $event)"
|
|
1915
2372
|
(dragover)="onHeaderDragOver($event)"
|
|
1916
2373
|
(drop)="onHeaderDrop(column.id, $event)"
|
|
1917
2374
|
>
|
|
1918
2375
|
@if (column.sortable) {
|
|
1919
|
-
<button type="button" class="data-grid-sort-button" data-grid-own [attr.tabindex]="isGrid() ? -1 : null" (click)="toggleSort(column)">
|
|
2376
|
+
<button type="button" class="data-grid-sort-button" data-grid-own [attr.tabindex]="isGrid() ? -1 : null" (click)="toggleSort(column, $any($event).shiftKey)">
|
|
1920
2377
|
@if (column.headerCell; as headerTemplate) {
|
|
1921
2378
|
<ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: column }" />
|
|
1922
2379
|
} @else {<ng-container>{{ column.header }}</ng-container>}
|
|
@@ -1993,6 +2450,7 @@ var DataGrid = class DataGrid {
|
|
|
1993
2450
|
[attr.data-cell-type]="column.type ?? null"
|
|
1994
2451
|
[attr.data-pinned]="column.pinned ?? null"
|
|
1995
2452
|
[attr.data-error]="column.errorOf?.(entry.row) !== undefined ? '' : null"
|
|
2453
|
+
[attr.data-empty]="isMissing(ctx.value) ? '' : null"
|
|
1996
2454
|
[attr.tabindex]="isGrid() ? (activeRow() === item.index && activeCol() === colOffset() + colIndex ? 0 : -1) : null"
|
|
1997
2455
|
[attr.style]="cellStyle(column)"
|
|
1998
2456
|
[lilyDataGridAttrs]="column.cellAttrs?.(ctx)"
|
|
@@ -2013,6 +2471,7 @@ var DataGrid = class DataGrid {
|
|
|
2013
2471
|
[attr.data-readonly]="column.type === 'rollup' ? '' : null"
|
|
2014
2472
|
[attr.aria-readonly]="column.type === 'rollup' ? 'true' : null"
|
|
2015
2473
|
[attr.data-error]="column.errorOf?.(entry.row) !== undefined ? '' : null"
|
|
2474
|
+
[attr.data-empty]="isMissing(ctx.value) ? '' : null"
|
|
2016
2475
|
[attr.data-editing]="isEditingCell(entry, column) ? '' : null"
|
|
2017
2476
|
[attr.tabindex]="isGrid() ? (activeRow() === item.index && activeCol() === colOffset() + colIndex ? 0 : -1) : null"
|
|
2018
2477
|
[attr.style]="cellStyle(column)"
|
|
@@ -2136,6 +2595,8 @@ var DataGrid = class DataGrid {
|
|
|
2136
2595
|
} @else {
|
|
2137
2596
|
@if (column.cell; as cellTemplate) {
|
|
2138
2597
|
<ng-container *ngTemplateOutlet="cellTemplate; context: { $implicit: ctx }" />
|
|
2598
|
+
} @else if (isMissing(ctx.value) && (column.emptyValue ?? emptyValue())) {
|
|
2599
|
+
<ng-container>{{ column.emptyValue ?? emptyValue() }}</ng-container>
|
|
2139
2600
|
} @else if (column.type === 'person') {
|
|
2140
2601
|
@let people = peopleIn(ctx.value);
|
|
2141
2602
|
@if (people.length > 0) {
|
|
@@ -2280,6 +2741,25 @@ var DataGrid = class DataGrid {
|
|
|
2280
2741
|
type: DataTableTH,
|
|
2281
2742
|
selector: "th[lily-data-table-th]",
|
|
2282
2743
|
inputs: ["scope", "className"]
|
|
2744
|
+
},
|
|
2745
|
+
{
|
|
2746
|
+
kind: "component",
|
|
2747
|
+
type: PaginationNav,
|
|
2748
|
+
selector: "lily-pagination-nav",
|
|
2749
|
+
inputs: ["label", "className"]
|
|
2750
|
+
},
|
|
2751
|
+
{
|
|
2752
|
+
kind: "component",
|
|
2753
|
+
type: SearchInput,
|
|
2754
|
+
selector: "lily-search-input",
|
|
2755
|
+
inputs: [
|
|
2756
|
+
"label",
|
|
2757
|
+
"className",
|
|
2758
|
+
"value",
|
|
2759
|
+
"required",
|
|
2760
|
+
"disabled"
|
|
2761
|
+
],
|
|
2762
|
+
outputs: ["valueChange"]
|
|
2283
2763
|
}
|
|
2284
2764
|
],
|
|
2285
2765
|
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
@@ -2305,18 +2785,31 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2305
2785
|
DataTableHead,
|
|
2306
2786
|
DataTableRow,
|
|
2307
2787
|
DataTableTD,
|
|
2308
|
-
DataTableTH
|
|
2788
|
+
DataTableTH,
|
|
2789
|
+
PaginationNav,
|
|
2790
|
+
SearchInput
|
|
2309
2791
|
],
|
|
2310
2792
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2311
2793
|
template: `
|
|
2312
2794
|
<div #rootEl class="data-grid {{ className() }}">
|
|
2313
2795
|
@if (labels().search || hidableColumns().length > 0 || hasExportButtons()) {
|
|
2314
2796
|
<div class="data-grid-toolbar">
|
|
2797
|
+
@if (labels().search && labels().searchField && labels().searchAllFields) {
|
|
2798
|
+
<select class="data-grid-search-field" [attr.aria-label]="labels().searchField" [value]="filterField()" (change)="onFilterFieldChange($event)">
|
|
2799
|
+
<option value="">{{ labels().searchAllFields }}</option>
|
|
2800
|
+
@for (c of filterableColumns(); track c.id) {
|
|
2801
|
+
<option [value]="c.id">{{ c.header }}</option>
|
|
2802
|
+
}
|
|
2803
|
+
</select>
|
|
2804
|
+
}
|
|
2315
2805
|
@if (labels().search; as searchLabel) {
|
|
2316
|
-
<input
|
|
2806
|
+
<lily-search-input className="data-grid-search" [label]="searchLabel" [value]="filter()" (valueChange)="setFilter($event)" (keydown)="onFilterKeydown($event)" />
|
|
2317
2807
|
}
|
|
2318
2808
|
@if (hasExportButtons()) {
|
|
2319
2809
|
<div class="data-grid-export" role="group" [attr.aria-label]="labels().export ?? null">
|
|
2810
|
+
@if (labels().copy; as copy) {
|
|
2811
|
+
<button type="button" class="data-grid-export-button" data-grid-export-format="copy" [disabled]="exportEntries().length === 0" (click)="runCopy()">{{ copy }}</button>
|
|
2812
|
+
}
|
|
2320
2813
|
@if (labels().exportCsv; as csv) {
|
|
2321
2814
|
<button type="button" class="data-grid-export-button" data-grid-export-format="csv" [disabled]="exportEntries().length === 0" (click)="runExport('csv')">{{ csv }}</button>
|
|
2322
2815
|
}
|
|
@@ -2360,16 +2853,26 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2360
2853
|
}
|
|
2361
2854
|
|
|
2362
2855
|
@if (pageSize()) {
|
|
2363
|
-
|
|
2364
|
-
<
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2856
|
+
@if (labels().pagination; as pagination) {
|
|
2857
|
+
<lily-pagination-nav className="data-grid-pagination" [label]="pagination">
|
|
2858
|
+
<ng-container *ngTemplateOutlet="pager" />
|
|
2859
|
+
</lily-pagination-nav>
|
|
2860
|
+
} @else {
|
|
2861
|
+
<div class="data-grid-pagination">
|
|
2862
|
+
<ng-container *ngTemplateOutlet="pager" />
|
|
2863
|
+
</div>
|
|
2864
|
+
}
|
|
2368
2865
|
}
|
|
2369
2866
|
|
|
2370
2867
|
<p class="data-grid-status" aria-live="polite">{{ statusMessage() }}</p>
|
|
2371
2868
|
</div>
|
|
2372
2869
|
|
|
2870
|
+
<ng-template #pager>
|
|
2871
|
+
<button type="button" class="data-grid-page-previous" [disabled]="clampedPage() <= 1" (click)="goToPage(clampedPage() - 1)">{{ labels().previousPage }}</button>
|
|
2872
|
+
<span class="data-grid-page-status">{{ labels().pageStatus?.(clampedPage(), pageCount(), sortedEntries().length) }}</span>
|
|
2873
|
+
<button type="button" class="data-grid-page-next" [disabled]="clampedPage() >= pageCount()" (click)="goToPage(clampedPage() + 1)">{{ labels().nextPage }}</button>
|
|
2874
|
+
</ng-template>
|
|
2875
|
+
|
|
2373
2876
|
<ng-template #tableTemplate>
|
|
2374
2877
|
<lily-data-table
|
|
2375
2878
|
[label]="label()"
|
|
@@ -2417,15 +2920,16 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2417
2920
|
[attr.data-pinned]="column.pinned ?? null"
|
|
2418
2921
|
[attr.tabindex]="isGrid() ? (activeRow() === -1 && activeCol() === colOffset() + i ? 0 : -1) : null"
|
|
2419
2922
|
[attr.aria-sort]="ariaSortFor(column)"
|
|
2923
|
+
[attr.data-sort-order]="sortOrderFor(column)"
|
|
2420
2924
|
[attr.aria-describedby]="column.type === 'rollup' && labels().rollupDescription ? gridId + '-' + column.id + '-description' : null"
|
|
2421
2925
|
[attr.style]="cellStyle(column)"
|
|
2422
|
-
[attr.draggable]="column
|
|
2926
|
+
[attr.draggable]="isMovable(column) ? 'true' : null"
|
|
2423
2927
|
(dragstart)="onHeaderDragStart(column, $event)"
|
|
2424
2928
|
(dragover)="onHeaderDragOver($event)"
|
|
2425
2929
|
(drop)="onHeaderDrop(column.id, $event)"
|
|
2426
2930
|
>
|
|
2427
2931
|
@if (column.sortable) {
|
|
2428
|
-
<button type="button" class="data-grid-sort-button" data-grid-own [attr.tabindex]="isGrid() ? -1 : null" (click)="toggleSort(column)">
|
|
2932
|
+
<button type="button" class="data-grid-sort-button" data-grid-own [attr.tabindex]="isGrid() ? -1 : null" (click)="toggleSort(column, $any($event).shiftKey)">
|
|
2429
2933
|
@if (column.headerCell; as headerTemplate) {
|
|
2430
2934
|
<ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: column }" />
|
|
2431
2935
|
} @else {<ng-container>{{ column.header }}</ng-container>}
|
|
@@ -2502,6 +3006,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2502
3006
|
[attr.data-cell-type]="column.type ?? null"
|
|
2503
3007
|
[attr.data-pinned]="column.pinned ?? null"
|
|
2504
3008
|
[attr.data-error]="column.errorOf?.(entry.row) !== undefined ? '' : null"
|
|
3009
|
+
[attr.data-empty]="isMissing(ctx.value) ? '' : null"
|
|
2505
3010
|
[attr.tabindex]="isGrid() ? (activeRow() === item.index && activeCol() === colOffset() + colIndex ? 0 : -1) : null"
|
|
2506
3011
|
[attr.style]="cellStyle(column)"
|
|
2507
3012
|
[lilyDataGridAttrs]="column.cellAttrs?.(ctx)"
|
|
@@ -2522,6 +3027,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2522
3027
|
[attr.data-readonly]="column.type === 'rollup' ? '' : null"
|
|
2523
3028
|
[attr.aria-readonly]="column.type === 'rollup' ? 'true' : null"
|
|
2524
3029
|
[attr.data-error]="column.errorOf?.(entry.row) !== undefined ? '' : null"
|
|
3030
|
+
[attr.data-empty]="isMissing(ctx.value) ? '' : null"
|
|
2525
3031
|
[attr.data-editing]="isEditingCell(entry, column) ? '' : null"
|
|
2526
3032
|
[attr.tabindex]="isGrid() ? (activeRow() === item.index && activeCol() === colOffset() + colIndex ? 0 : -1) : null"
|
|
2527
3033
|
[attr.style]="cellStyle(column)"
|
|
@@ -2645,6 +3151,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2645
3151
|
} @else {
|
|
2646
3152
|
@if (column.cell; as cellTemplate) {
|
|
2647
3153
|
<ng-container *ngTemplateOutlet="cellTemplate; context: { $implicit: ctx }" />
|
|
3154
|
+
} @else if (isMissing(ctx.value) && (column.emptyValue ?? emptyValue())) {
|
|
3155
|
+
<ng-container>{{ column.emptyValue ?? emptyValue() }}</ng-container>
|
|
2648
3156
|
} @else if (column.type === 'person') {
|
|
2649
3157
|
@let people = peopleIn(ctx.value);
|
|
2650
3158
|
@if (people.length > 0) {
|
|
@@ -2774,6 +3282,46 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2774
3282
|
required: false
|
|
2775
3283
|
}]
|
|
2776
3284
|
}],
|
|
3285
|
+
emptyValue: [{
|
|
3286
|
+
type: i0.Input,
|
|
3287
|
+
args: [{
|
|
3288
|
+
isSignal: true,
|
|
3289
|
+
alias: "emptyValue",
|
|
3290
|
+
required: false
|
|
3291
|
+
}]
|
|
3292
|
+
}],
|
|
3293
|
+
warnMissingLabels: [{
|
|
3294
|
+
type: i0.Input,
|
|
3295
|
+
args: [{
|
|
3296
|
+
isSignal: true,
|
|
3297
|
+
alias: "warnMissingLabels",
|
|
3298
|
+
required: false
|
|
3299
|
+
}]
|
|
3300
|
+
}],
|
|
3301
|
+
nulls: [{
|
|
3302
|
+
type: i0.Input,
|
|
3303
|
+
args: [{
|
|
3304
|
+
isSignal: true,
|
|
3305
|
+
alias: "nulls",
|
|
3306
|
+
required: false
|
|
3307
|
+
}]
|
|
3308
|
+
}],
|
|
3309
|
+
movableColumns: [{
|
|
3310
|
+
type: i0.Input,
|
|
3311
|
+
args: [{
|
|
3312
|
+
isSignal: true,
|
|
3313
|
+
alias: "movableColumns",
|
|
3314
|
+
required: false
|
|
3315
|
+
}]
|
|
3316
|
+
}],
|
|
3317
|
+
rowFilter: [{
|
|
3318
|
+
type: i0.Input,
|
|
3319
|
+
args: [{
|
|
3320
|
+
isSignal: true,
|
|
3321
|
+
alias: "rowFilter",
|
|
3322
|
+
required: false
|
|
3323
|
+
}]
|
|
3324
|
+
}],
|
|
2777
3325
|
rowId: [{
|
|
2778
3326
|
type: i0.Input,
|
|
2779
3327
|
args: [{
|
|
@@ -2910,6 +3458,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2910
3458
|
required: false
|
|
2911
3459
|
}]
|
|
2912
3460
|
}],
|
|
3461
|
+
copyHandler: [{
|
|
3462
|
+
type: i0.Input,
|
|
3463
|
+
args: [{
|
|
3464
|
+
isSignal: true,
|
|
3465
|
+
alias: "copyHandler",
|
|
3466
|
+
required: false
|
|
3467
|
+
}]
|
|
3468
|
+
}],
|
|
2913
3469
|
virtualize: [{
|
|
2914
3470
|
type: i0.Input,
|
|
2915
3471
|
args: [{
|
|
@@ -2948,6 +3504,28 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2948
3504
|
type: i0.Output,
|
|
2949
3505
|
args: ["filterChange"]
|
|
2950
3506
|
}],
|
|
3507
|
+
filterField: [{
|
|
3508
|
+
type: i0.Input,
|
|
3509
|
+
args: [{
|
|
3510
|
+
isSignal: true,
|
|
3511
|
+
alias: "filterField",
|
|
3512
|
+
required: false
|
|
3513
|
+
}]
|
|
3514
|
+
}, {
|
|
3515
|
+
type: i0.Output,
|
|
3516
|
+
args: ["filterFieldChange"]
|
|
3517
|
+
}],
|
|
3518
|
+
thenBy: [{
|
|
3519
|
+
type: i0.Input,
|
|
3520
|
+
args: [{
|
|
3521
|
+
isSignal: true,
|
|
3522
|
+
alias: "thenBy",
|
|
3523
|
+
required: false
|
|
3524
|
+
}]
|
|
3525
|
+
}, {
|
|
3526
|
+
type: i0.Output,
|
|
3527
|
+
args: ["thenByChange"]
|
|
3528
|
+
}],
|
|
2951
3529
|
sort: [{
|
|
2952
3530
|
type: i0.Input,
|
|
2953
3531
|
args: [{
|
|
@@ -3001,6 +3579,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3001
3579
|
type: i0.Output,
|
|
3002
3580
|
args: ["exportFile"]
|
|
3003
3581
|
}],
|
|
3582
|
+
copy: [{
|
|
3583
|
+
type: i0.Output,
|
|
3584
|
+
args: ["copy"]
|
|
3585
|
+
}],
|
|
3004
3586
|
rootRef: [{
|
|
3005
3587
|
type: i0.ViewChild,
|
|
3006
3588
|
args: ["rootEl", { isSignal: true }]
|
|
@@ -3011,6 +3593,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3011
3593
|
}]
|
|
3012
3594
|
}
|
|
3013
3595
|
});
|
|
3014
|
-
export { DataGrid, DataGridAttrs, MIME_TYPES, aggregateValues, cellText, civilDateToDate, columnName, crc32, downloadBlob, exportBlob, exportFilename, filesOf, isCivilDate, isCivilTime, isExportFormula, isInvalidForType, isValidTimeZone, isZonedTime, linkLabel, linkParts, nextDataGridId, peopleOf, personName, resolveColumn, safeHref, toCsv, toDelimited, toTsv, toXlsx, zipStore, zonedMinutes, zonedUtcTime };
|
|
3596
|
+
export { ANNOUNCEMENT_LABELS, CONTROL_LABELS, DataGrid, DataGridAttrs, EMPTY_HISTORY, HISTORY_LIMIT, MIME_TYPES, TYPEAHEAD_RESET_MS, aggregateValues, cellText, civilDateToDate, columnName, copyToClipboard, crc32, downloadBlob, exportBlob, exportFilename, filesOf, findTypeaheadMatch, foldText, highlightSegments, historyKey, isCivilDate, isCivilTime, isExportFormula, isInvalidForType, isTypeaheadKey, isValidTimeZone, isZonedTime, linkLabel, linkParts, matchesSearch, missingLabels, nextDataGridId, nextTypeaheadState, peopleOf, personName, recordEdit, redoEdit, requiredLabels, resolveColumn, safeHref, searchTerms, toClipboardText, toCsv, toDelimited, toTsv, toXlsx, undoEdit, zipStore, zonedMinutes, zonedUtcTime };
|
|
3015
3597
|
|
|
3016
3598
|
//# sourceMappingURL=lilydesignsystem-angular-data-grid.mjs.map
|