import { AfterContentInit, ChangeDetectorRef, Component, ContentChildren, EventEmitter, HostListener, Inject, Input, Optional, Output, QueryList, } from '@angular/core'; import { ContextMenuService, IContextMenuClickEvent } from './context-menu.service'; import { ContextMenuItemDirective } from './context-menu.item.directive'; export interface ILinkConfig { click: (item: any, $event?: MouseEvent) => void; enabled?: (item: any) => boolean; html: (item: any) => string; } @Component({ selector: 'context-menu', styles: [ ] , template: ` `, }) export class ContextMenuComponent implements AfterContentInit { @Output() public close: EventEmitter = new EventEmitter(); @ContentChildren(ContextMenuItemDirective) public menuItems: QueryList; public visibleMenuItems: ContextMenuItemDirective[] = []; public links: ILinkConfig[] = []; public isShown: boolean = false; public isOpening: boolean = false; public item: any; private mouseLocation: { left: number, top: number } = { left: 0, top: 0 }; constructor( private _contextMenuService: ContextMenuService, private changeDetector: ChangeDetectorRef, ) { _contextMenuService.show.subscribe(menuEvent => this.onMenuEvent(menuEvent)); } get locationCss(): any { return { 'position': 'fixed', 'display': this.isShown ? 'block' : 'none', left: this.mouseLocation.left + 'px', top: this.mouseLocation.top + 'px', }; } @HostListener('document:click') @HostListener('document:contextmenu') public clickedOutside(): void { if (!this.isOpening) { this.hideMenu(); } } public ngAfterContentInit(): void { this.menuItems.forEach(menuItem => { menuItem.execute.subscribe(() => this.hideMenu()); }); } public isMenuItemEnabled(menuItem: ContextMenuItemDirective): boolean { return this.evaluateIfFunction(menuItem.enabled); } public isMenuItemVisible(menuItem: ContextMenuItemDirective): boolean { return this.evaluateIfFunction(menuItem.visible); } public evaluateIfFunction(value: any): any { if (value instanceof Function) { return value(this.item); } return value; } public isDisabled(link: ILinkConfig): boolean { return link.enabled && !link.enabled(this.item); } public execute(link: ILinkConfig, $event?: MouseEvent): void { if (this.isDisabled(link)) { return; } this.hideMenu(); link.click(this.item, $event); } public onMenuEvent(menuEvent: IContextMenuClickEvent): void { let { actions, contextMenu, event, item } = menuEvent; if (contextMenu && contextMenu !== this) { this.hideMenu(); return; } this.isOpening = true; setTimeout(() => this.isOpening = false, 400); if (actions) { if (console && console.warn) { console.warn(`actions configuration object is deprecated and will be removed in version 1.x. See https://github.com/isaacplmann/angular2-contextmenu for the new declarative syntax.`); } } if (actions && actions.length > 0) { // Imperative context menu this.setVisibleMenuItems(); this.showMenu(); } else if (this.menuItems) { // Declarative context menu setTimeout(() => { this.setVisibleMenuItems(); if (this.visibleMenuItems.length > 0) { this.showMenu(); } else { this.hideMenu(); } }); } else { this.hideMenu(); } this.links = actions; this.item = item; this.mouseLocation = { left: event.clientX, top: event.clientY, }; } public setVisibleMenuItems(): void { this.visibleMenuItems = this.menuItems.filter(menuItem => this.isMenuItemVisible(menuItem)); } public showMenu(): void { this.isShown = true; this.changeDetector.markForCheck(); } public hideMenu(): void { if (this.isShown === true) { this.close.emit({}); } this.isShown = false; this.changeDetector.markForCheck(); } }