Skip to content

Commit c6dedb7

Browse files
committed
feat(cdk-experimental/tree): add nav mode
1 parent 464adf0 commit c6dedb7

File tree

5 files changed

+95
-4
lines changed

5 files changed

+95
-4
lines changed

src/cdk-experimental/tree/tree.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ export class CdkTree<V> {
112112
/** Text direction. */
113113
readonly textDirection = inject(Directionality).valueSignal;
114114

115+
/** Whether the tree is in navigation mode. */
116+
readonly nav = input(false);
117+
118+
/** The aria-current type. */
119+
readonly currentType = input<'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'>(
120+
'page',
121+
);
122+
115123
/** The UI pattern for the tree. */
116124
readonly pattern: TreePattern<V> = new TreePattern<V>({
117125
...this,
@@ -174,6 +182,7 @@ export class CdkTree<V> {
174182
'[id]': 'pattern.id()',
175183
'[attr.aria-expanded]': 'pattern.expandable() ? pattern.expanded() : null',
176184
'[attr.aria-selected]': 'pattern.selected()',
185+
'[attr.aria-current]': 'pattern.current()',
177186
'[attr.aria-disabled]': 'pattern.disabled()',
178187
'[attr.aria-level]': 'pattern.level()',
179188
'[attr.aria-owns]': 'group()?.id',

src/cdk-experimental/ui-patterns/tree/tree.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,20 @@ export class TreeItemPattern<V> implements ExpansionItem {
8787
readonly tabindex = computed(() => this.tree().focusManager.getItemTabindex(this));
8888

8989
/** Whether the item is selected. */
90-
readonly selected = computed(() => this.tree().value().includes(this.value()));
90+
readonly selected = computed(() => {
91+
if (this.tree().nav()) {
92+
return undefined;
93+
}
94+
return this.tree().value().includes(this.value());
95+
});
96+
97+
/** The current type of this item. */
98+
readonly current = computed(() => {
99+
if (!this.tree().nav()) {
100+
return undefined;
101+
}
102+
return this.tree().value().includes(this.value()) ? this.tree().currentType() : undefined;
103+
});
91104

92105
constructor(readonly inputs: TreeItemInputs<V>) {
93106
this.id = inputs.id;
@@ -136,6 +149,12 @@ export interface TreeInputs<V>
136149
> {
137150
/** All items in the tree, in document order (DFS-like, a flattened list). */
138151
allItems: SignalLike<TreeItemPattern<V>[]>;
152+
153+
/** Whether the tree is in navigation mode. */
154+
nav: SignalLike<boolean>;
155+
156+
/** The aria-current type. */
157+
currentType: SignalLike<'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'>;
139158
}
140159

141160
export interface TreePattern<V> extends TreeInputs<V> {}
@@ -337,6 +356,8 @@ export class TreePattern<V> {
337356
});
338357

339358
constructor(readonly inputs: TreeInputs<V>) {
359+
this.nav = inputs.nav;
360+
this.currentType = inputs.currentType;
340361
this.allItems = inputs.allItems;
341362
this.focusMode = inputs.focusMode;
342363
this.disabled = inputs.disabled;
@@ -345,7 +366,7 @@ export class TreePattern<V> {
345366
this.wrap = inputs.wrap;
346367
this.orientation = inputs.orientation;
347368
this.textDirection = inputs.textDirection;
348-
this.multi = inputs.multi;
369+
this.multi = computed(() => (this.nav() ? false : this.inputs.multi()));
349370
this.value = inputs.value;
350371
this.selectionMode = inputs.selectionMode;
351372
this.typeaheadDelay = inputs.typeaheadDelay;

src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@
3737
color: var(--mat-sys-on-surface-variant);
3838
}
3939

40+
.example-tree-item-content[aria-current] {
41+
background-color: var(--mat-sys-inverse-primary);
42+
}
43+
44+
.example-tree-item-content[aria-disabled='true'] {
45+
background-color: var(--mat-sys-surface-container);
46+
color: var(--mat-sys-on-surface-variant);
47+
}
48+
49+
4050
.example-tree-item-content {
4151
display: flex;
4252
align-items: center;

src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<mat-checkbox [formControl]="multi">Multi</mat-checkbox>
44
<mat-checkbox [formControl]="disabled">Disabled</mat-checkbox>
55
<mat-checkbox [formControl]="skipDisabled">Skip Disabled</mat-checkbox>
6+
<mat-checkbox [formControl]="nav">Nav Mode</mat-checkbox>
67

78
<mat-form-field subscriptSizing="dynamic" appearance="outline">
89
<mat-label>Orientation</mat-label>
@@ -43,10 +44,17 @@
4344
[focusMode]="focusMode"
4445
[wrap]="wrap.value"
4546
[skipDisabled]="skipDisabled.value"
47+
[nav]="nav.value"
4648
[(value)]="selectedValues"
4749
#tree="cdkTree"
4850
>
49-
@for (node of treeData; track node) {
50-
<example-node [node]="node" />
51+
@if (nav.value) {
52+
@for (node of treeData; track node) {
53+
<example-nav-node [node]="node" />
54+
}
55+
} @else {
56+
@for (node of treeData; track node) {
57+
<example-node [node]="node" />
58+
}
5159
}
5260
</ul>

src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,47 @@ export class ExampleNodeComponent {
6060
node = input.required<ExampleNode>();
6161
}
6262

63+
@Component({
64+
selector: 'example-nav-node',
65+
styleUrl: 'cdk-tree-example.css',
66+
template: `
67+
<li class="example-tree-item">
68+
<a
69+
cdkTreeItem
70+
class="example-tree-item-content"
71+
[value]="node().value"
72+
[label]="node().label || node().value"
73+
[disabled]="node().disabled"
74+
#treeItem="cdkTreeItem"
75+
[style.paddingLeft.px]="(treeItem.pattern.level() - 1) * 24"
76+
href="#{{node().value}}"
77+
(click)="$event.preventDefault()"
78+
>
79+
<mat-icon class="example-tree-item-icon" aria-hidden="true">
80+
@if (treeItem.pattern.expandable()) {
81+
{{ treeItem.pattern.expanded() ? 'expand_less' : 'expand_more' }}
82+
}
83+
</mat-icon>
84+
{{ node().label }}
85+
</a>
86+
87+
@if (node().children !== undefined && node().children!.length > 0) {
88+
<ul cdkTreeItemGroup [value]="node().value">
89+
<ng-template cdkTreeItemGroupContent>
90+
@for (child of node().children; track child) {
91+
<example-nav-node [node]="child" />
92+
}
93+
</ng-template>
94+
</ul>
95+
}
96+
</li>
97+
`,
98+
imports: [MatIconModule, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent],
99+
})
100+
export class ExampleNavNodeComponent {
101+
node = input.required<ExampleNode>();
102+
}
103+
63104
/** @title Tree using CdkTree and CdkTreeItem. */
64105
@Component({
65106
selector: 'cdk-tree-example',
@@ -75,6 +116,7 @@ export class ExampleNodeComponent {
75116
MatIconModule,
76117
CdkTree,
77118
ExampleNodeComponent,
119+
ExampleNavNodeComponent,
78120
],
79121
})
80122
export class CdkTreeExample {
@@ -138,6 +180,7 @@ export class CdkTreeExample {
138180
disabled = new FormControl(false, {nonNullable: true});
139181
wrap = new FormControl(true, {nonNullable: true});
140182
skipDisabled = new FormControl(true, {nonNullable: true});
183+
nav = new FormControl(false, {nonNullable: true});
141184

142185
selectedValues = model<string[]>(['books']);
143186
}

0 commit comments

Comments
 (0)