Skip to content

Commit 1ea3ba3

Browse files
committedNov 7, 2024·
fix(cdk/listbox): make tabindex zoneless compatible (#29970)
(cherry picked from commit 17ff5be)
1 parent ccbdb6d commit 1ea3ba3

File tree

3 files changed

+50
-34
lines changed

3 files changed

+50
-34
lines changed
 

‎src/cdk/a11y/key-manager/list-key-manager.ts

+15-14
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {EffectRef, Injector, QueryList, Signal, effect, isSignal} from '@angular/core';
10-
import {Subject, Subscription} from 'rxjs';
119
import {
12-
UP_ARROW,
1310
DOWN_ARROW,
11+
END,
12+
HOME,
1413
LEFT_ARROW,
14+
PAGE_DOWN,
15+
PAGE_UP,
1516
RIGHT_ARROW,
1617
TAB,
18+
UP_ARROW,
1719
hasModifierKey,
18-
HOME,
19-
END,
20-
PAGE_UP,
21-
PAGE_DOWN,
2220
} from '@angular/cdk/keycodes';
21+
import {EffectRef, Injector, QueryList, Signal, effect, isSignal, signal} from '@angular/core';
22+
import {Subject, Subscription} from 'rxjs';
2323
import {Typeahead} from './typeahead';
2424

2525
/** This interface is for items that can be passed to a ListKeyManager. */
@@ -40,7 +40,7 @@ export type ListKeyManagerModifierKey = 'altKey' | 'ctrlKey' | 'metaKey' | 'shif
4040
*/
4141
export class ListKeyManager<T extends ListKeyManagerOption> {
4242
private _activeItemIndex = -1;
43-
private _activeItem: T | null = null;
43+
private _activeItem = signal<T | null>(null);
4444
private _wrap = false;
4545
private _typeaheadSubscription = Subscription.EMPTY;
4646
private _itemChangesSubscription?: Subscription;
@@ -204,11 +204,11 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
204204
setActiveItem(item: T): void;
205205

206206
setActiveItem(item: any): void {
207-
const previousActiveItem = this._activeItem;
207+
const previousActiveItem = this._activeItem();
208208

209209
this.updateActiveItem(item);
210210

211-
if (this._activeItem !== previousActiveItem) {
211+
if (this._activeItem() !== previousActiveItem) {
212212
this.change.next(this._activeItemIndex);
213213
}
214214
}
@@ -317,7 +317,7 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
317317

318318
/** The active item. */
319319
get activeItem(): T | null {
320-
return this._activeItem;
320+
return this._activeItem();
321321
}
322322

323323
/** Gets whether the user is currently typing into the manager using the typeahead feature. */
@@ -365,7 +365,7 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
365365
const activeItem = itemArray[index];
366366

367367
// Explicitly check for `null` and `undefined` because other falsy values are valid.
368-
this._activeItem = activeItem == null ? null : activeItem;
368+
this._activeItem.set(activeItem == null ? null : activeItem);
369369
this._activeItemIndex = index;
370370
this._typeahead?.setCurrentSelectedItemIndex(index);
371371
}
@@ -452,8 +452,9 @@ export class ListKeyManager<T extends ListKeyManagerOption> {
452452
/** Callback for when the items have changed. */
453453
private _itemsChanged(newItems: T[] | readonly T[]) {
454454
this._typeahead?.setItems(newItems);
455-
if (this._activeItem) {
456-
const newIndex = newItems.indexOf(this._activeItem);
455+
const activeItem = this._activeItem();
456+
if (activeItem) {
457+
const newIndex = newItems.indexOf(activeItem);
457458

458459
if (newIndex > -1 && newIndex !== this._activeItemIndex) {
459460
this._activeItemIndex = newIndex;

‎src/cdk/listbox/listbox.ts

+25-12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
OnDestroy,
3838
Output,
3939
QueryList,
40+
signal,
4041
} from '@angular/core';
4142
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
4243
import {defer, fromEvent, merge, Observable, Subject} from 'rxjs';
@@ -119,24 +120,24 @@ export class CdkOption<T = unknown> implements ListKeyManagerOption, Highlightab
119120
/** Whether this option is disabled. */
120121
@Input({alias: 'cdkOptionDisabled', transform: booleanAttribute})
121122
get disabled(): boolean {
122-
return this.listbox.disabled || this._disabled;
123+
return this.listbox.disabled || this._disabled();
123124
}
124125
set disabled(value: boolean) {
125-
this._disabled = value;
126+
this._disabled.set(value);
126127
}
127-
private _disabled: boolean = false;
128+
private _disabled = signal(false);
128129

129130
/** The tabindex of the option when it is enabled. */
130131
@Input('tabindex')
131132
get enabledTabIndex() {
132-
return this._enabledTabIndex === undefined
133+
return this._enabledTabIndex() === undefined
133134
? this.listbox.enabledTabIndex
134-
: this._enabledTabIndex;
135+
: this._enabledTabIndex();
135136
}
136137
set enabledTabIndex(value) {
137-
this._enabledTabIndex = value;
138+
this._enabledTabIndex.set(value);
138139
}
139-
private _enabledTabIndex?: number | null;
140+
private _enabledTabIndex = signal<number | null | undefined>(undefined);
140141

141142
/** The option's host element */
142143
readonly element: HTMLElement = inject(ElementRef).nativeElement;
@@ -269,12 +270,12 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
269270
/** The tabindex to use when the listbox is enabled. */
270271
@Input('tabindex')
271272
get enabledTabIndex() {
272-
return this._enabledTabIndex === undefined ? 0 : this._enabledTabIndex;
273+
return this._enabledTabIndex() === undefined ? 0 : this._enabledTabIndex();
273274
}
274275
set enabledTabIndex(value) {
275-
this._enabledTabIndex = value;
276+
this._enabledTabIndex.set(value);
276277
}
277-
private _enabledTabIndex?: number | null;
278+
private _enabledTabIndex = signal<number | null | undefined>(undefined);
278279

279280
/** The value selected in the listbox, represented as an array of option values. */
280281
@Input('cdkListboxValue')
@@ -303,11 +304,23 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
303304

304305
/** Whether the listbox is disabled. */
305306
@Input({alias: 'cdkListboxDisabled', transform: booleanAttribute})
306-
disabled: boolean = false;
307+
get disabled() {
308+
return this._disabled();
309+
}
310+
set disabled(value: boolean) {
311+
this._disabled.set(value);
312+
}
313+
private _disabled = signal(false);
307314

308315
/** Whether the listbox will use active descendant or will move focus onto the options. */
309316
@Input({alias: 'cdkListboxUseActiveDescendant', transform: booleanAttribute})
310-
useActiveDescendant: boolean = false;
317+
get useActiveDescendant() {
318+
return this._useActiveDescendant();
319+
}
320+
set useActiveDescendant(value: boolean) {
321+
this._useActiveDescendant.set(value);
322+
}
323+
private _useActiveDescendant = signal(false);
311324

312325
/** The orientation of the listbox. Only affects keyboard interaction, not visual layout. */
313326
@Input('cdkListboxOrientation')

‎tools/public_api_guard/cdk/listbox.md

+10-8
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
2626
deselect(option: CdkOption<T>): void;
2727
deselectValue(value: T): void;
2828
protected readonly destroyed: Subject<void>;
29-
disabled: boolean;
29+
get disabled(): boolean;
30+
set disabled(value: boolean);
3031
protected readonly element: HTMLElement;
31-
get enabledTabIndex(): number | null;
32-
set enabledTabIndex(value: number | null);
32+
get enabledTabIndex(): number | null | undefined;
33+
set enabledTabIndex(value: number | null | undefined);
3334
focus(): void;
3435
protected _getAriaActiveDescendant(): string | null | undefined;
35-
protected _getTabIndex(): number | null;
36+
protected _getTabIndex(): number | null | undefined;
3637
protected _handleFocus(): void;
3738
protected _handleFocusIn(): void;
3839
protected _handleFocusOut(event: FocusEvent): void;
@@ -79,7 +80,8 @@ export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, Con
7980
toggleValue(value: T): void;
8081
protected triggerOption(option: CdkOption<T> | null): void;
8182
protected triggerRange(trigger: CdkOption<T> | null, from: number, to: number, on: boolean): void;
82-
useActiveDescendant: boolean;
83+
get useActiveDescendant(): boolean;
84+
set useActiveDescendant(value: boolean);
8385
get value(): readonly T[];
8486
set value(value: readonly T[]);
8587
readonly valueChange: Subject<ListboxValueChangeEvent<T>>;
@@ -108,11 +110,11 @@ export class CdkOption<T = unknown> implements ListKeyManagerOption, Highlightab
108110
get disabled(): boolean;
109111
set disabled(value: boolean);
110112
readonly element: HTMLElement;
111-
get enabledTabIndex(): number | null;
112-
set enabledTabIndex(value: number | null);
113+
get enabledTabIndex(): number | null | undefined;
114+
set enabledTabIndex(value: number | null | undefined);
113115
focus(): void;
114116
getLabel(): string;
115-
protected _getTabIndex(): number | null;
117+
protected _getTabIndex(): number | null | undefined;
116118
protected _handleFocus(): void;
117119
get id(): string;
118120
set id(value: string);

0 commit comments

Comments
 (0)
Please sign in to comment.