ng-dynamic-datatable 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -34
- package/fesm2022/ng-dynamic-datatable.mjs +469 -0
- package/fesm2022/ng-dynamic-datatable.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/dynamic-datatable.component.d.ts +79 -0
- package/lib/dynamic-datatable.types.d.ts +4 -0
- package/lib/models.d.ts +45 -0
- package/package.json +35 -38
- package/{projects/dynamic-datatable/src/public-api.ts → public-api.d.ts} +2 -6
- package/.editorconfig +0 -17
- package/.vscode/extensions.json +0 -4
- package/.vscode/launch.json +0 -20
- package/.vscode/tasks.json +0 -42
- package/angular.json +0 -44
- package/projects/dynamic-datatable/README.md +0 -134
- package/projects/dynamic-datatable/ng-package.json +0 -7
- package/projects/dynamic-datatable/package.json +0 -24
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.component.html +0 -137
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.component.scss +0 -237
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.component.spec.ts +0 -23
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.component.ts +0 -556
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.service.spec.ts +0 -16
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.service.ts +0 -9
- package/projects/dynamic-datatable/src/lib/dynamic-datatable.types.ts +0 -4
- package/projects/dynamic-datatable/src/lib/models.ts +0 -52
- package/projects/dynamic-datatable/tsconfig.lib.json +0 -15
- package/projects/dynamic-datatable/tsconfig.lib.prod.json +0 -11
- package/projects/dynamic-datatable/tsconfig.spec.json +0 -15
- package/tsconfig.json +0 -32
package/README.md
CHANGED
|
@@ -1,59 +1,134 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dynamic-datatable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Config-driven Angular DataTable library that accepts columns, JSON data, and feature configuration.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Dynamic columns and data inputs
|
|
8
|
+
- Sorting (column-wise)
|
|
9
|
+
- Searching across visible columns
|
|
10
|
+
- Pagination and rows-per-page selection
|
|
11
|
+
- Row selection and select-all
|
|
12
|
+
- Row action events
|
|
13
|
+
- CSV and Excel export
|
|
14
|
+
|
|
15
|
+
## Install
|
|
8
16
|
|
|
9
17
|
```bash
|
|
10
|
-
|
|
18
|
+
npm install dynamic-datatable
|
|
11
19
|
```
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Import the standalone component directly in your consuming component:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Component } from '@angular/core';
|
|
27
|
+
import {
|
|
28
|
+
DynamicDatatableComponent,
|
|
29
|
+
DynamicDataTableConfig,
|
|
30
|
+
DataTableColumn
|
|
31
|
+
} from 'dynamic-datatable';
|
|
32
|
+
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'app-users',
|
|
35
|
+
standalone: true,
|
|
36
|
+
imports: [DynamicDatatableComponent],
|
|
37
|
+
template: `
|
|
38
|
+
<lib-dynamic-datatable
|
|
39
|
+
[config]="config"
|
|
40
|
+
[columns]="columns"
|
|
41
|
+
[data]="rows"
|
|
42
|
+
(actionSelected)="onAction($event)"
|
|
43
|
+
(selectionChange)="onSelection($event)"
|
|
44
|
+
/>
|
|
45
|
+
`
|
|
46
|
+
})
|
|
47
|
+
export class UsersComponent {
|
|
48
|
+
rows = [
|
|
49
|
+
{ id: 1, name: 'Aarav Sharma', email: 'aarav.sharma@company.in', salary: '19366.53', status: 'Resigned' },
|
|
50
|
+
{ id: 2, name: 'Ananya Patel', email: 'ananya.patel@company.in', salary: '10745.32', status: 'Applied' }
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
columns: DataTableColumn[] = [
|
|
54
|
+
{ key: 'name', label: 'Name', sortable: true, width: '220px' },
|
|
55
|
+
{ key: 'email', label: 'Email', sortable: true, width: '260px' },
|
|
56
|
+
{ key: 'salary', label: 'Salary', sortable: true, width: '120px' },
|
|
57
|
+
{
|
|
58
|
+
key: 'status',
|
|
59
|
+
label: 'Status',
|
|
60
|
+
sortable: true,
|
|
61
|
+
render: (row) => `<span>${row['status']}</span>`
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
config: DynamicDataTableConfig = {
|
|
66
|
+
title: 'Users',
|
|
67
|
+
features: {
|
|
68
|
+
export: true,
|
|
69
|
+
sorting: true,
|
|
70
|
+
searching: true,
|
|
71
|
+
pagination: true,
|
|
72
|
+
rowsPerPage: true,
|
|
73
|
+
actions: true,
|
|
74
|
+
checkboxSelection: true
|
|
75
|
+
},
|
|
76
|
+
rowsPerPageOptions: [5, 10, 25],
|
|
77
|
+
defaultRowsPerPage: 10,
|
|
78
|
+
exportOptions: { fileName: 'users-export', formats: ['csv', 'excel'] },
|
|
79
|
+
actions: [
|
|
80
|
+
{ label: 'Details', id: 'details' },
|
|
81
|
+
{ label: 'Delete', id: 'delete', danger: true }
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
onAction(event: unknown): void {
|
|
86
|
+
console.log(event);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
onSelection(rows: unknown[]): void {
|
|
90
|
+
console.log(rows);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
21
93
|
```
|
|
22
94
|
|
|
23
|
-
|
|
95
|
+
## Component API
|
|
24
96
|
|
|
25
|
-
|
|
26
|
-
ng generate --help
|
|
27
|
-
```
|
|
97
|
+
Inputs:
|
|
28
98
|
|
|
29
|
-
|
|
99
|
+
- `config: DynamicDataTableConfig`
|
|
100
|
+
- `columns: DataTableColumn[]`
|
|
101
|
+
- `data: Record<string, unknown>[]`
|
|
30
102
|
|
|
31
|
-
|
|
103
|
+
Outputs:
|
|
32
104
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
105
|
+
- `actionSelected`
|
|
106
|
+
- `selectionChange`
|
|
107
|
+
- `sortChange`
|
|
108
|
+
- `pageChange`
|
|
109
|
+
- `exportTriggered`
|
|
36
110
|
|
|
37
|
-
|
|
111
|
+
Public methods:
|
|
38
112
|
|
|
39
|
-
|
|
113
|
+
- `refresh()`
|
|
114
|
+
- `updateData(newData)`
|
|
115
|
+
- `updateConfig(newConfig)`
|
|
116
|
+
- `getSelectedRows()`
|
|
117
|
+
- `clearSelection()`
|
|
40
118
|
|
|
41
|
-
|
|
119
|
+
## Build
|
|
42
120
|
|
|
43
121
|
```bash
|
|
44
|
-
ng
|
|
122
|
+
ng build dynamic-datatable
|
|
45
123
|
```
|
|
46
124
|
|
|
47
|
-
|
|
125
|
+
Artifacts are generated in `dist/dynamic-datatable`.
|
|
48
126
|
|
|
49
|
-
|
|
127
|
+
## Publish to npm
|
|
50
128
|
|
|
51
129
|
```bash
|
|
52
|
-
|
|
130
|
+
cd dist/dynamic-datatable
|
|
131
|
+
npm publish --access public
|
|
53
132
|
```
|
|
54
133
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
## Additional Resources
|
|
58
|
-
|
|
59
|
-
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
|
134
|
+
Before publishing, ensure the package name in `projects/dynamic-datatable/package.json` is unique on npm.
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import * as i1 from '@angular/common';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { EventEmitter, Output, Input, Component } from '@angular/core';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
title: 'Dynamic DataTable',
|
|
8
|
+
features: {
|
|
9
|
+
export: true,
|
|
10
|
+
sorting: true,
|
|
11
|
+
searching: true,
|
|
12
|
+
pagination: true,
|
|
13
|
+
rowsPerPage: true,
|
|
14
|
+
actions: true,
|
|
15
|
+
checkboxSelection: true
|
|
16
|
+
},
|
|
17
|
+
rowsPerPageOptions: [5, 10, 25, 50],
|
|
18
|
+
defaultRowsPerPage: 10,
|
|
19
|
+
exportOptions: {
|
|
20
|
+
fileName: 'datatable-export',
|
|
21
|
+
formats: ['csv', 'excel']
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
class DynamicDatatableComponent {
|
|
25
|
+
config = {};
|
|
26
|
+
columns = [];
|
|
27
|
+
data = [];
|
|
28
|
+
actionSelected = new EventEmitter();
|
|
29
|
+
selectionChange = new EventEmitter();
|
|
30
|
+
sortChange = new EventEmitter();
|
|
31
|
+
pageChange = new EventEmitter();
|
|
32
|
+
exportTriggered = new EventEmitter();
|
|
33
|
+
mergedConfig = {
|
|
34
|
+
title: DEFAULT_CONFIG.title,
|
|
35
|
+
features: { ...DEFAULT_CONFIG.features },
|
|
36
|
+
rowsPerPageOptions: [...DEFAULT_CONFIG.rowsPerPageOptions],
|
|
37
|
+
defaultRowsPerPage: DEFAULT_CONFIG.defaultRowsPerPage,
|
|
38
|
+
exportOptions: { ...DEFAULT_CONFIG.exportOptions },
|
|
39
|
+
columns: [],
|
|
40
|
+
actions: []
|
|
41
|
+
};
|
|
42
|
+
mergedColumns = [];
|
|
43
|
+
allRows = [];
|
|
44
|
+
filteredRows = [];
|
|
45
|
+
pagedRows = [];
|
|
46
|
+
searchTerm = '';
|
|
47
|
+
currentPage = 1;
|
|
48
|
+
rowsPerPage = DEFAULT_CONFIG.defaultRowsPerPage;
|
|
49
|
+
totalPages = 1;
|
|
50
|
+
sortState = { key: null, direction: 'asc' };
|
|
51
|
+
selectedRows = new Set();
|
|
52
|
+
openActionMenuRowId = null;
|
|
53
|
+
ngOnChanges(_changes) {
|
|
54
|
+
this.refresh();
|
|
55
|
+
}
|
|
56
|
+
refresh() {
|
|
57
|
+
this.applyConfig();
|
|
58
|
+
this.normalizeRows();
|
|
59
|
+
this.applyFiltersSortAndPagination();
|
|
60
|
+
}
|
|
61
|
+
updateData(newData) {
|
|
62
|
+
this.data = Array.isArray(newData) ? [...newData] : [];
|
|
63
|
+
this.refresh();
|
|
64
|
+
}
|
|
65
|
+
updateConfig(newConfig) {
|
|
66
|
+
this.config = { ...this.config, ...newConfig };
|
|
67
|
+
this.refresh();
|
|
68
|
+
}
|
|
69
|
+
getSelectedRows() {
|
|
70
|
+
return this.allRows.filter((row) => this.selectedRows.has(row.id)).map((row) => row.data);
|
|
71
|
+
}
|
|
72
|
+
clearSelection() {
|
|
73
|
+
this.selectedRows.clear();
|
|
74
|
+
this.selectionChange.emit([]);
|
|
75
|
+
}
|
|
76
|
+
isFeatureOn(feature) {
|
|
77
|
+
return Boolean(this.mergedConfig.features?.[feature]);
|
|
78
|
+
}
|
|
79
|
+
isExportFormatEnabled(format) {
|
|
80
|
+
const formats = this.mergedConfig.exportOptions?.formats ?? [];
|
|
81
|
+
return formats.includes(format);
|
|
82
|
+
}
|
|
83
|
+
onSearchInput(event) {
|
|
84
|
+
const input = event.target;
|
|
85
|
+
this.searchTerm = (input.value || '').trim().toLowerCase();
|
|
86
|
+
this.currentPage = 1;
|
|
87
|
+
this.applyFiltersSortAndPagination();
|
|
88
|
+
}
|
|
89
|
+
onRowsPerPageChange(event) {
|
|
90
|
+
const select = event.target;
|
|
91
|
+
const value = Number(select.value);
|
|
92
|
+
this.rowsPerPage = Number.isFinite(value) && value > 0 ? value : this.getInitialRowsPerPage();
|
|
93
|
+
this.currentPage = 1;
|
|
94
|
+
this.applyFiltersSortAndPagination();
|
|
95
|
+
}
|
|
96
|
+
toggleSort(column) {
|
|
97
|
+
if (!this.isFeatureOn('sorting') || !column.sortable) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const key = String(column.key);
|
|
101
|
+
if (this.sortState.key === key) {
|
|
102
|
+
this.sortState.direction = this.sortState.direction === 'asc' ? 'desc' : 'asc';
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.sortState = { key, direction: 'asc' };
|
|
106
|
+
}
|
|
107
|
+
this.currentPage = 1;
|
|
108
|
+
this.sortChange.emit({ ...this.sortState });
|
|
109
|
+
this.applyFiltersSortAndPagination();
|
|
110
|
+
}
|
|
111
|
+
getSortIndicator(columnKey) {
|
|
112
|
+
if (this.sortState.key !== columnKey) {
|
|
113
|
+
return '↕';
|
|
114
|
+
}
|
|
115
|
+
return this.sortState.direction === 'asc' ? '↑' : '↓';
|
|
116
|
+
}
|
|
117
|
+
goToPage(page) {
|
|
118
|
+
if (!this.isFeatureOn('pagination')) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const bounded = Math.min(Math.max(page, 1), this.totalPages);
|
|
122
|
+
if (bounded === this.currentPage) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
this.currentPage = bounded;
|
|
126
|
+
this.pageChange.emit(this.currentPage);
|
|
127
|
+
this.updatePagedRowsOnly();
|
|
128
|
+
}
|
|
129
|
+
getPaginationPages() {
|
|
130
|
+
return Array.from({ length: this.totalPages }, (_, idx) => idx + 1);
|
|
131
|
+
}
|
|
132
|
+
isRowSelected(rowId) {
|
|
133
|
+
return this.selectedRows.has(rowId);
|
|
134
|
+
}
|
|
135
|
+
toggleRowSelection(row, event) {
|
|
136
|
+
const input = event.target;
|
|
137
|
+
if (input.checked) {
|
|
138
|
+
this.selectedRows.add(row.id);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.selectedRows.delete(row.id);
|
|
142
|
+
}
|
|
143
|
+
this.emitSelection();
|
|
144
|
+
}
|
|
145
|
+
toggleSelectAllCurrentPage(event) {
|
|
146
|
+
const input = event.target;
|
|
147
|
+
this.pagedRows.forEach((row) => {
|
|
148
|
+
if (input.checked) {
|
|
149
|
+
this.selectedRows.add(row.id);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.selectedRows.delete(row.id);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
this.emitSelection();
|
|
156
|
+
}
|
|
157
|
+
isCurrentPageFullySelected() {
|
|
158
|
+
if (!this.pagedRows.length) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return this.pagedRows.every((row) => this.selectedRows.has(row.id));
|
|
162
|
+
}
|
|
163
|
+
toggleActionMenu(rowId, event) {
|
|
164
|
+
event.stopPropagation();
|
|
165
|
+
this.openActionMenuRowId = this.openActionMenuRowId === rowId ? null : rowId;
|
|
166
|
+
}
|
|
167
|
+
closeActionMenu() {
|
|
168
|
+
this.openActionMenuRowId = null;
|
|
169
|
+
}
|
|
170
|
+
isActionMenuOpen(rowId) {
|
|
171
|
+
return this.openActionMenuRowId === rowId;
|
|
172
|
+
}
|
|
173
|
+
onActionItemClick(actionIndex, row) {
|
|
174
|
+
if (!Number.isInteger(actionIndex) || actionIndex < 0) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const action = this.mergedConfig.actions?.[actionIndex];
|
|
178
|
+
if (!action) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const rowIndex = this.filteredRows.findIndex((item) => item.id === row.id);
|
|
182
|
+
this.actionSelected.emit({ action, row: row.data, rowIndex });
|
|
183
|
+
this.closeActionMenu();
|
|
184
|
+
}
|
|
185
|
+
exportCSV() {
|
|
186
|
+
const rows = this.getRowsForExport();
|
|
187
|
+
this.exportTriggered.emit({ format: 'csv', rows });
|
|
188
|
+
const headers = this.mergedColumns.map((column) => this.escapeCsv(column.label));
|
|
189
|
+
const lines = rows.map((row) => {
|
|
190
|
+
return this.mergedColumns
|
|
191
|
+
.map((column) => this.escapeCsv(this.getCellTextValue(row, column)))
|
|
192
|
+
.join(',');
|
|
193
|
+
});
|
|
194
|
+
const csv = [headers.join(','), ...lines].join('\n');
|
|
195
|
+
this.downloadFile(csv, `${this.getExportFileName()}.csv`, 'text/csv;charset=utf-8;');
|
|
196
|
+
}
|
|
197
|
+
exportExcel() {
|
|
198
|
+
const rows = this.getRowsForExport();
|
|
199
|
+
this.exportTriggered.emit({ format: 'excel', rows });
|
|
200
|
+
const headerCells = this.mergedColumns.map((column) => `<th>${this.escapeHtml(column.label)}</th>`).join('');
|
|
201
|
+
const bodyRows = rows
|
|
202
|
+
.map((row) => {
|
|
203
|
+
const cells = this.mergedColumns
|
|
204
|
+
.map((column) => `<td>${this.escapeHtml(this.getCellTextValue(row, column))}</td>`)
|
|
205
|
+
.join('');
|
|
206
|
+
return `<tr>${cells}</tr>`;
|
|
207
|
+
})
|
|
208
|
+
.join('');
|
|
209
|
+
const html = `<html><head><meta charset="UTF-8"></head><body><table border="1"><thead><tr>${headerCells}</tr></thead><tbody>${bodyRows}</tbody></table></body></html>`;
|
|
210
|
+
this.downloadFile(html, `${this.getExportFileName()}.xls`, 'application/vnd.ms-excel;charset=utf-8;');
|
|
211
|
+
}
|
|
212
|
+
getRenderedCell(row, column) {
|
|
213
|
+
if (typeof column.render === 'function') {
|
|
214
|
+
return column.render(row);
|
|
215
|
+
}
|
|
216
|
+
const key = String(column.key);
|
|
217
|
+
const value = row[key];
|
|
218
|
+
return this.escapeHtml(value == null ? '' : String(value));
|
|
219
|
+
}
|
|
220
|
+
toColumnWidth(width) {
|
|
221
|
+
if (typeof width === 'number') {
|
|
222
|
+
return `${width}px`;
|
|
223
|
+
}
|
|
224
|
+
if (typeof width === 'string' && width.trim()) {
|
|
225
|
+
return width.trim();
|
|
226
|
+
}
|
|
227
|
+
return 'auto';
|
|
228
|
+
}
|
|
229
|
+
getColumnCount() {
|
|
230
|
+
let count = this.mergedColumns.length;
|
|
231
|
+
if (this.isFeatureOn('checkboxSelection')) {
|
|
232
|
+
count += 1;
|
|
233
|
+
}
|
|
234
|
+
if (this.isFeatureOn('actions')) {
|
|
235
|
+
count += 1;
|
|
236
|
+
}
|
|
237
|
+
return count;
|
|
238
|
+
}
|
|
239
|
+
getStartEntry() {
|
|
240
|
+
if (!this.filteredRows.length) {
|
|
241
|
+
return 0;
|
|
242
|
+
}
|
|
243
|
+
if (!this.isFeatureOn('pagination')) {
|
|
244
|
+
return 1;
|
|
245
|
+
}
|
|
246
|
+
return (this.currentPage - 1) * this.rowsPerPage + 1;
|
|
247
|
+
}
|
|
248
|
+
getEndEntry() {
|
|
249
|
+
if (!this.filteredRows.length) {
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
if (!this.isFeatureOn('pagination')) {
|
|
253
|
+
return this.filteredRows.length;
|
|
254
|
+
}
|
|
255
|
+
return Math.min(this.currentPage * this.rowsPerPage, this.filteredRows.length);
|
|
256
|
+
}
|
|
257
|
+
applyConfig() {
|
|
258
|
+
const features = {
|
|
259
|
+
...DEFAULT_CONFIG.features,
|
|
260
|
+
...(this.config.features ?? {})
|
|
261
|
+
};
|
|
262
|
+
const exportOptions = {
|
|
263
|
+
...DEFAULT_CONFIG.exportOptions,
|
|
264
|
+
...(this.config.exportOptions ?? {})
|
|
265
|
+
};
|
|
266
|
+
const rowsPerPageOptions = (this.config.rowsPerPageOptions ?? DEFAULT_CONFIG.rowsPerPageOptions).filter((item) => Number.isFinite(item) && item > 0);
|
|
267
|
+
this.mergedConfig = {
|
|
268
|
+
title: this.config.title ?? DEFAULT_CONFIG.title,
|
|
269
|
+
features,
|
|
270
|
+
rowsPerPageOptions: rowsPerPageOptions.length ? rowsPerPageOptions : [...DEFAULT_CONFIG.rowsPerPageOptions],
|
|
271
|
+
defaultRowsPerPage: this.config.defaultRowsPerPage ?? DEFAULT_CONFIG.defaultRowsPerPage,
|
|
272
|
+
exportOptions,
|
|
273
|
+
actions: [...(this.config.actions ?? [])],
|
|
274
|
+
columns: []
|
|
275
|
+
};
|
|
276
|
+
this.mergedColumns = [...(this.columns.length ? this.columns : (this.config.columns ?? []))];
|
|
277
|
+
this.rowsPerPage = this.getInitialRowsPerPage();
|
|
278
|
+
}
|
|
279
|
+
normalizeRows() {
|
|
280
|
+
this.allRows = (Array.isArray(this.data) ? this.data : []).map((row, index) => {
|
|
281
|
+
const rowId = row['id'];
|
|
282
|
+
const id = rowId == null ? `row-${index + 1}` : String(rowId);
|
|
283
|
+
return { id, data: row };
|
|
284
|
+
});
|
|
285
|
+
const validIds = new Set(this.allRows.map((row) => row.id));
|
|
286
|
+
for (const selectedId of Array.from(this.selectedRows)) {
|
|
287
|
+
if (!validIds.has(selectedId)) {
|
|
288
|
+
this.selectedRows.delete(selectedId);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
applyFiltersSortAndPagination() {
|
|
293
|
+
if (!this.isFeatureOn('searching') || !this.searchTerm) {
|
|
294
|
+
this.filteredRows = [...this.allRows];
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.filteredRows = this.allRows.filter((row) => {
|
|
298
|
+
const searchSpace = this.mergedColumns
|
|
299
|
+
.map((column) => this.getCellTextValue(row.data, column).toLowerCase())
|
|
300
|
+
.join(' ');
|
|
301
|
+
return searchSpace.includes(this.searchTerm);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (this.isFeatureOn('sorting') && this.sortState.key) {
|
|
305
|
+
const column = this.mergedColumns.find((item) => String(item.key) === this.sortState.key);
|
|
306
|
+
if (column?.sortable) {
|
|
307
|
+
this.filteredRows.sort((a, b) => {
|
|
308
|
+
const aVal = this.getCellTextValue(a.data, column);
|
|
309
|
+
const bVal = this.getCellTextValue(b.data, column);
|
|
310
|
+
const compare = this.compareValues(aVal, bVal);
|
|
311
|
+
return this.sortState.direction === 'asc' ? compare : -compare;
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
this.totalPages = this.isFeatureOn('pagination')
|
|
316
|
+
? Math.max(1, Math.ceil(this.filteredRows.length / this.rowsPerPage))
|
|
317
|
+
: 1;
|
|
318
|
+
if (this.currentPage > this.totalPages) {
|
|
319
|
+
this.currentPage = this.totalPages;
|
|
320
|
+
}
|
|
321
|
+
this.updatePagedRowsOnly();
|
|
322
|
+
this.emitSelection();
|
|
323
|
+
}
|
|
324
|
+
updatePagedRowsOnly() {
|
|
325
|
+
if (!this.isFeatureOn('pagination')) {
|
|
326
|
+
this.pagedRows = [...this.filteredRows];
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const start = (this.currentPage - 1) * this.rowsPerPage;
|
|
330
|
+
this.pagedRows = this.filteredRows.slice(start, start + this.rowsPerPage);
|
|
331
|
+
}
|
|
332
|
+
getCellTextValue(row, column) {
|
|
333
|
+
const key = String(column.key);
|
|
334
|
+
if (row[key] != null) {
|
|
335
|
+
return String(row[key]);
|
|
336
|
+
}
|
|
337
|
+
const rendered = this.getRenderedCell(row, column);
|
|
338
|
+
return this.stripHtml(rendered);
|
|
339
|
+
}
|
|
340
|
+
compareValues(a, b) {
|
|
341
|
+
const aText = a.trim();
|
|
342
|
+
const bText = b.trim();
|
|
343
|
+
const aNum = this.parseStrictNumber(aText);
|
|
344
|
+
const bNum = this.parseStrictNumber(bText);
|
|
345
|
+
if (aNum !== null && bNum !== null) {
|
|
346
|
+
return aNum - bNum;
|
|
347
|
+
}
|
|
348
|
+
const aDate = this.parseDateValue(aText);
|
|
349
|
+
const bDate = this.parseDateValue(bText);
|
|
350
|
+
if (aDate !== null && bDate !== null) {
|
|
351
|
+
return aDate - bDate;
|
|
352
|
+
}
|
|
353
|
+
return aText.localeCompare(bText, undefined, { numeric: true, sensitivity: 'base' });
|
|
354
|
+
}
|
|
355
|
+
parseStrictNumber(value) {
|
|
356
|
+
const normalized = value.replace(/[$,\s]/g, '');
|
|
357
|
+
if (!/^[-+]?\d*\.?\d+$/.test(normalized)) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const numberValue = Number(normalized);
|
|
361
|
+
return Number.isNaN(numberValue) ? null : numberValue;
|
|
362
|
+
}
|
|
363
|
+
parseDateValue(value) {
|
|
364
|
+
const usDate = value.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
|
|
365
|
+
if (usDate) {
|
|
366
|
+
const month = Number(usDate[1]);
|
|
367
|
+
const day = Number(usDate[2]);
|
|
368
|
+
const year = Number(usDate[3]);
|
|
369
|
+
const utc = Date.UTC(year, month - 1, day);
|
|
370
|
+
return Number.isNaN(utc) ? null : utc;
|
|
371
|
+
}
|
|
372
|
+
const isoDate = value.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
|
373
|
+
if (isoDate) {
|
|
374
|
+
const year = Number(isoDate[1]);
|
|
375
|
+
const month = Number(isoDate[2]);
|
|
376
|
+
const day = Number(isoDate[3]);
|
|
377
|
+
const utc = Date.UTC(year, month - 1, day);
|
|
378
|
+
return Number.isNaN(utc) ? null : utc;
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
getRowsForExport() {
|
|
383
|
+
const selected = this.getSelectedRows();
|
|
384
|
+
if (selected.length) {
|
|
385
|
+
return selected;
|
|
386
|
+
}
|
|
387
|
+
return this.filteredRows.map((item) => item.data);
|
|
388
|
+
}
|
|
389
|
+
getExportFileName() {
|
|
390
|
+
return this.mergedConfig.exportOptions?.fileName || 'datatable-export';
|
|
391
|
+
}
|
|
392
|
+
downloadFile(content, fileName, mimeType) {
|
|
393
|
+
if (typeof document === 'undefined') {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const blob = new Blob([content], { type: mimeType });
|
|
397
|
+
const url = URL.createObjectURL(blob);
|
|
398
|
+
const anchor = document.createElement('a');
|
|
399
|
+
anchor.href = url;
|
|
400
|
+
anchor.download = fileName;
|
|
401
|
+
document.body.appendChild(anchor);
|
|
402
|
+
anchor.click();
|
|
403
|
+
document.body.removeChild(anchor);
|
|
404
|
+
URL.revokeObjectURL(url);
|
|
405
|
+
}
|
|
406
|
+
escapeCsv(value) {
|
|
407
|
+
if (/[",\n]/.test(value)) {
|
|
408
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
409
|
+
}
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
stripHtml(input) {
|
|
413
|
+
if (typeof document === 'undefined') {
|
|
414
|
+
return input;
|
|
415
|
+
}
|
|
416
|
+
const temp = document.createElement('div');
|
|
417
|
+
temp.innerHTML = input;
|
|
418
|
+
return temp.textContent || temp.innerText || '';
|
|
419
|
+
}
|
|
420
|
+
escapeHtml(value) {
|
|
421
|
+
return String(value)
|
|
422
|
+
.replace(/&/g, '&')
|
|
423
|
+
.replace(/</g, '<')
|
|
424
|
+
.replace(/>/g, '>')
|
|
425
|
+
.replace(/"/g, '"')
|
|
426
|
+
.replace(/'/g, ''');
|
|
427
|
+
}
|
|
428
|
+
getInitialRowsPerPage() {
|
|
429
|
+
const options = this.mergedConfig.rowsPerPageOptions ?? DEFAULT_CONFIG.rowsPerPageOptions;
|
|
430
|
+
const preferred = Number(this.mergedConfig.defaultRowsPerPage ?? DEFAULT_CONFIG.defaultRowsPerPage);
|
|
431
|
+
return options.includes(preferred) ? preferred : options[0] ?? DEFAULT_CONFIG.defaultRowsPerPage;
|
|
432
|
+
}
|
|
433
|
+
emitSelection() {
|
|
434
|
+
this.selectionChange.emit(this.getSelectedRows());
|
|
435
|
+
}
|
|
436
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicDatatableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
437
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: DynamicDatatableComponent, isStandalone: true, selector: "lib-dynamic-datatable", inputs: { config: "config", columns: "columns", data: "data" }, outputs: { actionSelected: "actionSelected", selectionChange: "selectionChange", sortChange: "sortChange", pageChange: "pageChange", exportTriggered: "exportTriggered" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"ndt-card\" (click)=\"closeActionMenu()\">\r\n <div class=\"ndt-header\">\r\n <h3 class=\"ndt-title\">{{ mergedConfig.title }}</h3>\r\n </div>\r\n\r\n <div class=\"ndt-toolbar\">\r\n <div class=\"ndt-left-tools\">\r\n <ng-container *ngIf=\"isFeatureOn('export')\">\r\n <button class=\"ndt-btn\" type=\"button\" (click)=\"exportCSV()\" *ngIf=\"isExportFormatEnabled('csv')\">\r\n Export CSV\r\n </button>\r\n <button class=\"ndt-btn\" type=\"button\" (click)=\"exportExcel()\" *ngIf=\"isExportFormatEnabled('excel')\">\r\n Export Excel\r\n </button>\r\n </ng-container>\r\n\r\n <label class=\"ndt-inline-label\" *ngIf=\"isFeatureOn('rowsPerPage') && isFeatureOn('pagination')\">\r\n Rows per page:\r\n <select [value]=\"rowsPerPage\" (change)=\"onRowsPerPageChange($event)\">\r\n <option *ngFor=\"let option of mergedConfig.rowsPerPageOptions || []\" [value]=\"option\">{{ option }}</option>\r\n </select>\r\n </label>\r\n </div>\r\n\r\n <div class=\"ndt-right-tools\" *ngIf=\"isFeatureOn('searching')\">\r\n <input\r\n type=\"search\"\r\n placeholder=\"Search records\"\r\n [value]=\"searchTerm\"\r\n (input)=\"onSearchInput($event)\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"ndt-table-wrap\">\r\n <table class=\"ndt-table\">\r\n <colgroup>\r\n <col *ngIf=\"isFeatureOn('checkboxSelection')\" style=\"width: 44px\" />\r\n <col *ngFor=\"let column of mergedColumns\" [style.width]=\"toColumnWidth(column.width)\" />\r\n <col *ngIf=\"isFeatureOn('actions')\" style=\"width: 78px\" />\r\n </colgroup>\r\n\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"isFeatureOn('checkboxSelection')\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isCurrentPageFullySelected()\"\r\n (change)=\"toggleSelectAllCurrentPage($event)\"\r\n />\r\n </th>\r\n <th\r\n *ngFor=\"let column of mergedColumns\"\r\n [class.ndt-sortable]=\"isFeatureOn('sorting') && !!column.sortable\"\r\n (click)=\"toggleSort(column)\"\r\n >\r\n <span class=\"ndt-th-content\">\r\n <span class=\"ndt-th-label\">{{ column.label }}</span>\r\n <span class=\"ndt-sort-indicator\" *ngIf=\"isFeatureOn('sorting') && column.sortable\">\r\n {{ getSortIndicator(column.key.toString()) }}\r\n </span>\r\n </span>\r\n </th>\r\n <th *ngIf=\"isFeatureOn('actions')\">Actions</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <tr *ngIf=\"!pagedRows.length\">\r\n <td [attr.colspan]=\"getColumnCount()\" class=\"ndt-empty\">No records found.</td>\r\n </tr>\r\n\r\n <tr *ngFor=\"let row of pagedRows; let rowIndex = index\" [class.ndt-selected]=\"isRowSelected(row.id)\">\r\n <td *ngIf=\"isFeatureOn('checkboxSelection')\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isRowSelected(row.id)\"\r\n (change)=\"toggleRowSelection(row, $event)\"\r\n />\r\n </td>\r\n\r\n <td *ngFor=\"let column of mergedColumns\" [innerHTML]=\"getRenderedCell(row.data, column)\"></td>\r\n\r\n <td *ngIf=\"isFeatureOn('actions')\" class=\"ndt-actions-cell\">\r\n <div class=\"ndt-dropdown\" (click)=\"$event.stopPropagation()\">\r\n <button\r\n type=\"button\"\r\n class=\"ndt-actions-btn\"\r\n (click)=\"toggleActionMenu(row.id, $event)\"\r\n [attr.aria-expanded]=\"isActionMenuOpen(row.id)\"\r\n >\r\n <span class=\"ndt-dots\">...</span>\r\n </button>\r\n\r\n <ul class=\"ndt-dropdown-menu\" *ngIf=\"isActionMenuOpen(row.id)\">\r\n <li *ngFor=\"let action of mergedConfig.actions || []; let i = index\">\r\n <button\r\n type=\"button\"\r\n class=\"ndt-dropdown-item\"\r\n [class.ndt-danger]=\"!!action.danger\"\r\n (click)=\"onActionItemClick(i, row)\"\r\n >\r\n <i *ngIf=\"action.icon\" [class]=\"action.icon\"></i>\r\n <span>{{ action.label }}</span>\r\n </button>\r\n </li>\r\n </ul>\r\n </div>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <div class=\"ndt-footer\">\r\n <div>\r\n {{ selectedRows.size }} selected\r\n </div>\r\n\r\n <div>\r\n Showing {{ getStartEntry() }} to {{ getEndEntry() }} of {{ filteredRows.length }} entries\r\n </div>\r\n\r\n <div class=\"ndt-pagination\" *ngIf=\"isFeatureOn('pagination') && totalPages > 1\">\r\n <button type=\"button\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage <= 1\">Prev</button>\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let page of getPaginationPages()\"\r\n [class.active]=\"page === currentPage\"\r\n (click)=\"goToPage(page)\"\r\n >\r\n {{ page }}\r\n </button>\r\n <button type=\"button\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage >= totalPages\">Next</button>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;--ndt-primary: #4f46e5;--ndt-primary-soft: #eef2ff;--ndt-border: #dbe4ff;--ndt-text: #1f2a44;--ndt-muted: #6d7895}.ndt-card{border:1px solid var(--ndt-border);border-radius:14px;background:#fff;box-shadow:0 10px 30px #2d489114;overflow:hidden;color:var(--ndt-text);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ndt-header,.ndt-toolbar,.ndt-footer{padding:.9rem 1rem}.ndt-header{border-bottom:1px solid #edf1ff}.ndt-title{margin:0;font-size:1.1rem}.ndt-toolbar{display:flex;justify-content:space-between;gap:.75rem;flex-wrap:wrap}.ndt-left-tools,.ndt-right-tools{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.ndt-btn,.ndt-pagination button,.ndt-actions-cell select,.ndt-toolbar select,.ndt-toolbar input{border:1px solid #cfd9fb;background:#fff;color:#33446e;border-radius:8px;padding:.35rem .6rem;font-size:.85rem}.ndt-btn:hover,.ndt-pagination button:hover,.ndt-pagination button.active{border-color:var(--ndt-primary);background:var(--ndt-primary-soft);color:var(--ndt-primary)}.ndt-inline-label{display:inline-flex;align-items:center;gap:.4rem;color:var(--ndt-muted);font-size:.85rem}.ndt-table-wrap{overflow-x:auto;border-top:1px solid #edf1ff;border-bottom:1px solid #edf1ff}.ndt-table{width:100%;min-width:860px;border-collapse:collapse;table-layout:fixed}.ndt-table th,.ndt-table td{padding:.72rem;text-align:left;border-bottom:1px solid #edf1ff}.ndt-table th{color:#4a577f;font-size:.8rem;text-transform:uppercase;letter-spacing:.03em;background:#f8faff}.ndt-th-content{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.ndt-th-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ndt-sortable{cursor:pointer}.ndt-sort-indicator{font-size:.75rem;color:#8a98c2}.ndt-table tbody tr:hover{background:#f5f8ff}.ndt-selected{background:#ebf1ff}.ndt-actions-cell{text-align:center;position:relative}.ndt-dropdown{position:relative;display:inline-block}.ndt-actions-btn{border:1px solid #cfd9fb;background:#fff;color:#33446e;border-radius:8px;width:34px;height:30px;padding:0;cursor:pointer}.ndt-actions-btn:hover{border-color:var(--ndt-primary);color:var(--ndt-primary);background:var(--ndt-primary-soft)}.ndt-dots{font-size:1rem;line-height:1}.ndt-dropdown-menu{position:absolute;right:0;top:calc(100% + .35rem);list-style:none;margin:0;padding:.35rem 0;min-width:150px;border:1px solid #dbe4ff;border-radius:10px;background:#fff;box-shadow:0 12px 24px #2d489124;z-index:20}.ndt-dropdown-item{width:100%;border:0;background:#fff;color:#33446e;display:flex;align-items:center;gap:.45rem;text-align:left;padding:.42rem .72rem;font-size:.84rem;cursor:pointer}.ndt-dropdown-item:hover{background:#f5f8ff;color:var(--ndt-primary)}.ndt-dropdown-item.ndt-danger{color:#dc3545}.ndt-dropdown-item.ndt-danger:hover{background:#fff1f3}.ndt-empty{text-align:center;color:var(--ndt-muted)}.ndt-footer{display:flex;justify-content:space-between;gap:.75rem;flex-wrap:wrap;color:#55658f;font-size:.86rem}.ndt-pagination{display:flex;align-items:center;gap:.35rem}@media(max-width:900px){.ndt-toolbar,.ndt-footer{flex-direction:column;align-items:flex-start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
438
|
+
}
|
|
439
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: DynamicDatatableComponent, decorators: [{
|
|
440
|
+
type: Component,
|
|
441
|
+
args: [{ selector: 'lib-dynamic-datatable', standalone: true, imports: [CommonModule], template: "<div class=\"ndt-card\" (click)=\"closeActionMenu()\">\r\n <div class=\"ndt-header\">\r\n <h3 class=\"ndt-title\">{{ mergedConfig.title }}</h3>\r\n </div>\r\n\r\n <div class=\"ndt-toolbar\">\r\n <div class=\"ndt-left-tools\">\r\n <ng-container *ngIf=\"isFeatureOn('export')\">\r\n <button class=\"ndt-btn\" type=\"button\" (click)=\"exportCSV()\" *ngIf=\"isExportFormatEnabled('csv')\">\r\n Export CSV\r\n </button>\r\n <button class=\"ndt-btn\" type=\"button\" (click)=\"exportExcel()\" *ngIf=\"isExportFormatEnabled('excel')\">\r\n Export Excel\r\n </button>\r\n </ng-container>\r\n\r\n <label class=\"ndt-inline-label\" *ngIf=\"isFeatureOn('rowsPerPage') && isFeatureOn('pagination')\">\r\n Rows per page:\r\n <select [value]=\"rowsPerPage\" (change)=\"onRowsPerPageChange($event)\">\r\n <option *ngFor=\"let option of mergedConfig.rowsPerPageOptions || []\" [value]=\"option\">{{ option }}</option>\r\n </select>\r\n </label>\r\n </div>\r\n\r\n <div class=\"ndt-right-tools\" *ngIf=\"isFeatureOn('searching')\">\r\n <input\r\n type=\"search\"\r\n placeholder=\"Search records\"\r\n [value]=\"searchTerm\"\r\n (input)=\"onSearchInput($event)\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"ndt-table-wrap\">\r\n <table class=\"ndt-table\">\r\n <colgroup>\r\n <col *ngIf=\"isFeatureOn('checkboxSelection')\" style=\"width: 44px\" />\r\n <col *ngFor=\"let column of mergedColumns\" [style.width]=\"toColumnWidth(column.width)\" />\r\n <col *ngIf=\"isFeatureOn('actions')\" style=\"width: 78px\" />\r\n </colgroup>\r\n\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"isFeatureOn('checkboxSelection')\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isCurrentPageFullySelected()\"\r\n (change)=\"toggleSelectAllCurrentPage($event)\"\r\n />\r\n </th>\r\n <th\r\n *ngFor=\"let column of mergedColumns\"\r\n [class.ndt-sortable]=\"isFeatureOn('sorting') && !!column.sortable\"\r\n (click)=\"toggleSort(column)\"\r\n >\r\n <span class=\"ndt-th-content\">\r\n <span class=\"ndt-th-label\">{{ column.label }}</span>\r\n <span class=\"ndt-sort-indicator\" *ngIf=\"isFeatureOn('sorting') && column.sortable\">\r\n {{ getSortIndicator(column.key.toString()) }}\r\n </span>\r\n </span>\r\n </th>\r\n <th *ngIf=\"isFeatureOn('actions')\">Actions</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <tr *ngIf=\"!pagedRows.length\">\r\n <td [attr.colspan]=\"getColumnCount()\" class=\"ndt-empty\">No records found.</td>\r\n </tr>\r\n\r\n <tr *ngFor=\"let row of pagedRows; let rowIndex = index\" [class.ndt-selected]=\"isRowSelected(row.id)\">\r\n <td *ngIf=\"isFeatureOn('checkboxSelection')\">\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"isRowSelected(row.id)\"\r\n (change)=\"toggleRowSelection(row, $event)\"\r\n />\r\n </td>\r\n\r\n <td *ngFor=\"let column of mergedColumns\" [innerHTML]=\"getRenderedCell(row.data, column)\"></td>\r\n\r\n <td *ngIf=\"isFeatureOn('actions')\" class=\"ndt-actions-cell\">\r\n <div class=\"ndt-dropdown\" (click)=\"$event.stopPropagation()\">\r\n <button\r\n type=\"button\"\r\n class=\"ndt-actions-btn\"\r\n (click)=\"toggleActionMenu(row.id, $event)\"\r\n [attr.aria-expanded]=\"isActionMenuOpen(row.id)\"\r\n >\r\n <span class=\"ndt-dots\">...</span>\r\n </button>\r\n\r\n <ul class=\"ndt-dropdown-menu\" *ngIf=\"isActionMenuOpen(row.id)\">\r\n <li *ngFor=\"let action of mergedConfig.actions || []; let i = index\">\r\n <button\r\n type=\"button\"\r\n class=\"ndt-dropdown-item\"\r\n [class.ndt-danger]=\"!!action.danger\"\r\n (click)=\"onActionItemClick(i, row)\"\r\n >\r\n <i *ngIf=\"action.icon\" [class]=\"action.icon\"></i>\r\n <span>{{ action.label }}</span>\r\n </button>\r\n </li>\r\n </ul>\r\n </div>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <div class=\"ndt-footer\">\r\n <div>\r\n {{ selectedRows.size }} selected\r\n </div>\r\n\r\n <div>\r\n Showing {{ getStartEntry() }} to {{ getEndEntry() }} of {{ filteredRows.length }} entries\r\n </div>\r\n\r\n <div class=\"ndt-pagination\" *ngIf=\"isFeatureOn('pagination') && totalPages > 1\">\r\n <button type=\"button\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage <= 1\">Prev</button>\r\n <button\r\n type=\"button\"\r\n *ngFor=\"let page of getPaginationPages()\"\r\n [class.active]=\"page === currentPage\"\r\n (click)=\"goToPage(page)\"\r\n >\r\n {{ page }}\r\n </button>\r\n <button type=\"button\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage >= totalPages\">Next</button>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;--ndt-primary: #4f46e5;--ndt-primary-soft: #eef2ff;--ndt-border: #dbe4ff;--ndt-text: #1f2a44;--ndt-muted: #6d7895}.ndt-card{border:1px solid var(--ndt-border);border-radius:14px;background:#fff;box-shadow:0 10px 30px #2d489114;overflow:hidden;color:var(--ndt-text);font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ndt-header,.ndt-toolbar,.ndt-footer{padding:.9rem 1rem}.ndt-header{border-bottom:1px solid #edf1ff}.ndt-title{margin:0;font-size:1.1rem}.ndt-toolbar{display:flex;justify-content:space-between;gap:.75rem;flex-wrap:wrap}.ndt-left-tools,.ndt-right-tools{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.ndt-btn,.ndt-pagination button,.ndt-actions-cell select,.ndt-toolbar select,.ndt-toolbar input{border:1px solid #cfd9fb;background:#fff;color:#33446e;border-radius:8px;padding:.35rem .6rem;font-size:.85rem}.ndt-btn:hover,.ndt-pagination button:hover,.ndt-pagination button.active{border-color:var(--ndt-primary);background:var(--ndt-primary-soft);color:var(--ndt-primary)}.ndt-inline-label{display:inline-flex;align-items:center;gap:.4rem;color:var(--ndt-muted);font-size:.85rem}.ndt-table-wrap{overflow-x:auto;border-top:1px solid #edf1ff;border-bottom:1px solid #edf1ff}.ndt-table{width:100%;min-width:860px;border-collapse:collapse;table-layout:fixed}.ndt-table th,.ndt-table td{padding:.72rem;text-align:left;border-bottom:1px solid #edf1ff}.ndt-table th{color:#4a577f;font-size:.8rem;text-transform:uppercase;letter-spacing:.03em;background:#f8faff}.ndt-th-content{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.ndt-th-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ndt-sortable{cursor:pointer}.ndt-sort-indicator{font-size:.75rem;color:#8a98c2}.ndt-table tbody tr:hover{background:#f5f8ff}.ndt-selected{background:#ebf1ff}.ndt-actions-cell{text-align:center;position:relative}.ndt-dropdown{position:relative;display:inline-block}.ndt-actions-btn{border:1px solid #cfd9fb;background:#fff;color:#33446e;border-radius:8px;width:34px;height:30px;padding:0;cursor:pointer}.ndt-actions-btn:hover{border-color:var(--ndt-primary);color:var(--ndt-primary);background:var(--ndt-primary-soft)}.ndt-dots{font-size:1rem;line-height:1}.ndt-dropdown-menu{position:absolute;right:0;top:calc(100% + .35rem);list-style:none;margin:0;padding:.35rem 0;min-width:150px;border:1px solid #dbe4ff;border-radius:10px;background:#fff;box-shadow:0 12px 24px #2d489124;z-index:20}.ndt-dropdown-item{width:100%;border:0;background:#fff;color:#33446e;display:flex;align-items:center;gap:.45rem;text-align:left;padding:.42rem .72rem;font-size:.84rem;cursor:pointer}.ndt-dropdown-item:hover{background:#f5f8ff;color:var(--ndt-primary)}.ndt-dropdown-item.ndt-danger{color:#dc3545}.ndt-dropdown-item.ndt-danger:hover{background:#fff1f3}.ndt-empty{text-align:center;color:var(--ndt-muted)}.ndt-footer{display:flex;justify-content:space-between;gap:.75rem;flex-wrap:wrap;color:#55658f;font-size:.86rem}.ndt-pagination{display:flex;align-items:center;gap:.35rem}@media(max-width:900px){.ndt-toolbar,.ndt-footer{flex-direction:column;align-items:flex-start}}\n"] }]
|
|
442
|
+
}], propDecorators: { config: [{
|
|
443
|
+
type: Input
|
|
444
|
+
}], columns: [{
|
|
445
|
+
type: Input
|
|
446
|
+
}], data: [{
|
|
447
|
+
type: Input
|
|
448
|
+
}], actionSelected: [{
|
|
449
|
+
type: Output
|
|
450
|
+
}], selectionChange: [{
|
|
451
|
+
type: Output
|
|
452
|
+
}], sortChange: [{
|
|
453
|
+
type: Output
|
|
454
|
+
}], pageChange: [{
|
|
455
|
+
type: Output
|
|
456
|
+
}], exportTriggered: [{
|
|
457
|
+
type: Output
|
|
458
|
+
}] } });
|
|
459
|
+
|
|
460
|
+
/*
|
|
461
|
+
* Public API Surface of dynamic-datatable
|
|
462
|
+
*/
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Generated bundle index. Do not edit.
|
|
466
|
+
*/
|
|
467
|
+
|
|
468
|
+
export { DynamicDatatableComponent };
|
|
469
|
+
//# sourceMappingURL=ng-dynamic-datatable.mjs.map
|