@nova-design-system/nova-angular-19 3.20.0 → 3.21.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.
@@ -4,7 +4,7 @@ import { __decorate } from 'tslib';
4
4
  import { fromEvent, BehaviorSubject } from 'rxjs';
5
5
  import * as i2 from '@angular/common';
6
6
  import { CommonModule } from '@angular/common';
7
- import { memo, createTable, getCoreRowModel, getPaginationRowModel } from '@tanstack/table-core';
7
+ import { memo, createTable, getCoreRowModel, getSortedRowModel, getPaginationRowModel } from '@tanstack/table-core';
8
8
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
9
9
  export * from '@nova-design-system/nova-webcomponents/constants';
10
10
 
@@ -252,11 +252,11 @@ let NvButton = class NvButton {
252
252
  this.el = r.nativeElement;
253
253
  }
254
254
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvButton, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
255
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.5", type: NvButton, isStandalone: false, selector: "nv-button", inputs: { active: "active", danger: "danger", disabled: "disabled", emphasis: "emphasis", fluid: "fluid", form: "form", loading: "loading", size: "size", type: "type" }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
255
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.5", type: NvButton, isStandalone: false, selector: "nv-button", inputs: { active: "active", danger: "danger", disableTabindex: "disableTabindex", disabled: "disabled", emphasis: "emphasis", fluid: "fluid", form: "form", loading: "loading", size: "size", type: "type" }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
256
256
  };
257
257
  NvButton = __decorate([
258
258
  ProxyCmp({
259
- inputs: ['active', 'danger', 'disabled', 'emphasis', 'fluid', 'form', 'loading', 'size', 'type']
259
+ inputs: ['active', 'danger', 'disableTabindex', 'disabled', 'emphasis', 'fluid', 'form', 'loading', 'size', 'type']
260
260
  })
261
261
  ], NvButton);
262
262
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvButton, decorators: [{
@@ -266,7 +266,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
266
266
  changeDetection: ChangeDetectionStrategy.OnPush,
267
267
  template: '<ng-content></ng-content>',
268
268
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
269
- inputs: ['active', 'danger', 'disabled', 'emphasis', 'fluid', 'form', 'loading', 'size', 'type'],
269
+ inputs: ['active', 'danger', 'disableTabindex', 'disabled', 'emphasis', 'fluid', 'form', 'loading', 'size', 'type'],
270
270
  standalone: false
271
271
  }]
272
272
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }] });
@@ -902,11 +902,11 @@ let NvIconbutton = class NvIconbutton {
902
902
  this.el = r.nativeElement;
903
903
  }
904
904
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvIconbutton, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
905
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.5", type: NvIconbutton, isStandalone: false, selector: "nv-iconbutton", inputs: { active: "active", disabled: "disabled", emphasis: "emphasis", loading: "loading", name: "name", shape: "shape", size: "size", type: "type" }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
905
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.5", type: NvIconbutton, isStandalone: false, selector: "nv-iconbutton", inputs: { active: "active", disableTabindex: "disableTabindex", disabled: "disabled", emphasis: "emphasis", loading: "loading", name: "name", shape: "shape", size: "size", type: "type" }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
906
906
  };
907
907
  NvIconbutton = __decorate([
908
908
  ProxyCmp({
909
- inputs: ['active', 'disabled', 'emphasis', 'loading', 'name', 'shape', 'size', 'type']
909
+ inputs: ['active', 'disableTabindex', 'disabled', 'emphasis', 'loading', 'name', 'shape', 'size', 'type']
910
910
  })
911
911
  ], NvIconbutton);
912
912
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvIconbutton, decorators: [{
@@ -916,7 +916,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
916
916
  changeDetection: ChangeDetectionStrategy.OnPush,
917
917
  template: '<ng-content></ng-content>',
918
918
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
919
- inputs: ['active', 'disabled', 'emphasis', 'loading', 'name', 'shape', 'size', 'type'],
919
+ inputs: ['active', 'disableTabindex', 'disabled', 'emphasis', 'loading', 'name', 'shape', 'size', 'type'],
920
920
  standalone: false
921
921
  }]
