@gridnexa/angular 0.0.1 → 0.0.3
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 +64 -5
- package/dist/index.cjs +271 -26
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +271 -26
- package/package.json +44 -11
package/README.md
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
# @gridnexa/angular
|
|
2
2
|
|
|
3
|
-
Angular
|
|
3
|
+
Enterprise Angular data grid for modern UI products that need spreadsheet-grade power without giving up product polish.
|
|
4
|
+
|
|
5
|
+
GridNexa is built for React, Angular, Vue, and JavaScript teams. The Angular package gives you a native Angular grid with typed columns, row selection, row numbers, sorting, filtering, advanced filters, formulas, inline editing, grouped headers, grouping, pivoting, tree data, master/detail, CSV export, Excel export, and theme-ready UI.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @gridnexa/angular
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @gridnexa/angular
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
4
18
|
|
|
5
19
|
```ts
|
|
6
20
|
import { Component } from "@angular/core";
|
|
7
|
-
import { GridNexaAngularComponent } from "@gridnexa/angular";
|
|
21
|
+
import { GridNexaAngularComponent, type Column } from "@gridnexa/angular";
|
|
22
|
+
|
|
23
|
+
interface Employee {
|
|
24
|
+
id: number;
|
|
25
|
+
name: string;
|
|
26
|
+
department: string;
|
|
27
|
+
city: string;
|
|
28
|
+
score: number;
|
|
29
|
+
adjustedScore: string;
|
|
30
|
+
}
|
|
8
31
|
|
|
9
32
|
@Component({
|
|
10
33
|
selector: "app-root",
|
|
@@ -16,16 +39,52 @@ import { GridNexaAngularComponent } from "@gridnexa/angular";
|
|
|
16
39
|
[rows]="rows"
|
|
17
40
|
[rowNumbers]="true"
|
|
18
41
|
[checkboxSelection]="true"
|
|
42
|
+
[enableFillHandle]="true"
|
|
43
|
+
[enableUndoRedo]="true"
|
|
44
|
+
[pageSize]="20"
|
|
45
|
+
[getRowId]="getRowId"
|
|
19
46
|
(rowSelectionChange)="onSelection($event)"
|
|
47
|
+
(cellValueChange)="onCellValueChange($event)"
|
|
20
48
|
/>
|
|
21
49
|
`,
|
|
22
50
|
})
|
|
23
51
|
export class AppComponent {
|
|
24
|
-
columns =
|
|
25
|
-
|
|
52
|
+
columns: Column<Employee>[] = [
|
|
53
|
+
{ id: "name", field: "name", headerName: "Name", sortable: true, filter: "text", editable: true },
|
|
54
|
+
{ id: "department", field: "department", headerName: "Department", filter: "set" },
|
|
55
|
+
{ id: "score", field: "score", headerName: "Score", filter: "number", editable: true },
|
|
56
|
+
{ id: "adjustedScore", field: "adjustedScore", headerName: "Adjusted" },
|
|
57
|
+
];
|
|
26
58
|
|
|
27
|
-
|
|
59
|
+
rows: Employee[] = [
|
|
60
|
+
{ id: 1, name: "John Carter", department: "Operations", city: "London", score: 92, adjustedScore: "=score * 1.05" },
|
|
61
|
+
{ id: 2, name: "Alice Moreau", department: "Product", city: "Paris", score: 87, adjustedScore: "=score * 1.05" },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
getRowId = (row: Employee) => row.id;
|
|
65
|
+
|
|
66
|
+
onSelection(rows: Employee[]) {
|
|
28
67
|
console.log(rows);
|
|
29
68
|
}
|
|
69
|
+
|
|
70
|
+
onCellValueChange(event: unknown) {
|
|
71
|
+
console.log(event);
|
|
72
|
+
}
|
|
30
73
|
}
|
|
31
74
|
```
|
|
75
|
+
|
|
76
|
+
## Features
|
|
77
|
+
|
|
78
|
+
- Native Angular data grid with TypeScript column models
|
|
79
|
+
- Excel-style features: formulas, fill, copy/paste, undo/redo, find, CSV export, and Excel export
|
|
80
|
+
- Filtering: column filters, quick filter, external filter, and advanced filter model
|
|
81
|
+
- Data modeling: grouping, pivoting, tree data indentation, master/detail, and transactions
|
|
82
|
+
- Grid UX: row selection, row numbers, pagination, status output, and tools panel
|
|
83
|
+
- Columns: merged headers, column visibility tools, column reorder, and row reorder
|
|
84
|
+
- Events: row selection, cell value changes, pivot model changes, advanced filter changes, and server-side operation events
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- Website: https://www.gridnexa.in/
|
|
89
|
+
- Help: https://www.gridnexa.in/help
|
|
90
|
+
- Repository: https://github.com/mhalungekar9/SmartGrid
|
package/dist/index.cjs
CHANGED
|
@@ -135,6 +135,12 @@ function buildPivot(rows, columns, groupBy, pivotBy, pivotValueColumns, pivotAgg
|
|
|
135
135
|
});
|
|
136
136
|
return { columns: pivotColumns, rows: pivotRows, active: true };
|
|
137
137
|
}
|
|
138
|
+
function buildGroupSummary(rows, columns, groupBy) {
|
|
139
|
+
return columns.filter((column) => column.field !== groupBy).map((column) => {
|
|
140
|
+
const values = rows.map((row) => Number(value(row, column, columns))).filter(Number.isFinite);
|
|
141
|
+
return values.length ? `${column.headerName}: ${values.reduce((sum, entry) => sum + entry, 0).toLocaleString()}` : "";
|
|
142
|
+
}).filter(Boolean).slice(0, 3).join(" | ");
|
|
143
|
+
}
|
|
138
144
|
function cell(text, tag = "td") {
|
|
139
145
|
const element = document.createElement(tag);
|
|
140
146
|
element.textContent = text;
|
|
@@ -176,9 +182,17 @@ var GridNexaAngularComponent = class {
|
|
|
176
182
|
sortState = null;
|
|
177
183
|
hiddenColumnIds = /* @__PURE__ */ new Set();
|
|
178
184
|
activeCell = null;
|
|
185
|
+
rangeAnchor = null;
|
|
186
|
+
rangeEnd = null;
|
|
187
|
+
contextMenu = null;
|
|
188
|
+
expandedDetailIds = /* @__PURE__ */ new Set();
|
|
189
|
+
collapsedGroups = /* @__PURE__ */ new Set();
|
|
190
|
+
collapsedTreeKeys = /* @__PURE__ */ new Set();
|
|
179
191
|
findText = "";
|
|
180
192
|
toolsOpen = false;
|
|
181
193
|
draggedColumnId = null;
|
|
194
|
+
draggedRowIndex = null;
|
|
195
|
+
columnWidths = /* @__PURE__ */ new Map();
|
|
182
196
|
undoStack = [];
|
|
183
197
|
redoStack = [];
|
|
184
198
|
ngAfterViewInit() {
|
|
@@ -186,6 +200,9 @@ var GridNexaAngularComponent = class {
|
|
|
186
200
|
}
|
|
187
201
|
ngOnChanges() {
|
|
188
202
|
this.hiddenColumnIds = new Set(this.columns.filter((column) => column.hidden).map((column) => column.id));
|
|
203
|
+
this.columns.forEach((column) => {
|
|
204
|
+
if (column.width && !this.columnWidths.has(column.id)) this.columnWidths.set(column.id, column.width);
|
|
205
|
+
});
|
|
189
206
|
this.applyTransaction();
|
|
190
207
|
this.render();
|
|
191
208
|
}
|
|
@@ -257,15 +274,36 @@ var GridNexaAngularComponent = class {
|
|
|
257
274
|
}
|
|
258
275
|
fillDown() {
|
|
259
276
|
if (!this.activeCell || !this.enableFillHandle) return;
|
|
277
|
+
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
278
|
+
if (this.rangeAnchor && this.rangeEnd && column) {
|
|
279
|
+
const minRow = Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex);
|
|
280
|
+
const maxRow = Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex);
|
|
281
|
+
const sourceRow2 = this.rows[minRow];
|
|
282
|
+
if (!sourceRow2 || column.editable === false) return;
|
|
283
|
+
const sourceValue = rawValue(sourceRow2, column);
|
|
284
|
+
for (let rowIndex = minRow + 1; rowIndex <= maxRow; rowIndex += 1) {
|
|
285
|
+
const row = this.rows[rowIndex];
|
|
286
|
+
if (row) this.setCellValue(row, rowIndex, column, sourceValue);
|
|
287
|
+
}
|
|
288
|
+
this.render();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
260
291
|
const sourceRow = this.rows[this.activeCell.rowIndex];
|
|
261
292
|
const targetRow = this.rows[this.activeCell.rowIndex + 1];
|
|
262
|
-
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
263
293
|
if (!sourceRow || !targetRow || !column || column.editable === false) return;
|
|
264
294
|
this.setCellValue(targetRow, this.activeCell.rowIndex + 1, column, rawValue(sourceRow, column));
|
|
265
295
|
this.render();
|
|
266
296
|
}
|
|
267
297
|
async copyActiveCell() {
|
|
268
298
|
if (!this.activeCell || typeof navigator === "undefined") return;
|
|
299
|
+
if (this.rangeAnchor && this.rangeEnd) {
|
|
300
|
+
const columns = this.columns.filter((column2) => !this.hiddenColumnIds.has(column2.id) && !column2.hidden);
|
|
301
|
+
const anchorColumn = columns.findIndex((entry) => entry.id === this.rangeAnchor?.columnId);
|
|
302
|
+
const endColumn = columns.findIndex((entry) => entry.id === this.rangeEnd?.columnId);
|
|
303
|
+
const text = this.rows.slice(Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex), Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) + 1).map((row2) => columns.slice(Math.min(anchorColumn, endColumn), Math.max(anchorColumn, endColumn) + 1).map((column2) => format(row2, column2, this.columns)).join(" ")).join("\n");
|
|
304
|
+
await navigator.clipboard?.writeText(text);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
269
307
|
const row = this.rows[this.activeCell.rowIndex];
|
|
270
308
|
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
271
309
|
if (!row || !column) return;
|
|
@@ -273,11 +311,17 @@ var GridNexaAngularComponent = class {
|
|
|
273
311
|
}
|
|
274
312
|
async pasteActiveCell() {
|
|
275
313
|
if (!this.activeCell || typeof navigator === "undefined") return;
|
|
276
|
-
const row = this.rows[this.activeCell.rowIndex];
|
|
277
|
-
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
278
|
-
if (!row || !column || column.editable === false) return;
|
|
279
314
|
const text = await navigator.clipboard?.readText();
|
|
280
|
-
this.
|
|
315
|
+
const columns = this.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden);
|
|
316
|
+
const startColumn = columns.findIndex((entry) => entry.id === this.activeCell?.columnId);
|
|
317
|
+
text.split(/\r?\n/).forEach((line, rowOffset) => {
|
|
318
|
+
line.split(" ").forEach((value2, columnOffset) => {
|
|
319
|
+
const rowIndex = this.activeCell.rowIndex + rowOffset;
|
|
320
|
+
const row = this.rows[rowIndex];
|
|
321
|
+
const column = columns[startColumn + columnOffset];
|
|
322
|
+
if (row && column && column.editable !== false) this.setCellValue(row, rowIndex, column, value2);
|
|
323
|
+
});
|
|
324
|
+
});
|
|
281
325
|
this.render();
|
|
282
326
|
}
|
|
283
327
|
moveRow(rowIndex, direction) {
|
|
@@ -289,6 +333,14 @@ var GridNexaAngularComponent = class {
|
|
|
289
333
|
this.rows = rows;
|
|
290
334
|
this.render();
|
|
291
335
|
}
|
|
336
|
+
reorderRow(sourceIndex, targetIndex) {
|
|
337
|
+
if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= this.rows.length || targetIndex >= this.rows.length) return;
|
|
338
|
+
const rows = [...this.rows];
|
|
339
|
+
const [row] = rows.splice(sourceIndex, 1);
|
|
340
|
+
rows.splice(targetIndex, 0, row);
|
|
341
|
+
this.rows = rows;
|
|
342
|
+
this.render();
|
|
343
|
+
}
|
|
292
344
|
moveColumn(sourceId, targetId) {
|
|
293
345
|
if (sourceId === targetId) return;
|
|
294
346
|
const columns = [...this.columns];
|
|
@@ -300,6 +352,63 @@ var GridNexaAngularComponent = class {
|
|
|
300
352
|
this.columns = columns;
|
|
301
353
|
this.render();
|
|
302
354
|
}
|
|
355
|
+
makeDisplayRows(rows) {
|
|
356
|
+
if (this.groupBy) {
|
|
357
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
358
|
+
rows.forEach((row) => {
|
|
359
|
+
const key = String(row[this.groupBy] ?? "Ungrouped");
|
|
360
|
+
buckets.set(key, [...buckets.get(key) ?? [], row]);
|
|
361
|
+
});
|
|
362
|
+
return Array.from(buckets.entries()).flatMap(([key, bucket]) => [
|
|
363
|
+
{ kind: "group", key, label: key, rows: bucket, summaries: buildGroupSummary(bucket, this.columns, this.groupBy) },
|
|
364
|
+
...this.collapsedGroups.has(key) ? [] : bucket.map((row) => ({ kind: "data", row, rowIndex: this.rows.indexOf(row) }))
|
|
365
|
+
]);
|
|
366
|
+
}
|
|
367
|
+
if (this.getTreeDataPath) {
|
|
368
|
+
return rows.map((row) => {
|
|
369
|
+
const path = this.getTreeDataPath?.(row).filter(Boolean) ?? [];
|
|
370
|
+
return { row, path, key: path.join("/") };
|
|
371
|
+
}).sort((left, right) => left.key.localeCompare(right.key)).filter((entry) => entry.path.slice(0, -1).every((_, index) => !this.collapsedTreeKeys.has(entry.path.slice(0, index + 1).join("/")))).map((entry, _index, entries) => ({
|
|
372
|
+
kind: "data",
|
|
373
|
+
row: entry.row,
|
|
374
|
+
rowIndex: this.rows.indexOf(entry.row),
|
|
375
|
+
depth: Math.max(0, entry.path.length - 1),
|
|
376
|
+
treeKey: entry.key,
|
|
377
|
+
hasChildren: entries.some((other) => other.key.startsWith(`${entry.key}/`))
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
return rows.map((row) => ({ kind: "data", row, rowIndex: this.rows.indexOf(row) }));
|
|
381
|
+
}
|
|
382
|
+
isCellInRange(rowIndex, columnId, columns) {
|
|
383
|
+
if (!this.rangeAnchor || !this.rangeEnd || !this.enableRangeSelection) return false;
|
|
384
|
+
const columnIndex = columns.findIndex((column) => column.id === columnId);
|
|
385
|
+
const anchorIndex = columns.findIndex((column) => column.id === this.rangeAnchor?.columnId);
|
|
386
|
+
const endIndex = columns.findIndex((column) => column.id === this.rangeEnd?.columnId);
|
|
387
|
+
return rowIndex >= Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) && rowIndex <= Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) && columnIndex >= Math.min(anchorIndex, endIndex) && columnIndex <= Math.max(anchorIndex, endIndex);
|
|
388
|
+
}
|
|
389
|
+
startColumnResize(event, column) {
|
|
390
|
+
event.preventDefault();
|
|
391
|
+
event.stopPropagation();
|
|
392
|
+
const startX = event.clientX;
|
|
393
|
+
const startWidth = this.columnWidths.get(column.id) ?? column.width ?? 150;
|
|
394
|
+
const move = (moveEvent) => {
|
|
395
|
+
this.columnWidths.set(column.id, Math.max(72, startWidth + moveEvent.clientX - startX));
|
|
396
|
+
this.render();
|
|
397
|
+
};
|
|
398
|
+
const up = () => {
|
|
399
|
+
document.removeEventListener("mousemove", move);
|
|
400
|
+
document.removeEventListener("mouseup", up);
|
|
401
|
+
};
|
|
402
|
+
document.addEventListener("mousemove", move);
|
|
403
|
+
document.addEventListener("mouseup", up);
|
|
404
|
+
}
|
|
405
|
+
pinnedStyle(column, columns) {
|
|
406
|
+
if (!column.pinned) return "";
|
|
407
|
+
const index = columns.findIndex((entry) => entry.id === column.id);
|
|
408
|
+
const width = (entry) => this.columnWidths.get(entry.id) ?? entry.width ?? 150;
|
|
409
|
+
const offset = column.pinned === "left" ? columns.slice(0, index).filter((entry) => entry.pinned === "left").reduce((sum, entry) => sum + width(entry), 0) : columns.slice(index + 1).filter((entry) => entry.pinned === "right").reduce((sum, entry) => sum + width(entry), 0);
|
|
410
|
+
return `position:sticky;${column.pinned}:${offset}px;z-index:2;background:white;box-shadow:${column.pinned === "left" ? "inset -1px 0 #dbe3ef" : "inset 1px 0 #dbe3ef"};`;
|
|
411
|
+
}
|
|
303
412
|
updateAdvancedFilter(columnId, operator, filterValue) {
|
|
304
413
|
const model = {
|
|
305
414
|
kind: "group",
|
|
@@ -328,13 +437,15 @@ var GridNexaAngularComponent = class {
|
|
|
328
437
|
if (!this.host) return;
|
|
329
438
|
const sourceRows = this.visibleRows();
|
|
330
439
|
const pivot = buildPivot(sourceRows, this.columns, this.groupBy, this.pivotBy, this.pivotValueColumns, this.pivotAggregation);
|
|
331
|
-
const columns = pivot.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden);
|
|
440
|
+
const columns = pivot.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden).sort((left, right) => (left.pinned === "left" ? 0 : left.pinned === "right" ? 2 : 1) - (right.pinned === "left" ? 0 : right.pinned === "right" ? 2 : 1));
|
|
332
441
|
const pageRows = this.pageSize ? pivot.rows.slice(this.pageIndex * this.pageSize, this.pageIndex * this.pageSize + this.pageSize) : pivot.rows;
|
|
442
|
+
const displayRows = pivot.active ? pageRows.map((row) => ({ kind: "data", row, rowIndex: pivot.rows.indexOf(row) })) : this.makeDisplayRows(pageRows);
|
|
333
443
|
const root = document.createElement("div");
|
|
334
444
|
root.className = "gridnexa-angular-grid";
|
|
335
|
-
root.append(this.renderToolbar(columns, pivot.rows), this.renderTable(columns,
|
|
445
|
+
root.append(this.renderToolbar(columns, pivot.rows), this.renderTable(columns, displayRows));
|
|
336
446
|
if (this.toolsOpen) root.appendChild(this.renderToolsPanel());
|
|
337
447
|
root.appendChild(this.renderStatus(pivot.rows.length));
|
|
448
|
+
if (this.contextMenu) root.appendChild(this.renderContextMenu());
|
|
338
449
|
this.host.nativeElement.replaceChildren(root);
|
|
339
450
|
this.serverSideOperation.emit({
|
|
340
451
|
sortModel: this.sortState ? [this.sortState] : [],
|
|
@@ -366,6 +477,16 @@ var GridNexaAngularComponent = class {
|
|
|
366
477
|
this.findText = find.value;
|
|
367
478
|
this.render();
|
|
368
479
|
});
|
|
480
|
+
const quickFilter = document.createElement("input");
|
|
481
|
+
quickFilter.type = "search";
|
|
482
|
+
quickFilter.placeholder = "Quick filter";
|
|
483
|
+
quickFilter.value = this.quickFilterText;
|
|
484
|
+
quickFilter.style.cssText = "min-height:32px;padding:0 10px;border:1px solid #bfdbfe;border-radius:8px";
|
|
485
|
+
quickFilter.addEventListener("input", () => {
|
|
486
|
+
this.quickFilterText = quickFilter.value;
|
|
487
|
+
this.pageIndex = 0;
|
|
488
|
+
this.render();
|
|
489
|
+
});
|
|
369
490
|
const pageCount = this.pageSize ? Math.max(1, Math.ceil(rows.length / this.pageSize)) : 1;
|
|
370
491
|
if (this.pageSize) {
|
|
371
492
|
const prev = this.button("Prev", () => {
|
|
@@ -380,7 +501,7 @@ var GridNexaAngularComponent = class {
|
|
|
380
501
|
next.disabled = this.pageIndex >= pageCount - 1;
|
|
381
502
|
actions.append(prev, ` Page ${this.pageIndex + 1} `, next);
|
|
382
503
|
}
|
|
383
|
-
actions.
|
|
504
|
+
actions.append(quickFilter, find);
|
|
384
505
|
if (this.enableUndoRedo) {
|
|
385
506
|
const undo = this.button("Undo", () => this.undo());
|
|
386
507
|
undo.disabled = !this.undoStack.length;
|
|
@@ -413,7 +534,7 @@ var GridNexaAngularComponent = class {
|
|
|
413
534
|
if (this.rowNumbers) header.appendChild(cell("#", "th"));
|
|
414
535
|
columns.forEach((column) => {
|
|
415
536
|
const th = cell(`${column.headerName}${this.sortState?.columnId === column.id ? this.sortState.direction === "asc" ? " \u2191" : " \u2193" : ""}`, "th");
|
|
416
|
-
th.style.cssText =
|
|
537
|
+
th.style.cssText = `padding:10px;border:1px solid #dbe3ef;background:#f8fbff;text-align:left;width:${this.columnWidths.get(column.id) ?? column.width ?? 150}px;${this.pinnedStyle(column, columns)}`;
|
|
417
538
|
th.draggable = true;
|
|
418
539
|
th.addEventListener("dragstart", () => {
|
|
419
540
|
this.draggedColumnId = column.id;
|
|
@@ -429,12 +550,25 @@ var GridNexaAngularComponent = class {
|
|
|
429
550
|
this.sortState = this.sortState?.columnId !== column.id ? { columnId: column.id, direction: "asc" } : this.sortState.direction === "asc" ? { columnId: column.id, direction: "desc" } : null;
|
|
430
551
|
this.render();
|
|
431
552
|
});
|
|
553
|
+
if (column.resizable !== false) {
|
|
554
|
+
const resizer = document.createElement("span");
|
|
555
|
+
resizer.style.cssText = "float:right;width:7px;height:24px;cursor:col-resize;border-right:2px solid #bfdbfe";
|
|
556
|
+
resizer.addEventListener("mousedown", (event) => this.startColumnResize(event, column));
|
|
557
|
+
th.appendChild(resizer);
|
|
558
|
+
}
|
|
432
559
|
header.appendChild(th);
|
|
433
560
|
});
|
|
434
561
|
thead.appendChild(header);
|
|
435
562
|
table.appendChild(thead);
|
|
436
563
|
const tbody = document.createElement("tbody");
|
|
437
|
-
rows.forEach((
|
|
564
|
+
rows.forEach((entry) => {
|
|
565
|
+
if (entry.kind === "group") this.appendGroupRow(tbody, entry, columns.length + leading);
|
|
566
|
+
if (entry.kind === "data") {
|
|
567
|
+
this.appendRow(tbody, entry.row, entry.rowIndex, columns, leading, entry);
|
|
568
|
+
const rowId = this.rowId(entry.row, entry.rowIndex);
|
|
569
|
+
if (this.masterDetailRenderer && this.expandedDetailIds.has(rowId)) this.appendDetailRow(tbody, entry.row, columns.length + leading);
|
|
570
|
+
}
|
|
571
|
+
});
|
|
438
572
|
table.appendChild(tbody);
|
|
439
573
|
return table;
|
|
440
574
|
}
|
|
@@ -455,8 +589,41 @@ var GridNexaAngularComponent = class {
|
|
|
455
589
|
});
|
|
456
590
|
return row;
|
|
457
591
|
}
|
|
458
|
-
|
|
592
|
+
appendGroupRow(tbody, entry, colSpan) {
|
|
593
|
+
const tr = document.createElement("tr");
|
|
594
|
+
const td = document.createElement("td");
|
|
595
|
+
td.colSpan = colSpan;
|
|
596
|
+
td.style.cssText = "padding:10px;border:1px solid #dbe3ef;background:#eef4ff;color:#153e90;font-weight:800;text-transform:uppercase";
|
|
597
|
+
const toggle = this.button(this.collapsedGroups.has(entry.key) ? "+" : "-", () => {
|
|
598
|
+
this.collapsedGroups.has(entry.key) ? this.collapsedGroups.delete(entry.key) : this.collapsedGroups.add(entry.key);
|
|
599
|
+
this.render();
|
|
600
|
+
});
|
|
601
|
+
td.append(toggle, `${entry.label} ${entry.rows.length} rows${entry.summaries ? ` ${entry.summaries}` : ""}`);
|
|
602
|
+
tr.appendChild(td);
|
|
603
|
+
tbody.appendChild(tr);
|
|
604
|
+
}
|
|
605
|
+
appendDetailRow(tbody, row, colSpan) {
|
|
606
|
+
const detailRow = document.createElement("tr");
|
|
607
|
+
const detail = document.createElement("td");
|
|
608
|
+
detail.colSpan = colSpan;
|
|
609
|
+
detail.style.cssText = "padding:12px;border:1px solid #dbe3ef;background:#f8fbff;color:#334155";
|
|
610
|
+
const content = this.masterDetailRenderer?.(row);
|
|
611
|
+
content instanceof Node ? detail.appendChild(content) : detail.textContent = String(content ?? "");
|
|
612
|
+
detailRow.appendChild(detail);
|
|
613
|
+
tbody.appendChild(detailRow);
|
|
614
|
+
}
|
|
615
|
+
appendRow(tbody, row, rowIndex, columns, leading, display) {
|
|
459
616
|
const tr = document.createElement("tr");
|
|
617
|
+
tr.draggable = true;
|
|
618
|
+
tr.addEventListener("dragstart", () => {
|
|
619
|
+
this.draggedRowIndex = rowIndex;
|
|
620
|
+
});
|
|
621
|
+
tr.addEventListener("dragover", (event) => event.preventDefault());
|
|
622
|
+
tr.addEventListener("drop", (event) => {
|
|
623
|
+
event.preventDefault();
|
|
624
|
+
if (this.draggedRowIndex != null) this.reorderRow(this.draggedRowIndex, rowIndex);
|
|
625
|
+
this.draggedRowIndex = null;
|
|
626
|
+
});
|
|
460
627
|
if (this.checkboxSelection) {
|
|
461
628
|
const td = document.createElement("td");
|
|
462
629
|
const checkbox = document.createElement("input");
|
|
@@ -481,45 +648,123 @@ var GridNexaAngularComponent = class {
|
|
|
481
648
|
}
|
|
482
649
|
columns.forEach((column, columnIndex) => {
|
|
483
650
|
const td = cell(format(row, column, this.columns));
|
|
484
|
-
td.style.cssText =
|
|
651
|
+
td.style.cssText = `padding:10px;border:1px solid #dbe3ef;width:${this.columnWidths.get(column.id) ?? column.width ?? 150}px;${this.pinnedStyle(column, columns)}`;
|
|
485
652
|
if (this.activeCell?.rowIndex === rowIndex && this.activeCell.columnId === column.id) {
|
|
486
653
|
td.style.outline = "2px solid #2563eb";
|
|
487
654
|
td.style.outlineOffset = "-2px";
|
|
488
655
|
}
|
|
656
|
+
if (this.isCellInRange(rowIndex, column.id, columns)) td.style.background = "rgba(37,99,235,.12)";
|
|
489
657
|
if (this.findText && format(row, column, this.columns).toLowerCase().includes(this.findText.toLowerCase())) {
|
|
490
658
|
td.style.background = "rgba(37,99,235,.1)";
|
|
491
659
|
}
|
|
492
|
-
if (this.getTreeDataPath && columnIndex === 0)
|
|
493
|
-
|
|
660
|
+
if (this.getTreeDataPath && columnIndex === 0) {
|
|
661
|
+
td.style.paddingLeft = `${12 + (display?.depth ?? Math.max(0, this.getTreeDataPath(row).length - 1)) * 24}px`;
|
|
662
|
+
if (display?.hasChildren && display.treeKey) {
|
|
663
|
+
const treeKey = display.treeKey;
|
|
664
|
+
const toggle = this.button(this.collapsedTreeKeys.has(treeKey) ? "+" : "-", () => {
|
|
665
|
+
this.collapsedTreeKeys.has(treeKey) ? this.collapsedTreeKeys.delete(treeKey) : this.collapsedTreeKeys.add(treeKey);
|
|
666
|
+
this.render();
|
|
667
|
+
});
|
|
668
|
+
td.prepend(toggle);
|
|
669
|
+
}
|
|
670
|
+
} else if (this.masterDetailRenderer && columnIndex === 0) {
|
|
671
|
+
const rowId = this.rowId(row, rowIndex);
|
|
672
|
+
const toggle = this.button(this.expandedDetailIds.has(rowId) ? "-" : "+", () => {
|
|
673
|
+
this.expandedDetailIds.has(rowId) ? this.expandedDetailIds.delete(rowId) : this.expandedDetailIds.add(rowId);
|
|
674
|
+
this.render();
|
|
675
|
+
});
|
|
676
|
+
td.prepend(toggle);
|
|
677
|
+
}
|
|
678
|
+
td.addEventListener("click", (event) => {
|
|
679
|
+
this.contextMenu = null;
|
|
494
680
|
this.activeCell = { rowIndex, columnId: column.id };
|
|
681
|
+
if (event.shiftKey && this.rangeAnchor) {
|
|
682
|
+
this.rangeEnd = { rowIndex, columnId: column.id };
|
|
683
|
+
} else {
|
|
684
|
+
this.rangeAnchor = { rowIndex, columnId: column.id };
|
|
685
|
+
this.rangeEnd = { rowIndex, columnId: column.id };
|
|
686
|
+
}
|
|
495
687
|
this.cellClick.emit({ row, rowIndex, column });
|
|
496
688
|
this.render();
|
|
497
689
|
});
|
|
690
|
+
td.addEventListener("contextmenu", (event) => {
|
|
691
|
+
event.preventDefault();
|
|
692
|
+
this.activeCell = { rowIndex, columnId: column.id };
|
|
693
|
+
this.contextMenu = { x: event.clientX, y: event.clientY, rowIndex, columnId: column.id };
|
|
694
|
+
this.render();
|
|
695
|
+
});
|
|
498
696
|
if (column.editable) td.addEventListener("dblclick", () => this.editCell(td, row, rowIndex, column));
|
|
499
697
|
tr.appendChild(td);
|
|
500
698
|
});
|
|
501
699
|
tbody.appendChild(tr);
|
|
502
|
-
if (this.masterDetailRenderer) {
|
|
503
|
-
const detailRow = document.createElement("tr");
|
|
504
|
-
const detail = document.createElement("td");
|
|
505
|
-
detail.colSpan = columns.length + leading;
|
|
506
|
-
const content = this.masterDetailRenderer(row);
|
|
507
|
-
content instanceof Node ? detail.appendChild(content) : detail.textContent = String(content ?? "");
|
|
508
|
-
detailRow.appendChild(detail);
|
|
509
|
-
tbody.appendChild(detailRow);
|
|
510
|
-
}
|
|
511
700
|
}
|
|
512
701
|
editCell(td, row, rowIndex, column) {
|
|
513
702
|
const oldValue = rawValue(row, column);
|
|
514
|
-
const input =
|
|
515
|
-
input.value = String(oldValue ?? "");
|
|
703
|
+
const input = this.createEditor(column, oldValue);
|
|
516
704
|
td.replaceChildren(input);
|
|
517
705
|
input.focus();
|
|
518
706
|
input.addEventListener("blur", () => {
|
|
519
|
-
this.setCellValue(row, rowIndex, column, input.value);
|
|
707
|
+
this.setCellValue(row, rowIndex, column, input instanceof HTMLInputElement && input.type === "checkbox" ? input.checked : input.value);
|
|
520
708
|
this.render();
|
|
521
709
|
}, { once: true });
|
|
522
710
|
}
|
|
711
|
+
createEditor(column, current) {
|
|
712
|
+
const editor = column.editor;
|
|
713
|
+
if (editor === "checkbox") {
|
|
714
|
+
const input2 = document.createElement("input");
|
|
715
|
+
input2.type = "checkbox";
|
|
716
|
+
input2.checked = Boolean(current);
|
|
717
|
+
return input2;
|
|
718
|
+
}
|
|
719
|
+
if (editor === "number" || editor === "date") {
|
|
720
|
+
const input2 = document.createElement("input");
|
|
721
|
+
input2.type = editor;
|
|
722
|
+
input2.value = String(current ?? "");
|
|
723
|
+
return input2;
|
|
724
|
+
}
|
|
725
|
+
if (editor && typeof editor === "object" && (editor.type === "select" || editor.type === "advancedSelect")) {
|
|
726
|
+
const select = document.createElement("select");
|
|
727
|
+
(editor.values ?? []).forEach((item) => {
|
|
728
|
+
const option = document.createElement("option");
|
|
729
|
+
option.value = String(item);
|
|
730
|
+
option.textContent = String(item);
|
|
731
|
+
select.appendChild(option);
|
|
732
|
+
});
|
|
733
|
+
select.value = String(current ?? "");
|
|
734
|
+
return select;
|
|
735
|
+
}
|
|
736
|
+
const input = document.createElement("input");
|
|
737
|
+
input.value = String(current ?? "");
|
|
738
|
+
return input;
|
|
739
|
+
}
|
|
740
|
+
renderContextMenu() {
|
|
741
|
+
const menu = document.createElement("div");
|
|
742
|
+
menu.style.cssText = `position:fixed;z-index:9999;left:${this.contextMenu?.x ?? 0}px;top:${this.contextMenu?.y ?? 0}px;display:grid;min-width:150px;padding:6px;border:1px solid #dbe3ef;border-radius:10px;background:white;box-shadow:0 18px 48px rgba(15,23,42,.18)`;
|
|
743
|
+
const row = this.contextMenu ? this.rows[this.contextMenu.rowIndex] : void 0;
|
|
744
|
+
const column = this.columns.find((entry) => entry.id === this.contextMenu?.columnId);
|
|
745
|
+
menu.append(
|
|
746
|
+
this.button("Copy", () => void this.copyActiveCell()),
|
|
747
|
+
this.button("Paste", () => void this.pasteActiveCell()),
|
|
748
|
+
this.button("Edit cell", () => {
|
|
749
|
+
if (!row || !column || column.editable === false || !this.contextMenu) return;
|
|
750
|
+
const nextValue = window.prompt(`Edit ${column.headerName}`, String(rawValue(row, column) ?? ""));
|
|
751
|
+
if (nextValue != null) this.setCellValue(row, this.contextMenu.rowIndex, column, nextValue);
|
|
752
|
+
this.contextMenu = null;
|
|
753
|
+
this.render();
|
|
754
|
+
}),
|
|
755
|
+
this.button("Clear cell", () => {
|
|
756
|
+
if (row && column && column.editable !== false && this.contextMenu) this.setCellValue(row, this.contextMenu.rowIndex, column, "");
|
|
757
|
+
this.contextMenu = null;
|
|
758
|
+
this.render();
|
|
759
|
+
}),
|
|
760
|
+
this.button("Hide column", () => {
|
|
761
|
+
if (column) this.hiddenColumnIds.add(column.id);
|
|
762
|
+
this.contextMenu = null;
|
|
763
|
+
this.render();
|
|
764
|
+
})
|
|
765
|
+
);
|
|
766
|
+
return menu;
|
|
767
|
+
}
|
|
523
768
|
renderToolsPanel() {
|
|
524
769
|
const panel = document.createElement("div");
|
|
525
770
|
panel.style.cssText = "display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;padding:12px;border:1px solid #dbe3ef;border-top:0;background:#f8fbff";
|
package/dist/index.d.cts
CHANGED
|
@@ -57,9 +57,17 @@ declare class GridNexaAngularComponent<T = Record<string, unknown>> implements A
|
|
|
57
57
|
private sortState;
|
|
58
58
|
private hiddenColumnIds;
|
|
59
59
|
private activeCell;
|
|
60
|
+
private rangeAnchor;
|
|
61
|
+
private rangeEnd;
|
|
62
|
+
private contextMenu;
|
|
63
|
+
private expandedDetailIds;
|
|
64
|
+
private collapsedGroups;
|
|
65
|
+
private collapsedTreeKeys;
|
|
60
66
|
private findText;
|
|
61
67
|
private toolsOpen;
|
|
62
68
|
private draggedColumnId;
|
|
69
|
+
private draggedRowIndex;
|
|
70
|
+
private columnWidths;
|
|
63
71
|
private undoStack;
|
|
64
72
|
private redoStack;
|
|
65
73
|
ngAfterViewInit(): void;
|
|
@@ -75,15 +83,24 @@ declare class GridNexaAngularComponent<T = Record<string, unknown>> implements A
|
|
|
75
83
|
private copyActiveCell;
|
|
76
84
|
private pasteActiveCell;
|
|
77
85
|
private moveRow;
|
|
86
|
+
private reorderRow;
|
|
78
87
|
private moveColumn;
|
|
88
|
+
private makeDisplayRows;
|
|
89
|
+
private isCellInRange;
|
|
90
|
+
private startColumnResize;
|
|
91
|
+
private pinnedStyle;
|
|
79
92
|
private updateAdvancedFilter;
|
|
80
93
|
private updatePivot;
|
|
81
94
|
private render;
|
|
82
95
|
private renderToolbar;
|
|
83
96
|
private renderTable;
|
|
84
97
|
private renderMergedHeaders;
|
|
98
|
+
private appendGroupRow;
|
|
99
|
+
private appendDetailRow;
|
|
85
100
|
private appendRow;
|
|
86
101
|
private editCell;
|
|
102
|
+
private createEditor;
|
|
103
|
+
private renderContextMenu;
|
|
87
104
|
private renderToolsPanel;
|
|
88
105
|
private select;
|
|
89
106
|
private renderStatus;
|
package/dist/index.d.ts
CHANGED
|
@@ -57,9 +57,17 @@ declare class GridNexaAngularComponent<T = Record<string, unknown>> implements A
|
|
|
57
57
|
private sortState;
|
|
58
58
|
private hiddenColumnIds;
|
|
59
59
|
private activeCell;
|
|
60
|
+
private rangeAnchor;
|
|
61
|
+
private rangeEnd;
|
|
62
|
+
private contextMenu;
|
|
63
|
+
private expandedDetailIds;
|
|
64
|
+
private collapsedGroups;
|
|
65
|
+
private collapsedTreeKeys;
|
|
60
66
|
private findText;
|
|
61
67
|
private toolsOpen;
|
|
62
68
|
private draggedColumnId;
|
|
69
|
+
private draggedRowIndex;
|
|
70
|
+
private columnWidths;
|
|
63
71
|
private undoStack;
|
|
64
72
|
private redoStack;
|
|
65
73
|
ngAfterViewInit(): void;
|
|
@@ -75,15 +83,24 @@ declare class GridNexaAngularComponent<T = Record<string, unknown>> implements A
|
|
|
75
83
|
private copyActiveCell;
|
|
76
84
|
private pasteActiveCell;
|
|
77
85
|
private moveRow;
|
|
86
|
+
private reorderRow;
|
|
78
87
|
private moveColumn;
|
|
88
|
+
private makeDisplayRows;
|
|
89
|
+
private isCellInRange;
|
|
90
|
+
private startColumnResize;
|
|
91
|
+
private pinnedStyle;
|
|
79
92
|
private updateAdvancedFilter;
|
|
80
93
|
private updatePivot;
|
|
81
94
|
private render;
|
|
82
95
|
private renderToolbar;
|
|
83
96
|
private renderTable;
|
|
84
97
|
private renderMergedHeaders;
|
|
98
|
+
private appendGroupRow;
|
|
99
|
+
private appendDetailRow;
|
|
85
100
|
private appendRow;
|
|
86
101
|
private editCell;
|
|
102
|
+
private createEditor;
|
|
103
|
+
private renderContextMenu;
|
|
87
104
|
private renderToolsPanel;
|
|
88
105
|
private select;
|
|
89
106
|
private renderStatus;
|
package/dist/index.js
CHANGED
|
@@ -119,6 +119,12 @@ function buildPivot(rows, columns, groupBy, pivotBy, pivotValueColumns, pivotAgg
|
|
|
119
119
|
});
|
|
120
120
|
return { columns: pivotColumns, rows: pivotRows, active: true };
|
|
121
121
|
}
|
|
122
|
+
function buildGroupSummary(rows, columns, groupBy) {
|
|
123
|
+
return columns.filter((column) => column.field !== groupBy).map((column) => {
|
|
124
|
+
const values = rows.map((row) => Number(value(row, column, columns))).filter(Number.isFinite);
|
|
125
|
+
return values.length ? `${column.headerName}: ${values.reduce((sum, entry) => sum + entry, 0).toLocaleString()}` : "";
|
|
126
|
+
}).filter(Boolean).slice(0, 3).join(" | ");
|
|
127
|
+
}
|
|
122
128
|
function cell(text, tag = "td") {
|
|
123
129
|
const element = document.createElement(tag);
|
|
124
130
|
element.textContent = text;
|
|
@@ -160,9 +166,17 @@ var GridNexaAngularComponent = class {
|
|
|
160
166
|
sortState = null;
|
|
161
167
|
hiddenColumnIds = /* @__PURE__ */ new Set();
|
|
162
168
|
activeCell = null;
|
|
169
|
+
rangeAnchor = null;
|
|
170
|
+
rangeEnd = null;
|
|
171
|
+
contextMenu = null;
|
|
172
|
+
expandedDetailIds = /* @__PURE__ */ new Set();
|
|
173
|
+
collapsedGroups = /* @__PURE__ */ new Set();
|
|
174
|
+
collapsedTreeKeys = /* @__PURE__ */ new Set();
|
|
163
175
|
findText = "";
|
|
164
176
|
toolsOpen = false;
|
|
165
177
|
draggedColumnId = null;
|
|
178
|
+
draggedRowIndex = null;
|
|
179
|
+
columnWidths = /* @__PURE__ */ new Map();
|
|
166
180
|
undoStack = [];
|
|
167
181
|
redoStack = [];
|
|
168
182
|
ngAfterViewInit() {
|
|
@@ -170,6 +184,9 @@ var GridNexaAngularComponent = class {
|
|
|
170
184
|
}
|
|
171
185
|
ngOnChanges() {
|
|
172
186
|
this.hiddenColumnIds = new Set(this.columns.filter((column) => column.hidden).map((column) => column.id));
|
|
187
|
+
this.columns.forEach((column) => {
|
|
188
|
+
if (column.width && !this.columnWidths.has(column.id)) this.columnWidths.set(column.id, column.width);
|
|
189
|
+
});
|
|
173
190
|
this.applyTransaction();
|
|
174
191
|
this.render();
|
|
175
192
|
}
|
|
@@ -241,15 +258,36 @@ var GridNexaAngularComponent = class {
|
|
|
241
258
|
}
|
|
242
259
|
fillDown() {
|
|
243
260
|
if (!this.activeCell || !this.enableFillHandle) return;
|
|
261
|
+
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
262
|
+
if (this.rangeAnchor && this.rangeEnd && column) {
|
|
263
|
+
const minRow = Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex);
|
|
264
|
+
const maxRow = Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex);
|
|
265
|
+
const sourceRow2 = this.rows[minRow];
|
|
266
|
+
if (!sourceRow2 || column.editable === false) return;
|
|
267
|
+
const sourceValue = rawValue(sourceRow2, column);
|
|
268
|
+
for (let rowIndex = minRow + 1; rowIndex <= maxRow; rowIndex += 1) {
|
|
269
|
+
const row = this.rows[rowIndex];
|
|
270
|
+
if (row) this.setCellValue(row, rowIndex, column, sourceValue);
|
|
271
|
+
}
|
|
272
|
+
this.render();
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
244
275
|
const sourceRow = this.rows[this.activeCell.rowIndex];
|
|
245
276
|
const targetRow = this.rows[this.activeCell.rowIndex + 1];
|
|
246
|
-
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
247
277
|
if (!sourceRow || !targetRow || !column || column.editable === false) return;
|
|
248
278
|
this.setCellValue(targetRow, this.activeCell.rowIndex + 1, column, rawValue(sourceRow, column));
|
|
249
279
|
this.render();
|
|
250
280
|
}
|
|
251
281
|
async copyActiveCell() {
|
|
252
282
|
if (!this.activeCell || typeof navigator === "undefined") return;
|
|
283
|
+
if (this.rangeAnchor && this.rangeEnd) {
|
|
284
|
+
const columns = this.columns.filter((column2) => !this.hiddenColumnIds.has(column2.id) && !column2.hidden);
|
|
285
|
+
const anchorColumn = columns.findIndex((entry) => entry.id === this.rangeAnchor?.columnId);
|
|
286
|
+
const endColumn = columns.findIndex((entry) => entry.id === this.rangeEnd?.columnId);
|
|
287
|
+
const text = this.rows.slice(Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex), Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) + 1).map((row2) => columns.slice(Math.min(anchorColumn, endColumn), Math.max(anchorColumn, endColumn) + 1).map((column2) => format(row2, column2, this.columns)).join(" ")).join("\n");
|
|
288
|
+
await navigator.clipboard?.writeText(text);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
253
291
|
const row = this.rows[this.activeCell.rowIndex];
|
|
254
292
|
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
255
293
|
if (!row || !column) return;
|
|
@@ -257,11 +295,17 @@ var GridNexaAngularComponent = class {
|
|
|
257
295
|
}
|
|
258
296
|
async pasteActiveCell() {
|
|
259
297
|
if (!this.activeCell || typeof navigator === "undefined") return;
|
|
260
|
-
const row = this.rows[this.activeCell.rowIndex];
|
|
261
|
-
const column = this.columns.find((entry) => entry.id === this.activeCell?.columnId);
|
|
262
|
-
if (!row || !column || column.editable === false) return;
|
|
263
298
|
const text = await navigator.clipboard?.readText();
|
|
264
|
-
this.
|
|
299
|
+
const columns = this.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden);
|
|
300
|
+
const startColumn = columns.findIndex((entry) => entry.id === this.activeCell?.columnId);
|
|
301
|
+
text.split(/\r?\n/).forEach((line, rowOffset) => {
|
|
302
|
+
line.split(" ").forEach((value2, columnOffset) => {
|
|
303
|
+
const rowIndex = this.activeCell.rowIndex + rowOffset;
|
|
304
|
+
const row = this.rows[rowIndex];
|
|
305
|
+
const column = columns[startColumn + columnOffset];
|
|
306
|
+
if (row && column && column.editable !== false) this.setCellValue(row, rowIndex, column, value2);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
265
309
|
this.render();
|
|
266
310
|
}
|
|
267
311
|
moveRow(rowIndex, direction) {
|
|
@@ -273,6 +317,14 @@ var GridNexaAngularComponent = class {
|
|
|
273
317
|
this.rows = rows;
|
|
274
318
|
this.render();
|
|
275
319
|
}
|
|
320
|
+
reorderRow(sourceIndex, targetIndex) {
|
|
321
|
+
if (sourceIndex === targetIndex || sourceIndex < 0 || targetIndex < 0 || sourceIndex >= this.rows.length || targetIndex >= this.rows.length) return;
|
|
322
|
+
const rows = [...this.rows];
|
|
323
|
+
const [row] = rows.splice(sourceIndex, 1);
|
|
324
|
+
rows.splice(targetIndex, 0, row);
|
|
325
|
+
this.rows = rows;
|
|
326
|
+
this.render();
|
|
327
|
+
}
|
|
276
328
|
moveColumn(sourceId, targetId) {
|
|
277
329
|
if (sourceId === targetId) return;
|
|
278
330
|
const columns = [...this.columns];
|
|
@@ -284,6 +336,63 @@ var GridNexaAngularComponent = class {
|
|
|
284
336
|
this.columns = columns;
|
|
285
337
|
this.render();
|
|
286
338
|
}
|
|
339
|
+
makeDisplayRows(rows) {
|
|
340
|
+
if (this.groupBy) {
|
|
341
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
342
|
+
rows.forEach((row) => {
|
|
343
|
+
const key = String(row[this.groupBy] ?? "Ungrouped");
|
|
344
|
+
buckets.set(key, [...buckets.get(key) ?? [], row]);
|
|
345
|
+
});
|
|
346
|
+
return Array.from(buckets.entries()).flatMap(([key, bucket]) => [
|
|
347
|
+
{ kind: "group", key, label: key, rows: bucket, summaries: buildGroupSummary(bucket, this.columns, this.groupBy) },
|
|
348
|
+
...this.collapsedGroups.has(key) ? [] : bucket.map((row) => ({ kind: "data", row, rowIndex: this.rows.indexOf(row) }))
|
|
349
|
+
]);
|
|
350
|
+
}
|
|
351
|
+
if (this.getTreeDataPath) {
|
|
352
|
+
return rows.map((row) => {
|
|
353
|
+
const path = this.getTreeDataPath?.(row).filter(Boolean) ?? [];
|
|
354
|
+
return { row, path, key: path.join("/") };
|
|
355
|
+
}).sort((left, right) => left.key.localeCompare(right.key)).filter((entry) => entry.path.slice(0, -1).every((_, index) => !this.collapsedTreeKeys.has(entry.path.slice(0, index + 1).join("/")))).map((entry, _index, entries) => ({
|
|
356
|
+
kind: "data",
|
|
357
|
+
row: entry.row,
|
|
358
|
+
rowIndex: this.rows.indexOf(entry.row),
|
|
359
|
+
depth: Math.max(0, entry.path.length - 1),
|
|
360
|
+
treeKey: entry.key,
|
|
361
|
+
hasChildren: entries.some((other) => other.key.startsWith(`${entry.key}/`))
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
return rows.map((row) => ({ kind: "data", row, rowIndex: this.rows.indexOf(row) }));
|
|
365
|
+
}
|
|
366
|
+
isCellInRange(rowIndex, columnId, columns) {
|
|
367
|
+
if (!this.rangeAnchor || !this.rangeEnd || !this.enableRangeSelection) return false;
|
|
368
|
+
const columnIndex = columns.findIndex((column) => column.id === columnId);
|
|
369
|
+
const anchorIndex = columns.findIndex((column) => column.id === this.rangeAnchor?.columnId);
|
|
370
|
+
const endIndex = columns.findIndex((column) => column.id === this.rangeEnd?.columnId);
|
|
371
|
+
return rowIndex >= Math.min(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) && rowIndex <= Math.max(this.rangeAnchor.rowIndex, this.rangeEnd.rowIndex) && columnIndex >= Math.min(anchorIndex, endIndex) && columnIndex <= Math.max(anchorIndex, endIndex);
|
|
372
|
+
}
|
|
373
|
+
startColumnResize(event, column) {
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
event.stopPropagation();
|
|
376
|
+
const startX = event.clientX;
|
|
377
|
+
const startWidth = this.columnWidths.get(column.id) ?? column.width ?? 150;
|
|
378
|
+
const move = (moveEvent) => {
|
|
379
|
+
this.columnWidths.set(column.id, Math.max(72, startWidth + moveEvent.clientX - startX));
|
|
380
|
+
this.render();
|
|
381
|
+
};
|
|
382
|
+
const up = () => {
|
|
383
|
+
document.removeEventListener("mousemove", move);
|
|
384
|
+
document.removeEventListener("mouseup", up);
|
|
385
|
+
};
|
|
386
|
+
document.addEventListener("mousemove", move);
|
|
387
|
+
document.addEventListener("mouseup", up);
|
|
388
|
+
}
|
|
389
|
+
pinnedStyle(column, columns) {
|
|
390
|
+
if (!column.pinned) return "";
|
|
391
|
+
const index = columns.findIndex((entry) => entry.id === column.id);
|
|
392
|
+
const width = (entry) => this.columnWidths.get(entry.id) ?? entry.width ?? 150;
|
|
393
|
+
const offset = column.pinned === "left" ? columns.slice(0, index).filter((entry) => entry.pinned === "left").reduce((sum, entry) => sum + width(entry), 0) : columns.slice(index + 1).filter((entry) => entry.pinned === "right").reduce((sum, entry) => sum + width(entry), 0);
|
|
394
|
+
return `position:sticky;${column.pinned}:${offset}px;z-index:2;background:white;box-shadow:${column.pinned === "left" ? "inset -1px 0 #dbe3ef" : "inset 1px 0 #dbe3ef"};`;
|
|
395
|
+
}
|
|
287
396
|
updateAdvancedFilter(columnId, operator, filterValue) {
|
|
288
397
|
const model = {
|
|
289
398
|
kind: "group",
|
|
@@ -312,13 +421,15 @@ var GridNexaAngularComponent = class {
|
|
|
312
421
|
if (!this.host) return;
|
|
313
422
|
const sourceRows = this.visibleRows();
|
|
314
423
|
const pivot = buildPivot(sourceRows, this.columns, this.groupBy, this.pivotBy, this.pivotValueColumns, this.pivotAggregation);
|
|
315
|
-
const columns = pivot.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden);
|
|
424
|
+
const columns = pivot.columns.filter((column) => !this.hiddenColumnIds.has(column.id) && !column.hidden).sort((left, right) => (left.pinned === "left" ? 0 : left.pinned === "right" ? 2 : 1) - (right.pinned === "left" ? 0 : right.pinned === "right" ? 2 : 1));
|
|
316
425
|
const pageRows = this.pageSize ? pivot.rows.slice(this.pageIndex * this.pageSize, this.pageIndex * this.pageSize + this.pageSize) : pivot.rows;
|
|
426
|
+
const displayRows = pivot.active ? pageRows.map((row) => ({ kind: "data", row, rowIndex: pivot.rows.indexOf(row) })) : this.makeDisplayRows(pageRows);
|
|
317
427
|
const root = document.createElement("div");
|
|
318
428
|
root.className = "gridnexa-angular-grid";
|
|
319
|
-
root.append(this.renderToolbar(columns, pivot.rows), this.renderTable(columns,
|
|
429
|
+
root.append(this.renderToolbar(columns, pivot.rows), this.renderTable(columns, displayRows));
|
|
320
430
|
if (this.toolsOpen) root.appendChild(this.renderToolsPanel());
|
|
321
431
|
root.appendChild(this.renderStatus(pivot.rows.length));
|
|
432
|
+
if (this.contextMenu) root.appendChild(this.renderContextMenu());
|
|
322
433
|
this.host.nativeElement.replaceChildren(root);
|
|
323
434
|
this.serverSideOperation.emit({
|
|
324
435
|
sortModel: this.sortState ? [this.sortState] : [],
|
|
@@ -350,6 +461,16 @@ var GridNexaAngularComponent = class {
|
|
|
350
461
|
this.findText = find.value;
|
|
351
462
|
this.render();
|
|
352
463
|
});
|
|
464
|
+
const quickFilter = document.createElement("input");
|
|
465
|
+
quickFilter.type = "search";
|
|
466
|
+
quickFilter.placeholder = "Quick filter";
|
|
467
|
+
quickFilter.value = this.quickFilterText;
|
|
468
|
+
quickFilter.style.cssText = "min-height:32px;padding:0 10px;border:1px solid #bfdbfe;border-radius:8px";
|
|
469
|
+
quickFilter.addEventListener("input", () => {
|
|
470
|
+
this.quickFilterText = quickFilter.value;
|
|
471
|
+
this.pageIndex = 0;
|
|
472
|
+
this.render();
|
|
473
|
+
});
|
|
353
474
|
const pageCount = this.pageSize ? Math.max(1, Math.ceil(rows.length / this.pageSize)) : 1;
|
|
354
475
|
if (this.pageSize) {
|
|
355
476
|
const prev = this.button("Prev", () => {
|
|
@@ -364,7 +485,7 @@ var GridNexaAngularComponent = class {
|
|
|
364
485
|
next.disabled = this.pageIndex >= pageCount - 1;
|
|
365
486
|
actions.append(prev, ` Page ${this.pageIndex + 1} `, next);
|
|
366
487
|
}
|
|
367
|
-
actions.
|
|
488
|
+
actions.append(quickFilter, find);
|
|
368
489
|
if (this.enableUndoRedo) {
|
|
369
490
|
const undo = this.button("Undo", () => this.undo());
|
|
370
491
|
undo.disabled = !this.undoStack.length;
|
|
@@ -397,7 +518,7 @@ var GridNexaAngularComponent = class {
|
|
|
397
518
|
if (this.rowNumbers) header.appendChild(cell("#", "th"));
|
|
398
519
|
columns.forEach((column) => {
|
|
399
520
|
const th = cell(`${column.headerName}${this.sortState?.columnId === column.id ? this.sortState.direction === "asc" ? " \u2191" : " \u2193" : ""}`, "th");
|
|
400
|
-
th.style.cssText =
|
|
521
|
+
th.style.cssText = `padding:10px;border:1px solid #dbe3ef;background:#f8fbff;text-align:left;width:${this.columnWidths.get(column.id) ?? column.width ?? 150}px;${this.pinnedStyle(column, columns)}`;
|
|
401
522
|
th.draggable = true;
|
|
402
523
|
th.addEventListener("dragstart", () => {
|
|
403
524
|
this.draggedColumnId = column.id;
|
|
@@ -413,12 +534,25 @@ var GridNexaAngularComponent = class {
|
|
|
413
534
|
this.sortState = this.sortState?.columnId !== column.id ? { columnId: column.id, direction: "asc" } : this.sortState.direction === "asc" ? { columnId: column.id, direction: "desc" } : null;
|
|
414
535
|
this.render();
|
|
415
536
|
});
|
|
537
|
+
if (column.resizable !== false) {
|
|
538
|
+
const resizer = document.createElement("span");
|
|
539
|
+
resizer.style.cssText = "float:right;width:7px;height:24px;cursor:col-resize;border-right:2px solid #bfdbfe";
|
|
540
|
+
resizer.addEventListener("mousedown", (event) => this.startColumnResize(event, column));
|
|
541
|
+
th.appendChild(resizer);
|
|
542
|
+
}
|
|
416
543
|
header.appendChild(th);
|
|
417
544
|
});
|
|
418
545
|
thead.appendChild(header);
|
|
419
546
|
table.appendChild(thead);
|
|
420
547
|
const tbody = document.createElement("tbody");
|
|
421
|
-
rows.forEach((
|
|
548
|
+
rows.forEach((entry) => {
|
|
549
|
+
if (entry.kind === "group") this.appendGroupRow(tbody, entry, columns.length + leading);
|
|
550
|
+
if (entry.kind === "data") {
|
|
551
|
+
this.appendRow(tbody, entry.row, entry.rowIndex, columns, leading, entry);
|
|
552
|
+
const rowId = this.rowId(entry.row, entry.rowIndex);
|
|
553
|
+
if (this.masterDetailRenderer && this.expandedDetailIds.has(rowId)) this.appendDetailRow(tbody, entry.row, columns.length + leading);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
422
556
|
table.appendChild(tbody);
|
|
423
557
|
return table;
|
|
424
558
|
}
|
|
@@ -439,8 +573,41 @@ var GridNexaAngularComponent = class {
|
|
|
439
573
|
});
|
|
440
574
|
return row;
|
|
441
575
|
}
|
|
442
|
-
|
|
576
|
+
appendGroupRow(tbody, entry, colSpan) {
|
|
577
|
+
const tr = document.createElement("tr");
|
|
578
|
+
const td = document.createElement("td");
|
|
579
|
+
td.colSpan = colSpan;
|
|
580
|
+
td.style.cssText = "padding:10px;border:1px solid #dbe3ef;background:#eef4ff;color:#153e90;font-weight:800;text-transform:uppercase";
|
|
581
|
+
const toggle = this.button(this.collapsedGroups.has(entry.key) ? "+" : "-", () => {
|
|
582
|
+
this.collapsedGroups.has(entry.key) ? this.collapsedGroups.delete(entry.key) : this.collapsedGroups.add(entry.key);
|
|
583
|
+
this.render();
|
|
584
|
+
});
|
|
585
|
+
td.append(toggle, `${entry.label} ${entry.rows.length} rows${entry.summaries ? ` ${entry.summaries}` : ""}`);
|
|
586
|
+
tr.appendChild(td);
|
|
587
|
+
tbody.appendChild(tr);
|
|
588
|
+
}
|
|
589
|
+
appendDetailRow(tbody, row, colSpan) {
|
|
590
|
+
const detailRow = document.createElement("tr");
|
|
591
|
+
const detail = document.createElement("td");
|
|
592
|
+
detail.colSpan = colSpan;
|
|
593
|
+
detail.style.cssText = "padding:12px;border:1px solid #dbe3ef;background:#f8fbff;color:#334155";
|
|
594
|
+
const content = this.masterDetailRenderer?.(row);
|
|
595
|
+
content instanceof Node ? detail.appendChild(content) : detail.textContent = String(content ?? "");
|
|
596
|
+
detailRow.appendChild(detail);
|
|
597
|
+
tbody.appendChild(detailRow);
|
|
598
|
+
}
|
|
599
|
+
appendRow(tbody, row, rowIndex, columns, leading, display) {
|
|
443
600
|
const tr = document.createElement("tr");
|
|
601
|
+
tr.draggable = true;
|
|
602
|
+
tr.addEventListener("dragstart", () => {
|
|
603
|
+
this.draggedRowIndex = rowIndex;
|
|
604
|
+
});
|
|
605
|
+
tr.addEventListener("dragover", (event) => event.preventDefault());
|
|
606
|
+
tr.addEventListener("drop", (event) => {
|
|
607
|
+
event.preventDefault();
|
|
608
|
+
if (this.draggedRowIndex != null) this.reorderRow(this.draggedRowIndex, rowIndex);
|
|
609
|
+
this.draggedRowIndex = null;
|
|
610
|
+
});
|
|
444
611
|
if (this.checkboxSelection) {
|
|
445
612
|
const td = document.createElement("td");
|
|
446
613
|
const checkbox = document.createElement("input");
|
|
@@ -465,45 +632,123 @@ var GridNexaAngularComponent = class {
|
|
|
465
632
|
}
|
|
466
633
|
columns.forEach((column, columnIndex) => {
|
|
467
634
|
const td = cell(format(row, column, this.columns));
|
|
468
|
-
td.style.cssText =
|
|
635
|
+
td.style.cssText = `padding:10px;border:1px solid #dbe3ef;width:${this.columnWidths.get(column.id) ?? column.width ?? 150}px;${this.pinnedStyle(column, columns)}`;
|
|
469
636
|
if (this.activeCell?.rowIndex === rowIndex && this.activeCell.columnId === column.id) {
|
|
470
637
|
td.style.outline = "2px solid #2563eb";
|
|
471
638
|
td.style.outlineOffset = "-2px";
|
|
472
639
|
}
|
|
640
|
+
if (this.isCellInRange(rowIndex, column.id, columns)) td.style.background = "rgba(37,99,235,.12)";
|
|
473
641
|
if (this.findText && format(row, column, this.columns).toLowerCase().includes(this.findText.toLowerCase())) {
|
|
474
642
|
td.style.background = "rgba(37,99,235,.1)";
|
|
475
643
|
}
|
|
476
|
-
if (this.getTreeDataPath && columnIndex === 0)
|
|
477
|
-
|
|
644
|
+
if (this.getTreeDataPath && columnIndex === 0) {
|
|
645
|
+
td.style.paddingLeft = `${12 + (display?.depth ?? Math.max(0, this.getTreeDataPath(row).length - 1)) * 24}px`;
|
|
646
|
+
if (display?.hasChildren && display.treeKey) {
|
|
647
|
+
const treeKey = display.treeKey;
|
|
648
|
+
const toggle = this.button(this.collapsedTreeKeys.has(treeKey) ? "+" : "-", () => {
|
|
649
|
+
this.collapsedTreeKeys.has(treeKey) ? this.collapsedTreeKeys.delete(treeKey) : this.collapsedTreeKeys.add(treeKey);
|
|
650
|
+
this.render();
|
|
651
|
+
});
|
|
652
|
+
td.prepend(toggle);
|
|
653
|
+
}
|
|
654
|
+
} else if (this.masterDetailRenderer && columnIndex === 0) {
|
|
655
|
+
const rowId = this.rowId(row, rowIndex);
|
|
656
|
+
const toggle = this.button(this.expandedDetailIds.has(rowId) ? "-" : "+", () => {
|
|
657
|
+
this.expandedDetailIds.has(rowId) ? this.expandedDetailIds.delete(rowId) : this.expandedDetailIds.add(rowId);
|
|
658
|
+
this.render();
|
|
659
|
+
});
|
|
660
|
+
td.prepend(toggle);
|
|
661
|
+
}
|
|
662
|
+
td.addEventListener("click", (event) => {
|
|
663
|
+
this.contextMenu = null;
|
|
478
664
|
this.activeCell = { rowIndex, columnId: column.id };
|
|
665
|
+
if (event.shiftKey && this.rangeAnchor) {
|
|
666
|
+
this.rangeEnd = { rowIndex, columnId: column.id };
|
|
667
|
+
} else {
|
|
668
|
+
this.rangeAnchor = { rowIndex, columnId: column.id };
|
|
669
|
+
this.rangeEnd = { rowIndex, columnId: column.id };
|
|
670
|
+
}
|
|
479
671
|
this.cellClick.emit({ row, rowIndex, column });
|
|
480
672
|
this.render();
|
|
481
673
|
});
|
|
674
|
+
td.addEventListener("contextmenu", (event) => {
|
|
675
|
+
event.preventDefault();
|
|
676
|
+
this.activeCell = { rowIndex, columnId: column.id };
|
|
677
|
+
this.contextMenu = { x: event.clientX, y: event.clientY, rowIndex, columnId: column.id };
|
|
678
|
+
this.render();
|
|
679
|
+
});
|
|
482
680
|
if (column.editable) td.addEventListener("dblclick", () => this.editCell(td, row, rowIndex, column));
|
|
483
681
|
tr.appendChild(td);
|
|
484
682
|
});
|
|
485
683
|
tbody.appendChild(tr);
|
|
486
|
-
if (this.masterDetailRenderer) {
|
|
487
|
-
const detailRow = document.createElement("tr");
|
|
488
|
-
const detail = document.createElement("td");
|
|
489
|
-
detail.colSpan = columns.length + leading;
|
|
490
|
-
const content = this.masterDetailRenderer(row);
|
|
491
|
-
content instanceof Node ? detail.appendChild(content) : detail.textContent = String(content ?? "");
|
|
492
|
-
detailRow.appendChild(detail);
|
|
493
|
-
tbody.appendChild(detailRow);
|
|
494
|
-
}
|
|
495
684
|
}
|
|
496
685
|
editCell(td, row, rowIndex, column) {
|
|
497
686
|
const oldValue = rawValue(row, column);
|
|
498
|
-
const input =
|
|
499
|
-
input.value = String(oldValue ?? "");
|
|
687
|
+
const input = this.createEditor(column, oldValue);
|
|
500
688
|
td.replaceChildren(input);
|
|
501
689
|
input.focus();
|
|
502
690
|
input.addEventListener("blur", () => {
|
|
503
|
-
this.setCellValue(row, rowIndex, column, input.value);
|
|
691
|
+
this.setCellValue(row, rowIndex, column, input instanceof HTMLInputElement && input.type === "checkbox" ? input.checked : input.value);
|
|
504
692
|
this.render();
|
|
505
693
|
}, { once: true });
|
|
506
694
|
}
|
|
695
|
+
createEditor(column, current) {
|
|
696
|
+
const editor = column.editor;
|
|
697
|
+
if (editor === "checkbox") {
|
|
698
|
+
const input2 = document.createElement("input");
|
|
699
|
+
input2.type = "checkbox";
|
|
700
|
+
input2.checked = Boolean(current);
|
|
701
|
+
return input2;
|
|
702
|
+
}
|
|
703
|
+
if (editor === "number" || editor === "date") {
|
|
704
|
+
const input2 = document.createElement("input");
|
|
705
|
+
input2.type = editor;
|
|
706
|
+
input2.value = String(current ?? "");
|
|
707
|
+
return input2;
|
|
708
|
+
}
|
|
709
|
+
if (editor && typeof editor === "object" && (editor.type === "select" || editor.type === "advancedSelect")) {
|
|
710
|
+
const select = document.createElement("select");
|
|
711
|
+
(editor.values ?? []).forEach((item) => {
|
|
712
|
+
const option = document.createElement("option");
|
|
713
|
+
option.value = String(item);
|
|
714
|
+
option.textContent = String(item);
|
|
715
|
+
select.appendChild(option);
|
|
716
|
+
});
|
|
717
|
+
select.value = String(current ?? "");
|
|
718
|
+
return select;
|
|
719
|
+
}
|
|
720
|
+
const input = document.createElement("input");
|
|
721
|
+
input.value = String(current ?? "");
|
|
722
|
+
return input;
|
|
723
|
+
}
|
|
724
|
+
renderContextMenu() {
|
|
725
|
+
const menu = document.createElement("div");
|
|
726
|
+
menu.style.cssText = `position:fixed;z-index:9999;left:${this.contextMenu?.x ?? 0}px;top:${this.contextMenu?.y ?? 0}px;display:grid;min-width:150px;padding:6px;border:1px solid #dbe3ef;border-radius:10px;background:white;box-shadow:0 18px 48px rgba(15,23,42,.18)`;
|
|
727
|
+
const row = this.contextMenu ? this.rows[this.contextMenu.rowIndex] : void 0;
|
|
728
|
+
const column = this.columns.find((entry) => entry.id === this.contextMenu?.columnId);
|
|
729
|
+
menu.append(
|
|
730
|
+
this.button("Copy", () => void this.copyActiveCell()),
|
|
731
|
+
this.button("Paste", () => void this.pasteActiveCell()),
|
|
732
|
+
this.button("Edit cell", () => {
|
|
733
|
+
if (!row || !column || column.editable === false || !this.contextMenu) return;
|
|
734
|
+
const nextValue = window.prompt(`Edit ${column.headerName}`, String(rawValue(row, column) ?? ""));
|
|
735
|
+
if (nextValue != null) this.setCellValue(row, this.contextMenu.rowIndex, column, nextValue);
|
|
736
|
+
this.contextMenu = null;
|
|
737
|
+
this.render();
|
|
738
|
+
}),
|
|
739
|
+
this.button("Clear cell", () => {
|
|
740
|
+
if (row && column && column.editable !== false && this.contextMenu) this.setCellValue(row, this.contextMenu.rowIndex, column, "");
|
|
741
|
+
this.contextMenu = null;
|
|
742
|
+
this.render();
|
|
743
|
+
}),
|
|
744
|
+
this.button("Hide column", () => {
|
|
745
|
+
if (column) this.hiddenColumnIds.add(column.id);
|
|
746
|
+
this.contextMenu = null;
|
|
747
|
+
this.render();
|
|
748
|
+
})
|
|
749
|
+
);
|
|
750
|
+
return menu;
|
|
751
|
+
}
|
|
507
752
|
renderToolsPanel() {
|
|
508
753
|
const panel = document.createElement("div");
|
|
509
754
|
panel.style.cssText = "display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;padding:12px;border:1px solid #dbe3ef;border-top:0;background:#f8fbff";
|
package/package.json
CHANGED
|
@@ -1,37 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gridnexa/angular",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Angular
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Enterprise Angular data grid for modern UI apps with Excel-like filtering, editing, formulas, grouping, pivoting, export, and native Angular UX.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
9
16
|
"files": [
|
|
10
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
11
19
|
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --external @angular/core",
|
|
14
|
-
"dev": "tsup src/index.ts --watch --external @angular/core",
|
|
15
|
-
"clean": "rimraf dist"
|
|
16
|
-
},
|
|
17
20
|
"keywords": [
|
|
18
21
|
"gridnexa",
|
|
22
|
+
"angular-grid",
|
|
23
|
+
"angular-data-grid",
|
|
19
24
|
"angular",
|
|
20
25
|
"data-grid",
|
|
26
|
+
"datagrid",
|
|
27
|
+
"enterprise-grid",
|
|
28
|
+
"excel-grid",
|
|
29
|
+
"spreadsheet",
|
|
21
30
|
"table",
|
|
22
|
-
"
|
|
31
|
+
"pivot-table",
|
|
32
|
+
"tree-grid",
|
|
33
|
+
"filtering",
|
|
34
|
+
"sorting",
|
|
35
|
+
"editable-grid",
|
|
36
|
+
"csv-export",
|
|
37
|
+
"excel-export",
|
|
38
|
+
"typescript"
|
|
23
39
|
],
|
|
40
|
+
"homepage": "https://www.gridnexa.in/",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://www.gridnexa.in/help"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/mhalungekar9/SmartGrid.git",
|
|
47
|
+
"directory": "packages/angular"
|
|
48
|
+
},
|
|
24
49
|
"author": "Sachin M",
|
|
25
50
|
"license": "MIT",
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
26
54
|
"peerDependencies": {
|
|
27
55
|
"@angular/core": ">=16"
|
|
28
56
|
},
|
|
29
57
|
"dependencies": {
|
|
30
|
-
"@gridnexa/core": "
|
|
58
|
+
"@gridnexa/core": "^0.0.3"
|
|
31
59
|
},
|
|
32
60
|
"devDependencies": {
|
|
33
61
|
"rimraf": "^6.1.3",
|
|
34
62
|
"tsup": "^8.5.1",
|
|
35
63
|
"typescript": "^5.9.3"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --external @angular/core",
|
|
67
|
+
"dev": "tsup src/index.ts --watch --external @angular/core",
|
|
68
|
+
"clean": "rimraf dist"
|
|
36
69
|
}
|
|
37
|
-
}
|
|
70
|
+
}
|