diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.html new file mode 100644 index 000000000000..3664c058e89a --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.ts new file mode 100644 index 000000000000..796e04456b6e --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-active-descendant/cdk-tree-active-descendant-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with active descendant focus. + */ +@Component({ + selector: 'cdk-tree-active-descendant-example', + exportAs: 'cdkTreeActiveDescendantExample', + templateUrl: 'cdk-tree-active-descendant-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeActiveDescendantExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.html new file mode 100644 index 000000000000..eaa03543ae97 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.html @@ -0,0 +1,76 @@ +
    + Wrap + Multi + Disabled + Skip Disabled + Nav Mode + + + Selection Strategy + + Explicit + Follow + + + + + Focus Strategy + + Roving + Active Descendant + + +
    + +
    + Selected Values: {{ selectedValues().join(', ') || 'None' }} +
    + + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } + } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.ts new file mode 100644 index 000000000000..5eb796390743 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-configurable/cdk-tree-configurable-example.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ +import {Component, model} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatSelectModule} from '@angular/material/select'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {NODES, TreeNode} from '../tree-data'; + +/** @title Configurable Tree. */ +@Component({ + selector: 'cdk-tree-configurable-example', + exportAs: 'cdkTreeConfigurableExample', + templateUrl: 'cdk-tree-configurable-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [ + FormsModule, + ReactiveFormsModule, + MatCheckboxModule, + MatFormFieldModule, + MatSelectModule, + NgTemplateOutlet, + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, + ], +}) +export class CdkTreeConfigurableExample { + nodes: TreeNode[] = NODES; + + selectionMode: 'explicit' | 'follow' = 'explicit'; + focusMode: 'roving' | 'activedescendant' = 'roving'; + + multi = new FormControl(false, {nonNullable: true}); + disabled = new FormControl(false, {nonNullable: true}); + wrap = new FormControl(true, {nonNullable: true}); + skipDisabled = new FormControl(true, {nonNullable: true}); + nav = new FormControl(false, {nonNullable: true}); + + selectedValues = model(['package.json']); +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.html new file mode 100644 index 000000000000..9e33a443440a --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.ts new file mode 100644 index 000000000000..76b7e790d5e8 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with focusable disabled items. + */ +@Component({ + selector: 'cdk-tree-disabled-focusable-example', + exportAs: 'cdkTreeDisabledFocusableExample', + templateUrl: 'cdk-tree-disabled-focusable-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeDisabledFocusableExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.html new file mode 100644 index 000000000000..240ffa174316 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.ts new file mode 100644 index 000000000000..47001f183eed --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with skipped disabled items. + */ +@Component({ + selector: 'cdk-tree-disabled-skipped-example', + exportAs: 'cdkTreeDisabledSkippedExample', + templateUrl: 'cdk-tree-disabled-skipped-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeDisabledSkippedExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.html new file mode 100644 index 000000000000..82709b9c04d7 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.ts new file mode 100644 index 000000000000..e913c03084b1 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-disabled/cdk-tree-disabled-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with disabled state. + */ +@Component({ + selector: 'cdk-tree-disabled-example', + exportAs: 'cdkTreeDisabledExample', + templateUrl: 'cdk-tree-disabled-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeDisabledExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.html new file mode 100644 index 000000000000..25a07e19fe0d --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.ts new file mode 100644 index 000000000000..d88e35b1b740 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with multi-selection and follow focus. + */ +@Component({ + selector: 'cdk-tree-multi-select-follow-focus-example', + exportAs: 'cdkTreeMultiSelectFollowFocusExample', + templateUrl: 'cdk-tree-multi-select-follow-focus-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeMultiSelectFollowFocusExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.html new file mode 100644 index 000000000000..410b78b6aa7e --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.html @@ -0,0 +1,36 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } + } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.ts new file mode 100644 index 000000000000..e2c57f7050d0 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-multi-select/cdk-tree-multi-select-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with multi-selection. + */ +@Component({ + selector: 'cdk-tree-multi-select-example', + exportAs: 'cdkTreeMultiSelectExample', + templateUrl: 'cdk-tree-multi-select-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeMultiSelectExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.html new file mode 100644 index 000000000000..ad9e4aae2e6f --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.html @@ -0,0 +1,48 @@ + + + + @for (node of nodes; track node.value) { + + + + {{ node.name }} + + + + @if (node.children) { + + } + } + diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.ts new file mode 100644 index 000000000000..66ff271015ac --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-nav/cdk-tree-nav-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with nav mode. + */ +@Component({ + selector: 'cdk-tree-nav-example', + exportAs: 'cdkTreeNavExample', + templateUrl: 'cdk-tree-nav-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeNavExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.html new file mode 100644 index 000000000000..5397f9620ce7 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.ts new file mode 100644 index 000000000000..b6b1295199df --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with single selection and follow focus. + */ +@Component({ + selector: 'cdk-tree-single-select-follow-focus-example', + exportAs: 'cdkTreeSingleSelectFollowFocusExample', + templateUrl: 'cdk-tree-single-select-follow-focus-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeSingleSelectFollowFocusExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.html new file mode 100644 index 000000000000..0b5c27e9b945 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.html @@ -0,0 +1,35 @@ + + + + @for (node of nodes; track node.value) { +
  • + + + {{ node.name }} + +
  • + + @if (node.children) { + + } } +
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.ts new file mode 100644 index 000000000000..17af4f90c448 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/cdk-tree-single-select/cdk-tree-single-select-example.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {Component} from '@angular/core'; +import {NgTemplateOutlet} from '@angular/common'; +import { + CdkTree, + CdkTreeItem, + CdkTreeItemGroup, + CdkTreeItemGroupContent, +} from '@angular/cdk-experimental/tree'; +import {TreeNode, NODES} from '../tree-data'; + +/** + * @title Tree with single selection. + */ +@Component({ + selector: 'cdk-tree-single-select-example', + exportAs: 'cdkTreeSingleSelectExample', + templateUrl: 'cdk-tree-single-select-example.html', + styleUrl: '../tree-common.css', + standalone: true, + imports: [CdkTree, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent, NgTemplateOutlet], +}) +export class CdkTreeSingleSelectExample { + nodes: TreeNode[] = NODES; +} diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.html b/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.html deleted file mode 100644 index 55c66aad4daf..000000000000 --- a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.html +++ /dev/null @@ -1,93 +0,0 @@ -
    - Wrap - Multi - Disabled - Skip Disabled - Nav Mode - - - Orientation - - Vertical - Horizontal - - - - - Selection Strategy - - Explicit - Follow - - - - - Focus Strategy - - Roving - Active Descendant - - -
    - -
    - Selected Values: {{ selectedValues().join(', ') || 'None' }} -
    - - - - -
  • - - - {{ node.label }} - - - @if (node.children !== undefined && node.children!.length > 0) { -
      - - @for (child of node.children; track child) { - - } - -
    - } -
  • -
    diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.ts b/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.ts deleted file mode 100644 index 4a0923d70f13..000000000000 --- a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.ts +++ /dev/null @@ -1,152 +0,0 @@ -import {Component, model, input} from '@angular/core'; -import {NgTemplateOutlet} from '@angular/common'; -import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {MatCheckboxModule} from '@angular/material/checkbox'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatSelectModule} from '@angular/material/select'; -import {MatIconModule} from '@angular/material/icon'; -import { - CdkTree, - CdkTreeItem, - CdkTreeItemGroup, - CdkTreeItemGroupContent, -} from '@angular/cdk-experimental/tree'; - -interface ExampleNode { - value: string; - label?: string; - disabled?: boolean; - expanded?: boolean; - children?: ExampleNode[]; -} - -@Component({ - selector: 'example-node', - styleUrl: 'cdk-tree-example.css', - template: ` -
  • - - - {{ node().label }} - - - @if (node().children !== undefined && node().children!.length > 0) { -
    - - @for (child of node().children; track child) { - - } - -
    - } -
  • - `, - imports: [MatIconModule, CdkTreeItem, CdkTreeItemGroup, CdkTreeItemGroupContent], -}) -export class ExampleNodeComponent { - node = input.required(); - - parent = input.required | CdkTreeItemGroup>(); -} - -/** @title Tree using CdkTree and CdkTreeItem. */ -@Component({ - selector: 'cdk-tree-example', - exportAs: 'cdkTreeExample', - templateUrl: 'cdk-tree-example.html', - styleUrl: 'cdk-tree-example.css', - imports: [ - FormsModule, - ReactiveFormsModule, - MatCheckboxModule, - MatFormFieldModule, - MatSelectModule, - MatIconModule, - NgTemplateOutlet, - CdkTree, - CdkTreeItem, - CdkTreeItemGroup, - CdkTreeItemGroupContent, - ExampleNodeComponent, - ], -}) -export class CdkTreeExample { - // Tree data - treeData: ExampleNode[] = [ - { - value: 'electronics', - label: 'electronics', - children: [ - { - value: 'audio', - label: 'audio equipment', - children: [ - {value: 'headphones', label: 'headphones'}, - {value: 'speakers', label: 'speakers (disabled)', disabled: true}, - {value: 'amps', label: 'amplifiers'}, - ], - }, - { - value: 'computers', - label: 'computers & tablets', - children: [ - {value: 'laptops', label: 'laptops'}, - {value: 'desktops', label: 'desktops'}, - {value: 'tablets', label: 'tablets'}, - ], - }, - {value: 'cameras', label: 'cameras'}, - ], - }, - { - value: 'furniture', - label: 'furniture', - children: [ - {value: 'tables', label: 'tables'}, - {value: 'chairs', label: 'chairs'}, - {value: 'sofas', label: 'sofas'}, - ], - }, - { - value: 'books', - label: 'books (no children)', - }, - { - value: 'clothing', - label: 'clothing (disabled parent)', - disabled: true, - children: [ - {value: 'shirts', label: 'shirts'}, - {value: 'pants', label: 'pants'}, - ], - }, - ]; - - // TODO(ok7sai): add styling to horizontal tree view. - orientation: 'vertical' | 'horizontal' = 'vertical'; - selectionMode: 'explicit' | 'follow' = 'explicit'; - focusMode: 'roving' | 'activedescendant' = 'roving'; - - multi = new FormControl(false, {nonNullable: true}); - disabled = new FormControl(false, {nonNullable: true}); - wrap = new FormControl(true, {nonNullable: true}); - skipDisabled = new FormControl(true, {nonNullable: true}); - nav = new FormControl(false, {nonNullable: true}); - - selectedValues = model(['books']); -} diff --git a/src/components-examples/cdk-experimental/tree/index.ts b/src/components-examples/cdk-experimental/tree/index.ts index 731d29286979..06ce14065313 100644 --- a/src/components-examples/cdk-experimental/tree/index.ts +++ b/src/components-examples/cdk-experimental/tree/index.ts @@ -1 +1,10 @@ -export {CdkTreeExample} from './cdk-tree/cdk-tree-example'; +export {CdkTreeConfigurableExample} from './cdk-tree-configurable/cdk-tree-configurable-example'; +export {CdkTreeActiveDescendantExample} from './cdk-tree-active-descendant/cdk-tree-active-descendant-example'; +export {CdkTreeDisabledExample} from './cdk-tree-disabled/cdk-tree-disabled-example'; +export {CdkTreeDisabledFocusableExample} from './cdk-tree-disabled-focusable/cdk-tree-disabled-focusable-example'; +export {CdkTreeDisabledSkippedExample} from './cdk-tree-disabled-skipped/cdk-tree-disabled-skipped-example'; +export {CdkTreeMultiSelectExample} from './cdk-tree-multi-select/cdk-tree-multi-select-example'; +export {CdkTreeMultiSelectFollowFocusExample} from './cdk-tree-multi-select-follow-focus/cdk-tree-multi-select-follow-focus-example'; +export {CdkTreeNavExample} from './cdk-tree-nav/cdk-tree-nav-example'; +export {CdkTreeSingleSelectExample} from './cdk-tree-single-select/cdk-tree-single-select-example'; +export {CdkTreeSingleSelectFollowFocusExample} from './cdk-tree-single-select-follow-focus/cdk-tree-single-select-follow-focus-example'; diff --git a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.css b/src/components-examples/cdk-experimental/tree/tree-common.css similarity index 54% rename from src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.css rename to src/components-examples/cdk-experimental/tree/tree-common.css index 8b1e8377f4d0..6f936085d232 100644 --- a/src/components-examples/cdk-experimental/tree/cdk-tree/cdk-tree-example.css +++ b/src/components-examples/cdk-experimental/tree/tree-common.css @@ -20,16 +20,32 @@ .example-tree-item { cursor: pointer; - user-select: none; list-style: none; -} - -.example-tree-item-content { + text-decoration: none; display: flex; align-items: center; - padding: 2px 0; /* Minimal padding for item itself */ + gap: 1rem; + padding: 0.3rem 1rem; +} + +.example-icon { + margin: 0; + width: 24px; +} + +.example-parent-icon { + transition: transform 0.2s ease; +} + +.example-tree-item[aria-expanded='true'] .example-parent-icon { + transform: rotate(90deg); +} + +.example-selected-icon { + visibility: hidden; + margin-left: auto; } -.example-tree-item-icon { - margin-right: 8px; +.example-tree-item[aria-selected='true'] .example-selected-icon { + visibility: visible; } diff --git a/src/components-examples/cdk-experimental/tree/tree-data.ts b/src/components-examples/cdk-experimental/tree/tree-data.ts new file mode 100644 index 000000000000..8436794a43b8 --- /dev/null +++ b/src/components-examples/cdk-experimental/tree/tree-data.ts @@ -0,0 +1,60 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ +export type TreeNode = { + name: string; + value: string; + children?: TreeNode[]; + disabled?: boolean; +}; + +export const NODES: TreeNode[] = [ + { + name: 'public', + value: 'public', + children: [ + {name: 'index.html', value: 'public/index.html'}, + {name: 'favicon.ico', value: 'public/favicon.ico'}, + {name: 'styles.css', value: 'public/styles.css'}, + ], + }, + { + name: 'src', + value: 'src', + children: [ + { + name: 'app', + value: 'src/app', + children: [ + {name: 'app.component.ts', value: 'src/app/app.component.ts'}, + {name: 'app.module.ts', value: 'src/app/app.module.ts', disabled: true}, + {name: 'app.css', value: 'src/app/app.css'}, + ], + }, + { + name: 'assets', + value: 'src/assets', + children: [{name: 'logo.png', value: 'src/assets/logo.png'}], + }, + { + name: 'environments', + value: 'src/environments', + children: [ + {name: 'environment.prod.ts', value: 'src/environments/environment.prod.ts'}, + {name: 'environment.ts', value: 'src/environments/environment.ts'}, + ], + }, + {name: 'main.ts', value: 'src/main.ts'}, + {name: 'polyfills.ts', value: 'src/polyfills.ts'}, + {name: 'styles.css', value: 'src/styles.css', disabled: true}, + {name: 'test.ts', value: 'src/test.ts'}, + ], + }, + {name: 'angular.json', value: 'angular.json'}, + {name: 'package.json', value: 'package.json'}, + {name: 'README.md', value: 'README.md'}, +]; diff --git a/src/dev-app/cdk-experimental-tree/BUILD.bazel b/src/dev-app/cdk-experimental-tree/BUILD.bazel index 9091a214d35a..09b225cf66cd 100644 --- a/src/dev-app/cdk-experimental-tree/BUILD.bazel +++ b/src/dev-app/cdk-experimental-tree/BUILD.bazel @@ -5,6 +5,9 @@ package(default_visibility = ["//visibility:public"]) ng_project( name = "cdk-experimental-tree", srcs = glob(["**/*.ts"]), - assets = ["cdk-tree-demo.html"], + assets = [ + "cdk-tree-demo.html", + "cdk-tree-demo.css", + ], deps = ["//src/components-examples/cdk-experimental/tree"], ) diff --git a/src/dev-app/cdk-experimental-tree/cdk-tree-demo.css b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.css new file mode 100644 index 000000000000..d20f23bdfdd4 --- /dev/null +++ b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.css @@ -0,0 +1,20 @@ +.example-tree-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); + gap: 20px; +} + +.example-tree-container { + width: 500px; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.example-configurable-tree-container { + padding-top: 40px; +} + +h2 { + font-size: 1.1rem; +} diff --git a/src/dev-app/cdk-experimental-tree/cdk-tree-demo.html b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.html index 76cfa8843aef..3d530113dd1f 100644 --- a/src/dev-app/cdk-experimental-tree/cdk-tree-demo.html +++ b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.html @@ -1,4 +1,53 @@
    -

    Tree View using UI Patterns

    - +
    +
    +

    Multi Select with Selection Follows Focus

    + +
    + +
    +

    Single Select with Selection Follows Focus

    + +
    + +
    +

    Multi Select

    + +
    + +
    +

    Single Select

    + +
    + +
    +

    Disabled

    + +
    + +
    +

    Disabled Options are Skipped

    + +
    + +
    +

    Disabled Options are Focusable

    + +
    + +
    +

    Active Descendant

    + +
    + +
    +

    Nav Mode

    + +
    +
    + +
    +

    Configurable

    + +
    diff --git a/src/dev-app/cdk-experimental-tree/cdk-tree-demo.ts b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.ts index c9b973635b0a..635c2c401a3d 100644 --- a/src/dev-app/cdk-experimental-tree/cdk-tree-demo.ts +++ b/src/dev-app/cdk-experimental-tree/cdk-tree-demo.ts @@ -6,12 +6,36 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ChangeDetectionStrategy, Component} from '@angular/core'; -import {CdkTreeExample} from '@angular/components-examples/cdk-experimental/tree'; +import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core'; +import { + CdkTreeConfigurableExample, + CdkTreeActiveDescendantExample, + CdkTreeDisabledExample, + CdkTreeDisabledFocusableExample, + CdkTreeDisabledSkippedExample, + CdkTreeMultiSelectExample, + CdkTreeMultiSelectFollowFocusExample, + CdkTreeNavExample, + CdkTreeSingleSelectExample, + CdkTreeSingleSelectFollowFocusExample, +} from '@angular/components-examples/cdk-experimental/tree'; @Component({ templateUrl: 'cdk-tree-demo.html', - imports: [CdkTreeExample], + imports: [ + CdkTreeConfigurableExample, + CdkTreeActiveDescendantExample, + CdkTreeDisabledExample, + CdkTreeDisabledFocusableExample, + CdkTreeDisabledSkippedExample, + CdkTreeMultiSelectExample, + CdkTreeMultiSelectFollowFocusExample, + CdkTreeNavExample, + CdkTreeSingleSelectExample, + CdkTreeSingleSelectFollowFocusExample, + ], + styleUrl: 'cdk-tree-demo.css', + encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CdkExperimentalTreeDemo {} diff --git a/src/dev-app/common-classes.css b/src/dev-app/common-classes.css index 07a7e4122edc..5036839dc7c1 100644 --- a/src/dev-app/common-classes.css +++ b/src/dev-app/common-classes.css @@ -9,10 +9,11 @@ [aria-disabled='true'] .example-stateful, .example-stateful[aria-disabled='true'] { - color: color-mix(in srgb, var(--mat-sys-on-surface) 38%, transparent); + color: color-mix(in srgb, var(--mat-sys-on-surface) 70%, transparent); } -.example-stateful:focus { +.example-stateful:focus, +[aria-activedescendant]:focus-within .example-stateful.cdk-active { background: color-mix( in srgb, var(--mat-sys-on-surface) calc(var(--mat-sys-focus-state-layer-opacity) * 100%), @@ -35,20 +36,28 @@ background: var(--mat-sys-surface); } +.example-selectable { + color: var(--mat-sys-on-surface); + outline: transparent solid 2px; +} + .example-selectable[aria-selected='true'], .example-selectable[aria-checked='true'], -.example-selectable[aria-current] { +.example-selectable[aria-current], +[aria-activedescendant]:focus-within .example-selectable[aria-selected='true'], +[aria-activedescendant]:focus-within .example-selectable[aria-checked='true'], +[aria-activedescendant]:focus-within .example-selectable[aria-current] { background: color-mix( in srgb, var(--mat-sys-primary) calc(var(--mat-sys-pressed-state-layer-opacity) * 100%), transparent ); + color: var(--mat-sys-primary); } -.example-selectable[aria-selected='true']:focus-within, -.example-selectable[aria-checked='true']:focus-within, -.example-selectable[aria-current]:focus-within { +.example-selectable:focus-within { outline: var(--mat-sys-primary) solid 2px; + border-radius: var(--mat-sys-corner-extra-small); } [aria-disabled='true'] .example-selectable[aria-selected='true'], @@ -57,5 +66,14 @@ .example-selectable[aria-disabled='true'][aria-checked='true'], [aria-disabled='true'] .example-selectable[aria-current], .example-selectable[aria-disabled='true'][aria-current] { + color: color-mix(in srgb, var(--mat-sys-on-surface) 70%, transparent); background: color-mix(in srgb, var(--mat-sys-on-surface) 12%, transparent); } + +.example-selectable[aria-selected='true'] .example-icon { + color: var(--mat-sys-primary); +} + +[aria-disabled='true'] .example-selectable[aria-selected='true'] .example-icon { + color: color-mix(in srgb, var(--mat-sys-on-surface) 70%, transparent); +}