Skip to content

Commit e9cc395

Browse files
committed
wip
1 parent 65ec485 commit e9cc395

File tree

5 files changed

+49
-50
lines changed

5 files changed

+49
-50
lines changed

src/cdk/a11y/key-manager/tree-key-manager-strategy.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,6 @@ export interface TreeKeyManagerStrategy<T extends TreeKeyManagerItem> {
100100
/** The currently active item. */
101101
getActiveItem(): T | null;
102102

103-
/**
104-
* Called the first time the Tree component is focused. This method will only be called once over
105-
* the lifetime of the Tree component.
106-
*
107-
* Intended to be used to focus the first item in the tree.
108-
*/
109-
onInitialFocus(): void;
110-
111103
/**
112104
* Focus the provided item by index.
113105
*

src/cdk/a11y/key-manager/tree-key-manager.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
106106

107107
this._setTypeAhead(typeAheadInterval);
108108
}
109+
110+
this._focusInitialNode();
109111
}
110112

111113
/** Stream that emits any time the focused item changes. */
@@ -225,6 +227,8 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
225227
focusItem(item: T, options?: {emitChangeEvent?: boolean}): void;
226228
focusItem(itemOrIndex: number | T, options?: {emitChangeEvent?: boolean}): void;
227229
focusItem(itemOrIndex: number | T, options: {emitChangeEvent?: boolean} = {}) {
230+
console.log('TreeKeyManager', 'focusItem', itemOrIndex);
231+
228232
// Set default options
229233
options.emitChangeEvent ??= true;
230234

@@ -250,6 +254,7 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
250254

251255
if (options.emitChangeEvent) {
252256
// Emit to `change` stream as required by TreeKeyManagerStrategy interface.
257+
console.log('emitting change event');
253258
this.change.next(this._activeItem);
254259
}
255260
this._activeItem?.focus();
@@ -281,9 +286,7 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
281286
this._items.length &&
282287
this._items.some(item => typeof item.getLabel !== 'function')
283288
) {
284-
throw new Error(
285-
'TreeKeyManager items in typeahead mode must implement the `getLabel` method.',
286-
);
289+
console.error('TreeKeyManager items in typeahead mode must implement the `getLabel` method.');
287290
}
288291

289292
// Debounce the presses of non-navigational keys, collect the ones that correspond to letters
@@ -417,6 +420,12 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
417420
private _activateCurrentItem() {
418421
this._activeItem?.activate();
419422
}
423+
424+
private _focusInitialNode() {
425+
console.log('TreeKeyManager', '_focusInitialNode', `this._items.length:${this._items.length}`);
426+
// TODO: handle when all the items are disabled
427+
this._focusFirstItem();
428+
}
420429
}
421430

422431
/** @docs-private */

src/cdk/tree/toggle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ export class CdkTreeNodeToggle<T, K = T> {
4444
? this._tree.toggleDescendants(this._treeNode.data)
4545
: this._tree.toggle(this._treeNode.data);
4646

47-
this._tree._keyManager.focusItem(this._treeNode);
47+
this._tree._keyManager?.focusItem(this._treeNode);
4848
}
4949
}

src/cdk/tree/tree.ts

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ type RenderingData<T> =
110110
'class': 'cdk-tree',
111111
'role': 'tree',
112112
'(keydown)': '_sendKeydownToKeyManager($event)',
113-
'(focus)': '_focusInitialTreeItem()',
114113
},
115114
encapsulation: ViewEncapsulation.None,
116115

@@ -259,22 +258,26 @@ export class CdkTree<T, K = T>
259258
private _keyManagerFactory = inject(TREE_KEY_MANAGER) as TreeKeyManagerFactory<CdkTreeNode<T, K>>;
260259

261260
/** The key manager for this tree. Handles focus and activation based on user keyboard input. */
262-
_keyManager: TreeKeyManagerStrategy<CdkTreeNode<T, K>>;
261+
_keyManager: TreeKeyManagerStrategy<CdkTreeNode<T, K>> | undefined;
263262

264263
constructor(
265264
private _differs: IterableDiffers,
266265
private _changeDetectorRef: ChangeDetectorRef,
267266
private _dir: Directionality,
268267
private _elementRef: ElementRef<HTMLElement>,
269-
) {}
268+
) {
269+
console.log('CdkTree', 'constructor');
270+
}
270271

271272
ngAfterContentInit() {
272-
this._initializeKeyManager();
273+
console.log('CdkTree', 'ngAfterContentInit');
273274
}
274275

275276
ngAfterContentChecked() {
277+
console.log('CdkTree', 'ngAfterContentChecked');
276278
this._updateDefaultNodeDefinition();
277279
this._subscribeToDataChanges();
280+
this._initializeKeyManager();
278281
}
279282

280283
ngOnDestroy() {
@@ -295,6 +298,7 @@ export class CdkTree<T, K = T>
295298
}
296299

297300
ngOnInit() {
301+
console.log('CdkTree', 'ngOnInit');
298302
this._checkTreeControlUsage();
299303
this._initializeDataDiffer();
300304
}
@@ -319,23 +323,6 @@ export class CdkTree<T, K = T>
319323
}
320324
}
321325

