6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
- import { QueryList } from '@angular/core' ;
9
+ import { EffectRef , Injector , QueryList , Signal , effect , isSignal } from '@angular/core' ;
10
10
import { Subject , Subscription } from 'rxjs' ;
11
11
import {
12
12
UP_ARROW ,
@@ -54,6 +54,7 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
54
54
private _allowedModifierKeys : ListKeyManagerModifierKey [ ] = [ ] ;
55
55
private _homeAndEnd = false ;
56
56
private _pageUpAndDown = { enabled : false , delta : 10 } ;
57
+ private _effectRef : EffectRef | undefined ;
57
58
58
59
/**
59
60
* Predicate function that can be used to check whether an item should be skipped
@@ -64,21 +65,25 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
64
65
// Buffer for the letters that the user has pressed when the typeahead option is turned on.
65
66
private _pressedLetters : string [ ] = [ ] ;
66
67
67
- constructor ( private _items : QueryList < T > | T [ ] ) {
68
+ constructor ( items : QueryList < T > | T [ ] | readonly T [ ] ) ;
69
+ constructor ( items : Signal < T [ ] > | Signal < readonly T [ ] > , injector : Injector ) ;
70
+ constructor (
71
+ private _items : QueryList < T > | T [ ] | readonly T [ ] | Signal < T [ ] > | Signal < readonly T [ ] > ,
72
+ injector ?: Injector ,
73
+ ) {
68
74
// We allow for the items to be an array because, in some cases, the consumer may
69
75
// not have access to a QueryList of the items they want to manage (e.g. when the
70
76
// items aren't being collected via `ViewChildren` or `ContentChildren`).
71
77
if ( _items instanceof QueryList ) {
72
- this . _itemChangesSubscription = _items . changes . subscribe ( ( newItems : QueryList < T > ) => {
73
- if ( this . _activeItem ) {
74
- const itemArray = newItems . toArray ( ) ;
75
- const newIndex = itemArray . indexOf ( this . _activeItem ) ;
78
+ this . _itemChangesSubscription = _items . changes . subscribe ( ( newItems : QueryList < T > ) =>
79
+ this . _itemsChanged ( newItems . toArray ( ) ) ,
80
+ ) ;
81
+ } else if ( isSignal ( _items ) ) {
82
+ if ( ! injector && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) {
83
+ throw new Error ( 'ListKeyManager constructed with a signal must receive an injector' ) ;
84
+ }
76
85
77
- if ( newIndex > - 1 && newIndex !== this . _activeItemIndex ) {
78
- this . _activeItemIndex = newIndex ;
79
- }
80
- }
81
- } ) ;
86
+ this . _effectRef = effect ( ( ) => this . _itemsChanged ( _items ( ) ) , { injector} ) ;
82
87
}
83
88
}
84
89
@@ -144,12 +149,11 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
144
149
* @param debounceInterval Time to wait after the last keystroke before setting the active item.
145
150
*/
146
151
withTypeAhead ( debounceInterval : number = 200 ) : this {
147
- if (
148
- ( typeof ngDevMode === 'undefined' || ngDevMode ) &&
149
- this . _items . length &&
150
- this . _items . some ( item => typeof item . getLabel !== 'function' )
151
- ) {
152
- throw Error ( 'ListKeyManager items in typeahead mode must implement the `getLabel` method.' ) ;
152
+ if ( typeof ngDevMode === 'undefined' || ngDevMode ) {
153
+ const items = this . _getItemsArray ( ) ;
154
+ if ( items . length > 0 && items . some ( item => typeof item . getLabel !== 'function' ) ) {
155
+ throw Error ( 'ListKeyManager items in typeahead mode must implement the `getLabel` method.' ) ;
156
+ }
153
157
}
154
158
155
159
this . _typeaheadSubscription . unsubscribe ( ) ;
@@ -403,6 +407,7 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
403
407
destroy ( ) {
404
408
this . _typeaheadSubscription . unsubscribe ( ) ;
405
409
this . _itemChangesSubscription ?. unsubscribe ( ) ;
410
+ this . _effectRef ?. destroy ( ) ;
406
411
this . _letterKeyStream . complete ( ) ;
407
412
this . tabOut . complete ( ) ;
408
413
this . change . complete ( ) ;
@@ -470,7 +475,22 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
470
475
}
471
476
472
477
/** Returns the items as an array. */
473
- private _getItemsArray ( ) : T [ ] {
478
+ private _getItemsArray ( ) : T [ ] | readonly T [ ] {
479
+ if ( isSignal ( this . _items ) ) {
480
+ return this . _items ( ) ;
481
+ }
482
+
474
483
return this . _items instanceof QueryList ? this . _items . toArray ( ) : this . _items ;
475
484
}
485
+
486
+ /** Callback for when the items have changed. */
487
+ private _itemsChanged ( newItems : T [ ] | readonly T [ ] ) {
488
+ if ( this . _activeItem ) {
489
+ const newIndex = newItems . indexOf ( this . _activeItem ) ;
490
+
491
+ if ( newIndex > - 1 && newIndex !== this . _activeItemIndex ) {
492
+ this . _activeItemIndex = newIndex ;
493
+ }
494
+ }
495
+ }
476
496
}
0 commit comments