922
922
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }] });
@@ -1175,6 +1175,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
1175
1175
  standalone: false
1176
1176
  }]
1177
1177
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }] });
1178
+ let NvTableheader = class NvTableheader {
1179
+ constructor(c, r, z) {
1180
+ this.z = z;
1181
+ c.detach();
1182
+ this.el = r.nativeElement;
1183
+ proxyOutputs(this, this.el, ['sortDirectionChanged']);
1184
+ }
1185
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvTableheader, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
1186
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.5", type: NvTableheader, isStandalone: false, selector: "nv-tableheader", inputs: { sortDirection: "sortDirection", sortable: "sortable" }, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1187
+ };
1188
+ NvTableheader = __decorate([
1189
+ ProxyCmp({
1190
+ inputs: ['sortDirection', 'sortable']
1191
+ })
1192
+ ], NvTableheader);
1193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvTableheader, decorators: [{
1194
+ type: Component,
1195
+ args: [{
1196
+ selector: 'nv-tableheader',
1197
+ changeDetection: ChangeDetectionStrategy.OnPush,
1198
+ template: '<ng-content></ng-content>',
1199
+ // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1200
+ inputs: ['sortDirection', 'sortable'],
1201
+ standalone: false
1202
+ }]
1203
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.NgZone }] });
1178
1204
  let NvToggle = class NvToggle {
1179
1205
  constructor(c, r, z) {
1180
1206
  this.z = z;
@@ -1324,6 +1350,7 @@ const DIRECTIVES = [
1324
1350
  NvSplit,
1325
1351
  NvStack,
1326
1352
  NvTable,
1353
+ NvTableheader,
1327
1354
  NvToggle,
1328
1355
  NvTogglebutton,
1329
1356
  NvTogglebuttongroup,
@@ -2092,6 +2119,7 @@ function createAngularTable(options) {
2092
2119
 
2093
2120
  /* eslint-disable jsdoc/require-jsdoc */
2094
2121
  /* eslint-disable jsdoc/require-returns */
2122
+ /* eslint-disable jsdoc/require-param */
2095
2123
  /**
2096
2124
  * A powerful, flexible datatable component built on TanStack Table.
2097
2125
  * Supports custom cell rendering, column configuration, pagination, and full TypeScript typing.
@@ -2101,6 +2129,37 @@ class NvDatatable {
2101
2129
  table() {
2102
2130
  return this.tableInstance();
2103
2131
  }
2132
+ /**
2133
+ * Handle sort direction change from table header
2134
+ * @param {Event} event The sort direction change event or direction string
2135
+ * @param {object} header The table header object
2136
+ */
2137
+ handleSortDirectionChanged(event, header) {
2138
+ // Extract direction from event or use directly if string
2139
+ let direction;
2140
+ if (typeof event === 'string') {
2141
+ direction = event;
2142
+ }
2143
+ else if ('detail' in event && typeof event.detail === 'string') {
2144
+ direction = event.detail;
2145
+ }
2146
+ else {
2147
+ // Fallback - shouldn't happen but handle gracefully
2148
+ console.warn('Unexpected event type in handleSortDirectionChanged:', event);
2149
+ return;
2150
+ }
2151
+ const sortingConfig = this.sorting();
2152
+ // Check if multi-sort is enabled
2153
+ const isMultiSort = sortingConfig?.enableMultiSort ?? false;
2154
+ if (direction === 'none') {
2155
+ // Clear this column's sort
2156
+ header.column.clearSorting();
2157
+ }
2158
+ else {
2159
+ // Toggle sort - TanStack will handle the logic internally
2160
+ header.column.toggleSorting(direction === 'desc', isMultiSort);
2161
+ }
2162
+ }
2104
2163
  constructor() {
2105
2164
  /** Column definitions */
2106
2165
  this.columns = input([]);
@@ -2108,6 +2167,8 @@ class NvDatatable {
2108
2167
  this.rows = input([]);
2109
2168
  /** Optional pagination configuration */
2110
2169
  this.pagination = input(undefined);
2170
+ /** Optional sorting configuration */
2171
+ this.sorting = input(undefined);
2111
2172
  /** Should the header stick to the top of the table when scrolling? */
2112
2173
  this.stickyHeader = input(false);
2113
2174
  /** Signal to track cell templates array */
@@ -2125,6 +2186,8 @@ class NvDatatable {
2125
2186
  pageIndex: 0,
2126
2187
  pageSize: this.pagination()?.initialPageSize || 10,
2127
2188
  });
2189
+ /** Sorting state for controlled sorting (server mode) */
2190
+ this.sortingState = signal(this.sorting()?.sortState || []);
2128
2191
  /** Reference to table rows for infinite scroll observer */
2129
2192
  this.tableRows = viewChild('tableRow');
2130
2193
  /** Intersection observer for infinite scroll */
@@ -2153,7 +2216,7 @@ class NvDatatable {
2153
2216
  // Default: just return the value (formatted if valueFormatter was used)
2154
2217
  return context.getValue();
2155
2218
  };
2156
- return {
2219
+ const columnDef = {
2157
2220
  accessorKey: col.field,
2158
2221
  accessorFn: col.valueFormatter
2159
2222
  ? (row) => {
@@ -2168,27 +2231,82 @@ class NvDatatable {
2168
2231
  header: col.headerName || String(col.field),
2169
2232
  size: col.width,
2170
2233
  enableResizing: col.resizable ?? true,
2234
+ // Sorting configuration
2235
+ enableSorting: this.sorting() ? col.sortable ?? true : false,
2171
2236
  cell: cellRenderer,
2172
2237
  };
2238
+ // Add optional sorting properties only if defined
2239
+ if (col.sortingFn !== undefined) {
2240
+ // @ts-expect-error - TanStack typing is strict but this works at runtime
2241
+ columnDef.sortingFn = col.sortingFn;
2242
+ }
2243
+ if (col.sortDescFirst !== undefined) {
2244
+ columnDef.sortDescFirst = col.sortDescFirst;
2245
+ }
2246
+ if (col.invertSorting !== undefined) {
2247
+ columnDef.invertSorting = col.invertSorting;
2248
+ }
2249
+ if (col.sortUndefined !== undefined) {
2250
+ columnDef.sortUndefined = col.sortUndefined;
2251
+ }
2252
+ return columnDef;
2173
2253
  });
2174
2254
  });
2175
2255
  /** TanStack table instance with Signals */
2176
2256
  this.tableInstance = createAngularTable(() => {
2177
2257
  const paginationConfig = this.pagination();
2178
- if (!paginationConfig || paginationConfig.mode === 'infinite') {
2179
- // No pagination or infinite scroll - simple config
2180
- return {
2258
+ const sortingConfig = this.sorting();
2259
+ // Determine base table configuration with sorting
2260
+ const getBaseTableConfig = () => {
2261
+ const baseConfig = {
2181
2262
  data: this.rows(),
2182
2263
  columns: this.tableColumns(),
2183
2264
  getCoreRowModel: getCoreRowModel(),
2265
+ // Sorting configuration
2266
+ ...(sortingConfig && {
2267
+ state: {
2268
+ sorting: sortingConfig.mode === 'server' && sortingConfig.sortState
2269
+ ? sortingConfig.sortState
2270
+ : this.sortingState(),
2271
+ },
2272
+ onSortingChange: (updaterOrValue) => {
2273
+ const currentSort = sortingConfig.mode === 'server' && sortingConfig.sortState
2274
+ ? sortingConfig.sortState
2275
+ : this.sortingState();
2276
+ const newSort = typeof updaterOrValue === 'function'
2277
+ ? updaterOrValue(currentSort)
2278
+ : updaterOrValue;
2279
+ // Always update internal state for reactivity
2280
+ this.sortingState.set(newSort);
2281
+ // For server-side sorting, also call the callback
2282
+ if (sortingConfig?.mode === 'server' &&
2283
+ sortingConfig.onSortingChange) {
2284
+ sortingConfig.onSortingChange(newSort);
2285
+ }
2286
+ },
2287
+ manualSorting: sortingConfig.mode === 'server',
2288
+ enableSorting: true,
2289
+ enableMultiSort: sortingConfig.enableMultiSort ?? false,
2290
+ enableSortingRemoval: sortingConfig.enableSortingRemoval ?? true,
2291
+ maxMultiSortColCount: sortingConfig.maxMultiSortColCount,
2292
+ sortDescFirst: sortingConfig.sortDescFirst ?? false,
2293
+ // When multi-sort is enabled, treat all clicks as multi-sort events
2294
+ isMultiSortEvent: sortingConfig.enableMultiSort
2295
+ ? () => true
2296
+ : undefined,
2297
+ getSortedRowModel: sortingConfig.mode === 'client' ? getSortedRowModel() : undefined,
2298
+ }),
2184
2299
  };
2300
+ return baseConfig;
2301
+ };
2302
+ if (!paginationConfig || paginationConfig.mode === 'infinite') {
2303
+ // No pagination or infinite scroll - simple config
2304
+ return getBaseTableConfig();
2185
2305
  }
2186
2306
  else if (paginationConfig.mode === 'client') {
2187
2307
  // Client-side pagination
2188
2308
  return {
2189
- data: this.rows(),
2190
- columns: this.tableColumns(),
2191
- getCoreRowModel: getCoreRowModel(),
2309
+ ...getBaseTableConfig(),
2192
2310
  getPaginationRowModel: getPaginationRowModel(),
2193
2311
  initialState: {
2194
2312
  pagination: {
@@ -2199,12 +2317,11 @@ class NvDatatable {
2199
2317
  };
2200
2318
  }
2201
2319
  else {
2202
- // Server-side pagination
2320
+ // Server-side pagination - manual pagination with reactive state
2321
+ const baseConfig = getBaseTableConfig();
2203
2322
  const pageSize = this.paginationState().pageSize;
2204
2323
  return {
2205
- data: this.rows(),
2206
- columns: this.tableColumns(),
2207
- getCoreRowModel: getCoreRowModel(),
2324
+ ...baseConfig,
2208
2325
  manualPagination: true,
2209
2326
  pageCount: paginationConfig.totalPageCount !== undefined
2210
2327
  ? paginationConfig.totalPageCount
@@ -2213,13 +2330,19 @@ class NvDatatable {
2213
2330
  : -1,
2214
2331
  state: {
2215
2332
  pagination: this.paginationState(),
2333
+ ...(baseConfig.state && { sorting: baseConfig.state.sorting }),
2216
2334
  },
2217
2335
  onPaginationChange: (updaterOrValue) => {
2218
- if (typeof updaterOrValue === 'function') {
2219
- this.paginationState.set(updaterOrValue(this.paginationState()));
2220
- }
2221
- else {
2222
- this.paginationState.set(updaterOrValue);
2336
+ const newState = typeof updaterOrValue === 'function'
2337
+ ? updaterOrValue(this.paginationState())
2338
+ : updaterOrValue;
2339
+ this.paginationState.set(newState);
2340
+ // Call user's callback for server-side pagination
2341
+ if (paginationConfig.onPaginationChange) {
2342
+ paginationConfig.onPaginationChange({
2343
+ pageIndex: newState.pageIndex,
2344
+ pageSize: newState.pageSize,
2345
+ });
2223
2346
  }
2224
2347
  },
2225
2348
  };
@@ -2258,18 +2381,6 @@ class NvDatatable {
2258
2381
  : undefined,
2259
2382
  };
2260
2383
  });
2261
- // Watch pagination state changes for server mode
2262
- effect(() => {
2263
- const paginationConfig = this.pagination();
2264
- const state = this.paginationState();
2265
- if (paginationConfig?.mode === 'server' &&
2266
- paginationConfig.onPaginationChange) {
2267
- paginationConfig.onPaginationChange({
2268
- pageIndex: state.pageIndex,
2269
- pageSize: state.pageSize,
2270
- });
2271
- }
2272
- }, { allowSignalWrites: true });
2273
2384
  // Set up intersection observer for infinite scroll
2274
2385
  effect(() => {
2275
2386
  const paginationConfig = this.pagination();
@@ -2328,7 +2439,7 @@ class NvDatatable {
2328
2439
  }
2329
2440
  }
2330
2441
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NvDatatable, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2331
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.5", type: NvDatatable, isStandalone: true, selector: "nv-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "paginationTemplate", first: true, predicate: ["paginationTemplate"], descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: NvDatatableCellDirective }], viewQueries: [{ propertyName: "tableRows", first: true, predicate: ["tableRow"], descendants: true, isSignal: true }], ngImport: i0, template: `
2442
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.5", type: NvDatatable, isStandalone: true, selector: "nv-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "paginationTemplate", first: true, predicate: ["paginationTemplate"], descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: NvDatatableCellDirective }], viewQueries: [{ propertyName: "tableRows", first: true, predicate: ["tableRow"], descendants: true, isSignal: true }], ngImport: i0, template: `
2332
2443
  <nv-table>
2333
2444
  <table>
2334
2445
  <thead [attr.data-sticky-top]="stickyHeader() ? 'true' : null">
@@ -2347,9 +2458,19 @@ class NvDatatable {
2347
2458
  !header.column.columnDef.enableResizing ? 'true' : null
2348
2459
  "
2349
2460
  >
2350
- @if (!header.isPlaceholder) {
2461
+ @if (!header.isPlaceholder) { @if (header.column.getCanSort()) {
2462
+ <nv-tableheader
2463
+ [sortable]="true"
2464
+ [sortDirection]="header.column.getIsSorted() || 'none'"
2465
+ (sortDirectionChanged)="
2466
+ handleSortDirectionChanged($event, header)
2467
+ "
2468
+ >
2469
+ {{ header.column.columnDef.header }}
2470
+ </nv-tableheader>
2471
+ } @else {
2351
2472
  {{ header.column.columnDef.header }}
2352
- }
2473
+ } }
2353
2474
  </th>
2354
2475
  }
2355
2476
  </tr>
@@ -2360,7 +2481,7 @@ class NvDatatable {
2360
2481
  @for (row of table().getRowModel().rows; track row.id; let i = $index)
2361
2482
  {
2362
2483
  <tr
2363
- [attr.data-testid]="'datatable-row-' + row.id"
2484
+ [attr.data-testid]="'datatable-row-' + i"
2364
2485
  #tableRow
2365
2486
  [attr.data-is-last]="
2366
2487
  isInfiniteScroll() && i === table().getRowModel().rows.length - 1
@@ -2369,7 +2490,7 @@ class NvDatatable {
2369
2490
  "
2370
2491
  >
2371
2492
  @for (cell of row.getVisibleCells(); track cell.id) {
2372
- <td [attr.data-testid]="'datatable-cell-' + cell.id">
2493
+ <td [attr.data-testid]="'datatable-cell-' + cell.column.id">
2373
2494
  <ng-container
2374
2495
  *flexRender="
2375
2496
  cell.column.columnDef.cell;
@@ -2424,9 +2545,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
2424
2545
  !header.column.columnDef.enableResizing ? 'true' : null
2425
2546
  "
2426
2547
  >
2427
- @if (!header.isPlaceholder) {
2548
+ @if (!header.isPlaceholder) { @if (header.column.getCanSort()) {
2549
+ <nv-tableheader
2550
+ [sortable]="true"
2551
+ [sortDirection]="header.column.getIsSorted() || 'none'"
2552
+ (sortDirectionChanged)="
2553
+ handleSortDirectionChanged($event, header)
2554
+ "
2555
+ >
2556
+ {{ header.column.columnDef.header }}
2557
+ </nv-tableheader>
2558
+ } @else {
2428
2559
  {{ header.column.columnDef.header }}
2429
- }
2560
+ } }
2430
2561
  </th>
2431
2562
  }
2432
2563
  </tr>
@@ -2437,7 +2568,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
2437
2568
  @for (row of table().getRowModel().rows; track row.id; let i = $index)
2438
2569
  {
2439
2570
  <tr
2440
- [attr.data-testid]="'datatable-row-' + row.id"
2571
+ [attr.data-testid]="'datatable-row-' + i"
2441
2572
  #tableRow
2442
2573
  [attr.data-is-last]="
2443
2574
  isInfiniteScroll() && i === table().getRowModel().rows.length - 1
@@ -2446,7 +2577,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
2446
2577
  "
2447
2578
  >
2448
2579
  @for (cell of row.getVisibleCells(); track cell.id) {
2449
- <td [attr.data-testid]="'datatable-cell-' + cell.id">
2580
+ <td [attr.data-testid]="'datatable-cell-' + cell.column.id">
2450
2581
  <ng-container
2451
2582
  *flexRender="
2452
2583
  cell.column.columnDef.cell;
@@ -3380,7 +3511,7 @@ const VALUE_ACCESSORS = [
3380
3511
 
3381
3512
  class NovaComponentsModule {
3382
3513
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NovaComponentsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3383
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.5", ngImport: i0, type: NovaComponentsModule, declarations: [NvAccordion, NvAccordionItem, NvAlert, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCol, NvDatagrid, NvDatagridcolumn, NvDialog, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFielddate, NvFielddaterange, NvFielddropdown, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldnumber, NvFieldpassword, NvFieldradio, NvFieldselect, NvFieldslider, NvFieldtext, NvFieldtextarea, NvFieldtime, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationcontainer, NvPopover, NvRow, NvSplit, NvStack, NvTable, NvToggle, NvTogglebutton, NvTogglebuttongroup, NvTooltip], imports: [NvDatatable], exports: [NvAccordion, NvAccordionItem, NvAlert, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCol, NvDatagrid, NvDatagridcolumn, NvDialog, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFielddate, NvFielddaterange, NvFielddropdown, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldnumber, NvFieldpassword, NvFieldradio, NvFieldselect, NvFieldslider, NvFieldtext, NvFieldtextarea, NvFieldtime, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationcontainer, NvPopover, NvRow, NvSplit, NvStack, NvTable, NvToggle, NvTogglebutton, NvTogglebuttongroup, NvTooltip, NvDatatable] }); }
3514
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.5", ngImport: i0, type: NovaComponentsModule, declarations: [NvAccordion, NvAccordionItem, NvAlert, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCol, NvDatagrid, NvDatagridcolumn, NvDialog, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFielddate, NvFielddaterange, NvFielddropdown, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldnumber, NvFieldpassword, NvFieldradio, NvFieldselect, NvFieldslider, NvFieldtext, NvFieldtextarea, NvFieldtime, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationcontainer, NvPopover, NvRow, NvSplit, NvStack, NvTable, NvTableheader, NvToggle, NvTogglebutton, NvTogglebuttongroup, NvTooltip], imports: [NvDatatable], exports: [NvAccordion, NvAccordionItem, NvAlert, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCol, NvDatagrid, NvDatagridcolumn, NvDialog, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFielddate, NvFielddaterange, NvFielddropdown, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldnumber, NvFieldpassword, NvFieldradio, NvFieldselect, NvFieldslider, NvFieldtext, NvFieldtextarea, NvFieldtime, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationcontainer, NvPopover, NvRow, NvSplit, NvStack, NvTable, NvTableheader, NvToggle, NvTogglebutton, NvTogglebuttongroup, NvTooltip, NvDatatable] }); }
3384
3515
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NovaComponentsModule, imports: [NvDatatable] }); }
3385
3516
  }
3386
3517
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NovaComponentsModule, decorators: [{
@@ -3428,6 +3559,7 @@ class NotificationService {
3428
3559
  }
3429
3560
  constructor() {
3430
3561
  this._notifications = new BehaviorSubject([]);
3562
+ this.timers = new Map();
3431
3563
  /**
3432
3564
  * Observable stream of active notifications.
3433
3565
  */
@@ -3465,6 +3597,14 @@ class NotificationService {
3465
3597
  clearRefs() {
3466
3598
  this.elRefs.clear();
3467
3599
  }
3600
+ /**
3601
+ * Clean up all timers and references.
3602
+ */
3603
+ ngOnDestroy() {
3604
+ this.timers.forEach((timer) => clearTimeout(timer));
3605
+ this.timers.clear();
3606
+ this.clearRefs();
3607
+ }
3468
3608
  /**
3469
3609
  * Show a new notification.
3470
3610
  *
@@ -3483,6 +3623,7 @@ class NotificationService {
3483
3623
  icon: options.icon,
3484
3624
  actions: options.actions ?? [],
3485
3625
  actionSlot: options.actionSlot,
3626
+ duration: options.duration ?? 0,
3486
3627
  createdAt: Date.now(),
3487
3628
  };
3488
3629
  const currentNotifications = this._notifications.value;
@@ -3497,6 +3638,13 @@ class NotificationService {
3497
3638
  setTimeout(() => {
3498
3639
  const ref = this.elRefs.get(id);
3499
3640
  ref?.show();
3641
+ // Set up auto-dismiss timer if duration > 0
3642
+ if (notification.duration && notification.duration > 0) {
3643
+ const timer = setTimeout(() => {
3644
+ this.dismiss(id);
3645
+ }, notification.duration);
3646
+ this.timers.set(id, timer);
3647
+ }
3500
3648
  }, 0);
3501
3649
  return id;
3502
3650
  }
@@ -3507,6 +3655,12 @@ class NotificationService {
3507
3655
  * @param {string} id The notification ID to dismiss
3508
3656
  */
3509
3657
  dismiss(id) {
3658
+ // Clear timer if exists
3659
+ const timer = this.timers.get(id);
3660
+ if (timer) {
3661
+ clearTimeout(timer);
3662
+ this.timers.delete(id);
3663
+ }
3510
3664
  this.elRefs.get(id)?.dismiss?.();
3511
3665
  }
3512
3666
  /**
@@ -3515,6 +3669,12 @@ class NotificationService {
3515
3669
  * @param {string} id The notification ID to dismiss
3516
3670
  */
3517
3671
  remove(id) {
3672
+ // Clear timer if exists
3673
+ const timer = this.timers.get(id);
3674
+ if (timer) {
3675
+ clearTimeout(timer);
3676
+ this.timers.delete(id);
3677
+ }
3518
3678
  const currentNotifications = this._notifications.value;
3519
3679
  const filteredNotifications = currentNotifications.filter((notification) => notification.id !== id);
3520
3680
  this._notifications.next(filteredNotifications);
@@ -3523,6 +3683,9 @@ class NotificationService {
3523
3683
  * Immediately remove all active notifications.
3524
3684
  */
3525
3685
  removeAll() {
3686
+ // Clear all timers
3687
+ this.timers.forEach((timer) => clearTimeout(timer));
3688
+ this.timers.clear();
3526
3689
  this._notifications.next([]);
3527
3690
  }
3528
3691
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.5", ngImport: i0, type: NotificationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
@@ -3721,5 +3884,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.5", ngImpor
3721
3884
  * Generated bundle index. Do not edit.
3722
3885
  */
3723
3886
 
3724
- export { NotificationService, NotificationServiceComponent, NovaComponentsModule, NovaComponentsValueAccessorModule, NvAccordion, NvAccordionItem, NvAccordionValueAccessor, NvAlert, NvAlertValueAccessor, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCalendarValueAccessor, NvCol, NvDatagrid, NvDatagridValueAccessor, NvDatagridcolumn, NvDatatable, NvDatatableCellDirective, NvDialog, NvDialogValueAccessor, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFieldcheckboxValueAccessor, NvFielddate, NvFielddateValueAccessor, NvFielddaterange, NvFielddaterangeValueAccessor, NvFielddropdown, NvFielddropdownValueAccessor, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldmultiselectValueAccessor, NvFieldnumber, NvFieldnumberValueAccessor, NvFieldpassword, NvFieldpasswordValueAccessor, NvFieldradio, NvFieldradioValueAccessor, NvFieldselect, NvFieldselectValueAccessor, NvFieldslider, NvFieldsliderValueAccessor, NvFieldtext, NvFieldtextValueAccessor, NvFieldtextarea, NvFieldtextareaValueAccessor, NvFieldtime, NvFieldtimeValueAccessor, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationValueAccessor, NvNotificationcontainer, NvPopover, NvPopoverValueAccessor, NvRow, NvSplit, NvSplitValueAccessor, NvStack, NvTable, NvToggle, NvToggleValueAccessor, NvTogglebutton, NvTogglebuttongroup, NvTogglebuttongroupValueAccessor, NvTooltip, VALUE_ACCESSORS, makeColumn, flexRenderComponent as nvDatatableRenderComponent };
3887
+ export { NotificationService, NotificationServiceComponent, NovaComponentsModule, NovaComponentsValueAccessorModule, NvAccordion, NvAccordionItem, NvAccordionValueAccessor, NvAlert, NvAlertValueAccessor, NvAvatar, NvBadge, NvBreadcrumb, NvBreadcrumbs, NvButton, NvButtongroup, NvCalendar, NvCalendarValueAccessor, NvCol, NvDatagrid, NvDatagridValueAccessor, NvDatagridcolumn, NvDatatable, NvDatatableCellDirective, NvDialog, NvDialogValueAccessor, NvDialogfooter, NvDialogheader, NvFieldcheckbox, NvFieldcheckboxValueAccessor, NvFielddate, NvFielddateValueAccessor, NvFielddaterange, NvFielddaterangeValueAccessor, NvFielddropdown, NvFielddropdownValueAccessor, NvFielddropdownitem, NvFielddropdownitemcheck, NvFieldmultiselect, NvFieldmultiselectValueAccessor, NvFieldnumber, NvFieldnumberValueAccessor, NvFieldpassword, NvFieldpasswordValueAccessor, NvFieldradio, NvFieldradioValueAccessor, NvFieldselect, NvFieldselectValueAccessor, NvFieldslider, NvFieldsliderValueAccessor, NvFieldtext, NvFieldtextValueAccessor, NvFieldtextarea, NvFieldtextareaValueAccessor, NvFieldtime, NvFieldtimeValueAccessor, NvIcon, NvIconbutton, NvLoader, NvMenu, NvMenuitem, NvNotification, NvNotificationValueAccessor, NvNotificationcontainer, NvPopover, NvPopoverValueAccessor, NvRow, NvSplit, NvSplitValueAccessor, NvStack, NvTable, NvTableheader, NvToggle, NvToggleValueAccessor, NvTogglebutton, NvTogglebuttongroup, NvTogglebuttongroupValueAccessor, NvTooltip, VALUE_ACCESSORS, makeColumn, flexRenderComponent as nvDatatableRenderComponent };
3725
3888
  //# sourceMappingURL=nova-components.mjs.map