Skip to content

Commit

Permalink
feat(node-menu): bring custom menu items to the node menu. Closes #48,
Browse files Browse the repository at this point in the history
…closes #53, closes #25, closes #161 (#170)
  • Loading branch information
Georgii Rychko authored Nov 19, 2017
1 parent 68c4dcf commit d776886
Show file tree
Hide file tree
Showing 17 changed files with 216 additions and 38 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ Here is an example of its usage:
'node': '<i class="fa fa-folder-o fa-lg"></i>',
'leaf': '<i class="fa fa-file-o fa-lg"></i>',
'leftMenu': '<i class="fa fa-navicon fa-lg"></i>'
},
'menuItems': [
{ action: NodeMenuItemAction.Custom, name: 'Foo', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Bar', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Baz', cssClass: 'fa fa-arrow-right'}
]
}
},
children: [
Expand All @@ -358,6 +364,7 @@ Here is an example of its usage:
* `node` - String - It specifies a html template which will be included to the left of the node's value.
* `leaf` - String - It specifies a html template which will be included to the left of the leaf's value.
* `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.
* `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.

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.

Expand Down
10 changes: 9 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {

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

import { NodeMenuItemAction } from './src/menu/menu.events';
import { NodeMenuItem } from './src/menu/node-menu.component';

import {
NodeEvent,
NodeCreatedEvent,
Expand All @@ -18,6 +21,7 @@ import {
NodeSelectedEvent,
NodeExpandedEvent,
NodeCollapsedEvent,
MenuItemSelectedEvent,
NodeDestructiveEvent
} from './src/tree.events';

Expand All @@ -41,5 +45,9 @@ export {
NodeCollapsedEvent,
NodeDestructiveEvent,
TreeComponent,
TreeModule
TreeModule,
NodeMenuItemAction,
NodeMenuItem,
ChildrenLoadingFunction,
MenuItemSelectedEvent
};
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@types/jasmine": "2.8.2",
"@types/node": "8.0.53",
"alertifyjs": "1.10.0",
"codelyzer": "3.0.1",
"codelyzer": "4.0.1",
"conventional-changelog": "1.1.7",
"conventional-changelog-cli": "1.3.5",
"conventional-github-releaser": "2.0.0",
Expand All @@ -88,13 +88,11 @@
"shelljs": "0.7.8",
"systemjs-builder": "0.16.12",
"ts-node": "3.3.0",
"tslint": "5.4.3",
"tslint-config-valorsoft": "2.0.1",
"tslint": "5.8.0",
"tslint-config-valorsoft": "2.1.1",
"typescript": "2.4.2",
"uuid": "3.1.0",
"webpack": "3.8.1",
"zone.js": "0.8.18"
},
"dependencies": {
"uuid": "3.1.0"
}
}
18 changes: 16 additions & 2 deletions src/demo/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { NodeEvent, TreeModel, RenamableNode, Ng2TreeSettings } from '../../../index';
import { Ng2TreeSettings, NodeEvent, RenamableNode, TreeModel } from '../../../index';
import { NodeMenuItemAction } from '../../menu/menu.events';
import { MenuItemSelectedEvent } from '../../tree.events';

declare const alertify: any;

Expand All @@ -12,6 +14,7 @@ declare const alertify: any;
<div class="tree-content">
<tree #treeFonts
[tree]="fonts"
(menuItemSelected)="onMenuItemSelected($event)"
(nodeRemoved)="onNodeRemoved($event)"
(nodeRenamed)="onNodeRenamed($event)"
(nodeSelected)="onNodeSelected($event)"
Expand Down Expand Up @@ -202,8 +205,15 @@ export class AppComponent implements OnInit {
]
},
{
value: 'Sans-serif',
value: 'Sans-serif (Right click me - I have a custom menu)',
id: 11,
settings: {
menuItems: [
{ action: NodeMenuItemAction.Custom, name: 'Foo', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Bar', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Baz', cssClass: 'fa fa-arrow-right'}
]
},
children: [
{value: 'Arial', id: 12},
{value: 'Century Gothic', id: 13},
Expand Down Expand Up @@ -567,6 +577,10 @@ export class AppComponent implements OnInit {
AppComponent.logEvent(e, 'Selected');
}

public onMenuItemSelected(e: MenuItemSelectedEvent) {
AppComponent.logEvent(e, `You selected ${e.selectedItem} menu item`);
}

public onNodeExpanded(e: NodeEvent): void {
AppComponent.logEvent(e, 'Expanded');
}
Expand Down
4 changes: 3 additions & 1 deletion src/menu/menu.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ export enum NodeMenuItemAction {
NewFolder,
NewTag,
Rename,
Remove
Remove,
Custom
}

export enum NodeMenuAction {
Expand All @@ -16,4 +17,5 @@ export interface NodeMenuEvent {

export interface NodeMenuItemSelectedEvent {
nodeMenuItemAction: NodeMenuItemAction;
nodeMenuItemSelected?: string;
}
14 changes: 11 additions & 3 deletions src/menu/node-menu.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
Component, EventEmitter, Output, Inject, OnDestroy, OnInit, ViewChild, Renderer2
Component, EventEmitter, Output, Input, Inject, OnDestroy, OnInit, ViewChild, Renderer2
} from '@angular/core';
import { NodeMenuService } from './node-menu.service';
import { NodeMenuItemSelectedEvent, NodeMenuItemAction, NodeMenuAction } from './menu.events';
Expand All @@ -23,6 +23,9 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
@Output()
public menuItemSelected: EventEmitter<NodeMenuItemSelectedEvent> = new EventEmitter<NodeMenuItemSelectedEvent>();

@Input()
public menuItems: NodeMenuItem[];

@ViewChild('menuContainer') public menuContainer: any;

public availableMenuItems: NodeMenuItem[] = [
Expand Down Expand Up @@ -55,6 +58,7 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
}

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

public onMenuItemSelected(e: MouseEvent, selectedMenuItem: NodeMenuItem): void {
if (isLeftButtonClicked(e)) {
this.menuItemSelected.emit({nodeMenuItemAction: selectedMenuItem.action});
this.menuItemSelected.emit({
nodeMenuItemAction: selectedMenuItem.action,
nodeMenuItemSelected: selectedMenuItem.name
});

this.nodeMenuService.fireMenuEvent(e.target as HTMLElement, NodeMenuAction.Close);
}
}
Expand All @@ -84,5 +92,5 @@ export class NodeMenuComponent implements OnInit, OnDestroy {
export interface NodeMenuItem {
name: string;
action: NodeMenuItemAction;
cssClass: string;
cssClass?: string;
}
35 changes: 28 additions & 7 deletions src/tree-internal.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Component, ElementRef, TemplateRef, Inject, Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core';
import {
Component,
ElementRef,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
TemplateRef
} from '@angular/core';
import * as TreeTypes from './tree.types';
import { Tree } from './tree';
import { TreeController } from './tree-controller';
Expand Down Expand Up @@ -41,13 +50,19 @@ import { get } from './utils/fn.utils';
<div class="node-left-menu" *ngIf="tree.hasLeftMenu()" (click)="showLeftMenu($event)" [innerHTML]="tree.leftMenuTemplate">
</div>
<node-menu *ngIf="tree.hasLeftMenu() && isLeftMenuVisible"
<node-menu *ngIf="tree.hasLeftMenu() && isLeftMenuVisible && !hasCustomMenu()"
(menuItemSelected)="onMenuItemSelected($event)">
</node-menu>
</div>
<node-menu *ngIf="isRightMenuVisible" (menuItemSelected)="onMenuItemSelected($event)"></node-menu>
<node-menu *ngIf="isRightMenuVisible && !hasCustomMenu()"
(menuItemSelected)="onMenuItemSelected($event)">
</node-menu>
<node-menu *ngIf="hasCustomMenu() && (isRightMenuVisible || isLeftMenuVisible)"
[menuItems]="tree.menuItems"
(menuItemSelected)="onMenuItemSelected($event)">
</node-menu>
<ng-template [ngIf]="tree.isNodeExpanded()">
<tree-internal *ngFor="let child of tree.childrenAsync | async" [tree]="child" [template]="template"></tree-internal>
</ng-template>
Expand All @@ -72,9 +87,9 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {

private subscriptions: Subscription[] = [];

public constructor(@Inject(NodeMenuService) private nodeMenuService: NodeMenuService,
@Inject(TreeService) public treeService: TreeService,
@Inject(ElementRef) public element: ElementRef) {
public constructor(private nodeMenuService: NodeMenuService,
public treeService: TreeService,
public element: ElementRef) {
}

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

this.settings = this.settings || { rootIsVisible: true };

this.subscriptions.push(this.nodeMenuService.hideMenuStream(this.element)
.subscribe(() => {
this.isRightMenuVisible = false;
Expand Down Expand Up @@ -182,6 +196,9 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
case NodeMenuItemAction.Remove:
this.onRemoveSelected();
break;
case NodeMenuItemAction.Custom:
this.treeService.fireMenuItemSelected(this.tree, e.nodeMenuItemSelected);
break;
default:
throw new Error(`Chosen menu item doesn't exist`);
}
Expand Down Expand Up @@ -235,4 +252,8 @@ export class TreeInternalComponent implements OnInit, OnChanges, OnDestroy {
public isRootHidden(): boolean {
return this.tree.isRoot() && !this.settings.rootIsVisible;
}

public hasCustomMenu(): boolean {
return this.tree.hasCustomMenu();
}
}
13 changes: 10 additions & 3 deletions src/tree.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from '@angular/core';
import { TreeService } from './tree.service';
import * as TreeTypes from './tree.types';
import { NodeEvent } from './tree.events';
import { NodeEvent, MenuItemSelectedEvent } from './tree.events';
import { Tree } from './tree';
import { TreeController } from './tree-controller';
import { Subscription } from 'rxjs/Subscription';
Expand Down Expand Up @@ -46,8 +46,11 @@ export class TreeComponent implements OnInit, OnChanges, OnDestroy {
@Output()
public nodeCollapsed: EventEmitter<any> = new EventEmitter();

@Output()
public loadNextLevel: EventEmitter<any> = new EventEmitter();
@Output()
public menuItemSelected: EventEmitter<any> = new EventEmitter();

@Output()
public loadNextLevel: EventEmitter<any> = new EventEmitter();

public tree: Tree;
@ViewChild('rootComponent') public rootComponent;
Expand Down Expand Up @@ -96,6 +99,10 @@ public loadNextLevel: EventEmitter<any> = new EventEmitter();
this.nodeCollapsed.emit(e);
}));

this.subscriptions.push(this.treeService.menuItemSelected$.subscribe((e: MenuItemSelectedEvent) => {
this.menuItemSelected.emit(e);
}));

this.subscriptions.push(this.treeService.loadNextLevel$.subscribe((e: NodeEvent) => {
this.loadNextLevel.emit(e);
}));
Expand Down
6 changes: 6 additions & 0 deletions src/tree.events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export class NodeCollapsedEvent extends NodeEvent {
}
}

export class MenuItemSelectedEvent extends NodeEvent {
public constructor(node: Tree, public selectedItem: string) {
super(node);
}
}

export class LoadNextLevelEvent extends NodeEvent {
public constructor(node: Tree) {
super(node);
Expand Down
8 changes: 7 additions & 1 deletion src/tree.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
NodeRemovedEvent,
NodeRenamedEvent,
NodeSelectedEvent,
MenuItemSelectedEvent,
LoadNextLevelEvent
} from './tree.events';
import { RenamableNode } from './tree.types';
Expand All @@ -15,7 +16,7 @@ import { Observable, Subject } from 'rxjs/Rx';
import { ElementRef, Inject, Injectable } from '@angular/core';
import { NodeDraggableService } from './draggable/node-draggable.service';
import { NodeDraggableEvent } from './draggable/draggable.events';
import {isEmpty} from './utils/fn.utils';
import { isEmpty } from './utils/fn.utils';

@Injectable()
export class TreeService {
Expand All @@ -26,6 +27,7 @@ export class TreeService {
public nodeSelected$: Subject<NodeSelectedEvent> = new Subject<NodeSelectedEvent>();
public nodeExpanded$: Subject<NodeExpandedEvent> = new Subject<NodeExpandedEvent>();
public nodeCollapsed$: Subject<NodeCollapsedEvent> = new Subject<NodeCollapsedEvent>();
public menuItemSelected$: Subject<MenuItemSelectedEvent> = new Subject<MenuItemSelectedEvent>();
public loadNextLevel$: Subject<LoadNextLevelEvent> = new Subject<LoadNextLevelEvent>();

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

public fireMenuItemSelected(tree: Tree, selectedItem: string): void {
this.menuItemSelected$.next(new MenuItemSelectedEvent(tree, selectedItem));
}

public fireNodeSwitchFoldingType(tree: Tree): void {
if (tree.isNodeExpanded()) {
this.fireNodeExpanded(tree);
Expand Down
16 changes: 16 additions & 0 deletions src/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {

import { Observable, Observer } from 'rxjs/Rx';
import { TreeModel, RenamableNode, FoldingType, TreeStatus, TreeModelSettings, ChildrenLoadingFunction } from './tree.types';
import { NodeMenuItem } from './menu/node-menu.component';

import * as uuidv4 from 'uuid/v4';

Expand Down Expand Up @@ -343,6 +344,21 @@ export class Tree {
return !this.isBranch();
}

/**
* Get menu items of the current tree.
* @returns {NodeMenuItem[]} The menu items of the current tree.
*/
public get menuItems(): NodeMenuItem[] {
return get(this.node.settings, 'menuItems');
}

/**
* Check whether or not this tree has a custom menu.
* @returns {boolean} A flag indicating whether or not this tree has a custom menu.
*/
public hasCustomMenu(): boolean {
return !this.isStatic() && !!get(this.node.settings, 'menuItems', false);
}
/**
* Check whether this tree is "Branch" or not. "Branch" is a node that has children.
* @returns {boolean} A flag indicating whether or not this tree is a "Branch".
Expand Down
8 changes: 8 additions & 0 deletions src/tree.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { get, defaultsDeep } from './utils/fn.utils';
import { NodeMenuItem } from './menu/node-menu.component';

export class FoldingType {
public static Expanded: FoldingType = new FoldingType('node-expanded');
Expand Down Expand Up @@ -76,6 +77,13 @@ export class TreeModelSettings {
*/
public rightMenu?: boolean;

/**
* "menu" property when set will be available as custom context menu.
* @name TreeModelSettings#MenuItems
* @type NodeMenuItem
*/
public menuItems?: NodeMenuItem[];

/**
* "static" property when set to true makes it impossible to drag'n'drop tree or call a menu on it.
* @name TreeModelSettings#static
Expand Down
Loading

0 comments on commit d776886

Please sign in to comment.