tangrid 0.1.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.
Files changed (38) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +124 -0
  3. package/config/index.d.ts +5 -0
  4. package/config/ngs-config.d.ts +16 -0
  5. package/config/public-api.d.ts +1 -0
  6. package/esm2022/config/ngs-config.mjs +24 -0
  7. package/esm2022/config/public-api.mjs +2 -0
  8. package/esm2022/config/tangrid-config.mjs +5 -0
  9. package/esm2022/index.mjs +17 -0
  10. package/esm2022/table/public-api.mjs +4 -0
  11. package/esm2022/table/table.component.mjs +1433 -0
  12. package/esm2022/table/table.module.mjs +19 -0
  13. package/esm2022/table/table.types.mjs +2 -0
  14. package/esm2022/table/tangrid-table.mjs +5 -0
  15. package/esm2022/table/virtual-provider.mjs +165 -0
  16. package/esm2022/tangrid.mjs +5 -0
  17. package/esm2022/theme/ngs-theme.service.mjs +83 -0
  18. package/esm2022/theme/public-api.mjs +2 -0
  19. package/esm2022/theme/tangrid-theme.mjs +5 -0
  20. package/fesm2022/tangrid-config.mjs +31 -0
  21. package/fesm2022/tangrid-config.mjs.map +1 -0
  22. package/fesm2022/tangrid-table.mjs +1618 -0
  23. package/fesm2022/tangrid-table.mjs.map +1 -0
  24. package/fesm2022/tangrid-theme.mjs +90 -0
  25. package/fesm2022/tangrid-theme.mjs.map +1 -0
  26. package/fesm2022/tangrid.mjs +1732 -0
  27. package/fesm2022/tangrid.mjs.map +1 -0
  28. package/index.d.ts +10 -0
  29. package/package.json +78 -0
  30. package/table/index.d.ts +5 -0
  31. package/table/public-api.d.ts +3 -0
  32. package/table/table.component.d.ts +542 -0
  33. package/table/table.module.d.ts +10 -0
  34. package/table/table.types.d.ts +202 -0
  35. package/table/virtual-provider.d.ts +9 -0
  36. package/theme/index.d.ts +5 -0
  37. package/theme/ngs-theme.service.d.ts +34 -0
  38. package/theme/public-api.d.ts +1 -0
