Skip to content

Commit 6cba1cc

Browse files
author
Georgii Rychko
authored
Merge pull request #60 from IndustriaTech/feature/settings-menu
Right menu and left menu
2 parents 4172c93 + 3fb649b commit 6cba1cc

12 files changed

Lines changed: 1425 additions & 344 deletions

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ Here is an example of its usage:
265265
{
266266
value: 'Prototype-based programming',
267267
settings: {
268-
'static': true
268+
'static': true,
269+
'rightMenu': true,
270+
'leftMenu': true
269271
},
270272
children: [
271273
{value: 'JavaScript'},
@@ -275,8 +277,11 @@ Here is an example of its usage:
275277
}
276278
```
277279

278-
Right now only one option is supported - `static`. This option makes it impossible to drag a tree or modify it in a some way, though you still can select nodes in the static tree and appropriate events will be generated.
279-
`static` option that's defined on a `parent` is automatically applied to its children. If you don't want to make `static` all the children, then you can override `settings` of the child node.
280+
* `static` - This option makes it impossible to drag a tree or modify it in a some way, though you still can select nodes in the static tree and appropriate events will be generated.
281+
* `rightMenu` - This option allows you to activate (true, by default) or deactivate (false) right menu when clicking with right button of a mouse.
282+
* `leftMenu` - This option allows you to activate (true) or deactivate (false, by default) left menu.
283+
284+
All options that's defined on a `parent` are automatically applied to children. If you want you can override them by `settings` of the child node.
280285

281286
### [settings]
282287

demo/app.component.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ declare const alertify: any;
3333
(nodeCreated)="onNodeCreated($event)">
3434
</tree>
3535
</div>
36+
<div class="tree-container">
37+
<p class="tree-title">Directory/File structure</p>
38+
<p class="notice">this tree has advanced configurations</p>
39+
<tree
40+
[tree]="dfs"
41+
(nodeRemoved)="onNodeRemoved($event)"
42+
(nodeRenamed)="onNodeRenamed($event)"
43+
(nodeSelected)="onNodeSelected($event)"
44+
(nodeMoved)="onNodeMoved($event)"
45+
(nodeCreated)="onNodeCreated($event)">
46+
</tree>
47+
</div>
3648
</div>
3749
`,
3850
styles: [`
@@ -121,6 +133,23 @@ export class AppComponent implements OnInit {
121133

122134
public pls: TreeModel;
123135

136+
public dfs: TreeModel = {
137+
value: '/',
138+
settings: {
139+
leftMenu: true,
140+
rightMenu: false
141+
},
142+
children: [
143+
{
144+
value: 'bin',
145+
children: [
146+
{value: 'bash'},
147+
{value: 'umount'}
148+
]
149+
}
150+
]
151+
};
152+
124153
public ngOnInit(): void {
125154
setTimeout(() => {
126155
this.pls = {

src/menu/node-menu.component.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, EventEmitter, Output, Renderer, Inject, OnDestroy, OnInit } from '@angular/core';
1+
import { Component, EventEmitter, Output, Renderer, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
22
import { NodeMenuService } from './node-menu.service';
33
import { NodeMenuItemSelectedEvent, NodeMenuItemAction, NodeMenuAction } from './menu.events';
44
import { isLeftButtonClicked, isEscapePressed } from '../utils/event.utils';
@@ -7,9 +7,9 @@ import { isLeftButtonClicked, isEscapePressed } from '../utils/event.utils';
77
selector: 'node-menu',
88
template: `
99
<div class="node-menu">
10-
<ul class="node-menu-content">
10+
<ul class="node-menu-content" #menuContainer>
1111
<li class="node-menu-item" *ngFor="let menuItem of availableMenuItems"
12-
(click)="onMenuItemSelected($event, menuItem)">
12+
(click)="onMenuItemSelected($event, menuItem)">
1313
<div class="node-menu-item-icon {{menuItem.cssClass}}"></div>
1414
<span class="node-menu-item-value">{{menuItem.name}}</span>
1515
</li>
@@ -21,6 +21,8 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
2121
@Output()
2222
public menuItemSelected: EventEmitter<NodeMenuItemSelectedEvent> = new EventEmitter<NodeMenuItemSelectedEvent>();
2323

24+
@ViewChild('menuContainer') public menuContainer: any;
25+
2426
public availableMenuItems: NodeMenuItem[] = [
2527
{
2628
name: 'New tag',
@@ -52,7 +54,7 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
5254

5355
public ngOnInit(): void {
5456
this.disposersForGlobalListeners.push(this.renderer.listenGlobal('document', 'keyup', this.closeMenu.bind(this)));
55-
this.disposersForGlobalListeners.push(this.renderer.listenGlobal('document', 'click', this.closeMenu.bind(this)));
57+
this.disposersForGlobalListeners.push(this.renderer.listenGlobal('document', 'mousedown', this.closeMenu.bind(this)));
5658
}
5759

5860
public ngOnDestroy(): void {
@@ -62,12 +64,16 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
6264
public onMenuItemSelected(e: MouseEvent, selectedMenuItem: NodeMenuItem): void {
6365
if (isLeftButtonClicked(e)) {
6466
this.menuItemSelected.emit({nodeMenuItemAction: selectedMenuItem.action});
67+
this.nodeMenuService.fireMenuEvent(e.target as HTMLElement, NodeMenuAction.Close);
6568
}
6669
}
6770

6871
private closeMenu(e: MouseEvent | KeyboardEvent): void {
6972
const mouseClicked = e instanceof MouseEvent;
70-
if (mouseClicked || isEscapePressed(e as KeyboardEvent)) {
73+
// Check if the click is fired on an element inside a menu
74+
const containingTarget = (this.menuContainer.nativeElement !== e.target && this.menuContainer.nativeElement.contains(e.target));
75+
76+
if (mouseClicked && !containingTarget || isEscapePressed(e as KeyboardEvent)) {
7177
this.nodeMenuService.fireMenuEvent(e.target as HTMLElement, NodeMenuAction.Close);
7278
}
7379
}

src/styles.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ tree-internal .tree .node-value:hover:after {
103103
width: 100%;
104104
}
105105

106+
tree-internal .tree .node-left-menu {
107+
display: inline-block;
108+
height: 100%;
109+
width: auto;
110+
}
111+
112+
tree-internal .tree .node-left-menu:before {
113+
content: '\2026';
114+
color: #757575;
115+
}
116+
106117
tree-internal .tree .node-selected:after {
107118
width: 100%;
108119
}

src/tree-internal.component.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { Observable } from 'rxjs';
1616
<li>
1717
<div class="value-container"
1818
[ngClass]="{rootless: isRootHidden()}"
19-
(contextmenu)="showMenu($event)"
19+
(contextmenu)="showRightMenu($event)"
2020
[nodeDraggable]="element"
2121
[tree]="tree">
2222
@@ -32,9 +32,16 @@ import { Observable } from 'rxjs';
3232
*ngIf="shouldShowInputForTreeValue()"
3333
[nodeEditable]="tree.value"
3434
(valueChanged)="applyNewValue($event)"/>
35+
36+
<div class="node-left-menu" #leftMenuButton
37+
*ngIf="tree.hasLeftMenu()" (click)="showLeftMenu($event)">
38+
</div>
39+
<node-menu *ngIf="tree.hasLeftMenu() && isLeftMenuVisible"
40+
(menuItemSelected)="onMenuItemSelected($event)">
41+
</node-menu>
3542
</div>
3643
37-
<node-menu *ngIf="isMenuVisible" (menuItemSelected)="onMenuItemSelected($event)"></node-menu>
44+
<node-menu *ngIf="isRightMenuVisible" (menuItemSelected)="onMenuItemSelected($event)"></node-menu>
3845
3946
<template [ngIf]="tree.isNodeExpanded()">
4047
<tree-internal *ngFor="let child of tree.childrenAsync | async" [tree]="child"></tree-internal>
@@ -51,7 +58,8 @@ export class TreeInternalComponent implements OnInit {
5158
public settings: Ng2TreeSettings;
5259

5360
public isSelected: boolean = false;
54-
public isMenuVisible: boolean = false;
61+
public isRightMenuVisible: boolean = false;
62+
public isLeftMenuVisible: boolean = false;
5563

5664
public constructor(@Inject(NodeMenuService) private nodeMenuService: NodeMenuService,
5765
@Inject(TreeService) private treeService: TreeService,
@@ -62,7 +70,10 @@ export class TreeInternalComponent implements OnInit {
6270
this.settings = this.settings || { rootIsVisible: true };
6371

6472
this.nodeMenuService.hideMenuStream(this.element)
65-
.subscribe(() => this.isMenuVisible = false);
73+
.subscribe(() => {
74+
this.isRightMenuVisible = false;
75+
this.isLeftMenuVisible = false;
76+
});
6677

6778
this.treeService.unselectStream(this.tree)
6879
.subscribe(() => this.isSelected = false);
@@ -103,18 +114,32 @@ export class TreeInternalComponent implements OnInit {
103114
}
104115
}
105116

106-
public showMenu(e: MouseEvent): void {
107-
if (this.tree.isStatic()) {
117+
public showRightMenu(e: MouseEvent): void {
118+
if (!this.tree.hasRightMenu()) {
108119
return;
109120
}
110121

111122
if (EventUtils.isRightButtonClicked(e)) {
112-
this.isMenuVisible = !this.isMenuVisible;
123+
this.isRightMenuVisible = !this.isRightMenuVisible;
113124
this.nodeMenuService.hideMenuForAllNodesExcept(this.element);
114125
}
115126
e.preventDefault();
116127
}
117128

129+
public showLeftMenu(e: MouseEvent): void {
130+
if (!this.tree.hasLeftMenu()) {
131+
return;
132+
}
133+
134+
if (EventUtils.isLeftButtonClicked(e)) {
135+
this.isLeftMenuVisible = !this.isLeftMenuVisible;
136+
this.nodeMenuService.hideMenuForAllNodesExcept(this.element);
137+
if (this.isLeftMenuVisible) {
138+
e.preventDefault();
139+
}
140+
}
141+
}
142+
118143
public onMenuItemSelected(e: NodeMenuItemSelectedEvent): void {
119144
switch (e.nodeMenuItemAction) {
120145
case NodeMenuItemAction.NewTag:
@@ -136,12 +161,14 @@ export class TreeInternalComponent implements OnInit {
136161

137162
private onNewSelected(e: NodeMenuItemSelectedEvent): void {
138163
this.tree.createNode(e.nodeMenuItemAction === NodeMenuItemAction.NewFolder);
139-
this.isMenuVisible = false;
164+
this.isRightMenuVisible = false;
165+
this.isLeftMenuVisible = false;
140166
}
141167

142168
private onRenameSelected(): void {
143169
this.tree.markAsBeingRenamed();
144-
this.isMenuVisible = false;
170+
this.isRightMenuVisible = false;
171+
this.isLeftMenuVisible = false;
145172
}
146173

147174
private onRemoveSelected(): void {

src/tree.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,22 @@ export class Tree {
204204
return _.get(this.node.settings, 'static', false);
205205
}
206206

207+
/**
208+
* Check whether or not this tree has a left menu.
209+
* @returns {boolean} A flag indicating whether or not this has a left menu.
210+
*/
211+
public hasLeftMenu(): boolean {
212+
return !_.get(this.node.settings, 'static', false) && _.get(this.node.settings, 'leftMenu', false);
213+
}
214+
215+
/**
216+
* Check whether or not this tree has a right menu.
217+
* @returns {boolean} A flag indicating whether or not this has a right menu.
218+
*/
219+
public hasRightMenu(): boolean {
220+
return !_.get(this.node.settings, 'static', false) && _.get(this.node.settings, 'rightMenu', false);
221+
}
222+
207223
/**
208224
* Check whether this tree is "Leaf" or not.
209225
* @returns {boolean} A flag indicating whether or not this tree is a "Leaf".

src/tree.types.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ export interface TreeModel {
2525
}
2626

2727
export class TreeModelSettings {
28+
/**
29+
* "leftMenu" property when set to true makes left menu available.
30+
* @name TreeModelSettings#leftMenu
31+
* @type boolean
32+
* @default false
33+
*/
34+
public leftMenu?: boolean;
35+
36+
/**
37+
* "rightMenu" property when set to true makes right menu available.
38+
* @name TreeModelSettings#rightMenu
39+
* @type boolean
40+
* @default true
41+
*/
42+
public rightMenu?: boolean;
43+
2844
/**
2945
* "static" property when set to true makes it impossible to drag'n'drop tree or call a menu on it.
3046
* @name TreeModelSettings#static
@@ -34,7 +50,7 @@ export class TreeModelSettings {
3450
public static?: boolean;
3551

3652
public static merge(sourceA: TreeModel, sourceB: TreeModel): TreeModelSettings {
37-
return _.defaults({}, _.get(sourceA, 'settings'), _.get(sourceB, 'settings'), {static: false});
53+
return _.defaults({}, _.get(sourceA, 'settings'), _.get(sourceB, 'settings'), {static: false, leftMenu: false, rightMenu: true});
3854
}
3955
}
4056

0 commit comments

Comments
 (0)