Skip to content

Commit

Permalink
feat(docs): adding checklist tree example (#UIM-391) (#429)
Browse files Browse the repository at this point in the history
8x
  • Loading branch information
Margar1ta authored Mar 25, 2020
1 parent a939bb4 commit bd494a7
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 1 deletion.
8 changes: 7 additions & 1 deletion packages/mosaic-examples/mosaic/tree/module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { McCheckboxModule } from '@ptsecurity/mosaic/checkbox';
import { McHighlightModule } from '@ptsecurity/mosaic/core';
import { McFormFieldModule } from '@ptsecurity/mosaic/form-field';
import { McIconModule } from '@ptsecurity/mosaic/icon';
Expand All @@ -8,13 +9,17 @@ import { McTreeModule } from '@ptsecurity/mosaic/tree';

import { TreeFilteringExample } from './tree-filtering/tree-filtering-example';
import { TreeMultipleCheckboxExample } from './tree-multiple-checkbox/tree-multiple-checkbox-example';
import {
TreeMultipleChecklistExample
} from './tree-multiple-checklist/tree-multiple-checklist-example';
import { TreeMultipleKeyboardExample } from './tree-multiple-keyboard/tree-multiple-keyboard-example';
import { TreeOverviewExample } from './tree-overview/tree-overview-example';


const EXAMPLES = [
TreeOverviewExample,
TreeMultipleCheckboxExample,
TreeMultipleChecklistExample,
TreeMultipleKeyboardExample,
TreeFilteringExample
];
Expand All @@ -26,7 +31,8 @@ const EXAMPLES = [
McInputModule,
McTreeModule,
McIconModule,
McHighlightModule
McHighlightModule,
McCheckboxModule
],
declarations: EXAMPLES,
exports: EXAMPLES
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tree-example-checkbox {
margin-right: 8px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<mc-tree-selection
[(ngModel)]="modelValue"
[dataSource]="dataSource"
[treeControl]="treeControl">

<mc-tree-option *mcTreeNodeDef="let node" mcTreeNodePadding [disabled]="node.name === 'tests'">
<mc-checkbox class= "tree-example-checkbox" [disabled]="node.name === 'tests'"
[checked]="checklistSelection.isSelected(node)"
(change)="fileSelectionToggle(node)"></mc-checkbox>
<span [innerHTML]="treeControl.getViewValue(node)"></span>
</mc-tree-option>

<mc-tree-option *mcTreeNodeDef="let node; when: hasChild" mcTreeNodePadding>
<mc-tree-node-toggle [node]="node"></mc-tree-node-toggle>
<mc-checkbox class= "tree-example-checkbox"
[checked]="descendantsAllSelected(node)"
[indeterminate]="descendantsPartiallySelected(node)"
(change)="fileSelectionToggle(node)"></mc-checkbox>
<span [innerHTML]="treeControl.getViewValue(node)"></span>
</mc-tree-option>
</mc-tree-selection>
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/* tslint:disable:no-reserved-keywords object-literal-key-quotes */
import { SelectionModel } from '@angular/cdk/collections';
import { Component } from '@angular/core';
import { FlatTreeControl } from '@ptsecurity/cdk/tree';
import { McTreeFlatDataSource, McTreeFlattener } from '@ptsecurity/mosaic/tree';


export class FileNode {
children: FileNode[];
name: string;
type: any;
}

/** Flat node with expandable and level information */
export class FileFlatNode {
name: string;
type: any;
level: number;
expandable: boolean;
parent: any;
}

/**
* Build the file structure tree. The `value` is the Json object, or a sub-tree of a Json object.
* The return value is the list of `FileNode`.
*/
export function buildFileTree(value: any, level: number): FileNode[] {
const data: any[] = [];

for (const k of Object.keys(value)) {
const v = value[k];
const node = new FileNode();

node.name = `${k}`;
if (v === null || v === undefined) {
// no action
} else if (typeof v === 'object') {
node.children = buildFileTree(v, level + 1);
} else {
node.type = v;
}

data.push(node);
}

return data;
}

export const DATA_OBJECT = {
docs: 'app',
src: {
cdk: {
a11ly: {
'aria-describer': {
'aria-describer': 'ts',
'aria-describer.spec': 'ts',
'aria-reference': 'ts',
'aria-reference.spec': 'ts'
},
'focus-monitor': {
'focus-monitor': 'ts',
'focus-monitor.spec': 'ts'
}
}
},
documentation: {
source: '',
tools: ''
},
mosaic: {
autocomplete: '',
button: '',
'button-toggle': '',
index: 'ts',
package: 'json',
version: 'ts'
},
'mosaic-dev': {
alert: '',
badge: ''
},
'mosaic-examples': '',
'mosaic-moment-adapter': '',
README: 'md',
'tsconfig.build': 'json',
wallabyTest: 'ts'
},
scripts: {
deploy: {
'cleanup-preview': 'ts',
'publish-artifacts': 'sh',
'publish-docs': 'sh',
'publish-docs-preview': 'ts'
},
'tsconfig.deploy': 'json'
},
tests: ''
};

/**
* @title Checklist tree
*/
@Component({
selector: 'tree-multiple-checklist-example',
templateUrl: 'tree-multiple-checklist-example.html',
styleUrls: ['tree-multiple-checklist-example.css']
})
export class TreeMultipleChecklistExample {
treeControl: FlatTreeControl<FileFlatNode>;
treeFlattener: McTreeFlattener<FileNode, FileFlatNode>;

dataSource: McTreeFlatDataSource<FileNode, FileFlatNode>;

modelValue: any = [];

/** The selection for checklist */
checklistSelection = new SelectionModel<FileFlatNode>(true /* multiple */);

constructor() {
this.treeFlattener = new McTreeFlattener(
this.transformer, this.getLevel, this.isExpandable, this.getChildren
);

this.treeControl = new FlatTreeControl<FileFlatNode>(
this.getLevel, this.isExpandable, this.getValue, this.getViewValue
);
this.dataSource = new McTreeFlatDataSource(this.treeControl, this.treeFlattener);

this.dataSource.data = buildFileTree(DATA_OBJECT, 0);
}

hasChild(_: number, nodeData: FileFlatNode) { return nodeData.expandable; }

/** Whether all the descendants of the node are selected. */
descendantsAllSelected(node: FileFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);

return descendants.every((child) => this.checklistSelection.isSelected(child));
}

/** Whether part of the descendants are selected */
descendantsPartiallySelected(node: FileFlatNode): boolean {
const descendants = this.treeControl.getDescendants(node);
const result = descendants.some((child) => this.checklistSelection.isSelected(child));

return result && !this.descendantsAllSelected(node);
}

/** Toggle the to-do item selection. Select/deselect all the descendants node */
fileSelectionToggle(node: FileFlatNode): void {
this.checklistSelection.toggle(node);
const descendants = this.treeControl.getDescendants(node);
this.checklistSelection.isSelected(node)
? this.checklistSelection.select(...descendants)
: this.checklistSelection.deselect(...descendants);

// Force update for the parent
descendants.every((child) =>
this.checklistSelection.isSelected(child)
);
this.checkAllParentsSelection(node);
}

/* Checks all the parents when a leaf node is selected/unselected */
checkAllParentsSelection(node: FileFlatNode): void {
let parent: FileFlatNode | null = this.getParentNode(node);
while (parent) {
this.checkRootNodeSelection(parent);
parent = this.getParentNode(parent);
}
}

/** Check root node checked state and change it accordingly */
checkRootNodeSelection(node: FileFlatNode): void {
const nodeSelected = this.checklistSelection.isSelected(node);
const descendants = this.treeControl.getDescendants(node);
const descAllSelected = descendants.every((child) =>
this.checklistSelection.isSelected(child)
);
if (nodeSelected && !descAllSelected) {
this.checklistSelection.deselect(node);
} else if (!nodeSelected && descAllSelected) {
this.checklistSelection.select(node);
}
}

/* Get the parent node of a node */
getParentNode(node: FileFlatNode): FileFlatNode | null {
const currentLevel = this.getLevel(node);

if (currentLevel < 1) {
return null;
}

const startIndex = this.treeControl.dataNodes.indexOf(node) - 1;

for (let i = startIndex; i >= 0; i--) {
const currentNode = this.treeControl.dataNodes[i];

if (this.getLevel(currentNode) < currentLevel) {
return currentNode;
}
}

return null;
}

private transformer = (node: FileNode, level: number, parent: any) => {
const flatNode = new FileFlatNode();

flatNode.name = node.name;
flatNode.parent = parent;
flatNode.type = node.type;
flatNode.level = level;
flatNode.expandable = !!node.children;

return flatNode;
}

private getLevel = (node: FileFlatNode) => {
return node.level;
}

private isExpandable = (node: FileFlatNode) => {
return node.expandable;
}

private getChildren = (node: FileNode): FileNode[] => {
return node.children;
}

private getValue = (node: FileNode): string => {
return node.name;
}

private getViewValue = (node: FileNode): string => {
const nodeType = node.type ? `.${node.type}` : '';

return `${node.name}${nodeType}`;
}
}
3 changes: 3 additions & 0 deletions packages/mosaic/tree/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
### Multiple mode with checkboxes
<!-- example(tree-multiple-checkbox) -->

### Multiple mode with checkboxes with child selection
<!-- example(tree-multiple-checklist) -->

### Multiple mode without checkboxes
<!-- example(tree-multiple-keyboard) -->

Expand Down

0 comments on commit bd494a7

Please sign in to comment.