@@ -0,0 +1,1618 @@
1
+ import * as i0 from '@angular/core';
2
+ import { untracked, computed, signal, effect, afterNextRender, AfterRenderPhase, inject, DestroyRef, EventEmitter, TemplateRef, ViewChild, Output, Input, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i2 from '@angular/forms';
6
+ import { FormsModule } from '@angular/forms';
7
+ import { Virtualizer, elementScroll, observeElementOffset, observeElementRect, windowScroll, observeWindowOffset, observeWindowRect } from '@tanstack/virtual-core';
8
+ import Sortable from 'sortablejs';
9
+ import { createAngularTable, getPaginationRowModel, getFilteredRowModel, getSortedRowModel, getExpandedRowModel, getCoreRowModel, FlexRenderDirective } from '@tanstack/angular-table';
10
+
11
+ function proxyVirtualizer(virtualizerSignal, lazyInit) {
12
+ return new Proxy(virtualizerSignal, {
13
+ apply() {
14
+ return virtualizerSignal();
15
+ },
16
+ get(target, property) {
17
+ const untypedTarget = target;
18
+ if (untypedTarget[property]) {
19
+ return untypedTarget[property];
20
+ }
21
+ let virtualizer = untracked(virtualizerSignal);
22
+ if (virtualizer == null) {
23
+ virtualizer = lazyInit();
24
+ untracked(() => virtualizerSignal.set(virtualizer));
25
+ }
26
+ // Create computed signals for each property that represents a reactive value
27
+ if (typeof property === 'string' &&
28
+ [
29
+ 'getTotalSize',
30
+ 'getVirtualItems',
31
+ 'isScrolling',
32
+ 'options',
33
+ 'range',
34
+ 'scrollDirection',
35
+ 'scrollElement',
36
+ 'scrollOffset',
37
+ 'scrollRect',
38
+ 'measureElementCache',
39
+ 'measurementsCache',
40
+ ].includes(property)) {
41
+ const isFunction = typeof virtualizer[property] === 'function';
42
+ Object.defineProperty(untypedTarget, property, {
43
+ value: isFunction
44
+ ? computed(() => target()[property]())
45
+ : computed(() => target()[property]),
46
+ configurable: true,
47
+ enumerable: true,
48
+ });
49
+ }
50
+ // Create plain signals for functions that accept arguments and return reactive values
51
+ if (typeof property === 'string' &&
52
+ [
53
+ 'getOffsetForAlignment',
54
+ 'getOffsetForIndex',
55
+ 'getVirtualItemForOffset',
56
+ 'indexFromElement',
57
+ ].includes(property)) {
58
+ const fn = virtualizer[property];
59
+ Object.defineProperty(untypedTarget, property, {
60
+ value: toComputed(virtualizerSignal, fn),
61
+ configurable: true,
62
+ enumerable: true,
63
+ });
64
+ }
65
+ return untypedTarget[property] || virtualizer[property];
66
+ },
67
+ has(_, property) {
68
+ return !!untracked(virtualizerSignal)[property];
69
+ },
70
+ ownKeys() {
71
+ return Reflect.ownKeys(untracked(virtualizerSignal));
72
+ },
73
+ getOwnPropertyDescriptor() {
74
+ return {
75
+ enumerable: true,
76
+ configurable: true,
77
+ };
78
+ },
79
+ });
80
+ }
81
+ function toComputed(signal, fn) {
82
+ const computedCache = {};
83
+ return (...args) => {
84
+ // Cache computeds by their arguments to avoid re-creating the computed on each call
85
+ const serializedArgs = serializeArgs(...args);
86
+ if (Object.prototype.hasOwnProperty.call(computedCache, serializedArgs)) {
87
+ return computedCache[serializedArgs]?.();
88
+ }
89
+ const computedSignal = computed(() => {
90
+ void signal();
91
+ return fn(...args);
92
+ });
93
+ computedCache[serializedArgs] = computedSignal;
94
+ return computedSignal();
95
+ };
96
+ }
97
+ function serializeArgs(...args) {
98
+ return JSON.stringify(args);
99
+ }
100
+ function createVirtualizerBase(options) {
101
+ let virtualizer;
102
+ function lazyInit() {
103
+ if (!virtualizer) {
104
+ virtualizer = new Virtualizer(options());
105
+ }
106
+ return virtualizer;
107
+ }
108
+ const virtualizerSignal = signal(virtualizer, { equal: () => false });
109
+ // two-way sync options
110
+ effect(() => {
111
+ const _options = options();
112
+ lazyInit();
113
+ virtualizerSignal.set(virtualizer);
114
+ virtualizer.setOptions({
115
+ ..._options,
116
+ onChange: (instance, sync) => {
117
+ // update virtualizerSignal so that dependent computeds recompute.
118
+ virtualizerSignal.set(instance);
119
+ _options.onChange?.(instance, sync);
120
+ },
121
+ });
122
+ // update virtualizerSignal so that dependent computeds recompute.
123
+ virtualizerSignal.set(virtualizer);
124
+ }, { allowSignalWrites: true });
125
+ const scrollElement = computed(() => options().getScrollElement());
126
+ // let the virtualizer know when the scroll element is changed
127
+ effect(() => {
128
+ const el = scrollElement();
129
+ if (el) {
130
+ untracked(virtualizerSignal)._willUpdate();
131
+ }
132
+ }, { allowSignalWrites: true });
133
+ let cleanup;
134
+ // FIX: Use function callback with options for afterNextRender
135
+ afterNextRender(() => (virtualizer ?? lazyInit())._didMount(), { phase: AfterRenderPhase.Read });
136
+ inject(DestroyRef).onDestroy(() => cleanup?.());
137
+ return proxyVirtualizer(virtualizerSignal, lazyInit);
138
+ }
139
+ function injectVirtualizer(options) {
140
+ const resolvedOptions = computed(() => {
141
+ const opts = options();
142
+ return {
143
+ observeElementRect: observeElementRect,
144
+ observeElementOffset: observeElementOffset,
145
+ scrollToFn: elementScroll,
146
+ getScrollElement: () => {
147
+ const elementOrRef = opts.scrollElement;
148
+ return ((isElementRef(elementOrRef)
149
+ ? elementOrRef.nativeElement
150
+ : elementOrRef) ?? null);
151
+ },
152
+ ...opts,
153
+ };
154
+ });
155
+ return createVirtualizerBase(resolvedOptions);
156
+ }
157
+ function isElementRef(elementOrRef) {
158
+ return elementOrRef != null && 'nativeElement' in elementOrRef;
159
+ }
160
+ function injectWindowVirtualizer(options) {
161
+ const resolvedOptions = computed(() => {
162
+ return {
163
+ getScrollElement: () => (typeof document !== 'undefined' ? window : null),
164
+ observeElementRect: observeWindowRect,
165
+ observeElementOffset: observeWindowOffset,
166
+ scrollToFn: windowScroll,
167
+ initialOffset: () => (typeof document !== 'undefined' ? window.scrollY : 0),
168
+ ...options(),
169
+ };
170
+ });
171
+ return createVirtualizerBase(resolvedOptions);
172
+ }
173
+
174
+ /**
175
+ * Data table component with sorting, filtering, pagination, and selection.
176
+ * Wraps TanStack Table for Angular.
177
+ */
178
+ class TanGrid {
179
+ set virtualScrollViewport(el) {
180
+ this.scrollElementSignal.set(el?.nativeElement || null);
181
+ }
182
+ set headerRowContainer(el) {
183
+ this._headerRowContainer = el;
184
+ this._initSortable();
185
+ }
186
+ get headerRowContainer() {
187
+ return this._headerRowContainer;
188
+ }
189
+ constructor(ngZone, cdr) {
190
+ this.ngZone = ngZone;
191
+ this.cdr = cdr;
192
+ /**
193
+ * Table data array
194
+ */
195
+ this.data = [];
196
+ /**
197
+ * Column definitions
198
+ */
199
+ this.columns = [];
200
+ /**
201
+ * Whether table is in loading state
202
+ * @default false
203
+ */
204
+ this.loading = false;
205
+ /**
206
+ * Whether table is striped
207
+ * @default false
208
+ */
209
+ this.striped = false;
210
+ /**
211
+ * Whether table has borders
212
+ * @default true
213
+ */
214
+ this.bordered = true;
215
+ /**
216
+ * Whether table is hoverable
217
+ * @default true
218
+ */
219
+ this.hoverable = true;
220
+ /**
221
+ * Whether table is compact
222
+ * @default true
223
+ */
224
+ this.compact = true;
225
+ /**
226
+ * Table theme
227
+ * @default 'default'
228
+ */
229
+ this.theme = 'default';
230
+ /**
231
+ * Selection mode
232
+ * @default 'none'
233
+ */
234
+ this.selectionMode = 'none';
235
+ /**
236
+ * Selected row IDs
237
+ */
238
+ this.selectedRowIds = [];
239
+ /**
240
+ * Row ID accessor function
241
+ */
242
+ this.rowIdFn = (row, index) => String(index);
243
+ /**
244
+ * Pagination enabled
245
+ * @default true
246
+ */
247
+ this.pagination = true;
248
+ /**
249
+ * Initial page size
250
+ * @default 10
251
+ */
252
+ this.pageSize = 10;
253
+ /**
254
+ * Page size options
255
+ * @default [10, 20, 50, 100]
256
+ */
257
+ this.pageSizeOptions = [10, 20, 50, 100];
258
+ /**
259
+ * Pagination mode: 'client' | 'server'
260
+ * - 'client': Pagination is handled by TanStack Table (default)
261
+ * - 'server': Pagination is handled by the server, emits paginationChange event
262
+ * @default 'client'
263
+ */
264
+ this.paginationMode = 'client';
265
+ /**
266
+ * Total items (required for server-side pagination)
267
+ * @default 0
268
+ */
269
+ this.totalItems = 0;
270
+ /**
271
+ * Sorting enabled
272
+ * @default true
273
+ */
274
+ this.sorting = true;
275
+ /**
276
+ * Sorting mode: 'client' | 'server'
277
+ * - 'client': Sorting is handled by TanStack Table (default)
278
+ * - 'server': Sorting is handled by the server, emits sortChange event
279
+ * @default 'client'
280
+ */
281
+ this.sortingMode = 'client';
282
+ /**
283
+ * Filtering enabled
284
+ * @default true
285
+ */
286
+ this.filtering = true;
287
+ /**
288
+ * Filtering mode: 'client' | 'server'
289
+ * - 'client': Filtering is handled by TanStack Table (default)
290
+ * - 'server': Filtering is handled by the server, emits filterChange event
291
+ * @default 'client'
292
+ */
293
+ this.filteringMode = 'client';
294
+ /**
295
+ * Global search enabled
296
+ * @default false
297
+ */
298
+ this.globalSearch = false;
299
+ /**
300
+ * Show global search input
301
+ * @default true
302
+ */
303
+ this.showGlobalSearchInput = true;
304
+ /**
305
+ * Global filter value
306
+ */
307
+ this.globalFilter = '';
308
+ /**
309
+ * Empty state message
310
+ * @default 'No data available'
311
+ */
312
+ this.emptyMessage = 'No data available';
313
+ /**
314
+ * Loading message
315
+ * @default 'Loading...'
316
+ */
317
+ this.loadingMessage = 'Loading...';
318
+ /**
319
+ * Loading type: 'spinner' | 'placeholder' | 'template'
320
+ * @default 'spinner'
321
+ */
322
+ this.loadingType = 'spinner';
323
+ /**
324
+ * Number of placeholder rows to show when loadingType is 'placeholder'
325
+ * @default 5
326
+ */
327
+ this.placeholderRows = 5;
328
+ /**
329
+ * Enable virtual scrolling for large datasets
330
+ * Requires @angular/cdk to be installed
331
+ * @default false
332
+ */
333
+ this.virtualScroll = false;
334
+ /**
335
+ * Height of each row in pixels when virtual scrolling is enabled
336
+ * @default 48
337
+ */
338
+ this.rowHeight = 48;
339
+ /**
340
+ * Number of buffer rows to render outside the viewport
341
+ * Higher values provide smoother scrolling but use more memory
342
+ * @default 3
343
+ */
344
+ this.virtualScrollBufferSize = 3;
345
+ /**
346
+ * Column IDs to pin to the left side
347
+ * These columns will remain visible when scrolling horizontally
348
+ */
349
+ this.pinnedLeft = [];
350
+ /**
351
+ * Column IDs to pin to the right side
352
+ * These columns will remain visible when scrolling horizontally
353
+ */
354
+ this.pinnedRight = [];
355
+ /**
356
+ * Whether to show column pinning UI (icons in headers)
357
+ * @default false
358
+ */
359
+ this.enablePinning = false;
360
+ /**
361
+ * Enable column reordering via drag-and-drop on headers
362
+ * @default false
363
+ */
364
+ this.reorderable = false;
365
+ /**
366
+ * Whether rows can be expanded
367
+ * @default false
368
+ */
369
+ this.expandable = false;
370
+ /**
371
+ * Pre-expanded row IDs
372
+ */
373
+ this.expandedRowIds = [];
374
+ /**
375
+ * Whether inline editing is enabled
376
+ * @default false
377
+ */
378
+ this.editable = false;
379
+ /**
380
+ * Whether export functionality is enabled
381
+ * @default false
382
+ */
383
+ this.exportable = false;
384
+ /**
385
+ * Available export formats
386
+ * @default ['csv']
387
+ */
388
+ this.exportFormats = ['csv'];
389
+ /**
390
+ * Event emitted when row is clicked
391
+ */
392
+ this.rowClick = new EventEmitter();
393
+ this.rowDblClick = new EventEmitter();
394
+ /**
395
+ * Event emitted when selection changes
396
+ */
397
+ this.selectionChange = new EventEmitter();
398
+ /**
399
+ * Event emitted when sorting changes
400
+ */
401
+ this.sortChange = new EventEmitter();
402
+ /**
403
+ * Event emitted when pagination changes
404
+ */
405
+ this.paginationChange = new EventEmitter();
406
+ /**
407
+ * Event emitted when filter changes
408
+ */
409
+ this.filterChange = new EventEmitter();
410
+ /**
411
+ * Event emitted when row expansion changes
412
+ */
413
+ this.expansionChange = new EventEmitter();
414
+ /**
415
+ * Event emitted when column pinning changes
416
+ */
417
+ this.pinChange = new EventEmitter();
418
+ /**
419
+ * Event emitted when a cell is edited
420
+ */
421
+ this.cellEditChange = new EventEmitter();
422
+ /**
423
+ * Event emitted when column order changes
424
+ */
425
+ this.columnOrderChange = new EventEmitter();
426
+ // Internal state
427
+ this.dataSignal = signal([]);
428
+ this.columnsSignal = signal([]);
429
+ this.globalFilterSignal = signal('');
430
+ this.sortingSignal = signal([]);
431
+ this.rowSelectionSignal = signal({});
432
+ this.columnSizingSignal = signal({});
433
+ this.rowExpansionSignal = signal({});
434
+ this.editingCellSignal = signal(null);
435
+ this.editingValueSignal = signal(null);
436
+ this.columnOrderSignal = signal([]);
437
+ this.hasHorizontalScroll = signal(false);
438
+ this.pageIndex = signal(0);
439
+ this.pageSizeSignal = signal(10);
440
+ this.resizeAnimationFrame = null;
441
+ this.resizeObserver = null;
442
+ this.scrollElementSignal = signal(null);
443
+ this._sortable = null;
444
+ /**
445
+ * Virtualizer for row rendering
446
+ */
447
+ this.virtualizer = injectVirtualizer(() => ({
448
+ count: this.table ? this.table.getRowModel().rows.length : 0,
449
+ scrollElement: this.scrollElementSignal() || undefined,
450
+ estimateSize: () => this.rowHeight,
451
+ overscan: this.virtualScrollBufferSize,
452
+ }));
453
+ }
454
+ ngOnInit() {
455
+ this.dataSignal.set(this.data);
456
+ this.pageSizeSignal.set(this.pageSize);
457
+ // Initialize expanded rows from input
458
+ if (this.expandedRowIds.length > 0) {
459
+ const expansionState = {};
460
+ this.expandedRowIds.forEach((id) => {
461
+ expansionState[id] = true;
462
+ });
463
+ this.rowExpansionSignal.set(expansionState);
464
+ }
465
+ this._convertColumns();
466
+ this._createTable();
467
+ }
468
+ ngOnChanges(changes) {
469
+ if (changes['data'] && !changes['data'].firstChange) {
470
+ this.dataSignal.set(this.data);
471
+ }
472
+ if (changes['columns'] && !changes['columns'].firstChange) {
473
+ this._convertColumns();
474
+ this._createTable();
475
+ }
476
+ if (changes['pageSize'] && !changes['pageSize'].firstChange) {
477
+ this.pageSizeSignal.set(this.pageSize);
478
+ }
479
+ if (changes['expandedRowIds'] && !changes['expandedRowIds'].firstChange) {
480
+ const expansionState = {};
481
+ this.expandedRowIds.forEach((id) => {
482
+ expansionState[id] = true;
483
+ });
484
+ this.rowExpansionSignal.set(expansionState);
485
+ if (this.table) {
486
+ this._createTable();
487
+ }
488
+ }
489
+ if (changes['globalFilter']) {
490
+ this.onGlobalSearch(this.globalFilter);
491
+ }
492
+ if (changes['reorderable']) {
493
+ this._initSortable();
494
+ }
495
+ }
496
+ /**
497
+ * Get selected rows
498
+ */
499
+ getSelectedRows() {
500
+ const selectedIds = Object.keys(this.rowSelectionSignal()).filter((id) => this.rowSelectionSignal()[id] === true);
501
+ return this.data.filter((row, index) => selectedIds.includes(this.rowIdFn(row, index)));
502
+ }
503
+ /**
504
+ * Clear selection
505
+ */
506
+ clearSelection() {
507
+ this.rowSelectionSignal.set({});
508
+ this._emitSelectionChange();
509
+ }
510
+ /**
511
+ * Select all rows
512
+ */
513
+ selectAll() {
514
+ if (this.selectionMode !== 'multiple') {
515
+ return;
516
+ }
517
+ const selection = {};
518
+ this.data.forEach((row, index) => {
519
+ selection[this.rowIdFn(row, index)] = true;
520
+ });
521
+ this.rowSelectionSignal.set(selection);
522
+ this._emitSelectionChange();
523
+ }
524
+ /**
525
+ * Toggle row selection
526
+ */
527
+ toggleRowSelection(rowId) {
528
+ const current = this.rowSelectionSignal();
529
+ const newSelection = { ...current };
530
+ if (this.selectionMode === 'single') {
531
+ // Single selection - clear others
532
+ Object.keys(newSelection).forEach((id) => {
533
+ newSelection[id] = false;
534
+ });
535
+ newSelection[rowId] = !newSelection[rowId];
536
+ }
537
+ else if (this.selectionMode === 'multiple') {
538
+ newSelection[rowId] = !newSelection[rowId];
539
+ }
540
+ this.rowSelectionSignal.set(newSelection);
541
+ this._emitSelectionChange();
542
+ }
543
+ /**
544
+ * Check if row is selected
545
+ */
546
+ isRowSelected(rowId) {
547
+ return this.rowSelectionSignal()[rowId] === true;
548
+ }
549
+ /**
550
+ * Handle global search
551
+ */
552
+ onGlobalSearch(value) {
553
+ this.globalFilterSignal.set(value);
554
+ if (this.table) {
555
+ this.table.setGlobalFilter(value);
556
+ }
557
+ }
558
+ /**
559
+ * Handle column filter change
560
+ */
561
+ onColumnFilterChange(column, value) {
562
+ column.setFilterValue(value || undefined);
563
+ this._emitFilterChange();
564
+ }
565
+ /**
566
+ * Get filter placeholder text
567
+ */
568
+ getFilterPlaceholder(column) {
569
+ const header = column.columnDef.header;
570
+ if (typeof header === 'string') {
571
+ return `Filter ${header}`;
572
+ }
573
+ return 'Filter';
574
+ }
575
+ /**
576
+ * Handle resize start
577
+ */
578
+ onResizeStart(event, header) {
579
+ event.preventDefault();
580
+ event.stopPropagation();
581
+ const resizeHandler = header.getResizeHandler();
582
+ if (resizeHandler && typeof resizeHandler === 'function') {
583
+ // Run resize handler outside Angular zone for better performance
584
+ this.ngZone.runOutsideAngular(() => {
585
+ resizeHandler(event);
586
+ });
587
+ }
588
+ }
589
+ /**
590
+ * Track by function for virtual scrolling
591
+ */
592
+ trackByRowId(index, row) {
593
+ return row.id;
594
+ }
595
+ /**
596
+ * Get row index for virtual scrolling
597
+ */
598
+ getRowIndex(row) {
599
+ return this.table.getRowModel().rows.findIndex((r) => r.id === row.id);
600
+ }
601
+ /**
602
+ * Check if a column is pinned to the left
603
+ */
604
+ isPinnedLeft(columnId) {
605
+ return this.pinnedLeft.includes(columnId);
606
+ }
607
+ /**
608
+ * Check if a column is pinned to the right
609
+ */
610
+ isPinnedRight(columnId) {
611
+ return this.pinnedRight.includes(columnId);
612
+ }
613
+ /**
614
+ * Check if a column is in the left half of the visible columns
615
+ */
616
+ isColumnOnLeft(columnId) {
617
+ if (!this.table)
618
+ return true;
619
+ const allColumns = this.table.getVisibleLeafColumns();
620
+ const index = allColumns.findIndex((c) => c.id === columnId);
621
+ if (index === -1)
622
+ return true;
623
+ return index < allColumns.length / 2;
624
+ }
625
+ /**
626
+ * Get the left offset for a pinned column
627
+ */
628
+ getPinnedLeftOffset(columnId) {
629
+ if (!this.isPinnedLeft(columnId))
630
+ return 0;
631
+ let offset = 0;
632
+ if (this.selectionMode !== 'none') {
633
+ offset += 40; // Selection column width
634
+ }
635
+ const pinnedIndex = this.pinnedLeft.indexOf(columnId);
636
+ for (let i = 0; i < pinnedIndex; i++) {
637
+ const col = this.table.getColumn(this.pinnedLeft[i]);
638
+ if (col) {
639
+ offset += col.getSize();
640
+ }
641
+ }
642
+ return offset;
643
+ }
644
+ /**
645
+ * Get the right offset for a pinned column
646
+ */
647
+ getPinnedRightOffset(columnId) {
648
+ if (!this.isPinnedRight(columnId))
649
+ return 0;
650
+ let offset = 0;
651
+ const pinnedIndex = this.pinnedRight.indexOf(columnId);
652
+ const totalPinnedRight = this.pinnedRight.length;
653
+ // Calculate offset from right
654
+ for (let i = pinnedIndex + 1; i < totalPinnedRight; i++) {
655
+ const col = this.table.getColumn(this.pinnedRight[i]);
656
+ if (col) {
657
+ offset += col.getSize();
658
+ }
659
+ }
660
+ return offset;
661
+ }
662
+ /**
663
+ * Check if a row is expanded
664
+ */
665
+ isRowExpanded(rowId) {
666
+ return this.rowExpansionSignal()[rowId] === true;
667
+ }
668
+ /**
669
+ * Toggle row expansion
670
+ */
671
+ toggleRowExpansion(row) {
672
+ const rowId = row.id;
673
+ const currentState = this.rowExpansionSignal();
674
+ const newState = { ...currentState };
675
+ const isExpanded = currentState[rowId] === true;
676
+ if (isExpanded) {
677
+ delete newState[rowId];
678
+ }
679
+ else {
680
+ newState[rowId] = true;
681
+ }
682
+ this.rowExpansionSignal.set(newState);
683
+ this.expansionChange.emit({ rowId, expanded: !isExpanded });
684
+ // Recreate table to reflect expansion state
685
+ if (this.table) {
686
+ this._createTable();
687
+ }
688
+ }
689
+ /**
690
+ * Check if a cell is currently being edited
691
+ */
692
+ isEditing(rowId, columnId) {
693
+ const editing = this.editingCellSignal();
694
+ return editing !== null && editing.rowId === rowId && editing.columnId === columnId;
695
+ }
696
+ /**
697
+ * Start editing a cell
698
+ */
699
+ startEdit(row, columnId) {
700
+ if (!this.editable)
701
+ return;
702
+ const rowId = row.id;
703
+ const column = this.table.getColumn(columnId);
704
+ if (!column)
705
+ return;
706
+ // Get current cell value
707
+ const cell = row.getVisibleCells().find((c) => c.column.id === columnId);
708
+ if (!cell)
709
+ return;
710
+ // Get the original value from the row data
711
+ const originalValue = this._getCellValue(row.original, columnId);
712
+ this.editingCellSignal.set({ rowId, columnId });
713
+ this.editingValueSignal.set(originalValue);
714
+ }
715
+ /**
716
+ * Save the edited cell value
717
+ */
718
+ saveEdit(row, columnId) {
719
+ const editing = this.editingCellSignal();
720
+ if (!editing || editing.rowId !== row.id || editing.columnId !== columnId) {
721
+ return;
722
+ }
723
+ const newValue = this.editingValueSignal();
724
+ const oldValue = this._getCellValue(row.original, columnId);
725
+ // Validate if validator exists
726
+ const column = this.columns.find((col) => (col.id || String(col.accessorKey)) === columnId);
727
+ if (column?.editValidator) {
728
+ const validationResult = column.editValidator(newValue, row.original);
729
+ if (validationResult !== true) {
730
+ // Validation failed - don't save
731
+ this.cancelEdit();
732
+ return;
733
+ }
734
+ }
735
+ // Update the data
736
+ this._setCellValue(row.original, columnId, newValue);
737
+ // Emit edit change event
738
+ const rowIndex = this.table.getRowModel().rows.findIndex((r) => r.id === row.id);
739
+ this.cellEditChange.emit({
740
+ row: row.original,
741
+ rowIndex,
742
+ columnId,
743
+ oldValue,
744
+ newValue,
745
+ });
746
+ // Clear editing state
747
+ this.editingCellSignal.set(null);
748
+ this.editingValueSignal.set(null);
749
+ // Update data signal to trigger change detection
750
+ this.dataSignal.set([...this.dataSignal()]);
751
+ }
752
+ /**
753
+ * Cancel editing a cell
754
+ */
755
+ cancelEdit() {
756
+ this.editingCellSignal.set(null);
757
+ this.editingValueSignal.set(null);
758
+ }
759
+ /**
760
+ * Get cell value for editing
761
+ */
762
+ getEditingValue() {
763
+ return this.editingValueSignal();
764
+ }
765
+ /**
766
+ * Update editing value
767
+ */
768
+ updateEditingValue(value) {
769
+ this.editingValueSignal.set(value);
770
+ }
771
+ /**
772
+ * Get cell value from row data
773
+ */
774
+ _getCellValue(row, columnId) {
775
+ const column = this.columns.find((col) => (col.id || String(col.accessorKey)) === columnId);
776
+ if (!column)
777
+ return undefined;
778
+ if (column.accessorFn) {
779
+ return column.accessorFn(row);
780
+ }
781
+ else if (column.accessorKey) {
782
+ return row[column.accessorKey];
783
+ }
784
+ return undefined;
785
+ }
786
+ /**
787
+ * Set cell value in row data
788
+ */
789
+ _setCellValue(row, columnId, value) {
790
+ const column = this.columns.find((col) => (col.id || String(col.accessorKey)) === columnId);
791
+ if (!column || !column.accessorKey)
792
+ return;
793
+ row[column.accessorKey] = value;
794
+ }
795
+ /**
796
+ * Check if a column is editable
797
+ */
798
+ getColumnEditable(columnId) {
799
+ const column = this.columns.find((col) => (col.id || String(col.accessorKey)) === columnId);
800
+ return column?.editable === true;
801
+ }
802
+ /**
803
+ * Get input type for editable column
804
+ */
805
+ getColumnEditType(columnId) {
806
+ const column = this.columns.find((col) => (col.id || String(col.accessorKey)) === columnId);
807
+ return column?.editType || 'text';
808
+ }
809
+ /**
810
+ * Export to CSV (Public API)
811
+ * Allows triggering CSV export programmatically or from custom UI
812
+ */
813
+ exportToCsv(options) {
814
+ const exportOptions = {
815
+ format: 'csv',
816
+ filename: 'table-export',
817
+ selectedOnly: false,
818
+ currentPageOnly: false,
819
+ includeHeaders: true,
820
+ ...options
821
+ };
822
+ this.exportData(exportOptions);
823
+ }
824
+ /**
825
+ * Export to Excel (Public API)
826
+ * Allows triggering Excel export programmatically or from custom UI
827
+ */
828
+ exportToExcel(options) {
829
+ const exportOptions = {
830
+ format: 'excel',
831
+ filename: 'table-export',
832
+ selectedOnly: false,
833
+ currentPageOnly: false,
834
+ includeHeaders: true,
835
+ ...options
836
+ };
837
+ this.exportData(exportOptions);
838
+ }
839
+ /**
840
+ * Export to PDF (Public API)
841
+ * Allows triggering PDF export programmatically or from custom UI
842
+ */
843
+ exportToPdf(options) {
844
+ const exportOptions = {
845
+ format: 'pdf',
846
+ filename: 'table-export',
847
+ selectedOnly: false,
848
+ currentPageOnly: false,
849
+ includeHeaders: true,
850
+ ...options
851
+ };
852
+ this.exportData(exportOptions);
853
+ }
854
+ /**
855
+ * Export table data
856
+ */
857
+ exportData(options) {
858
+ if (!this.table)
859
+ return;
860
+ let dataToExport = [];
861
+ const visibleColumns = this.columns.filter((col) => col.visible !== false);
862
+ // Determine which rows to export
863
+ if (options.selectedOnly && this.selectionMode !== 'none') {
864
+ const selectedRows = this.getSelectedRows();
865
+ dataToExport = selectedRows;
866
+ }
867
+ else if (options.currentPageOnly && this.pagination) {
868
+ dataToExport = this.table.getRowModel().rows.map((row) => row.original);
869
+ }
870
+ else {
871
+ dataToExport = this.dataSignal();
872
+ }
873
+ // Export based on format
874
+ switch (options.format) {
875
+ case 'csv':
876
+ this._exportToCSV(dataToExport, visibleColumns, options);
877
+ break;
878
+ case 'excel':
879
+ this._exportToExcel(dataToExport, visibleColumns, options);
880
+ break;
881
+ case 'pdf':
882
+ this._exportToPDF(dataToExport, visibleColumns, options);
883
+ break;
884
+ }
885
+ }
886
+ /**
887
+ * Toggle pin state for a column
888
+ */
889
+ togglePin(column) {
890
+ const columnId = column.id;
891
+ if (this.isPinnedLeft(columnId) || this.isPinnedRight(columnId)) {
892
+ this.onPinChange(columnId, 'none');
893
+ }
894
+ else {
895
+ // Determine which side to pin based on column position
896
+ if (this.isColumnOnLeft(columnId)) {
897
+ if (this.canPinLeft(columnId)) {
898
+ this.onPinChange(columnId, 'left');
899
+ }
900
+ }
901
+ else {
902
+ if (this.canPinRight(columnId)) {
903
+ this.onPinChange(columnId, 'right');
904
+ }
905
+ }
906
+ }
907
+ }
908
+ /**
909
+ * Export to CSV
910
+ */
911
+ _exportToCSV(data, columns, options) {
912
+ const rows = [];
913
+ // Add headers if requested
914
+ if (options.includeHeaders !== false) {
915
+ const headers = columns.map((col) => {
916
+ const header = typeof col.header === 'string' ? col.header : col.id || String(col.accessorKey) || '';
917
+ return this._escapeCSV(header);
918
+ });
919
+ rows.push(headers.join(','));
920
+ }
921
+ // Add data rows
922
+ data.forEach((row) => {
923
+ const values = columns.map((col) => {
924
+ let value;
925
+ if (col.accessorFn) {
926
+ value = col.accessorFn(row);
927
+ }
928
+ else if (col.accessorKey) {
929
+ value = row[col.accessorKey];
930
+ }
931
+ // Convert value to string and escape
932
+ return this._escapeCSV(value != null ? String(value) : '');
933
+ });
934
+ rows.push(values.join(','));
935
+ });
936
+ // Create CSV content
937
+ const csvContent = rows.join('\n');
938
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
939
+ this._downloadFile(blob, options.filename || 'table-export', 'csv');
940
+ }
941
+ /**
942
+ * Export to Excel (XLSX)
943
+ * Note: Requires xlsx library to be installed
944
+ */
945
+ _exportToExcel(data, columns, options) {
946
+ // Use a dynamic import that Vite won't try to resolve at build time
947
+ // We use Function constructor to make the import path truly dynamic
948
+ const importXlsx = new Function('return import("xlsx")');
949
+ importXlsx()
950
+ .then((XLSX) => {
951
+ // Prepare data for Excel export
952
+ const worksheetData = [];
953
+ // Add headers if requested
954
+ if (options.includeHeaders !== false) {
955
+ const headers = {};
956
+ columns.forEach((col) => {
957
+ const header = typeof col.header === 'string' ? col.header : col.id || String(col.accessorKey) || '';
958
+ const key = col.id || String(col.accessorKey) || '';
959
+ headers[key] = header;
960
+ });
961
+ worksheetData.push(headers);
962
+ }
963
+ // Add data rows
964
+ data.forEach((row) => {
965
+ const rowData = {};
966
+ columns.forEach((col) => {
967
+ let value;
968
+ if (col.accessorFn) {
969
+ value = col.accessorFn(row);
970
+ }
971
+ else if (col.accessorKey) {
972
+ value = row[col.accessorKey];
973
+ }
974
+ const key = col.id || String(col.accessorKey) || '';
975
+ rowData[key] = value != null ? value : '';
976
+ });
977
+ worksheetData.push(rowData);
978
+ });
979
+ // Create worksheet from JSON data
980
+ const worksheet = XLSX.utils.json_to_sheet(worksheetData, { skipHeader: options.includeHeaders === false });
981
+ const workbook = XLSX.utils.book_new();
982
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
983
+ // Generate Excel file
984
+ const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
985
+ const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
986
+ this._downloadFile(blob, options.filename || 'table-export', 'xlsx');
987
+ })
988
+ .catch(() => {
989
+ console.warn('xlsx library is not installed. Please install it: npm install xlsx');
990
+ // Fallback to CSV
991
+ this._exportToCSV(data, columns, { ...options, format: 'csv' });
992
+ });
993
+ }
994
+ /**
995
+ * Export to PDF
996
+ * Note: Requires jspdf library to be installed
997
+ */
998
+ _exportToPDF(data, columns, options) {
999
+ // Use dynamic imports that Vite won't try to resolve at build time
1000
+ // We use Function constructor to make the import paths truly dynamic
1001
+ const importJspdf = new Function('return import("jspdf")');
1002
+ const importAutotable = new Function('return import("jspdf-autotable")');
1003
+ Promise.all([importJspdf(), importAutotable()])
1004
+ .then(([jsPDF, autoTable]) => {
1005
+ const doc = new jsPDF.default();
1006
+ // Prepare table data
1007
+ const tableData = [];
1008
+ // Add headers if requested
1009
+ if (options.includeHeaders !== false) {
1010
+ const headers = columns.map((col) => {
1011
+ return typeof col.header === 'string' ? col.header : col.id || String(col.accessorKey) || '';
1012
+ });
1013
+ tableData.push(headers);
1014
+ }
1015
+ // Add data rows
1016
+ data.forEach((row) => {
1017
+ const rowData = columns.map((col) => {
1018
+ let value;
1019
+ if (col.accessorFn) {
1020
+ value = col.accessorFn(row);
1021
+ }
1022
+ else if (col.accessorKey) {
1023
+ value = row[col.accessorKey];
1024
+ }
1025
+ return value != null ? String(value) : '';
1026
+ });
1027
+ tableData.push(rowData);
1028
+ });
1029
+ // Generate PDF
1030
+ doc.autoTable({
1031
+ head: options.includeHeaders !== false ? [tableData[0]] : undefined,
1032
+ body: options.includeHeaders !== false ? tableData.slice(1) : tableData,
1033
+ });
1034
+ doc.save(`${options.filename || 'table-export'}.pdf`);
1035
+ })
1036
+ .catch(() => {
1037
+ console.warn('jspdf or jspdf-autotable library is not installed. Please install it: npm install jspdf jspdf-autotable');
1038
+ // Fallback to CSV
1039
+ this._exportToCSV(data, columns, { ...options, format: 'csv' });
1040
+ });
1041
+ }
1042
+ /**
1043
+ * Escape CSV value
1044
+ */
1045
+ _escapeCSV(value) {
1046
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
1047
+ return `"${value.replace(/"/g, '""')}"`;
1048
+ }
1049
+ return value;
1050
+ }
1051
+ /**
1052
+ * Download file
1053
+ */
1054
+ _downloadFile(blob, filename, extension) {
1055
+ const url = window.URL.createObjectURL(blob);
1056
+ const link = document.createElement('a');
1057
+ link.href = url;
1058
+ link.download = `${filename}.${extension}`;
1059
+ document.body.appendChild(link);
1060
+ link.click();
1061
+ document.body.removeChild(link);
1062
+ window.URL.revokeObjectURL(url);
1063
+ }
1064
+ /**
1065
+ * Emit filter change event
1066
+ */
1067
+ _emitFilterChange() {
1068
+ if (!this.table)
1069
+ return;
1070
+ const filters = [];
1071
+ this.table.getAllColumns().forEach((col) => {
1072
+ const filterValue = col.getFilterValue();
1073
+ if (filterValue !== undefined && filterValue !== null && filterValue !== '') {
1074
+ filters.push({
1075
+ columnId: col.id,
1076
+ value: filterValue,
1077
+ });
1078
+ }
1079
+ });
1080
+ this.filterChange.emit(filters);
1081
+ }
1082
+ /**
1083
+ * Check if value is a TemplateRef
1084
+ */
1085
+ isTemplateRef(value) {
1086
+ return value instanceof TemplateRef;
1087
+ }
1088
+ /**
1089
+ * Handle page change
1090
+ */
1091
+ onPageChange(page) {
1092
+ this.pageIndex.set(page - 1); // TanStack uses 0-indexed
1093
+ this.table.setPageIndex(page - 1);
1094
+ this._emitPaginationChange();
1095
+ }
1096
+ /**
1097
+ * Handle page size change
1098
+ */
1099
+ onPageSizeChange(size) {
1100
+ if (!this.table) {
1101
+ return;
1102
+ }
1103
+ const newPageSize = size;
1104
+ // Always reset to the first page when page size changes
1105
+ const newPageIndex = 0;
1106
+ // Update signals first
1107
+ this.pageSizeSignal.set(newPageSize);
1108
+ this.pageIndex.set(newPageIndex);
1109
+ // Ensure view is updated
1110
+ this.cdr.markForCheck();
1111
+ // Emit pagination change manually since we're handling it here
1112
+ // Use setTimeout to ensure the table state has been fully updated
1113
+ setTimeout(() => {
1114
+ this._emitPaginationChange();
1115
+ }, 0);
1116
+ }
1117
+ /**
1118
+ * Handle row click
1119
+ */
1120
+ onRowClick(row, index) {
1121
+ this.rowClick.emit({ row: row.original, index });
1122
+ }
1123
+ /**
1124
+ * Handle row double click
1125
+ */
1126
+ onRowDblClick(row, index) {
1127
+ this.rowDblClick.emit({ row: row.original, index });
1128
+ }
1129
+ onPinChange(columnId, position) {
1130
+ this.pinChange.emit({ columnId, position });
1131
+ }
1132
+ ngAfterViewInit() {
1133
+ if (this.tableContainer?.nativeElement) {
1134
+ this.resizeObserver = new ResizeObserver(() => {
1135
+ this.ngZone.run(() => {
1136
+ this.checkScroll();
1137
+ });
1138
+ });
1139
+ this.resizeObserver.observe(this.tableContainer.nativeElement);
1140
+ this.checkScroll();
1141
+ }
1142
+ }
1143
+ ngOnDestroy() {
1144
+ this.resizeObserver?.disconnect();
1145
+ }
1146
+ checkScroll() {
1147
+ if (this.tableContainer?.nativeElement) {
1148
+ const el = this.tableContainer.nativeElement;
1149
+ this.hasHorizontalScroll.set(el.scrollWidth > el.clientWidth);
1150
+ this.cdr.markForCheck();
1151
+ }
1152
+ }
1153
+ canPinLeft(columnId) {
1154
+ const def = this.columns.find(c => (c.id || c.accessorKey) === columnId);
1155
+ if (!def)
1156
+ return true;
1157
+ if (def.pinnable === false)
1158
+ return false;
1159
+ if (def.pinnable === 'right')
1160
+ return false;
1161
+ return true;
1162
+ }
1163
+ canPinRight(columnId) {
1164
+ const def = this.columns.find(c => (c.id || c.accessorKey) === columnId);
1165
+ if (!def)
1166
+ return true;
1167
+ if (def.pinnable === false)
1168
+ return false;
1169
+ if (def.pinnable === 'left')
1170
+ return false;
1171
+ return true;
1172
+ }
1173
+ /**
1174
+ * Get total pages
1175
+ */
1176
+ getTotalPages() {
1177
+ if (!this.table) {
1178
+ return 1;
1179
+ }
1180
+ return this.table.getPageCount();
1181
+ }
1182
+ /**
1183
+ * Get current page (1-indexed)
1184
+ */
1185
+ getCurrentPage() {
1186
+ if (!this.table) {
1187
+ return 1;
1188
+ }
1189
+ return this.table.getState().pagination.pageIndex + 1;
1190
+ }
1191
+ /**
1192
+ * Get total items
1193
+ */
1194
+ getTotalItems() {
1195
+ if (this.paginationMode === 'server') {
1196
+ return this.totalItems;
1197
+ }
1198
+ if (!this.table) {
1199
+ return 0;
1200
+ }
1201
+ return this.table.getFilteredRowModel().rows.length;
1202
+ }
1203
+ /**
1204
+ * Get displayed items range
1205
+ */
1206
+ getDisplayedRange() {
1207
+ const pageIndex = this.table.getState().pagination.pageIndex;
1208
+ const pageSize = this.table.getState().pagination.pageSize;
1209
+ const start = pageIndex * pageSize + 1;
1210
+ const end = Math.min(start + pageSize - 1, this.getTotalItems());
1211
+ return { start, end };
1212
+ }
1213
+ /**
1214
+ * Get pagination page numbers to display
1215
+ */
1216
+ getPaginationPages() {
1217
+ const currentPage = this.getCurrentPage();
1218
+ const totalPages = this.getTotalPages();
1219
+ const pages = [];
1220
+ if (totalPages <= 7) {
1221
+ // Show all pages if 7 or fewer
1222
+ for (let i = 1; i <= totalPages; i++) {
1223
+ pages.push(i);
1224
+ }
1225
+ }
1226
+ else {
1227
+ // Always show first page
1228
+ pages.push(1);
1229
+ if (currentPage <= 4) {
1230
+ // Near the start
1231
+ for (let i = 2; i <= 5; i++) {
1232
+ pages.push(i);
1233
+ }
1234
+ pages.push('ellipsis');
1235
+ pages.push(totalPages);
1236
+ }
1237
+ else if (currentPage >= totalPages - 3) {
1238
+ // Near the end
1239
+ pages.push('ellipsis');
1240
+ for (let i = totalPages - 4; i <= totalPages; i++) {
1241
+ pages.push(i);
1242
+ }
1243
+ }
1244
+ else {
1245
+ // In the middle
1246
+ pages.push('ellipsis');
1247
+ for (let i = currentPage - 1; i <= currentPage + 1; i++) {
1248
+ pages.push(i);
1249
+ }
1250
+ pages.push('ellipsis');
1251
+ pages.push(totalPages);
1252
+ }
1253
+ }
1254
+ return pages;
1255
+ }
1256
+ /**
1257
+ * Go to specific page
1258
+ */
1259
+ goToPage(page) {
1260
+ if (page >= 1 && page <= this.getTotalPages()) {
1261
+ this.onPageChange(page);
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Get placeholder width for skeleton loader (Facebook-style)
1266
+ */
1267
+ getPlaceholderWidth(index) {
1268
+ const widths = [60, 80, 70, 90, 75, 85];
1269
+ return widths[index % widths.length];
1270
+ }
1271
+ _convertColumns() {
1272
+ const columnDefs = this.columns.map((col) => {
1273
+ const colId = col.id || String(col.accessorKey || 'col') || (typeof col.header === 'string' ? col.header : 'col');
1274
+ const headerValue = col.headerTemplate || (this.isTemplateRef(col.header) ? col.header : typeof col.header === 'string' ? col.header : String(col.header));
1275
+ const def = {
1276
+ id: colId,
1277
+ header: headerValue,
1278
+ enableSorting: col.sortable ?? false,
1279
+ enableColumnFilter: col.filterable ?? false,
1280
+ size: col.width ? parseInt(col.width) : undefined,
1281
+ minSize: col.minWidth ? parseInt(col.minWidth) : undefined,
1282
+ maxSize: col.maxWidth ? parseInt(col.maxWidth) : undefined,
1283
+ enableResizing: col.resizable ?? false,
1284
+ };
1285
+ // Set accessor - prefer accessorFn over accessorKey
1286
+ if (col.accessorFn) {
1287
+ def.accessorFn = col.accessorFn;
1288
+ }
1289
+ else if (col.accessorKey) {
1290
+ def.accessorKey = col.accessorKey;
1291
+ }
1292
+ // Custom cell renderer - FlexRender handles TemplateRef, Component, or function
1293
+ if (col.cell) {
1294
+ // Return the TemplateRef/Component/function for FlexRender to handle
1295
+ // FlexRender will automatically render TemplateRefs, Components, or execute functions
1296
+ def.cell = () => col.cell;
1297
+ }
1298
+ // Custom filter function
1299
+ if (col.filterFn) {
1300
+ def.filterFn = col.filterFn;
1301
+ }
1302
+ return def;
1303
+ });
1304
+ this.columnsSignal.set(columnDefs);
1305
+ // Initialize column order from column definitions if not set
1306
+ if (this.columnOrderSignal().length === 0) {
1307
+ this.columnOrderSignal.set(columnDefs.map((def) => String(def.id)));
1308
+ }
1309
+ }
1310
+ _createTable() {
1311
+ this.table = createAngularTable(() => ({
1312
+ data: this.dataSignal(),
1313
+ columns: this.columnsSignal(),
1314
+ enableRowSelection: this.selectionMode !== 'none',
1315
+ enableExpanding: this.expandable,
1316
+ getRowId: (row, index) => this.rowIdFn(row, index),
1317
+ state: {
1318
+ sorting: this.sortingSignal(),
1319
+ rowSelection: this.rowSelectionSignal(),
1320
+ globalFilter: this.globalFilterSignal(),
1321
+ columnSizing: this.columnSizingSignal(),
1322
+ expanded: this.rowExpansionSignal(),
1323
+ columnOrder: this.columnOrderSignal(),
1324
+ pagination: {
1325
+ pageIndex: this.pageIndex(),
1326
+ pageSize: this.pageSizeSignal(),
1327
+ },
1328
+ },
1329
+ initialState: {
1330
+ pagination: {
1331
+ pageIndex: 0,
1332
+ pageSize: this.pageSizeSignal(),
1333
+ },
1334
+ },
1335
+ onSortingChange: (updater) => {
1336
+ const newSorting = typeof updater === 'function' ? updater(this.sortingSignal()) : updater;
1337
+ this.sortingSignal.set(newSorting);
1338
+ if (newSorting.length > 0) {
1339
+ const sort = newSorting[0];
1340
+ this.sortChange.emit({
1341
+ columnId: sort.id,
1342
+ direction: sort.desc ? 'desc' : 'asc',
1343
+ });
1344
+ }
1345
+ },
1346
+ onRowSelectionChange: (updater) => {
1347
+ const newSelection = typeof updater === 'function' ? updater(this.rowSelectionSignal()) : updater;
1348
+ this.rowSelectionSignal.set(newSelection);
1349
+ this._emitSelectionChange();
1350
+ },
1351
+ onGlobalFilterChange: (updater) => {
1352
+ const newFilter = typeof updater === 'function' ? updater(this.globalFilterSignal()) : updater;
1353
+ this.globalFilterSignal.set(newFilter);
1354
+ },
1355
+ onColumnSizingChange: (updater) => {
1356
+ // Run outside Angular zone for smoother performance during resize
1357
+ this.ngZone.runOutsideAngular(() => {
1358
+ const newSizing = typeof updater === 'function' ? updater(this.columnSizingSignal()) : updater;
1359
+ // Cancel any pending animation frame
1360
+ if (this.resizeAnimationFrame !== null) {
1361
+ cancelAnimationFrame(this.resizeAnimationFrame);
1362
+ }
1363
+ // Use requestAnimationFrame to batch updates for smoother resizing
1364
+ this.resizeAnimationFrame = requestAnimationFrame(() => {
1365
+ this.ngZone.run(() => {
1366
+ this.columnSizingSignal.set(newSizing);
1367
+ this.cdr.markForCheck();
1368
+ });
1369
+ this.resizeAnimationFrame = null;
1370
+ });
1371
+ });
1372
+ },
1373
+ onExpandedChange: (updater) => {
1374
+ const currentState = this.rowExpansionSignal();
1375
+ const newExpansion = typeof updater === 'function' ? updater(currentState) : updater;
1376
+ // TanStack Table's ExpandedState can be boolean or Record<string, boolean>
1377
+ // We only support Record<string, boolean> for row-level expansion
1378
+ if (typeof newExpansion === 'object' && newExpansion !== null && !Array.isArray(newExpansion)) {
1379
+ this.rowExpansionSignal.set(newExpansion);
1380
+ }
1381
+ else if (typeof newExpansion === 'boolean') {
1382
+ // If it's a boolean, convert to Record format
1383
+ const expansionState = {};
1384
+ if (newExpansion) {
1385
+ // Expand all rows
1386
+ this.data.forEach((row, index) => {
1387
+ expansionState[this.rowIdFn(row, index)] = true;
1388
+ });
1389
+ }
1390
+ this.rowExpansionSignal.set(expansionState);
1391
+ }
1392
+ },
1393
+ onPaginationChange: (updater) => {
1394
+ // Skip if we're in the middle of a page size update (handled manually)
1395
+ if (this.table?._isUpdatingPageSize) {
1396
+ return;
1397
+ }
1398
+ // Get current state from the table instance (not from signals)
1399
+ const currentState = this.table.getState().pagination;
1400
+ const newPagination = typeof updater === 'function' ? updater(currentState) : updater;
1401
+ // Only update if values actually changed to avoid unnecessary updates
1402
+ if (newPagination.pageIndex !== currentState.pageIndex || newPagination.pageSize !== currentState.pageSize) {
1403
+ // Validate page index is within bounds based on current page count
1404
+ const totalPages = this.table.getPageCount();
1405
+ const validPageIndex = Math.min(Math.max(0, newPagination.pageIndex), Math.max(0, totalPages - 1));
1406
+ this.pageIndex.set(validPageIndex);
1407
+ this.pageSizeSignal.set(newPagination.pageSize);
1408
+ this._emitPaginationChange();
1409
+ }
1410
+ },
1411
+ onColumnOrderChange: (updater) => {
1412
+ const currentOrder = this.columnOrderSignal();
1413
+ const newOrder = typeof updater === 'function' ? updater(currentOrder) : updater;
1414
+ const normalizedOrder = (newOrder || []).map((id) => String(id));
1415
+ this.columnOrderSignal.set(normalizedOrder);
1416
+ this.columnOrderChange.emit(normalizedOrder);
1417
+ },
1418
+ getCoreRowModel: getCoreRowModel(),
1419
+ getExpandedRowModel: this.expandable ? getExpandedRowModel() : undefined,
1420
+ getSortedRowModel: this.sortingMode === 'client' ? getSortedRowModel() : undefined,
1421
+ getFilteredRowModel: this.filteringMode === 'client' ? getFilteredRowModel() : undefined,
1422
+ getPaginationRowModel: this.virtualScroll || this.paginationMode === 'server' ? undefined : getPaginationRowModel(), // Disable internal pagination for virtual scroll or server mode
1423
+ enableSorting: this.sorting,
1424
+ enableFiltering: this.filtering,
1425
+ enableGlobalFilter: this.globalSearch,
1426
+ enableColumnResizing: true,
1427
+ manualSorting: this.sortingMode === 'server', // Disable automatic sorting for server mode
1428
+ manualPagination: this.paginationMode === 'server',
1429
+ manualFiltering: this.filteringMode === 'server',
1430
+ rowCount: this.paginationMode === 'server' ? this.totalItems : undefined,
1431
+ }));
1432
+ }
1433
+ _emitSelectionChange() {
1434
+ const selectedRows = this.getSelectedRows();
1435
+ this.selectionChange.emit(selectedRows);
1436
+ }
1437
+ _emitPaginationChange() {
1438
+ this.paginationChange.emit({
1439
+ pageIndex: this.getCurrentPage(),
1440
+ pageSize: this.pageSizeSignal(),
1441
+ total: this.getTotalItems(),
1442
+ });
1443
+ }
1444
+ _initSortable() {
1445
+ if (this._sortable) {
1446
+ this._sortable.destroy();
1447
+ this._sortable = null;
1448
+ }
1449
+ if (this.reorderable && this.headerRowContainer?.nativeElement) {
1450
+ this._sortable = new Sortable(this.headerRowContainer.nativeElement, {
1451
+ animation: 150,
1452
+ onEnd: (evt) => {
1453
+ const { oldIndex, newIndex } = evt;
1454
+ if (oldIndex !== newIndex && this.table) {
1455
+ const visibleColumns = this.table.getVisibleLeafColumns();
1456
+ const columnIds = visibleColumns.map((c) => c.id);
1457
+ const [moved] = columnIds.splice(oldIndex, 1);
1458
+ columnIds.splice(newIndex, 0, moved);
1459
+ this.table.setColumnOrder(columnIds);
1460
+ }
1461
+ }
1462
+ });
1463
+ }
1464
+ }
1465
+ /**
1466
+ * Determine if there are any columns that can be filtered
1467
+ */
1468
+ hasFilterableColumns() {
1469
+ if (!this.table) {
1470
+ return false;
1471
+ }
1472
+ return this.table.getAllColumns().some((col) => col.getCanFilter());
1473
+ }
1474
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.7", ngImport: i0, type: TanGrid, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1475
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.7", type: TanGrid, isStandalone: true, selector: "tan-grid", inputs: { data: "data", columns: "columns", loading: "loading", striped: "striped", bordered: "bordered", hoverable: "hoverable", compact: "compact", theme: "theme", selectionMode: "selectionMode", selectedRowIds: "selectedRowIds", rowIdFn: "rowIdFn", pagination: "pagination", pageSize: "pageSize", pageSizeOptions: "pageSizeOptions", paginationMode: "paginationMode", totalItems: "totalItems", sorting: "sorting", sortingMode: "sortingMode", filtering: "filtering", filteringMode: "filteringMode", globalSearch: "globalSearch", showGlobalSearchInput: "showGlobalSearchInput", globalFilter: "globalFilter", emptyMessage: "emptyMessage", loadingMessage: "loadingMessage", loadingType: "loadingType", placeholderRows: "placeholderRows", virtualScroll: "virtualScroll", rowHeight: "rowHeight", virtualScrollBufferSize: "virtualScrollBufferSize", virtualScrollViewportHeight: "virtualScrollViewportHeight", pinnedLeft: "pinnedLeft", pinnedRight: "pinnedRight", enablePinning: "enablePinning", reorderable: "reorderable", emptyTemplate: "emptyTemplate", loadingTemplate: "loadingTemplate", expandable: "expandable", expandedRowTemplate: "expandedRowTemplate", expandedRowIds: "expandedRowIds", editable: "editable", exportable: "exportable", exportFormats: "exportFormats" }, outputs: { rowClick: "rowClick", rowDblClick: "rowDblClick", selectionChange: "selectionChange", sortChange: "sortChange", paginationChange: "paginationChange", filterChange: "filterChange", expansionChange: "expansionChange", pinChange: "pinChange", cellEditChange: "cellEditChange", columnOrderChange: "columnOrderChange" }, viewQueries: [{ propertyName: "tableContainer", first: true, predicate: ["tableContainer"], descendants: true }, { propertyName: "virtualScrollViewport", first: true, predicate: ["virtualScrollViewport"], descendants: true }, { propertyName: "headerRowContainer", first: true, predicate: ["headerRowContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"tan-grid__wrapper\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\">\n\t<!-- Toolbar: Search and Export -->\n\t@if (globalSearch || exportable) {\n\t\t<div class=\"tan-grid__toolbar\">\n\t\t\t@if (globalSearch && showGlobalSearchInput) {\n\t\t\t\t<div class=\"tan-grid__search\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tclass=\"tan-grid__search-input\"\n\t\t\t\t\t\t[value]=\"globalFilterSignal()\"\n\t\t\t\t\t\t(input)=\"onGlobalSearch($any($event.target).value)\"\n\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\taria-label=\"Search table\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@if (exportable) {\n\t\t\t\t<div class=\"tan-grid__export\">\n\t\t\t\t\t@if (exportFormats.includes('csv')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'csv', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to CSV\"\n\t\t\t\t\t\t\taria-label=\"Export to CSV\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCE5</span>\n\t\t\t\t\t\t\t<span>CSV</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t\t@if (exportFormats.includes('excel')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'excel', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to Excel\"\n\t\t\t\t\t\t\taria-label=\"Export to Excel\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCCA</span>\n\t\t\t\t\t\t\t<span>Excel</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t\t@if (exportFormats.includes('pdf')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'pdf', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to PDF\"\n\t\t\t\t\t\t\taria-label=\"Export to PDF\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCC4</span>\n\t\t\t\t\t\t\t<span>PDF</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t}\n\n\t<!-- Table Container -->\n\t<div #tableContainer class=\"tan-grid__container\" [class.tan-grid__container--virtual]=\"virtualScroll\">\n\t\t<!-- Loading Overlay -->\n\t\t@if (loading && loadingType !== 'placeholder') {\n\t\t\t<div class=\"tan-grid__loading-overlay\">\n\t\t\t\t@if (loadingTemplate) {\n\t\t\t\t\t<ng-container *ngTemplateOutlet=\"loadingTemplate\"></ng-container>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"tan-grid__loading-content\">\n\t\t\t\t\t\t<div class=\"tan-grid__spinner\"></div>\n\t\t\t\t\t\t<p>{{ loadingMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\n\t\t@if (virtualScroll) {\n\t\t\t\t<!-- Virtual Scrolling Table (using divs) -->\n\t\t\t\t<div class=\"tan-grid tan-grid--virtual\" [class.tan-grid--striped]=\"striped\" [class.tan-grid--bordered]=\"bordered\" [class.tan-grid--hoverable]=\"hoverable\" [class.tan-grid--compact]=\"compact\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\">\n\t\t\t\t\t<!-- Header -->\n\t\t\t\t\t<div class=\"tan-grid__head tan-grid__head--virtual\">\n\t\t\t\t\t\t@for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row tan-grid__row--header\"\n\t\t\t\t\t\t\t\t#headerRowContainer\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header tan-grid__header--expand tan-grid__header--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header tan-grid__header--select tan-grid__header--virtual\">\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode === 'multiple') {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"table.getIsAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t[indeterminate]=\"table.getIsSomeRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"table.toggleAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Select all rows\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (header of headerGroup.headers; track header.id) {\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__header tan-grid__header--virtual\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sortable]=\"header.column.getCanSort()\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sorted]=\"header.column.getIsSorted()\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-left]=\"isPinnedLeft(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-right]=\"isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.width]=\"header.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"header.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"header.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(header.column.id) ? getPinnedLeftOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(header.column.id) ? getPinnedRightOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (!header.isPlaceholder) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header-content\">\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Pin Button -->\n\t\t\t\t\t\t\t\t\t\t\t\t@if (enablePinning && hasHorizontalScroll() && (canPinLeft(header.column.id) || canPinRight(header.column.id))) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pin-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.active]=\"isPinnedLeft(header.column.id) || isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"togglePin(header.column); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Toggle Pin\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"22\"></line><path d=\"M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z\"></path></svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanSort()) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"header.column.toggleSorting()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-sort]=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.getIsSorted() === 'asc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'ascending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: header.column.getIsSorted() === 'desc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'descending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'none'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__sort-icons\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--up\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'asc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2191\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--down\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'desc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2193\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanResize()) {\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__resize-handle\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(mousedown)=\"onResizeStart($event, header)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Resize column\"\n\t\t\t\t\t\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<!-- Filter Row -->\n\t\t\t\t\t\t@if (filtering && hasFilterableColumns()) {\n\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--filter tan-grid__row--virtual\">\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (header of table.getHeaderGroups()[0].headers; track header.id) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanFilter()) {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__filter-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"header.column.getFilterValue() || ''\"\n\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"onColumnFilterChange(header.column, $any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[placeholder]=\"getFilterPlaceholder(header.column)\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Filter column\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- Virtual Scroll Viewport -->\n\t\t\t\t\t@if (loading && loadingType === 'placeholder') {\n\t\t\t\t\t\t<div class=\"tan-grid__body tan-grid__body--virtual\">\n\t\t\t\t\t\t\t@for (row of [].constructor(placeholderRows); track $index) {\n\t\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--placeholder tan-grid__row--virtual\">\n\t\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--select tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-checkbox\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t@for (col of table.getAllColumns(); track col.id; let i = $index) {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-cell\" [style.width.%]=\"getPlaceholderWidth(i)\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t} @else if (table.getRowModel().rows.length === 0) {\n\t\t\t\t\t\t<div class=\"tan-grid__body tan-grid__body--virtual\">\n\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--virtual\" [style.width.%]=\"100\">\n\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty tan-grid__empty--virtual\" [style.width.%]=\"100\">\n\t\t\t\t\t\t\t\t\t@if (emptyTemplate) {\n\t\t\t\t\t\t\t\t\t\t<ng-container *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty-content\">\n\t\t\t\t\t\t\t\t\t\t\t<p>{{ emptyMessage }}</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t#virtualScrollViewport\n\t\t\t\t\t\t\t[style.height.px]=\"virtualScrollViewportHeight || 400\"\n\t\t\t\t\t\t\tclass=\"tan-grid__virtual-viewport\"\n\t\t\t\t\t\t\tstyle=\"overflow-y: auto; contain: strict;\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t[style.height.px]=\"virtualizer.getTotalSize()\"\n\t\t\t\t\t\t\t\tstyle=\"position: relative; width: 100%;\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@for (virtualRow of virtualizer.getVirtualItems(); track virtualRow.index) {\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t*ngIf=\"table.getRowModel().rows[virtualRow.index] as row\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__row tan-grid__row--virtual\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__row--selected]=\"isRowSelected(row.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__row--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.height.px]=\"virtualRow.size\"\n\t\t\t\t\t\t\t\t\t\t[style.transform]=\"'translateY(' + virtualRow.start + 'px)'\"\n\t\t\t\t\t\t\t\t\t\tstyle=\"position: absolute; top: 0; left: 0; width: 100%;\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"onRowClick(row, virtualRow.index)\"\n\t\t\t\t\t\t\t\t\t\t(dblclick)=\"onRowDblClick(row, virtualRow.index)\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--expand tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__expand-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__expand-button--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"isRowExpanded(row.id) ? 'Collapse row' : 'Expand row'\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--select tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"row.getIsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"row.toggleSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Select row ' + row.id\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@for (cell of row.getVisibleCells(); track cell.id) {\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell tan-grid__cell--virtual\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-left]=\"isPinnedLeft(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-right]=\"isPinnedRight(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editable]=\"editable && getColumnEditable(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editing]=\"isEditing(row.id, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.width]=\"cell.column.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"cell.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"cell.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(cell.column.id) ? getPinnedLeftOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(cell.column.id) ? getPinnedRightOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t\t\t(dblclick)=\"editable && getColumnEditable(cell.column.id) && startEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t@if (editable && getColumnEditable(cell.column.id) && isEditing(row.id, cell.column.id)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell-edit\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[type]=\"getColumnEditType(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"getEditingValue()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"updateEditingValue($any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.enter)=\"saveEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.escape)=\"cancelEdit(); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(blur)=\"saveEdit(row, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcell.column.columnDef.cell;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: cell.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet cellValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ cellValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t} @else {\n\t\t\t\t<!-- Regular Table (using table element) -->\n\t\t\t\t<table\n\t\t\t\t\tclass=\"tan-grid\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\"\n\t\t\t\t\t[class.tan-grid--striped]=\"striped\"\n\t\t\t\t\t[class.tan-grid--bordered]=\"bordered\"\n\t\t\t\t\t[class.tan-grid--hoverable]=\"hoverable\"\n\t\t\t\t\t[class.tan-grid--compact]=\"compact\"\n\t\t\t\t>\n\t\t\t\t<thead class=\"tan-grid__head\">\n\t\t\t\t\t\t\t@for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row\"\n\t\t\t\t\t\t\t\t#headerRowContainer\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<th class=\"tan-grid__header tan-grid__header--expand\" scope=\"col\"></th>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<th class=\"tan-grid__header tan-grid__header--select\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode === 'multiple') {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"table.getIsAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t[indeterminate]=\"table.getIsSomeRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"table.toggleAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Select all rows\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t@for (header of headerGroup.headers; track header.id) {\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tclass=\"tan-grid__header\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sortable]=\"header.column.getCanSort()\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sorted]=\"header.column.getIsSorted()\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-left]=\"isPinnedLeft(header.column.id)\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-right]=\"isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t[style.width]=\"header.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.min-width]=\"header.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.max-width]=\"header.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(header.column.id) ? getPinnedLeftOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(header.column.id) ? getPinnedRightOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (!header.isPlaceholder) {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header-content\">\n\t\t\t\t\t\t\t\t\t\t\t<!-- Pin Button -->\n\t\t\t\t\t\t\t\t\t\t\t@if (enablePinning && hasHorizontalScroll() && (canPinLeft(header.column.id) || canPinRight(header.column.id))) {\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pin-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[class.active]=\"isPinnedLeft(header.column.id) || isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"togglePin(header.column); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Toggle Pin\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"22\"></line><path d=\"M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z\"></path></svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanSort()) {\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"header.column.toggleSorting()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-sort]=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.getIsSorted() === 'asc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'ascending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: header.column.getIsSorted() === 'desc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'descending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'none'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__sort-icons\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--up\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'asc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2191\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--down\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'desc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2193\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanResize()) {\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__resize-handle\"\n\t\t\t\t\t\t\t\t\t\t\t\t(mousedown)=\"onResizeStart($event, header)\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Resize column\"\n\t\t\t\t\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t\t\t<!-- Filter Row -->\n\t\t\t\t\t\t@if (filtering && hasFilterableColumns()) {\n\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--filter\">\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\"></td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\"></td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t@for (header of table.getHeaderGroups()[0].headers; track header.id) {\n\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\">\n\t\t\t\t\t\t\t\t\t@if (header.column.getCanFilter()) {\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__filter-input\"\n\t\t\t\t\t\t\t\t\t\t\t[value]=\"header.column.getFilterValue() || ''\"\n\t\t\t\t\t\t\t\t\t\t\t(input)=\"onColumnFilterChange(header.column, $any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t[placeholder]=\"getFilterPlaceholder(header.column)\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Filter column\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t</thead>\n\t\t\t\t<tbody class=\"tan-grid__body\">\n\t\t\t\t\t@if (loading && loadingType === 'placeholder') {\n\t\t\t\t\t\t@for (row of [].constructor(placeholderRows); track $index) {\n\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--placeholder\">\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--select\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-checkbox\"></div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (col of table.getAllColumns(); track col.id; let i = $index) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-cell\" [style.width.%]=\"getPlaceholderWidth(i)\"></div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t}\n\t\t\t\t\t} @else if (table.getRowModel().rows.length === 0) {\n\t\t\t\t\t\t<tr class=\"tan-grid__row\">\n\t\t\t\t\t\t\t<td [attr.colspan]=\"table.getAllColumns().length + (selectionMode !== 'none' ? 1 : 0) + (expandable ? 1 : 0)\" class=\"tan-grid__empty\">\n\t\t\t\t\t\t\t\t@if (emptyTemplate) {\n\t\t\t\t\t\t\t\t\t<ng-container *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty-content\">\n\t\t\t\t\t\t\t\t\t\t<p>{{ emptyMessage }}</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t@for (row of table.getRowModel().rows; track row.id) {\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row\"\n\t\t\t\t\t\t\t\t[class.tan-grid__row--selected]=\"isRowSelected(row.id)\"\n\t\t\t\t\t\t\t\t[class.tan-grid__row--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t(click)=\"onRowClick(row, $index)\"\n\t\t\t\t\t\t\t\t(dblclick)=\"onRowDblClick(row, $index)\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--expand\">\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__expand-button\"\n\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__expand-button--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t(click)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"isRowExpanded(row.id) ? 'Collapse row' : 'Expand row'\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--select\">\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t[checked]=\"row.getIsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t(change)=\"row.toggleSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Select row ' + row.id\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (cell of row.getVisibleCells(); track cell.id) {\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-left]=\"isPinnedLeft(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-right]=\"isPinnedRight(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editable]=\"editable && getColumnEditable(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editing]=\"isEditing(row.id, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.width]=\"cell.column.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"cell.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"cell.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(cell.column.id) ? getPinnedLeftOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(cell.column.id) ? getPinnedRightOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t(dblclick)=\"editable && getColumnEditable(cell.column.id) && startEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (editable && getColumnEditable(cell.column.id) && isEditing(row.id, cell.column.id)) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell-edit\">\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t[type]=\"getColumnEditType(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"getEditingValue()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"updateEditingValue($any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.enter)=\"saveEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.escape)=\"cancelEdit(); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(blur)=\"saveEdit(row, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tcell.column.columnDef.cell;\n\t\t\t\t\t\t\t\t\t\t\t\t\tprops: cell.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\tlet cellValue\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- FlexRender handles strings, TemplateRefs, and Components automatically -->\n\t\t\t\t\t\t\t\t\t\t\t\t{{ cellValue }}\n\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t@if (expandable && isRowExpanded(row.id) && expandedRowTemplate) {\n\t\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--expanded-content\">\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\t[attr.colspan]=\"table.getAllColumns().length + (selectionMode !== 'none' ? 1 : 0) + (expandable ? 1 : 0)\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell tan-grid__cell--expanded\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t*ngTemplateOutlet=\"expandedRowTemplate; context: { $implicit: row.original, row: row.original }\"\n\t\t\t\t\t\t\t\t\t\t></ng-container>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t\t}\n\t\t</div>\n\n\t\t<!-- Pagination -->\n\t\t@if (pagination && !virtualScroll) {\n\t\t\t<div class=\"tan-grid__pagination\">\n\t\t\t\t<div class=\"tan-grid__pagination-info\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t{{ getDisplayedRange().start }} - {{ getDisplayedRange().end }} of {{ getTotalItems() }} Items\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"tan-grid__pagination-controls\">\n\t\t\t\t\t@if (table.getPageCount() > 1) {\n\t\t\t\t\t\t<div class=\"tan-grid__pagination-pages\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn\"\n\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--disabled]=\"!table.getCanPreviousPage()\"\n\t\t\t\t\t\t\t\t[disabled]=\"!table.getCanPreviousPage()\"\n\t\t\t\t\t\t\t\t(click)=\"table.previousPage()\"\n\t\t\t\t\t\t\t\taria-label=\"Previous page\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t<path d=\"M10 12L6 8L10 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t@for (page of getPaginationPages(); track $index) {\n\t\t\t\t\t\t\t\t@if (page === 'ellipsis') {\n\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__pagination-ellipsis\">...</span>\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn tan-grid__pagination-btn--page\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--active]=\"page === getCurrentPage()\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"goToPage(page)\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Go to page ' + page\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-current]=\"page === getCurrentPage() ? 'page' : null\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{{ page }}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn\"\n\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--disabled]=\"!table.getCanNextPage()\"\n\t\t\t\t\t\t\t\t[disabled]=\"!table.getCanNextPage()\"\n\t\t\t\t\t\t\t\t(click)=\"table.nextPage()\"\n\t\t\t\t\t\t\t\taria-label=\"Next page\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t<div class=\"tan-grid__pagination-size\">\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclass=\"tan-grid__page-size-select\"\n\t\t\t\t\t\t\t[value]=\"pageSizeSignal()\"\n\t\t\t\t\t\t\t(change)=\"onPageSizeChange(+$any($event.target).value)\"\n\t\t\t\t\t\t\taria-label=\"Items per page\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (size of pageSizeOptions; track size) {\n\t\t\t\t\t\t\t\t<option [value]=\"size\">{{ size }} / page</option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t</div>\n\n\n", styles: [":root,[data-ngs-theme=light]{--ngs-primary: #007bff;--ngs-primary-hover: #0056b3;--ngs-primary-active: #004085;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #004085;--ngs-secondary: #6c757d;--ngs-secondary-hover: #5a6268;--ngs-secondary-active: #484f54;--ngs-secondary-light: #e2e3e5;--ngs-secondary-dark: #383d41;--ngs-success: #28a745;--ngs-success-hover: #218838;--ngs-success-active: #1e7e34;--ngs-success-light: #d4edda;--ngs-success-dark: #155724;--ngs-danger: #dc3545;--ngs-danger-hover: #c82333;--ngs-danger-active: #bd2130;--ngs-danger-light: #f8d7da;--ngs-danger-dark: #721c24;--ngs-warning: #ffc107;--ngs-warning-hover: #e0a800;--ngs-warning-active: #d39e00;--ngs-warning-light: #fff3cd;--ngs-warning-dark: #856404;--ngs-info: #17a2b8;--ngs-info-hover: #138496;--ngs-info-active: #117a8b;--ngs-info-light: #d1ecf1;--ngs-info-dark: #0c5460;--ngs-text-primary: #212529;--ngs-text-secondary: #6c757d;--ngs-text-muted: #868e96;--ngs-text-inverse: #ffffff;--ngs-bg-primary: #ffffff;--ngs-bg-secondary: #f8f9fa;--ngs-bg-tertiary: #e9ecef;--ngs-bg-dark: #212529;--ngs-border-color: #dee2e6;--ngs-border-color-light: #e9ecef;--ngs-border-color-dark: #adb5bd;--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-border-radius-xl: 0;--ngs-border-radius-pill: 50rem;--ngs-border-radius-circle: 50%;--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-xs: .75rem;--ngs-font-size-sm: .8125rem;--ngs-font-size-base: .875rem;--ngs-font-size-lg: 1rem;--ngs-font-size-xl: 1.125rem;--ngs-font-size-2xl: 1.25rem;--ngs-font-size-3xl: 1.5rem;--ngs-font-size-4xl: 2rem;--ngs-font-weight-light: 300;--ngs-font-weight-normal: 400;--ngs-font-weight-medium: 500;--ngs-font-weight-semibold: 600;--ngs-font-weight-bold: 700;--ngs-line-height-tight: 1.25;--ngs-line-height-normal: 1.5;--ngs-line-height-relaxed: 1.75;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-transition-base: all .2s ease-in-out;--ngs-transition-fast: all .15s ease-in-out;--ngs-transition-slow: all .3s ease-in-out;--ngs-z-index-dropdown: 1000;--ngs-z-index-sticky: 1020;--ngs-z-index-fixed: 1030;--ngs-z-index-modal-backdrop: 1040;--ngs-z-index-modal: 1050;--ngs-z-index-popover: 1060;--ngs-z-index-tooltip: 1070}[data-ngs-theme=dark]{--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #031633;--ngs-primary-dark: #0a58ca;--ngs-secondary: #6c757d;--ngs-secondary-hover: #5c636a;--ngs-secondary-active: #565e64;--ngs-secondary-light: #41464b;--ngs-secondary-dark: #adb5bd;--ngs-success: #198754;--ngs-success-hover: #157347;--ngs-success-active: #146c43;--ngs-success-light: #0f5132;--ngs-success-dark: #75b798;--ngs-danger: #dc3545;--ngs-danger-hover: #bb2d3b;--ngs-danger-active: #b02a37;--ngs-danger-light: #842029;--ngs-danger-dark: #ea868f;--ngs-warning: #ffc107;--ngs-warning-hover: #ffca2c;--ngs-warning-active: #ffcd39;--ngs-warning-light: #664d03;--ngs-warning-dark: #ffcd39;--ngs-info: #0dcaf0;--ngs-info-hover: #31d2f2;--ngs-info-active: #3dd5f3;--ngs-info-light: #055160;--ngs-info-dark: #6edff6;--ngs-text-primary: #ffffff;--ngs-text-secondary: #adb5bd;--ngs-text-muted: #868e96;--ngs-text-inverse: #212529;--ngs-bg-primary: #212529;--ngs-bg-secondary: #343a40;--ngs-bg-tertiary: #495057;--ngs-bg-dark: #121416;--ngs-border-color: #495057;--ngs-border-color-light: #343a40;--ngs-border-color-dark: #6c757d}.tan-grid__wrapper{display:flex;flex-direction:column;gap:var(--ngs-spacing-md)}.tan-grid__toolbar{display:flex;align-items:center;justify-content:space-between;gap:var(--ngs-spacing-md);margin-bottom:var(--ngs-spacing-md);flex-wrap:wrap}.tan-grid__search{flex:1;min-width:200px}.tan-grid__search-input{width:100%;max-width:300px;padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);font-size:var(--ngs-font-size-base);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__search-input:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__export{display:flex;align-items:center;gap:var(--ngs-spacing-xs);flex-wrap:wrap}.tan-grid__export-btn{display:flex;align-items:center;gap:var(--ngs-spacing-xs);padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);background-color:var(--ngs-bg-primary);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);color:var(--ngs-text-primary);font-size:var(--ngs-font-size-sm);font-weight:var(--ngs-font-weight-medium);cursor:pointer;transition:var(--ngs-transition-base)}.tan-grid__export-btn:hover{background-color:var(--ngs-primary);border-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__export-btn:active{transform:translateY(1px)}.tan-grid__export-btn span:first-child{font-size:var(--ngs-font-size-base)}.tan-grid__container{overflow-x:auto;overflow-y:visible;border-radius:var(--ngs-border-radius);position:relative;min-height:200px}.tan-grid__container--virtual{overflow-y:hidden}.tan-grid{width:100%;border-collapse:collapse;font-size:var(--ngs-font-size-base);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary)}.tan-grid:has(.tan-grid__header--pinned-left),.tan-grid:has(.tan-grid__header--pinned-right){min-width:max-content;width:max-content}.tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:var(--ngs-bg-secondary)}.tan-grid--bordered,.tan-grid--bordered .tan-grid__cell,.tan-grid--bordered .tan-grid__header{border:1px solid var(--ngs-border-color)}.tan-grid--hoverable .tan-grid__body .tan-grid__row{transition:var(--ngs-transition-base)}.tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:var(--ngs-bg-secondary);cursor:pointer}.tan-grid--compact .tan-grid__cell,.tan-grid--compact .tan-grid__header{padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm)}.tan-grid__head{background-color:var(--ngs-bg-secondary);border:none}.tan-grid__head .tan-grid__row{border:none}.tan-grid__header{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);text-align:left;font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);position:relative;white-space:nowrap;overflow:visible}.tan-grid__header--select,.tan-grid__header--expand{width:40px;text-align:center}.tan-grid__header--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.tan-grid__header--sortable:hover,.tan-grid__header--sorted{background-color:var(--ngs-bg-tertiary)}.tan-grid__resize-handle{position:absolute;right:-2px;top:0;height:100%;width:5px;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:10;background:transparent;will-change:background-color;transition:background-color .15s ease}.tan-grid__resize-handle:hover{background-color:var(--ngs-primary);opacity:.6}.tan-grid__resize-handle:active{background-color:var(--ngs-primary);opacity:1}.tan-grid__header-content{display:flex;align-items:center;gap:var(--ngs-spacing-xs)}.tan-grid__header-text{flex:1}.tan-grid__sort-button{display:flex;align-items:center;justify-content:space-between;width:100%;gap:var(--ngs-spacing-xs);background:transparent;border:none;padding:0;cursor:pointer;color:inherit;font:inherit;text-align:left}.tan-grid__sort-icons{display:flex;flex-direction:row;gap:0;font-size:var(--ngs-font-size-xs);line-height:1;margin-left:var(--ngs-spacing-xs)}.tan-grid__sort-icon{opacity:.3;transition:var(--ngs-transition-base);display:inline-block;width:10px;text-align:center;line-height:.8}.tan-grid__sort-icon--up{margin-right:-2px}.tan-grid__sort-icon--down{margin-left:-2px}.tan-grid__sort-icon--active{opacity:1;color:var(--ngs-primary);font-weight:var(--ngs-font-weight-bold)}.tan-grid__body .tan-grid__row--selected{background-color:var(--ngs-primary-light)!important}.tan-grid__row--filter,.tan-grid__row--expanded,.tan-grid__row--expanded-content{background-color:var(--ngs-bg-secondary)}.tan-grid__cell{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md)}.tan-grid__cell--filter{padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm);vertical-align:middle}.tan-grid__cell--expand,.tan-grid__cell--select{width:40px;text-align:center;padding:var(--ngs-spacing-sm)}.tan-grid__cell--expanded{padding:var(--ngs-spacing-lg);background-color:var(--ngs-bg-secondary);border-top:2px solid var(--ngs-primary)}.tan-grid__filter-input{width:100%;padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm);font-size:var(--ngs-font-size-sm);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__filter-input:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__filter-input::placeholder{color:var(--ngs-text-secondary);opacity:.6}.tan-grid__empty{padding:var(--ngs-spacing-2xl);text-align:center;color:var(--ngs-text-secondary)}.tan-grid--virtual{display:flex;flex-direction:column;border-collapse:separate;border-spacing:0;width:100%}.tan-grid--virtual:has(.tan-grid__header--pinned-left),.tan-grid--virtual:has(.tan-grid__header--pinned-right){min-width:100%;width:max-content}.tan-grid__head--virtual{display:flex;flex-direction:column;background-color:var(--ngs-bg-secondary);flex-shrink:0}.tan-grid__row--header,.tan-grid__row--virtual{display:flex;width:100%;box-sizing:border-box;flex-shrink:0;flex-grow:0}.tan-grid--virtual:has(.tan-grid__header--pinned-left) .tan-grid__row--header,.tan-grid--virtual:has(.tan-grid__header--pinned-right) .tan-grid__row--header,.tan-grid--virtual:has(.tan-grid__header--pinned-left) .tan-grid__row--virtual,.tan-grid--virtual:has(.tan-grid__header--pinned-right) .tan-grid__row--virtual{min-width:max-content}.tan-grid__header--virtual,.tan-grid__cell--virtual{display:flex;align-items:center;flex-shrink:0;box-sizing:border-box}.tan-grid__header--virtual{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);text-align:left;font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);position:relative;white-space:nowrap;overflow:visible;border-right:1px solid var(--ngs-border-color);border-bottom:1px solid var(--ngs-border-color)}.tan-grid__header--virtual:last-child{border-right:none}.tan-grid__cell--virtual{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);border-right:1px solid var(--ngs-border-color);border-bottom:1px solid var(--ngs-border-color);box-sizing:border-box;flex-shrink:0;height:100%}.tan-grid__cell--virtual:last-child{border-right:none}.tan-grid__body--virtual{display:flex;flex-direction:column}.tan-grid__virtual-viewport{width:100%;overflow:auto;display:block;position:relative;contain:layout style paint}.tan-grid__virtual-viewport .cdk-virtual-scroll-content-wrapper{display:block;box-sizing:border-box;width:100%}.tan-grid__virtual-viewport .cdk-virtual-scroll-spacer{box-sizing:border-box;display:block;width:1px;pointer-events:none}.tan-grid--striped .tan-grid__row--virtual:nth-child(2n){background-color:var(--ngs-bg-secondary)}.tan-grid--hoverable .tan-grid__row--virtual{transition:var(--ngs-transition-base)}.tan-grid--hoverable .tan-grid__row--virtual:hover{background-color:var(--ngs-bg-secondary);cursor:pointer}.tan-grid__empty--virtual{display:flex;align-items:center;justify-content:center;padding:var(--ngs-spacing-2xl);text-align:center;color:var(--ngs-text-secondary);width:100%}.tan-grid__empty-content p{margin:0;font-size:var(--ngs-font-size-base)}.tan-grid__loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:#ffffffb3;z-index:50;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.tan-grid__loading{display:flex;align-items:center;justify-content:center;min-height:200px;padding:var(--ngs-spacing-2xl)}.tan-grid__loading-content{display:flex;flex-direction:column;align-items:center;gap:var(--ngs-spacing-md)}.tan-grid__loading-content p{margin:0;color:var(--ngs-text-secondary)}.tan-grid__spinner{width:40px;height:40px;border:3px solid var(--ngs-border-color);border-top-color:var(--ngs-primary);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.tan-grid__pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--ngs-spacing-lg);padding:var(--ngs-spacing-md);border-top:1px solid var(--ngs-border-color);flex-wrap:wrap}@media (max-width: 768px){.tan-grid__pagination{flex-direction:column;align-items:stretch}}.tan-grid__pagination-info{display:flex;align-items:center;font-size:var(--ngs-font-size-sm);color:var(--ngs-text-secondary)}.tan-grid__pagination-controls{display:flex;align-items:center;gap:var(--ngs-spacing-lg)}.tan-grid__pagination-pages{display:flex;align-items:center;gap:var(--ngs-spacing-xs)}.tan-grid__pagination-btn{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--ngs-spacing-sm);font-size:var(--ngs-font-size-sm);line-height:1;border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);cursor:pointer;transition:var(--ngs-transition-base);-webkit-user-select:none;user-select:none;font-weight:var(--ngs-font-weight-normal)}.tan-grid__pagination-btn:hover:not(:disabled):not(.tan-grid__pagination-btn--active){border-color:var(--ngs-primary);color:var(--ngs-primary);background-color:var(--ngs-bg-secondary)}.tan-grid__pagination-btn:active:not(:disabled){transform:scale(.98)}.tan-grid__pagination-btn:disabled,.tan-grid__pagination-btn--disabled{opacity:.4;cursor:not-allowed;pointer-events:none;background-color:var(--ngs-bg-secondary)}.tan-grid__pagination-btn--page{font-weight:var(--ngs-font-weight-normal)}.tan-grid__pagination-btn--active{border-color:var(--ngs-primary);background-color:var(--ngs-primary);color:var(--ngs-text-inverse);font-weight:var(--ngs-font-weight-medium);cursor:default;box-shadow:0 1px 2px #0000001a}.tan-grid__pagination-btn--active:hover{border-color:var(--ngs-primary);background-color:var(--ngs-primary-hover);color:var(--ngs-text-inverse)}.tan-grid__pagination-btn svg{width:16px;height:16px}.tan-grid__pagination-ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--ngs-spacing-xs);color:var(--ngs-text-secondary);font-size:var(--ngs-font-size-sm);-webkit-user-select:none;user-select:none}.tan-grid__pagination-size{display:flex;align-items:center}.tan-grid__page-size-select{padding:var(--ngs-spacing-xs) var(--ngs-spacing-md);padding-right:calc(var(--ngs-spacing-md) + 20px);font-size:var(--ngs-font-size-sm);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);cursor:pointer;transition:var(--ngs-transition-base);appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right var(--ngs-spacing-sm) center;background-size:12px;height:32px}.tan-grid__page-size-select:hover{border-color:var(--ngs-primary)}.tan-grid__page-size-select:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__row--placeholder .tan-grid__cell{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md)}.tan-grid__placeholder-cell,.tan-grid__placeholder-checkbox{height:14px;background:linear-gradient(90deg,#f0f0f0 0% 40%,#e0e0e0 50%,#f0f0f0 60% 100%);background-size:200% 100%;animation:shimmer 1.2s ease-in-out infinite;border-radius:0;position:relative;overflow:hidden}.tan-grid__placeholder-checkbox{width:16px;height:16px;margin:0 auto;border-radius:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.tan-grid__header--pinned-left,.tan-grid__cell--pinned-left{position:sticky;left:0;z-index:10;background-color:var(--ngs-bg-primary)}.tan-grid__header--pinned-left:after,.tan-grid__cell--pinned-left:after{content:\"\";position:absolute;right:0;top:0;bottom:0;width:1px;background-color:var(--ngs-border-color);box-shadow:2px 0 4px #0000001a}.tan-grid__header--pinned-left{background-color:var(--ngs-bg-secondary);z-index:11}.tan-grid__header--pinned-right,.tan-grid__cell--pinned-right{position:sticky;right:0;z-index:10;background-color:var(--ngs-bg-primary)}.tan-grid__header--pinned-right:before,.tan-grid__cell--pinned-right:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:1px;background-color:var(--ngs-border-color);box-shadow:-2px 0 4px #0000001a}.tan-grid__header--pinned-right{background-color:var(--ngs-bg-secondary);z-index:11}.tan-grid--striped .tan-grid__row:nth-child(2n) .tan-grid__cell--pinned-left,.tan-grid--striped .tan-grid__row:nth-child(2n) .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid--striped .tan-grid__row--virtual:nth-child(2n) .tan-grid__cell--pinned-left,.tan-grid--striped .tan-grid__row--virtual:nth-child(2n) .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid--hoverable .tan-grid__row:hover .tan-grid__cell--pinned-left,.tan-grid--hoverable .tan-grid__row:hover .tan-grid__cell--pinned-right,.tan-grid--hoverable .tan-grid__row--virtual:hover .tan-grid__cell--pinned-left,.tan-grid--hoverable .tan-grid__row--virtual:hover .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid__cell--editable{cursor:pointer;position:relative}.tan-grid__cell--editable:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__cell--editing{background-color:var(--ngs-primary-light);padding:0}.tan-grid__cell-edit{width:100%;height:100%;display:flex;align-items:center}.tan-grid__cell-input{width:100%;padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);border:2px solid var(--ngs-primary);border-radius:var(--ngs-border-radius-sm);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);font-size:var(--ngs-font-size-base);font-family:inherit;outline:none;box-sizing:border-box}.tan-grid__cell-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__cell-input::placeholder{color:var(--ngs-text-secondary);opacity:.6}.tan-grid__expand-button{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);cursor:pointer;color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__expand-button svg{transition:transform .2s ease}.tan-grid__expand-button:hover{background-color:var(--ngs-bg-secondary);border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__expand-button:focus{outline:none;box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__expand-button--expanded{background-color:var(--ngs-primary);border-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__expand-button--expanded svg{transform:rotate(90deg)}.tan-grid__expand-button--expanded:hover{background-color:var(--ngs-primary-dark);border-color:var(--ngs-primary-dark)}.tan-grid__wrapper.tan-grid--theme-material{--ngs-border-radius: .25rem;--ngs-border-radius-sm: .125rem;--ngs-border-radius-lg: .375rem;--ngs-primary: #6200ee;--ngs-primary-hover: #3700b3;--ngs-primary-active: #000000;--ngs-primary-light: #e0d4f7;--ngs-primary-dark: #3700b3;--ngs-shadow-sm: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);--ngs-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 3px 6px rgba(0, 0, 0, .23);--ngs-shadow-lg: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);--ngs-spacing-xs: .25rem;--ngs-spacing-sm: .5rem;--ngs-spacing-md: .75rem;--ngs-spacing-lg: 1rem;--ngs-spacing-xl: 1.5rem;--ngs-spacing-2xl: 2rem;--ngs-spacing-3xl: 3rem;--ngs-font-family: \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--ngs-font-size-base: .875rem;--ngs-font-weight-medium: 500;box-shadow:var(--ngs-shadow-sm);border-radius:var(--ngs-border-radius);overflow:hidden;background-color:var(--ngs-bg-primary)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid{border:none;border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered{border:none}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered .tan-grid__header{border:none;border-bottom:1px solid var(--ngs-border-color-light)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#00000005}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__head{background-color:var(--ngs-bg-primary);box-shadow:0 2px 4px #00000014}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000de;letter-spacing:.01785714em}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header--sortable:hover{background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header--sorted{background-color:#6200ee14;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000de}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-material .tan-grid__filter-input{border-radius:var(--ngs-border-radius);border:1px solid var(--ngs-border-color);transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-material .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #6200ee33}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__export-btn{border-radius:var(--ngs-border-radius);text-transform:uppercase;font-weight:var(--ngs-font-weight-medium);letter-spacing:.0892857143em;transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__export-btn:hover{box-shadow:var(--ngs-shadow-sm)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn{border-radius:50%;min-width:40px;height:40px;transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn:hover:not(.tan-grid__pagination-btn--active){background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__checkbox{border-radius:2px}.tan-grid.tan-grid--theme-material{--ngs-border-radius: .25rem;--ngs-border-radius-sm: .125rem;--ngs-border-radius-lg: .375rem;--ngs-primary: #6200ee;--ngs-primary-hover: #3700b3;--ngs-primary-active: #000000;--ngs-primary-light: #e0d4f7;--ngs-primary-dark: #3700b3;--ngs-shadow-sm: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);--ngs-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 3px 6px rgba(0, 0, 0, .23);--ngs-shadow-lg: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);--ngs-spacing-xs: .25rem;--ngs-spacing-sm: .5rem;--ngs-spacing-md: .75rem;--ngs-spacing-lg: 1rem;--ngs-spacing-xl: 1.5rem;--ngs-spacing-2xl: 2rem;--ngs-spacing-3xl: 3rem;--ngs-font-family: \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--ngs-font-size-base: .875rem;--ngs-font-weight-medium: 500;border:none;border-radius:var(--ngs-border-radius)}.tan-grid.tan-grid--theme-material--bordered{border:none}.tan-grid.tan-grid--theme-material--bordered .tan-grid__cell,.tan-grid.tan-grid--theme-material--bordered .tan-grid__header{border:none;border-bottom:1px solid var(--ngs-border-color-light)}.tan-grid__wrapper.tan-grid--theme-bootstrap{--ngs-border-radius: .375rem;--ngs-border-radius-sm: .25rem;--ngs-border-radius-lg: .5rem;--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #084298;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-base: 1rem}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid{border-collapse:separate;border-spacing:0}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered{border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered .tan-grid__header{border:1px solid var(--ngs-border-color)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#0000000d}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#00000013}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__head{background-color:var(--ngs-bg-secondary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);vertical-align:bottom}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header--sortable:hover{background-color:var(--ngs-bg-tertiary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header--sorted{background-color:var(--ngs-primary-light);color:var(--ngs-primary-dark)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:var(--ngs-text-primary);vertical-align:top}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__filter-input{border-radius:var(--ngs-border-radius);border:1px solid var(--ngs-border-color);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__filter-input:focus{border-color:#86b7fe;box-shadow:0 0 0 .25rem #0d6efd40}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn{border-radius:var(--ngs-border-radius);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn:hover{background-color:var(--ngs-primary-hover)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn:active{background-color:var(--ngs-primary-active)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn{border-radius:var(--ngs-border-radius);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__checkbox{border-radius:var(--ngs-border-radius-sm)}.tan-grid.tan-grid--theme-bootstrap{--ngs-border-radius: .375rem;--ngs-border-radius-sm: .25rem;--ngs-border-radius-lg: .5rem;--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #084298;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-base: 1rem;border-collapse:separate;border-spacing:0}.tan-grid.tan-grid--theme-bootstrap--bordered{border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-ant{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid{border:1px solid #f0f0f0;border-radius:0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered .tan-grid__header{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#f5f5f5}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__head{background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000d9;background-color:#fafafa;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header--sortable:hover{background-color:#e6f7ff}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header--sorted{background-color:#e6f7ff;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000d9;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__row:last-child .tan-grid__cell{border-bottom:none}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input:hover,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input:hover{border-color:#40a9ff}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #1890ff33}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn:active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease;min-width:32px;height:32px}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__checkbox{border-radius:0}.tan-grid.tan-grid--theme-ant{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem;border:1px solid #f0f0f0;border-radius:0}.tan-grid.tan-grid--theme-ant--bordered{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid{border:1px solid #f0f0f0;border-radius:0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered .tan-grid__header{border:none;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#f5f5f5}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__head{background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000d9;background-color:#fafafa;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header--sortable:hover{background-color:#e6f7ff}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header--sorted{background-color:#e6f7ff;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000d9;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__row:last-child .tan-grid__cell{border-bottom:none}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input:hover,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input:hover{border-color:#40a9ff}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #1890ff33}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn:active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease;min-width:32px;height:32px}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__checkbox{border-radius:0}.tan-grid.tan-grid--theme-ant-alt{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem;border:1px solid #f0f0f0;border-radius:0}.tan-grid.tan-grid--theme-ant-alt--bordered{border:1px solid #f0f0f0}.tan-grid.tan-grid--theme-ant-alt--bordered .tan-grid__cell,.tan-grid.tan-grid--theme-ant-alt--bordered .tan-grid__header{border:none;border-bottom:1px solid #f0f0f0}.tan-grid__pin-btn{display:flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:.25rem;margin-right:.25rem;border-radius:4px;color:var(--ngs-text-secondary);transition:var(--ngs-transition-base)}.tan-grid__pin-btn:hover{background-color:var(--ngs-bg-secondary);color:var(--ngs-text-primary)}.tan-grid__pin-btn.active{color:var(--ngs-primary);background-color:var(--ngs-primary-light)}.tan-grid__menu{background-color:var(--ngs-bg-primary);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;padding:.25rem;display:flex;flex-direction:column;min-width:150px;z-index:1000}.tan-grid__menu-item{display:flex;align-items:center;width:100%;padding:.5rem .75rem;font-size:var(--ngs-font-size-sm);color:var(--ngs-text-primary);background:transparent;border:none;cursor:pointer;text-align:left;border-radius:var(--ngs-border-radius)}.tan-grid__menu-item:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__menu-icon{width:1.5rem;display:flex;align-items:center;justify-content:center;color:var(--ngs-primary);font-weight:700}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1476
+ }
1477
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.7", ngImport: i0, type: TanGrid, decorators: [{
1478
+ type: Component,
1479
+ args: [{ selector: 'tan-grid', standalone: true, imports: [CommonModule, FormsModule, FlexRenderDirective], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div class=\"tan-grid__wrapper\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\">\n\t<!-- Toolbar: Search and Export -->\n\t@if (globalSearch || exportable) {\n\t\t<div class=\"tan-grid__toolbar\">\n\t\t\t@if (globalSearch && showGlobalSearchInput) {\n\t\t\t\t<div class=\"tan-grid__search\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tclass=\"tan-grid__search-input\"\n\t\t\t\t\t\t[value]=\"globalFilterSignal()\"\n\t\t\t\t\t\t(input)=\"onGlobalSearch($any($event.target).value)\"\n\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\taria-label=\"Search table\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t}\n\t\t\t@if (exportable) {\n\t\t\t\t<div class=\"tan-grid__export\">\n\t\t\t\t\t@if (exportFormats.includes('csv')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'csv', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to CSV\"\n\t\t\t\t\t\t\taria-label=\"Export to CSV\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCE5</span>\n\t\t\t\t\t\t\t<span>CSV</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t\t@if (exportFormats.includes('excel')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'excel', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to Excel\"\n\t\t\t\t\t\t\taria-label=\"Export to Excel\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCCA</span>\n\t\t\t\t\t\t\t<span>Excel</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t\t@if (exportFormats.includes('pdf')) {\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"tan-grid__export-btn\"\n\t\t\t\t\t\t\t(click)=\"exportData({ format: 'pdf', filename: 'table-export' })\"\n\t\t\t\t\t\t\ttitle=\"Export to PDF\"\n\t\t\t\t\t\t\taria-label=\"Export to PDF\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>\uD83D\uDCC4</span>\n\t\t\t\t\t\t\t<span>PDF</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t}\n\t\t</div>\n\t}\n\n\t<!-- Table Container -->\n\t<div #tableContainer class=\"tan-grid__container\" [class.tan-grid__container--virtual]=\"virtualScroll\">\n\t\t<!-- Loading Overlay -->\n\t\t@if (loading && loadingType !== 'placeholder') {\n\t\t\t<div class=\"tan-grid__loading-overlay\">\n\t\t\t\t@if (loadingTemplate) {\n\t\t\t\t\t<ng-container *ngTemplateOutlet=\"loadingTemplate\"></ng-container>\n\t\t\t\t} @else {\n\t\t\t\t\t<div class=\"tan-grid__loading-content\">\n\t\t\t\t\t\t<div class=\"tan-grid__spinner\"></div>\n\t\t\t\t\t\t<p>{{ loadingMessage }}</p>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t</div>\n\t\t}\n\n\t\t@if (virtualScroll) {\n\t\t\t\t<!-- Virtual Scrolling Table (using divs) -->\n\t\t\t\t<div class=\"tan-grid tan-grid--virtual\" [class.tan-grid--striped]=\"striped\" [class.tan-grid--bordered]=\"bordered\" [class.tan-grid--hoverable]=\"hoverable\" [class.tan-grid--compact]=\"compact\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\">\n\t\t\t\t\t<!-- Header -->\n\t\t\t\t\t<div class=\"tan-grid__head tan-grid__head--virtual\">\n\t\t\t\t\t\t@for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row tan-grid__row--header\"\n\t\t\t\t\t\t\t\t#headerRowContainer\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header tan-grid__header--expand tan-grid__header--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header tan-grid__header--select tan-grid__header--virtual\">\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode === 'multiple') {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"table.getIsAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t[indeterminate]=\"table.getIsSomeRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"table.toggleAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Select all rows\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (header of headerGroup.headers; track header.id) {\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__header tan-grid__header--virtual\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sortable]=\"header.column.getCanSort()\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sorted]=\"header.column.getIsSorted()\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-left]=\"isPinnedLeft(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-right]=\"isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.width]=\"header.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"header.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"header.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(header.column.id) ? getPinnedLeftOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(header.column.id) ? getPinnedRightOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (!header.isPlaceholder) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header-content\">\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- Pin Button -->\n\t\t\t\t\t\t\t\t\t\t\t\t@if (enablePinning && hasHorizontalScroll() && (canPinLeft(header.column.id) || canPinRight(header.column.id))) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pin-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.active]=\"isPinnedLeft(header.column.id) || isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"togglePin(header.column); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Toggle Pin\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"22\"></line><path d=\"M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z\"></path></svg>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanSort()) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"header.column.toggleSorting()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-sort]=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.getIsSorted() === 'asc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'ascending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: header.column.getIsSorted() === 'desc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'descending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'none'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__sort-icons\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--up\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'asc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2191\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--down\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'desc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2193\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanResize()) {\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__resize-handle\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(mousedown)=\"onResizeStart($event, header)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Resize column\"\n\t\t\t\t\t\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t\t<!-- Filter Row -->\n\t\t\t\t\t\t@if (filtering && hasFilterableColumns()) {\n\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--filter tan-grid__row--virtual\">\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\"></div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (header of table.getHeaderGroups()[0].headers; track header.id) {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--filter tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanFilter()) {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__filter-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"header.column.getFilterValue() || ''\"\n\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"onColumnFilterChange(header.column, $any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[placeholder]=\"getFilterPlaceholder(header.column)\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Filter column\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- Virtual Scroll Viewport -->\n\t\t\t\t\t@if (loading && loadingType === 'placeholder') {\n\t\t\t\t\t\t<div class=\"tan-grid__body tan-grid__body--virtual\">\n\t\t\t\t\t\t\t@for (row of [].constructor(placeholderRows); track $index) {\n\t\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--placeholder tan-grid__row--virtual\">\n\t\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--select tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-checkbox\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t@for (col of table.getAllColumns(); track col.id; let i = $index) {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-cell\" [style.width.%]=\"getPlaceholderWidth(i)\"></div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t} @else if (table.getRowModel().rows.length === 0) {\n\t\t\t\t\t\t<div class=\"tan-grid__body tan-grid__body--virtual\">\n\t\t\t\t\t\t\t<div class=\"tan-grid__row tan-grid__row--virtual\" [style.width.%]=\"100\">\n\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty tan-grid__empty--virtual\" [style.width.%]=\"100\">\n\t\t\t\t\t\t\t\t\t@if (emptyTemplate) {\n\t\t\t\t\t\t\t\t\t\t<ng-container *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty-content\">\n\t\t\t\t\t\t\t\t\t\t\t<p>{{ emptyMessage }}</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t#virtualScrollViewport\n\t\t\t\t\t\t\t[style.height.px]=\"virtualScrollViewportHeight || 400\"\n\t\t\t\t\t\t\tclass=\"tan-grid__virtual-viewport\"\n\t\t\t\t\t\t\tstyle=\"overflow-y: auto; contain: strict;\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t[style.height.px]=\"virtualizer.getTotalSize()\"\n\t\t\t\t\t\t\t\tstyle=\"position: relative; width: 100%;\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@for (virtualRow of virtualizer.getVirtualItems(); track virtualRow.index) {\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t*ngIf=\"table.getRowModel().rows[virtualRow.index] as row\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__row tan-grid__row--virtual\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__row--selected]=\"isRowSelected(row.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__row--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.height.px]=\"virtualRow.size\"\n\t\t\t\t\t\t\t\t\t\t[style.transform]=\"'translateY(' + virtualRow.start + 'px)'\"\n\t\t\t\t\t\t\t\t\t\tstyle=\"position: absolute; top: 0; left: 0; width: 100%;\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"onRowClick(row, virtualRow.index)\"\n\t\t\t\t\t\t\t\t\t\t(dblclick)=\"onRowDblClick(row, virtualRow.index)\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--expand tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__expand-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__expand-button--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"isRowExpanded(row.id) ? 'Collapse row' : 'Expand row'\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell tan-grid__cell--select tan-grid__cell--virtual\">\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"row.getIsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"row.toggleSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Select row ' + row.id\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t@for (cell of row.getVisibleCells(); track cell.id) {\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell tan-grid__cell--virtual\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-left]=\"isPinnedLeft(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-right]=\"isPinnedRight(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editable]=\"editable && getColumnEditable(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editing]=\"isEditing(row.id, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.width]=\"cell.column.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"cell.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"cell.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(cell.column.id) ? getPinnedLeftOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(cell.column.id) ? getPinnedRightOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t\t\t(dblclick)=\"editable && getColumnEditable(cell.column.id) && startEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t@if (editable && getColumnEditable(cell.column.id) && isEditing(row.id, cell.column.id)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell-edit\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[type]=\"getColumnEditType(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"getEditingValue()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"updateEditingValue($any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.enter)=\"saveEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.escape)=\"cancelEdit(); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(blur)=\"saveEdit(row, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcell.column.columnDef.cell;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: cell.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet cellValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ cellValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t</div>\n\t\t\t} @else {\n\t\t\t\t<!-- Regular Table (using table element) -->\n\t\t\t\t<table\n\t\t\t\t\tclass=\"tan-grid\" [class.tan-grid--theme-material]=\"theme === 'material'\" [class.tan-grid--theme-bootstrap]=\"theme === 'bootstrap'\" [class.tan-grid--theme-ant]=\"theme === 'ant'\" [class.tan-grid--theme-ant-alt]=\"theme === 'ant-alt'\"\n\t\t\t\t\t[class.tan-grid--striped]=\"striped\"\n\t\t\t\t\t[class.tan-grid--bordered]=\"bordered\"\n\t\t\t\t\t[class.tan-grid--hoverable]=\"hoverable\"\n\t\t\t\t\t[class.tan-grid--compact]=\"compact\"\n\t\t\t\t>\n\t\t\t\t<thead class=\"tan-grid__head\">\n\t\t\t\t\t\t\t@for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row\"\n\t\t\t\t\t\t\t\t#headerRowContainer\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<th class=\"tan-grid__header tan-grid__header--expand\" scope=\"col\"></th>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<th class=\"tan-grid__header tan-grid__header--select\" scope=\"col\">\n\t\t\t\t\t\t\t\t\t\t@if (selectionMode === 'multiple') {\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\t[checked]=\"table.getIsAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t[indeterminate]=\"table.getIsSomeRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\t(change)=\"table.toggleAllRowsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t\taria-label=\"Select all rows\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t@for (header of headerGroup.headers; track header.id) {\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tclass=\"tan-grid__header\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sortable]=\"header.column.getCanSort()\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--sorted]=\"header.column.getIsSorted()\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-left]=\"isPinnedLeft(header.column.id)\"\n\t\t\t\t\t\t\t\t\t[class.tan-grid__header--pinned-right]=\"isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t[style.width]=\"header.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.min-width]=\"header.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.max-width]=\"header.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(header.column.id) ? getPinnedLeftOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(header.column.id) ? getPinnedRightOffset(header.column.id) : null\"\n\t\t\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (!header.isPlaceholder) {\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__header-content\">\n\t\t\t\t\t\t\t\t\t\t\t<!-- Pin Button -->\n\t\t\t\t\t\t\t\t\t\t\t@if (enablePinning && hasHorizontalScroll() && (canPinLeft(header.column.id) || canPinRight(header.column.id))) {\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pin-btn\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[class.active]=\"isPinnedLeft(header.column.id) || isPinnedRight(header.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"togglePin(header.column); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Toggle Pin\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"22\"></line><path d=\"M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z\"></path></svg>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanSort()) {\n\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"header.column.toggleSorting()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[attr.aria-sort]=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.getIsSorted() === 'asc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'ascending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: header.column.getIsSorted() === 'desc'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? 'descending'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: 'none'\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__sort-icons\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--up\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'asc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2191\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__sort-icon tan-grid__sort-icon--down\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__sort-icon--active]=\"header.column.getIsSorted() === 'desc'\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\u2193\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__header-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theader.column.columnDef.header;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprops: header.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlet headerValue\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{{ headerValue }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t@if (header.column.getCanResize()) {\n\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__resize-handle\"\n\t\t\t\t\t\t\t\t\t\t\t\t(mousedown)=\"onResizeStart($event, header)\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Resize column\"\n\t\t\t\t\t\t\t\t\t\t\t></div>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t\t\t<!-- Filter Row -->\n\t\t\t\t\t\t@if (filtering && hasFilterableColumns()) {\n\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--filter\">\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\"></td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\"></td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t@for (header of table.getHeaderGroups()[0].headers; track header.id) {\n\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--filter\">\n\t\t\t\t\t\t\t\t\t@if (header.column.getCanFilter()) {\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__filter-input\"\n\t\t\t\t\t\t\t\t\t\t\t[value]=\"header.column.getFilterValue() || ''\"\n\t\t\t\t\t\t\t\t\t\t\t(input)=\"onColumnFilterChange(header.column, $any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t[placeholder]=\"getFilterPlaceholder(header.column)\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Filter column\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t</thead>\n\t\t\t\t<tbody class=\"tan-grid__body\">\n\t\t\t\t\t@if (loading && loadingType === 'placeholder') {\n\t\t\t\t\t\t@for (row of [].constructor(placeholderRows); track $index) {\n\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--placeholder\">\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--select\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-checkbox\"></div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (col of table.getAllColumns(); track col.id; let i = $index) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__placeholder-cell\" [style.width.%]=\"getPlaceholderWidth(i)\"></div>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t}\n\t\t\t\t\t} @else if (table.getRowModel().rows.length === 0) {\n\t\t\t\t\t\t<tr class=\"tan-grid__row\">\n\t\t\t\t\t\t\t<td [attr.colspan]=\"table.getAllColumns().length + (selectionMode !== 'none' ? 1 : 0) + (expandable ? 1 : 0)\" class=\"tan-grid__empty\">\n\t\t\t\t\t\t\t\t@if (emptyTemplate) {\n\t\t\t\t\t\t\t\t\t<ng-container *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__empty-content\">\n\t\t\t\t\t\t\t\t\t\t<p>{{ emptyMessage }}</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t@for (row of table.getRowModel().rows; track row.id) {\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tclass=\"tan-grid__row\"\n\t\t\t\t\t\t\t\t[class.tan-grid__row--selected]=\"isRowSelected(row.id)\"\n\t\t\t\t\t\t\t\t[class.tan-grid__row--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t(click)=\"onRowClick(row, $index)\"\n\t\t\t\t\t\t\t\t(dblclick)=\"onRowDblClick(row, $index)\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t@if (expandable) {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--expand\">\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__expand-button\"\n\t\t\t\t\t\t\t\t\t\t\t[class.tan-grid__expand-button--expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t(click)=\"toggleRowExpansion(row); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-expanded]=\"isRowExpanded(row.id)\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"isRowExpanded(row.id) ? 'Collapse row' : 'Expand row'\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@if (selectionMode !== 'none') {\n\t\t\t\t\t\t\t\t\t<td class=\"tan-grid__cell tan-grid__cell--select\">\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t[checked]=\"row.getIsSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t(change)=\"row.toggleSelected()\"\n\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Select row ' + row.id\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t@for (cell of row.getVisibleCells(); track cell.id) {\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-left]=\"isPinnedLeft(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--pinned-right]=\"isPinnedRight(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editable]=\"editable && getColumnEditable(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__cell--editing]=\"isEditing(row.id, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t[style.width]=\"cell.column.getSize() + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.min-width]=\"cell.column.columnDef.minSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.max-width]=\"cell.column.columnDef.maxSize + 'px'\"\n\t\t\t\t\t\t\t\t\t\t[style.left.px]=\"isPinnedLeft(cell.column.id) ? getPinnedLeftOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t[style.right.px]=\"isPinnedRight(cell.column.id) ? getPinnedRightOffset(cell.column.id) : null\"\n\t\t\t\t\t\t\t\t\t\t(dblclick)=\"editable && getColumnEditable(cell.column.id) && startEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t@if (editable && getColumnEditable(cell.column.id) && isEditing(row.id, cell.column.id)) {\n\t\t\t\t\t\t\t\t\t\t\t<div class=\"tan-grid__cell-edit\">\n\t\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\t\t[type]=\"getColumnEditType(cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell-input\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t[value]=\"getEditingValue()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(input)=\"updateEditingValue($any($event.target).value)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.enter)=\"saveEdit(row, cell.column.id); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(keydown.escape)=\"cancelEdit(); $event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(blur)=\"saveEdit(row, cell.column.id)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t(click)=\"$event.stopPropagation()\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t\t*flexRender=\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tcell.column.columnDef.cell;\n\t\t\t\t\t\t\t\t\t\t\t\t\tprops: cell.getContext();\n\t\t\t\t\t\t\t\t\t\t\t\t\tlet cellValue\n\t\t\t\t\t\t\t\t\t\t\t\t\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<!-- FlexRender handles strings, TemplateRefs, and Components automatically -->\n\t\t\t\t\t\t\t\t\t\t\t\t{{ cellValue }}\n\t\t\t\t\t\t\t\t\t\t\t</ng-container>\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t@if (expandable && isRowExpanded(row.id) && expandedRowTemplate) {\n\t\t\t\t\t\t\t\t<tr class=\"tan-grid__row tan-grid__row--expanded-content\">\n\t\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\t\t[attr.colspan]=\"table.getAllColumns().length + (selectionMode !== 'none' ? 1 : 0) + (expandable ? 1 : 0)\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__cell tan-grid__cell--expanded\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<ng-container\n\t\t\t\t\t\t\t\t\t\t\t*ngTemplateOutlet=\"expandedRowTemplate; context: { $implicit: row.original, row: row.original }\"\n\t\t\t\t\t\t\t\t\t\t></ng-container>\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t\t}\n\t\t</div>\n\n\t\t<!-- Pagination -->\n\t\t@if (pagination && !virtualScroll) {\n\t\t\t<div class=\"tan-grid__pagination\">\n\t\t\t\t<div class=\"tan-grid__pagination-info\">\n\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t{{ getDisplayedRange().start }} - {{ getDisplayedRange().end }} of {{ getTotalItems() }} Items\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"tan-grid__pagination-controls\">\n\t\t\t\t\t@if (table.getPageCount() > 1) {\n\t\t\t\t\t\t<div class=\"tan-grid__pagination-pages\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn\"\n\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--disabled]=\"!table.getCanPreviousPage()\"\n\t\t\t\t\t\t\t\t[disabled]=\"!table.getCanPreviousPage()\"\n\t\t\t\t\t\t\t\t(click)=\"table.previousPage()\"\n\t\t\t\t\t\t\t\taria-label=\"Previous page\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t<path d=\"M10 12L6 8L10 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t@for (page of getPaginationPages(); track $index) {\n\t\t\t\t\t\t\t\t@if (page === 'ellipsis') {\n\t\t\t\t\t\t\t\t\t<span class=\"tan-grid__pagination-ellipsis\">...</span>\n\t\t\t\t\t\t\t\t} @else {\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn tan-grid__pagination-btn--page\"\n\t\t\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--active]=\"page === getCurrentPage()\"\n\t\t\t\t\t\t\t\t\t\t(click)=\"goToPage(page)\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-label]=\"'Go to page ' + page\"\n\t\t\t\t\t\t\t\t\t\t[attr.aria-current]=\"page === getCurrentPage() ? 'page' : null\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{{ page }}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"tan-grid__pagination-btn\"\n\t\t\t\t\t\t\t\t[class.tan-grid__pagination-btn--disabled]=\"!table.getCanNextPage()\"\n\t\t\t\t\t\t\t\t[disabled]=\"!table.getCanNextPage()\"\n\t\t\t\t\t\t\t\t(click)=\"table.nextPage()\"\n\t\t\t\t\t\t\t\taria-label=\"Next page\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t\t\t<path d=\"M6 4L10 8L6 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t}\n\t\t\t\t\t<div class=\"tan-grid__pagination-size\">\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclass=\"tan-grid__page-size-select\"\n\t\t\t\t\t\t\t[value]=\"pageSizeSignal()\"\n\t\t\t\t\t\t\t(change)=\"onPageSizeChange(+$any($event.target).value)\"\n\t\t\t\t\t\t\taria-label=\"Items per page\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t@for (size of pageSizeOptions; track size) {\n\t\t\t\t\t\t\t\t<option [value]=\"size\">{{ size }} / page</option>\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t}\n\t</div>\n\n\n", styles: [":root,[data-ngs-theme=light]{--ngs-primary: #007bff;--ngs-primary-hover: #0056b3;--ngs-primary-active: #004085;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #004085;--ngs-secondary: #6c757d;--ngs-secondary-hover: #5a6268;--ngs-secondary-active: #484f54;--ngs-secondary-light: #e2e3e5;--ngs-secondary-dark: #383d41;--ngs-success: #28a745;--ngs-success-hover: #218838;--ngs-success-active: #1e7e34;--ngs-success-light: #d4edda;--ngs-success-dark: #155724;--ngs-danger: #dc3545;--ngs-danger-hover: #c82333;--ngs-danger-active: #bd2130;--ngs-danger-light: #f8d7da;--ngs-danger-dark: #721c24;--ngs-warning: #ffc107;--ngs-warning-hover: #e0a800;--ngs-warning-active: #d39e00;--ngs-warning-light: #fff3cd;--ngs-warning-dark: #856404;--ngs-info: #17a2b8;--ngs-info-hover: #138496;--ngs-info-active: #117a8b;--ngs-info-light: #d1ecf1;--ngs-info-dark: #0c5460;--ngs-text-primary: #212529;--ngs-text-secondary: #6c757d;--ngs-text-muted: #868e96;--ngs-text-inverse: #ffffff;--ngs-bg-primary: #ffffff;--ngs-bg-secondary: #f8f9fa;--ngs-bg-tertiary: #e9ecef;--ngs-bg-dark: #212529;--ngs-border-color: #dee2e6;--ngs-border-color-light: #e9ecef;--ngs-border-color-dark: #adb5bd;--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-border-radius-xl: 0;--ngs-border-radius-pill: 50rem;--ngs-border-radius-circle: 50%;--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-xs: .75rem;--ngs-font-size-sm: .8125rem;--ngs-font-size-base: .875rem;--ngs-font-size-lg: 1rem;--ngs-font-size-xl: 1.125rem;--ngs-font-size-2xl: 1.25rem;--ngs-font-size-3xl: 1.5rem;--ngs-font-size-4xl: 2rem;--ngs-font-weight-light: 300;--ngs-font-weight-normal: 400;--ngs-font-weight-medium: 500;--ngs-font-weight-semibold: 600;--ngs-font-weight-bold: 700;--ngs-line-height-tight: 1.25;--ngs-line-height-normal: 1.5;--ngs-line-height-relaxed: 1.75;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-transition-base: all .2s ease-in-out;--ngs-transition-fast: all .15s ease-in-out;--ngs-transition-slow: all .3s ease-in-out;--ngs-z-index-dropdown: 1000;--ngs-z-index-sticky: 1020;--ngs-z-index-fixed: 1030;--ngs-z-index-modal-backdrop: 1040;--ngs-z-index-modal: 1050;--ngs-z-index-popover: 1060;--ngs-z-index-tooltip: 1070}[data-ngs-theme=dark]{--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #031633;--ngs-primary-dark: #0a58ca;--ngs-secondary: #6c757d;--ngs-secondary-hover: #5c636a;--ngs-secondary-active: #565e64;--ngs-secondary-light: #41464b;--ngs-secondary-dark: #adb5bd;--ngs-success: #198754;--ngs-success-hover: #157347;--ngs-success-active: #146c43;--ngs-success-light: #0f5132;--ngs-success-dark: #75b798;--ngs-danger: #dc3545;--ngs-danger-hover: #bb2d3b;--ngs-danger-active: #b02a37;--ngs-danger-light: #842029;--ngs-danger-dark: #ea868f;--ngs-warning: #ffc107;--ngs-warning-hover: #ffca2c;--ngs-warning-active: #ffcd39;--ngs-warning-light: #664d03;--ngs-warning-dark: #ffcd39;--ngs-info: #0dcaf0;--ngs-info-hover: #31d2f2;--ngs-info-active: #3dd5f3;--ngs-info-light: #055160;--ngs-info-dark: #6edff6;--ngs-text-primary: #ffffff;--ngs-text-secondary: #adb5bd;--ngs-text-muted: #868e96;--ngs-text-inverse: #212529;--ngs-bg-primary: #212529;--ngs-bg-secondary: #343a40;--ngs-bg-tertiary: #495057;--ngs-bg-dark: #121416;--ngs-border-color: #495057;--ngs-border-color-light: #343a40;--ngs-border-color-dark: #6c757d}.tan-grid__wrapper{display:flex;flex-direction:column;gap:var(--ngs-spacing-md)}.tan-grid__toolbar{display:flex;align-items:center;justify-content:space-between;gap:var(--ngs-spacing-md);margin-bottom:var(--ngs-spacing-md);flex-wrap:wrap}.tan-grid__search{flex:1;min-width:200px}.tan-grid__search-input{width:100%;max-width:300px;padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);font-size:var(--ngs-font-size-base);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__search-input:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__export{display:flex;align-items:center;gap:var(--ngs-spacing-xs);flex-wrap:wrap}.tan-grid__export-btn{display:flex;align-items:center;gap:var(--ngs-spacing-xs);padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);background-color:var(--ngs-bg-primary);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);color:var(--ngs-text-primary);font-size:var(--ngs-font-size-sm);font-weight:var(--ngs-font-weight-medium);cursor:pointer;transition:var(--ngs-transition-base)}.tan-grid__export-btn:hover{background-color:var(--ngs-primary);border-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__export-btn:active{transform:translateY(1px)}.tan-grid__export-btn span:first-child{font-size:var(--ngs-font-size-base)}.tan-grid__container{overflow-x:auto;overflow-y:visible;border-radius:var(--ngs-border-radius);position:relative;min-height:200px}.tan-grid__container--virtual{overflow-y:hidden}.tan-grid{width:100%;border-collapse:collapse;font-size:var(--ngs-font-size-base);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary)}.tan-grid:has(.tan-grid__header--pinned-left),.tan-grid:has(.tan-grid__header--pinned-right){min-width:max-content;width:max-content}.tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:var(--ngs-bg-secondary)}.tan-grid--bordered,.tan-grid--bordered .tan-grid__cell,.tan-grid--bordered .tan-grid__header{border:1px solid var(--ngs-border-color)}.tan-grid--hoverable .tan-grid__body .tan-grid__row{transition:var(--ngs-transition-base)}.tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:var(--ngs-bg-secondary);cursor:pointer}.tan-grid--compact .tan-grid__cell,.tan-grid--compact .tan-grid__header{padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm)}.tan-grid__head{background-color:var(--ngs-bg-secondary);border:none}.tan-grid__head .tan-grid__row{border:none}.tan-grid__header{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);text-align:left;font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);position:relative;white-space:nowrap;overflow:visible}.tan-grid__header--select,.tan-grid__header--expand{width:40px;text-align:center}.tan-grid__header--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.tan-grid__header--sortable:hover,.tan-grid__header--sorted{background-color:var(--ngs-bg-tertiary)}.tan-grid__resize-handle{position:absolute;right:-2px;top:0;height:100%;width:5px;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;z-index:10;background:transparent;will-change:background-color;transition:background-color .15s ease}.tan-grid__resize-handle:hover{background-color:var(--ngs-primary);opacity:.6}.tan-grid__resize-handle:active{background-color:var(--ngs-primary);opacity:1}.tan-grid__header-content{display:flex;align-items:center;gap:var(--ngs-spacing-xs)}.tan-grid__header-text{flex:1}.tan-grid__sort-button{display:flex;align-items:center;justify-content:space-between;width:100%;gap:var(--ngs-spacing-xs);background:transparent;border:none;padding:0;cursor:pointer;color:inherit;font:inherit;text-align:left}.tan-grid__sort-icons{display:flex;flex-direction:row;gap:0;font-size:var(--ngs-font-size-xs);line-height:1;margin-left:var(--ngs-spacing-xs)}.tan-grid__sort-icon{opacity:.3;transition:var(--ngs-transition-base);display:inline-block;width:10px;text-align:center;line-height:.8}.tan-grid__sort-icon--up{margin-right:-2px}.tan-grid__sort-icon--down{margin-left:-2px}.tan-grid__sort-icon--active{opacity:1;color:var(--ngs-primary);font-weight:var(--ngs-font-weight-bold)}.tan-grid__body .tan-grid__row--selected{background-color:var(--ngs-primary-light)!important}.tan-grid__row--filter,.tan-grid__row--expanded,.tan-grid__row--expanded-content{background-color:var(--ngs-bg-secondary)}.tan-grid__cell{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md)}.tan-grid__cell--filter{padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm);vertical-align:middle}.tan-grid__cell--expand,.tan-grid__cell--select{width:40px;text-align:center;padding:var(--ngs-spacing-sm)}.tan-grid__cell--expanded{padding:var(--ngs-spacing-lg);background-color:var(--ngs-bg-secondary);border-top:2px solid var(--ngs-primary)}.tan-grid__filter-input{width:100%;padding:var(--ngs-spacing-xs) var(--ngs-spacing-sm);font-size:var(--ngs-font-size-sm);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__filter-input:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__filter-input::placeholder{color:var(--ngs-text-secondary);opacity:.6}.tan-grid__empty{padding:var(--ngs-spacing-2xl);text-align:center;color:var(--ngs-text-secondary)}.tan-grid--virtual{display:flex;flex-direction:column;border-collapse:separate;border-spacing:0;width:100%}.tan-grid--virtual:has(.tan-grid__header--pinned-left),.tan-grid--virtual:has(.tan-grid__header--pinned-right){min-width:100%;width:max-content}.tan-grid__head--virtual{display:flex;flex-direction:column;background-color:var(--ngs-bg-secondary);flex-shrink:0}.tan-grid__row--header,.tan-grid__row--virtual{display:flex;width:100%;box-sizing:border-box;flex-shrink:0;flex-grow:0}.tan-grid--virtual:has(.tan-grid__header--pinned-left) .tan-grid__row--header,.tan-grid--virtual:has(.tan-grid__header--pinned-right) .tan-grid__row--header,.tan-grid--virtual:has(.tan-grid__header--pinned-left) .tan-grid__row--virtual,.tan-grid--virtual:has(.tan-grid__header--pinned-right) .tan-grid__row--virtual{min-width:max-content}.tan-grid__header--virtual,.tan-grid__cell--virtual{display:flex;align-items:center;flex-shrink:0;box-sizing:border-box}.tan-grid__header--virtual{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);text-align:left;font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);position:relative;white-space:nowrap;overflow:visible;border-right:1px solid var(--ngs-border-color);border-bottom:1px solid var(--ngs-border-color)}.tan-grid__header--virtual:last-child{border-right:none}.tan-grid__cell--virtual{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);border-right:1px solid var(--ngs-border-color);border-bottom:1px solid var(--ngs-border-color);box-sizing:border-box;flex-shrink:0;height:100%}.tan-grid__cell--virtual:last-child{border-right:none}.tan-grid__body--virtual{display:flex;flex-direction:column}.tan-grid__virtual-viewport{width:100%;overflow:auto;display:block;position:relative;contain:layout style paint}.tan-grid__virtual-viewport .cdk-virtual-scroll-content-wrapper{display:block;box-sizing:border-box;width:100%}.tan-grid__virtual-viewport .cdk-virtual-scroll-spacer{box-sizing:border-box;display:block;width:1px;pointer-events:none}.tan-grid--striped .tan-grid__row--virtual:nth-child(2n){background-color:var(--ngs-bg-secondary)}.tan-grid--hoverable .tan-grid__row--virtual{transition:var(--ngs-transition-base)}.tan-grid--hoverable .tan-grid__row--virtual:hover{background-color:var(--ngs-bg-secondary);cursor:pointer}.tan-grid__empty--virtual{display:flex;align-items:center;justify-content:center;padding:var(--ngs-spacing-2xl);text-align:center;color:var(--ngs-text-secondary);width:100%}.tan-grid__empty-content p{margin:0;font-size:var(--ngs-font-size-base)}.tan-grid__loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:#ffffffb3;z-index:50;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.tan-grid__loading{display:flex;align-items:center;justify-content:center;min-height:200px;padding:var(--ngs-spacing-2xl)}.tan-grid__loading-content{display:flex;flex-direction:column;align-items:center;gap:var(--ngs-spacing-md)}.tan-grid__loading-content p{margin:0;color:var(--ngs-text-secondary)}.tan-grid__spinner{width:40px;height:40px;border:3px solid var(--ngs-border-color);border-top-color:var(--ngs-primary);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.tan-grid__pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--ngs-spacing-lg);padding:var(--ngs-spacing-md);border-top:1px solid var(--ngs-border-color);flex-wrap:wrap}@media (max-width: 768px){.tan-grid__pagination{flex-direction:column;align-items:stretch}}.tan-grid__pagination-info{display:flex;align-items:center;font-size:var(--ngs-font-size-sm);color:var(--ngs-text-secondary)}.tan-grid__pagination-controls{display:flex;align-items:center;gap:var(--ngs-spacing-lg)}.tan-grid__pagination-pages{display:flex;align-items:center;gap:var(--ngs-spacing-xs)}.tan-grid__pagination-btn{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--ngs-spacing-sm);font-size:var(--ngs-font-size-sm);line-height:1;border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);cursor:pointer;transition:var(--ngs-transition-base);-webkit-user-select:none;user-select:none;font-weight:var(--ngs-font-weight-normal)}.tan-grid__pagination-btn:hover:not(:disabled):not(.tan-grid__pagination-btn--active){border-color:var(--ngs-primary);color:var(--ngs-primary);background-color:var(--ngs-bg-secondary)}.tan-grid__pagination-btn:active:not(:disabled){transform:scale(.98)}.tan-grid__pagination-btn:disabled,.tan-grid__pagination-btn--disabled{opacity:.4;cursor:not-allowed;pointer-events:none;background-color:var(--ngs-bg-secondary)}.tan-grid__pagination-btn--page{font-weight:var(--ngs-font-weight-normal)}.tan-grid__pagination-btn--active{border-color:var(--ngs-primary);background-color:var(--ngs-primary);color:var(--ngs-text-inverse);font-weight:var(--ngs-font-weight-medium);cursor:default;box-shadow:0 1px 2px #0000001a}.tan-grid__pagination-btn--active:hover{border-color:var(--ngs-primary);background-color:var(--ngs-primary-hover);color:var(--ngs-text-inverse)}.tan-grid__pagination-btn svg{width:16px;height:16px}.tan-grid__pagination-ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 var(--ngs-spacing-xs);color:var(--ngs-text-secondary);font-size:var(--ngs-font-size-sm);-webkit-user-select:none;user-select:none}.tan-grid__pagination-size{display:flex;align-items:center}.tan-grid__page-size-select{padding:var(--ngs-spacing-xs) var(--ngs-spacing-md);padding-right:calc(var(--ngs-spacing-md) + 20px);font-size:var(--ngs-font-size-sm);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);cursor:pointer;transition:var(--ngs-transition-base);appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right var(--ngs-spacing-sm) center;background-size:12px;height:32px}.tan-grid__page-size-select:hover{border-color:var(--ngs-primary)}.tan-grid__page-size-select:focus{outline:none;border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__row--placeholder .tan-grid__cell{padding:var(--ngs-spacing-sm) var(--ngs-spacing-md)}.tan-grid__placeholder-cell,.tan-grid__placeholder-checkbox{height:14px;background:linear-gradient(90deg,#f0f0f0 0% 40%,#e0e0e0 50%,#f0f0f0 60% 100%);background-size:200% 100%;animation:shimmer 1.2s ease-in-out infinite;border-radius:0;position:relative;overflow:hidden}.tan-grid__placeholder-checkbox{width:16px;height:16px;margin:0 auto;border-radius:0}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.tan-grid__header--pinned-left,.tan-grid__cell--pinned-left{position:sticky;left:0;z-index:10;background-color:var(--ngs-bg-primary)}.tan-grid__header--pinned-left:after,.tan-grid__cell--pinned-left:after{content:\"\";position:absolute;right:0;top:0;bottom:0;width:1px;background-color:var(--ngs-border-color);box-shadow:2px 0 4px #0000001a}.tan-grid__header--pinned-left{background-color:var(--ngs-bg-secondary);z-index:11}.tan-grid__header--pinned-right,.tan-grid__cell--pinned-right{position:sticky;right:0;z-index:10;background-color:var(--ngs-bg-primary)}.tan-grid__header--pinned-right:before,.tan-grid__cell--pinned-right:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:1px;background-color:var(--ngs-border-color);box-shadow:-2px 0 4px #0000001a}.tan-grid__header--pinned-right{background-color:var(--ngs-bg-secondary);z-index:11}.tan-grid--striped .tan-grid__row:nth-child(2n) .tan-grid__cell--pinned-left,.tan-grid--striped .tan-grid__row:nth-child(2n) .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid--striped .tan-grid__row--virtual:nth-child(2n) .tan-grid__cell--pinned-left,.tan-grid--striped .tan-grid__row--virtual:nth-child(2n) .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid--hoverable .tan-grid__row:hover .tan-grid__cell--pinned-left,.tan-grid--hoverable .tan-grid__row:hover .tan-grid__cell--pinned-right,.tan-grid--hoverable .tan-grid__row--virtual:hover .tan-grid__cell--pinned-left,.tan-grid--hoverable .tan-grid__row--virtual:hover .tan-grid__cell--pinned-right{background-color:var(--ngs-bg-secondary)}.tan-grid__cell--editable{cursor:pointer;position:relative}.tan-grid__cell--editable:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__cell--editing{background-color:var(--ngs-primary-light);padding:0}.tan-grid__cell-edit{width:100%;height:100%;display:flex;align-items:center}.tan-grid__cell-input{width:100%;padding:var(--ngs-spacing-sm) var(--ngs-spacing-md);border:2px solid var(--ngs-primary);border-radius:var(--ngs-border-radius-sm);background-color:var(--ngs-bg-primary);color:var(--ngs-text-primary);font-size:var(--ngs-font-size-base);font-family:inherit;outline:none;box-sizing:border-box}.tan-grid__cell-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__cell-input::placeholder{color:var(--ngs-text-secondary);opacity:.6}.tan-grid__expand-button{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;background:transparent;border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);cursor:pointer;color:var(--ngs-text-primary);transition:var(--ngs-transition-base)}.tan-grid__expand-button svg{transition:transform .2s ease}.tan-grid__expand-button:hover{background-color:var(--ngs-bg-secondary);border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__expand-button:focus{outline:none;box-shadow:0 0 0 2px var(--ngs-primary-light)}.tan-grid__expand-button--expanded{background-color:var(--ngs-primary);border-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__expand-button--expanded svg{transform:rotate(90deg)}.tan-grid__expand-button--expanded:hover{background-color:var(--ngs-primary-dark);border-color:var(--ngs-primary-dark)}.tan-grid__wrapper.tan-grid--theme-material{--ngs-border-radius: .25rem;--ngs-border-radius-sm: .125rem;--ngs-border-radius-lg: .375rem;--ngs-primary: #6200ee;--ngs-primary-hover: #3700b3;--ngs-primary-active: #000000;--ngs-primary-light: #e0d4f7;--ngs-primary-dark: #3700b3;--ngs-shadow-sm: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);--ngs-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 3px 6px rgba(0, 0, 0, .23);--ngs-shadow-lg: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);--ngs-spacing-xs: .25rem;--ngs-spacing-sm: .5rem;--ngs-spacing-md: .75rem;--ngs-spacing-lg: 1rem;--ngs-spacing-xl: 1.5rem;--ngs-spacing-2xl: 2rem;--ngs-spacing-3xl: 3rem;--ngs-font-family: \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--ngs-font-size-base: .875rem;--ngs-font-weight-medium: 500;box-shadow:var(--ngs-shadow-sm);border-radius:var(--ngs-border-radius);overflow:hidden;background-color:var(--ngs-bg-primary)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid{border:none;border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered{border:none}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-material .tan-grid--bordered .tan-grid__header{border:none;border-bottom:1px solid var(--ngs-border-color-light)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#00000005}.tan-grid__wrapper.tan-grid--theme-material .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__head{background-color:var(--ngs-bg-primary);box-shadow:0 2px 4px #00000014}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000de;letter-spacing:.01785714em}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header--sortable:hover{background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__header--sorted{background-color:#6200ee14;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000de}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-material .tan-grid__filter-input{border-radius:var(--ngs-border-radius);border:1px solid var(--ngs-border-color);transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-material .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #6200ee33}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__export-btn{border-radius:var(--ngs-border-radius);text-transform:uppercase;font-weight:var(--ngs-font-weight-medium);letter-spacing:.0892857143em;transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__export-btn:hover{box-shadow:var(--ngs-shadow-sm)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn{border-radius:50%;min-width:40px;height:40px;transition:all .2s ease-in-out}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn:hover:not(.tan-grid__pagination-btn--active){background-color:#0000000a}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-material .tan-grid__checkbox{border-radius:2px}.tan-grid.tan-grid--theme-material{--ngs-border-radius: .25rem;--ngs-border-radius-sm: .125rem;--ngs-border-radius-lg: .375rem;--ngs-primary: #6200ee;--ngs-primary-hover: #3700b3;--ngs-primary-active: #000000;--ngs-primary-light: #e0d4f7;--ngs-primary-dark: #3700b3;--ngs-shadow-sm: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);--ngs-shadow: 0 3px 6px rgba(0, 0, 0, .16), 0 3px 6px rgba(0, 0, 0, .23);--ngs-shadow-lg: 0 10px 20px rgba(0, 0, 0, .19), 0 6px 6px rgba(0, 0, 0, .23);--ngs-spacing-xs: .25rem;--ngs-spacing-sm: .5rem;--ngs-spacing-md: .75rem;--ngs-spacing-lg: 1rem;--ngs-spacing-xl: 1.5rem;--ngs-spacing-2xl: 2rem;--ngs-spacing-3xl: 3rem;--ngs-font-family: \"Roboto\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;--ngs-font-size-base: .875rem;--ngs-font-weight-medium: 500;border:none;border-radius:var(--ngs-border-radius)}.tan-grid.tan-grid--theme-material--bordered{border:none}.tan-grid.tan-grid--theme-material--bordered .tan-grid__cell,.tan-grid.tan-grid--theme-material--bordered .tan-grid__header{border:none;border-bottom:1px solid var(--ngs-border-color-light)}.tan-grid__wrapper.tan-grid--theme-bootstrap{--ngs-border-radius: .375rem;--ngs-border-radius-sm: .25rem;--ngs-border-radius-lg: .5rem;--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #084298;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-base: 1rem}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid{border-collapse:separate;border-spacing:0}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered{border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--bordered .tan-grid__header{border:1px solid var(--ngs-border-color)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#0000000d}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#00000013}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__head{background-color:var(--ngs-bg-secondary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-semibold);color:var(--ngs-text-primary);vertical-align:bottom}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header--sortable:hover{background-color:var(--ngs-bg-tertiary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__header--sorted{background-color:var(--ngs-primary-light);color:var(--ngs-primary-dark)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:var(--ngs-text-primary);vertical-align:top}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__filter-input{border-radius:var(--ngs-border-radius);border:1px solid var(--ngs-border-color);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__filter-input:focus{border-color:#86b7fe;box-shadow:0 0 0 .25rem #0d6efd40}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn{border-radius:var(--ngs-border-radius);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn:hover{background-color:var(--ngs-primary-hover)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__export-btn:active{background-color:var(--ngs-primary-active)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn{border-radius:var(--ngs-border-radius);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-bootstrap .tan-grid__checkbox{border-radius:var(--ngs-border-radius-sm)}.tan-grid.tan-grid--theme-bootstrap{--ngs-border-radius: .375rem;--ngs-border-radius-sm: .25rem;--ngs-border-radius-lg: .5rem;--ngs-primary: #0d6efd;--ngs-primary-hover: #0b5ed7;--ngs-primary-active: #0a58ca;--ngs-primary-light: #cfe2ff;--ngs-primary-dark: #084298;--ngs-shadow-sm: 0 .125rem .25rem rgba(0, 0, 0, .075);--ngs-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, .175);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;--ngs-font-size-base: 1rem;border-collapse:separate;border-spacing:0}.tan-grid.tan-grid--theme-bootstrap--bordered{border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius)}.tan-grid__wrapper.tan-grid--theme-ant{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid{border:1px solid #f0f0f0;border-radius:0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--bordered .tan-grid__header{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#f5f5f5}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__head{background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000d9;background-color:#fafafa;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header--sortable:hover{background-color:#e6f7ff}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__header--sorted{background-color:#e6f7ff;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000d9;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__row:last-child .tan-grid__cell{border-bottom:none}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input:hover,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input:hover{border-color:#40a9ff}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #1890ff33}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__export-btn:active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease;min-width:32px;height:32px}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant .tan-grid__checkbox{border-radius:0}.tan-grid.tan-grid--theme-ant{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem;border:1px solid #f0f0f0;border-radius:0}.tan-grid.tan-grid--theme-ant--bordered{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid{border:1px solid #f0f0f0;border-radius:0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered{border:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered .tan-grid__cell,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--bordered .tan-grid__header{border:none;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--striped .tan-grid__body .tan-grid__row:nth-child(2n){background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid--hoverable .tan-grid__body .tan-grid__row:hover{background-color:#f5f5f5}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__head{background-color:#fafafa}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);font-weight:var(--ngs-font-weight-medium);color:#000000d9;background-color:#fafafa;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header--sortable:hover{background-color:#e6f7ff}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__header--sorted{background-color:#e6f7ff;color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__cell{padding:var(--ngs-spacing-md) var(--ngs-spacing-lg);color:#000000d9;border-bottom:1px solid #f0f0f0}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__row:last-child .tan-grid__cell{border-bottom:none}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input:hover,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input:hover{border-color:#40a9ff}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__search-input:focus,.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__filter-input:focus{border-color:var(--ngs-primary);box-shadow:0 0 0 2px #1890ff33}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__export-btn:active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn{border-radius:0;border:1px solid #d9d9d9;transition:all .3s ease;min-width:32px;height:32px}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn:hover{border-color:var(--ngs-primary);color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__pagination-btn--active{background-color:var(--ngs-primary);color:var(--ngs-text-inverse);border-color:var(--ngs-primary)}.tan-grid__wrapper.tan-grid--theme-ant-alt .tan-grid__checkbox{border-radius:0}.tan-grid.tan-grid--theme-ant-alt{--ngs-border-radius: 0;--ngs-border-radius-sm: 0;--ngs-border-radius-lg: 0;--ngs-primary: #1890ff;--ngs-primary-hover: #40a9ff;--ngs-primary-active: #096dd9;--ngs-primary-light: #e6f7ff;--ngs-primary-dark: #0050b3;--ngs-shadow-sm: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow: 0 2px 8px rgba(0, 0, 0, .15);--ngs-shadow-lg: 0 4px 12px rgba(0, 0, 0, .15);--ngs-spacing-xs: .125rem;--ngs-spacing-sm: .25rem;--ngs-spacing-md: .5rem;--ngs-spacing-lg: .75rem;--ngs-spacing-xl: 1rem;--ngs-spacing-2xl: 1.5rem;--ngs-spacing-3xl: 2rem;--ngs-font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;--ngs-font-size-base: .875rem;border:1px solid #f0f0f0;border-radius:0}.tan-grid.tan-grid--theme-ant-alt--bordered{border:1px solid #f0f0f0}.tan-grid.tan-grid--theme-ant-alt--bordered .tan-grid__cell,.tan-grid.tan-grid--theme-ant-alt--bordered .tan-grid__header{border:none;border-bottom:1px solid #f0f0f0}.tan-grid__pin-btn{display:flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:.25rem;margin-right:.25rem;border-radius:4px;color:var(--ngs-text-secondary);transition:var(--ngs-transition-base)}.tan-grid__pin-btn:hover{background-color:var(--ngs-bg-secondary);color:var(--ngs-text-primary)}.tan-grid__pin-btn.active{color:var(--ngs-primary);background-color:var(--ngs-primary-light)}.tan-grid__menu{background-color:var(--ngs-bg-primary);border:1px solid var(--ngs-border-color);border-radius:var(--ngs-border-radius);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;padding:.25rem;display:flex;flex-direction:column;min-width:150px;z-index:1000}.tan-grid__menu-item{display:flex;align-items:center;width:100%;padding:.5rem .75rem;font-size:var(--ngs-font-size-sm);color:var(--ngs-text-primary);background:transparent;border:none;cursor:pointer;text-align:left;border-radius:var(--ngs-border-radius)}.tan-grid__menu-item:hover{background-color:var(--ngs-bg-secondary)}.tan-grid__menu-icon{width:1.5rem;display:flex;align-items:center;justify-content:center;color:var(--ngs-primary);font-weight:700}\n"] }]
1480
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }], propDecorators: { data: [{
1481
+ type: Input
1482
+ }], columns: [{
1483
+ type: Input
1484
+ }], loading: [{
1485
+ type: Input
1486
+ }], striped: [{
1487
+ type: Input
1488
+ }], bordered: [{
1489
+ type: Input
1490
+ }], hoverable: [{
1491
+ type: Input
1492
+ }], compact: [{
1493
+ type: Input
1494
+ }], theme: [{
1495
+ type: Input
1496
+ }], selectionMode: [{
1497
+ type: Input
1498
+ }], selectedRowIds: [{
1499
+ type: Input
1500
+ }], rowIdFn: [{
1501
+ type: Input
1502
+ }], pagination: [{
1503
+ type: Input
1504
+ }], pageSize: [{
1505
+ type: Input
1506
+ }], pageSizeOptions: [{
1507
+ type: Input
1508
+ }], paginationMode: [{
1509
+ type: Input
1510
+ }], totalItems: [{
1511
+ type: Input
1512
+ }], sorting: [{
1513
+ type: Input
1514
+ }], sortingMode: [{
1515
+ type: Input
1516
+ }], filtering: [{
1517
+ type: Input
1518
+ }], filteringMode: [{
1519
+ type: Input
1520
+ }], globalSearch: [{
1521
+ type: Input
1522
+ }], showGlobalSearchInput: [{
1523
+ type: Input
1524
+ }], globalFilter: [{
1525
+ type: Input
1526
+ }], emptyMessage: [{
1527
+ type: Input
1528
+ }], loadingMessage: [{
1529
+ type: Input
1530
+ }], loadingType: [{
1531
+ type: Input
1532
+ }], placeholderRows: [{
1533
+ type: Input
1534
+ }], virtualScroll: [{
1535
+ type: Input
1536
+ }], rowHeight: [{
1537
+ type: Input
1538
+ }], virtualScrollBufferSize: [{
1539
+ type: Input
1540
+ }], virtualScrollViewportHeight: [{
1541
+ type: Input
1542
+ }], pinnedLeft: [{
1543
+ type: Input
1544
+ }], pinnedRight: [{
1545
+ type: Input
1546
+ }], enablePinning: [{
1547
+ type: Input
1548
+ }], reorderable: [{
1549
+ type: Input
1550
+ }], emptyTemplate: [{
1551
+ type: Input
1552
+ }], loadingTemplate: [{
1553
+ type: Input
1554
+ }], expandable: [{
1555
+ type: Input
1556
+ }], expandedRowTemplate: [{
1557
+ type: Input
1558
+ }], expandedRowIds: [{
1559
+ type: Input
1560
+ }], editable: [{
1561
+ type: Input
1562
+ }], exportable: [{
1563
+ type: Input
1564
+ }], exportFormats: [{
1565
+ type: Input
1566
+ }], rowClick: [{
1567
+ type: Output
1568
+ }], rowDblClick: [{
1569
+ type: Output
1570
+ }], selectionChange: [{
1571
+ type: Output
1572
+ }], sortChange: [{
1573
+ type: Output
1574
+ }], paginationChange: [{
1575
+ type: Output
1576
+ }], filterChange: [{
1577
+ type: Output
1578
+ }], expansionChange: [{
1579
+ type: Output
1580
+ }], pinChange: [{
1581
+ type: Output
1582
+ }], cellEditChange: [{
1583
+ type: Output
1584
+ }], columnOrderChange: [{
1585
+ type: Output
1586
+ }], tableContainer: [{
1587
+ type: ViewChild,
1588
+ args: ['tableContainer']
1589
+ }], virtualScrollViewport: [{
1590
+ type: ViewChild,
1591
+ args: ['virtualScrollViewport']
1592
+ }], headerRowContainer: [{
1593
+ type: ViewChild,
1594
+ args: ['headerRowContainer']
1595
+ }] } });
1596
+
1597
+ /**
1598
+ * Table module
1599
+ */
1600
+ class TanGridModule {
1601
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.7", ngImport: i0, type: TanGridModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1602
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.7", ngImport: i0, type: TanGridModule, imports: [TanGrid], exports: [TanGrid] }); }
1603
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.7", ngImport: i0, type: TanGridModule, imports: [TanGrid] }); }
1604
+ }
1605
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.7", ngImport: i0, type: TanGridModule, decorators: [{
1606
+ type: NgModule,
1607
+ args: [{
1608
+ imports: [TanGrid],
1609
+ exports: [TanGrid],
1610
+ }]
1611
+ }] });
1612
+
1613
+ /**
1614
+ * Generated bundle index. Do not edit.
1615
+ */
1616
+
1617
+ export { TanGrid, TanGridModule };
1618
+ //# sourceMappingURL=tangrid-table.mjs.map