@truenas/ui-components 0.7.3 → 0.7.5

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.
@@ -2,12 +2,12 @@ import * as _truenas_ui_components from '@truenas/ui-components';
2
2
  import * as _angular_core from '@angular/core';
3
3
  import { Signal, InjectionToken, Renderer2, OnDestroy, ElementRef, AfterViewInit, TemplateRef, AfterContentInit, Provider, DoCheck, ChangeDetectorRef, OnInit, TrackByFunction, OnChanges, SimpleChanges, PipeTransform, ViewContainerRef, AfterViewChecked, ComponentRef } from '@angular/core';
4
4
  import { ControlValueAccessor, NgControl, AbstractControl, ValidationErrors } from '@angular/forms';
5
+ import * as rxjs from 'rxjs';
6
+ import { Observable, BehaviorSubject } from 'rxjs';
5
7
  import { ComponentHarness, BaseHarnessFilters, HarnessPredicate, TestKey, HarnessLoader, ModifierKeys as ModifierKeys$1 } from '@angular/cdk/testing';
6
8
  import { SafeHtml, SafeResourceUrl, DomSanitizer } from '@angular/platform-browser';
7
9
  import { ComponentFixture } from '@angular/core/testing';
8
10
  import { SelectionModel, DataSource, CollectionViewer } from '@angular/cdk/collections';
9
- import * as rxjs from 'rxjs';
10
- import { Observable, BehaviorSubject } from 'rxjs';
11
11
  import * as i1 from '@angular/cdk/tree';
12
12
  import { CdkTree, CdkTreeNode, CdkNestedTreeNode, CdkTreeNodeOutletContext, CdkTreeNodeDef } from '@angular/cdk/tree';
13
13
  export { FlatTreeControl } from '@angular/cdk/tree';
