Skip to content

Commit d776886

Browse files
author
Georgii Rychko
authored
feat(node-menu): bring custom menu items to the node menu. Closes #48, closes #53, closes #25, closes #161 (#170)
1 parent 68c4dcf commit d776886

17 files changed

+216
-38
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ Here is an example of its usage:
335335
'node': '<i class="fa fa-folder-o fa-lg"></i>',
336336
'leaf': '<i class="fa fa-file-o fa-lg"></i>',
337337
'leftMenu': '<i class="fa fa-navicon fa-lg"></i>'
338+
},
339+
'menuItems': [
340+
{ action: NodeMenuItemAction.Custom, name: 'Foo', cssClass: 'fa fa-arrow-right' },
341+
{ action: NodeMenuItemAction.Custom, name: 'Bar', cssClass: 'fa fa-arrow-right' },
342+
{ action: NodeMenuItemAction.Custom, name: 'Baz', cssClass: 'fa fa-arrow-right'}
343+
]
338344
}
339345
},
340346
children: [
@@ -358,6 +364,7 @@ Here is an example of its usage:
358364
* `node` - String - It specifies a html template which will be included to the left of the node's value.
359365
* `leaf` - String - It specifies a html template which will be included to the left of the leaf's value.
360366
* `leftMenu` - String - It specifies a html template to the right of the node's value. This template becomes clickable and shows a menu on node's click.
367+
* `menuItems` - here you can specify your custom menu items. You should feed an array of NodeMenuItem instances to this setting. Once done - setup a subscription to `MenuItemSelectedEvent`s by listening to `(menuItemSelected)="onMenuItemSelected($event)"` on the tree.
361368

362369
All options that are defined on a `parent` are automatically applied to children. If you want you can override them by `settings` of the child node.
363370

index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import {
99

1010
import { Tree } from './src/tree';
1111

12+
import { NodeMenuItemAction } from './src/menu/menu.events';
13+
import { NodeMenuItem } from './src/menu/node-menu.component';
14+
1215
import {
1316
NodeEvent,
1417
NodeCreatedEvent,
@@ -18,6 +21,7 @@ import {
1821
NodeSelectedEvent,
1922
NodeExpandedEvent,
2023
NodeCollapsedEvent,
24+
MenuItemSelectedEvent,
2125
NodeDestructiveEvent
2226
} from './src/tree.events';
2327

@@ -41,5 +45,9 @@ export {
4145
NodeCollapsedEvent,
4246
NodeDestructiveEvent,
4347
TreeComponent,
44-
TreeModule
48+
TreeModule,
49+
NodeMenuItemAction,
50+
NodeMenuItem,
51+
ChildrenLoadingFunction,
52+
MenuItemSelectedEvent
4553
};

package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@types/jasmine": "2.8.2",
6464
"@types/node": "8.0.53",
6565
"alertifyjs": "1.10.0",
66-
"codelyzer": "3.0.1",
66+
"codelyzer": "4.0.1",
6767
"conventional-changelog": "1.1.7",
6868
"conventional-changelog-cli": "1.3.5",
6969
"conventional-github-releaser": "2.0.0",
@@ -88,13 +88,11 @@
8888
"shelljs": "0.7.8",
8989
"systemjs-builder": "0.16.12",
9090
"ts-node": "3.3.0",
91-
"tslint": "5.4.3",
92-
"tslint-config-valorsoft": "2.0.1",
91+
"tslint": "5.8.0",
92+
"tslint-config-valorsoft": "2.1.1",
9393
"typescript": "2.4.2",
94+
"uuid": "3.1.0",
9495
"webpack": "3.8.1",
9596
"zone.js": "0.8.18"
96-
},
97-
"dependencies": {
98-
"uuid": "3.1.0"
9997
}
10098
}

src/demo/app/app.component.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Component, OnInit, ViewChild } from '@angular/core';
2-
import { NodeEvent, TreeModel, RenamableNode, Ng2TreeSettings } from '../../../index';
2+
import { Ng2TreeSettings, NodeEvent, RenamableNode, TreeModel } from '../../../index';
3+
import { NodeMenuItemAction } from '../../menu/menu.events';
4+
import { MenuItemSelectedEvent } from '../../tree.events';
35

