@truenas/ui-components 0.1.58 → 0.1.60

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.
@@ -1,15 +1,15 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { ElementRef, OnDestroy, AfterViewInit, AfterContentInit, TemplateRef, Provider, ChangeDetectorRef, OnInit, PipeTransform, ViewContainerRef, AfterViewChecked, ComponentRef, InjectionToken } from '@angular/core';
2
+ import { ElementRef, OnDestroy, AfterViewInit, TemplateRef, AfterContentInit, Provider, ChangeDetectorRef, OnInit, InjectionToken, Signal, PipeTransform, ViewContainerRef, AfterViewChecked, ComponentRef } from '@angular/core';
3
3
  import { ControlValueAccessor, NgControl } from '@angular/forms';
4
4
  import { ComponentHarness, BaseHarnessFilters, HarnessPredicate, HarnessLoader } from '@angular/cdk/testing';
5
5
  import { SafeHtml, SafeResourceUrl, DomSanitizer } from '@angular/platform-browser';
6
6
  import { ComponentFixture } from '@angular/core/testing';
7
7
  import { SelectionModel, DataSource } from '@angular/cdk/collections';
8
+ import * as rxjs from 'rxjs';
9
+ import { Observable } from 'rxjs';
8
10
  import * as i1 from '@angular/cdk/tree';
9
11
  import { CdkTree, FlatTreeControl, CdkTreeNode, CdkNestedTreeNode } from '@angular/cdk/tree';
10
12
  export { FlatTreeControl } from '@angular/cdk/tree';
11
- import * as rxjs from 'rxjs';
12
- import { Observable } from 'rxjs';
13
13
  import { Overlay } from '@angular/cdk/overlay';
14
14
  import { DialogConfig, DialogRef } from '@angular/cdk/dialog';
15
15
  import { ComponentType } from '@angular/cdk/portal';
@@ -524,7 +524,7 @@ interface BannerHarnessFilters extends BaseHarnessFilters {
524
524
  textContains?: string | RegExp;
525
525
  }
526
526
 
