Skip to content

Commit

Permalink
Fixed #1130
Browse files Browse the repository at this point in the history
  • Loading branch information
Çağatay Çivici committed Nov 30, 2016
1 parent 1718fc7 commit ac8bd91
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 37 deletions.
2 changes: 2 additions & 0 deletions components/common/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export interface TreeNode {
leaf?: boolean;
expanded?: boolean;
type?: string;
parent?: TreeNode;
partialSelected?: boolean;
}

export interface Confirmation {
Expand Down
8 changes: 4 additions & 4 deletions components/tree/tree.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@
.ui-tree .ui-tree-toggler {
cursor: pointer;
display: inline-block;
margin-top: 3px;
vertical-align: middle;
}

.ui-tree .ui-treenode-icon {
display: inline-block;
margin-top: 3px;
vertical-align: middle;
}

.ui-tree .ui-treenode-label {
display: inline-block;
margin: 2px 0 0 0;
padding: 0 3px;
vertical-align: middle;
}

.ui-tree .ui-treenode-label.ui-state-hover,
Expand All @@ -72,7 +72,7 @@

.ui-tree .ui-chkbox {
display: inline-block;
zoom: 1;
vertical-align: middle;
}

/** Fluid **/
Expand Down
124 changes: 108 additions & 16 deletions components/tree/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export class TreeNodeTemplateLoader implements OnInit {
<span class="ui-tree-toggler fa fa-fw" [ngClass]="{'fa-caret-right':!node.expanded,'fa-caret-down':node.expanded}" *ngIf="!isLeaf()"
(click)="toggle($event)"></span
><span class="ui-treenode-leaf-icon" *ngIf="isLeaf()"></span
><div class="ui-chkbox" *ngIf="tree.selectionMode == 'checkbox'"><div class="ui-chkbox-box ui-widget ui-corner-all ui-state-default">
<span class="ui-chkbox-icon ui-c fa fa-fw"
[ngClass]="{'fa-check':isSelected(),'fa-minus':node.partialSelected}"></span></div></div
><span [class]="getIcon()" *ngIf="node.icon||node.expandedIcon||node.collapsedIcon"></span
><span class="ui-treenode-label ui-corner-all"
[ngClass]="{'ui-state-hover':hover && tree.selectionMode,'ui-state-highlight':isSelected()}">
Expand All @@ -43,7 +46,7 @@ export class TreeNodeTemplateLoader implements OnInit {
</span>
</div>
<ul class="ui-treenode-children" style="display: none;" *ngIf="node.children && node.expanded" [style.display]="node.expanded ? 'block' : 'none'">
<p-treeNode *ngFor="let childNode of node.children" [node]="childNode"></p-treeNode>
<p-treeNode *ngFor="let childNode of node.children" [node]="childNode" [parentNode]="node"></p-treeNode>
</ul>
</li>
<table *ngIf="tree.horizontal">
Expand Down Expand Up @@ -94,6 +97,8 @@ export class UITreeNode {

@Input() node: TreeNode;

@Input() parentNode: TreeNode;

@Input() root: boolean;

@Input() firstChild: boolean;
Expand All @@ -103,6 +108,10 @@ export class UITreeNode {
hover: boolean = false;

constructor(@Inject(forwardRef(() => Tree)) public tree:Tree) {}

ngOnInit() {
this.node.parent = this.parentNode;
}

getIcon() {
let icon: string;
Expand Down Expand Up @@ -213,28 +222,48 @@ export class Tree implements AfterContentInit {
let index = this.findIndexInSelection(node);
let selected = (index >= 0);

if(selected && metaKey) {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(null);
if(this.isCheckboxSelectionMode()) {
if(selected) {
this.propagateSelectionDown(node, false);
if(node.parent) {
this.propagateSelectionUp(node.parent, false);
}
this.selectionChange.emit(this.selection);
this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
this.selection.splice(index,1);
this.propagateSelectionDown(node, true);
if(node.parent) {
this.propagateSelectionUp(node.parent, true);
}
this.selectionChange.emit(this.selection);
this.onNodeSelect.emit({originalEvent: event, node: node});
}

this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(node);
}
else if(this.isMultipleSelectionMode()) {
this.selection = (!metaKey) ? [] : this.selection||[];
this.selection.push(node);
this.selectionChange.emit(this.selection);
if(selected && metaKey) {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(null);
}
else {
this.selection.splice(index,1);
this.selectionChange.emit(this.selection);
}

this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(node);
}
else if(this.isMultipleSelectionMode()) {
this.selection = (!metaKey) ? [] : this.selection||[];
this.selection.push(node);
this.selectionChange.emit(this.selection);
}

this.onNodeSelect.emit({originalEvent: event, node: node});
this.onNodeSelect.emit({originalEvent: event, node: node});
}
}
}
}
Expand Down Expand Up @@ -270,7 +299,7 @@ export class Tree implements AfterContentInit {
if(this.isSingleSelectionMode()) {
index = (this.selection == node) ? 0 : - 1;
}
else if(this.isMultipleSelectionMode()) {
else {
for(let i = 0; i < this.selection.length; i++) {
if(this.selection[i] == node) {
index = i;
Expand All @@ -283,6 +312,65 @@ export class Tree implements AfterContentInit {
return index;
}

propagateSelectionUp(node: TreeNode, select: boolean) {
if(node.children && node.children.length) {
let selectedCount: number = 0;
let childPartialSelected: boolean = false;
for(let child of node.children) {
if(this.isSelected(child)) {
selectedCount++;
}
else if(child.partialSelected) {
childPartialSelected = true;
}
}

if(select && selectedCount == node.children.length) {
this.selection = this.selection||[];
this.selection.push(node);
node.partialSelected = false;
}
else {
if(!select) {
let index = this.findIndexInSelection(node);
if(index >= 0) {
this.selection.splice(index, 1);
}
}

if(childPartialSelected || selectedCount > 0 && selectedCount != node.children.length)
node.partialSelected = true;
else
node.partialSelected = false;
}
}

let parent = node.parent;
if(parent) {
this.propagateSelectionUp(parent, select);
}
}

propagateSelectionDown(node: TreeNode, select: boolean) {
let index = this.findIndexInSelection(node);

if(select && index == -1) {
this.selection = this.selection||[];
this.selection.push(node);
}
else if(!select && index > -1) {
this.selection.splice(index, 1);
}

node.partialSelected = false;

if(node.children && node.children.length) {
for(let child of node.children) {
this.propagateSelectionDown(child, select);
}
}
}

isSelected(node: TreeNode) {
return this.findIndexInSelection(node) != -1;
}
Expand All @@ -294,6 +382,10 @@ export class Tree implements AfterContentInit {
isMultipleSelectionMode() {
return this.selectionMode && this.selectionMode == 'multiple';
}

isCheckboxSelectionMode() {
return this.selectionMode && this.selectionMode == 'checkbox';
}

expandToNode(node: TreeNode): void {
const pathToNode: TreeNode[] = this.findPathToNode(node);
Expand Down
56 changes: 40 additions & 16 deletions showcase/demo/tree/treedemo.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,28 @@ <h3>Multiple Selection with Metakey</h3>
<p-tree [value]="filesTree3" selectionMode="multiple" [(selection)]="selectedFiles"
(onNodeSelect)="nodeSelect($event)" (onNodeUnselect)="nodeUnselect($event)"></p-tree>
<div>Selected Nodes: <span *ngFor="let file of selectedFiles">{{file.label}} </span></div>

<h3>Multiple Selection with Checkbox</h3>
<p-tree [value]="filesTree4" selectionMode="checkbox" [(selection)]="selectedFiles2"></p-tree>
<div>Selected Nodes: <span *ngFor="let file of selectedFiles2">{{file.label}} </span></div>

<h3>Lazy Loading</h3>
<p-tree [value]="lazyFiles" (onNodeExpand)="nodeExpand($event)" [style]="&#123;'max-height':'200px','overflow':'auto'&#125;"></p-tree>

<h3>Template</h3>
<p-tree [value]="filesTree4">
<p-tree [value]="filesTree5">
<template let-node pTemplate type="default">
<input [(ngModel)]="node.label" type="text" style="width:100%">
</template>
</p-tree>

<h3>Context Menu</h3>
<p-tree [value]="filesTree5" selectionMode="single" [(selection)]="selectedFile2" [contextMenu]="cm"></p-tree>
<p-tree [value]="filesTree6" selectionMode="single" [(selection)]="selectedFile2" [contextMenu]="cm"></p-tree>

<p-contextMenu #cm [model]="items"></p-contextMenu>

<h3>Programatic Tree Expansion</h3>
<p-tree #expandingTree [value]="filesTree6"></p-tree>
<p-tree #expandingTree [value]="filesTree7"></p-tree>

<div style="margin-top: 8px">
<button pButton type="text" label="Expand all" (click)="expandAll()"></button>
Expand All @@ -48,7 +52,7 @@ <h3>Programatic Tree Expansion</h3>
</div>

<h3>Horizontal Tree</h3>
<p-tree [value]="filesTree7" layout="horizontal" selectionMode="single" [(selection)]="selectedFile3" ></p-tree>
<p-tree [value]="filesTree8" layout="horizontal" selectionMode="single" [(selection)]="selectedFile3" ></p-tree>
<div style="margin-top:8px">Selected Node: {{selectedFile3 ? selectedFile3.label : 'none'}}</div>
</div>

Expand All @@ -63,7 +67,7 @@ <h3>Import</h3>
</pre>

<h3>Getting Started</h3>
<p>Tree component requires an array of TreeNode objects as its value. Let's begin with the TreeNode api.</p>
<p>Tree component requires an array of TreeNode objects as its value. Let's begin with the TreeNode api. Note that all of the properties are optional.</p>

<div class="doc-tablewrapper">
<table class="doc-table">
Expand Down Expand Up @@ -143,10 +147,16 @@ <h3>Getting Started</h3>
<td>Type of the node to match template type.</td>
</tr>
<tr>
<td>orientation</td>
<td>parent</td>
<td>TreeNode</td>
<td>null</td>
<td>Parent of the node.</td>
</tr>
<tr>
<td>type</td>
<td>string</td>
<td>vertical</td>
<td>Defines the orientation of the tree, valid values are 'vertical' and 'horizontal'.</td>
<td>null</td>
<td>Type of the node to match template type.</td>
</tr>
</tbody>
</table>
Expand Down Expand Up @@ -252,7 +262,7 @@ <h3>Getting Started</h3>
</pre>

<h3>Selection</h3>
<p>Tree supports two selection methods, single and multiple. Selection is enabled by setting selectionMode property and providing a single TreeNode or
<p>Tree supports 3 selection methods, single, multiple and checkbox. Selection is enabled by setting selectionMode property and providing a single TreeNode or
an array of TreeNodes to reference the selections depending on the selection mode.</p>
<pre>
<code class="language-typescript" pCode>
Expand All @@ -277,7 +287,7 @@ <h3>Selection</h3>
</code>
</pre>

<p>In multiple mode, selection property should be an array.</p>
<p>In multiple mode or checkbox mode, selection property should be an array.</p>
<pre>
<code class="language-typescript" pCode>
export class TreeDemoComponent implements OnInit &#123;
Expand Down Expand Up @@ -448,11 +458,11 @@ <h3>Lazy Loading</h3>
<h3>Horizontal Orientation</h3>
<p>Horizontal mode is the alternative option for orientation.</p>

<pre>
<code class="language-markup" pCode>
&lt;p-tree [value]="files" orientation="horizontal"&gt;&lt;/p-tree&gt;
</code>
</pre>
<pre>
<code class="language-markup" pCode>
&lt;p-tree [value]="files" layout="horizontal"&gt;&lt;/p-tree&gt;
</code>
</pre>

<h3>Attributes</h3>
<div class="doc-tablewrapper">
Expand Down Expand Up @@ -502,6 +512,12 @@ <h3>Attributes</h3>
<td>null</td>
<td>Context menu instance.</td>
</tr>
<tr>
<td>orientation</td>
<td>string</td>
<td>vertical</td>
<td>Defines the orientation of the tree, valid values are 'vertical' and 'horizontal'.</td>
</tr>
</tbody>
</table>
</div>
Expand Down Expand Up @@ -624,6 +640,10 @@ <h3>Dependencies</h3>
(onNodeSelect)="nodeSelect($event)" (onNodeUnselect)="nodeUnselect($event)"&gt;&lt;/p-tree&gt;
&lt;div style="margin-top:8px"Selected Nodes: &lt;span *ngFor="let file of selectedFiles"&gt;{{file.label}} &lt;/span&gt;&lt;/div&gt;

&lt;h3&gt;Multiple Selection with Checkbox&lt;/h3&gt;
&lt;p-tree [value]="filesTree4" selectionMode="checkbox" [(selection)]="selectedFiles2"&gt;&lt;/p-tree&gt;
&lt;div&gt;Selected Nodes: &lt;span *ngFor="let file of selectedFiles2"&gt;&#123;&#123;file.label&#125;&#125; &lt;/span&gt;&lt;/div&gt;

&lt;h3&gt;Lazy Loading&lt;/h3&gt;
&lt;p-tree [value]="lazyFiles" (onNodeExpand)="nodeExpand($event)" [style]="&#123;'max-height':'200px','overflow':'auto'&#125;"&gt;&lt;/p-tree&gt;

Expand Down Expand Up @@ -672,6 +692,7 @@ <h3>Dependencies</h3>
filesTree5: TreeNode[];
filesTree6: TreeNode[];
filesTree7: TreeNode[];
filesTree8: TreeNode[];

lazyFiles: TreeNode[];

Expand All @@ -683,6 +704,8 @@ <h3>Dependencies</h3>

selectedFiles: TreeNode[];

selectedFiles2: TreeNode[];

items: MenuItem[];

constructor(private nodeService: NodeService) &#123; &#125;
Expand All @@ -694,8 +717,9 @@ <h3>Dependencies</h3>
this.nodeService.getFiles().then(files => this.filesTree4 = files);
this.nodeService.getFiles().then(files => this.filesTree5 = files);
this.nodeService.getFiles().then(files => this.filesTree6 = files);
this.nodeService.getFiles().then(files => this.filesTree7 = files);
this.nodeService.getFiles().then(files => &#123;
this.filesTree7 = [&#123;
this.filesTree8 = [&#123;
label: 'Root',
children: files
&#125;];
Expand Down
Loading

0 comments on commit ac8bd91

Please sign in to comment.