46
declare const alertify: any;
57

@@ -12,6 +14,7 @@ declare const alertify: any;
1214
<div class="tree-content">
1315
<tree #treeFonts
1416
[tree]="fonts"
17+
(menuItemSelected)="onMenuItemSelected($event)"
1518
(nodeRemoved)="onNodeRemoved($event)"
1619
(nodeRenamed)="onNodeRenamed($event)"
1720
(nodeSelected)="onNodeSelected($event)"
@@ -202,8 +205,15 @@ export class AppComponent implements OnInit {
202205
]
203206
},
204207
{
205-
value: 'Sans-serif',
208+
value: 'Sans-serif (Right click me - I have a custom menu)',
206209
id: 11,
210+
settings: {
211+
menuItems: [
212+
{ action: NodeMenuItemAction.Custom, name: 'Foo', cssClass: 'fa fa-arrow-right' },
213+
{ action: NodeMenuItemAction.Custom, name: 'Bar', cssClass: 'fa fa-arrow-right' },
214+
{ action: NodeMenuItemAction.Custom, name: 'Baz', cssClass: 'fa fa-arrow-right'}
215+
]
216+
},
207217
children: [
208218
{value: 'Arial', id: 12},
209219
{value: 'Century Gothic', id: 13},
@@ -567,6 +577,10 @@ export class AppComponent implements OnInit {
567577
AppComponent.logEvent(e, 'Selected');
568578
}
569579

580+
public onMenuItemSelected(e: MenuItemSelectedEvent) {
581+
AppComponent.logEvent(e, `You selected ${e.selectedItem} menu item`);
582+
}
583+
570584
public onNodeExpanded(e: NodeEvent): void {
571585
AppComponent.logEvent(e, 'Expanded');
572586
}

src/menu/menu.events.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ export enum NodeMenuItemAction {
22
NewFolder,
33
NewTag,
44
Rename,
5-
Remove
5+
Remove,
6+
Custom
67
}
78

89
export enum NodeMenuAction {
@@ -16,4 +17,5 @@ export interface NodeMenuEvent {
1617

1718
export interface NodeMenuItemSelectedEvent {
1819
nodeMenuItemAction: NodeMenuItemAction;
20+
nodeMenuItemSelected?: string;
1921
}

src/menu/node-menu.component.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Component, EventEmitter, Output, Inject, OnDestroy, OnInit, ViewChild, Renderer2
2+
Component, EventEmitter, Output, Input, Inject, OnDestroy, OnInit, ViewChild, Renderer2
33
} from '@angular/core';
44
import { NodeMenuService } from './node-menu.service';
55
import { NodeMenuItemSelectedEvent, NodeMenuItemAction, NodeMenuAction } from './menu.events';
@@ -23,6 +23,9 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
2323
@Output()
2424
public menuItemSelected: EventEmitter<NodeMenuItemSelectedEvent> = new EventEmitter<NodeMenuItemSelectedEvent>();
2525

26+
@Input()
27+
public menuItems: NodeMenuItem[];
28+
2629
@ViewChild('menuContainer') public menuContainer: any;
2730

2831
public availableMenuItems: NodeMenuItem[] = [
@@ -55,6 +58,7 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
5558
}
5659

5760
public ngOnInit(): void {
61+
this.availableMenuItems = this.menuItems || this.availableMenuItems;
5862
this.disposersForGlobalListeners.push(this.renderer.listen('document', 'keyup', this.closeMenu.bind(this)));
5963
this.disposersForGlobalListeners.push(this.renderer.listen('document', 'mousedown', this.closeMenu.bind(this)));
6064
}
@@ -65,7 +69,11 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
6569

6670
public onMenuItemSelected(e: MouseEvent, selectedMenuItem: NodeMenuItem): void {
6771
if (isLeftButtonClicked(e)) {
68-
this.menuItemSelected.emit({nodeMenuItemAction: selectedMenuItem.action});
72+
this.menuItemSelected.emit({
73+
nodeMenuItemAction: selectedMenuItem.action,
74+
nodeMenuItemSelected: selectedMenuItem.name
75+
});
76+
6977
this.nodeMenuService.fireMenuEvent(e.target as HTMLElement, NodeMenuAction.Close);
7078
}
7179
}
@@ -84,5 +92,5 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
8492
export interface NodeMenuItem {
8593
name: string;
8694
action: NodeMenuItemAction;
87-
cssClass: string;
95+
cssClass?: string;
8896
}

src/tree-internal.component.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
import { Component, ElementRef, TemplateRef, Inject, Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core';
1+
import {
2+
Component,
3+
ElementRef,
4+
Input,
5+
OnChanges,
6+
OnDestroy,
7+
OnInit,
8+
SimpleChanges,
9+
TemplateRef
10+
} from '@angular/core';
211
import * as TreeTypes from './tree.types';
312
import { Tree } from './tree';
413
import { TreeController } from './tree-controller';
@@ -41,13 +50,19 @@ import { get } from './utils/fn.utils';
4150
4251
<div class="node-left-menu" *ngIf="tree.hasLeftMenu()" (click)="showLeftMenu($event)" [innerHTML]="tree.leftMenuTemplate">
4352
</div>
44-
<node-menu *ngIf="tree.hasLeftMenu() && isLeftMenuVisible"
53+
<node-menu *ngIf="tree.hasLeftMenu() && isLeftMenuVisible && !hasCustomMenu()"
4554
(menuItemSelected)="onMenuItemSelected($event)">
4655
</node-menu>
4756
</div>
4857
49-
<node-menu *ngIf="isRightMenuVisible" (menuItemSelected)="onMenuItemSelected($event)"></node-menu>
58+
<node-menu *ngIf="isRightMenuVisible && !hasCustomMenu()"
59+
(menuItemSelected)="onMenuItemSelected($event)">
60+
</node-menu>
5061
62+
<node-menu *ngIf="hasCustomMenu() && (isRightMenuVisible || isLeftMenuVisible)"
63+
[menuItems]="tree.menuItems"
64+
(menuItemSelected)="onMenuItemSelected($event)">
65+
</node-menu>
5166
<ng-template [ngIf]="tree.isNodeExpanded()">
5267
<tree-internal *ngFor="let child of tree.childrenAsync | async" [tree]="child" [template]="template"></tree-internal>
5368
</ng-template>
@@ -72,9 +87,9 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
7287

7388
private subscriptions: Subscription[] = [];
7489

75-
public constructor(@Inject(NodeMenuService) private nodeMenuService: NodeMenuService,
76-
@Inject(TreeService) public treeService: TreeService,
77-
@Inject(ElementRef) public element: ElementRef) {
90+
public constructor(private nodeMenuService: NodeMenuService,
91+
public treeService: TreeService,
92+
public element: ElementRef) {
7893
}
7994

8095
public ngOnInit(): void {
@@ -84,7 +99,6 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
8499
}
85100

86101
this.settings = this.settings || { rootIsVisible: true };
87-
88102
this.subscriptions.push(this.nodeMenuService.hideMenuStream(this.element)
89103
.subscribe(() => {
90104
this.isRightMenuVisible = false;
@@ -182,6 +196,9 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
182196
case NodeMenuItemAction.Remove:
183197
this.onRemoveSelected();
184198
break;
199+
case NodeMenuItemAction.Custom:
200+
this.treeService.fireMenuItemSelected(this.tree, e.nodeMenuItemSelected);
201+
break;
185202
default:
186203
throw new Error(`Chosen menu item doesn't exist`);
187204
}
@@ -235,4 +252,8 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
235252
public isRootHidden(): boolean {
236253
return this.tree.isRoot() && !this.settings.rootIsVisible;
237254
}
255+
256+
public hasCustomMenu(): boolean {
257+
return this.tree.hasCustomMenu();
258+
}
238259
}

src/tree.component.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from '@angular/core';
55
import { TreeService } from './tree.service';
66
import * as TreeTypes from './tree.types';
7-
import { NodeEvent } from './tree.events';
7+
import { NodeEvent, MenuItemSelectedEvent } from './tree.events';
88
import { Tree } from './tree';
99
import { TreeController } from './tree-controller';
1010
import { Subscription } from 'rxjs/Subscription';
@@ -46,8 +46,11 @@ export class TreeComponent implements OnInit, OnChanges, OnDestroy {
4646
@Output()
4747
public nodeCollapsed: EventEmitter<any> = new EventEmitter();
4848

49-
@Output()
50-
public loadNextLevel: EventEmitter<any> = new EventEmitter();
49+
@Output()
50+
public menuItemSelected: EventEmitter<any> = new EventEmitter();
51+
52+
@Output()
53+
public loadNextLevel: EventEmitter<any> = new EventEmitter();
5154

5255
public tree: Tree;
5356
@ViewChild('rootComponent') public rootComponent;
@@ -96,6 +99,10 @@ public loadNextLevel: EventEmitter<any> = new EventEmitter();
9699
this.nodeCollapsed.emit(e);
97100
}));
98101

102+
this.subscriptions.push(this.treeService.menuItemSelected$.subscribe((e: MenuItemSelectedEvent) => {
103+
this.menuItemSelected.emit(e);
104+
}));
105+
99106
this.subscriptions.push(this.treeService.loadNextLevel$.subscribe((e: NodeEvent) => {
100107
this.loadNextLevel.emit(e);
101108
}));

src/tree.events.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export class NodeCollapsedEvent extends NodeEvent {
5555
}
5656
}
5757

58+
export class MenuItemSelectedEvent extends NodeEvent {
59+
public constructor(node: Tree, public selectedItem: string) {
60+
super(node);
61+
}
62+
}
63+
5864
export class LoadNextLevelEvent extends NodeEvent {
5965
public constructor(node: Tree) {
6066
super(node);

src/tree.service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
NodeRemovedEvent,
77
NodeRenamedEvent,
88
NodeSelectedEvent,
9+
MenuItemSelectedEvent,
910
LoadNextLevelEvent
1011
} from './tree.events';
1112
import { RenamableNode } from './tree.types';
@@ -15,7 +16,7 @@ import { Observable, Subject } from 'rxjs/Rx';
1516
import { ElementRef, Inject, Injectable } from '@angular/core';
1617
import { NodeDraggableService } from './draggable/node-draggable.service';
1718
import { NodeDraggableEvent } from './draggable/draggable.events';
18-
import {isEmpty} from './utils/fn.utils';
19+
import { isEmpty } from './utils/fn.utils';
1920

2021
@Injectable()
2122
export class TreeService {
@@ -26,6 +27,7 @@ export class TreeService {
2627
public nodeSelected$: Subject<NodeSelectedEvent> = new Subject<NodeSelectedEvent>();
2728
public nodeExpanded$: Subject<NodeExpandedEvent> = new Subject<NodeExpandedEvent>();
2829
public nodeCollapsed$: Subject<NodeCollapsedEvent> = new Subject<NodeCollapsedEvent>();
30+
public menuItemSelected$: Subject<MenuItemSelectedEvent> = new Subject<MenuItemSelectedEvent>();
2931
public loadNextLevel$: Subject<LoadNextLevelEvent> = new Subject<LoadNextLevelEvent>();
3032

3133
private controllers: Map<string | number, TreeController> = new Map();
@@ -58,6 +60,10 @@ export class TreeService {
5860
this.nodeMoved$.next(new NodeMovedEvent(tree, parent));
5961
}
6062

63+
public fireMenuItemSelected(tree: Tree, selectedItem: string): void {
64+
this.menuItemSelected$.next(new MenuItemSelectedEvent(tree, selectedItem));
65+
}
66+
6167
public fireNodeSwitchFoldingType(tree: Tree): void {
6268
if (tree.isNodeExpanded()) {
6369
this.fireNodeExpanded(tree);

0 commit comments

Comments
 (0)