Skip to content

Commit afa5e4d

Browse files
shawonshawon
authored andcommitted
added new folder selection design
1 parent 44afb68 commit afa5e4d

File tree

3 files changed

+266
-0
lines changed

3 files changed

+266
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<mat-form-field class="w-100 p-2">
2+
<mat-label> {{ selectedNodeName || ('Folder not selected' | translate) }}</mat-label>
3+
<mat-select
4+
[attr.id]="id"
5+
[disabled]="disabled"
6+
[value]="selectedNodeId"
7+
[compareWith]="compareById"
8+
panelClass="folder-tree-select-panel"
9+
(openedChange)="onPanelOpened($event)"
10+
[multiple]="false"
11+
>
12+
<mat-select-trigger>
13+
{{ selectedNodeName || ('Folder not selected' | translate) }}
14+
</mat-select-trigger>
15+
16+
<mat-option>
17+
<div class="tree-container" (click)="$event.stopPropagation()">
18+
19+
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl">
20+
21+
<mat-tree-node
22+
*matTreeNodeDef="let node; when: hasChild"
23+
matTreeNodePadding
24+
class="node-row d-flex flex-row justify-content-between align-items-center gap-12"
25+
[class.selected]="node.id === selectedNodeId"
26+
>
27+
28+
<div (click)="treeControl.toggle(node)"
29+
class="node-label d-flex flex-row align-items-center gap-12"
30+
>
31+
<mat-icon>{{ node.id === selectedNodeId ? 'folder' : 'folder_open' }}</mat-icon>
32+
<span class="folder-tree-name">{{ node.name }} <small class="microting-uid">({{ node.microtingUId }})</small></span>
33+
34+
</div>
35+
<button mat-icon-button matTreeNodeToggle>
36+
<mat-icon>
37+
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
38+
</mat-icon>
39+
</button>
40+
</mat-tree-node>
41+
42+
<mat-tree-node
43+
*matTreeNodeDef="let node"
44+
matTreeNodePadding
45+
class="node-row leaf"
46+
[class.selected]="node.id === selectedNodeId"
47+
>
48+
<div
49+
(click)="selectNode(node)"
50+
class="node-label d-flex flex-row align-items-center gap-12"
51+
52+
>
53+
<mat-icon>{{ node.id === selectedNodeId ? 'folder' : 'folder_open' }}</mat-icon>
54+
<span class="folder-tree-name">{{ node.name }} <small class="microting-uid">({{ node.microtingUId }})</small></span>
55+
56+
</div>
57+
58+
</mat-tree-node>
59+
60+
</mat-tree>
61+
62+
</div>
63+
</mat-option>
64+
</mat-select>
65+
</mat-form-field>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
.tree-container {
2+
max-height: 260px;
3+
overflow-y: auto;
4+
padding: 4px 0;
5+
}
6+
7+
.node-row {
8+
display: flex;
9+
align-items: center;
10+
padding-right: 8px!important;
11+
}
12+
13+
.leaf {
14+
padding-left: 40px !important;
15+
}
16+
17+
.node-label {
18+
cursor: pointer;
19+
width: calc(100% - 16px)!important;
20+
display: flex;
21+
height: 40px;
22+
//padding: var(--28-px, 8px) var(--28-px, 8px) var(--28-px, 8px) var(--832-px, 32px) !important;
23+
padding: var(--28-px, 8px)!important;
24+
align-items: center;
25+
gap: var(--156-px, 6px);
26+
align-self: stretch;
27+
background: transparent !important;
28+
}
29+
30+
.leaf {
31+
.node-label {
32+
padding: var(--28-px, 8px) var(--28-px, 8px) var(--28-px, 8px) var(--832-px, 32px) !important;
33+
}
34+
}
35+
36+
.node-row{
37+
&:hover, &:active {
38+
color: var(--primary);
39+
background: var(--primary-light, #F5FCFC) !important;
40+
transition: color .5s;
41+
}
42+
}
43+
44+
.node-row.selected {
45+
color: var(--primary);
46+
background: var(--primary-light, #F5FCFC) !important;
47+
}
48+
49+
50+
.mdc-menu-surface {
51+
&--open {
52+
display: flex;
53+
width: 130px;
54+
padding: var(--28-px, 8px);
55+
flex-direction: column;
56+
align-items: flex-start;
57+
gap: var(--14-px, 4px);
58+
border-radius: var(--rounded-8, 8px);
59+
border: 1px solid var(--border, #EBEFF2);
60+
background: var(--bg, #FFF);
61+
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.10);
62+
}
63+
}
64+
65+
.mat-mdc-option:focus.mdc-list-item, .mat-mdc-option.mat-mdc-option-active.mdc-list-item {
66+
background-color: var(--bg, #FFF);
67+
width: calc(100% - var(--28-px));
68+
}
69+
70+
mat-tree-node.children {
71+
padding-left: 56px !important;
72+
}
73+
74+
.tree-container{
75+
overflow: hidden;
76+
}
77+
78+
mat-tree-node.selected {
79+
color: var(--primary);
80+
background: var(--primary-light, #F5FCFC) !important;
81+
}
82+
83+
84+
85+
::ng-deep .folder-tree-select-panel .mdc-list-item__primary-text {
86+
width: 100% !important;
87+
display: block !important;
88+
padding: 0 !important;
89+
}
90+
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {Component, Input, Output, EventEmitter, OnChanges, ViewChild} from '@angular/core';
2+
import { FlatTreeControl } from '@angular/cdk/tree';
3+
import { MatTreeFlatDataSource, MatTreeFlattener, MatTreeModule } from '@angular/material/tree';
4+
import { MatFormFieldModule } from '@angular/material/form-field';
5+
import {MatSelect, MatSelectModule} from '@angular/material/select';
6+
import { MatIconModule } from '@angular/material/icon';
7+
import { MatButtonModule } from '@angular/material/button';
8+
import { FolderDto } from 'src/app/common/models';
9+
import {TranslateModule} from '@ngx-translate/core';
10+
import {MtxSelect} from '@ng-matero/extensions/select';
11+
import {ReactiveFormsModule} from '@angular/forms';
12+
13+
interface FlatNode {
14+
expandable: boolean;
15+
name: string;
16+
level: number;
17+
id: number;
18+
microtingUId: number;
19+
parentId?: number;
20+
}
21+
22+
@Component({
23+
selector: 'app-folder-tree-select',
24+
standalone: true,
25+
imports: [
26+
MatFormFieldModule,
27+
MatSelectModule,
28+
MatTreeModule,
29+
MatIconModule,
30+
MatButtonModule,
31+
TranslateModule,
32+
ReactiveFormsModule
33+
],
34+
templateUrl: './folder-tree-select.component.html',
35+
styleUrls: ['./folder-tree-select.component.scss']
36+
})
37+
38+
export class FolderTreeSelectComponent implements OnChanges {
39+
@Input() nodes: FolderDto[] = [];
40+
@Input() selectedNodeId: number | null = null;
41+
@Input() disabled = false;
42+
@Input() id: string = '';
43+
44+
@Output() nodeSelected = new EventEmitter<FolderDto>();
45+
46+
@ViewChild(MatSelect) matSelect: MatSelect;
47+
48+
selectedNodeName: string = '';
49+
compareById = (a: number, b: number) => a === b;
50+
51+
private transformer = (node: FolderDto, level: number): FlatNode => ({
52+
expandable: !!node.children?.length,
53+
name: node.name,
54+
id: node.id,
55+
microtingUId: node.microtingUId,
56+
parentId: node.parentId,
57+
level
58+
});
59+
60+
treeControl = new FlatTreeControl<FlatNode>(
61+
node => node.level,
62+
node => node.expandable
63+
);
64+
65+
treeFlattener = new MatTreeFlattener(
66+
this.transformer,
67+
node => node.level,
68+
node => node.expandable,
69+
node => node.children
70+
);
71+
72+
dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener);
73+
74+
ngOnChanges() {
75+
this.dataSource.data = this.nodes;
76+
77+
if (this.selectedNodeId != null) {
78+
const flat = this.flatten(this.nodes);
79+
const selected = flat.find(x => x.id === this.selectedNodeId);
80+
this.selectedNodeId = selected.id;
81+
this.selectedNodeName = selected?.name || '';
82+
}
83+
}
84+
85+
hasChild = (_: number, node: FlatNode) => node.expandable;
86+
87+
selectNode(node: FlatNode) {
88+
const folder = this.flatten(this.nodes).find(x => x.id === node.id);
89+
if (!folder) {return;}
90+
91+
this.selectedNodeId = node.id;
92+
this.selectedNodeName = folder.name;
93+
this.nodeSelected.emit(folder);
94+
this.matSelect.close();
95+
}
96+
97+
98+
onPanelOpened(open: boolean) {
99+
if (open) {
100+
this.treeControl.expandAll();
101+
}
102+
}
103+
104+
flatten(array: FolderDto[]): FolderDto[] {
105+
let result: FolderDto[] = [...array];
106+
array.forEach(x => {
107+
if (x.children) {result = [...result, ...this.flatten(x.children)];}
108+
});
109+
return result;
110+
}
111+
}

0 commit comments

Comments
 (0)