@@ -575,6 +575,129 @@ declare class TnSelectComponent<T = unknown> implements ControlValueAccessor, On
575
575
  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; }; "allowEmpty": { "alias": "allowEmpty"; "required": false; "isSignal": true; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "showSelectAll": { "alias": "showSelectAll"; "required": false; "isSignal": true; }; "selectAllLabel": { "alias": "selectAllLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "optionTestIdKey": { "alias": "optionTestIdKey"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; "multiSelectionChange": "multiSelectionChange"; }, never, never, true, never>;
576
576
  }
577
577
 
578
+ /**
579
+ * Fetches one page of options for a server-driven dropdown.
580
+ *
581
+ * Stateless on purpose: the page number is passed in rather than held by the
582
+ * caller, so a source is a plain function of `(query, page)` and never has to
583
+ * track — or roll back — a cursor of its own. {@link createTnOptionsDataSource}
584
+ * owns the cursor, and rolls it back for you when a page fails.
585
+ *
586
+ * `page` is zero-based; page 0 is the first page of a fresh search.
587
+ *
588
+ * Only the FIRST emission is read, and an observable that completes without
589
+ * emitting is treated as an empty page — both normalized by
590
+ * {@link createTnOptionsDataSource} rather than required of the caller, since
591
+ * the signature promises neither. `(q) => q ? this.api.search(q) : EMPTY` is a
592
+ * shape consumers reach for, and left unnormalized it wedged the `loading`
593
+ * latch for the life of the field: both hosts prime with `''`, so the very
594
+ * first open would hang a spinner over an empty panel and kill paging with it.
595
+ */
596
+ type TnOptionsFetchFn<O> = (query: string, page: number) => Observable<O[]>;
597
+ /** Inputs {@link createTnOptionsDataSource} reads. All are signals so they stay live. */
598
+ interface TnOptionsDataSourceConfig<O> {
599
+ /** The fetch function, or `undefined` when the host has no async source bound. */
600
+ source: Signal<TnOptionsFetchFn<O> | undefined>;
601
+ /** Debounce applied to typing before a request goes out, in ms. */
602
+ debounceMs: Signal<number>;
603
+ /**
604
+ * Rows per page. Only used to detect exhaustion: a page shorter than this is
605
+ * the last one, after which further {@link TnOptionsDataSource.loadMore}
606
+ * calls are dropped instead of issuing queries that can only return `[]`.
607
+ */
608
+ pageSize: Signal<number>;
609
+ /** Identity of an option, used to drop duplicates when appending a page. */
610
+ identity: (option: O) => unknown;
611
+ /** Called once per failed request, so the host can surface it its own way. */
612
+ onError: (error: unknown) => void;
613
+ /**
614
+ * Called after every request settles, successfully or not. Hosts use this to
615
+ * release their own "a page is in flight" latches: a synchronous source
616
+ * flips {@link TnOptionsDataSource.loading} true and back within one block,
617
+ * so watching that signal misses the round trip entirely.
618
+ */
619
+ onSettled?: () => void;
620
+ }
621
+ /** The live state of a {@link createTnOptionsDataSource}. */
622
+ interface TnOptionsDataSource<O> {
623
+ /** Options fetched so far for the current query, in server order. */
624
+ readonly options: Signal<O[]>;
625
+ /** Whether a request is in flight. */
626
+ readonly loading: Signal<boolean>;
627
+ /** Whether the last page came back short, i.e. there is nothing more to page in. */
628
+ readonly exhausted: Signal<boolean>;
629
+ /** Run a fresh (debounced) search, resetting paging to page 0. */
630
+ search(query: string): void;
631
+ /**
632
+ * Fetch page 0 for the current term immediately if nothing has been fetched
633
+ * yet. For click-to-open pickers, where `search` never fires until the user
634
+ * types. A no-op once a query has run *successfully*, so reopening the panel
635
+ * does not refetch — but a request that failed is retried, as it is on the
636
+ * `search` path.
637
+ */
638
+ prime(): void;
639
+ /** Append the next page. No-op while loading, when exhausted, or with no source. */
640
+ loadMore(): void;
641
+ /**
642
+ * Declare everything fetched so far out of date, without fetching anything.
643
+ *
644
+ * The other half of the stable-source contract. A host whose `[dataSource]`
645
+ * is a fixed function reading live configuration — the shape that keeps a
646
+ * source from being swapped out from under a search in flight — has nothing
647
+ * here observing that configuration, so a change to it would otherwise take
648
+ * effect only on the next keystroke. This is how the host says it changed.
649
+ *
650
+ * Clears {@link TnOptionsDataSource.prime}'s latch and rolls paging back to
651
+ * page 0, so the next `prime` refetches instead of being answered from the
652
+ * previous configuration's rows — and is not suppressed for repeating the
653
+ * term already on screen. Deliberately does NOT issue a request itself: only
654
+ * the host knows whether its panel is open, and a field nobody has touched
655
+ * should not query the server because a sibling input moved. A host with rows
656
+ * on screen calls `prime` straight after this.
657
+ */
658
+ refresh(): void;
659
+ }
660
+ /**
661
+ * The part of a host component (`tn-autocomplete`, `tn-chip-input`) that an
662
+ * outer component wrapping it drives directly.
663
+ *
664
+ * The engine is private to the host, so a composite field — one that builds the
665
+ * `[dataSource]` itself and binds it once — needs this to invalidate what the
666
+ * host has fetched when its own inputs move.
667
+ */
668
+ interface TnAsyncOptionsHost {
669
+ /** See {@link TnOptionsDataSource.refresh}. */
670
+ refreshOptions(): void;
671
+ }
672
+ /**
673
+ * The stateful half of a server-driven dropdown: debounce, cancellation,
674
+ * paging, exhaustion, and error recovery, so a host component only has to
675
+ * render {@link TnOptionsDataSource.options} and forward its own events.
676
+ *
677
+ * Must be called in an injection context — it ties its subscription to the
678
+ * caller's {@link DestroyRef}.
679
+ *
680
+ * Notable behaviours, each of which was a bug in a hand-rolled copy of this:
681
+ *
682
+ * - **Errors never kill the stream.** `catchError` sits inside the `switchMap`,
683
+ * so a failed request reports and yields an empty page while the outer
684
+ * subscription survives; without that, one transient failure freezes the
685
+ * field for the life of the panel.
686
+ * - **A failed term is retryable.** `distinctUntilChanged` treats a repeat of a
687
+ * failed query as a change, so reopening a picker after an error refetches
688
+ * instead of being suppressed as a duplicate.
689
+ * - **A failed page does not advance the cursor**, so the next scroll re-requests
690
+ * the page that errored rather than stepping over those rows. The cursor
691
+ * tracks the *next* page to ask for rather than the last one loaded, so page 0
692
+ * rolls back like every other page instead of being silently skipped.
693
+ * - **A late page cannot contaminate a newer search.** Every response carries
694
+ * the generation it was requested in and is dropped if a search — or a
695
+ * {@link TnOptionsDataSource.refresh} — has since started. Cancellation is
696
+ * not enough on its own: `refresh` invalidates without issuing a request, so
697
+ * there is nothing for `switchMap` to unsubscribe the old one in favour of.
698
+ */
699
+ declare function createTnOptionsDataSource<O>(config: TnOptionsDataSourceConfig<O>): TnOptionsDataSource<O>;
700
+
578
701
  /**
579
702
  * Option shape for `tn-autocomplete` — the `label` is displayed, the `value`
580
703
  * is committed to the form control, and a truthy `disabled` keeps the row
@@ -607,7 +730,7 @@ declare const TN_AUTOCOMPLETE_DEFAULT_LABELS: TnAutocompleteLabels;
607
730
  * Explicit input bindings on `<tn-autocomplete>` still win over these defaults.
608
731
  */
609
732
  declare const TN_AUTOCOMPLETE_LABELS: InjectionToken<TnAutocompleteLabels | Signal<TnAutocompleteLabels>>;
610
- declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccessor, OnDestroy {
733
+ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccessor, TnAsyncOptionsHost, OnDestroy {
611
734
  private readonly elementRef;
612
735
  private readonly overlay;
613
736
  private readonly viewContainerRef;
@@ -618,6 +741,12 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
618
741
  * to the form control. A written value is resolved back to its option's
619
742
  * label for display — falling back to `String(value)` until the matching
620
743
  * option is available, and upgraded once an async option load lands.
744
+ *
745
+ * With a `[dataSource]` bound these are not listed in the dropdown, which is
746
+ * the source's, but they still NAME a value: pass the options a host already
747
+ * holds labels for — the ids on the record being edited — and the field
748
+ * reads them as names from the moment it renders, rather than as raw numbers
749
+ * until someone opens it.
621
750
  */
622
751
  options: _angular_core.InputSignal<TnAutocompleteOption<T>[]>;
623
752
  /**
@@ -646,9 +775,71 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
646
775
  * exclusive with `requireSelection`, which reverts unmatched text.
647
776
  */
648
777
  allowCustomValue: _angular_core.InputSignal<boolean>;
778
+ /**
779
+ * Server-driven options: a function of `(query, page)` returning one page of
780
+ * matches. Binding it hands the component the whole async lifecycle —
781
+ * debounced search, request cancellation, the paging cursor, exhaustion,
782
+ * error recovery and the loading state — so a consumer no longer writes a
783
+ * `searchChange` → subject → `switchMap` → `options` pipeline of its own.
784
+ *
785
+ * While set, the source is the only origin of DROPDOWN rows and the
786
+ * client-side filter is skipped, since the server already filtered — pass an
787
+ * explicit `[filterFn]` to filter the fetched page on top. `[options]` stays
788
+ * a source of LABELS, for values the host can already name before the first
789
+ * page is fetched; see {@link options}.
790
+ *
791
+ * The first page is fetched when the panel first opens, not on init, so a
792
+ * form full of pickers issues no queries until one is actually used. Binding
793
+ * a source that is `undefined` at that moment is fine — the field fetches as
794
+ * soon as one arrives, open panel included.
795
+ *
796
+ * Swapping one source function for ANOTHER is not a signal to refetch, since
797
+ * a source is expected to be a stable function reading live configuration.
798
+ * Call {@link refreshOptions} when that configuration moves.
799
+ *
800
+ * @example
801
+ * ```html
802
+ * <tn-autocomplete [dataSource]="userOptions" [pageSize]="50" />
803
+ * ```
804
+ * ```ts
805
+ * userOptions: TnOptionsFetchFn<string> =
806
+ * (query, page) => this.api.searchUsers(query, page * 50);
807
+ * ```
808
+ */
809
+ dataSource: _angular_core.InputSignal<TnOptionsFetchFn<TnAutocompleteOption<T>> | undefined>;
810
+ /** Debounce applied to typing before `dataSource` is queried, in ms. */
811
+ dataSourceDebounce: _angular_core.InputSignal<number>;
812
+ /**
813
+ * Rows `dataSource` returns per page. Used to detect exhaustion — a short
814
+ * page is the last one — so scrolling past the end stops querying rather
815
+ * than issuing requests at a growing offset that can only return nothing.
816
+ */
817
+ pageSize: _angular_core.InputSignal<number>;
818
+ /**
819
+ * A row pinned above the results that performs an action instead of
820
+ * selecting a value — "Add New…" and friends. It survives filtering and the
821
+ * `maxResults` cap, and choosing it emits {@link actionSelected} and closes
822
+ * the panel **without** touching the form control, so no placeholder
823
+ * sentinel is ever committed to the value.
824
+ *
825
+ * **Must be a stable reference.** The row is recognized by identity, so the
826
+ * obvious call site — `[actionOption]="{ label: 'Add ' + term, value: null }"`
827
+ * — builds a new object on every change-detection pass, re-firing the input,
828
+ * invalidating the filtered rows and marking the component dirty again. Hold
829
+ * it in a field, or derive it with `computed()`.
830
+ */
831
+ actionOption: _angular_core.InputSignal<TnAutocompleteOption<T> | undefined>;
832
+ /**
833
+ * Keep the committed value listed even when the page on screen does not
834
+ * contain it — a value sorted past the first page, or one just created that
835
+ * the query does not match yet. Without it such a field renders blank for a
836
+ * perfectly valid value. Only applies with `dataSource` bound.
837
+ */
838
+ keepSelectedOption: _angular_core.InputSignal<boolean>;
649
839
  /**
650
840
  * Show a loading row in the dropdown panel while options are being fetched.
651
- * Pair with `searchChange`/`loadMore` for server-driven options.
841
+ * Pair with `searchChange`/`loadMore` for server-driven options. Redundant
842
+ * with `dataSource`, which drives this state itself; the two are OR-ed.
652
843
  */
653
844
  loading: _angular_core.InputSignal<boolean>;
654
845
  /** Text shown next to the spinner while `loading` is set. */
@@ -717,12 +908,30 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
717
908
  * lookup is expensive.
718
909
  */
719
910
  searchChange: _angular_core.OutputEmitterRef<string>;
911
+ /**
912
+ * Emits when {@link actionOption} is chosen. No value is committed and the
913
+ * panel closes; the consumer runs whatever the row promises (typically
914
+ * opening a create form) and can then write the new value to the control.
915
+ */
916
+ actionSelected: _angular_core.OutputEmitterRef<void>;
917
+ /**
918
+ * Emits once per failed `dataSource` request. The component recovers on its
919
+ * own — the stream stays alive and the failed term stays retryable — so this
920
+ * is purely for the app to report the failure the way it reports others.
921
+ */
922
+ dataSourceError: _angular_core.OutputEmitterRef<unknown>;
720
923
  /**
721
924
  * Emits when the open dropdown is scrolled near its bottom — append the
722
925
  * next page to `options` (and use `loading` while it fetches). Suppressed
723
- * until the `options` COUNT changes so a slow consumer is not spammed —
724
- * which also means replacing the array with a same-length page (e.g. a
725
- * fixed-size window) does not re-arm the emitter; pagination must append.
926
+ * until the row COUNT changes so a slow consumer is not spammed — which also
927
+ * means replacing the array with a same-length page (e.g. a fixed-size
928
+ * window) does not re-arm the emitter; pagination must append.
929
+ *
930
+ * With a `[dataSource]` bound the count read is the resolved one (the
931
+ * fetched pages), and the latch is also released when a request settles, so
932
+ * a page that FAILED does not leave scrolling dead. The output still fires
933
+ * either way: it is for a host paging `options` itself, so do not wire both
934
+ * — the source already pages, and the two together fetch each page twice.
726
935
  */
727
936
  loadMore: _angular_core.OutputEmitterRef<void>;
728
937
  /**
@@ -745,14 +954,105 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
745
954
  protected highlightedIndex: _angular_core.WritableSignal<number>;
746
955
  /** The currently committed value (an option's `value`, or a custom-typed one) */
747
956
  private selectedValue;
957
+ /**
958
+ * Label of the option the value was chosen from, remembered so
959
+ * `keepSelectedOption` can re-list it with its real text after a search
960
+ * replaced the page it came from. Null for a programmatic write, whose
961
+ * label is not known until an option carrying it arrives.
962
+ */
963
+ private selectedLabel;
748
964
  /** CVA disabled state from the form */
749
965
  private formDisabled;
750
966
  /** Combined disabled state */
751
967
  isDisabled: Signal<boolean>;
752
- /** Filtered and capped options */
968
+ /** Async engine backing `dataSource`; idle while no source is bound. */
969
+ private readonly asyncOptions;
970
+ /**
971
+ * The synthetic row that keeps a committed value listed while the pages
972
+ * fetched so far do not contain it, or `undefined`.
973
+ *
974
+ * Held separately from {@link resolvedOptions} so {@link filteredOptions} can
975
+ * recognize it and pin it past the `maxResults` cap, the way the action row
976
+ * is pinned. Appended and then sliced with everything else, a full page
977
+ * filled the cap and cut off the one row this exists to guarantee.
978
+ */
979
+ private readonly keptSelectedOption;
980
+ /**
981
+ * Rows the host named itself, honoured even while a `dataSource` is bound.
982
+ *
983
+ * With a source the dropdown is the server's to fill — but its first page is
984
+ * not fetched until the field is focused, so a value the host already holds
985
+ * the label for renders as its raw `String(value)` until someone opens the
986
+ * panel. An edit form bound to an id shows the number. `options` is how that
987
+ * label is supplied, so it stays part of the LOOKUP even where it is not
988
+ * part of the dropdown. `tn-chip-input` treats its own `options` the same
989
+ * way, and the asymmetry was a bug rather than a design.
990
+ *
991
+ * Reached through {@link resolvableOptions} for display and
992
+ * {@link matchableOptions} for matching, so `commitCustomValue` and the
993
+ * `requireSelection` branch of `onBlur` recognise a pinned label too.
994
+ * Painting from a list wider than the one it reads back is the bug this
995
+ * shape exists to avoid.
996
+ */
997
+ private readonly pinnedOptions;
998
+ /**
999
+ * The rows to work from: the async engine's when `dataSource` is bound,
1000
+ * the `options` input otherwise. Everything that resolves a value to a
1001
+ * label, matches typed text, or counts rows reads this rather than
1002
+ * `options` — the action row is deliberately absent, since it carries no
1003
+ * selectable value.
1004
+ */
1005
+ private readonly resolvedOptions;
1006
+ /**
1007
+ * The rows a value may be resolved to a label FROM — the fetched pages plus
1008
+ * the host-pinned {@link pinnedOptions}, and never the synthetic kept row.
1009
+ * {@link matchableOptions} is the other direction.
1010
+ *
1011
+ * That row exists to keep a committed value listed, and its label is the
1012
+ * display text itself: `String(value)` for a value nothing has named yet.
1013
+ * Resolving against it therefore always "succeeds", which would report the
1014
+ * raw fallback as a resolved label and leave it on screen for good.
1015
+ */
1016
+ private readonly resolvableOptions;
1017
+ /**
1018
+ * The rows typed text may be matched BACK to a value by: every label this
1019
+ * field puts in front of the user, so that anything it PAINTS it also READS.
1020
+ *
1021
+ * {@link resolvableOptions} for the labels a value can be resolved from,
1022
+ * plus the synthetic kept row — which the two lists on either side each drop
1023
+ * for a reason that does not apply here:
1024
+ *
1025
+ * - {@link resolvedOptions} omits the host-pinned rows, since the dropdown
1026
+ * under a source is the server's to fill. But a pinned row is exactly how a
1027
+ * host names a value the pages do not carry, so matching without it painted
1028
+ * `g-7` as `admins` and then committed the raw string `'admins'` over the
1029
+ * id it names. That is the asymmetry `tn-chip-input` closed by pointing
1030
+ * `commitText` at its `labelOptions`.
1031
+ * - {@link resolvableOptions} omits the kept row, because resolving a VALUE
1032
+ * against it always "succeeds" and would report its `String(value)`
1033
+ * fallback as a real label. Matching a LABEL against it is the opposite
1034
+ * case: the row is listed, the user can click it, and its label may be one
1035
+ * remembered from the option the value was picked from. Dropping it would
1036
+ * turn "type the text you can see, then blur" into a free-text commit of
1037
+ * that text over the value it stands for.
1038
+ *
1039
+ * Order is the usual precedence — the server's rows, then the host's, then
1040
+ * the synthetic one — and every reader takes the first match.
1041
+ */
1042
+ private readonly matchableOptions;
1043
+ /** Loading state: the `loading` input OR-ed with the async engine's. */
1044
+ protected readonly resolvedLoading: Signal<boolean>;
1045
+ /** Filtered and capped options, with {@link actionOption} pinned on top. */
753
1046
  protected filteredOptions: Signal<TnAutocompleteOption<T>[]>;
754
- /** Whether there are any results to show */
1047
+ /** Whether there are any rows to render (the action row counts). */
755
1048
  protected hasResults: Signal<boolean>;
1049
+ /**
1050
+ * Whether the query matched anything selectable. Distinct from
1051
+ * {@link hasResults} so a search that found nothing still says so, rather
1052
+ * than being masked by a pinned action row — or by the kept-selected row,
1053
+ * which is held on screen for the committed value regardless of the query.
1054
+ */
1055
+ protected hasMatches: Signal<boolean>;
756
1056
  private onChange;
757
1057
  private onTouched;
758
1058
  /** Live overlay holding the dropdown panel, or undefined when closed. */
@@ -767,18 +1067,84 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
767
1067
  private lastOptionsCount;
768
1068
  /** Scroll distance (px) from the panel bottom that triggers `loadMore`. */
769
1069
  private static readonly loadMoreThresholdPx;
1070
+ /**
1071
+ * Cap on how many pages the underfill check may request without the user
1072
+ * scrolling, per search. The check re-arms whenever the option count
1073
+ * changes, which a `dataSource` answering synchronously does within the same
1074
+ * change-detection pass — so an inexhaustible source would spin the tab
1075
+ * rather than yield between rounds. Well beyond any real panel's capacity
1076
+ * (20 × a 50-row page), so it only ever trips on a misbehaving source.
1077
+ */
1078
+ private static readonly maxAutoFillPages;
1079
+ /** Underfill-driven pages requested since the last search or panel open. */
1080
+ private autoFillCount;
770
1081
  /** Guards the object-without-compareWith warning so it fires only once. */
771
1082
  private warnedAboutObjectCompare;
1083
+ /**
1084
+ * Whether the text on screen is the raw `String(value)` fallback rather than
1085
+ * an option's label — i.e. the value has never resolved and the user has not
1086
+ * typed over it.
1087
+ *
1088
+ * The upgrade effect otherwise leaves an open panel alone, on the grounds
1089
+ * that the text belongs to the user's search. With a `dataSource` that never
1090
+ * holds: rows only ever arrive while the panel is open, so a written value
1091
+ * would keep the fallback as its display text for good — and
1092
+ * `allowCustomValue` would then commit that text back over the real value on
1093
+ * blur. This says the text is nobody's draft, so upgrading it is safe.
1094
+ */
1095
+ private displayIsFallback;
772
1096
  constructor();
773
1097
  ngOnDestroy(): void;
774
1098
  writeValue(value: T | null): void;
775
1099
  registerOnChange(fn: (value: T | null) => void): void;
776
1100
  registerOnTouched(fn: () => void): void;
777
1101
  setDisabledState(isDisabled: boolean): void;
1102
+ /**
1103
+ * Discard the pages fetched from `dataSource` and re-query the current term.
1104
+ *
1105
+ * For a caller whose `[dataSource]` is a fixed function reading live
1106
+ * configuration — the shape that keeps the source from being swapped out
1107
+ * from under a search in flight — this is how a change to that configuration
1108
+ * takes effect, rather than waiting for the next keystroke to notice.
1109
+ */
1110
+ refreshOptions(): void;
1111
+ /**
1112
+ * Commit an option programmatically, its label included.
1113
+ *
1114
+ * `writeValue` deliberately forgets the label — the forms layer hands over a
1115
+ * value and nothing else — so a value the host picked itself would render as
1116
+ * `String(value)` until a search happened to return the row carrying it.
1117
+ * This is the path for a caller that already holds the option: the
1118
+ * `actionOption` handoff, where `(actionSelected)` creates an entity and the
1119
+ * host hands the new row straight back.
1120
+ *
1121
+ * It COMMITS, form control included. Updating only the display left the
1122
+ * field showing the new label, marking that row selected, and the bound
1123
+ * control still holding the previous value — which is what a submit then
1124
+ * sent. (With `requireSelection` and `keepSelectedOption` together, blur
1125
+ * happened to find the label on the synthetic kept row and reconcile it
1126
+ * through `selectOption`; on the default path nothing ever did.)
1127
+ *
1128
+ * `optionSelected` deliberately does NOT fire: that output means the user
1129
+ * picked a row, and this is the host telling the component what the host
1130
+ * already decided — echoing it back to the caller that made the call would
1131
+ * be noise. A host keeping a side model in sync updates it where it calls
1132
+ * this, not from the output.
1133
+ */
1134
+ setSelectedOption(option: TnAutocompleteOption<T>): void;
778
1135
  onInput(event: Event): void;
779
1136
  onFocus(): void;
780
1137
  onBlur(): void;
781
1138
  onOptionClick(option: TnAutocompleteOption<T>): void;
1139
+ /** Whether `option` is the pinned {@link actionOption} row rather than a value. */
1140
+ private isActionOption;
1141
+ /**
1142
+ * The action row was chosen: close the panel and hand off, leaving the
1143
+ * committed value and the text exactly as they were. Restoring the text
1144
+ * matters because the user has usually typed a partial term to find the
1145
+ * row — that draft must not survive as a value.
1146
+ */
1147
+ private runAction;
782
1148
  onKeydown(event: KeyboardEvent): void;
783
1149
  /**
784
1150
  * `loadMore` normally fires from a scroll event, which never happens when
@@ -787,6 +1153,12 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
787
1153
  * opens), emit once more if the panel has no scrollbar.
788
1154
  */
789
1155
  private checkUnderfill;
1156
+ /**
1157
+ * Ask for another page: the internal engine when `dataSource` is bound, the
1158
+ * consumer via `loadMore` otherwise. Both are notified — an app can page a
1159
+ * `dataSource` and still observe the event.
1160
+ */
1161
+ private requestMore;
790
1162
  onPanelScroll(event: Event): void;
791
1163
  /**
792
1164
  * Whether `option` carries the committed value — drives `aria-selected` so
@@ -827,13 +1199,50 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
827
1199
  * display match (case-insensitive, same as the requireSelection path)
828
1200
  * commits the matching option instead, so picking an existing entry by
829
1201
  * typing its full text behaves like clicking it.
1202
+ *
1203
+ * Matched against {@link matchableOptions} so the host-pinned rows count:
1204
+ * with `[dataSource]` + `[options]` the field painted `g-7` as `admins` and
1205
+ * then, on a page that did not carry that row, committed the raw string
1206
+ * `'admins'` over the id it names — a label it would not read back. Same
1207
+ * asymmetry `tn-chip-input` closed by pointing `commitText` at its
1208
+ * `labelOptions`.
830
1209
  */
831
1210
  private commitCustomValue;
832
1211
  /**
833
- * Resolves a committed value back to its option's display label, falling
834
- * back to the raw value's string until the matching option is available.
1212
+ * Show `value`'s label and record whether that text is a real label or the
1213
+ * raw `String(value)` fallback, so {@link displayIsFallback} — and with it
1214
+ * the upgrade effect — can tell a resolved display from an unresolved one.
1215
+ * Every path that re-derives the text from the committed value goes through
1216
+ * here, so the two can never disagree.
1217
+ *
1218
+ * A label remembered from the option the value was picked from counts as
1219
+ * resolved: it is the same text the option would give, and the row carrying
1220
+ * it may well have been paged away since.
835
1221
  */
836
- private displayValue;
1222
+ private setDisplayFromValue;
1223
+ /**
1224
+ * Abandon the draft term: repaint `value`'s label AND tell the async engine
1225
+ * the term is gone.
1226
+ *
1227
+ * `asyncOptions.search()` is otherwise only reached from `onInput`, so every
1228
+ * path that rewrites the text WITHOUT the user typing — the `requireSelection`
1229
+ * revert on blur, the `allowCustomValue` Escape branch, and `runAction` —
1230
+ * moved the display while the engine kept the query, the rows and the
1231
+ * `primed` latch of the term walked away from. Reopening then showed that
1232
+ * term's results (or "No results found") against a field displaying something
1233
+ * else, and issued nothing to correct it: `open()` only calls `prime()`,
1234
+ * which is a documented no-op once primed.
1235
+ *
1236
+ * `tn-chip-input.clearInput()` closes the same gap the same way. The request
1237
+ * goes through the usual debounce, and the duplicate-term guard drops it
1238
+ * entirely when the reverted text is the term already loaded — which is the
1239
+ * common case of an Escape with no draft typed.
1240
+ *
1241
+ * `writeValue` deliberately does NOT come through here: a programmatic write
1242
+ * is not a user abandoning a search, and querying there would break the rule
1243
+ * that a field nobody has opened issues no request.
1244
+ */
1245
+ private revertDisplayToValue;
837
1246
  /**
838
1247
  * Compares option values, honoring `compareWith` when provided. Without one,
839
1248
  * falls back to identity (`===`) — which never matches structurally-equal
@@ -858,7 +1267,7 @@ declare class TnAutocompleteComponent<T = unknown> implements ControlValueAccess
858
1267
  private detachOverlay;
859
1268
  private scrollToHighlighted;
860
1269
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnAutocompleteComponent<any>, never>;
861
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnAutocompleteComponent<any>, "tn-autocomplete", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "requireSelection": { "alias": "requireSelection"; "required": false; "isSignal": true; }; "allowCustomValue": { "alias": "allowCustomValue"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "filterFn": { "alias": "filterFn"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "maxResults": { "alias": "maxResults"; "required": false; "isSignal": true; }; "panelMaxHeight": { "alias": "panelMaxHeight"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "optionTestIdKey": { "alias": "optionTestIdKey"; "required": false; "isSignal": true; }; }, { "optionSelected": "optionSelected"; "searchChange": "searchChange"; "loadMore": "loadMore"; "opened": "opened"; }, never, never, true, never>;
1270
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnAutocompleteComponent<any>, "tn-autocomplete", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "requireSelection": { "alias": "requireSelection"; "required": false; "isSignal": true; }; "allowCustomValue": { "alias": "allowCustomValue"; "required": false; "isSignal": true; }; "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "dataSourceDebounce": { "alias": "dataSourceDebounce"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "actionOption": { "alias": "actionOption"; "required": false; "isSignal": true; }; "keepSelectedOption": { "alias": "keepSelectedOption"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "filterFn": { "alias": "filterFn"; "required": false; "isSignal": true; }; "noResultsText": { "alias": "noResultsText"; "required": false; "isSignal": true; }; "maxResults": { "alias": "maxResults"; "required": false; "isSignal": true; }; "panelMaxHeight": { "alias": "panelMaxHeight"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "optionTestIdKey": { "alias": "optionTestIdKey"; "required": false; "isSignal": true; }; }, { "optionSelected": "optionSelected"; "searchChange": "searchChange"; "actionSelected": "actionSelected"; "dataSourceError": "dataSourceError"; "loadMore": "loadMore"; "opened": "opened"; }, never, never, true, never>;
862
1271
  }
863
1272
 
864
1273
  /**
@@ -3635,6 +4044,26 @@ interface ChipHarnessFilters extends BaseHarnessFilters {
3635
4044
  * feed all three.
3636
4045
  */
3637
4046
  type TnChipInputOption<T = unknown> = TnSelectOption<T>;
4047
+ /**
4048
+ * Copy rendered inside `tn-chip-input` that is the same for every instance in
4049
+ * an app. Provide {@link TN_CHIP_INPUT_LABELS} at the app root rather than
4050
+ * repeating the identical string on each call site; the matching input on
4051
+ * `<tn-chip-input>` still wins where one instance needs its own wording.
4052
+ */
4053
+ interface TnChipInputLabels {
4054
+ /** Text shown next to the spinner while a `dataSource` request is in flight. */
4055
+ loading: string;
4056
+ }
4057
+ /** English defaults used when no {@link TN_CHIP_INPUT_LABELS} provider is registered. */
4058
+ declare const TN_CHIP_INPUT_DEFAULT_LABELS: TnChipInputLabels;
4059
+ /**
4060
+ * DI token for app-wide default labels. Provide either a static object or a
4061
+ * `Signal<TnChipInputLabels>` — the latter lets every chip input react to
4062
+ * language changes when the consumer wires it up to an i18n service.
4063
+ *
4064
+ * Explicit input bindings on `<tn-chip-input>` still win over these defaults.
4065
+ */
4066
+ declare const TN_CHIP_INPUT_LABELS: InjectionToken<TnChipInputLabels | Signal<TnChipInputLabels>>;
3638
4067
  /**
3639
4068
  * An editable, multi-value chip input — tokenized entry where typed text
3640
4069
  * becomes removable `tn-chip`s alongside an inline text field. Text is
@@ -3675,7 +4104,7 @@ type TnChipInputOption<T = unknown> = TnSelectOption<T>;
3675
4104
  * </tn-form-field>
3676
4105
  * ```
3677
4106
  */
3678
- declare class TnChipInputComponent<T = string> implements ControlValueAccessor, OnDestroy {
4107
+ declare class TnChipInputComponent<T = string> implements ControlValueAccessor, TnAsyncOptionsHost, OnDestroy {
3679
4108
  private readonly overlay;
3680
4109
  private readonly viewContainerRef;
3681
4110
  /** Unique instance id for ARIA linkage between the input and its dropdown. */
@@ -3721,8 +4150,45 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3721
4150
  * Value-mode option list (`{ label, value }`). When non-empty, chips display
3722
4151
  * the resolved `label` while the form model holds `value`s. Takes precedence
3723
4152
  * over `suggestions`. For async sources, update in response to `(searchChange)`.
4153
+ *
4154
+ * A bound `dataSource` supersedes these as the *suggestions*, but they are
4155
+ * still read when labelling a chip — see {@link labelOptions}.
3724
4156
  */
3725
4157
  options: _angular_core.InputSignal<TnChipInputOption<T>[]>;
4158
+ /**
4159
+ * Server-driven suggestions: a function of `(query, page)` returning the
4160
+ * matches for `query`. Binding it hands the component the debounce,
4161
+ * request cancellation, loading state and error recovery that a consumer
4162
+ * otherwise writes by hand around `(searchChange)`, and it supersedes both
4163
+ * `suggestions` and `options` as the source of the dropdown's rows —
4164
+ * `options` is still consulted when labelling a chip, so a host can name a
4165
+ * value the fetched pages have not produced.
4166
+ *
4167
+ * The chip dropdown is not paged, so `page` is always 0 — the parameter is
4168
+ * there only so one source function can feed both this and `tn-autocomplete`.
4169
+ *
4170
+ * The first query runs when the field is first focused, not on init. Binding
4171
+ * a source that is `undefined` at that moment is fine — the field fetches as
4172
+ * soon as one arrives, open panel included.
4173
+ *
4174
+ * Swapping one source function for ANOTHER is not a signal to refetch: a
4175
+ * source is expected to be a stable function reading live configuration,
4176
+ * which is what keeps it from being replaced out from under a request in
4177
+ * flight. Call `refreshOptions()` when that configuration moves.
4178
+ *
4179
+ * @example
4180
+ * ```html
4181
+ * <tn-chip-input [dataSource]="groupOptions" />
4182
+ * ```
4183
+ */
4184
+ dataSource: _angular_core.InputSignal<TnOptionsFetchFn<TnChipInputOption<T>> | undefined>;
4185
+ /** Debounce applied to typing before `dataSource` is queried, in ms. */
4186
+ dataSourceDebounce: _angular_core.InputSignal<number>;
4187
+ /**
4188
+ * Text shown next to the spinner while a `dataSource` request is in flight.
4189
+ * Falls back to {@link TN_CHIP_INPUT_LABELS}.
4190
+ */
4191
+ loadingText: _angular_core.InputSignal<string | undefined>;
3726
4192
  /**
3727
4193
  * Comparator for value equality — used for de-duplication, display resolution
3728
4194
  * and the selected-set. Defaults to identity (`===`), correct for primitives;
@@ -3749,7 +4215,7 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3749
4215
  */
3750
4216
  testId: _angular_core.InputSignal<TnTestIdValue>;
3751
4217
  /** Test-id base, falling back to the bound control name when `testId` is unset. */
3752
- protected resolvedTestId: _angular_core.Signal<TnTestIdValue>;
4218
+ protected resolvedTestId: Signal<TnTestIdValue>;
3753
4219
  /**
3754
4220
  * Optional extractor for the per-option test-id discriminator, applied to both
3755
4221
  * a suggestion row and the chip it becomes. Defaults to the option's `label`,
@@ -3774,11 +4240,36 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3774
4240
  * the consumer if the lookup is expensive.
3775
4241
  */
3776
4242
  searchChange: _angular_core.OutputEmitterRef<string>;
4243
+ /**
4244
+ * Emits once per failed `dataSource` request. The component recovers on its
4245
+ * own — the stream stays alive and the failed term stays retryable — so this
4246
+ * is purely for the app to report the failure the way it reports others.
4247
+ */
4248
+ dataSourceError: _angular_core.OutputEmitterRef<unknown>;
4249
+ private readonly defaultLabels;
4250
+ protected readonly resolvedLoadingText: Signal<string>;
3777
4251
  private readonly container;
3778
4252
  private readonly inputEl;
3779
4253
  private readonly dropdownTemplate;
3780
4254
  /** Committed chip values — the form model. */
3781
4255
  protected values: _angular_core.WritableSignal<T[]>;
4256
+ /**
4257
+ * The label each committed value was picked FROM, for values whose row is no
4258
+ * longer in {@link optionList} or {@link options}.
4259
+ *
4260
+ * With a `dataSource` the fetched page is the only list a chip's label can be
4261
+ * resolved from, and it is replaced on the next search: pick `admins`, type
4262
+ * `dev`, and the row carrying `g-7` is gone, so the chip the user just made
4263
+ * reverts to `g-7` and stays that way. The pinned `[options]` escape hatch
4264
+ * does not cover it — the host would have to append every freshly picked row
4265
+ * to that input, which is the bookkeeping `dataSource` exists to remove.
4266
+ *
4267
+ * `tn-autocomplete` remembers the same thing in its `selectedLabel`; this is
4268
+ * the multi-value form of it. Consulted LAST (see {@link labelOptions}), so a
4269
+ * live row or a host pin always wins over a name that may since have changed,
4270
+ * and pruned to the committed values so it cannot grow without bound.
4271
+ */
4272
+ private readonly rememberedLabels;
3782
4273
  /** Current text in the field. */
3783
4274
  protected inputValue: _angular_core.WritableSignal<string>;
3784
4275
  /** Whether the suggestion dropdown is open. */
@@ -3790,22 +4281,94 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3790
4281
  /** CVA disabled state pushed by the form. */
3791
4282
  private formDisabled;
3792
4283
  /** Combined disabled state from the input and the form. */
3793
- protected isDisabled: _angular_core.Signal<boolean>;
4284
+ protected isDisabled: Signal<boolean>;
3794
4285
  /** Whether another chip may still be added under `maxChips`. */
3795
- protected canAddMore: _angular_core.Signal<boolean>;
4286
+ protected canAddMore: Signal<boolean>;
4287
+ /** Async engine backing `dataSource`; idle while no source is bound. */
4288
+ private readonly asyncOptions;
4289
+ /**
4290
+ * Whether a `dataSource` request is in flight.
4291
+ *
4292
+ * Surfaced in the dropdown because with a `dataSource` bound the rows are NOT
4293
+ * re-filtered on the label — the server already applied the query — so the
4294
+ * panel keeps showing the *previous* term's matches, clickable and looking
4295
+ * current, for the debounce plus the round trip. Without a cue that is
4296
+ * indistinguishable from "these are your results".
4297
+ *
4298
+ * It covers the ROUND TRIP only. The debounce window before it is
4299
+ * deliberately left uncued: a spinner appearing and vanishing on every
4300
+ * keystroke of a term that is still being typed is noise, and the request it
4301
+ * would announce may never be sent.
4302
+ */
4303
+ protected readonly loading: Signal<boolean>;
4304
+ /**
4305
+ * Unified option list. A bound `dataSource` wins; then value-mode `options`;
4306
+ * otherwise string-mode `suggestions` lifted into `{ label: s, value: s }`.
4307
+ */
4308
+ protected optionList: Signal<TnChipInputOption<T>[]>;
3796
4309
  /**
3797
- * Unified option list. Value-mode `options` win; otherwise string-mode
3798
- * `suggestions` are lifted into `{ label: s, value: s }`.
4310
+ * The rows a chip's label and test id may be resolved from: the suggestions,
4311
+ * plus — with a `dataSource` bound — the `options` input.
4312
+ *
4313
+ * Those two lists are the same thing without a `dataSource`. With one, the
4314
+ * fetched pages are the only source of labels, and the first of them does not
4315
+ * exist until the field is focused: a form loaded with ids would render every
4316
+ * chip as its raw id until someone clicked into it. `options` is how a host
4317
+ * names values it already knows the labels for, so it stays part of the
4318
+ * lookup even where it is not part of the dropdown.
4319
+ *
4320
+ * Deliberately not deduplicated: the order is the precedence — the server's
4321
+ * fetched rows, then the host's pinned ones, then the labels this field
4322
+ * remembered committing ({@link rememberedLabels}) — and every reader takes
4323
+ * the first match, so the freshest name for a value wins.
4324
+ *
4325
+ * Read by {@link commitText} as well, so the labels the field paints and the
4326
+ * labels it accepts back are one set. Deliberately NOT read by
4327
+ * {@link filteredSuggestions}: with a source bound the rows are shown as the
4328
+ * server ordered and filtered them, and splicing the pinned rows in would
4329
+ * append the same entries to every term's results, matching or not.
3799
4330
  */
3800
- protected optionList: _angular_core.Signal<TnChipInputOption<T>[]>;
4331
+ private readonly labelOptions;
3801
4332
  /** Options matching the typed text and not already selected. */
3802
- protected filteredSuggestions: _angular_core.Signal<TnChipInputOption<T>[]>;
4333
+ protected filteredSuggestions: Signal<TnChipInputOption<T>[]>;
3803
4334
  private onChange;
3804
4335
  private onTouched;
3805
4336
  private overlayRef?;
3806
4337
  private overlaySubs;
4338
+ /**
4339
+ * Set when the panel was closed deliberately — by committing a chip, or by
4340
+ * Escape — and read by everything that would otherwise re-open it.
4341
+ *
4342
+ * Committing changes the suggestion list, since the chosen row is now
4343
+ * excluded, which re-runs the effect below. On the static path the empty
4344
+ * input makes `activelySearching` false and the close stands; with a
4345
+ * `dataSource` bound, `searchingEmpty` holds regardless, so the panel sprang
4346
+ * back open against an empty field, still listing the rows of the term just
4347
+ * committed.
4348
+ *
4349
+ * Escape is here for the same reason and one more: a response landing after
4350
+ * it reaches `syncDropdownAfterFetch`, which OPENS. Typing `an`, pressing
4351
+ * Escape inside the 250 ms debounce, and waiting put the panel straight back
4352
+ * with focus still in the field — Escape defeated for the whole in-flight
4353
+ * window. The ARIA combobox pattern is that Escape dismisses the popup and it
4354
+ * stays dismissed, so that was a keyboard and AT regression, not a cosmetic
4355
+ * one.
4356
+ *
4357
+ * Cleared by the next thing that is genuinely a search, so typing re-arms the
4358
+ * panel immediately.
4359
+ */
4360
+ private closedByUser;
3807
4361
  constructor();
3808
4362
  ngOnDestroy(): void;
4363
+ /**
4364
+ * Discard the pages fetched from `dataSource` and re-query the current term.
4365
+ *
4366
+ * For a caller whose `[dataSource]` is a fixed function reading live
4367
+ * configuration — the shape that keeps the source from being swapped out
4368
+ * from under a search in flight — this is how a change to that configuration
4369
+ * takes effect, rather than waiting for the next keystroke to notice.
4370
+ */
4371
+ refreshOptions(): void;
3809
4372
  writeValue(value: T[] | null | undefined): void;
3810
4373
  registerOnChange(fn: (value: T[]) => void): void;
3811
4374
  registerOnTouched(fn: () => void): void;
@@ -3850,6 +4413,8 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3850
4413
  * emitting one would invite collisions between inputs.
3851
4414
  */
3852
4415
  protected suggestionTestId(option: TnChipInputOption<T>): TnTestIdValue;
4416
+ /** Test-id parts for the dropdown's status row. Mirrors `tn-autocomplete`. */
4417
+ protected statusTestIdParts(status: 'loading'): (string | number | null | undefined)[];
3853
4418
  /**
3854
4419
  * Keeps a scoped id only when both halves of it survive normalization.
3855
4420
  *
@@ -3863,10 +4428,32 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3863
4428
  */
3864
4429
  private discriminatedTestId;
3865
4430
  private isCommitKey;
3866
- /** Commits typed text: resolve it to an option's value, else accept as custom. */
4431
+ /**
4432
+ * Commits typed text: resolve it to an option's value, else accept as custom.
4433
+ *
4434
+ * Matched against {@link labelOptions}, not the dropdown rows, so that every
4435
+ * label the field is willing to PAINT it is also willing to READ back. With a
4436
+ * `dataSource` bound, `options` names values the host already knows the
4437
+ * labels for; a chip rendered as `admins` that this method did not recognise
4438
+ * when `admins` was typed was silently dropped under
4439
+ * `allowCustomValue="false"` and committed as the raw string under the
4440
+ * default `true` — a second chip beside the one it duplicates.
4441
+ */
3867
4442
  private commitText;
3868
- /** Commits a resolved value, honouring duplicate and cap rules. */
4443
+ /**
4444
+ * Commits a resolved value, honouring duplicate and cap rules.
4445
+ *
4446
+ * `label` is the text of the option the value came from, when there was one —
4447
+ * remembered so the chip keeps that name after the page carrying its row is
4448
+ * replaced. See {@link rememberedLabels}. Free text passes none: such a chip
4449
+ * is its own label, and `String(value)` already renders it.
4450
+ */
3869
4451
  private commitValue;
4452
+ /**
4453
+ * Drop remembered labels for values no longer committed, so the memory stays
4454
+ * bounded by the chips on screen rather than by everything ever picked.
4455
+ */
4456
+ private pruneRememberedLabels;
3870
4457
  /**
3871
4458
  * The option a committed value came from, or `undefined` for a free-text chip.
3872
4459
  *
@@ -3888,10 +4475,34 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3888
4475
  private valuesIncludes;
3889
4476
  private valueMatches;
3890
4477
  private clearInput;
4478
+ /**
4479
+ * Re-decide the panel once a `dataSource` response lands.
4480
+ *
4481
+ * The constructor effect only ever OPENS — it must not fight Escape, blur or
4482
+ * the post-commit close — and with a source bound `onInput` runs
4483
+ * `syncDropdown()` against the PREVIOUS term's rows, which are still there,
4484
+ * so it leaves the panel open too. Nothing was left to retract it: a search
4485
+ * that matched nothing kept a bordered, empty `role="listbox"` attached,
4486
+ * `aria-expanded="true"` over it, until the next keystroke or blur. The
4487
+ * static path never reaches that state, because there the label filter has
4488
+ * already emptied the list by the time `syncDropdown()` reads it.
4489
+ *
4490
+ * The guards are every deliberate close: {@link closedByUser} covers the
4491
+ * post-commit close and Escape, `focused()` covers blur. This must not reopen
4492
+ * a panel any of them just shut.
4493
+ */
4494
+ private syncDropdownAfterFetch;
3891
4495
  /**
3892
4496
  * Opens the dropdown when there is something to show, closes it otherwise.
3893
4497
  * Stays closed once the chip cap is reached — suggesting rows that
3894
4498
  * `commitValue()` would reject is misleading.
4499
+ *
4500
+ * A request in flight counts as something to show even with no rows behind
4501
+ * it: the loading row is the only in-flight cue this field has, and the
4502
+ * fetch most likely to be slow is the COLD one on first focus, where there
4503
+ * is nothing to keep the panel open on its own. Without this the panel was
4504
+ * detached for that whole round trip — no spinner, no `aria-busy` — and the
4505
+ * empty listbox that carries the loading row cost no height.
3895
4506
  */
3896
4507
  private syncDropdown;
3897
4508
  /** Keeps the keyboard-highlighted suggestion visible within the scrolling panel. */
@@ -3901,7 +4512,7 @@ declare class TnChipInputComponent<T = string> implements ControlValueAccessor,
3901
4512
  private attachOverlay;
3902
4513
  private detachOverlay;
3903
4514
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnChipInputComponent<any>, never>;
3904
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnChipInputComponent<any>, "tn-chip-input", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "separatorKeys": { "alias": "separatorKeys"; "required": false; "isSignal": true; }; "addOnBlur": { "alias": "addOnBlur"; "required": false; "isSignal": true; }; "allowCustomValue": { "alias": "allowCustomValue"; "required": false; "isSignal": true; }; "allowDuplicates": { "alias": "allowDuplicates"; "required": false; "isSignal": true; }; "maxChips": { "alias": "maxChips"; "required": false; "isSignal": true; }; "suggestions": { "alias": "suggestions"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "optionTestIdKey": { "alias": "optionTestIdKey"; "required": false; "isSignal": true; }; }, { "chipAdded": "chipAdded"; "chipRemoved": "chipRemoved"; "searchChange": "searchChange"; }, never, never, true, never>;
4515
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TnChipInputComponent<any>, "tn-chip-input", never, { "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "separatorKeys": { "alias": "separatorKeys"; "required": false; "isSignal": true; }; "addOnBlur": { "alias": "addOnBlur"; "required": false; "isSignal": true; }; "allowCustomValue": { "alias": "allowCustomValue"; "required": false; "isSignal": true; }; "allowDuplicates": { "alias": "allowDuplicates"; "required": false; "isSignal": true; }; "maxChips": { "alias": "maxChips"; "required": false; "isSignal": true; }; "suggestions": { "alias": "suggestions"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "dataSource": { "alias": "dataSource"; "required": false; "isSignal": true; }; "dataSourceDebounce": { "alias": "dataSourceDebounce"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "testId": { "alias": "testId"; "required": false; "isSignal": true; }; "optionTestIdKey": { "alias": "optionTestIdKey"; "required": false; "isSignal": true; }; }, { "chipAdded": "chipAdded"; "chipRemoved": "chipRemoved"; "searchChange": "searchChange"; "dataSourceError": "dataSourceError"; }, never, never, true, never>;
3905
4516
  }
3906
4517
 
3907
4518
  /**
@@ -3944,6 +4555,21 @@ declare class TnChipInputHarness extends ComponentHarness {
3944
4555
  removeChip(value: string): Promise<void>;
3945
4556
  /** Visible suggestion option texts, or `[]` when the dropdown is closed. */
3946
4557
  getSuggestions(): Promise<string[]>;
4558
+ /**
4559
+ * Whether the dropdown is showing its loading row, i.e. a `[dataSource]`
4560
+ * request is in flight. Any rows on screen while this is true belong to the
4561
+ * previous term — a `dataSource` is trusted to have applied the query, so
4562
+ * they are not re-filtered as the user types. On the first focus there are
4563
+ * no rows yet and the panel holds the loading row alone.
4564
+ *
4565
+ * @example
4566
+ * ```typescript
4567
+ * const chips = await loader.getHarness(TnChipInputHarness);
4568
+ * await chips.focus();
4569
+ * expect(await chips.isLoading()).toBe(true);
4570
+ * ```
4571
+ */
4572
+ isLoading(): Promise<boolean>;
3947
4573
  isDisabled(): Promise<boolean>;
3948
4574
  }
3949
4575
  interface TnChipInputHarnessFilters extends BaseHarnessFilters {
@@ -9031,6 +9657,35 @@ declare class TnTableComponent<T = unknown> implements OnInit {
9031
9657
  dataSource: _angular_core.InputSignal<TnTableDataSource<T> | T[]>;
9032
9658
  displayedColumns: _angular_core.InputSignal<string[]>;
9033
9659
  trackBy: _angular_core.InputSignal<((index: number, item: T) => unknown) | undefined>;
9660
+ /**
9661
+ * Identity function for SELECTION. When set, the table tracks selected rows by the
9662
+ * key this returns rather than by object reference, and the selection survives a
9663
+ * `dataSource` change: paging, sorting, filtering, or a background reload that hands
9664
+ * back freshly-built row objects. Rows selected on a page the table is no longer
9665
+ * showing stay selected and keep coming back in `selectionChange`.
9666
+ *
9667
+ * Without it the table keeps its historical behaviour — any `dataSource` reference
9668
+ * change clears the selection and emits an empty `selectionChange` — because
9669
+ * reference-keyed selections cannot be recognised in a new array and would otherwise
9670
+ * leave the header checkbox counting rows that are no longer on screen.
9671
+ *
9672
+ * This is deliberately separate from {@link trackBy}, which is a rendering concern
9673
+ * (`@for` identity, index-based by default) and is free to stay so on a table whose
9674
+ * selection has to be stable. Pass a key that identifies the ROW, not its position —
9675
+ * and bind a stable member rather than writing the function in the template, since
9676
+ * Angular's expression grammar has no arrow functions:
9677
+ * `[selectionKey]="rowKey"` for a `rowKey = (row: User) => row.id` on the host.
9678
+ *
9679
+ * The select-all checkbox stays scoped to the visible page — it selects and clears
9680
+ * the rows the table is currently showing, and `isAllSelected` reflects only those,
9681
+ * so it never claims to have selected rows the user cannot see.
9682
+ *
9683
+ * Retention is unconditional, and it has to be: the table sees one page and cannot
9684
+ * tell a row that moved off-screen from one that was destroyed. A consumer whose rows
9685
+ * can disappear for good owns that check — reconcile `selectionChange` against the
9686
+ * full list it holds, and call {@link clearSelection} once a batch action is done.
9687
+ */
9688
+ selectionKey: _angular_core.InputSignal<((row: T) => unknown) | undefined>;
9034
9689
  emptyMessage: _angular_core.InputSignal<string>;
9035
9690
  /**
9036
9691
  * Optional second line under `emptyMessage`, for empty states that carry both a headline and an
@@ -9250,8 +9905,29 @@ declare class TnTableComponent<T = unknown> implements OnInit {
9250
9905
  expandedRows: _angular_core.WritableSignal<Set<unknown>>;
9251
9906
  private static instanceCount;
9252
9907
  private readonly instanceId;
9908
+ /**
9909
+ * The rows of the CURRENT `data()` that are selected. With `selectionKey` set this is
9910
+ * the visible slice of {@link selectedByKey} — it is what the row checkboxes and the
9911
+ * select-all state read, so both stay scoped to the page on screen.
9912
+ */
9253
9913
  selection: SelectionModel<T>;
9914
+ /**
9915
+ * The whole selection, keyed by `selectionKey`, including rows that are no longer in
9916
+ * `data()`. Empty (and unused) when no `selectionKey` is set.
9917
+ *
9918
+ * It holds the row objects, not just the keys, so `selectionChange` can hand a
9919
+ * consumer the rows it selected on an earlier page — a batch action has to act on
9920
+ * rows, and the table is the only thing that ever saw them.
9921
+ */
9922
+ private readonly selectedByKey;
9923
+ /** Number of selected rows present in the current `data()` — see {@link selection}. */
9254
9924
  private selectionCount;
9925
+ /**
9926
+ * The array last handed to `selectionChange`, so a reconcile that leaves the selection
9927
+ * exactly as the consumer already sees it can stay quiet — re-running because
9928
+ * `selectionKey`'s identity changed is not news.
9929
+ */
9930
+ private lastEmittedSelection;
9255
9931
  private initialized;
9256
9932
  private columnDefMap;
9257
9933
  constructor();
@@ -9462,6 +10138,29 @@ declare class TnTableComponent<T = unknown> implements OnInit {
9462
10138
  toggleSelectAll(): void;
9463
10139
  toggleRowSelection(row: T): void;
9464
10140
  isRowSelected(row: T): boolean;
10141
+ /**
10142
+ * Drops the whole selection, including rows retained from pages the table is not
10143
+ * showing. Use it after a batch action consumes the selection.
10144
+ *
10145
+ * Emits an empty `selectionChange` if anything was selected. The caller knows it
10146
+ * cleared, but `selectionChange` is the only way anything else can: a toolbar bound to
10147
+ * a mirrored copy of the selection a layer up would otherwise stay stale until the next
10148
+ * `dataSource` change happened to correct it. Clearing {@link selection} directly is not
10149
+ * enough once `selectionKey` is set: the retained rows would survive and come back on
10150
+ * the next reconcile.
10151
+ */
10152
+ clearSelection(): void;
10153
+ /** Re-applies the retained selection to the rows currently on screen. */
10154
+ private syncSelectionToData;
10155
+ /** What `selectionChange` carries: the whole selection when keyed, the page's otherwise. */
10156
+ private emittedSelection;
10157
+ /** Emits the current selection and records it. For the paths that always change it. */
10158
+ private emitSelection;
10159
+ /**
10160
+ * Emits only if the selection differs from the one last emitted, by row identity — for
10161
+ * the paths that may be re-run without anything having moved.
10162
+ */
10163
+ private emitSelectionIfChanged;
9465
10164
  /**
9466
10165
  * The column rendered as the card title. The first `displayedColumns` entry
9467
10166
  * whose def sets `cardTitle`, else the first column not marked `cardHidden` —
@@ -9527,7 +10226,7 @@ declare class TnTableComponent<T = unknown> implements OnInit {
9527
10226
  getCardLabel(column: string): string;
9528
10227
  getCellValue(row: T, column: string): unknown;
9529
10228
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<TnTableComponent<any>, never>;
9530
- 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; }; "emptyDescription": { "alias": "emptyDescription"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "scrollRegionAriaLabel": { "alias": "scrollRegionAriaLabel"; "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; }; "isRowExpandable": { "alias": "isRowExpandable"; "required": false; "isSignal": true; }; "activeRow": { "alias": "activeRow"; "required": false; "isSignal": true; }; "activeWhen": { "alias": "activeWhen"; "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; }; "expandOnRowClick": { "alias": "expandOnRowClick"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; "minColumnWidth": { "alias": "minColumnWidth"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "cardBreakpoint": { "alias": "cardBreakpoint"; "required": false; "isSignal": true; }; "cardPrimaryCount": { "alias": "cardPrimaryCount"; "required": false; "isSignal": true; }; "sortColumn": { "alias": "sortColumn"; "required": false; "isSignal": true; }; "sortDirection": { "alias": "sortDirection"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; "rowDoubleClick": "rowDoubleClick"; "sortColumn": "sortColumnChange"; "sortDirection": "sortDirectionChange"; }, ["columnDefs", "detailRowDef", "rowActionsDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
10229
+ 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; }; "selectionKey": { "alias": "selectionKey"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "emptyDescription": { "alias": "emptyDescription"; "required": false; "isSignal": true; }; "emptyIcon": { "alias": "emptyIcon"; "required": false; "isSignal": true; }; "scrollRegionAriaLabel": { "alias": "scrollRegionAriaLabel"; "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; }; "isRowExpandable": { "alias": "isRowExpandable"; "required": false; "isSignal": true; }; "activeRow": { "alias": "activeRow"; "required": false; "isSignal": true; }; "activeWhen": { "alias": "activeWhen"; "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; }; "expandOnRowClick": { "alias": "expandOnRowClick"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "fixedLayout": { "alias": "fixedLayout"; "required": false; "isSignal": true; }; "minColumnWidth": { "alias": "minColumnWidth"; "required": false; "isSignal": true; }; "minWidth": { "alias": "minWidth"; "required": false; "isSignal": true; }; "mobileLayout": { "alias": "mobileLayout"; "required": false; "isSignal": true; }; "cardBreakpoint": { "alias": "cardBreakpoint"; "required": false; "isSignal": true; }; "cardPrimaryCount": { "alias": "cardPrimaryCount"; "required": false; "isSignal": true; }; "sortColumn": { "alias": "sortColumn"; "required": false; "isSignal": true; }; "sortDirection": { "alias": "sortDirection"; "required": false; "isSignal": true; }; }, { "sortChange": "sortChange"; "selectionChange": "selectionChange"; "rowClick": "rowClick"; "rowDoubleClick": "rowDoubleClick"; "sortColumn": "sortColumnChange"; "sortDirection": "sortDirectionChange"; }, ["columnDefs", "detailRowDef", "rowActionsDef"], never, true, [{ directive: typeof TnTestIdDirective; inputs: { "tnTestId": "testId"; }; outputs: {}; }]>;
9531
10230
  }
9532
10231
 
9533
10232
  /**
@@ -15631,5 +16330,5 @@ declare const TN_THEME_DEFINITIONS: readonly TnThemeDefinition[];
15631
16330
  */
15632
16331
  declare const THEME_MAP: Map<TnTheme, TnThemeDefinition>;
15633
16332
 
15634
- export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, THEME_MAP, THEME_STORAGE_KEY, TN_AUTOCOMPLETE_DEFAULT_LABELS, TN_AUTOCOMPLETE_LABELS, TN_BRANDED_SPINNER_DEFAULT_LABEL, TN_CALENDAR_INTL, TN_CALENDAR_INTL_DEFAULTS, TN_DEFAULT_FALLBACK_LABELS, TN_DIALOG_CHROME_LABELS, TN_DIALOG_DEFAULT_CHROME_LABELS, TN_DIALOG_SHELL_DEFAULT_LABEL, TN_DRAWER_CONTENT_LABEL, TN_DRAWER_DEFAULT_LABEL, TN_FALLBACK_LABELS, TN_FORM_FIELD_CONTEXT, TN_FORM_FIELD_DISMISSIBLE_ERRORS, TN_FORM_FIELD_ERRORS, TN_FORM_LIST_CONTEXT, TN_PARTICLE_PROGRESS_BAR_DEFAULT_LABEL, TN_PROGRESS_BAR_DEFAULT_LABEL, TN_RADIO_GROUP, TN_SELECT_DEFAULT_LABELS, TN_SELECT_LABELS, TN_SIDE_PANEL_CONTENT_LABEL, TN_SIDE_PANEL_DEFAULT_LABEL, TN_SIDE_PANEL_OVERFLOW_TOLERANCE_PX, TN_SPINNER_DEFAULT_LABEL, TN_STEPPER_STATUS_COMPLETED, TN_STEPPER_STATUS_ERROR, TN_TABLE_DEFAULT_LABELS, TN_TABLE_LABELS, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TABLE_SCROLL_REGION_LABEL, TN_TAB_PANEL_CONTENT_LABEL, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TN_TOAST_ANNOUNCE_DELAY_MS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerActionHarness, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarCellHarness, TnCalendarComponent, TnCalendarHarness, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxGroupComponent, TnCheckboxGroupHarness, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerHarness, TnFilePickerPopupComponent, TnFormErrorsComponent, TnFormErrorsHarness, TnFormFieldComponent, TnFormFieldHarness, TnFormListComponent, TnFormListHarness, TnFormListItemComponent, TnFormListItemHarness, TnFormSectionComponent, TnFormSectionHarness, 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, TnNestedTreeDataSource, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioGroupComponent, TnRadioGroupHarness, TnRadioHarness, TnRowActionsDefDirective, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnStepperHarness, TnStepperNextDirective, TnStepperPreviousDirective, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTableTesting, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTooltipHarness, TnTooltipTesting, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeHarness, TnTreeNodeComponent, TnTreeNodeHarness, TnTreeNodeOutletDirective, TnTreeVirtualScrollNodeOutletDirective, TnTreeVirtualScrollViewComponent, TnTreeVirtualScrollViewHarness, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, allowsCurrentDirectorySelection, canonicalizeSize, composeTestId, controlTestId, createFlatTreeControl, createLucideLibrary, createNestedTreeControl, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, defaultTreeItemSize, formatSize, formatSizeExact, formatSizeForEditing, getSelectableTypes, injectTnCalendarIntl, injectTnFallbackName, injectTnFormFieldAria, injectTnLabels, isPathWithinRoot, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, normalizeRootPath, optionTestId, parseLabelMarkup, parseSize, parseSizeParts, registerLucideIcons, resolveErrorMessage, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
15635
- export type { AutocompleteHarnessFilters, BannerActionHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CalendarCellFill, CalendarCellHarnessFilters, CalendarHarnessFilters, CheckboxGroupHarnessFilters, CheckboxHarnessFilters, ChipColor, ChipHarnessFilters, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FileInputHarnessFilters, FilePickerCallbacks, FilePickerCreateAction, FilePickerCreateActionEvent, FilePickerError, FilePickerHarnessFilters, FilePickerMode, FileSystemItem, FileSystemItemType, FlatTreeControlOptions, FormFieldHarnessFilters, FormSectionHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelMarkupSegment, LabelMarkupSegmentType, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, NestedTreeControlOptions, ParsedSize, PathSegment, PlatformType, ProgressBarMode, RadioGroupHarnessFilters, RadioHarnessFilters, ResolveErrorMessageOptions, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SizeStandard, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, StepperHarnessFilters, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnAutocompleteLabels, TnAutocompleteOption, TnBannerType, TnButtonToggleType, TnCalendarIntl, TnCalendarIntlInput, TnCalendarView, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnCheckboxOption, TnChipInputHarnessFilters, TnChipInputOption, TnConfirmDialogData, TnDialogChromeLabels, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFallbackLabels, TnFallbackName, TnFlatTreeNode, TnFormErrorsHarnessFilters, TnFormFieldAriaBindings, TnFormFieldContext, TnFormFieldErrorMessage, TnFormFieldErrorMessages, TnFormFieldErrorResolver, TnFormListContext, TnFormListHarnessFilters, TnFormListItemHarnessFilters, TnMenuItem, TnOptionTestIdSource, TnRadioGroupApi, TnRadioOption, TnSelectLabels, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataProvider, TnTableDataSource, TnTableHarnessFilters, TnTableLabels, TnTableMobileLayout, TnTablePagerHarnessFilters, TnTablePagerLabels, TnTablePagination, TnTestAttrName, TnTestIdValue, TnThemeDefinition, TnToastCall, TnToastConfig, TnTreeExpansion, TnTreeHarnessFilters, TnTreeNodeHarnessFilters, TnTreeVirtualNodeData, TnTreeVirtualScrollViewHarnessFilters, TooltipHarnessFilters, TooltipPosition, YearCell };
16333
+ export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, THEME_MAP, THEME_STORAGE_KEY, TN_AUTOCOMPLETE_DEFAULT_LABELS, TN_AUTOCOMPLETE_LABELS, TN_BRANDED_SPINNER_DEFAULT_LABEL, TN_CALENDAR_INTL, TN_CALENDAR_INTL_DEFAULTS, TN_CHIP_INPUT_DEFAULT_LABELS, TN_CHIP_INPUT_LABELS, TN_DEFAULT_FALLBACK_LABELS, TN_DIALOG_CHROME_LABELS, TN_DIALOG_DEFAULT_CHROME_LABELS, TN_DIALOG_SHELL_DEFAULT_LABEL, TN_DRAWER_CONTENT_LABEL, TN_DRAWER_DEFAULT_LABEL, TN_FALLBACK_LABELS, TN_FORM_FIELD_CONTEXT, TN_FORM_FIELD_DISMISSIBLE_ERRORS, TN_FORM_FIELD_ERRORS, TN_FORM_LIST_CONTEXT, TN_PARTICLE_PROGRESS_BAR_DEFAULT_LABEL, TN_PROGRESS_BAR_DEFAULT_LABEL, TN_RADIO_GROUP, TN_SELECT_DEFAULT_LABELS, TN_SELECT_LABELS, TN_SIDE_PANEL_CONTENT_LABEL, TN_SIDE_PANEL_DEFAULT_LABEL, TN_SIDE_PANEL_OVERFLOW_TOLERANCE_PX, TN_SPINNER_DEFAULT_LABEL, TN_STEPPER_STATUS_COMPLETED, TN_STEPPER_STATUS_ERROR, TN_TABLE_DEFAULT_LABELS, TN_TABLE_LABELS, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TABLE_SCROLL_REGION_LABEL, TN_TAB_PANEL_CONTENT_LABEL, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TN_TOAST_ANNOUNCE_DELAY_MS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerActionHarness, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarCellHarness, TnCalendarComponent, TnCalendarHarness, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxGroupComponent, TnCheckboxGroupHarness, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerHarness, TnFilePickerPopupComponent, TnFormErrorsComponent, TnFormErrorsHarness, TnFormFieldComponent, TnFormFieldHarness, TnFormListComponent, TnFormListHarness, TnFormListItemComponent, TnFormListItemHarness, TnFormSectionComponent, TnFormSectionHarness, 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, TnNestedTreeDataSource, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioGroupComponent, TnRadioGroupHarness, TnRadioHarness, TnRowActionsDefDirective, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnStepperHarness, TnStepperNextDirective, TnStepperPreviousDirective, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTableTesting, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTooltipHarness, TnTooltipTesting, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeHarness, TnTreeNodeComponent, TnTreeNodeHarness, TnTreeNodeOutletDirective, TnTreeVirtualScrollNodeOutletDirective, TnTreeVirtualScrollViewComponent, TnTreeVirtualScrollViewHarness, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, allowsCurrentDirectorySelection, canonicalizeSize, composeTestId, controlTestId, createFlatTreeControl, createLucideLibrary, createNestedTreeControl, createShortcut, createTnOptionsDataSource, defaultSpriteBasePath, defaultSpriteConfigPath, defaultTreeItemSize, formatSize, formatSizeExact, formatSizeForEditing, getSelectableTypes, injectTnCalendarIntl, injectTnFallbackName, injectTnFormFieldAria, injectTnLabels, isPathWithinRoot, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, normalizeRootPath, optionTestId, parseLabelMarkup, parseSize, parseSizeParts, registerLucideIcons, resolveErrorMessage, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
16334
+ export type { AutocompleteHarnessFilters, BannerActionHarnessFilters, BannerHarnessFilters, ButtonHarnessFilters, ButtonToggleHarnessFilters, CalendarCell, CalendarCellFill, CalendarCellHarnessFilters, CalendarHarnessFilters, CheckboxGroupHarnessFilters, CheckboxHarnessFilters, ChipColor, ChipHarnessFilters, DateInputHarnessFilters, DateRange, DateRangeInputHarnessFilters, DialogHarnessFilters, EmptyHarnessFilters, ExpansionPanelHarnessFilters, FileInputHarnessFilters, FilePickerCallbacks, FilePickerCreateAction, FilePickerCreateActionEvent, FilePickerError, FilePickerHarnessFilters, FilePickerMode, FileSystemItem, FileSystemItemType, FlatTreeControlOptions, FormFieldHarnessFilters, FormSectionHarnessFilters, IconButtonHarnessFilters, IconHarnessFilters, IconLibrary, IconLibraryType, IconResult, IconSize, IconSource, IconTestingMockOverrides, InputHarnessFilters, KeyCombination, LabelMarkupSegment, LabelMarkupSegmentType, LabelType, LucideIconOptions, MenuHarnessFilters, MockIconRegistry, MockSpriteLoader, NestedTreeControlOptions, ParsedSize, PathSegment, PlatformType, ProgressBarMode, RadioGroupHarnessFilters, RadioHarnessFilters, ResolveErrorMessageOptions, ResolvedIcon, SelectHarnessFilters, ShortcutHandler, SidePanelHarnessFilters, SizeStandard, SlideToggleColor, SlideToggleHarnessFilters, SpinnerMode, SpriteConfig, StepperHarnessFilters, SubscriptSizing, TabChangeEvent, TabHarnessFilters, TabPanelHarnessFilters, TabsHarnessFilters, TnAsyncOptionsHost, TnAutocompleteLabels, TnAutocompleteOption, TnBannerType, TnButtonToggleType, TnCalendarIntl, TnCalendarIntlInput, TnCalendarView, TnCardAction, TnCardControl, TnCardFooterLink, TnCardHeaderStatus, TnCheckboxOption, TnChipInputHarnessFilters, TnChipInputLabels, TnChipInputOption, TnConfirmDialogData, TnDialogChromeLabels, TnDialogDefaults, TnDialogOpenTarget, TnDrawerMode, TnDrawerPosition, TnEmptySize, TnFallbackLabels, TnFallbackName, TnFlatTreeNode, TnFormErrorsHarnessFilters, TnFormFieldAriaBindings, TnFormFieldContext, TnFormFieldErrorMessage, TnFormFieldErrorMessages, TnFormFieldErrorResolver, TnFormListContext, TnFormListHarnessFilters, TnFormListItemHarnessFilters, TnMenuItem, TnOptionTestIdSource, TnOptionsDataSource, TnOptionsDataSourceConfig, TnOptionsFetchFn, TnRadioGroupApi, TnRadioOption, TnSelectLabels, TnSelectOption, TnSelectOptionGroup, TnSelectionChange, TnSortEvent, TnTableDataProvider, TnTableDataSource, TnTableHarnessFilters, TnTableLabels, TnTableMobileLayout, TnTablePagerHarnessFilters, TnTablePagerLabels, TnTablePagination, TnTestAttrName, TnTestIdValue, TnThemeDefinition, TnToastCall, TnToastConfig, TnTreeExpansion, TnTreeHarnessFilters, TnTreeNodeHarnessFilters, TnTreeVirtualNodeData, TnTreeVirtualScrollViewHarnessFilters, TooltipHarnessFilters, TooltipPosition, YearCell };