527
- declare class TnButtonComponent {
527
+ declare class TnButtonComponent implements AfterViewInit {
528
528
  size: string;
529
529
  primary: _angular_core.InputSignal<boolean>;
530
530
  color: _angular_core.InputSignal<"primary" | "secondary" | "warn" | "default">;
@@ -561,6 +561,9 @@ declare class TnButtonComponent {
561
561
  isRouterLink: _angular_core.Signal<boolean>;
562
562
  classes: _angular_core.Signal<string[]>;
563
563
  handleAnchorClick(event: MouseEvent): void;
564
+ private hostRef;
565
+ private innerRef;
566
+ ngAfterViewInit(): void;
564
567
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnButtonComponent, never>;
565
568
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnButtonComponent, "tn-button", never, { "primary": { "alias": "primary"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "routerLink": { "alias": "routerLink"; "required": false; "isSignal": true; }; "queryParams": { "alias": "queryParams"; "required": false; "isSignal": true; }; "fragment": { "alias": "fragment"; "required": false; "isSignal": true; }; "target": { "alias": "target"; "required": false; "isSignal": true; }; "rel": { "alias": "rel"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, never, true, never>;
566
569
  }
@@ -714,7 +717,7 @@ declare class TnIconComponent {
714
717
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnIconComponent, "tn-icon", never, { "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "library": { "alias": "library"; "required": false; "isSignal": true; }; "fullSize": { "alias": "fullSize"; "required": false; "isSignal": true; }; "customSize": { "alias": "customSize"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
715
718
  }
716
719
 
717
- declare class TnIconButtonComponent {
720
+ declare class TnIconButtonComponent implements AfterViewInit {
718
721
  disabled: _angular_core.InputSignal<boolean>;
719
722
  ariaLabel: _angular_core.InputSignal<string | undefined>;
720
723
  /**
@@ -728,8 +731,17 @@ declare class TnIconButtonComponent {
728
731
  tooltip: _angular_core.InputSignal<string | undefined>;
729
732
  library: _angular_core.InputSignal<IconLibraryType | undefined>;
730
733
  onClick: _angular_core.OutputEmitterRef<MouseEvent>;
734
+ private hostRef;
735
+ private buttonRef;
731
736
  classes: _angular_core.Signal<string[]>;
732
737
  effectiveAriaLabel: _angular_core.Signal<string>;
738
+ /**
739
+ * Focuses the inner native `<button>`. Exposed as a public method so callers
740
+ * with a `TnIconButtonComponent` reference (e.g. `@ViewChild`) can focus it
741
+ * without reaching into the DOM themselves.
742
+ */
743
+ focus(options?: FocusOptions): void;
744
+ ngAfterViewInit(): void;
733
745
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnIconButtonComponent, never>;
734
746
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnIconButtonComponent, "tn-icon-button", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "tooltip": { "alias": "tooltip"; "required": false; "isSignal": true; }; "library": { "alias": "library"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, never, true, never>;
735
747
  }
@@ -1298,6 +1310,68 @@ interface TnCardFooterLink {
1298
1310
  testId?: string;
1299
1311
  }
1300
1312
 
1313
+ /**
1314
+ * Projection-based menu item for use inside `<tn-menu>`.
1315
+ *
1316
+ * Two authoring modes:
1317
+ * 1. **Convenience** — set `label` (and optionally `icon`/`shortcut`); the
1318
+ * default icon + label + shortcut layout renders.
1319
+ * 2. **Custom content** — omit `label`; project arbitrary content via
1320
+ * `<ng-content>` (badges, two-line layouts, etc.).
1321
+ *
1322
+ * Existing `<tn-menu [items]="...">` consumers don't need this component;
1323
+ * the items-array API continues to work unchanged. Items-array entries and
1324
+ * projected `<tn-menu-item>` children render together inside one `<tn-menu>`,
1325
+ * share keyboard navigation, and look identical.
1326
+ *
1327
+ * **How it works:** the component itself renders nothing visible — it acts as
1328
+ * a configuration declaration. The parent `<tn-menu>` collects projected items
1329
+ * via `contentChildren`, re-renders them inside the CDK overlay alongside
1330
+ * items-array entries (with `cdkMenuItem` for full arrow-key navigation), and
1331
+ * routes clicks back to each item's `itemClick` output.
1332
+ *
1333
+ * **Accessibility:** because items are re-rendered inside the parent's
1334
+ * `CdkMenu`, all keyboard semantics from `@angular/cdk/menu` apply uniformly:
1335
+ * Arrow Up/Down/Home/End to move focus, Enter/Space to activate, Esc to
1336
+ * close, type-ahead search. Disabled items are skipped.
1337
+ *
1338
+ * **Limitation — custom content & keyboard nav:** the projected
1339
+ * `<ng-content>` (custom-content mode) is rendered inside a `<button
1340
+ * cdkMenuItem>` wrapper that owns Enter/Space/Arrow handling. Interactive
1341
+ * elements inside the projection (a nested `<button>`, link, toggle, etc.)
1342
+ * receive clicks but **do not** participate in CDK arrow-key navigation —
1343
+ * the wrapper button is the only keyboard-reachable target. Prefer
1344
+ * display-only content (icons, badges, two-line text); for cases that need
1345
+ * an extra interactive control, build a custom menu host with `cdkMenu`
1346
+ * directly instead of `<tn-menu>`.
1347
+ *
1348
+ * @example
1349
+ * ```html
1350
+ * <tn-menu>
1351
+ * <tn-menu-item label="JSON" [selected]="format === 'json'"
1352
+ * (itemClick)="setFormat('json')" />
1353
+ * <tn-menu-item label="CSV" [selected]="format === 'csv'"
1354
+ * (itemClick)="setFormat('csv')" />
1355
+ * </tn-menu>
1356
+ * ```
1357
+ */
1358
+ declare class TnMenuItemComponent {
1359
+ id: _angular_core.InputSignal<string | undefined>;
1360
+ label: _angular_core.InputSignal<string | undefined>;
1361
+ icon: _angular_core.InputSignal<string | undefined>;
1362
+ iconLibrary: _angular_core.InputSignal<"material" | "mdi" | "custom" | "lucide" | undefined>;
1363
+ shortcut: _angular_core.InputSignal<string | undefined>;
1364
+ disabled: _angular_core.InputSignal<boolean>;
1365
+ selected: _angular_core.InputSignal<boolean>;
1366
+ testId: _angular_core.InputSignal<string | undefined>;
1367
+ itemClick: _angular_core.OutputEmitterRef<MouseEvent>;
1368
+ /** Template capturing whatever the consumer projected as item content. */
1369
+ content: _angular_core.Signal<TemplateRef<unknown>>;
1370
+ resolvedTestId: _angular_core.Signal<string | undefined>;
1371
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuItemComponent, never>;
1372
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnMenuItemComponent, "tn-menu-item", never, { "id": { "alias": "id"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconLibrary": { "alias": "iconLibrary"; "required": false; "isSignal": true; }; "shortcut": { "alias": "shortcut"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["*"], true, never>;
1373
+ }
1374
+
1301
1375
  /**
1302
1376
  * Activates CDK menu hover-to-open behavior for menus opened via custom overlays.
1303
1377
  *
@@ -1335,7 +1409,7 @@ interface TnMenuItem {
1335
1409
  */
1336
1410
  selected?: boolean;
1337
1411
  }
1338
- declare class TnMenuComponent {
1412
+ declare class TnMenuComponent implements OnDestroy {
1339
1413
  items: _angular_core.InputSignal<TnMenuItem[]>;
1340
1414
  contextMenu: _angular_core.InputSignal<boolean>;
1341
1415
  menuItemClick: _angular_core.OutputEmitterRef<TnMenuItem>;
@@ -1344,11 +1418,17 @@ declare class TnMenuComponent {
1344
1418
  menuTemplate: _angular_core.Signal<TemplateRef<unknown>>;
1345
1419
  contextMenuTemplate: _angular_core.Signal<TemplateRef<unknown>>;
1346
1420
  private contextOverlayRef?;
1421
+ private contextBackdropSub?;
1347
1422
  private overlay;
1348
1423
  private viewContainerRef;
1349
1424
  onMenuItemClick(item: TnMenuItem): void;
1350
- private contentItems;
1351
- constructor();
1425
+ contentItems: _angular_core.Signal<readonly TnMenuItemComponent[]>;
1426
+ /**
1427
+ * Click handler for projected `<tn-menu-item>` entries. Emits the item's own
1428
+ * `itemClick` output, re-emits a synthetic entry on `menuItemClick` so
1429
+ * trigger-driven menus close uniformly, and closes any open context menu.
1430
+ */
1431
+ onProjectedItemClick(item: TnMenuItemComponent, event: MouseEvent): void;
1352
1432
  hasChildren: _angular_core.Signal<(item: TnMenuItem) => boolean>;
1353
1433
  onMenuOpen(): void;
1354
1434
  onMenuClose(): void;
@@ -1358,10 +1438,11 @@ declare class TnMenuComponent {
1358
1438
  getMenuTemplate(): TemplateRef<unknown> | null;
1359
1439
  openContextMenuAt(x: number, y: number): void;
1360
1440
  private closeContextMenu;
1441
+ ngOnDestroy(): void;
1361
1442
  onContextMenu(event: MouseEvent): void;
1362
1443
  trackByItemId(index: number, item: TnMenuItem): string;
1363
1444
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuComponent, never>;
1364
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnMenuComponent, "tn-menu", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "contextMenu": { "alias": "contextMenu"; "required": false; "isSignal": true; }; }, { "menuItemClick": "menuItemClick"; "menuOpen": "menuOpen"; "menuClose": "menuClose"; }, ["contentItems"], ["*", "tn-menu-item, .tn-menu-separator"], true, never>;
1445
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnMenuComponent, "tn-menu", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "contextMenu": { "alias": "contextMenu"; "required": false; "isSignal": true; }; }, { "menuItemClick": "menuItemClick"; "menuOpen": "menuOpen"; "menuClose": "menuClose"; }, ["contentItems"], ["*"], true, never>;
1365
1446
  }
1366
1447
 
1367
1448
  declare class TnCardComponent {
@@ -1394,6 +1475,7 @@ declare class TnCardComponent {
1394
1475
  private registerMdiIcons;
1395
1476
  classes: _angular_core.Signal<string[]>;
1396
1477
  hasHeader: _angular_core.Signal<boolean>;
1478
+ hasHeaderRight: _angular_core.Signal<boolean>;
1397
1479
  hasFooter: _angular_core.Signal<boolean>;
1398
1480
  onTitleClick(): void;
1399
1481
  onControlChange(checked: boolean): void;
@@ -2475,71 +2557,48 @@ interface TabsHarnessFilters extends BaseHarnessFilters {
2475
2557
  hasTab?: string | RegExp;
2476
2558
  }
2477
2559
 
2478
- /**
2479
- * Projection-based menu item for use inside `<tn-menu>`.
2480
- *
2481
- * Two authoring modes:
2482
- * 1. **Convenience** — set `label` (and optionally `icon`/`shortcut`); the
2483
- * default icon + label + shortcut layout renders.
2484
- * 2. **Custom content** — omit `label`; project arbitrary content via
2485
- * `<ng-content>` (badges, two-line layouts, etc.).
2486
- *
2487
- * Existing `<tn-menu [items]="...">` consumers don't need this component;
2488
- * the items-array API continues to work unchanged. Items-array entries and
2489
- * projected `<tn-menu-item>` children render together inside one `<tn-menu>`.
2490
- *
2491
- * Subscribe to either the projected item's own `itemClick` output (preferred,
2492
- * per-item handlers) or the parent menu's `menuItemClick` (uniform handler).
2493
- * Trigger-driven menus close automatically on projected-item click.
2494
- *
2495
- * **Note on keyboard navigation:** projected items render as `role="menuitem"`
2496
- * buttons but do not participate in `CdkMenu` arrow-key navigation (CdkMenuItem
2497
- * requires its parent `CdkMenu` in the same injector tree, which projection
2498
- * breaks). For menus that depend on arrow-key navigation between options, use
2499
- * the `items` input form. Tab/Shift+Tab and Enter/Space activation still work.
2500
- *
2501
- * @example
2502
- * ```html
2503
- * <tn-menu>
2504
- * <tn-menu-item label="JSON" [selected]="format === 'json'"
2505
- * (itemClick)="setFormat('json')" />
2506
- * <tn-menu-item label="CSV" [selected]="format === 'csv'"
2507
- * (itemClick)="setFormat('csv')" />
2508
- * </tn-menu>
2509
- * ```
2510
- */
2511
- declare class TnMenuItemComponent {
2512
- id: _angular_core.InputSignal<string | undefined>;
2513
- label: _angular_core.InputSignal<string | undefined>;
2514
- icon: _angular_core.InputSignal<string | undefined>;
2515
- iconLibrary: _angular_core.InputSignal<"material" | "mdi" | "custom" | "lucide" | undefined>;
2516
- shortcut: _angular_core.InputSignal<string | undefined>;
2517
- disabled: _angular_core.InputSignal<boolean>;
2518
- selected: _angular_core.InputSignal<boolean>;
2519
- testId: _angular_core.InputSignal<string | undefined>;
2520
- itemClick: _angular_core.OutputEmitterRef<MouseEvent>;
2521
- resolvedTestId: _angular_core.Signal<string | undefined>;
2522
- handleClick(event: MouseEvent): void;
2523
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuItemComponent, never>;
2524
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnMenuItemComponent, "tn-menu-item", never, { "id": { "alias": "id"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "iconLibrary": { "alias": "iconLibrary"; "required": false; "isSignal": true; }; "shortcut": { "alias": "shortcut"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, ["*"], true, never>;
2525
- }
2526
-
2527
2560
  /**
2528
2561
  * Directive that attaches a menu to any element.
2529
2562
  * Usage: <button [tnMenuTriggerFor]="menu">Open Menu</button>
2530
2563
  */
2531
- declare class TnMenuTriggerDirective {
2564
+ declare class TnMenuTriggerDirective implements OnDestroy {
2532
2565
  menu: _angular_core.InputSignal<TnMenuComponent>;
2533
2566
  tnMenuPosition: _angular_core.InputSignal<"after" | "before" | "above" | "below">;
2534
2567
  private overlayRef?;
2535
2568
  private isMenuOpen;
2536
2569
  private itemClickSub?;
2570
+ /**
2571
+ * RxJS subscriptions for the current overlay's `backdropClick()` and
2572
+ * `keydownEvents()`. Each `openMenu()` creates a fresh overlay (and fresh
2573
+ * subscriptions), so we collect them here and tear them down in `closeMenu`
2574
+ * / `ngOnDestroy` to keep open→close cycles leak-free.
2575
+ */
2576
+ private overlaySubs;
2537
2577
  private elementRef;
2538
2578
  private overlay;
2539
2579
  private viewContainerRef;
2540
2580
  onClick(): void;
2581
+ onArrowDown(event: Event): void;
2541
2582
  openMenu(): void;
2542
2583
  closeMenu(): void;
2584
+ ngOnDestroy(): void;
2585
+ private disposeOverlaySubs;
2586
+ /**
2587
+ * Return focus to the trigger element so keyboard users land somewhere
2588
+ * sensible after the menu closes (Escape, item click, or backdrop click).
2589
+ *
2590
+ * The host might be a custom-element wrapper like `<tn-button>` /
2591
+ * `<tn-icon-button>` whose host element isn't itself focusable — focus
2592
+ * lives on the inner `<button>` or `<a>`. We focus the host if it's
2593
+ * focusable; otherwise we drill into the first focusable descendant.
2594
+ *
2595
+ * Passes `focusVisible: true` (Chrome/Firefox) so the `:focus-visible`
2596
+ * outline reappears on the restored trigger — without it, browsers treat a
2597
+ * programmatic `.focus()` as non-keyboard and skip the focus ring, which
2598
+ * looks to users like focus has been lost. Safari ignores the option and
2599
+ * falls back to its heuristic; that's acceptable.
2600
+ */
2601
+ private restoreFocusToTrigger;
2543
2602
  private getPositions;
2544
2603
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuTriggerDirective, never>;
2545
2604
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TnMenuTriggerDirective, "[tnMenuTriggerFor]", ["tnMenuTrigger"], { "menu": { "alias": "tnMenuTriggerFor"; "required": true; "isSignal": true; }; "tnMenuPosition": { "alias": "tnMenuPosition"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
@@ -2929,44 +2988,162 @@ interface TnSelectOptionGroup<T = unknown> {
2929
2988
  options: TnSelectOption<T>[];
2930
2989
  disabled?: boolean;
2931
2990
  }
2932
- declare class TnSelectComponent<T = unknown> implements ControlValueAccessor {
2991
+ declare class TnSelectComponent<T = unknown> implements ControlValueAccessor, OnDestroy {
2933
2992
  options: _angular_core.InputSignal<TnSelectOption<T>[]>;
2934
2993
  optionGroups: _angular_core.InputSignal<TnSelectOptionGroup<T>[]>;
2935
2994
  placeholder: _angular_core.InputSignal<string>;
2995
+ /**
2996
+ * Accessible label for the select trigger. When set, this is used as the
2997
+ * trigger's `aria-label` instead of the visible `placeholder` — useful in
2998
+ * contexts (e.g. a pager's page-size dropdown) where the placeholder text
2999
+ * doesn't accurately describe the field's purpose to screen readers.
3000
+ */
3001
+ ariaLabel: _angular_core.InputSignal<string | undefined>;
3002
+ /**
3003
+ * Message shown inside the dropdown when no options (and no option groups)
3004
+ * are available. Defaults to the English `'No options available'`; consumers
3005
+ * with i18n requirements can pass a translated string.
3006
+ */
3007
+ noOptionsLabel: _angular_core.InputSignal<string>;
2936
3008
  disabled: _angular_core.InputSignal<boolean>;
2937
3009
  testId: _angular_core.InputSignal<string>;
2938
3010
  multiple: _angular_core.InputSignal<boolean>;
3011
+ /**
3012
+ * Custom comparator for matching option values against the selected value(s).
3013
+ *
3014
+ * When the option values are objects, **provide this** — the built-in
3015
+ * fallback uses `JSON.stringify`, which is key-order dependent and can
3016
+ * produce false negatives for structurally equal objects. For primitives the
3017
+ * default identity check is fine.
3018
+ *
3019
+ * @example
3020
+ * ```ts
3021
+ * compareWith = (a, b) => a?.id === b?.id;
3022
+ * ```
3023
+ */
2939
3024
  compareWith: _angular_core.InputSignal<((a: T | null, b: T | null) => boolean) | undefined>;
2940
3025
  selectionChange: _angular_core.OutputEmitterRef<T>;
2941
3026
  /** Emits the full array of selected values after each toggle in multiple mode. */
2942
3027
  multiSelectionChange: _angular_core.OutputEmitterRef<T[]>;
2943
3028
  protected isOpen: _angular_core.WritableSignal<boolean>;
3029
+ protected dropdownPosition: _angular_core.WritableSignal<"above" | "below">;
2944
3030
  protected selectedValue: _angular_core.WritableSignal<T | null>;
2945
3031
  protected selectedValues: _angular_core.WritableSignal<T[]>;
3032
+ /** Index into `flatOptions` of the keyboard-focused row (-1 when none). */
3033
+ protected focusedIndex: _angular_core.WritableSignal<number>;
2946
3034
  private formDisabled;
3035
+ private static instanceCounter;
3036
+ private instanceId;
3037
+ /**
3038
+ * Id namespace used by all DOM ids the template emits (dropdown panel,
3039
+ * option rows, group labels). Prefers `testId` when set so tests can target
3040
+ * specific instances; otherwise falls back to a per-instance counter so two
3041
+ * `<tn-select>`s on the same page never collide on `aria-controls`/group ids.
3042
+ */
3043
+ protected idNamespace: _angular_core.Signal<string>;
2947
3044
  isDisabled: _angular_core.Signal<boolean>;
3045
+ /**
3046
+ * Selectable, non-disabled options in display order (regular options first,
3047
+ * then groups). Used by keyboard navigation so we can skip disabled
3048
+ * entries and group headers without a separate filter pass.
3049
+ */
3050
+ navigableOptions: _angular_core.Signal<{
3051
+ option: TnSelectOption<T>;
3052
+ id: string;
3053
+ }[]>;
3054
+ /** Stable DOM id of the currently-highlighted option, for aria-activedescendant. */
3055
+ focusedOptionId: _angular_core.Signal<string | null>;
3056
+ /** Stable DOM id for an option; matches what navigableOptions() assigns. */
3057
+ optionId(option: TnSelectOption<T>): string | null;
3058
+ /** Whether `option` is the keyboard-highlighted item. */
3059
+ isOptionFocused(option: TnSelectOption<T>): boolean;
2948
3060
  private onChange;
2949
3061
  private onTouched;
2950
3062
  private elementRef;
2951
3063
  private cdr;
2952
- constructor();
3064
+ private overlay;
3065
+ private viewContainerRef;
3066
+ private triggerEl;
3067
+ private dropdownTemplate;
3068
+ private overlayRef?;
3069
+ private overlaySubs;
2953
3070
  writeValue(value: T | T[] | null): void;
2954
3071
  registerOnChange(fn: (value: T | T[] | null) => void): void;
2955
3072
  registerOnTouched(fn: () => void): void;
2956
3073
  setDisabledState(isDisabled: boolean): void;
2957
3074
  toggleDropdown(): void;
2958
- closeDropdown(): void;
3075
+ openDropdown(): void;
3076
+ /**
3077
+ * Attach the dropdown panel as a CDK overlay anchored to the trigger.
3078
+ *
3079
+ * Why an overlay (vs. an inline absolutely-positioned panel):
3080
+ * - Escapes parent `overflow: hidden`/clipping in surrounding layouts.
3081
+ * - `outsidePointerEvents()` notifies on outside pointerdown WITHOUT
3082
+ * intercepting the click (no backdrop) — so the user's click reaches
3083
+ * the underlying target while the select closes silently.
3084
+ * - Position is recomputed on scroll so the panel stays attached.
3085
+ * - Width is matched to the trigger so the panel doesn't jump in size.
3086
+ */
3087
+ private attachOverlay;
3088
+ private detachOverlay;
3089
+ ngOnDestroy(): void;
3090
+ /**
3091
+ * Closes the dropdown.
3092
+ *
3093
+ * @param restoreFocus When `true` (the default), returns focus to the
3094
+ * trigger. Used for explicit closes — Escape, Enter/Space activation,
3095
+ * option click — where the user is still interacting with the select.
3096
+ * Pass `false` for click-outside / blur paths so we don't steal focus
3097
+ * from the element the user actually navigated to.
3098
+ */
3099
+ closeDropdown(restoreFocus?: boolean): void;
2959
3100
  onOptionClick(option: TnSelectOption<T>, groupDisabled?: boolean): void;
2960
3101
  selectOption(option: TnSelectOption<T>): void;
2961
3102
  private toggleOption;
2962
3103
  isOptionSelected(option: TnSelectOption<T>): boolean;
2963
- getDisplayText: _angular_core.Signal<string>;
3104
+ protected displayText: _angular_core.Signal<string>;
2964
3105
  private findOptionByValue;
2965
- hasAnyOptions: _angular_core.Signal<boolean>;
3106
+ protected hasAnyOptions: _angular_core.Signal<boolean>;
3107
+ /** One-shot guard so the object-compare warning fires at most once per instance. */
3108
+ private warnedAboutObjectCompare;
3109
+ /**
3110
+ * Compares two option values for equality.
3111
+ *
3112
+ * - Uses `compareWith` when provided (the supported path for object values).
3113
+ * - Falls back to strict identity (`===`) — adequate for primitives.
3114
+ * - For object values WITHOUT `compareWith` we return `false` (no
3115
+ * structural compare) and emit a one-time warning. The previous
3116
+ * `JSON.stringify` fallback was key-order sensitive and produced silent
3117
+ * false-negatives that were hard to diagnose; returning `false` makes the
3118
+ * misuse loud (selection won't match) and the warning points to the fix.
3119
+ *
3120
+ * The warning is **unconditional** (not gated on `isDevMode()`) so prod
3121
+ * monitoring picks it up — consumers relying on the old stringify fallback
3122
+ * would otherwise see selections silently stop matching after upgrade with
3123
+ * no signal in production logs.
3124
+ */
2966
3125
  private compareValues;
3126
+ /**
3127
+ * Keyboard navigation for the combobox trigger.
3128
+ *
3129
+ * - **ArrowDown / ArrowUp** opens the dropdown if closed; otherwise moves
3130
+ * the keyboard-focus highlight (via aria-activedescendant) up/down,
3131
+ * skipping disabled options and group headers.
3132
+ * - **Home / End** jump to the first / last enabled option.
3133
+ * - **Enter / Space** opens the dropdown if closed; if open and an option
3134
+ * is highlighted, selects that option (in single mode) or toggles it
3135
+ * (in multiple mode).
3136
+ * - **Escape** closes the dropdown without changing the selection.
3137
+ *
3138
+ * All navigation keys call `event.preventDefault()` so the page does not
3139
+ * scroll while the user is moving through options.
3140
+ */
2967
3141
  onKeydown(event: KeyboardEvent): void;
3142
+ private moveFocus;
3143
+ private activateFocusedOption;
3144
+ private scrollFocusedIntoView;
2968
3145
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnSelectComponent<any>, never>;
2969
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnSelectComponent<any>, "tn-select", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "optionGroups": { "alias": "optionGroups"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; "multiSelectionChange": "multiSelectionChange"; }, never, never, true, never>;
3146
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnSelectComponent<any>, "tn-select", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "optionGroups": { "alias": "optionGroups"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "noOptionsLabel": { "alias": "noOptionsLabel"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; "multiSelectionChange": "multiSelectionChange"; }, never, never, true, never>;
2970
3147
  }
2971
3148
 
2972
3149
  /**
@@ -3768,8 +3945,49 @@ declare class TnTableComponent<T = unknown> implements OnInit {
3768
3945
  selectable: _angular_core.InputSignal<boolean>;
3769
3946
  expandable: _angular_core.InputSignal<boolean>;
3770
3947
  bordered: _angular_core.InputSignal<boolean>;
3948
+ /**
3949
+ * Marks a single row as "active" — adds the `tn-table__row--active` class
3950
+ * and a left-side indicator bar. Set to `null` (default) to clear.
3951
+ *
3952
+ * **Matched by object identity (`===`)** against the row references in
3953
+ * `dataSource`. Pass the exact reference you got from the data source (e.g.
3954
+ * via the `rowClick` event or a lookup into `dataSource()`), not a
3955
+ * structurally-equal copy — `{ id: 1 } !== { id: 1 }` and the row will not
3956
+ * highlight. This differs from `tn-select`, which supports a `compareWith`
3957
+ * input for object values; the table intentionally does not, because the
3958
+ * common use case (clicking a row to mark it active) already gives the
3959
+ * caller the original reference. If you need structural equality, look up
3960
+ * the row by id in your data source before assigning here.
3961
+ */
3962
+ activeRow: _angular_core.InputSignal<T | null>;
3963
+ /**
3964
+ * Overrides the active-row background color. Accepts any CSS color value
3965
+ * (`#hex`, `rgb()`, `var(--token)`). Defaults to `--tn-bg3` when null.
3966
+ */
3967
+ activeBg: _angular_core.InputSignal<string | null>;
3968
+ /**
3969
+ * Overrides the left-side active-row indicator color. Defaults to
3970
+ * `--tn-primary` when null.
3971
+ */
3972
+ activeIndicator: _angular_core.InputSignal<string | null>;
3973
+ /**
3974
+ * When true, shows a spinner overlay over the table. Existing rows remain
3975
+ * visible (dimmed) so reloads don't cause layout jumps; if there are no rows
3976
+ * yet, the spinner replaces the empty state.
3977
+ */
3978
+ loading: _angular_core.InputSignal<boolean>;
3979
+ /** Accessible label announced while loading. */
3980
+ loadingMessage: _angular_core.InputSignal<string>;
3981
+ /**
3982
+ * When true, rows become keyboard-focusable (tabindex=0) and clicking or
3983
+ * pressing Enter/Space emits `rowClick`. Use this for "click row to view
3984
+ * details" patterns. Independent of `selectable` (checkbox) and `expandable`.
3985
+ */
3986
+ clickable: _angular_core.InputSignal<boolean>;
3771
3987
  sortChange: _angular_core.OutputEmitterRef<TnSortEvent>;
3772
3988
  selectionChange: _angular_core.OutputEmitterRef<T[]>;
3989
+ /** Emits the row when a clickable row is activated (click or Enter/Space). */
3990
+ rowClick: _angular_core.OutputEmitterRef<T>;
3773
3991
  columnDefs: _angular_core.Signal<readonly TnTableColumnDirective[]>;
3774
3992
  detailRowDef: _angular_core.Signal<TnDetailRowDefDirective | undefined>;
3775
3993
  sortColumn: _angular_core.WritableSignal<string>;
@@ -3798,13 +4016,16 @@ declare class TnTableComponent<T = unknown> implements OnInit {
3798
4016
  isSorted(column: string): boolean;
3799
4017
  toggleRowExpansion(row: T): void;
3800
4018
  isRowExpanded(row: T): boolean;
4019
+ isRowActive(row: T): boolean;
4020
+ onRowClick(row: T): void;
4021
+ onRowKeydown(event: KeyboardEvent, row: T): void;
3801
4022
  toggleSelectAll(): void;
3802
4023
  toggleRowSelection(row: T): void;
3803
4024
  isRowSelected(row: T): boolean;
3804
4025
  getColumnDef(columnName: string): TnTableColumnDirective | undefined;
3805
4026
  getCellValue(row: T, column: string): unknown;
3806
4027
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTableComponent<any>, never>;
3807
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnTableComponent<any>, "tn-table", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "displayedColumns": { "alias": "displayedColumns"; "required": false; "isSignal": true; }; "trackBy": { "alias": "trackBy"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; }, ["columnDefs", "detailRowDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
4028
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnTableComponent<any>, "tn-table", never, { "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "displayedColumns": { "alias": "displayedColumns"; "required": false; "isSignal": true; }; "trackBy": { "alias": "trackBy"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "activeRow": { "alias": "activeRow"; "required": false; "isSignal": true; }; "activeBg": { "alias": "activeBg"; "required": false; "isSignal": true; }; "activeIndicator": { "alias": "activeIndicator"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingMessage": { "alias": "loadingMessage"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; }, ["columnDefs", "detailRowDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
3808
4029
  }
3809
4030
 
3810
4031
  /**
@@ -3922,6 +4143,44 @@ declare class TnTableHarness extends ComponentHarness {
3922
4143
  * @returns Promise resolving to true if the row has the expanded class.
3923
4144
  */
3924
4145
  isRowExpanded(rowIndex: number): Promise<boolean>;
4146
+ /**
4147
+ * Clicks a row (for tables with `clickable` enabled).
4148
+ *
4149
+ * @param rowIndex Zero-based index of the data row.
4150
+ */
4151
+ clickRow(rowIndex: number): Promise<void>;
4152
+ /**
4153
+ * Sends a keyboard event to a row (Enter/Space activate clickable rows).
4154
+ *
4155
+ * @param rowIndex Zero-based index of the data row.
4156
+ * @param key Which key to press — Enter or Space.
4157
+ */
4158
+ pressKeyOnRow(rowIndex: number, key: 'enter' | 'space'): Promise<void>;
4159
+ /**
4160
+ * Checks if a row is keyboard-focusable (tabindex=0).
4161
+ *
4162
+ * @param rowIndex Zero-based index of the data row.
4163
+ */
4164
+ isRowFocusable(rowIndex: number): Promise<boolean>;
4165
+ /**
4166
+ * Checks whether the table is currently in the loading state.
4167
+ *
4168
+ * @returns Promise resolving to true if the loading overlay is visible.
4169
+ */
4170
+ isLoading(): Promise<boolean>;
4171
+ /**
4172
+ * Checks if a data row is currently marked active.
4173
+ *
4174
+ * @param rowIndex Zero-based index of the data row.
4175
+ * @returns Promise resolving to true if the row has the active class.
4176
+ */
4177
+ isRowActive(rowIndex: number): Promise<boolean>;
4178
+ /**
4179
+ * Gets the index of the currently active row, or null if none is active.
4180
+ *
4181
+ * @returns Promise resolving to the active row index or null.
4182
+ */
4183
+ getActiveRowIndex(): Promise<number | null>;
3925
4184
  /**
3926
4185
  * Gets the text content of an expanded detail row.
3927
4186
  *
@@ -3943,6 +4202,238 @@ declare class TnTableHarness extends ComponentHarness {
3943
4202
  interface TnTableHarnessFilters extends BaseHarnessFilters {
3944
4203
  }
3945
4204
 
4205
+ /**
4206
+ * Default labels rendered inside `tn-table-pager`. Consumers can override any
4207
+ * subset of these at the app root by providing a value for the
4208
+ * `TN_TABLE_PAGER_LABELS` token — typically wired up to an i18n service so the
4209
+ * pager picks up translated copy without each call site having to bind six
4210
+ * label inputs. Inputs on `<tn-table-pager>` still win when explicitly set.
4211
+ */
4212
+ interface TnTablePagerLabels {
4213
+ itemsPerPage: string;
4214
+ of: string;
4215
+ firstPage: string;
4216
+ previousPage: string;
4217
+ nextPage: string;
4218
+ lastPage: string;
4219
+ /** Accessible label applied to the pager's `navigation` landmark. */
4220
+ tablePagination: string;
4221
+ }
4222
+ /** English defaults used when no `TN_TABLE_PAGER_LABELS` provider is registered. */
4223
+ declare const TN_TABLE_PAGER_DEFAULT_LABELS: TnTablePagerLabels;
4224
+ /**
4225
+ * DI token for app-wide default labels. Provide either a static object or a
4226
+ * `Signal<TnTablePagerLabels>` — the latter lets the pager react to language
4227
+ * changes when the consumer wires it up to an i18n service.
4228
+ *
4229
+ * Explicit input bindings on `<tn-table-pager>` still win over these defaults.
4230
+ */
4231
+ declare const TN_TABLE_PAGER_LABELS: InjectionToken<TnTablePagerLabels | Signal<TnTablePagerLabels>>;
4232
+ /** Pagination state shared between `tn-table-pager` and an arbitrary data layer. */
4233
+ interface TnTablePagination {
4234
+ pageNumber: number | null;
4235
+ pageSize: number | null;
4236
+ }
4237
+ /**
4238
+ * Minimal contract a data layer must implement to be driven by `tn-table-pager`
4239
+ * via the `dataProvider` input. Designed to match the shape of NAS's
4240
+ * `ix-table` `DataProvider<T>` so existing wrappers can be removed — but the
4241
+ * library itself only depends on this slim interface, not on any consumer code.
4242
+ */
4243
+ interface TnTableDataProvider {
4244
+ /** Total number of rows the data layer is aware of, across all pages. */
4245
+ totalRows: number;
4246
+ /** The data layer's current pagination state. */
4247
+ pagination: TnTablePagination;
4248
+ /**
4249
+ * Emits whenever the data layer's state changes in a way the pager should
4250
+ * react to. The emitted value itself isn't read — it's purely a "something
4251
+ * changed, re-read me" notification.
4252
+ *
4253
+ * **Implementations must emit on**:
4254
+ * - page-number changes (the canonical case),
4255
+ * - page-size changes the data layer applies on its own, and
4256
+ * - **`totalRows` changes** (e.g. after a filter/refresh).
4257
+ *
4258
+ * The pager reads `totalRows` imperatively inside its sync handler, so a
4259
+ * provider that only emits on `pageNumber` won't surface row-count updates
4260
+ * to the displayed range.
4261
+ *
4262
+ * Any stream type (`Subject`, `BehaviorSubject`, `ReplaySubject`, …) works —
4263
+ * the pager guards against feedback loops by remembering the last pagination
4264
+ * it pushed and treating a matching emission as its own echo. Replay
4265
+ * semantics are therefore harmless but not required.
4266
+ */
4267
+ currentPage$: Observable<unknown>;
4268
+ /** Pushes new pagination to the data layer; typically triggers a data refresh. */
4269
+ setPagination(pagination: TnTablePagination): void;
4270
+ }
4271
+ /**
4272
+ * Pagination control for the `tn-table` (or any list view that paginates).
4273
+ *
4274
+ * Works in two modes:
4275
+ *
4276
+ * 1. **Dumb mode** — bind `[currentPage]`, `[pageSize]`, `[totalItems]` and listen
4277
+ * to `pageChange` / `pageSizeChange`. The component owns no provider state.
4278
+ * 2. **Data-provider mode** — bind `[dataProvider]` and the component drives
4279
+ * `setPagination()` on the provider, mirrors `totalRows`, and reacts to
4280
+ * `currentPage$` changes (with an internal guard against feedback loops).
4281
+ *
4282
+ * @example Dumb mode
4283
+ * ```html
4284
+ * <tn-table-pager
4285
+ * [(currentPage)]="page"
4286
+ * [(pageSize)]="size"
4287
+ * [totalItems]="total()"
4288
+ * (pageChange)="loadPage($event)" />
4289
+ * ```
4290
+ *
4291
+ * @example Data-provider mode (replaces the typical ix-table-pager wrapper)
4292
+ * ```html
4293
+ * <tn-table-pager
4294
+ * [dataProvider]="dataProvider()"
4295
+ * [pageSize]="dataProvider().pagination.pageSize ?? 50"
4296
+ * [currentPage]="dataProvider().pagination.pageNumber ?? 1"
4297
+ * [itemsPerPageLabel]="'Items per page' | translate" />
4298
+ * ```
4299
+ */
4300
+ declare class TnTablePagerComponent {
4301
+ private destroyRef;
4302
+ /**
4303
+ * Normalize the injected token into a Signal so consumers can supply either
4304
+ * a plain object or a reactive signal (e.g. derived from a TranslateService's
4305
+ * onLangChange) and the pager re-renders when labels change.
4306
+ */
4307
+ private readonly defaultLabels;
4308
+ /** 1-based index of the currently displayed page. */
4309
+ currentPage: _angular_core.ModelSignal<number>;
4310
+ /** Number of items per page. */
4311
+ pageSize: _angular_core.ModelSignal<number>;
4312
+ /** Selectable page-size values rendered in the dropdown. */
4313
+ pageSizeOptions: _angular_core.InputSignal<number[]>;
4314
+ /**
4315
+ * Total item count across all pages — drives `totalPages` and the range
4316
+ * labels. Ignored when `dataProvider` is set (the provider's `totalRows`
4317
+ * wins, so consumers don't have to keep both in sync).
4318
+ */
4319
+ totalItems: _angular_core.InputSignal<number>;
4320
+ /**
4321
+ * Optional data-provider integration. When supplied, the pager initializes
4322
+ * the provider's pagination on the first effect run, mirrors `totalRows`
4323
+ * into the displayed total, and listens to `currentPage$` to sync external
4324
+ * page changes. Page/size changes from user input are pushed back via
4325
+ * `setPagination()`.
4326
+ */
4327
+ dataProvider: _angular_core.InputSignal<TnTableDataProvider | undefined>;
4328
+ /**
4329
+ * Label inputs are nullable on purpose: the template reads the resolved
4330
+ * `*Label` computed signals below, which fall back to the DI-provided default
4331
+ * (a signal — so language changes propagate live). An explicit input binding
4332
+ * always wins.
4333
+ */
4334
+ itemsPerPageLabel: _angular_core.InputSignal<string | undefined>;
4335
+ ofLabel: _angular_core.InputSignal<string | undefined>;
4336
+ firstPageLabel: _angular_core.InputSignal<string | undefined>;
4337
+ previousPageLabel: _angular_core.InputSignal<string | undefined>;
4338
+ nextPageLabel: _angular_core.InputSignal<string | undefined>;
4339
+ lastPageLabel: _angular_core.InputSignal<string | undefined>;
4340
+ tablePaginationLabel: _angular_core.InputSignal<string | undefined>;
4341
+ /** Resolved labels: explicit input takes precedence over the DI default. */
4342
+ protected resolvedItemsPerPageLabel: Signal<string>;
4343
+ protected resolvedOfLabel: Signal<string>;
4344
+ protected resolvedFirstPageLabel: Signal<string>;
4345
+ protected resolvedPreviousPageLabel: Signal<string>;
4346
+ protected resolvedNextPageLabel: Signal<string>;
4347
+ protected resolvedLastPageLabel: Signal<string>;
4348
+ protected resolvedTablePaginationLabel: Signal<string>;
4349
+ /** Emits the new 1-based page number whenever the user navigates. */
4350
+ pageChange: _angular_core.OutputEmitterRef<number>;
4351
+ /** Emits the new page-size value when the dropdown changes. */
4352
+ pageSizeChange: _angular_core.OutputEmitterRef<number>;
4353
+ /**
4354
+ * Total items reported by the data provider (when one is bound). Falls back
4355
+ * to `totalItems` input otherwise — see `effectiveTotalItems`.
4356
+ */
4357
+ private providerTotalItems;
4358
+ /** The provider reference we're currently bound to (used to detect swaps). */
4359
+ private currentProvider;
4360
+ /** Subscription to the current provider's `currentPage$` — torn down on swap. */
4361
+ private providerSub;
4362
+ /**
4363
+ * Last pagination value we pushed to the provider. Used to recognize the
4364
+ * provider's resulting emission as our own echo and break the feedback loop
4365
+ * (setPagination → provider emits → syncFromProvider → setPagination …)
4366
+ * regardless of whether the provider emits synchronously or asynchronously,
4367
+ * and regardless of whether its stream replays on subscribe.
4368
+ */
4369
+ private lastPushedPagination;
4370
+ protected effectiveTotalItems: Signal<number>;
4371
+ protected totalPages: Signal<number>;
4372
+ protected firstItemOnPage: Signal<number>;
4373
+ protected lastItemOnPage: Signal<number>;
4374
+ protected isFirstPageDisabled: Signal<boolean>;
4375
+ protected isLastPageDisabled: Signal<boolean>;
4376
+ protected pageSizeSelectOptions: Signal<TnSelectOption<number>[]>;
4377
+ constructor();
4378
+ private syncFromProvider;
4379
+ /**
4380
+ * Public navigation API: jump to a specific 1-based page. Used both by the
4381
+ * template (first/last buttons) and by consumers who want to drive the pager
4382
+ * programmatically. Out-of-range values and no-op transitions are silently
4383
+ * ignored. Sibling helpers `previousPage` / `nextPage` are template-only and
4384
+ * therefore `protected` — `goToPage` is intentionally part of the public API.
4385
+ */
4386
+ goToPage(pageNumber: number): void;
4387
+ protected previousPage(): void;
4388
+ protected nextPage(): void;
4389
+ protected onPageSizeChange(value: number): void;
4390
+ /** Forwards the current page/size to the data provider, if one is bound. */
4391
+ private pushToProvider;
4392
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTablePagerComponent, never>;
4393
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnTablePagerComponent, "tn-table-pager", never, { "currentPage": { "alias": "currentPage"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "totalItems": { "alias": "totalItems"; "required": false; "isSignal": true; }; "dataProvider": { "alias": "dataProvider"; "required": false; "isSignal": true; }; "itemsPerPageLabel": { "alias": "itemsPerPageLabel"; "required": false; "isSignal": true; }; "ofLabel": { "alias": "ofLabel"; "required": false; "isSignal": true; }; "firstPageLabel": { "alias": "firstPageLabel"; "required": false; "isSignal": true; }; "previousPageLabel": { "alias": "previousPageLabel"; "required": false; "isSignal": true; }; "nextPageLabel": { "alias": "nextPageLabel"; "required": false; "isSignal": true; }; "lastPageLabel": { "alias": "lastPageLabel"; "required": false; "isSignal": true; }; "tablePaginationLabel": { "alias": "tablePaginationLabel"; "required": false; "isSignal": true; }; }, { "currentPage": "currentPageChange"; "pageSize": "pageSizeChange"; "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; }, never, never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
4394
+ }
4395
+
4396
+ /**
4397
+ * Harness for interacting with `tn-table-pager` in tests.
4398
+ *
4399
+ * @example
4400
+ * ```ts
4401
+ * const pager = await loader.getHarness(TnTablePagerHarness);
4402
+ * await pager.nextPage();
4403
+ * expect(await pager.getRangeText()).toBe('21 – 40 of 47');
4404
+ * ```
4405
+ */
4406
+ declare class TnTablePagerHarness extends ComponentHarness {
4407
+ static hostSelector: string;
4408
+ static with(options?: TnTablePagerHarnessFilters): HarnessPredicate<TnTablePagerHarness>;
4409
+ private firstButton;
4410
+ private previousButton;
4411
+ private nextButton;
4412
+ private lastButton;
4413
+ private pageSizeSelect;
4414
+ private rangeEl;
4415
+ /**
4416
+ * Returns the rendered range text (e.g. `"1 – 20 of 47"`).
4417
+ */
4418
+ getRangeText(): Promise<string>;
4419
+ /** Clicks the "first page" button. */
4420
+ goToFirstPage(): Promise<void>;
4421
+ /** Clicks the "previous page" button. */
4422
+ previousPage(): Promise<void>;
4423
+ /** Clicks the "next page" button. */
4424
+ nextPage(): Promise<void>;
4425
+ /** Clicks the "last page" button. */
4426
+ goToLastPage(): Promise<void>;
4427
+ isFirstButtonDisabled(): Promise<boolean>;
4428
+ isPreviousButtonDisabled(): Promise<boolean>;
4429
+ isNextButtonDisabled(): Promise<boolean>;
4430
+ isLastButtonDisabled(): Promise<boolean>;
4431
+ /** Returns the harness for the underlying page-size `tn-select`. */
4432
+ getPageSizeSelect(): Promise<TnSelectHarness>;
4433
+ }
4434
+ interface TnTablePagerHarnessFilters extends BaseHarnessFilters {
4435
+ }
4436
+
3946
4437
  /** Flat node with expandable and level information */
3947
4438
  interface TnFlatTreeNode<T = unknown> {
3948
4439
  data: T;
@@ -4939,6 +5430,22 @@ declare class TnButtonToggleGroupComponent implements ControlValueAccessor {
4939
5430
  name: _angular_core.InputSignal<string>;
4940
5431
  ariaLabel: _angular_core.InputSignal<string>;
4941
5432
  ariaLabelledby: _angular_core.InputSignal<string>;
5433
+ /**
5434
+ * Overrides the background color of checked toggles in this group. Accepts
5435
+ * any CSS color value (`#hex`, `rgb()`, `var(--token)`, etc.). When null
5436
+ * (default), falls back to `--tn-alt-bg2`.
5437
+ */
5438
+ checkedBg: _angular_core.InputSignal<string | null>;
5439
+ /**
5440
+ * Overrides the text color of checked toggles in this group. Defaults to
5441
+ * `--tn-fg1` when null.
5442
+ */
5443
+ checkedColor: _angular_core.InputSignal<string | null>;
5444
+ /**
5445
+ * Overrides the border color of checked toggles in this group. Defaults to
5446
+ * `--tn-lines` when null.
5447
+ */
5448
+ checkedBorder: _angular_core.InputSignal<string | null>;
4942
5449
  /**
4943
5450
  * Test-id applied to the group root. Rendered under whichever attribute name
4944
5451
  * is configured via `TN_TEST_ATTR` (default `data-testid`).
@@ -4965,7 +5472,7 @@ declare class TnButtonToggleGroupComponent implements ControlValueAccessor {
4965
5472
  private updateTogglesFromValue;
4966
5473
  private updateTogglesFromValues;
4967
5474
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnButtonToggleGroupComponent, never>;
4968
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnButtonToggleGroupComponent, "tn-button-toggle-group", never, { "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "ariaLabelledby"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "change": "change"; }, ["buttonToggles"], ["*"], true, never>;
5475
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnButtonToggleGroupComponent, "tn-button-toggle-group", never, { "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaLabelledby": { "alias": "ariaLabelledby"; "required": false; "isSignal": true; }; "checkedBg": { "alias": "checkedBg"; "required": false; "isSignal": true; }; "checkedColor": { "alias": "checkedColor"; "required": false; "isSignal": true; }; "checkedBorder": { "alias": "checkedBorder"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; }, { "change": "change"; }, ["buttonToggles"], ["*"], true, never>;
4969
5476
  }
4970
5477
 
4971
5478
  declare class TnButtonToggleComponent implements ControlValueAccessor {
@@ -6343,5 +6850,5 @@ declare const TN_THEME_DEFINITIONS: readonly TnThemeDefinition[];
6343
6850
  */
6344
6851
  declare const THEME_MAP: Map<TnTheme, TnThemeDefinition>;
6345
6852
 
6346
- export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
6347
- export type { AutocompleteHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CheckboxHarnessFilters, ChipColor, CreateFolderEvent, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FilePickerCallbacks, FilePickerError, FilePickerMode, FileSystemItem, FormFieldHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, PathSegment, PlatformType, ProgressBarMode, RadioHarnessFilters, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnBannerType, TnButtonToggleType, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnConfirmDialogData, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFlatTreeNode, TnMenuItem, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataSource, TnTableHarnessFilters, TnTestAttrName, TnThemeDefinition, TnToastCall, TnToastConfig, TooltipPosition, YearCell };
6853
+ export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, libIconMarker, registerLucideIcons, setupLucideIntegration, tnIconMarker };
6854
+ export type { AutocompleteHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CheckboxHarnessFilters, ChipColor, CreateFolderEvent, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FilePickerCallbacks, FilePickerError, FilePickerMode, FileSystemItem, FormFieldHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, PathSegment, PlatformType, ProgressBarMode, RadioHarnessFilters, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnBannerType, TnButtonToggleType, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnConfirmDialogData, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFlatTreeNode, TnMenuItem, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataProvider, TnTableDataSource, TnTableHarnessFilters, TnTablePagerHarnessFilters, TnTablePagerLabels, TnTablePagination, TnTestAttrName, TnThemeDefinition, TnToastCall, TnToastConfig, TooltipPosition, YearCell };