UNPKG

ng2-right-click-menu

Version:
777 lines (770 loc) 24.2 kB
import { Directive, TemplateRef, Optional, Input, Output, EventEmitter, Injectable, ElementRef, Component, ViewEncapsulation, ContentChildren, ViewChildren, ViewChild, ViewContainerRef, QueryList, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Overlay, OverlayModule } from '@angular/cdk/overlay'; import { TemplatePortal } from '@angular/cdk/portal'; import { __spread } from 'tslib'; import { fromEvent, merge } from 'rxjs'; /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var MenuItemContext = /** @class */ (function () { function MenuItemContext() { this.$implicit = {}; } return MenuItemContext; }()); var ShContextMenuItemDirective = /** @class */ (function () { function ShContextMenuItemDirective(template) { this.template = template; this.closeOnClick = true; this.click = new EventEmitter(); this.context = new MenuItemContext(); } /** * @return {?} */ ShContextMenuItemDirective.prototype.setNotActive = /** * @return {?} */ function () { this._active = false; if (this.subMenu) { this.subMenu.setNotActive(); } }; /** * @return {?} */ ShContextMenuItemDirective.prototype.setActive = /** * @return {?} */ function () { this._active = true; }; ShContextMenuItemDirective.decorators = [ { type: Directive, args: [{ selector: '[shContextMenuItem]' },] } ]; /** @nocollapse */ ShContextMenuItemDirective.ctorParameters = function () { return [ { type: TemplateRef, decorators: [{ type: Optional }] } ]; }; ShContextMenuItemDirective.propDecorators = { subMenu: [{ type: Input }], divider: [{ type: Input }], visible: [{ type: Input }], disabled: [{ type: Input }], closeOnClick: [{ type: Input }], click: [{ type: Output }] }; return ShContextMenuItemDirective; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var ShContextMenuService = /** @class */ (function () { function ShContextMenuService(overlay) { this.overlay = overlay; this.activeOverlays = []; } /** * @param {?} ctxEvent * @return {?} */ ShContextMenuService.prototype.openMenu = /** * @param {?} ctxEvent * @return {?} */ function (ctxEvent) { this.closeCurrentOverlays(); var menu = ctxEvent.menu, mouseEvent = ctxEvent.mouseEvent, data = ctxEvent.data; this.activeMenu = menu; this.anchorElement = this.createAnchorElement(); /** @type {?} */ var scrollStrategy = this.buildScrollStrategy(); /** @type {?} */ var positionStrategy = this.buildPositionStrategy(this.anchorElement, mouseEvent); this.attachContextToItems(menu, data); /** @type {?} */ var overlayRef = this.createAndAttachOverlay(positionStrategy, scrollStrategy, menu, true); this.attachOverlayRef(menu, overlayRef); this.registerDetachEvents(overlayRef); }; /** * @param {?} ctxEvent * @return {?} */ ShContextMenuService.prototype.openSubMenu = /** * @param {?} ctxEvent * @return {?} */ function (ctxEvent) { var menu = ctxEvent.menu, mouseEvent = ctxEvent.mouseEvent, targetElement = ctxEvent.targetElement, data = ctxEvent.data, parentMenu = ctxEvent.parentMenu; mouseEvent.preventDefault(); mouseEvent.stopPropagation(); /** @type {?} */ var scrollStrategy = this.buildScrollStrategy(); /** @type {?} */ var positionStrategy = this.buildPositionStrategyForSubMenu(targetElement); /** @type {?} */ var overlayRef = this.createAndAttachOverlay(positionStrategy, scrollStrategy, menu, false); this.attachContextToItems(menu, data); this.attachThisContext(menu, parentMenu); this.attachOverlayRef(menu, overlayRef); }; /** * @return {?} */ ShContextMenuService.prototype.destroy = /** * @return {?} */ function () { this.closeCurrentOverlays(); this.subs.unsubscribe(); }; /** * @return {?} */ ShContextMenuService.prototype.ngOnDestroy = /** * @return {?} */ function () { this.destroy(); }; /** * @param {?} menu * @return {?} */ ShContextMenuService.prototype.closeSubMenus = /** * @param {?} menu * @return {?} */ function (menu) { var _this = this; /** @type {?} */ var itemsWithSubMenus = menu.menuItems.filter((/** * @param {?} i * @return {?} */ function (i) { return !!i.subMenu && !!i.subMenu.overlayRef; })); if (itemsWithSubMenus.length) { itemsWithSubMenus.forEach((/** * @param {?} sm * @return {?} */ function (sm) { return _this.closeSubMenus(sm.subMenu); })); /** @type {?} */ var overlayRefs = itemsWithSubMenus.map((/** * @param {?} i * @return {?} */ function (i) { return i.subMenu.overlayRef; })); overlayRefs.forEach((/** * @param {?} r * @return {?} */ function (r) { return r.dispose(); })); } }; /** * @private * @param {?} overlayRef * @return {?} */ ShContextMenuService.prototype.registerDetachEvents = /** * @private * @param {?} overlayRef * @return {?} */ function (overlayRef) { this.subs = overlayRef .backdropClick() .subscribe(this.closeCurrentOverlays.bind(this)); this.subs.add(overlayRef.detachments().subscribe(this.closeCurrentOverlays.bind(this))); }; /** * @private * @param {?} positionStrategy * @param {?} scrollStrategy * @param {?} menu * @param {?=} hasBackdrop * @return {?} */ ShContextMenuService.prototype.createAndAttachOverlay = /** * @private * @param {?} positionStrategy * @param {?} scrollStrategy * @param {?} menu * @param {?=} hasBackdrop * @return {?} */ function (positionStrategy, scrollStrategy, menu, hasBackdrop) { if (hasBackdrop === void 0) { hasBackdrop = true; } /** @type {?} */ var overlayRef = this.overlay.create({ positionStrategy: positionStrategy, scrollStrategy: scrollStrategy, hasBackdrop: hasBackdrop, backdropClass: 'sh-backdrop' }); /* TODO: try passing the TemplatePortal context (data) and then injecting it to the *ngTemplateOutlet in the component template */ /** @type {?} */ var menuPortal = new TemplatePortal(menu.menuTemplate, menu.menuContainer); overlayRef.attach(menuPortal); this.activeOverlays.push(overlayRef); return overlayRef; }; /** * @private * @return {?} */ ShContextMenuService.prototype.buildScrollStrategy = /** * @private * @return {?} */ function () { return this.overlay.scrollStrategies.reposition({ autoClose: true }); }; /** * @private * @param {?} ele * @param {?} event * @return {?} */ ShContextMenuService.prototype.buildPositionStrategy = /** * @private * @param {?} ele * @param {?} event * @return {?} */ function (ele, event) { var x = event.x, y = event.y; return this.overlay .position() .flexibleConnectedTo(ele) .withDefaultOffsetX(x) .withDefaultOffsetY(y) .withPositions(this.buildPositions()) .withFlexibleDimensions(false) .withPush(true); }; /** * @private * @param {?} elm * @return {?} */ ShContextMenuService.prototype.buildPositionStrategyForSubMenu = /** * @private * @param {?} elm * @return {?} */ function (elm) { return this.overlay .position() .flexibleConnectedTo(elm) .withPositions(this.buildSubMenuPositions()) .withFlexibleDimensions(false) .withPush(true); }; /** * @private * @return {?} */ ShContextMenuService.prototype.closeCurrentOverlays = /** * @private * @return {?} */ function () { if (this.anchorElement) { this.anchorElement.remove(); } this.activeOverlays.forEach((/** * @param {?} o * @return {?} */ function (o) { o.detach(); o.dispose(); })); this.activeOverlays = []; // TODO: create close subject and emit. // subscribe in component if (this.activeMenu) { this.activeMenu.close(); } }; /** * @private * @param {?} menu * @param {?} data * @return {?} */ ShContextMenuService.prototype.attachContextToItems = /** * @private * @param {?} menu * @param {?} data * @return {?} */ function (menu, data) { menu.menuItems.forEach((/** * @param {?} i * @return {?} */ function (i) { return (i.context.$implicit = data); })); }; /** * @private * @param {?} menu * @param {?} parentMenu * @return {?} */ ShContextMenuService.prototype.attachThisContext = /** * @private * @param {?} menu * @param {?} parentMenu * @return {?} */ function (menu, parentMenu) { menu.thisContext = parentMenu.thisContext; }; /** * @private * @param {?} menu * @param {?} overlayRef * @return {?} */ ShContextMenuService.prototype.attachOverlayRef = /** * @private * @param {?} menu * @param {?} overlayRef * @return {?} */ function (menu, overlayRef) { menu.overlayRef = overlayRef; }; /** * @private * @return {?} */ ShContextMenuService.prototype.createAnchorElement = /** * @private * @return {?} */ function () { /** @type {?} */ var div = document.createElement('div'); div.style.position = 'absolute'; div.style.top = '0'; div.style.bottom = '0'; div.style.left = '0'; div.style.right = '0'; document.body.appendChild(div); return div; }; /** * @private * @return {?} */ ShContextMenuService.prototype.buildSubMenuPositions = /** * @private * @return {?} */ function () { return [ { originX: 'end', originY: 'top', overlayX: 'start', overlayY: 'top' }, { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top' }, { originX: 'end', originY: 'bottom', overlayX: 'start', overlayY: 'bottom' }, { originX: 'start', originY: 'bottom', overlayX: 'end', overlayY: 'bottom' } ]; }; /** * @private * @return {?} */ ShContextMenuService.prototype.buildPositions = /** * @private * @return {?} */ function () { return [ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'top' }, { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'top' }, { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }, { originX: 'start', originY: 'top', overlayX: 'end', overlayY: 'bottom' } ]; }; ShContextMenuService.decorators = [ { type: Injectable } ]; /** @nocollapse */ ShContextMenuService.ctorParameters = function () { return [ { type: Overlay } ]; }; return ShContextMenuService; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var ShContextMenuComponent = /** @class */ (function () { function ShContextMenuComponent(ctxService) { this.ctxService = ctxService; this.contentChildrenItems = new QueryList(); this.viewChildrenItems = new QueryList(); } Object.defineProperty(ShContextMenuComponent.prototype, "menuItems", { get: /** * @return {?} */ function () { // when using the ShContextMenuComponent as menu, the ContentChildren is the source if (this.contentChildrenItems.length) { return this.contentChildrenItems; } // when using a custom component as menu the ViewChildren is the source return this.viewChildrenItems; }, enumerable: true, configurable: true }); /** * @param {?} $event * @param {?} item * @param {?} elm * @return {?} */ ShContextMenuComponent.prototype.onEnter = /** * @param {?} $event * @param {?} item * @param {?} elm * @return {?} */ function ($event, item, elm) { this.ctxService.closeSubMenus(this); this.setNotActive(); if (!item.subMenu) { return; } this.setActive(item); this.ctxService.openSubMenu({ data: item.context.$implicit, targetElement: new ElementRef(elm), menu: item.subMenu, mouseEvent: $event, parentMenu: this }); }; /** * @private * @param {?} item * @return {?} */ ShContextMenuComponent.prototype.setActive = /** * @private * @param {?} item * @return {?} */ function (item) { item.setActive(); this.subActive = true; }; /** * @param {?} event * @param {?} item * @return {?} */ ShContextMenuComponent.prototype.onClick = /** * @param {?} event * @param {?} item * @return {?} */ function (event, item) { // TODO: move click handling to service if (item.divider) { return; } if (!item.subMenu && item.closeOnClick) { this.ctxService.destroy(); item.click.emit({ data: item.context.$implicit, event: event }); } }; /** * @private * @param {?} fn * @param {?} fallbackContext * @param {?} data * @param {?} event * @return {?} */ ShContextMenuComponent.prototype.callWithContext = /** * @private * @param {?} fn * @param {?} fallbackContext * @param {?} data * @param {?} event * @return {?} */ function (fn, fallbackContext, data, event) { return fn.call(this.thisContext ? this.thisContext : fallbackContext, { data: data, event: event }); }; /** * @return {?} */ ShContextMenuComponent.prototype.close = /** * @return {?} */ function () { this.setNotActive(); this.menuContainer.detach(); if (this.overlayRef) { this.overlayRef.detach(); } }; /** * @return {?} */ ShContextMenuComponent.prototype.ngOnDestroy = /** * @return {?} */ function () { this.close(); }; /** * @return {?} */ ShContextMenuComponent.prototype.setNotActive = /** * @return {?} */ function () { this.subActive = false; this.menuItems.forEach((/** * @param {?} i * @return {?} */ function (i) { return i.setNotActive(); })); }; /** * @param {?} item * @return {?} */ ShContextMenuComponent.prototype.isVisible = /** * @param {?} item * @return {?} */ function (item) { if (!item.visible) { return true; } return this.callWithContext(item.visible, this, item.context.$implicit, null); }; ShContextMenuComponent.decorators = [ { type: Component, args: [{ selector: 'sh-context-menu', encapsulation: ViewEncapsulation.None, template: "\n\t\t<ng-container #menuContainer></ng-container>\n\t\t<ng-template #menuTemplate>\n\t\t\t<div class=\"sh-context-menu\">\n\t\t\t\t<div\n\t\t\t\t\t*ngFor=\"let menuItem of menuItems\"\n\t\t\t\t\t#itemElement\n\t\t\t\t\t[ngClass]=\"{\n\t\t\t\t\t\t'sh-sub-anchor': menuItem.subMenu,\n\t\t\t\t\t\t'sh-context-menu--item__divider': menuItem.divider,\n\t\t\t\t\t\t'sh-context-menu--item__sub-active': subActive && menuItem.active\n\t\t\t\t\t}\"\n\t\t\t\t\tclass=\"sh-context-menu--item\"\n\t\t\t\t\t(mouseenter)=\"onEnter($event, menuItem, itemElement)\"\n\t\t\t\t\t(click)=\"onClick($event, menuItem)\"\n\t\t\t\t>\n\t\t\t\t\t<ng-container *ngIf=\"!menuItem.divider || !isVisible(menuItem)\">\n\t\t\t\t\t\t<ng-content\n\t\t\t\t\t\t\t*ngTemplateOutlet=\"menuItem.template; context: menuItem.context\"\n\t\t\t\t\t\t></ng-content>\n\t\t\t\t\t</ng-container>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</ng-template>\n\t", styles: [".sh-backdrop{background-color:transparent}.sh-context-menu{background:#ececec;min-width:150px;border:1px solid rgba(0,0,0,.2);border-radius:3px;box-shadow:0 0 10px 2px rgba(0,0,0,.1);color:#000;padding:5px 0;margin:0}.sh-context-menu--item{padding:5px 10px 5px 15px;-webkit-transition:.15s;transition:.15s}.sh-context-menu--item:hover,.sh-context-menu--item__sub-active{background-color:#4b8bec;color:#fff;cursor:pointer}.sh-context-menu--item.sh-context-menu--item__divider:hover{background-color:#ececec;color:#000;cursor:default}.sh-context-menu--item__divider{height:1px;margin:1px 1px 8px;overflow:hidden;border-bottom:1px solid #d0d0d0}.sh-context-menu--item.sh-sub-anchor{position:relative;min-width:160px}.sh-sub-anchor:after{content:'';top:50%;right:6px;-webkit-transform:translateY(-50%);transform:translateY(-50%);position:absolute;border-top:6px solid transparent;border-bottom:6px solid transparent;border-left:8px solid #000}"] }] } ]; /** @nocollapse */ ShContextMenuComponent.ctorParameters = function () { return [ { type: ShContextMenuService } ]; }; ShContextMenuComponent.propDecorators = { thisContext: [{ type: Input, args: ['this',] }], contentChildrenItems: [{ type: ContentChildren, args: [ShContextMenuItemDirective, { read: ShContextMenuItemDirective },] }], viewChildrenItems: [{ type: ViewChildren, args: [ShContextMenuItemDirective, { read: ShContextMenuItemDirective },] }], menuTemplate: [{ type: ViewChild, args: ['menuTemplate', { read: TemplateRef, static: true },] }], menuContainer: [{ type: ViewChild, args: ['menuContainer', { read: ViewContainerRef, static: true },] }] }; return ShContextMenuComponent; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var ShAttachMenuDirective = /** @class */ (function () { function ShAttachMenuDirective(ctxService, elm) { this.ctxService = ctxService; this.elm = elm; this.open = new EventEmitter(); } /** * @return {?} */ ShAttachMenuDirective.prototype.ngOnInit = /** * @return {?} */ function () { this.setupEvents(); }; /** * @private * @return {?} */ ShAttachMenuDirective.prototype.setupEvents = /** * @private * @return {?} */ function () { var _this = this; /** @type {?} */ var observables = []; if (!this.triggers) { observables.push(fromEvent(this.elm.nativeElement, 'contextmenu')); } else { this.triggers.forEach((/** * @param {?} t * @return {?} */ function (t) { observables.push(fromEvent(_this.elm.nativeElement, t)); })); } this.sub = merge.apply(void 0, __spread(observables)).subscribe(this.openMenu.bind(this)); }; /** * @param {?} event * @return {?} */ ShAttachMenuDirective.prototype.openMenu = /** * @param {?} event * @return {?} */ function (event) { event.preventDefault(); event.stopPropagation(); /** @type {?} */ var preventOpen = false; this.open.emit({ data: this.data, mouseEvent: event, preventOpen: (/** * @return {?} */ function () { preventOpen = true; }) }); if (preventOpen) return; this.ctxService.openMenu({ menu: this.menu, mouseEvent: event, targetElement: this.elm, data: this.data }); }; /** * @return {?} */ ShAttachMenuDirective.prototype.ngOnDestroy = /** * @return {?} */ function () { if (this.sub) { this.sub.unsubscribe(); } }; ShAttachMenuDirective.decorators = [ { type: Directive, args: [{ selector: '[shAttachMenu]' },] } ]; /** @nocollapse */ ShAttachMenuDirective.ctorParameters = function () { return [ { type: ShContextMenuService }, { type: ElementRef } ]; }; ShAttachMenuDirective.propDecorators = { menu: [{ type: Input, args: ['shAttachMenu',] }], triggers: [{ type: Input, args: ['shMenuTriggers',] }], data: [{ type: Input, args: ['shMenuData',] }], open: [{ type: Output }] }; return ShAttachMenuDirective; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ var ShContextMenuModule = /** @class */ (function () { function ShContextMenuModule() { } ShContextMenuModule.decorators = [ { type: NgModule, args: [{ declarations: [ ShAttachMenuDirective, ShContextMenuComponent, ShContextMenuItemDirective ], exports: [ ShAttachMenuDirective, ShContextMenuComponent, ShContextMenuItemDirective ], providers: [ShContextMenuService], imports: [CommonModule, OverlayModule], entryComponents: [ShContextMenuComponent] },] } ]; return ShContextMenuModule; }()); export { ShAttachMenuDirective, ShContextMenuComponent, ShContextMenuItemDirective, ShContextMenuModule, ShContextMenuService, MenuItemContext as ɵa }; //# sourceMappingURL=ng2-right-click-menu.js.map