@truenas/ui-components 0.1.59 → 0.1.61

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,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { ElementRef, OnDestroy, AfterViewInit, AfterContentInit, TemplateRef, Provider, ChangeDetectorRef, OnInit, InjectionToken, Signal, PipeTransform, ViewContainerRef, AfterViewChecked, ComponentRef } from '@angular/core';
2
+ import { ElementRef, OnDestroy, AfterViewInit, OnInit, TemplateRef, AfterContentInit, Provider, ChangeDetectorRef, 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';
@@ -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,9 +717,60 @@ 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
+ type TooltipPosition = 'above' | 'below' | 'left' | 'right' | 'before' | 'after';
721
+ declare class TnTooltipDirective implements OnInit, OnDestroy {
722
+ message: _angular_core.InputSignal<string>;
723
+ position: _angular_core.InputSignal<TooltipPosition>;
724
+ disabled: _angular_core.InputSignal<boolean>;
725
+ showDelay: _angular_core.InputSignal<number>;
726
+ hideDelay: _angular_core.InputSignal<number>;
727
+ tooltipClass: _angular_core.InputSignal<string>;
728
+ private _overlayRef;
729
+ private _tooltipInstance;
730
+ private _showTimeout;
731
+ private _hideTimeout;
732
+ private _isTooltipVisible;
733
+ private _positionSub;
734
+ private _tooltipId;
735
+ /**
736
+ * Only point `aria-describedby` at the tooltip when there is actually a message to
737
+ * describe. Otherwise every host (e.g. an icon button with no tooltip) would carry a
738
+ * dangling reference to a tooltip element that is never rendered.
739
+ */
740
+ protected get _ariaDescribedBy(): string | null;
741
+ private _overlay;
742
+ private _elementRef;
743
+ private _viewContainerRef;
744
+ private _overlayPositionBuilder;
745
+ ngOnInit(): void;
746
+ ngOnDestroy(): void;
747
+ _onMouseEnter(): void;
748
+ _onMouseLeave(): void;
749
+ _onFocus(): void;
750
+ _onBlur(): void;
751
+ _onKeydown(event: KeyboardEvent): void;
752
+ /** Shows the tooltip */
753
+ show(delay?: number): void;
754
+ /** Hides the tooltip */
755
+ hide(delay?: number): void;
756
+ /** Toggle the tooltip visibility */
757
+ toggle(): void;
758
+ private _createOverlay;
759
+ private _resolvePosition;
760
+ private _attachTooltip;
761
+ private _getPositions;
762
+ private _clearTimeouts;
763
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTooltipDirective, never>;
764
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TnTooltipDirective, "[tnTooltip]", never, { "message": { "alias": "tnTooltip"; "required": false; "isSignal": true; }; "position": { "alias": "tnTooltipPosition"; "required": false; "isSignal": true; }; "disabled": { "alias": "tnTooltipDisabled"; "required": false; "isSignal": true; }; "showDelay": { "alias": "tnTooltipShowDelay"; "required": false; "isSignal": true; }; "hideDelay": { "alias": "tnTooltipHideDelay"; "required": false; "isSignal": true; }; "tooltipClass": { "alias": "tnTooltipClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
765
+ }
766
+
767
+ declare class TnIconButtonComponent implements AfterViewInit {
718
768
  disabled: _angular_core.InputSignal<boolean>;
769
+ /** Compact variant with reduced padding, for dense contexts like toolbars. */
770
+ dense: _angular_core.InputSignal<boolean>;
719
771
  ariaLabel: _angular_core.InputSignal<string | undefined>;
772
+ /** Reflects an expanded/collapsed state (e.g. toggling a panel) onto the inner button. */
773
+ ariaExpanded: _angular_core.InputSignal<boolean | undefined>;
720
774
  /**
721
775
  * Test-id applied to the rendered `<button>` element. Rendered under whichever attribute
722
776
  * name is configured via `TN_TEST_ATTR` (default `data-testid`).
@@ -726,12 +780,25 @@ declare class TnIconButtonComponent {
726
780
  size: _angular_core.InputSignal<IconSize>;
727
781
  color: _angular_core.InputSignal<string | undefined>;
728
782
  tooltip: _angular_core.InputSignal<string | undefined>;
783
+ /** Position of the styled tooltip relative to the button. */
784
+ tooltipPosition: _angular_core.InputSignal<TooltipPosition>;
729
785
  library: _angular_core.InputSignal<IconLibraryType | undefined>;
786
+ /** Extra class(es) applied to the inner icon, e.g. for animations or state colors. */
787
+ iconClass: _angular_core.InputSignal<string>;
730
788
  onClick: _angular_core.OutputEmitterRef<MouseEvent>;
789
+ private hostRef;
790
+ private buttonRef;
731
791
  classes: _angular_core.Signal<string[]>;
732
792
  effectiveAriaLabel: _angular_core.Signal<string>;
793
+ /**
794
+ * Focuses the inner native `<button>`. Exposed as a public method so callers
795
+ * with a `TnIconButtonComponent` reference (e.g. `@ViewChild`) can focus it
796
+ * without reaching into the DOM themselves.
797
+ */
798
+ focus(options?: FocusOptions): void;
799
+ ngAfterViewInit(): void;
733
800
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnIconButtonComponent, never>;
734
- 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>;
801
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnIconButtonComponent, "tn-icon-button", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "dense": { "alias": "dense"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "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; }; "tooltipPosition": { "alias": "tooltipPosition"; "required": false; "isSignal": true; }; "library": { "alias": "library"; "required": false; "isSignal": true; }; "iconClass": { "alias": "iconClass"; "required": false; "isSignal": true; }; }, { "onClick": "onClick"; }, never, ["*"], true, never>;
735
802
  }
736
803
 
737
804
  /**
@@ -1298,6 +1365,68 @@ interface TnCardFooterLink {
1298
1365
  testId?: string;
1299
1366
  }
1300
1367
 
1368
+ /**
1369
+ * Projection-based menu item for use inside `<tn-menu>`.
1370
+ *
1371
+ * Two authoring modes:
1372
+ * 1. **Convenience** — set `label` (and optionally `icon`/`shortcut`); the
1373
+ * default icon + label + shortcut layout renders.
1374
+ * 2. **Custom content** — omit `label`; project arbitrary content via
1375
+ * `<ng-content>` (badges, two-line layouts, etc.).
1376
+ *
1377
+ * Existing `<tn-menu [items]="...">` consumers don't need this component;
1378
+ * the items-array API continues to work unchanged. Items-array entries and
1379
+ * projected `<tn-menu-item>` children render together inside one `<tn-menu>`,
1380
+ * share keyboard navigation, and look identical.
1381
+ *
1382
+ * **How it works:** the component itself renders nothing visible — it acts as
1383
+ * a configuration declaration. The parent `<tn-menu>` collects projected items
1384
+ * via `contentChildren`, re-renders them inside the CDK overlay alongside
1385
+ * items-array entries (with `cdkMenuItem` for full arrow-key navigation), and
1386
+ * routes clicks back to each item's `itemClick` output.
1387
+ *
1388
+ * **Accessibility:** because items are re-rendered inside the parent's
1389
+ * `CdkMenu`, all keyboard semantics from `@angular/cdk/menu` apply uniformly:
1390
+ * Arrow Up/Down/Home/End to move focus, Enter/Space to activate, Esc to
1391
+ * close, type-ahead search. Disabled items are skipped.
1392
+ *
1393
+ * **Limitation — custom content & keyboard nav:** the projected
1394
+ * `<ng-content>` (custom-content mode) is rendered inside a `<button
1395
+ * cdkMenuItem>` wrapper that owns Enter/Space/Arrow handling. Interactive
1396
+ * elements inside the projection (a nested `<button>`, link, toggle, etc.)
1397
+ * receive clicks but **do not** participate in CDK arrow-key navigation —
1398
+ * the wrapper button is the only keyboard-reachable target. Prefer
1399
+ * display-only content (icons, badges, two-line text); for cases that need
1400
+ * an extra interactive control, build a custom menu host with `cdkMenu`
1401
+ * directly instead of `<tn-menu>`.
1402
+ *
1403
+ * @example
1404
+ * ```html
1405
+ * <tn-menu>
1406
+ * <tn-menu-item label="JSON" [selected]="format === 'json'"
1407
+ * (itemClick)="setFormat('json')" />
1408
+ * <tn-menu-item label="CSV" [selected]="format === 'csv'"
1409
+ * (itemClick)="setFormat('csv')" />
1410
+ * </tn-menu>
1411
+ * ```
1412
+ */
1413
+ declare class TnMenuItemComponent {
1414
+ id: _angular_core.InputSignal<string | undefined>;
1415
+ label: _angular_core.InputSignal<string | undefined>;
1416
+ icon: _angular_core.InputSignal<string | undefined>;
1417
+ iconLibrary: _angular_core.InputSignal<"material" | "mdi" | "custom" | "lucide" | undefined>;
1418
+ shortcut: _angular_core.InputSignal<string | undefined>;
1419
+ disabled: _angular_core.InputSignal<boolean>;
1420
+ selected: _angular_core.InputSignal<boolean>;
1421
+ testId: _angular_core.InputSignal<string | undefined>;
1422
+ itemClick: _angular_core.OutputEmitterRef<MouseEvent>;
1423
+ /** Template capturing whatever the consumer projected as item content. */
1424
+ content: _angular_core.Signal<TemplateRef<unknown>>;
1425
+ resolvedTestId: _angular_core.Signal<string | undefined>;
1426
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuItemComponent, never>;
1427
+ 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>;
1428
+ }
1429
+
1301
1430
  /**
1302
1431
  * Activates CDK menu hover-to-open behavior for menus opened via custom overlays.
1303
1432
  *
@@ -1335,7 +1464,7 @@ interface TnMenuItem {
1335
1464
  */
1336
1465
  selected?: boolean;
1337
1466
  }
1338
- declare class TnMenuComponent {
1467
+ declare class TnMenuComponent implements OnDestroy {
1339
1468
  items: _angular_core.InputSignal<TnMenuItem[]>;
1340
1469
  contextMenu: _angular_core.InputSignal<boolean>;
1341
1470
  menuItemClick: _angular_core.OutputEmitterRef<TnMenuItem>;
@@ -1344,11 +1473,17 @@ declare class TnMenuComponent {
1344
1473
  menuTemplate: _angular_core.Signal<TemplateRef<unknown>>;
1345
1474
  contextMenuTemplate: _angular_core.Signal<TemplateRef<unknown>>;
1346
1475
  private contextOverlayRef?;
1476
+ private contextBackdropSub?;
1347
1477
  private overlay;
1348
1478
  private viewContainerRef;
1349
1479
  onMenuItemClick(item: TnMenuItem): void;
1350
- private contentItems;
1351
- constructor();
1480
+ contentItems: _angular_core.Signal<readonly TnMenuItemComponent[]>;
1481
+ /**
1482
+ * Click handler for projected `<tn-menu-item>` entries. Emits the item's own
1483
+ * `itemClick` output, re-emits a synthetic entry on `menuItemClick` so
1484
+ * trigger-driven menus close uniformly, and closes any open context menu.
1485
+ */
1486
+ onProjectedItemClick(item: TnMenuItemComponent, event: MouseEvent): void;
1352
1487
  hasChildren: _angular_core.Signal<(item: TnMenuItem) => boolean>;
1353
1488
  onMenuOpen(): void;
1354
1489
  onMenuClose(): void;
@@ -1358,10 +1493,11 @@ declare class TnMenuComponent {
1358
1493
  getMenuTemplate(): TemplateRef<unknown> | null;
1359
1494
  openContextMenuAt(x: number, y: number): void;
1360
1495
  private closeContextMenu;
1496
+ ngOnDestroy(): void;
1361
1497
  onContextMenu(event: MouseEvent): void;
1362
1498
  trackByItemId(index: number, item: TnMenuItem): string;
1363
1499
  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>;
1500
+ 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
1501
  }
1366
1502
 
1367
1503
  declare class TnCardComponent {
@@ -1394,6 +1530,7 @@ declare class TnCardComponent {
1394
1530
  private registerMdiIcons;
1395
1531
  classes: _angular_core.Signal<string[]>;
1396
1532
  hasHeader: _angular_core.Signal<boolean>;
1533
+ hasHeaderRight: _angular_core.Signal<boolean>;
1397
1534
  hasFooter: _angular_core.Signal<boolean>;
1398
1535
  onTitleClick(): void;
1399
1536
  onControlChange(checked: boolean): void;
@@ -1918,7 +2055,7 @@ interface RadioHarnessFilters extends BaseHarnessFilters {
1918
2055
  type SlideToggleColor = 'primary' | 'accent' | 'warn';
1919
2056
  declare class TnSlideToggleComponent implements AfterViewInit, OnDestroy, ControlValueAccessor {
1920
2057
  toggleEl: _angular_core.Signal<ElementRef<HTMLInputElement>>;
1921
- labelPosition: _angular_core.InputSignal<"after" | "before">;
2058
+ labelPosition: _angular_core.InputSignal<"before" | "after">;
1922
2059
  label: _angular_core.InputSignal<string | undefined>;
1923
2060
  disabled: _angular_core.InputSignal<boolean>;
1924
2061
  required: _angular_core.InputSignal<boolean>;
@@ -2083,7 +2220,7 @@ declare class TnTabsComponent implements AfterContentInit, AfterViewInit, OnDest
2083
2220
  tabHeader: _angular_core.Signal<ElementRef<HTMLElement>>;
2084
2221
  selectedIndex: _angular_core.InputSignal<number>;
2085
2222
  orientation: _angular_core.InputSignal<"horizontal" | "vertical">;
2086
- highlightPosition: _angular_core.InputSignal<"top" | "bottom" | "left" | "right">;
2223
+ highlightPosition: _angular_core.InputSignal<"left" | "right" | "top" | "bottom">;
2087
2224
  /**
2088
2225
  * Test-id applied to the tablist root element. Rendered under whichever attribute name
2089
2226
  * is configured via `TN_TEST_ATTR` (default `data-testid`).
@@ -2475,71 +2612,48 @@ interface TabsHarnessFilters extends BaseHarnessFilters {
2475
2612
  hasTab?: string | RegExp;
2476
2613
  }
2477
2614
 
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
2615
  /**
2528
2616
  * Directive that attaches a menu to any element.
2529
2617
  * Usage: <button [tnMenuTriggerFor]="menu">Open Menu</button>
2530
2618
  */
2531
- declare class TnMenuTriggerDirective {
2619
+ declare class TnMenuTriggerDirective implements OnDestroy {
2532
2620
  menu: _angular_core.InputSignal<TnMenuComponent>;
2533
- tnMenuPosition: _angular_core.InputSignal<"after" | "before" | "above" | "below">;
2621
+ tnMenuPosition: _angular_core.InputSignal<"above" | "below" | "before" | "after">;
2534
2622
  private overlayRef?;
2535
2623
  private isMenuOpen;
2536
2624
  private itemClickSub?;
2625
+ /**
2626
+ * RxJS subscriptions for the current overlay's `backdropClick()` and
2627
+ * `keydownEvents()`. Each `openMenu()` creates a fresh overlay (and fresh
2628
+ * subscriptions), so we collect them here and tear them down in `closeMenu`
2629
+ * / `ngOnDestroy` to keep open→close cycles leak-free.
2630
+ */
2631
+ private overlaySubs;
2537
2632
  private elementRef;
2538
2633
  private overlay;
2539
2634
  private viewContainerRef;
2540
2635
  onClick(): void;
2636
+ onArrowDown(event: Event): void;
2541
2637
  openMenu(): void;
2542
2638
  closeMenu(): void;
2639
+ ngOnDestroy(): void;
2640
+ private disposeOverlaySubs;
2641
+ /**
2642
+ * Return focus to the trigger element so keyboard users land somewhere
2643
+ * sensible after the menu closes (Escape, item click, or backdrop click).
2644
+ *
2645
+ * The host might be a custom-element wrapper like `<tn-button>` /
2646
+ * `<tn-icon-button>` whose host element isn't itself focusable — focus
2647
+ * lives on the inner `<button>` or `<a>`. We focus the host if it's
2648
+ * focusable; otherwise we drill into the first focusable descendant.
2649
+ *
2650
+ * Passes `focusVisible: true` (Chrome/Firefox) so the `:focus-visible`
2651
+ * outline reappears on the restored trigger — without it, browsers treat a
2652
+ * programmatic `.focus()` as non-keyboard and skip the focus ring, which
2653
+ * looks to users like focus has been lost. Safari ignores the option and
2654
+ * falls back to its heuristic; that's acceptable.
2655
+ */
2656
+ private restoreFocusToTrigger;
2543
2657
  private getPositions;
2544
2658
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnMenuTriggerDirective, never>;
2545
2659
  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,7 +3043,7 @@ interface TnSelectOptionGroup<T = unknown> {
2929
3043
  options: TnSelectOption<T>[];
2930
3044
  disabled?: boolean;
2931
3045
  }
2932
- declare class TnSelectComponent<T = unknown> implements ControlValueAccessor {
3046
+ declare class TnSelectComponent<T = unknown> implements ControlValueAccessor, OnDestroy {
2933
3047
  options: _angular_core.InputSignal<TnSelectOption<T>[]>;
2934
3048
  optionGroups: _angular_core.InputSignal<TnSelectOptionGroup<T>[]>;
2935
3049
  placeholder: _angular_core.InputSignal<string>;
@@ -2956,6 +3070,11 @@ declare class TnSelectComponent<T = unknown> implements ControlValueAccessor {
2956
3070
  * fallback uses `JSON.stringify`, which is key-order dependent and can
2957
3071
  * produce false negatives for structurally equal objects. For primitives the
2958
3072
  * default identity check is fine.
3073
+ *
3074
+ * @example
3075
+ * ```ts
3076
+ * compareWith = (a, b) => a?.id === b?.id;
3077
+ * ```
2959
3078
  */
2960
3079
  compareWith: _angular_core.InputSignal<((a: T | null, b: T | null) => boolean) | undefined>;
2961
3080
  selectionChange: _angular_core.OutputEmitterRef<T>;
@@ -2968,103 +3087,115 @@ declare class TnSelectComponent<T = unknown> implements ControlValueAccessor {
2968
3087
  /** Index into `flatOptions` of the keyboard-focused row (-1 when none). */
2969
3088
  protected focusedIndex: _angular_core.WritableSignal<number>;
2970
3089
  private formDisabled;
2971
- private static readonly DROPDOWN_MAX_HEIGHT_VAR;
2972
- private static readonly DROPDOWN_MAX_HEIGHT_FALLBACK;
2973
- private readonly fallbackId;
2974
- protected uniqueId: _angular_core.Signal<string>;
2975
- isDisabled: _angular_core.Signal<boolean>;
3090
+ private static instanceCounter;
3091
+ private instanceId;
2976
3092
  /**
2977
- * Flattened option list (ungrouped + grouped, in render order). The keyboard
2978
- * navigation walks this list entries from disabled groups are kept but
2979
- * marked disabled so the cursor skips over them correctly.
3093
+ * Id namespace used by all DOM ids the template emits (dropdown panel,
3094
+ * option rows, group labels). Prefers `testId` when set so tests can target
3095
+ * specific instances; otherwise falls back to a per-instance counter so two
3096
+ * `<tn-select>`s on the same page never collide on `aria-controls`/group ids.
2980
3097
  */
2981
- protected flatOptions: _angular_core.Signal<TnSelectOption<T>[]>;
3098
+ protected idNamespace: _angular_core.Signal<string>;
3099
+ isDisabled: _angular_core.Signal<boolean>;
2982
3100
  /**
2983
- * Starting flat-index of each option group, used by the template to
2984
- * translate a (group, option) pair into the matching `flatOptions` index.
3101
+ * Selectable, non-disabled options in display order (regular options first,
3102
+ * then groups). Used by keyboard navigation so we can skip disabled
3103
+ * entries and group headers without a separate filter pass.
2985
3104
  */
2986
- protected groupOffsets: _angular_core.Signal<number[]>;
2987
- /** `aria-activedescendant` id for the focused option (or null). */
2988
- protected activeOptionId: _angular_core.Signal<string | null>;
3105
+ navigableOptions: _angular_core.Signal<{
3106
+ option: TnSelectOption<T>;
3107
+ id: string;
3108
+ }[]>;
3109
+ /** Stable DOM id of the currently-highlighted option, for aria-activedescendant. */
3110
+ focusedOptionId: _angular_core.Signal<string | null>;
3111
+ /** Stable DOM id for an option; matches what navigableOptions() assigns. */
3112
+ optionId(option: TnSelectOption<T>): string | null;
3113
+ /** Whether `option` is the keyboard-highlighted item. */
3114
+ isOptionFocused(option: TnSelectOption<T>): boolean;
2989
3115
  private onChange;
2990
3116
  private onTouched;
2991
3117
  private elementRef;
2992
3118
  private cdr;
2993
- protected triggerEl: _angular_core.Signal<ElementRef<HTMLElement> | undefined>;
2994
- constructor();
3119
+ private overlay;
3120
+ private viewContainerRef;
3121
+ private triggerEl;
3122
+ private dropdownTemplate;
3123
+ private overlayRef?;
3124
+ private overlaySubs;
2995
3125
  writeValue(value: T | T[] | null): void;
2996
3126
  registerOnChange(fn: (value: T | T[] | null) => void): void;
2997
3127
  registerOnTouched(fn: () => void): void;
2998
3128
  setDisabledState(isDisabled: boolean): void;
2999
3129
  toggleDropdown(): void;
3130
+ openDropdown(): void;
3000
3131
  /**
3001
- * Open the dropdown, seed the keyboard cursor on the currently-selected
3002
- * option (or the first focusable one), and decide whether to flip up.
3003
- */
3004
- private openDropdown;
3005
- /**
3006
- * Close the dropdown.
3132
+ * Attach the dropdown panel as a CDK overlay anchored to the trigger.
3007
3133
  *
3008
- * @param restoreFocus When `true` (default), return focus to the trigger so
3009
- * keyboard users land somewhere sensible. Pass `false` for click-outside
3010
- * so we don't steal focus from the element the user just navigated to.
3011
- */
3012
- closeDropdown(options?: {
3013
- restoreFocus?: boolean;
3014
- }): void;
3015
- /** Picks the initial focused-row index when the dropdown opens. */
3016
- private initialFocusIndex;
3017
- /**
3018
- * Decide whether the dropdown should open above or below the trigger.
3019
- * Opens above when there isn't enough space below the trigger AND there is
3020
- * more space above — otherwise stays below. Falls back to `'below'` when no
3021
- * trigger element is found yet.
3134
+ * Why an overlay (vs. an inline absolutely-positioned panel):
3135
+ * - Escapes parent `overflow: hidden`/clipping in surrounding layouts.
3136
+ * - `outsidePointerEvents()` notifies on outside pointerdown WITHOUT
3137
+ * intercepting the click (no backdrop) — so the user's click reaches
3138
+ * the underlying target while the select closes silently.
3139
+ * - Position is recomputed on scroll so the panel stays attached.
3140
+ * - Width is matched to the trigger so the panel doesn't jump in size.
3022
3141
  */
3023
- private computeDropdownPosition;
3142
+ private attachOverlay;
3143
+ private detachOverlay;
3144
+ ngOnDestroy(): void;
3024
3145
  /**
3025
- * Reads the dropdown's max-height from the CSS custom property set in
3026
- * select.component.scss. Single source of truth for the flip-up threshold —
3027
- * if the stylesheet changes, the heuristic follows automatically.
3146
+ * Closes the dropdown.
3147
+ *
3148
+ * @param restoreFocus When `true` (the default), returns focus to the
3149
+ * trigger. Used for explicit closes — Escape, Enter/Space activation,
3150
+ * option click — where the user is still interacting with the select.
3151
+ * Pass `false` for click-outside / blur paths so we don't steal focus
3152
+ * from the element the user actually navigated to.
3028
3153
  */
3029
- private readDropdownMaxHeight;
3154
+ closeDropdown(restoreFocus?: boolean): void;
3030
3155
  onOptionClick(option: TnSelectOption<T>, groupDisabled?: boolean): void;
3031
3156
  selectOption(option: TnSelectOption<T>): void;
3032
3157
  private toggleOption;
3033
3158
  isOptionSelected(option: TnSelectOption<T>): boolean;
3034
- /** Build a stable DOM id for the option at `index` for aria-activedescendant. */
3035
- protected optionId(index: number): string;
3036
3159
  protected displayText: _angular_core.Signal<string>;
3037
3160
  private findOptionByValue;
3038
- protected anyOptionsPresent: _angular_core.Signal<boolean>;
3161
+ protected hasAnyOptions: _angular_core.Signal<boolean>;
3162
+ /** One-shot guard so the object-compare warning fires at most once per instance. */
3163
+ private warnedAboutObjectCompare;
3039
3164
  /**
3040
- * Compares two option values for equality. Uses `compareWith` if provided,
3041
- * otherwise identity (`===`). For object values it falls back to
3042
- * `JSON.stringify`, which is key-order dependent consumers with object
3043
- * values should provide `compareWith` to avoid subtle bugs.
3165
+ * Compares two option values for equality.
3166
+ *
3167
+ * - Uses `compareWith` when provided (the supported path for object values).
3168
+ * - Falls back to strict identity (`===`) — adequate for primitives.
3169
+ * - For object values WITHOUT `compareWith` we return `false` (no
3170
+ * structural compare) and emit a one-time warning. The previous
3171
+ * `JSON.stringify` fallback was key-order sensitive and produced silent
3172
+ * false-negatives that were hard to diagnose; returning `false` makes the
3173
+ * misuse loud (selection won't match) and the warning points to the fix.
3174
+ *
3175
+ * The warning is **unconditional** (not gated on `isDevMode()`) so prod
3176
+ * monitoring picks it up — consumers relying on the old stringify fallback
3177
+ * would otherwise see selections silently stop matching after upgrade with
3178
+ * no signal in production logs.
3044
3179
  */
3045
3180
  private compareValues;
3046
3181
  /**
3047
- * Keyboard handling on the trigger (focus stays on the trigger while the
3048
- * dropdown is open — options use mousedown-preventDefault to avoid stealing
3049
- * it). Implements the WAI-ARIA combobox pattern subset we need:
3182
+ * Keyboard navigation for the combobox trigger.
3050
3183
  *
3051
- * - **Enter / Space**: open closed dropdown, or select the focused row
3052
- * (toggle in multi-mode).
3053
- * - **ArrowDown / ArrowUp**: move the focused row; opens the dropdown first
3054
- * if it's closed.
3055
- * - **Home / End**: jump to first / last focusable row (when open).
3056
- * - **Escape**: close and restore focus to the trigger.
3057
- * - **Tab**: close without preventing default so focus moves to the next
3058
- * element naturally.
3184
+ * - **ArrowDown / ArrowUp** opens the dropdown if closed; otherwise moves
3185
+ * the keyboard-focus highlight (via aria-activedescendant) up/down,
3186
+ * skipping disabled options and group headers.
3187
+ * - **Home / End** jump to the first / last enabled option.
3188
+ * - **Enter / Space** opens the dropdown if closed; if open and an option
3189
+ * is highlighted, selects that option (in single mode) or toggles it
3190
+ * (in multiple mode).
3191
+ * - **Escape** closes the dropdown without changing the selection.
3192
+ *
3193
+ * All navigation keys call `event.preventDefault()` so the page does not
3194
+ * scroll while the user is moving through options.
3059
3195
  */
3060
3196
  onKeydown(event: KeyboardEvent): void;
3061
- /** Step the focused row by ±1 (or more), skipping disabled options. */
3062
3197
  private moveFocus;
3063
- /** Move focus to a specific index, scanning forward/backward to skip disabled. */
3064
- private moveFocusTo;
3065
- /** Select (or toggle, in multi-mode) the currently keyboard-focused row. */
3066
- private selectFocused;
3067
- /** Scrolls the keyboard-focused option into view if it's outside the dropdown's viewport. */
3198
+ private activateFocusedOption;
3068
3199
  private scrollFocusedIntoView;
3069
3200
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnSelectComponent<any>, never>;
3070
3201
  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>;
@@ -3869,8 +4000,49 @@ declare class TnTableComponent<T = unknown> implements OnInit {
3869
4000
  selectable: _angular_core.InputSignal<boolean>;
3870
4001
  expandable: _angular_core.InputSignal<boolean>;
3871
4002
  bordered: _angular_core.InputSignal<boolean>;
4003
+ /**
4004
+ * Marks a single row as "active" — adds the `tn-table__row--active` class
4005
+ * and a left-side indicator bar. Set to `null` (default) to clear.
4006
+ *
4007
+ * **Matched by object identity (`===`)** against the row references in
4008
+ * `dataSource`. Pass the exact reference you got from the data source (e.g.
4009
+ * via the `rowClick` event or a lookup into `dataSource()`), not a
4010
+ * structurally-equal copy — `{ id: 1 } !== { id: 1 }` and the row will not
4011
+ * highlight. This differs from `tn-select`, which supports a `compareWith`
4012
+ * input for object values; the table intentionally does not, because the
4013
+ * common use case (clicking a row to mark it active) already gives the
4014
+ * caller the original reference. If you need structural equality, look up
4015
+ * the row by id in your data source before assigning here.
4016
+ */
4017
+ activeRow: _angular_core.InputSignal<T | null>;
4018
+ /**
4019
+ * Overrides the active-row background color. Accepts any CSS color value
4020
+ * (`#hex`, `rgb()`, `var(--token)`). Defaults to `--tn-bg3` when null.
4021
+ */
4022
+ activeBg: _angular_core.InputSignal<string | null>;
4023
+ /**
4024
+ * Overrides the left-side active-row indicator color. Defaults to
4025
+ * `--tn-primary` when null.
4026
+ */
4027
+ activeIndicator: _angular_core.InputSignal<string | null>;
4028
+ /**
4029
+ * When true, shows a spinner overlay over the table. Existing rows remain
4030
+ * visible (dimmed) so reloads don't cause layout jumps; if there are no rows
4031
+ * yet, the spinner replaces the empty state.
4032
+ */
4033
+ loading: _angular_core.InputSignal<boolean>;
4034
+ /** Accessible label announced while loading. */
4035
+ loadingMessage: _angular_core.InputSignal<string>;
4036
+ /**
4037
+ * When true, rows become keyboard-focusable (tabindex=0) and clicking or
4038
+ * pressing Enter/Space emits `rowClick`. Use this for "click row to view
4039
+ * details" patterns. Independent of `selectable` (checkbox) and `expandable`.
4040
+ */
4041
+ clickable: _angular_core.InputSignal<boolean>;
3872
4042
  sortChange: _angular_core.OutputEmitterRef<TnSortEvent>;
3873
4043
  selectionChange: _angular_core.OutputEmitterRef<T[]>;
4044
+ /** Emits the row when a clickable row is activated (click or Enter/Space). */
4045
+ rowClick: _angular_core.OutputEmitterRef<T>;
3874
4046
  columnDefs: _angular_core.Signal<readonly TnTableColumnDirective[]>;
3875
4047
  detailRowDef: _angular_core.Signal<TnDetailRowDefDirective | undefined>;
3876
4048
  sortColumn: _angular_core.WritableSignal<string>;
@@ -3899,13 +4071,16 @@ declare class TnTableComponent<T = unknown> implements OnInit {
3899
4071
  isSorted(column: string): boolean;
3900
4072
  toggleRowExpansion(row: T): void;
3901
4073
  isRowExpanded(row: T): boolean;
4074
+ isRowActive(row: T): boolean;
4075
+ onRowClick(row: T): void;
4076
+ onRowKeydown(event: KeyboardEvent, row: T): void;
3902
4077
  toggleSelectAll(): void;
3903
4078
  toggleRowSelection(row: T): void;
3904
4079
  isRowSelected(row: T): boolean;
3905
4080
  getColumnDef(columnName: string): TnTableColumnDirective | undefined;
3906
4081
  getCellValue(row: T, column: string): unknown;
3907
4082
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTableComponent<any>, never>;
3908
- 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: {}; }]>;
4083
+ 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: {}; }]>;
3909
4084
  }
3910
4085
 
3911
4086
  /**
@@ -4023,6 +4198,44 @@ declare class TnTableHarness extends ComponentHarness {
4023
4198
  * @returns Promise resolving to true if the row has the expanded class.
4024
4199
  */
4025
4200
  isRowExpanded(rowIndex: number): Promise<boolean>;
4201
+ /**
4202
+ * Clicks a row (for tables with `clickable` enabled).
4203
+ *
4204
+ * @param rowIndex Zero-based index of the data row.
4205
+ */
4206
+ clickRow(rowIndex: number): Promise<void>;
4207
+ /**
4208
+ * Sends a keyboard event to a row (Enter/Space activate clickable rows).
4209
+ *
4210
+ * @param rowIndex Zero-based index of the data row.
4211
+ * @param key Which key to press — Enter or Space.
4212
+ */
4213
+ pressKeyOnRow(rowIndex: number, key: 'enter' | 'space'): Promise<void>;
4214
+ /**
4215
+ * Checks if a row is keyboard-focusable (tabindex=0).
4216
+ *
4217
+ * @param rowIndex Zero-based index of the data row.
4218
+ */
4219
+ isRowFocusable(rowIndex: number): Promise<boolean>;
4220
+ /**
4221
+ * Checks whether the table is currently in the loading state.
4222
+ *
4223
+ * @returns Promise resolving to true if the loading overlay is visible.
4224
+ */
4225
+ isLoading(): Promise<boolean>;
4226
+ /**
4227
+ * Checks if a data row is currently marked active.
4228
+ *
4229
+ * @param rowIndex Zero-based index of the data row.
4230
+ * @returns Promise resolving to true if the row has the active class.
4231
+ */
4232
+ isRowActive(rowIndex: number): Promise<boolean>;
4233
+ /**
4234
+ * Gets the index of the currently active row, or null if none is active.
4235
+ *
4236
+ * @returns Promise resolving to the active row index or null.
4237
+ */
4238
+ getActiveRowIndex(): Promise<number | null>;
4026
4239
  /**
4027
4240
  * Gets the text content of an expanded detail row.
4028
4241
  *
@@ -5272,6 +5485,22 @@ declare class TnButtonToggleGroupComponent implements ControlValueAccessor {
5272
5485
  name: _angular_core.InputSignal<string>;
5273
5486
  ariaLabel: _angular_core.InputSignal<string>;
5274
5487
  ariaLabelledby: _angular_core.InputSignal<string>;
5488
+ /**
5489
+ * Overrides the background color of checked toggles in this group. Accepts
5490
+ * any CSS color value (`#hex`, `rgb()`, `var(--token)`, etc.). When null
5491
+ * (default), falls back to `--tn-alt-bg2`.
5492
+ */
5493
+ checkedBg: _angular_core.InputSignal<string | null>;
5494
+ /**
5495
+ * Overrides the text color of checked toggles in this group. Defaults to
5496
+ * `--tn-fg1` when null.
5497
+ */
5498
+ checkedColor: _angular_core.InputSignal<string | null>;
5499
+ /**
5500
+ * Overrides the border color of checked toggles in this group. Defaults to
5501
+ * `--tn-lines` when null.
5502
+ */
5503
+ checkedBorder: _angular_core.InputSignal<string | null>;
5275
5504
  /**
5276
5505
  * Test-id applied to the group root. Rendered under whichever attribute name
5277
5506
  * is configured via `TN_TEST_ATTR` (default `data-testid`).
@@ -5298,7 +5527,7 @@ declare class TnButtonToggleGroupComponent implements ControlValueAccessor {
5298
5527
  private updateTogglesFromValue;
5299
5528
  private updateTogglesFromValues;
5300
5529
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnButtonToggleGroupComponent, never>;
5301
- 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>;
5530
+ 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>;
5302
5531
  }
5303
5532
 
5304
5533
  declare class TnButtonToggleComponent implements ControlValueAccessor {
@@ -5500,47 +5729,6 @@ interface ButtonToggleHarnessFilters extends BaseHarnessFilters {
5500
5729
  label?: string | RegExp;
5501
5730
  }
5502
5731
 
5503
- type TooltipPosition = 'above' | 'below' | 'left' | 'right' | 'before' | 'after';
5504
- declare class TnTooltipDirective implements OnInit, OnDestroy {
5505
- message: _angular_core.InputSignal<string>;
5506
- position: _angular_core.InputSignal<TooltipPosition>;
5507
- disabled: _angular_core.InputSignal<boolean>;
5508
- showDelay: _angular_core.InputSignal<number>;
5509
- hideDelay: _angular_core.InputSignal<number>;
5510
- tooltipClass: _angular_core.InputSignal<string>;
5511
- private _overlayRef;
5512
- private _tooltipInstance;
5513
- private _showTimeout;
5514
- private _hideTimeout;
5515
- private _isTooltipVisible;
5516
- private _positionSub;
5517
- protected _ariaDescribedBy: string | null;
5518
- private _overlay;
5519
- private _elementRef;
5520
- private _viewContainerRef;
5521
- private _overlayPositionBuilder;
5522
- ngOnInit(): void;
5523
- ngOnDestroy(): void;
5524
- _onMouseEnter(): void;
5525
- _onMouseLeave(): void;
5526
- _onFocus(): void;
5527
- _onBlur(): void;
5528
- _onKeydown(event: KeyboardEvent): void;
5529
- /** Shows the tooltip */
5530
- show(delay?: number): void;
5531
- /** Hides the tooltip */
5532
- hide(delay?: number): void;
5533
- /** Toggle the tooltip visibility */
5534
- toggle(): void;
5535
- private _createOverlay;
5536
- private _resolvePosition;
5537
- private _attachTooltip;
5538
- private _getPositions;
5539
- private _clearTimeouts;
5540
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTooltipDirective, never>;
5541
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TnTooltipDirective, "[tnTooltip]", never, { "message": { "alias": "tnTooltip"; "required": false; "isSignal": true; }; "position": { "alias": "tnTooltipPosition"; "required": false; "isSignal": true; }; "disabled": { "alias": "tnTooltipDisabled"; "required": false; "isSignal": true; }; "showDelay": { "alias": "tnTooltipShowDelay"; "required": false; "isSignal": true; }; "hideDelay": { "alias": "tnTooltipHideDelay"; "required": false; "isSignal": true; }; "tooltipClass": { "alias": "tnTooltipClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
5542
- }
5543
-
5544
5732
  declare class TnTooltipComponent {
5545
5733
  message: _angular_core.InputSignal<string>;
5546
5734
  id: _angular_core.InputSignal<string>;
@@ -5588,11 +5776,15 @@ declare class TnDialog {
5588
5776
  declare class TnDialogShellComponent implements OnInit {
5589
5777
  title: _angular_core.InputSignal<string>;
5590
5778
  showFullscreenButton: _angular_core.InputSignal<boolean>;
5779
+ /** Stable id for the title heading, referenced by the dialog's aria-labelledby. */
5780
+ readonly titleId: string;
5591
5781
  isFullscreen: _angular_core.WritableSignal<boolean>;
5592
5782
  private originalStyles;
5593
5783
  private ref;
5594
5784
  private document;
5785
+ private host;
5595
5786
  private data;
5787
+ constructor();
5596
5788
  ngOnInit(): void;
5597
5789
  close(result?: unknown): void;
5598
5790
  toggleFullscreen(): void;
@@ -5700,6 +5892,20 @@ declare class TnDialogHarness extends ComponentHarness {
5700
5892
  * ```
5701
5893
  */
5702
5894
  close(): Promise<void>;
5895
+ /**
5896
+ * Gets the `tabindex` attribute of the close button, or null if unset.
5897
+ * A null/non-negative value means the button is reachable via the Tab key.
5898
+ *
5899
+ * @returns Promise resolving to the tabindex attribute value.
5900
+ */
5901
+ getCloseButtonTabIndex(): Promise<string | null>;
5902
+ /**
5903
+ * Gets the `tabindex` attribute of the fullscreen button, or null if unset
5904
+ * or if the dialog has no fullscreen button.
5905
+ *
5906
+ * @returns Promise resolving to the tabindex attribute value.
5907
+ */
5908
+ getFullscreenButtonTabIndex(): Promise<string | null>;
5703
5909
  /**
5704
5910
  * Clicks an action button in the dialog footer by its label.
5705
5911
  * Only matches buttons inside the `tnDialogAction` footer area, not buttons in content.
@@ -6231,11 +6437,18 @@ interface TnToastConfig {
6231
6437
  type?: TnToastType;
6232
6438
  /** Vertical position. Default: TnToastPosition.Top. */
6233
6439
  position?: TnToastPosition;
6440
+ /**
6441
+ * Test id applied to the action button. Rendered under whichever attribute name is
6442
+ * configured via `TN_TEST_ATTR` (default `data-testid`). Only relevant when an action
6443
+ * is provided.
6444
+ */
6445
+ actionTestId?: string;
6234
6446
  }
6235
6447
 
6236
6448
  declare class TnToastComponent {
6237
6449
  message: _angular_core.WritableSignal<string>;
6238
6450
  action: _angular_core.WritableSignal<string | null>;
6451
+ actionTestId: _angular_core.WritableSignal<string | undefined>;
6239
6452
  type: _angular_core.WritableSignal<TnToastType>;
6240
6453
  position: _angular_core.WritableSignal<TnToastPosition>;
6241
6454
  visible: _angular_core.WritableSignal<boolean>;