-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
665 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/app/core-ui/material/tree-with-search/checklist-database.service.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
|
||
import { ChecklistDatabaseService } from './checklist-database.service'; | ||
|
||
describe('ChecklistDatabaseService', () => { | ||
beforeEach(() => TestBed.configureTestingModule({})); | ||
|
||
it('should be created', () => { | ||
const service: ChecklistDatabaseService = TestBed.get(ChecklistDatabaseService); | ||
expect(service).toBeTruthy(); | ||
}); | ||
}); |
85 changes: 85 additions & 0 deletions
85
src/app/core-ui/material/tree-with-search/checklist-database.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; | ||
import { Category } from 'app/core/market/api/category/category.model'; | ||
import { ItemNode } from 'app/core-ui/material/tree-with-search/model/item-node'; | ||
|
||
@Injectable({ | ||
providedIn: 'root' | ||
}) | ||
export class ChecklistDatabaseService { | ||
|
||
dataChange: BehaviorSubject<ItemNode[]> = new BehaviorSubject<ItemNode[]>([]); | ||
treeData: any; | ||
get data(): ItemNode[] { return this.dataChange.value; } | ||
|
||
constructor() { } | ||
|
||
initialize(TREE_DATA: Category[]) { | ||
|
||
this.treeData = TREE_DATA; | ||
// Build the tree nodes from Json object. The result is a list of `ItemNode` with nested | ||
// file node as children. | ||
const data = this.buildCategoryTree(TREE_DATA, 0); | ||
|
||
// Notify the change. | ||
this.dataChange.next(data); | ||
} | ||
|
||
|
||
buildCategoryTree(categories: Category[], level: number): ItemNode[] { | ||
return categories.reduce<ItemNode[]>((accumulator, key) => { | ||
const value = key.subCategoryList; | ||
const node = new ItemNode(); | ||
node.item = key.name; | ||
node.id = key.id; | ||
|
||
if (value != null && value.length) { | ||
if (Array.isArray(value)) { | ||
node.children = this.buildCategoryTree(value, level + 1); | ||
} | ||
} | ||
|
||
return accumulator.concat(node); | ||
}, []) | ||
} | ||
|
||
/** Add an item to to-do list */ | ||
insertItem(parent: ItemNode, name: string) { | ||
if (parent.children) { | ||
parent.children.push({ item: name } as ItemNode); | ||
this.dataChange.next(this.data); | ||
} | ||
} | ||
|
||
updateItem(node: ItemNode, name: string) { | ||
node.item = name; | ||
this.dataChange.next(this.data); | ||
} | ||
|
||
public filter(filterText: string) { | ||
let filteredTreeData; | ||
if (filterText) { | ||
filteredTreeData = this.treeData.map(d => { | ||
if (d.name.toLocaleLowerCase().indexOf(filterText.toLocaleLowerCase()) > -1) { | ||
return d; | ||
} | ||
d.subCategoryList = d.subCategoryList.filter((sd) => { | ||
return (sd.name.toLocaleLowerCase().indexOf(filterText.toLocaleLowerCase()) > -1); | ||
}) | ||
|
||
return d; | ||
}).filter((d) => (d.subCategoryList.length)); | ||
|
||
} else { | ||
|
||
filteredTreeData = this.treeData; | ||
} | ||
|
||
// Build the tree nodes from Json object. The result is a list of `ItemNode` with nested | ||
// file node as children. | ||
const data = this.buildCategoryTree(filteredTreeData, 0); | ||
// Notify the change. | ||
this.dataChange.next(data); | ||
} | ||
|
||
} |
7 changes: 7 additions & 0 deletions
7
src/app/core-ui/material/tree-with-search/model/item-flat-node.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ItemFlatNode } from './item-flat-node'; | ||
|
||
describe('ItemFlatNode', () => { | ||
it('should create an instance', () => { | ||
expect(new ItemFlatNode()).toBeTruthy(); | ||
}); | ||
}); |
7 changes: 7 additions & 0 deletions
7
src/app/core-ui/material/tree-with-search/model/item-flat-node.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** Flat to-do item node with expandable and level information */ | ||
export class ItemFlatNode { | ||
id: number; | ||
item: string; | ||
level: number; | ||
expandable: boolean; | ||
} |
7 changes: 7 additions & 0 deletions
7
src/app/core-ui/material/tree-with-search/model/item-node.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ItemNode } from './item-node'; | ||
|
||
describe('ItemNode', () => { | ||
it('should create an instance', () => { | ||
expect(new ItemNode()).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export class ItemNode { | ||
children: ItemNode[]; | ||
item: string; | ||
id: number; | ||
} |
17 changes: 17 additions & 0 deletions
17
...search/tree-with-search-single-selection/tree-with-search-single-selection.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> | ||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle class="subcategory"> | ||
<button mat-icon-button disabled></button> | ||
<mat-checkbox class="checklist-leaf-node" [checked]="checklistSelection.isSelected(node)" (change)="todoLeafItemSelectionToggle(node)">{{node.item}}</mat-checkbox> | ||
</mat-tree-node> | ||
|
||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" class="category"> | ||
<button mat-button matTreeNodeToggle [attr.aria-label]="'toggle ' + node.filename" class="full-width text-left"> | ||
<mat-icon class="part-icon" [ngClass]="{ | ||
'part-triangle-down': treeControl.isExpanded(node), | ||
'part-triangle-right': !treeControl.isExpanded(node) | ||
}"> | ||
</mat-icon> | ||
{{node.item}} | ||
</button> | ||
</mat-tree-node> | ||
</mat-tree> |
Empty file.
28 changes: 28 additions & 0 deletions
28
...rch/tree-with-search-single-selection/tree-with-search-single-selection.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { TreeWithSearchSingleSelectionComponent } from './tree-with-search-single-selection.component'; | ||
import { CoreUiModule } from 'app/core-ui/core-ui.module'; | ||
|
||
describe('TreeWithSearchSingleSelectionComponent', () => { | ||
let component: TreeWithSearchSingleSelectionComponent; | ||
let fixture: ComponentFixture<TreeWithSearchSingleSelectionComponent>; | ||
|
||
beforeEach(async(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ | ||
CoreUiModule.forRoot() | ||
] | ||
}) | ||
.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(TreeWithSearchSingleSelectionComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
102 changes: 102 additions & 0 deletions
102
...h-search/tree-with-search-single-selection/tree-with-search-single-selection.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { SelectionModel } from '@angular/cdk/collections'; | ||
import { FlatTreeControl } from '@angular/cdk/tree'; | ||
import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'; | ||
import { MatTreeFlatDataSource, MatTreeFlattener } from '@angular/material/tree'; | ||
import { Log } from 'ng2-logger'; | ||
import { ItemFlatNode } from 'app/core-ui/material/tree-with-search/model/item-flat-node'; | ||
import { ItemNode } from 'app/core-ui/material/tree-with-search/model/item-node'; | ||
import { ChecklistDatabaseService } from 'app/core-ui/material/tree-with-search/checklist-database.service'; | ||
|
||
@Component({ | ||
selector: 'app-tree-with-search-single-selection', | ||
templateUrl: './tree-with-search-single-selection.component.html', | ||
styleUrls: ['./tree-with-search-single-selection.component.scss'] | ||
}) | ||
export class TreeWithSearchSingleSelectionComponent implements OnInit { | ||
|
||
log: any = Log.create('tree-with-search-single-selection'); | ||
@Input() options: any = []; | ||
@Output() onChange: EventEmitter<any> = new EventEmitter<any>(); | ||
|
||
/** Map from flat node to nested node. This helps us finding the nested node to be modified */ | ||
flatNodeMap: any = new Map<ItemFlatNode, ItemNode>(); | ||
|
||
/** Map from nested node to flattened node. This helps us to keep the same object for selection */ | ||
nestedNodeMap: any = new Map<ItemNode, ItemFlatNode>(); | ||
|
||
/** A selected parent node to be inserted */ | ||
selectedParent: ItemFlatNode | null = null; | ||
|
||
/** The new item's name */ | ||
newItemName: string = ''; | ||
|
||
treeControl: FlatTreeControl<ItemFlatNode>; | ||
|
||
treeFlattener: MatTreeFlattener<ItemNode, ItemFlatNode>; | ||
|
||
dataSource: MatTreeFlatDataSource<ItemNode, ItemFlatNode>; | ||
|
||
/** The selection for checklist */ | ||
checklistSelection: any = new SelectionModel<ItemFlatNode>(); | ||
|
||
constructor(private database: ChecklistDatabaseService) { | ||
this.treeFlattener = new MatTreeFlattener(this.transformer, this.getLevel, | ||
this.isExpandable, this.getChildren); | ||
this.treeControl = new FlatTreeControl<ItemFlatNode>(this.getLevel, this.isExpandable); | ||
this.dataSource = new MatTreeFlatDataSource(this.treeControl, this.treeFlattener); | ||
|
||
database.dataChange.subscribe(data => { | ||
this.dataSource.data = data; | ||
}); | ||
|
||
} | ||
|
||
ngOnInit() { | ||
if (this.options) { | ||
this.database.initialize(this.options); | ||
} else { | ||
this.log.d('category options are not available'); | ||
} | ||
} | ||
|
||
getLevel = (node: ItemFlatNode) => node.level; | ||
|
||
isExpandable = (node: ItemFlatNode) => node.expandable; | ||
|
||
getChildren = (node: ItemNode): ItemNode[] => node.children; | ||
|
||
hasChild = (_: number, _nodeData: ItemFlatNode) => _nodeData.expandable; | ||
|
||
hasNoContent = (_: number, _nodeData: ItemFlatNode) => _nodeData.item === ''; | ||
|
||
/** | ||
* Transformer to convert nested node to flat node. Record the nodes in maps for later use. | ||
*/ | ||
transformer = (node: ItemNode, level: number) => { | ||
const existingNode = this.nestedNodeMap.get(node); | ||
const flatNode = existingNode && existingNode.item === node.item | ||
? existingNode | ||
: new ItemFlatNode(); | ||
|
||
flatNode.item = node.item; | ||
flatNode.level = level; | ||
flatNode.id = node.id; | ||
flatNode.expandable = !!node.children; | ||
this.flatNodeMap.set(flatNode, node); | ||
this.nestedNodeMap.set(node, flatNode); | ||
return flatNode; | ||
} | ||
|
||
|
||
/** Toggle a leaf to-do item selection. Check all the parents to see if they changed */ | ||
todoLeafItemSelectionToggle(node: ItemFlatNode): void { | ||
this.checklistSelection.toggle(node); | ||
|
||
if (this.checklistSelection.isSelected(node)) { | ||
this.onChange.emit(node); | ||
} else { | ||
this.onChange.emit(null); | ||
} | ||
} | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
src/app/core-ui/material/tree-with-search/tree-with-search.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<mat-form-field> | ||
<input matInput placeholder="Search" (input)="filterChanged($event.target.value)"> | ||
</mat-form-field> | ||
|
||
|
||
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> | ||
<mat-tree-node *matTreeNodeDef="let node" matTreeNodeToggle matTreeNodePadding class="subcategory"> | ||
<button mat-icon-button disabled></button> | ||
<mat-checkbox class="checklist-leaf-node" | ||
[checked]="checklistSelection.isSelected(node)" | ||
(change)="todoLeafItemSelectionToggle(node)">{{node.item}}</mat-checkbox> | ||
</mat-tree-node> | ||
|
||
<mat-tree-node *matTreeNodeDef="let node; when: hasChild" class="category"> | ||
<button mat-icon-button matTreeNodeToggle | ||
[attr.aria-label]="'toggle ' + node.filename"> | ||
<mat-icon class="part-icon" | ||
[ngClass]="{ | ||
'part-triangle-down': treeControl.isExpanded(node), | ||
'part-triangle-right': !treeControl.isExpanded(node) | ||
}"> | ||
</mat-icon> | ||
</button> | ||
<mat-checkbox [checked]="descendantsAllSelected(node)" | ||
[indeterminate]="descendantsPartiallySelected(node)" | ||
(change)="todoItemSelectionToggle(node)">{{node.item}}</mat-checkbox> | ||
</mat-tree-node> | ||
</mat-tree> |
15 changes: 15 additions & 0 deletions
15
src/app/core-ui/material/tree-with-search/tree-with-search.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; | ||
|
||
body { | ||
font-family: Roboto, Arial, sans-serif; | ||
margin: 0; | ||
} | ||
|
||
.basic-container { | ||
padding: 30px; | ||
} | ||
|
||
.version-info { | ||
font-size: 8pt; | ||
float: right; | ||
} |
Oops, something went wrong.