@tociva/tailng-ui 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/tociva-tailng-ui-buttons-indicators.mjs +497 -16
- package/fesm2022/tociva-tailng-ui-buttons-indicators.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-data-table-structure.mjs +802 -17
- package/fesm2022/tociva-tailng-ui-data-table-structure.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-form-controls.mjs +3096 -17
- package/fesm2022/tociva-tailng-ui-form-controls.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-layout.mjs +438 -17
- package/fesm2022/tociva-tailng-ui-layout.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-navigation.mjs +780 -17
- package/fesm2022/tociva-tailng-ui-navigation.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-popups-overlays.mjs +1115 -17
- package/fesm2022/tociva-tailng-ui-popups-overlays.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui-utilities.mjs +129 -16
- package/fesm2022/tociva-tailng-ui-utilities.mjs.map +1 -1
- package/fesm2022/tociva-tailng-ui.mjs +15 -17
- package/fesm2022/tociva-tailng-ui.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1,22 +1,807 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import { NgTemplateOutlet, NgStyle } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { InjectionToken, Directive, input, inject, effect, ContentChild, Component, signal, EventEmitter, computed, Output, ContentChildren, HostListener, HostBinding, ElementRef } from '@angular/core';
|
|
4
|
+
import { TngOverlayRef, TngConnectedOverlay, TngOverlayPanel } from '@tociva/tailng-ui/popups-overlays';
|
|
5
|
+
|
|
6
|
+
const TNG_TABLE = new InjectionToken('TNG_TABLE');
|
|
7
|
+
|
|
8
|
+
class TngCellDef {
|
|
9
|
+
tpl;
|
|
10
|
+
constructor(tpl) {
|
|
11
|
+
this.tpl = tpl;
|
|
12
|
+
}
|
|
13
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngCellDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
14
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: TngCellDef, isStandalone: true, selector: "ng-template[tngCell]", ngImport: i0 });
|
|
15
|
+
}
|
|
16
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngCellDef, decorators: [{
|
|
17
|
+
type: Directive,
|
|
18
|
+
args: [{
|
|
19
|
+
selector: 'ng-template[tngCell]',
|
|
20
|
+
standalone: true,
|
|
21
|
+
}]
|
|
22
|
+
}], ctorParameters: () => [{ type: i0.TemplateRef }] });
|
|
23
|
+
|
|
24
|
+
class TngHeaderDef {
|
|
25
|
+
tpl;
|
|
26
|
+
constructor(tpl) {
|
|
27
|
+
this.tpl = tpl;
|
|
28
|
+
}
|
|
29
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngHeaderDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
30
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.6", type: TngHeaderDef, isStandalone: true, selector: "ng-template[tngHeader]", ngImport: i0 });
|
|
31
|
+
}
|
|
32
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngHeaderDef, decorators: [{
|
|
33
|
+
type: Directive,
|
|
34
|
+
args: [{
|
|
35
|
+
selector: 'ng-template[tngHeader]',
|
|
36
|
+
standalone: true,
|
|
37
|
+
}]
|
|
38
|
+
}], ctorParameters: () => [{ type: i0.TemplateRef }] });
|
|
39
|
+
|
|
40
|
+
class TngCol {
|
|
41
|
+
/** unique column id */
|
|
42
|
+
id = input.required(...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
43
|
+
/** header label (fallback when no tngHeader template is provided) */
|
|
44
|
+
header = input('', ...(ngDevMode ? [{ debugName: "header" }] : []));
|
|
45
|
+
/** optional value resolver */
|
|
46
|
+
value = input(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
47
|
+
/** width (e.g. '120px', '10rem', '20%') */
|
|
48
|
+
width = input(null, ...(ngDevMode ? [{ debugName: "width" }] : []));
|
|
49
|
+
/** alignment */
|
|
50
|
+
align = input('left', ...(ngDevMode ? [{ debugName: "align" }] : []));
|
|
51
|
+
/** extra CSS classes applied to th/td */
|
|
52
|
+
klass = input(null, ...(ngDevMode ? [{ debugName: "klass" }] : []));
|
|
53
|
+
/** default filter meta (used by tng-filter-panel) */
|
|
54
|
+
filter = input(null, ...(ngDevMode ? [{ debugName: "filter" }] : []));
|
|
55
|
+
table = inject(TNG_TABLE);
|
|
56
|
+
// Projected templates
|
|
57
|
+
cellDef;
|
|
58
|
+
headerDef;
|
|
59
|
+
constructor() {
|
|
60
|
+
// Register column meta for default filter panel + any future features
|
|
61
|
+
effect((onCleanup) => {
|
|
62
|
+
const id = this.id();
|
|
63
|
+
this.table.registerColumn({
|
|
64
|
+
id,
|
|
65
|
+
label: this.resolveHeader(),
|
|
66
|
+
filter: this.filter() ?? undefined,
|
|
67
|
+
});
|
|
68
|
+
onCleanup(() => this.table.unregisterColumn(id));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
resolveHeader() {
|
|
72
|
+
return this.header() || this.id();
|
|
73
|
+
}
|
|
74
|
+
resolveValue(row) {
|
|
75
|
+
const valueFn = this.value();
|
|
76
|
+
return valueFn ? valueFn(row) : row?.[this.id()];
|
|
77
|
+
}
|
|
78
|
+
resolveCellTpl() {
|
|
79
|
+
return this.cellDef?.tpl;
|
|
80
|
+
}
|
|
81
|
+
resolveHeaderTpl() {
|
|
82
|
+
return this.headerDef?.tpl;
|
|
83
|
+
}
|
|
84
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngCol, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
85
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: TngCol, isStandalone: true, selector: "tng-col", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, klass: { classPropertyName: "klass", publicName: "klass", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "cellDef", first: true, predicate: TngCellDef, descendants: true }, { propertyName: "headerDef", first: true, predicate: TngHeaderDef, descendants: true }], ngImport: i0, template: '', isInline: true });
|
|
86
|
+
}
|
|
87
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngCol, decorators: [{
|
|
88
|
+
type: Component,
|
|
89
|
+
args: [{
|
|
90
|
+
selector: 'tng-col',
|
|
91
|
+
standalone: true,
|
|
92
|
+
template: '',
|
|
93
|
+
}]
|
|
94
|
+
}], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], klass: [{ type: i0.Input, args: [{ isSignal: true, alias: "klass", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], cellDef: [{
|
|
95
|
+
type: ContentChild,
|
|
96
|
+
args: [TngCellDef]
|
|
97
|
+
}], headerDef: [{
|
|
98
|
+
type: ContentChild,
|
|
99
|
+
args: [TngHeaderDef]
|
|
100
|
+
}] } });
|
|
101
|
+
|
|
102
|
+
// core/features/sort.feature.ts
|
|
103
|
+
class TngTableSortFeature {
|
|
104
|
+
sort = signal({ active: '', direction: '' }, ...(ngDevMode ? [{ debugName: "sort" }] : []));
|
|
105
|
+
toggleSort(active) {
|
|
106
|
+
const cur = this.sort();
|
|
107
|
+
const same = cur.active === active;
|
|
108
|
+
const nextDir = !same ? 'asc' : cur.direction === 'asc' ? 'desc' : cur.direction === 'desc' ? '' : 'asc';
|
|
109
|
+
this.sort.set({
|
|
110
|
+
active: nextDir ? active : '',
|
|
111
|
+
direction: nextDir,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
setSort(sort) {
|
|
115
|
+
this.sort.set(sort);
|
|
116
|
+
}
|
|
117
|
+
clearSort() {
|
|
118
|
+
this.sort.set({ active: '', direction: '' });
|
|
119
|
+
}
|
|
120
|
+
directionFor(colId) {
|
|
121
|
+
const s = this.sort();
|
|
122
|
+
return s.active === colId ? s.direction : '';
|
|
123
|
+
}
|
|
124
|
+
isSorted(colId) {
|
|
125
|
+
return this.directionFor(colId) !== '';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
class TngSortController {
|
|
130
|
+
featureId = 'sort';
|
|
131
|
+
feature = new TngTableSortFeature();
|
|
132
|
+
sort = this.feature.sort;
|
|
133
|
+
toggleSort(active) {
|
|
134
|
+
this.feature.toggleSort(active);
|
|
135
|
+
}
|
|
136
|
+
setSort(sort) {
|
|
137
|
+
this.feature.setSort(sort);
|
|
138
|
+
}
|
|
139
|
+
clearSort() {
|
|
140
|
+
this.feature.clearSort();
|
|
141
|
+
}
|
|
142
|
+
directionFor(colId) {
|
|
143
|
+
return this.feature.directionFor(colId);
|
|
144
|
+
}
|
|
145
|
+
isSorted(colId) {
|
|
146
|
+
return this.feature.isSorted(colId);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class TngTableFilterFeature {
|
|
151
|
+
filters = signal({}, ...(ngDevMode ? [{ debugName: "filters" }] : []));
|
|
152
|
+
/** Active filter UI column id ('' means closed) */
|
|
153
|
+
openFilterColId = signal('', ...(ngDevMode ? [{ debugName: "openFilterColId" }] : []));
|
|
154
|
+
/** Anchor element for positioning the overlay */
|
|
155
|
+
anchorEl = signal(null, ...(ngDevMode ? [{ debugName: "anchorEl" }] : []));
|
|
156
|
+
openFilter(colId, anchorEl) {
|
|
157
|
+
this.openFilterColId.set(colId);
|
|
158
|
+
this.anchorEl.set(anchorEl ?? null);
|
|
159
|
+
}
|
|
160
|
+
closeFilter() {
|
|
161
|
+
this.openFilterColId.set('');
|
|
162
|
+
this.anchorEl.set(null);
|
|
163
|
+
}
|
|
164
|
+
toggleFilter(colId, anchorEl) {
|
|
165
|
+
if (this.openFilterColId() === colId) {
|
|
166
|
+
this.closeFilter();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.openFilter(colId, anchorEl);
|
|
170
|
+
}
|
|
171
|
+
isFilterOpenFor(colId) {
|
|
172
|
+
return this.openFilterColId() === colId;
|
|
173
|
+
}
|
|
174
|
+
setFilter(colId, value) {
|
|
175
|
+
this.filters.update((cur) => ({ ...cur, [colId]: value }));
|
|
176
|
+
}
|
|
177
|
+
clearFilter(colId) {
|
|
178
|
+
this.filters.update((cur) => {
|
|
179
|
+
if (!(colId in cur))
|
|
180
|
+
return cur;
|
|
181
|
+
const next = { ...cur };
|
|
182
|
+
delete next[colId];
|
|
183
|
+
return next;
|
|
184
|
+
});
|
|
185
|
+
if (this.openFilterColId() === colId)
|
|
186
|
+
this.closeFilter();
|
|
187
|
+
}
|
|
188
|
+
clearAllFilters() {
|
|
189
|
+
this.filters.set({});
|
|
190
|
+
this.closeFilter();
|
|
191
|
+
}
|
|
192
|
+
filterValueFor(colId) {
|
|
193
|
+
return this.filters()[colId];
|
|
194
|
+
}
|
|
195
|
+
isFiltered(colId) {
|
|
196
|
+
return colId in this.filters();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class TngFilterController {
|
|
201
|
+
featureId = 'filter';
|
|
202
|
+
feature = new TngTableFilterFeature();
|
|
203
|
+
filters = this.feature.filters;
|
|
204
|
+
openFilterColId = this.feature.openFilterColId;
|
|
205
|
+
filterAnchorEl = this.feature.anchorEl;
|
|
206
|
+
_filterPanelKlass = signal('', ...(ngDevMode ? [{ debugName: "_filterPanelKlass" }] : []));
|
|
207
|
+
setFilterPanelKlass(v) {
|
|
208
|
+
this._filterPanelKlass.set(v);
|
|
209
|
+
}
|
|
210
|
+
filterPanelKlass() {
|
|
211
|
+
return this._filterPanelKlass();
|
|
212
|
+
}
|
|
213
|
+
openFilter(colId, anchorEl) {
|
|
214
|
+
this.feature.openFilter(colId, anchorEl);
|
|
215
|
+
}
|
|
216
|
+
closeFilter() {
|
|
217
|
+
this.feature.closeFilter();
|
|
218
|
+
}
|
|
219
|
+
toggleFilter(colId, anchorEl) {
|
|
220
|
+
this.feature.toggleFilter(colId, anchorEl);
|
|
221
|
+
}
|
|
222
|
+
isFilterOpenFor(colId) {
|
|
223
|
+
return this.feature.isFilterOpenFor(colId);
|
|
224
|
+
}
|
|
225
|
+
setFilter(colId, value) {
|
|
226
|
+
this.feature.setFilter(colId, value);
|
|
227
|
+
}
|
|
228
|
+
clearFilter(colId) {
|
|
229
|
+
this.feature.clearFilter(colId);
|
|
230
|
+
}
|
|
231
|
+
clearAllFilters() {
|
|
232
|
+
this.feature.clearAllFilters();
|
|
233
|
+
}
|
|
234
|
+
filterValueFor(colId) {
|
|
235
|
+
return this.feature.filterValueFor(colId);
|
|
236
|
+
}
|
|
237
|
+
isFiltered(colId) {
|
|
238
|
+
return this.feature.isFiltered(colId);
|
|
239
|
+
}
|
|
240
|
+
setFilters(filters) {
|
|
241
|
+
this.filters.set(filters);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class TngColumnMetaController {
|
|
246
|
+
featureId = 'column-meta';
|
|
247
|
+
colMeta = signal({}, ...(ngDevMode ? [{ debugName: "colMeta" }] : []));
|
|
248
|
+
registerColumn(meta) {
|
|
249
|
+
this.colMeta.update((cur) => ({ ...cur, [meta.id]: meta }));
|
|
250
|
+
}
|
|
251
|
+
unregisterColumn(colId) {
|
|
252
|
+
this.colMeta.update((cur) => {
|
|
253
|
+
if (!(colId in cur))
|
|
254
|
+
return cur;
|
|
255
|
+
const next = { ...cur };
|
|
256
|
+
delete next[colId];
|
|
257
|
+
return next;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
metaFor(colId) {
|
|
261
|
+
return this.colMeta()[colId];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
class TngTableController {
|
|
266
|
+
_featureId = 'table';
|
|
267
|
+
// Keep references to run lifecycle hooks if you need them later
|
|
268
|
+
features = [];
|
|
269
|
+
sortCtrl = new TngSortController();
|
|
270
|
+
filterCtrl = new TngFilterController();
|
|
271
|
+
colMetaCtrl = new TngColumnMetaController();
|
|
272
|
+
constructor() {
|
|
273
|
+
this.features = [this.sortCtrl, this.filterCtrl, this.colMetaCtrl];
|
|
274
|
+
this.features.forEach((f) => f.onInit?.());
|
|
275
|
+
}
|
|
276
|
+
destroy() {
|
|
277
|
+
this.features.forEach((f) => f.onDestroy?.());
|
|
278
|
+
}
|
|
279
|
+
// Optional: re-expose a flat API (so existing code doesn't change)
|
|
280
|
+
sort = this.sortCtrl.sort;
|
|
281
|
+
toggleSort = this.sortCtrl.toggleSort.bind(this.sortCtrl);
|
|
282
|
+
setSort = this.sortCtrl.setSort.bind(this.sortCtrl);
|
|
283
|
+
clearSort = this.sortCtrl.clearSort.bind(this.sortCtrl);
|
|
284
|
+
directionFor = this.sortCtrl.directionFor.bind(this.sortCtrl);
|
|
285
|
+
isSorted = this.sortCtrl.isSorted.bind(this.sortCtrl);
|
|
286
|
+
filters = this.filterCtrl.filters;
|
|
287
|
+
openFilterColId = this.filterCtrl.openFilterColId;
|
|
288
|
+
filterAnchorEl = this.filterCtrl.filterAnchorEl;
|
|
289
|
+
setFilterPanelKlass = this.filterCtrl.setFilterPanelKlass.bind(this.filterCtrl);
|
|
290
|
+
filterPanelKlass = this.filterCtrl.filterPanelKlass.bind(this.filterCtrl);
|
|
291
|
+
openFilter = this.filterCtrl.openFilter.bind(this.filterCtrl);
|
|
292
|
+
closeFilter = this.filterCtrl.closeFilter.bind(this.filterCtrl);
|
|
293
|
+
toggleFilter = this.filterCtrl.toggleFilter.bind(this.filterCtrl);
|
|
294
|
+
isFilterOpenFor = this.filterCtrl.isFilterOpenFor.bind(this.filterCtrl);
|
|
295
|
+
setFilter = this.filterCtrl.setFilter.bind(this.filterCtrl);
|
|
296
|
+
clearFilter = this.filterCtrl.clearFilter.bind(this.filterCtrl);
|
|
297
|
+
clearAllFilters = this.filterCtrl.clearAllFilters.bind(this.filterCtrl);
|
|
298
|
+
filterValueFor = this.filterCtrl.filterValueFor.bind(this.filterCtrl);
|
|
299
|
+
isFiltered = this.filterCtrl.isFiltered.bind(this.filterCtrl);
|
|
300
|
+
setFilters = this.filterCtrl.setFilters.bind(this.filterCtrl);
|
|
301
|
+
registerColumn = this.colMetaCtrl.registerColumn.bind(this.colMetaCtrl);
|
|
302
|
+
unregisterColumn = this.colMetaCtrl.unregisterColumn.bind(this.colMetaCtrl);
|
|
303
|
+
metaFor = this.colMetaCtrl.metaFor.bind(this.colMetaCtrl);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
class TngTable {
|
|
307
|
+
colDefs;
|
|
308
|
+
controller = inject(TNG_TABLE);
|
|
309
|
+
/** Emits on any sort change (both client & server modes). */
|
|
310
|
+
sortChange = new EventEmitter();
|
|
311
|
+
/* =====================
|
|
312
|
+
* Inputs
|
|
313
|
+
* ===================== */
|
|
314
|
+
rows = input([], ...(ngDevMode ? [{ debugName: "rows" }] : []));
|
|
315
|
+
rowKey = input(null, ...(ngDevMode ? [{ debugName: "rowKey" }] : []));
|
|
316
|
+
/** Default: client (static) sort */
|
|
317
|
+
sortMode = input('client', ...(ngDevMode ? [{ debugName: "sortMode" }] : []));
|
|
318
|
+
/** styling */
|
|
319
|
+
tableKlass = input('w-full text-sm', ...(ngDevMode ? [{ debugName: "tableKlass" }] : []));
|
|
320
|
+
theadKlass = input('bg-alternate-background', ...(ngDevMode ? [{ debugName: "theadKlass" }] : []));
|
|
321
|
+
thKlass = input('px-3 py-2 text-left font-semibold border-b border-border', ...(ngDevMode ? [{ debugName: "thKlass" }] : []));
|
|
322
|
+
tdKlass = input('px-3 py-2 border-b border-border align-middle', ...(ngDevMode ? [{ debugName: "tdKlass" }] : []));
|
|
323
|
+
tbodyKlass = input('bg-bg', ...(ngDevMode ? [{ debugName: "tbodyKlass" }] : []));
|
|
324
|
+
/** empty */
|
|
325
|
+
emptyText = input('No data', ...(ngDevMode ? [{ debugName: "emptyText" }] : []));
|
|
326
|
+
/* =====================
|
|
327
|
+
* Internal projected columns signal
|
|
328
|
+
* ===================== */
|
|
329
|
+
projectedCols = signal([], ...(ngDevMode ? [{ debugName: "projectedCols" }] : []));
|
|
330
|
+
constructor() {
|
|
331
|
+
// Emit sortChange whenever controller.sort changes (skip initial emission)
|
|
332
|
+
let first = true;
|
|
333
|
+
effect(() => {
|
|
334
|
+
const s = this.controller.sort();
|
|
335
|
+
if (first) {
|
|
336
|
+
first = false;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
this.sortChange.emit(s);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
ngAfterContentInit() {
|
|
343
|
+
const sync = () => this.projectedCols.set(this.colDefs?.toArray() ?? []);
|
|
344
|
+
sync();
|
|
345
|
+
this.colDefs.changes.subscribe(sync);
|
|
346
|
+
}
|
|
347
|
+
/* =====================
|
|
348
|
+
* Columns
|
|
349
|
+
* ===================== */
|
|
350
|
+
columns = computed(() => this.projectedCols().map((c) => ({
|
|
351
|
+
id: c.id(),
|
|
352
|
+
header: c.resolveHeader(),
|
|
353
|
+
align: c.align(),
|
|
354
|
+
width: c.width() ?? undefined,
|
|
355
|
+
klass: c.klass() ?? undefined,
|
|
356
|
+
value: (row) => c.resolveValue(row),
|
|
357
|
+
headerTpl: c.resolveHeaderTpl(),
|
|
358
|
+
cellTpl: c.resolveCellTpl(),
|
|
359
|
+
})), ...(ngDevMode ? [{ debugName: "columns" }] : []));
|
|
360
|
+
/* =====================
|
|
361
|
+
* Rows view (client sorting)
|
|
362
|
+
* ===================== */
|
|
363
|
+
viewRows = computed(() => {
|
|
364
|
+
const rows = this.rows();
|
|
365
|
+
const mode = this.sortMode();
|
|
366
|
+
const sort = this.controller.sort();
|
|
367
|
+
if (mode !== 'client')
|
|
368
|
+
return rows;
|
|
369
|
+
if (!sort.active || !sort.direction)
|
|
370
|
+
return rows;
|
|
371
|
+
const dir = sort.direction === 'asc' ? 1 : -1;
|
|
372
|
+
const colId = sort.active;
|
|
373
|
+
// stable sort
|
|
374
|
+
return rows
|
|
375
|
+
.map((row, i) => ({ row, i, key: this.sortValue(row, colId) }))
|
|
376
|
+
.sort((a, b) => {
|
|
377
|
+
const c = this.compare(a.key, b.key);
|
|
378
|
+
return c !== 0 ? c * dir : a.i - b.i;
|
|
379
|
+
})
|
|
380
|
+
.map((x) => x.row);
|
|
381
|
+
}, ...(ngDevMode ? [{ debugName: "viewRows" }] : []));
|
|
382
|
+
hasRows = computed(() => (this.viewRows()?.length ?? 0) > 0, ...(ngDevMode ? [{ debugName: "hasRows" }] : []));
|
|
383
|
+
trackRow = (index, row) => {
|
|
384
|
+
const key = this.rowKey();
|
|
385
|
+
return key ? row?.[key] ?? index : index;
|
|
386
|
+
};
|
|
387
|
+
/* =====================
|
|
388
|
+
* Rendering helpers
|
|
389
|
+
* ===================== */
|
|
390
|
+
alignClass(align) {
|
|
391
|
+
switch (align) {
|
|
392
|
+
case 'right':
|
|
393
|
+
return 'text-right';
|
|
394
|
+
case 'center':
|
|
395
|
+
return 'text-center';
|
|
396
|
+
default:
|
|
397
|
+
return 'text-left';
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
styleWidth(col) {
|
|
401
|
+
return col.width ? { width: col.width } : null;
|
|
402
|
+
}
|
|
403
|
+
cellCtx(row, rowIndex, col) {
|
|
404
|
+
const value = col.value ? col.value(row) : row?.[col.id];
|
|
405
|
+
return { $implicit: row, row, rowIndex, colId: col.id, value };
|
|
406
|
+
}
|
|
407
|
+
headerCtx(col) {
|
|
408
|
+
return { colId: col.id, header: col.header };
|
|
409
|
+
}
|
|
410
|
+
/* =====================
|
|
411
|
+
* Sort helpers
|
|
412
|
+
* ===================== */
|
|
413
|
+
sortValue(row, colId) {
|
|
414
|
+
const col = this.columns().find((c) => c.id === colId);
|
|
415
|
+
if (col?.value)
|
|
416
|
+
return col.value(row);
|
|
417
|
+
return row?.[colId];
|
|
418
|
+
}
|
|
419
|
+
compare(a, b) {
|
|
420
|
+
// null/undefined first
|
|
421
|
+
if (a == null && b == null)
|
|
422
|
+
return 0;
|
|
423
|
+
if (a == null)
|
|
424
|
+
return -1;
|
|
425
|
+
if (b == null)
|
|
426
|
+
return 1;
|
|
427
|
+
// numbers (avoid booleans)
|
|
428
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
429
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
430
|
+
}
|
|
431
|
+
const an = typeof a === 'string' ? Number(a) : NaN;
|
|
432
|
+
const bn = typeof b === 'string' ? Number(b) : NaN;
|
|
433
|
+
if (Number.isFinite(an) && Number.isFinite(bn)) {
|
|
434
|
+
return an === bn ? 0 : an < bn ? -1 : 1;
|
|
435
|
+
}
|
|
436
|
+
// ISO-ish dates (string)
|
|
437
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
438
|
+
const at = Date.parse(a);
|
|
439
|
+
const bt = Date.parse(b);
|
|
440
|
+
if (!Number.isNaN(at) && !Number.isNaN(bt)) {
|
|
441
|
+
return at === bt ? 0 : at < bt ? -1 : 1;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// fallback: locale string
|
|
445
|
+
return String(a).localeCompare(String(b));
|
|
446
|
+
}
|
|
447
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
448
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngTable, isStandalone: true, selector: "tng-table", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, sortMode: { classPropertyName: "sortMode", publicName: "sortMode", isSignal: true, isRequired: false, transformFunction: null }, tableKlass: { classPropertyName: "tableKlass", publicName: "tableKlass", isSignal: true, isRequired: false, transformFunction: null }, theadKlass: { classPropertyName: "theadKlass", publicName: "theadKlass", isSignal: true, isRequired: false, transformFunction: null }, thKlass: { classPropertyName: "thKlass", publicName: "thKlass", isSignal: true, isRequired: false, transformFunction: null }, tdKlass: { classPropertyName: "tdKlass", publicName: "tdKlass", isSignal: true, isRequired: false, transformFunction: null }, tbodyKlass: { classPropertyName: "tbodyKlass", publicName: "tbodyKlass", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange" }, providers: [{ provide: TNG_TABLE, useFactory: () => new TngTableController() }], queries: [{ propertyName: "colDefs", predicate: TngCol }], ngImport: i0, template: "<div class=\"relative\">\n <table [class]=\"tableKlass()\">\n <thead [class]=\"theadKlass()\">\n <tr>\n @for (col of columns(); track col.id) {\n <th\n scope=\"col\"\n [class]=\"(thKlass() + ' ' + alignClass(col.align) + (col.klass ? ' ' + col.klass : '')).trim()\"\n [ngStyle]=\"styleWidth(col)\"\n >\n @if (col.headerTpl) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTpl\"\n [ngTemplateOutletContext]=\"headerCtx(col)\"\n />\n } @else {\n {{ col.header }}\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [class]=\"tbodyKlass()\">\n @if (hasRows()) {\n @for (row of viewRows(); let rowIndex = $index; track row?.[rowKey()] ?? rowIndex) {\n <tr>\n @for (col of columns(); track col.id) {\n <td\n [class]=\"(tdKlass() + ' ' + alignClass(col.align) + (col.klass ? ' ' + col.klass : '')).trim()\"\n >\n @if (col.cellTpl) {\n <ng-container\n [ngTemplateOutlet]=\"col.cellTpl\"\n [ngTemplateOutletContext]=\"cellCtx(row, rowIndex, col)\"\n />\n } @else {\n {{ col.value?.(row) }}\n }\n </td>\n }\n </tr>\n }\n } @else {\n <tr>\n <td [attr.colspan]=\"columns().length || 1\" class=\"px-3 py-8 text-center text-disable\">\n {{ emptyText() }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n\n <!-- Render projected extras like filter panel -->\n <ng-content />\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
|
|
449
|
+
}
|
|
450
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTable, decorators: [{
|
|
451
|
+
type: Component,
|
|
452
|
+
args: [{ selector: 'tng-table', standalone: true, imports: [NgTemplateOutlet, NgStyle], providers: [{ provide: TNG_TABLE, useFactory: () => new TngTableController() }], template: "<div class=\"relative\">\n <table [class]=\"tableKlass()\">\n <thead [class]=\"theadKlass()\">\n <tr>\n @for (col of columns(); track col.id) {\n <th\n scope=\"col\"\n [class]=\"(thKlass() + ' ' + alignClass(col.align) + (col.klass ? ' ' + col.klass : '')).trim()\"\n [ngStyle]=\"styleWidth(col)\"\n >\n @if (col.headerTpl) {\n <ng-container\n [ngTemplateOutlet]=\"col.headerTpl\"\n [ngTemplateOutletContext]=\"headerCtx(col)\"\n />\n } @else {\n {{ col.header }}\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody [class]=\"tbodyKlass()\">\n @if (hasRows()) {\n @for (row of viewRows(); let rowIndex = $index; track row?.[rowKey()] ?? rowIndex) {\n <tr>\n @for (col of columns(); track col.id) {\n <td\n [class]=\"(tdKlass() + ' ' + alignClass(col.align) + (col.klass ? ' ' + col.klass : '')).trim()\"\n >\n @if (col.cellTpl) {\n <ng-container\n [ngTemplateOutlet]=\"col.cellTpl\"\n [ngTemplateOutletContext]=\"cellCtx(row, rowIndex, col)\"\n />\n } @else {\n {{ col.value?.(row) }}\n }\n </td>\n }\n </tr>\n }\n } @else {\n <tr>\n <td [attr.colspan]=\"columns().length || 1\" class=\"px-3 py-8 text-center text-disable\">\n {{ emptyText() }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n\n <!-- Render projected extras like filter panel -->\n <ng-content />\n</div>\n" }]
|
|
453
|
+
}], ctorParameters: () => [], propDecorators: { colDefs: [{
|
|
454
|
+
type: ContentChildren,
|
|
455
|
+
args: [TngCol]
|
|
456
|
+
}], sortChange: [{
|
|
457
|
+
type: Output
|
|
458
|
+
}], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], sortMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortMode", required: false }] }], tableKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableKlass", required: false }] }], theadKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "theadKlass", required: false }] }], thKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "thKlass", required: false }] }], tdKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "tdKlass", required: false }] }], tbodyKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "tbodyKlass", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }] } });
|
|
459
|
+
|
|
460
|
+
class TngSortHeaderDirective {
|
|
461
|
+
colId = input.required(...(ngDevMode ? [{ debugName: "colId" }] : []));
|
|
462
|
+
table = inject(TNG_TABLE);
|
|
463
|
+
direction = computed(() => this.table.directionFor(this.colId()), ...(ngDevMode ? [{ debugName: "direction" }] : []));
|
|
464
|
+
isSorted = computed(() => this.direction() !== '', ...(ngDevMode ? [{ debugName: "isSorted" }] : []));
|
|
465
|
+
// a11y
|
|
466
|
+
role = 'button';
|
|
467
|
+
tabindex = 0;
|
|
468
|
+
get ariaSort() {
|
|
469
|
+
const dir = this.direction();
|
|
470
|
+
if (dir === 'asc')
|
|
471
|
+
return 'ascending';
|
|
472
|
+
if (dir === 'desc')
|
|
473
|
+
return 'descending';
|
|
474
|
+
return 'none';
|
|
475
|
+
}
|
|
476
|
+
onClick() {
|
|
477
|
+
this.table.toggleSort(this.colId());
|
|
478
|
+
}
|
|
479
|
+
onKey(ev) {
|
|
480
|
+
ev.preventDefault();
|
|
481
|
+
this.table.toggleSort(this.colId());
|
|
482
|
+
}
|
|
483
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
484
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TngSortHeaderDirective, isStandalone: true, selector: "[tngSortHeader]", inputs: { colId: { classPropertyName: "colId", publicName: "colId", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "onClick()", "keydown.enter": "onKey($event)", "keydown.space": "onKey($event)" }, properties: { "attr.role": "this.role", "attr.tabindex": "this.tabindex", "attr.aria-sort": "this.ariaSort" } }, ngImport: i0 });
|
|
485
|
+
}
|
|
486
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortHeaderDirective, decorators: [{
|
|
487
|
+
type: Directive,
|
|
488
|
+
args: [{
|
|
489
|
+
selector: '[tngSortHeader]',
|
|
490
|
+
standalone: true,
|
|
491
|
+
}]
|
|
492
|
+
}], propDecorators: { colId: [{ type: i0.Input, args: [{ isSignal: true, alias: "colId", required: true }] }], role: [{
|
|
493
|
+
type: HostBinding,
|
|
494
|
+
args: ['attr.role']
|
|
495
|
+
}], tabindex: [{
|
|
496
|
+
type: HostBinding,
|
|
497
|
+
args: ['attr.tabindex']
|
|
498
|
+
}], ariaSort: [{
|
|
499
|
+
type: HostBinding,
|
|
500
|
+
args: ['attr.aria-sort']
|
|
501
|
+
}], onClick: [{
|
|
502
|
+
type: HostListener,
|
|
503
|
+
args: ['click']
|
|
504
|
+
}], onKey: [{
|
|
505
|
+
type: HostListener,
|
|
506
|
+
args: ['keydown.enter', ['$event']]
|
|
507
|
+
}, {
|
|
508
|
+
type: HostListener,
|
|
509
|
+
args: ['keydown.space', ['$event']]
|
|
510
|
+
}] } });
|
|
511
|
+
|
|
512
|
+
class TngSortIcon {
|
|
513
|
+
host = inject(TngSortHeaderDirective);
|
|
514
|
+
dir = this.host.direction;
|
|
515
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortIcon, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
516
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngSortIcon, isStandalone: true, selector: "tng-sort-icon", ngImport: i0, template: `
|
|
517
|
+
@switch (dir()) {
|
|
518
|
+
@case ('asc') { <span aria-hidden="true">▲</span> }
|
|
519
|
+
@case ('desc') { <span aria-hidden="true">▼</span> }
|
|
520
|
+
@default { <span aria-hidden="true" class="opacity-40">↕</span> }
|
|
521
|
+
}
|
|
522
|
+
`, isInline: true });
|
|
523
|
+
}
|
|
524
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortIcon, decorators: [{
|
|
525
|
+
type: Component,
|
|
526
|
+
args: [{
|
|
527
|
+
selector: 'tng-sort-icon',
|
|
528
|
+
standalone: true,
|
|
529
|
+
template: `
|
|
530
|
+
@switch (dir()) {
|
|
531
|
+
@case ('asc') { <span aria-hidden="true">▲</span> }
|
|
532
|
+
@case ('desc') { <span aria-hidden="true">▼</span> }
|
|
533
|
+
@default { <span aria-hidden="true" class="opacity-40">↕</span> }
|
|
534
|
+
}
|
|
535
|
+
`,
|
|
536
|
+
}]
|
|
537
|
+
}] });
|
|
538
|
+
|
|
539
|
+
class TngFilterTrigger {
|
|
540
|
+
colId = input.required(...(ngDevMode ? [{ debugName: "colId" }] : []));
|
|
541
|
+
table = inject(TNG_TABLE);
|
|
542
|
+
el = inject((ElementRef));
|
|
543
|
+
panelKlass = input('', ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
|
|
544
|
+
isFiltered = computed(() => this.table.isFiltered(this.colId()), ...(ngDevMode ? [{ debugName: "isFiltered" }] : []));
|
|
545
|
+
isOpen = computed(() => this.table.isFilterOpenFor(this.colId()), ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
546
|
+
// a11y
|
|
547
|
+
role = 'button';
|
|
548
|
+
tabindex = 0;
|
|
549
|
+
ariaHaspopup = 'dialog';
|
|
550
|
+
get ariaExpanded() {
|
|
551
|
+
return this.isOpen() ? 'true' : 'false';
|
|
552
|
+
}
|
|
553
|
+
onClick() {
|
|
554
|
+
this.table.setFilterPanelKlass(this.panelKlass()); // store it
|
|
555
|
+
this.table.toggleFilter(this.colId(), this.el.nativeElement);
|
|
556
|
+
}
|
|
557
|
+
onKey(ev) {
|
|
558
|
+
ev.preventDefault();
|
|
559
|
+
this.table.setFilterPanelKlass(this.panelKlass()); // store it
|
|
560
|
+
this.table.toggleFilter(this.colId(), this.el.nativeElement);
|
|
561
|
+
}
|
|
562
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngFilterTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
563
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.6", type: TngFilterTrigger, isStandalone: true, selector: "[tngFilterTrigger]", inputs: { colId: { classPropertyName: "colId", publicName: "colId", isSignal: true, isRequired: true, transformFunction: null }, panelKlass: { classPropertyName: "panelKlass", publicName: "panelKlass", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()", "keydown.enter": "onKey($event)", "keydown.space": "onKey($event)" }, properties: { "attr.role": "this.role", "attr.tabindex": "this.tabindex", "attr.aria-haspopup": "this.ariaHaspopup", "attr.aria-expanded": "this.ariaExpanded" } }, ngImport: i0 });
|
|
564
|
+
}
|
|
565
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngFilterTrigger, decorators: [{
|
|
566
|
+
type: Directive,
|
|
567
|
+
args: [{
|
|
568
|
+
selector: '[tngFilterTrigger]',
|
|
569
|
+
standalone: true,
|
|
570
|
+
}]
|
|
571
|
+
}], propDecorators: { colId: [{ type: i0.Input, args: [{ isSignal: true, alias: "colId", required: true }] }], panelKlass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelKlass", required: false }] }], role: [{
|
|
572
|
+
type: HostBinding,
|
|
573
|
+
args: ['attr.role']
|
|
574
|
+
}], tabindex: [{
|
|
575
|
+
type: HostBinding,
|
|
576
|
+
args: ['attr.tabindex']
|
|
577
|
+
}], ariaHaspopup: [{
|
|
578
|
+
type: HostBinding,
|
|
579
|
+
args: ['attr.aria-haspopup']
|
|
580
|
+
}], ariaExpanded: [{
|
|
581
|
+
type: HostBinding,
|
|
582
|
+
args: ['attr.aria-expanded']
|
|
583
|
+
}], onClick: [{
|
|
584
|
+
type: HostListener,
|
|
585
|
+
args: ['click']
|
|
586
|
+
}], onKey: [{
|
|
587
|
+
type: HostListener,
|
|
588
|
+
args: ['keydown.enter', ['$event']]
|
|
589
|
+
}, {
|
|
590
|
+
type: HostListener,
|
|
591
|
+
args: ['keydown.space', ['$event']]
|
|
592
|
+
}] } });
|
|
593
|
+
|
|
594
|
+
class TngFilterPanel {
|
|
595
|
+
table = inject(TNG_TABLE);
|
|
596
|
+
activeColId = computed(() => this.table.openFilterColId(), ...(ngDevMode ? [{ debugName: "activeColId" }] : []));
|
|
597
|
+
isOpen = computed(() => this.activeColId() !== '', ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
|
|
598
|
+
anchorEl = computed(() => this.table.filterAnchorEl(), ...(ngDevMode ? [{ debugName: "anchorEl" }] : []));
|
|
599
|
+
meta = computed(() => {
|
|
600
|
+
const id = this.activeColId();
|
|
601
|
+
return id ? this.table.metaFor(id) : undefined;
|
|
602
|
+
}, ...(ngDevMode ? [{ debugName: "meta" }] : []));
|
|
603
|
+
filterType = computed(() => this.meta()?.filter?.type ?? 'text', ...(ngDevMode ? [{ debugName: "filterType" }] : []));
|
|
604
|
+
titleSuffix = computed(() => {
|
|
605
|
+
const label = this.meta()?.label;
|
|
606
|
+
return label ? ` • ${label}` : '';
|
|
607
|
+
}, ...(ngDevMode ? [{ debugName: "titleSuffix" }] : []));
|
|
608
|
+
panelKlass = computed(() => {
|
|
609
|
+
const base = 'min-w-80 max-w-[360px] p-0';
|
|
610
|
+
const fromTrigger = this.table.filterPanelKlass();
|
|
611
|
+
return `${base} ${fromTrigger}`.trim();
|
|
612
|
+
}, ...(ngDevMode ? [{ debugName: "panelKlass" }] : []));
|
|
613
|
+
// ---- open/close wiring ----
|
|
614
|
+
onOverlayOpenChange(open) {
|
|
615
|
+
// if overlay closes, reflect that back to controller
|
|
616
|
+
if (!open && this.isOpen())
|
|
617
|
+
this.close();
|
|
618
|
+
}
|
|
619
|
+
onOverlayClosed() {
|
|
620
|
+
// overlay stack says it's closed (outside click / escape)
|
|
621
|
+
if (this.isOpen())
|
|
622
|
+
this.close();
|
|
623
|
+
}
|
|
624
|
+
close() {
|
|
625
|
+
this.table.closeFilter();
|
|
626
|
+
}
|
|
627
|
+
clear() {
|
|
628
|
+
const id = this.activeColId();
|
|
629
|
+
if (id)
|
|
630
|
+
this.table.clearFilter(id);
|
|
631
|
+
}
|
|
632
|
+
// ---------------- TEXT ----------------
|
|
633
|
+
textPlaceholder = computed(() => {
|
|
634
|
+
const f = this.meta()?.filter;
|
|
635
|
+
return f?.type === 'text' ? f.placeholder ?? 'Type to filter…' : 'Type to filter…';
|
|
636
|
+
}, ...(ngDevMode ? [{ debugName: "textPlaceholder" }] : []));
|
|
637
|
+
textValue() {
|
|
638
|
+
const id = this.activeColId();
|
|
639
|
+
const v = id ? this.table.filterValueFor(id) : undefined;
|
|
640
|
+
return typeof v === 'string' ? v : '';
|
|
641
|
+
}
|
|
642
|
+
onTextInput(ev) {
|
|
643
|
+
const value = ev.target?.value ?? '';
|
|
644
|
+
const id = this.activeColId();
|
|
645
|
+
if (!id)
|
|
646
|
+
return;
|
|
647
|
+
const trimmed = value.trim();
|
|
648
|
+
if (!trimmed)
|
|
649
|
+
this.table.clearFilter(id);
|
|
650
|
+
else
|
|
651
|
+
this.table.setFilter(id, trimmed);
|
|
652
|
+
}
|
|
653
|
+
// ---------------- NUMBER ----------------
|
|
654
|
+
numberValue() {
|
|
655
|
+
const id = this.activeColId();
|
|
656
|
+
const v = id ? this.table.filterValueFor(id) : undefined;
|
|
657
|
+
return v && typeof v === 'object' && !Array.isArray(v) ? v : {};
|
|
658
|
+
}
|
|
659
|
+
numberMin() {
|
|
660
|
+
const v = this.numberValue().min;
|
|
661
|
+
return typeof v === 'number' && Number.isFinite(v) ? String(v) : '';
|
|
662
|
+
}
|
|
663
|
+
numberMax() {
|
|
664
|
+
const v = this.numberValue().max;
|
|
665
|
+
return typeof v === 'number' && Number.isFinite(v) ? String(v) : '';
|
|
666
|
+
}
|
|
667
|
+
onNumberMinInput(ev) {
|
|
668
|
+
const raw = ev.target?.value ?? '';
|
|
669
|
+
const id = this.activeColId();
|
|
670
|
+
if (!id)
|
|
671
|
+
return;
|
|
672
|
+
const cur = this.numberValue();
|
|
673
|
+
const n = raw === '' ? undefined : Number(raw);
|
|
674
|
+
const next = { ...cur, min: Number.isFinite(n) ? n : undefined };
|
|
675
|
+
if (!next.min && !next.max)
|
|
676
|
+
this.table.clearFilter(id);
|
|
677
|
+
else
|
|
678
|
+
this.table.setFilter(id, next);
|
|
679
|
+
}
|
|
680
|
+
onNumberMaxInput(ev) {
|
|
681
|
+
const raw = ev.target?.value ?? '';
|
|
682
|
+
const id = this.activeColId();
|
|
683
|
+
if (!id)
|
|
684
|
+
return;
|
|
685
|
+
const cur = this.numberValue();
|
|
686
|
+
const n = raw === '' ? undefined : Number(raw);
|
|
687
|
+
const next = { ...cur, max: Number.isFinite(n) ? n : undefined };
|
|
688
|
+
if (!next.min && !next.max)
|
|
689
|
+
this.table.clearFilter(id);
|
|
690
|
+
else
|
|
691
|
+
this.table.setFilter(id, next);
|
|
692
|
+
}
|
|
693
|
+
// ---------------- DATE ----------------
|
|
694
|
+
dateValue() {
|
|
695
|
+
const id = this.activeColId();
|
|
696
|
+
const v = id ? this.table.filterValueFor(id) : undefined;
|
|
697
|
+
return v && typeof v === 'object' && !Array.isArray(v) ? v : {};
|
|
698
|
+
}
|
|
699
|
+
dateFrom() {
|
|
700
|
+
return this.dateValue().from ?? '';
|
|
701
|
+
}
|
|
702
|
+
dateTo() {
|
|
703
|
+
return this.dateValue().to ?? '';
|
|
704
|
+
}
|
|
705
|
+
onDateFromInput(ev) {
|
|
706
|
+
const from = ev.target?.value ?? '';
|
|
707
|
+
const id = this.activeColId();
|
|
708
|
+
if (!id)
|
|
709
|
+
return;
|
|
710
|
+
const cur = this.dateValue();
|
|
711
|
+
const next = { ...cur, from: from || undefined };
|
|
712
|
+
if (!next.from && !next.to)
|
|
713
|
+
this.table.clearFilter(id);
|
|
714
|
+
else
|
|
715
|
+
this.table.setFilter(id, next);
|
|
716
|
+
}
|
|
717
|
+
onDateToInput(ev) {
|
|
718
|
+
const to = ev.target?.value ?? '';
|
|
719
|
+
const id = this.activeColId();
|
|
720
|
+
if (!id)
|
|
721
|
+
return;
|
|
722
|
+
const cur = this.dateValue();
|
|
723
|
+
const next = { ...cur, to: to || undefined };
|
|
724
|
+
if (!next.from && !next.to)
|
|
725
|
+
this.table.clearFilter(id);
|
|
726
|
+
else
|
|
727
|
+
this.table.setFilter(id, next);
|
|
728
|
+
}
|
|
729
|
+
// ---------------- ENUM ----------------
|
|
730
|
+
enumOptions() {
|
|
731
|
+
const f = this.meta()?.filter;
|
|
732
|
+
return f?.type === 'enum' ? f.options : [];
|
|
733
|
+
}
|
|
734
|
+
enumValue() {
|
|
735
|
+
const id = this.activeColId();
|
|
736
|
+
const v = id ? this.table.filterValueFor(id) : undefined;
|
|
737
|
+
return Array.isArray(v) ? v : [];
|
|
738
|
+
}
|
|
739
|
+
enumChecked(value) {
|
|
740
|
+
return this.enumValue().includes(value);
|
|
741
|
+
}
|
|
742
|
+
toggleEnum(value) {
|
|
743
|
+
const id = this.activeColId();
|
|
744
|
+
if (!id)
|
|
745
|
+
return;
|
|
746
|
+
const cur = this.enumValue();
|
|
747
|
+
const next = cur.includes(value) ? cur.filter((v) => v !== value) : [...cur, value];
|
|
748
|
+
if (next.length === 0)
|
|
749
|
+
this.table.clearFilter(id);
|
|
750
|
+
else
|
|
751
|
+
this.table.setFilter(id, next);
|
|
752
|
+
}
|
|
753
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngFilterPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
754
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngFilterPanel, isStandalone: true, selector: "tng-filter-panel", ngImport: i0, template: "<tng-overlay-ref\n #overlayRef=\"tngOverlayRef\"\n [open]=\"isOpen()\"\n (openChange)=\"onOverlayOpenChange($event)\"\n (closed)=\"onOverlayClosed()\"\n>\n <tng-connected-overlay\n [open]=\"overlayRef.isOpen()\"\n [anchor]=\"anchorEl()\"\n placement=\"bottom-start\"\n (closed)=\"overlayRef.close($event)\"\n [closeOnInsideClick]=\"false\"\n >\n <!-- set width at panel level (menu-style) -->\n <tng-overlay-panel [klass]=\"panelKlass()\">\n <div class=\"w-full\">\n <div class=\"flex items-center justify-between gap-3 border-b border-slate-200 px-3 py-2\">\n <div class=\"min-w-0\">\n <div class=\"truncate text-sm font-semibold text-slate-900\">\n Filter{{ titleSuffix() }}\n </div>\n <div class=\"truncate text-xs text-slate-500\">{{ activeColId() }}</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs text-slate-600 hover:bg-slate-50\"\n (click)=\"clear()\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs text-slate-600 hover:bg-slate-50\"\n (click)=\"close()\"\n aria-label=\"Close filter\"\n >\n \u2715\n </button>\n </div>\n </div>\n\n <div class=\"p-3\">\n @switch (filterType()) {\n @case ('text') {\n <label class=\"text-xs font-medium text-slate-700\">Contains</label>\n <input\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [placeholder]=\"textPlaceholder()\"\n [value]=\"textValue()\"\n (input)=\"onTextInput($event)\"\n />\n }\n\n @case ('number') {\n <div class=\"grid grid-cols-2 gap-3\">\n <div>\n <label class=\"text-xs font-medium text-slate-700\">Min</label>\n <input\n type=\"number\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"numberMin()\"\n (input)=\"onNumberMinInput($event)\"\n />\n </div>\n <div>\n <label class=\"text-xs font-medium text-slate-700\">Max</label>\n <input\n type=\"number\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"numberMax()\"\n (input)=\"onNumberMaxInput($event)\"\n />\n </div>\n </div>\n }\n\n @case ('date') {\n <div class=\"grid grid-cols-2 gap-3\">\n <div>\n <label class=\"text-xs font-medium text-slate-700\">From</label>\n <input\n type=\"date\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"dateFrom()\"\n (input)=\"onDateFromInput($event)\"\n />\n </div>\n <div>\n <label class=\"text-xs font-medium text-slate-700\">To</label>\n <input\n type=\"date\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"dateTo()\"\n (input)=\"onDateToInput($event)\"\n />\n </div>\n </div>\n }\n\n @case ('enum') {\n <div class=\"space-y-2\">\n <div class=\"text-xs font-medium text-slate-700\">Select</div>\n\n <div class=\"max-h-56 overflow-auto rounded border border-slate-200\">\n @for (opt of enumOptions(); track opt.value) {\n <label class=\"flex cursor-pointer items-center gap-2 px-2 py-1 text-sm hover:bg-slate-50\">\n <input\n type=\"checkbox\"\n class=\"h-4 w-4\"\n [checked]=\"enumChecked(opt.value)\"\n (change)=\"toggleEnum(opt.value)\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </label>\n }\n </div>\n </div>\n }\n\n @default {\n <div class=\"text-sm text-slate-600\">\n No default filter configured for this column.\n </div>\n }\n }\n </div>\n </div>\n </tng-overlay-panel>\n </tng-connected-overlay>\n</tng-overlay-ref>\n", dependencies: [{ kind: "component", type: TngOverlayRef, selector: "tng-overlay-ref", inputs: ["open"], outputs: ["openChange", "opened", "closed"], exportAs: ["tngOverlayRef"] }, { kind: "component", type: TngConnectedOverlay, selector: "tng-connected-overlay", inputs: ["open", "anchor", "placement", "offset", "width", "closeOnOutsideClick", "closeOnInsideClick", "closeOnEscape", "hasBackdrop", "backdropClass"], outputs: ["opened", "closed", "backdropClick"] }, { kind: "component", type: TngOverlayPanel, selector: "tng-overlay-panel", inputs: ["klass", "modal", "role", "ariaLabel", "ariaLabelledby", "ariaDescribedby", "restoreFocus", "autoCapture", "deferCaptureElements"] }] });
|
|
755
|
+
}
|
|
756
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngFilterPanel, decorators: [{
|
|
757
|
+
type: Component,
|
|
758
|
+
args: [{ selector: 'tng-filter-panel', standalone: true, imports: [TngOverlayRef, TngConnectedOverlay, TngOverlayPanel], template: "<tng-overlay-ref\n #overlayRef=\"tngOverlayRef\"\n [open]=\"isOpen()\"\n (openChange)=\"onOverlayOpenChange($event)\"\n (closed)=\"onOverlayClosed()\"\n>\n <tng-connected-overlay\n [open]=\"overlayRef.isOpen()\"\n [anchor]=\"anchorEl()\"\n placement=\"bottom-start\"\n (closed)=\"overlayRef.close($event)\"\n [closeOnInsideClick]=\"false\"\n >\n <!-- set width at panel level (menu-style) -->\n <tng-overlay-panel [klass]=\"panelKlass()\">\n <div class=\"w-full\">\n <div class=\"flex items-center justify-between gap-3 border-b border-slate-200 px-3 py-2\">\n <div class=\"min-w-0\">\n <div class=\"truncate text-sm font-semibold text-slate-900\">\n Filter{{ titleSuffix() }}\n </div>\n <div class=\"truncate text-xs text-slate-500\">{{ activeColId() }}</div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs text-slate-600 hover:bg-slate-50\"\n (click)=\"clear()\"\n >\n Clear\n </button>\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-xs text-slate-600 hover:bg-slate-50\"\n (click)=\"close()\"\n aria-label=\"Close filter\"\n >\n \u2715\n </button>\n </div>\n </div>\n\n <div class=\"p-3\">\n @switch (filterType()) {\n @case ('text') {\n <label class=\"text-xs font-medium text-slate-700\">Contains</label>\n <input\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [placeholder]=\"textPlaceholder()\"\n [value]=\"textValue()\"\n (input)=\"onTextInput($event)\"\n />\n }\n\n @case ('number') {\n <div class=\"grid grid-cols-2 gap-3\">\n <div>\n <label class=\"text-xs font-medium text-slate-700\">Min</label>\n <input\n type=\"number\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"numberMin()\"\n (input)=\"onNumberMinInput($event)\"\n />\n </div>\n <div>\n <label class=\"text-xs font-medium text-slate-700\">Max</label>\n <input\n type=\"number\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"numberMax()\"\n (input)=\"onNumberMaxInput($event)\"\n />\n </div>\n </div>\n }\n\n @case ('date') {\n <div class=\"grid grid-cols-2 gap-3\">\n <div>\n <label class=\"text-xs font-medium text-slate-700\">From</label>\n <input\n type=\"date\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"dateFrom()\"\n (input)=\"onDateFromInput($event)\"\n />\n </div>\n <div>\n <label class=\"text-xs font-medium text-slate-700\">To</label>\n <input\n type=\"date\"\n class=\"mt-2 w-full rounded border border-slate-200 px-2 py-1 text-sm outline-none focus:border-slate-400\"\n [value]=\"dateTo()\"\n (input)=\"onDateToInput($event)\"\n />\n </div>\n </div>\n }\n\n @case ('enum') {\n <div class=\"space-y-2\">\n <div class=\"text-xs font-medium text-slate-700\">Select</div>\n\n <div class=\"max-h-56 overflow-auto rounded border border-slate-200\">\n @for (opt of enumOptions(); track opt.value) {\n <label class=\"flex cursor-pointer items-center gap-2 px-2 py-1 text-sm hover:bg-slate-50\">\n <input\n type=\"checkbox\"\n class=\"h-4 w-4\"\n [checked]=\"enumChecked(opt.value)\"\n (change)=\"toggleEnum(opt.value)\"\n />\n <span class=\"truncate\">{{ opt.label }}</span>\n </label>\n }\n </div>\n </div>\n }\n\n @default {\n <div class=\"text-sm text-slate-600\">\n No default filter configured for this column.\n </div>\n }\n }\n </div>\n </div>\n </tng-overlay-panel>\n </tng-connected-overlay>\n</tng-overlay-ref>\n" }]
|
|
759
|
+
}] });
|
|
760
|
+
|
|
761
|
+
class TngSortHeader {
|
|
762
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
763
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TngSortHeader, isStandalone: true, selector: "tng-sort-header", ngImport: i0, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Welcome hello component - Sort Header</p>\n</div>\n" });
|
|
764
|
+
}
|
|
765
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngSortHeader, decorators: [{
|
|
766
|
+
type: Component,
|
|
767
|
+
args: [{ selector: 'tng-sort-header', standalone: true, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Welcome hello component - Sort Header</p>\n</div>\n" }]
|
|
768
|
+
}] });
|
|
769
|
+
|
|
770
|
+
class TngTree {
|
|
771
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
772
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TngTree, isStandalone: true, selector: "tng-tree", ngImport: i0, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Welcome hello component - Tree</p>\n</div>\n" });
|
|
773
|
+
}
|
|
774
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngTree, decorators: [{
|
|
775
|
+
type: Component,
|
|
776
|
+
args: [{ selector: 'tng-tree', standalone: true, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Welcome hello component - Tree</p>\n</div>\n" }]
|
|
777
|
+
}] });
|
|
778
|
+
|
|
779
|
+
class TngEmptyState {
|
|
780
|
+
title = input('No data available', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
781
|
+
message = input('', ...(ngDevMode ? [{ debugName: "message" }] : []));
|
|
782
|
+
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : []));
|
|
783
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngEmptyState, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
784
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TngEmptyState, isStandalone: true, selector: "tng-empty-state", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"flex flex-col items-center justify-center p-8 text-center\">\n @if (icon()) {\n <div class=\"mb-4 text-4xl text-gray-400\">{{ icon() }}</div>\n }\n <h3 class=\"text-lg font-semibold text-gray-900 mb-2\">{{ title() }}</h3>\n @if (message()) {\n <p class=\"text-sm text-gray-600\">{{ message() }}</p>\n }\n</div>\n" });
|
|
785
|
+
}
|
|
786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngEmptyState, decorators: [{
|
|
787
|
+
type: Component,
|
|
788
|
+
args: [{ selector: 'tng-empty-state', standalone: true, template: "<div class=\"flex flex-col items-center justify-center p-8 text-center\">\n @if (icon()) {\n <div class=\"mb-4 text-4xl text-gray-400\">{{ icon() }}</div>\n }\n <h3 class=\"text-lg font-semibold text-gray-900 mb-2\">{{ title() }}</h3>\n @if (message()) {\n <p class=\"text-sm text-gray-600\">{{ message() }}</p>\n }\n</div>\n" }]
|
|
789
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
|
|
790
|
+
|
|
791
|
+
class TngVirtualScroll {
|
|
792
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngVirtualScroll, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
793
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: TngVirtualScroll, isStandalone: true, selector: "tng-virtual-scroll", ngImport: i0, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Virtual Scroll component - placeholder</p>\n</div>\n" });
|
|
794
|
+
}
|
|
795
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TngVirtualScroll, decorators: [{
|
|
796
|
+
type: Component,
|
|
797
|
+
args: [{ selector: 'tng-virtual-scroll', standalone: true, template: "<div class=\"p-4 border border-gray-200 rounded-md\">\n <p class=\"text-gray-600\">Virtual Scroll component - placeholder</p>\n</div>\n" }]
|
|
798
|
+
}] });
|
|
799
|
+
|
|
800
|
+
// Table (components, defs, types, controllers, directives, features)
|
|
18
801
|
|
|
19
802
|
/**
|
|
20
803
|
* Generated bundle index. Do not edit.
|
|
21
804
|
*/
|
|
805
|
+
|
|
806
|
+
export { TNG_TABLE, TngCellDef, TngCol, TngColumnMetaController, TngEmptyState, TngFilterController, TngFilterPanel, TngFilterTrigger, TngHeaderDef, TngSortController, TngSortHeader, TngSortHeaderDirective, TngSortIcon, TngTable, TngTableController, TngTableFilterFeature, TngTableSortFeature, TngTree, TngVirtualScroll };
|
|
22
807
|
//# sourceMappingURL=tociva-tailng-ui-data-table-structure.mjs.map
|