322-
/**
323-
* Sets the tabIndex on the host element.
324-
*
325-
* NB: we don't set this as a host binding since children being activated
326-
* (e.g. on user click) doesn't trigger this component's change detection.
327-
*/
328-
_setTabIndex() {
329-
// If the `TreeKeyManager` has no active item, then we know that we need to focus the initial
330-
// item when the tree is focused. We set the tabindex to be `0` so that we can capture
331-
// the focus event and redirect it. Otherwise, we unset it.
332-
if (!this._keyManager.getActiveItem()) {
333-
this._elementRef.nativeElement.setAttribute('tabindex', '0');
334-
} else {
335-
this._elementRef.nativeElement.removeAttribute('tabindex');
336-
}
337-
}
338-
339326
/**
340327
* Switch to the provided data source by resetting the data and unsubscribing from the current
341328
* render change subscription if one exists. If the data source is null, interpret this by
@@ -425,6 +412,9 @@ export class CdkTree<T, K = T>
425412
}
426413

427414
private _renderDataChanges(data: RenderingData<T>) {
415+
console.log(
416+
`renderDataChanges nodeType:${data.nodeType} renderNodes:${data.renderNodes.length}`,
417+
);
428418
if (data.nodeType === null) {
429419
this._renderNodeChanges(data.renderNodes);
430420
return;
@@ -454,6 +444,10 @@ export class CdkTree<T, K = T>
454444
}
455445

456446
private _initializeKeyManager() {
447+
if (this._keyManager) {
448+
return;
449+
}
450+
457451
const items = combineLatest([this._keyManagerNodes, this._nodes]).pipe(
458452
map(([keyManagerNodes, renderNodes]) =>
459453
keyManagerNodes.reduce<CdkTreeNode<T, K>[]>((items, data) => {
@@ -478,14 +472,10 @@ export class CdkTree<T, K = T>
478472
this._keyManager.change
479473
.pipe(startWith(null), pairwise(), takeUntil(this._onDestroy))
480474
.subscribe(([prev, next]) => {
475+
console.log('keyManage change subscription', prev, next);
481476
prev?._setTabUnfocusable();
482477
next?._setTabFocusable();
483478
});
484-
485-
this._keyManager.change.pipe(startWith(null), takeUntil(this._onDestroy)).subscribe(() => {
486-
// refresh the tabindex when the active item changes.
487-
this._setTabIndex();
488-
});
489479
}
490480

491481
private _initializeDataDiffer() {
@@ -861,15 +851,7 @@ export class CdkTree<T, K = T>
861851

862852
/** `keydown` event handler; this just passes the event to the `TreeKeyManager`. */
863853
_sendKeydownToKeyManager(event: KeyboardEvent) {
864-
this._keyManager.onKeydown(event);
865-
}
866-
867-
/** `focus` event handler; this focuses the initial item if there isn't already one available. */
868-
_focusInitialTreeItem() {
869-
if (this._keyManager.getActiveItem()) {
870-
return;
871-
}
872-
this._keyManager.onInitialFocus();
854+
this._keyManager?.onKeydown(event);
873855
}
874856

875857
/** Gets all nested descendants of a given node. */
@@ -1140,7 +1122,7 @@ export class CdkTree<T, K = T>
11401122
'[attr.aria-level]': 'level + 1',
11411123
'[attr.aria-posinset]': '_getPositionInSet()',
11421124
'[attr.aria-setsize]': '_getSetSize()',
1143-
'tabindex': '-1',
1125+
'[tabindex]': '_getTabindex()',
11441126
'role': 'treeitem',
11451127
'(click)': '_focusItem()',
11461128
'(focus)': '_focusItem()',
@@ -1282,6 +1264,17 @@ export class CdkTreeNode<T, K = T> implements OnDestroy, OnInit, TreeKeyManagerI
12821264
return this._tree._getPositionInSet(this._data);
12831265
}
12841266

1267+
protected _getTabindex(): number | null {
1268+
if (!this._tree._keyManager) {
1269+
return null;
1270+
}
1271+
// TODO: determine if this equailty check will work in SSR
1272+
if (this._tree._keyManager.getActiveItem() === this) {
1273+
return 0;
1274+
}
1275+
return -1;
1276+
}
1277+
12851278
constructor(
12861279
protected _elementRef: ElementRef<HTMLElement>,
12871280
protected _tree: CdkTree<T, K>,
@@ -1343,18 +1336,20 @@ export class CdkTreeNode<T, K = T> implements OnDestroy, OnInit, TreeKeyManagerI
13431336
}
13441337

13451338
_setTabFocusable() {
1339+
console.log('_setTabFocusable', this._elementRef.nativeElement.textContent);
13461340
this._elementRef.nativeElement.setAttribute('tabindex', '0');
13471341
}
13481342

13491343
_setTabUnfocusable() {
1344+
console.log('_setTabUnfocusable', this._elementRef.nativeElement.textContent);
13501345
this._elementRef.nativeElement.setAttribute('tabindex', '-1');
13511346
}
13521347

13531348
_focusItem() {
13541349
if (this.isDisabled) {
13551350
return;
13561351
}
1357-
this._tree._keyManager.focusItem(this);
1352+
this._tree._keyManager?.focusItem(this);
13581353
}
13591354

13601355
_emitExpansionState(expanded: boolean) {

src/material/tree/node.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ export class MatTreeNode<T, K = T> extends CdkTreeNode<T, K> implements OnInit,
8686
*/
8787
defaultTabIndex = 0;
8888

89-
protected _getTabindexAttribute() {
89+
protected _getTabindexAttribute(): number | null {
90+
if (!this._tree._keyManager) {
91+
return null;
92+
}
9093
if (isNoopTreeKeyManager(this._tree._keyManager)) {
9194
return this.tabIndex;
9295
}

0 commit comments

Comments
 (0)