Initial commit
This commit is contained in:
95
src/app/file-tree/file-tree.component.ts
Normal file
95
src/app/file-tree/file-tree.component.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { DataSource } from "@angular/cdk/collections";
|
||||
import { FlatTreeControl } from "@angular/cdk/tree";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
Signal,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
} from "@angular/core";
|
||||
import {
|
||||
MatTreeFlatDataSource,
|
||||
MatTreeFlattener,
|
||||
MatTreeModule,
|
||||
} from "@angular/material/tree";
|
||||
import { MatIconModule } from "@angular/material/icon";
|
||||
|
||||
export interface FileOrFolder {
|
||||
isDirectory: boolean;
|
||||
name: string;
|
||||
children?: FileOrFolder[];
|
||||
}
|
||||
|
||||
interface FileNode {
|
||||
file: FileOrFolder;
|
||||
expandable: boolean;
|
||||
level: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-file-tree",
|
||||
standalone: true,
|
||||
imports: [CommonModule, MatTreeModule, MatIconModule],
|
||||
template: `<mat-tree [dataSource]="dataSource()" [treeControl]="treeControl">
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node"
|
||||
matTreeNodePadding
|
||||
(click)="fileSelected.emit(node.file)"
|
||||
>
|
||||
<button mat-icon-button disabled></button>
|
||||
{{ node.file.name }}
|
||||
</mat-tree-node>
|
||||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild">
|
||||
<button
|
||||
mat-icon-button
|
||||
matTreeNodePadding
|
||||
[attr.aria-label]="'Toggle ' + node.file.name"
|
||||
>
|
||||
<mat-icon class="mat-icon-rtl-mirror">
|
||||
@if (treeControl.isExpanded(node)) { expand_more } @else {
|
||||
chevron_right }
|
||||
</mat-icon>
|
||||
</button>
|
||||
{{ node.name }}
|
||||
</mat-tree-node>
|
||||
</mat-tree>`,
|
||||
styleUrl: "./file-tree.component.css",
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileTreeComponent {
|
||||
files = input<FileOrFolder[]>([]);
|
||||
|
||||
fileSelected = output<FileOrFolder>();
|
||||
|
||||
protected hasChild = (_: number, node: FileNode) => node.expandable;
|
||||
|
||||
private _transformer = (node: FileOrFolder, level: number) => ({
|
||||
expandable: !!node.children && node.children.length > 0,
|
||||
file: node,
|
||||
level: level,
|
||||
});
|
||||
|
||||
protected treeControl = new FlatTreeControl<FileNode>(
|
||||
(node) => node.level,
|
||||
(node) => node.expandable
|
||||
);
|
||||
|
||||
private treeFlattener = new MatTreeFlattener(
|
||||
this._transformer,
|
||||
(node) => node.level,
|
||||
(node) => node.expandable,
|
||||
(node) => node.children
|
||||
);
|
||||
|
||||
// Use computed signals to create datasource
|
||||
protected dataSource = computed(() => {
|
||||
const dataSource = new MatTreeFlatDataSource(
|
||||
this.treeControl,
|
||||
this.treeFlattener
|
||||
);
|
||||
dataSource.data = this.files();
|
||||
return dataSource;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user