ngx-contextmenu.js 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207
  1. import { Overlay, ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay';
  2. import { CommonModule } from '@angular/common';
  3. import { EventEmitter, Directive, TemplateRef, ElementRef, Input, Output, InjectionToken, QueryList, Component, ChangeDetectorRef, Optional, Inject, ViewChild, ViewChildren, HostListener, Injectable, ViewEncapsulation, ContentChildren, NgModule } from '@angular/core';
  4. import { Subscription, Subject } from 'rxjs';
  5. import { first } from 'rxjs/operators';
  6. import { ComponentPortal } from '@angular/cdk/portal';
  7. import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
  8. /**
  9. * @fileoverview added by tsickle
  10. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  11. */
  12. class ContextMenuItemDirective {
  13. /**
  14. * @param {?} template
  15. * @param {?} elementRef
  16. */
  17. constructor(template, elementRef) {
  18. this.template = template;
  19. this.elementRef = elementRef;
  20. this.divider = false;
  21. this.enabled = true;
  22. this.passive = false;
  23. this.visible = true;
  24. this.execute = new EventEmitter();
  25. this.isActive = false;
  26. }
  27. /**
  28. * @return {?}
  29. */
  30. get disabled() {
  31. return this.passive ||
  32. this.divider ||
  33. !this.evaluateIfFunction(this.enabled, this.currentItem);
  34. }
  35. /**
  36. * @param {?} value
  37. * @param {?} item
  38. * @return {?}
  39. */
  40. evaluateIfFunction(value, item) {
  41. if (value instanceof Function) {
  42. return value(item);
  43. }
  44. return value;
  45. }
  46. /**
  47. * @return {?}
  48. */
  49. setActiveStyles() {
  50. this.isActive = true;
  51. }
  52. /**
  53. * @return {?}
  54. */
  55. setInactiveStyles() {
  56. this.isActive = false;
  57. }
  58. /**
  59. * @param {?} item
  60. * @param {?=} $event
  61. * @return {?}
  62. */
  63. triggerExecute(item, $event) {
  64. if (!this.evaluateIfFunction(this.enabled, item)) {
  65. return;
  66. }
  67. this.execute.emit({ event: $event, item });
  68. }
  69. }
  70. ContextMenuItemDirective.decorators = [
  71. { type: Directive, args: [{
  72. /* tslint:disable:directive-selector-type */
  73. selector: '[contextMenuItem]',
  74. },] }
  75. ];
  76. /** @nocollapse */
  77. ContextMenuItemDirective.ctorParameters = () => [
  78. { type: TemplateRef },
  79. { type: ElementRef }
  80. ];
  81. ContextMenuItemDirective.propDecorators = {
  82. subMenu: [{ type: Input }],
  83. divider: [{ type: Input }],
  84. enabled: [{ type: Input }],
  85. passive: [{ type: Input }],
  86. visible: [{ type: Input }],
  87. execute: [{ type: Output }]
  88. };
  89. if (false) {
  90. /** @type {?} */
  91. ContextMenuItemDirective.prototype.subMenu;
  92. /** @type {?} */
  93. ContextMenuItemDirective.prototype.divider;
  94. /** @type {?} */
  95. ContextMenuItemDirective.prototype.enabled;
  96. /** @type {?} */
  97. ContextMenuItemDirective.prototype.passive;
  98. /** @type {?} */
  99. ContextMenuItemDirective.prototype.visible;
  100. /** @type {?} */
  101. ContextMenuItemDirective.prototype.execute;
  102. /** @type {?} */
  103. ContextMenuItemDirective.prototype.currentItem;
  104. /** @type {?} */
  105. ContextMenuItemDirective.prototype.isActive;
  106. /** @type {?} */
  107. ContextMenuItemDirective.prototype.template;
  108. /** @type {?} */
  109. ContextMenuItemDirective.prototype.elementRef;
  110. }
  111. /**
  112. * @fileoverview added by tsickle
  113. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  114. */
  115. /** @type {?} */
  116. const CONTEXT_MENU_OPTIONS = new InjectionToken('CONTEXT_MENU_OPTIONS');
  117. /**
  118. * @fileoverview added by tsickle
  119. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  120. */
  121. /**
  122. * @record
  123. */
  124. function ILinkConfig() { }
  125. if (false) {
  126. /** @type {?} */
  127. ILinkConfig.prototype.click;
  128. /** @type {?|undefined} */
  129. ILinkConfig.prototype.enabled;
  130. /** @type {?} */
  131. ILinkConfig.prototype.html;
  132. }
  133. /** @type {?} */
  134. const ARROW_LEFT_KEYCODE = 37;
  135. class ContextMenuContentComponent {
  136. /**
  137. * @param {?} changeDetector
  138. * @param {?} elementRef
  139. * @param {?} options
  140. */
  141. constructor(changeDetector, elementRef, options) {
  142. this.changeDetector = changeDetector;
  143. this.elementRef = elementRef;
  144. this.options = options;
  145. this.menuItems = [];
  146. this.isLeaf = false;
  147. this.execute = new EventEmitter();
  148. this.openSubMenu = new EventEmitter();
  149. this.closeLeafMenu = new EventEmitter();
  150. this.closeAllMenus = new EventEmitter();
  151. this.autoFocus = false;
  152. this.useBootstrap4 = false;
  153. this.subscription = new Subscription();
  154. if (options) {
  155. this.autoFocus = options.autoFocus;
  156. this.useBootstrap4 = options.useBootstrap4;
  157. }
  158. }
  159. /**
  160. * @return {?}
  161. */
  162. ngOnInit() {
  163. this.menuItems.forEach((/**
  164. * @param {?} menuItem
  165. * @return {?}
  166. */
  167. menuItem => {
  168. menuItem.currentItem = this.item;
  169. this.subscription.add(menuItem.execute.subscribe((/**
  170. * @param {?} event
  171. * @return {?}
  172. */
  173. event => this.execute.emit(Object.assign({}, event, { menuItem })))));
  174. }));
  175. /** @type {?} */
  176. const queryList = new QueryList();
  177. queryList.reset(this.menuItems);
  178. this._keyManager = new ActiveDescendantKeyManager(queryList).withWrap();
  179. }
  180. /**
  181. * @return {?}
  182. */
  183. ngAfterViewInit() {
  184. if (this.autoFocus) {
  185. setTimeout((/**
  186. * @return {?}
  187. */
  188. () => this.focus()));
  189. }
  190. this.overlay.updatePosition();
  191. }
  192. /**
  193. * @return {?}
  194. */
  195. ngOnDestroy() {
  196. this.subscription.unsubscribe();
  197. }
  198. /**
  199. * @return {?}
  200. */
  201. focus() {
  202. if (this.autoFocus) {
  203. this.menuElement.nativeElement.focus();
  204. }
  205. }
  206. /**
  207. * @param {?} $event
  208. * @return {?}
  209. */
  210. stopEvent($event) {
  211. $event.stopPropagation();
  212. }
  213. /**
  214. * @param {?} menuItem
  215. * @return {?}
  216. */
  217. isMenuItemEnabled(menuItem) {
  218. return this.evaluateIfFunction(menuItem && menuItem.enabled);
  219. }
  220. /**
  221. * @param {?} menuItem
  222. * @return {?}
  223. */
  224. isMenuItemVisible(menuItem) {
  225. return this.evaluateIfFunction(menuItem && menuItem.visible);
  226. }
  227. /**
  228. * @param {?} value
  229. * @return {?}
  230. */
  231. evaluateIfFunction(value) {
  232. if (value instanceof Function) {
  233. return value(this.item);
  234. }
  235. return value;
  236. }
  237. /**
  238. * @param {?} link
  239. * @return {?}
  240. */
  241. isDisabled(link) {
  242. return link.enabled && !link.enabled(this.item);
  243. }
  244. /**
  245. * @param {?} event
  246. * @return {?}
  247. */
  248. onKeyEvent(event) {
  249. if (!this.isLeaf) {
  250. return;
  251. }
  252. this._keyManager.onKeydown(event);
  253. }
  254. /**
  255. * @param {?=} event
  256. * @return {?}
  257. */
  258. keyboardOpenSubMenu(event) {
  259. if (!this.isLeaf) {
  260. return;
  261. }
  262. this.cancelEvent(event);
  263. /** @type {?} */
  264. const menuItem = this.menuItems[this._keyManager.activeItemIndex];
  265. if (menuItem) {
  266. this.onOpenSubMenu(menuItem);
  267. }
  268. }
  269. /**
  270. * @param {?=} event
  271. * @return {?}
  272. */
  273. keyboardMenuItemSelect(event) {
  274. if (!this.isLeaf) {
  275. return;
  276. }
  277. this.cancelEvent(event);
  278. /** @type {?} */
  279. const menuItem = this.menuItems[this._keyManager.activeItemIndex];
  280. if (menuItem) {
  281. this.onMenuItemSelect(menuItem, event);
  282. }
  283. }
  284. /**
  285. * @param {?} event
  286. * @return {?}
  287. */
  288. onCloseLeafMenu(event) {
  289. if (!this.isLeaf) {
  290. return;
  291. }
  292. this.cancelEvent(event);
  293. this.closeLeafMenu.emit({
  294. exceptRootMenu: event.keyCode === ARROW_LEFT_KEYCODE,
  295. event
  296. });
  297. }
  298. /**
  299. * @param {?} event
  300. * @return {?}
  301. */
  302. closeMenu(event) {
  303. if (event.type === 'click' && event.button === 2) {
  304. return;
  305. }
  306. this.closeAllMenus.emit({ event });
  307. }
  308. /**
  309. * @param {?} menuItem
  310. * @param {?=} event
  311. * @return {?}
  312. */
  313. onOpenSubMenu(menuItem, event) {
  314. /** @type {?} */
  315. const anchorElementRef = this.menuItemElements.toArray()[this._keyManager.activeItemIndex];
  316. /** @type {?} */
  317. const anchorElement = anchorElementRef && anchorElementRef.nativeElement;
  318. this.openSubMenu.emit({
  319. anchorElement,
  320. contextMenu: menuItem.subMenu,
  321. event,
  322. item: this.item,
  323. parentContextMenu: this
  324. });
  325. }
  326. /**
  327. * @param {?} menuItem
  328. * @param {?} event
  329. * @return {?}
  330. */
  331. onMenuItemSelect(menuItem, event) {
  332. event.preventDefault();
  333. event.stopPropagation();
  334. this.onOpenSubMenu(menuItem, event);
  335. if (!menuItem.subMenu) {
  336. menuItem.triggerExecute(this.item, event);
  337. }
  338. }
  339. /**
  340. * @private
  341. * @param {?} event
  342. * @return {?}
  343. */
  344. cancelEvent(event) {
  345. if (!event) {
  346. return;
  347. }
  348. /** @type {?} */
  349. const target = event.target;
  350. if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(target.tagName) > -1 ||
  351. target.isContentEditable) {
  352. return;
  353. }
  354. event.preventDefault();
  355. event.stopPropagation();
  356. }
  357. }
  358. ContextMenuContentComponent.decorators = [
  359. { type: Component, args: [{
  360. selector: 'context-menu-content',
  361. template: `
  362. <div
  363. class="dropdown open show ngx-contextmenu"
  364. [ngClass]="menuClass"
  365. tabindex="0"
  366. >
  367. <ul
  368. #menu
  369. class="dropdown-menu show"
  370. style="position: static; float: none;"
  371. tabindex="0"
  372. >
  373. <li
  374. #li
  375. *ngFor="let menuItem of menuItems; let i = index"
  376. [class.disabled]="!isMenuItemEnabled(menuItem)"
  377. [class.divider]="menuItem.divider"
  378. [class.dropdown-divider]="useBootstrap4 && menuItem.divider"
  379. [class.active]="menuItem.isActive && isMenuItemEnabled(menuItem)"
  380. [attr.role]="menuItem.divider ? 'separator' : undefined"
  381. >
  382. <a
  383. *ngIf="!menuItem.divider && !menuItem.passive"
  384. href
  385. [class.dropdown-item]="useBootstrap4"
  386. [class.active]="menuItem.isActive && isMenuItemEnabled(menuItem)"
  387. [class.disabled]="useBootstrap4 && !isMenuItemEnabled(menuItem)"
  388. [class.hasSubMenu]="!!menuItem.subMenu"
  389. (click)="onMenuItemSelect(menuItem, $event)"
  390. (mouseenter)="onOpenSubMenu(menuItem, $event)"
  391. >
  392. <ng-template
  393. [ngTemplateOutlet]="menuItem.template"
  394. [ngTemplateOutletContext]="{ $implicit: item }"
  395. ></ng-template>
  396. </a>
  397. <span
  398. (click)="stopEvent($event)"
  399. (contextmenu)="stopEvent($event)"
  400. class="passive"
  401. *ngIf="!menuItem.divider && menuItem.passive"
  402. [class.dropdown-item]="useBootstrap4"
  403. [class.disabled]="useBootstrap4 && !isMenuItemEnabled(menuItem)"
  404. >
  405. <ng-template
  406. [ngTemplateOutlet]="menuItem.template"
  407. [ngTemplateOutletContext]="{ $implicit: item }"
  408. ></ng-template>
  409. </span>
  410. </li>
  411. </ul>
  412. </div>
  413. `,
  414. styles: [`
  415. .passive {
  416. display: block;
  417. padding: 3px 20px;
  418. clear: both;
  419. font-weight: normal;
  420. line-height: @line-height-base;
  421. white-space: nowrap;
  422. }
  423. .hasSubMenu:before {
  424. content: '\u25B6';
  425. float: right;
  426. }
  427. `]
  428. }] }
  429. ];
  430. /** @nocollapse */
  431. ContextMenuContentComponent.ctorParameters = () => [
  432. { type: ChangeDetectorRef },
  433. { type: ElementRef },
  434. { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [CONTEXT_MENU_OPTIONS,] }] }
  435. ];
  436. ContextMenuContentComponent.propDecorators = {
  437. menuItems: [{ type: Input }],
  438. item: [{ type: Input }],
  439. event: [{ type: Input }],
  440. parentContextMenu: [{ type: Input }],
  441. menuClass: [{ type: Input }],
  442. overlay: [{ type: Input }],
  443. isLeaf: [{ type: Input }],
  444. execute: [{ type: Output }],
  445. openSubMenu: [{ type: Output }],
  446. closeLeafMenu: [{ type: Output }],
  447. closeAllMenus: [{ type: Output }],
  448. menuElement: [{ type: ViewChild, args: ['menu', { static: true },] }],
  449. menuItemElements: [{ type: ViewChildren, args: ['li',] }],
  450. onKeyEvent: [{ type: HostListener, args: ['window:keydown.ArrowDown', ['$event'],] }, { type: HostListener, args: ['window:keydown.ArrowUp', ['$event'],] }],
  451. keyboardOpenSubMenu: [{ type: HostListener, args: ['window:keydown.ArrowRight', ['$event'],] }],
  452. keyboardMenuItemSelect: [{ type: HostListener, args: ['window:keydown.Enter', ['$event'],] }, { type: HostListener, args: ['window:keydown.Space', ['$event'],] }],
  453. onCloseLeafMenu: [{ type: HostListener, args: ['window:keydown.Escape', ['$event'],] }, { type: HostListener, args: ['window:keydown.ArrowLeft', ['$event'],] }],
  454. closeMenu: [{ type: HostListener, args: ['document:click', ['$event'],] }, { type: HostListener, args: ['document:contextmenu', ['$event'],] }]
  455. };
  456. if (false) {
  457. /** @type {?} */
  458. ContextMenuContentComponent.prototype.menuItems;
  459. /** @type {?} */
  460. ContextMenuContentComponent.prototype.item;
  461. /** @type {?} */
  462. ContextMenuContentComponent.prototype.event;
  463. /** @type {?} */
  464. ContextMenuContentComponent.prototype.parentContextMenu;
  465. /** @type {?} */
  466. ContextMenuContentComponent.prototype.menuClass;
  467. /** @type {?} */
  468. ContextMenuContentComponent.prototype.overlay;
  469. /** @type {?} */
  470. ContextMenuContentComponent.prototype.isLeaf;
  471. /** @type {?} */
  472. ContextMenuContentComponent.prototype.execute;
  473. /** @type {?} */
  474. ContextMenuContentComponent.prototype.openSubMenu;
  475. /** @type {?} */
  476. ContextMenuContentComponent.prototype.closeLeafMenu;
  477. /** @type {?} */
  478. ContextMenuContentComponent.prototype.closeAllMenus;
  479. /** @type {?} */
  480. ContextMenuContentComponent.prototype.menuElement;
  481. /** @type {?} */
  482. ContextMenuContentComponent.prototype.menuItemElements;
  483. /** @type {?} */
  484. ContextMenuContentComponent.prototype.autoFocus;
  485. /** @type {?} */
  486. ContextMenuContentComponent.prototype.useBootstrap4;
  487. /**
  488. * @type {?}
  489. * @private
  490. */
  491. ContextMenuContentComponent.prototype._keyManager;
  492. /**
  493. * @type {?}
  494. * @private
  495. */
  496. ContextMenuContentComponent.prototype.subscription;
  497. /**
  498. * @type {?}
  499. * @private
  500. */
  501. ContextMenuContentComponent.prototype.changeDetector;
  502. /**
  503. * @type {?}
  504. * @private
  505. */
  506. ContextMenuContentComponent.prototype.elementRef;
  507. /**
  508. * @type {?}
  509. * @private
  510. */
  511. ContextMenuContentComponent.prototype.options;
  512. }
  513. /**
  514. * @fileoverview added by tsickle
  515. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  516. */
  517. /**
  518. * @record
  519. */
  520. function IContextMenuClickEvent() { }
  521. if (false) {
  522. /** @type {?|undefined} */
  523. IContextMenuClickEvent.prototype.anchorElement;
  524. /** @type {?|undefined} */
  525. IContextMenuClickEvent.prototype.contextMenu;
  526. /** @type {?|undefined} */
  527. IContextMenuClickEvent.prototype.event;
  528. /** @type {?|undefined} */
  529. IContextMenuClickEvent.prototype.parentContextMenu;
  530. /** @type {?} */
  531. IContextMenuClickEvent.prototype.item;
  532. /** @type {?|undefined} */
  533. IContextMenuClickEvent.prototype.activeMenuItemIndex;
  534. }
  535. /**
  536. * @record
  537. */
  538. function IContextMenuContext() { }
  539. if (false) {
  540. /** @type {?} */
  541. IContextMenuContext.prototype.menuItems;
  542. /** @type {?} */
  543. IContextMenuContext.prototype.menuClass;
  544. }
  545. /**
  546. * @record
  547. */
  548. function CloseLeafMenuEvent() { }
  549. if (false) {
  550. /** @type {?|undefined} */
  551. CloseLeafMenuEvent.prototype.exceptRootMenu;
  552. /** @type {?|undefined} */
  553. CloseLeafMenuEvent.prototype.event;
  554. }
  555. /**
  556. * @record
  557. */
  558. function OverlayRefWithContextMenu() { }
  559. if (false) {
  560. /** @type {?|undefined} */
  561. OverlayRefWithContextMenu.prototype.contextMenu;
  562. }
  563. /**
  564. * @record
  565. */
  566. function CancelContextMenuEvent() { }
  567. if (false) {
  568. /** @type {?} */
  569. CancelContextMenuEvent.prototype.eventType;
  570. /** @type {?|undefined} */
  571. CancelContextMenuEvent.prototype.event;
  572. }
  573. /**
  574. * @record
  575. */
  576. function ExecuteContextMenuEvent() { }
  577. if (false) {
  578. /** @type {?} */
  579. ExecuteContextMenuEvent.prototype.eventType;
  580. /** @type {?|undefined} */
  581. ExecuteContextMenuEvent.prototype.event;
  582. /** @type {?} */
  583. ExecuteContextMenuEvent.prototype.item;
  584. /** @type {?} */
  585. ExecuteContextMenuEvent.prototype.menuItem;
  586. }
  587. class ContextMenuService {
  588. /**
  589. * @param {?} overlay
  590. * @param {?} scrollStrategy
  591. */
  592. constructor(overlay, scrollStrategy) {
  593. this.overlay = overlay;
  594. this.scrollStrategy = scrollStrategy;
  595. this.isDestroyingLeafMenu = false;
  596. this.show = new Subject();
  597. this.triggerClose = new Subject();
  598. this.close = new Subject();
  599. this.overlays = [];
  600. this.fakeElement = {
  601. getBoundingClientRect: (/**
  602. * @return {?}
  603. */
  604. () => ({
  605. bottom: 0,
  606. height: 0,
  607. left: 0,
  608. right: 0,
  609. top: 0,
  610. width: 0,
  611. }))
  612. };
  613. }
  614. /**
  615. * @param {?} context
  616. * @return {?}
  617. */
  618. openContextMenu(context) {
  619. const { anchorElement, event, parentContextMenu } = context;
  620. if (!parentContextMenu) {
  621. /** @type {?} */
  622. const mouseEvent = (/** @type {?} */ (event));
  623. this.fakeElement.getBoundingClientRect = (/**
  624. * @return {?}
  625. */
  626. () => ({
  627. bottom: mouseEvent.clientY,
  628. height: 0,
  629. left: mouseEvent.clientX,
  630. right: mouseEvent.clientX,
  631. top: mouseEvent.clientY,
  632. width: 0,
  633. }));
  634. this.closeAllContextMenus({ eventType: 'cancel', event });
  635. /** @type {?} */
  636. const positionStrategy = this.overlay.position().connectedTo(new ElementRef(anchorElement || this.fakeElement), { originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' })
  637. .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' })
  638. .withFallbackPosition({ originX: 'end', originY: 'top' }, { overlayX: 'start', overlayY: 'top' })
  639. .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' })
  640. .withFallbackPosition({ originX: 'end', originY: 'center' }, { overlayX: 'start', overlayY: 'center' })
  641. .withFallbackPosition({ originX: 'start', originY: 'center' }, { overlayX: 'end', overlayY: 'center' });
  642. this.overlays = [this.overlay.create({
  643. positionStrategy,
  644. panelClass: 'ngx-contextmenu',
  645. scrollStrategy: this.scrollStrategy.close(),
  646. })];
  647. this.attachContextMenu(this.overlays[0], context);
  648. }
  649. else {
  650. /** @type {?} */
  651. const positionStrategy = this.overlay.position().connectedTo(new ElementRef(event ? event.target : anchorElement), { originX: 'end', originY: 'top' }, { overlayX: 'start', overlayY: 'top' })
  652. .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' })
  653. .withFallbackPosition({ originX: 'end', originY: 'bottom' }, { overlayX: 'start', overlayY: 'bottom' })
  654. .withFallbackPosition({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'bottom' });
  655. /** @type {?} */
  656. const newOverlay = this.overlay.create({
  657. positionStrategy,
  658. panelClass: 'ngx-contextmenu',
  659. scrollStrategy: this.scrollStrategy.close(),
  660. });
  661. this.destroySubMenus(parentContextMenu);
  662. this.overlays = this.overlays.concat(newOverlay);
  663. this.attachContextMenu(newOverlay, context);
  664. }
  665. }
  666. /**
  667. * @param {?} overlay
  668. * @param {?} context
  669. * @return {?}
  670. */
  671. attachContextMenu(overlay, context) {
  672. const { event, item, menuItems, menuClass } = context;
  673. /** @type {?} */
  674. const contextMenuContent = overlay.attach(new ComponentPortal(ContextMenuContentComponent));
  675. contextMenuContent.instance.event = event;
  676. contextMenuContent.instance.item = item;
  677. contextMenuContent.instance.menuItems = menuItems;
  678. contextMenuContent.instance.overlay = overlay;
  679. contextMenuContent.instance.isLeaf = true;
  680. contextMenuContent.instance.menuClass = menuClass;
  681. ((/** @type {?} */ (overlay))).contextMenu = contextMenuContent.instance;
  682. /** @type {?} */
  683. const subscriptions = new Subscription();
  684. subscriptions.add(contextMenuContent.instance.execute.asObservable()
  685. .subscribe((/**
  686. * @param {?} executeEvent
  687. * @return {?}
  688. */
  689. (executeEvent) => this.closeAllContextMenus(Object.assign({ eventType: 'execute' }, executeEvent)))));
  690. subscriptions.add(contextMenuContent.instance.closeAllMenus.asObservable()
  691. .subscribe((/**
  692. * @param {?} closeAllEvent
  693. * @return {?}
  694. */
  695. (closeAllEvent) => this.closeAllContextMenus(Object.assign({ eventType: 'cancel' }, closeAllEvent)))));
  696. subscriptions.add(contextMenuContent.instance.closeLeafMenu.asObservable()
  697. .subscribe((/**
  698. * @param {?} closeLeafMenuEvent
  699. * @return {?}
  700. */
  701. closeLeafMenuEvent => this.destroyLeafMenu(closeLeafMenuEvent))));
  702. subscriptions.add(contextMenuContent.instance.openSubMenu.asObservable()
  703. .subscribe((/**
  704. * @param {?} subMenuEvent
  705. * @return {?}
  706. */
  707. (subMenuEvent) => {
  708. this.destroySubMenus(contextMenuContent.instance);
  709. if (!subMenuEvent.contextMenu) {
  710. contextMenuContent.instance.isLeaf = true;
  711. return;
  712. }
  713. contextMenuContent.instance.isLeaf = false;
  714. this.show.next(subMenuEvent);
  715. })));
  716. contextMenuContent.onDestroy((/**
  717. * @return {?}
  718. */
  719. () => {
  720. menuItems.forEach((/**
  721. * @param {?} menuItem
  722. * @return {?}
  723. */
  724. menuItem => menuItem.isActive = false));
  725. subscriptions.unsubscribe();
  726. }));
  727. contextMenuContent.changeDetectorRef.detectChanges();
  728. }
  729. /**
  730. * @param {?} closeEvent
  731. * @return {?}
  732. */
  733. closeAllContextMenus(closeEvent) {
  734. if (this.overlays) {
  735. this.close.next(closeEvent);
  736. this.overlays.forEach((/**
  737. * @param {?} overlay
  738. * @param {?} index
  739. * @return {?}
  740. */
  741. (overlay, index) => {
  742. overlay.detach();
  743. overlay.dispose();
  744. }));
  745. }
  746. this.overlays = [];
  747. }
  748. /**
  749. * @return {?}
  750. */
  751. getLastAttachedOverlay() {
  752. /** @type {?} */
  753. let overlay = this.overlays[this.overlays.length - 1];
  754. while (this.overlays.length > 1 && overlay && !overlay.hasAttached()) {
  755. overlay.detach();
  756. overlay.dispose();
  757. this.overlays = this.overlays.slice(0, -1);
  758. overlay = this.overlays[this.overlays.length - 1];
  759. }
  760. return overlay;
  761. }
  762. /**
  763. * @param {?=} __0
  764. * @return {?}
  765. */
  766. destroyLeafMenu({ exceptRootMenu, event } = {}) {
  767. if (this.isDestroyingLeafMenu) {
  768. return;
  769. }
  770. this.isDestroyingLeafMenu = true;
  771. setTimeout((/**
  772. * @return {?}
  773. */
  774. () => {
  775. /** @type {?} */
  776. const overlay = this.getLastAttachedOverlay();
  777. if (this.overlays.length > 1 && overlay) {
  778. overlay.detach();
  779. overlay.dispose();
  780. }
  781. if (!exceptRootMenu && this.overlays.length > 0 && overlay) {
  782. this.close.next({ eventType: 'cancel', event });
  783. overlay.detach();
  784. overlay.dispose();
  785. }
  786. /** @type {?} */
  787. const newLeaf = this.getLastAttachedOverlay();
  788. if (newLeaf) {
  789. newLeaf.contextMenu.isLeaf = true;
  790. }
  791. this.isDestroyingLeafMenu = false;
  792. }));
  793. }
  794. /**
  795. * @param {?} contextMenu
  796. * @return {?}
  797. */
  798. destroySubMenus(contextMenu) {
  799. /** @type {?} */
  800. const overlay = contextMenu.overlay;
  801. /** @type {?} */
  802. const index = this.overlays.indexOf(overlay);
  803. this.overlays.slice(index + 1).forEach((/**
  804. * @param {?} subMenuOverlay
  805. * @return {?}
  806. */
  807. subMenuOverlay => {
  808. subMenuOverlay.detach();
  809. subMenuOverlay.dispose();
  810. }));
  811. }
  812. /**
  813. * @param {?} contextMenuContent
  814. * @return {?}
  815. */
  816. isLeafMenu(contextMenuContent) {
  817. /** @type {?} */
  818. const overlay = this.getLastAttachedOverlay();
  819. return contextMenuContent.overlay === overlay;
  820. }
  821. }
  822. ContextMenuService.decorators = [
  823. { type: Injectable }
  824. ];
  825. /** @nocollapse */
  826. ContextMenuService.ctorParameters = () => [
  827. { type: Overlay },
  828. { type: ScrollStrategyOptions }
  829. ];
  830. if (false) {
  831. /** @type {?} */
  832. ContextMenuService.prototype.isDestroyingLeafMenu;
  833. /** @type {?} */
  834. ContextMenuService.prototype.show;
  835. /** @type {?} */
  836. ContextMenuService.prototype.triggerClose;
  837. /** @type {?} */
  838. ContextMenuService.prototype.close;
  839. /**
  840. * @type {?}
  841. * @private
  842. */
  843. ContextMenuService.prototype.contextMenuContent;
  844. /**
  845. * @type {?}
  846. * @private
  847. */
  848. ContextMenuService.prototype.overlays;
  849. /**
  850. * @type {?}
  851. * @private
  852. */
  853. ContextMenuService.prototype.fakeElement;
  854. /**
  855. * @type {?}
  856. * @private
  857. */
  858. ContextMenuService.prototype.overlay;
  859. /**
  860. * @type {?}
  861. * @private
  862. */
  863. ContextMenuService.prototype.scrollStrategy;
  864. }
  865. /**
  866. * @fileoverview added by tsickle
  867. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  868. */
  869. /**
  870. * @record
  871. */
  872. function ILinkConfig$1() { }
  873. if (false) {
  874. /** @type {?} */
  875. ILinkConfig$1.prototype.click;
  876. /** @type {?|undefined} */
  877. ILinkConfig$1.prototype.enabled;
  878. /** @type {?} */
  879. ILinkConfig$1.prototype.html;
  880. }
  881. /**
  882. * @record
  883. */
  884. function MouseLocation() { }
  885. if (false) {
  886. /** @type {?|undefined} */
  887. MouseLocation.prototype.left;
  888. /** @type {?|undefined} */
  889. MouseLocation.prototype.marginLeft;
  890. /** @type {?|undefined} */
  891. MouseLocation.prototype.marginTop;
  892. /** @type {?|undefined} */
  893. MouseLocation.prototype.top;
  894. }
  895. class ContextMenuComponent {
  896. /**
  897. * @param {?} _contextMenuService
  898. * @param {?} changeDetector
  899. * @param {?} elementRef
  900. * @param {?} options
  901. */
  902. constructor(_contextMenuService, changeDetector, elementRef, options) {
  903. this._contextMenuService = _contextMenuService;
  904. this.changeDetector = changeDetector;
  905. this.elementRef = elementRef;
  906. this.options = options;
  907. this.menuClass = "";
  908. this.autoFocus = false;
  909. this.useBootstrap4 = false;
  910. this.disabled = false;
  911. this.close = new EventEmitter();
  912. this.open = new EventEmitter();
  913. this.visibleMenuItems = [];
  914. this.links = [];
  915. this.subscription = new Subscription();
  916. if (options) {
  917. this.autoFocus = options.autoFocus;
  918. this.useBootstrap4 = options.useBootstrap4;
  919. }
  920. this.subscription.add(_contextMenuService.show.subscribe((/**
  921. * @param {?} menuEvent
  922. * @return {?}
  923. */
  924. menuEvent => {
  925. this.onMenuEvent(menuEvent);
  926. })));
  927. }
  928. /**
  929. * @return {?}
  930. */
  931. ngOnDestroy() {
  932. this.subscription.unsubscribe();
  933. }
  934. /**
  935. * @param {?} menuEvent
  936. * @return {?}
  937. */
  938. onMenuEvent(menuEvent) {
  939. if (this.disabled) {
  940. return;
  941. }
  942. const { contextMenu, event, item } = menuEvent;
  943. if (contextMenu && contextMenu !== this) {
  944. return;
  945. }
  946. this.event = event;
  947. this.item = item;
  948. this.setVisibleMenuItems();
  949. this._contextMenuService.openContextMenu(Object.assign({}, menuEvent, { menuItems: this.visibleMenuItems, menuClass: this.menuClass }));
  950. this._contextMenuService.close.asObservable().pipe(first()).subscribe((/**
  951. * @param {?} closeEvent
  952. * @return {?}
  953. */
  954. closeEvent => this.close.emit(closeEvent)));
  955. this.open.next(menuEvent);
  956. }
  957. /**
  958. * @param {?} menuItem
  959. * @return {?}
  960. */
  961. isMenuItemVisible(menuItem) {
  962. return this.evaluateIfFunction(menuItem.visible);
  963. }
  964. /**
  965. * @return {?}
  966. */
  967. setVisibleMenuItems() {
  968. this.visibleMenuItems = this.menuItems.filter((/**
  969. * @param {?} menuItem
  970. * @return {?}
  971. */
  972. menuItem => this.isMenuItemVisible(menuItem)));
  973. }
  974. /**
  975. * @param {?} value
  976. * @return {?}
  977. */
  978. evaluateIfFunction(value) {
  979. if (value instanceof Function) {
  980. return value(this.item);
  981. }
  982. return value;
  983. }
  984. }
  985. ContextMenuComponent.decorators = [
  986. { type: Component, args: [{
  987. encapsulation: ViewEncapsulation.None,
  988. selector: 'context-menu',
  989. template: ` `,
  990. styles: [`
  991. .cdk-overlay-container {
  992. position: fixed;
  993. z-index: 1000;
  994. pointer-events: none;
  995. top: 0;
  996. left: 0;
  997. width: 100%;
  998. height: 100%;
  999. }
  1000. .ngx-contextmenu.cdk-overlay-pane {
  1001. position: absolute;
  1002. pointer-events: auto;
  1003. box-sizing: border-box;
  1004. }
  1005. `]
  1006. }] }
  1007. ];
  1008. /** @nocollapse */
  1009. ContextMenuComponent.ctorParameters = () => [
  1010. { type: ContextMenuService },
  1011. { type: ChangeDetectorRef },
  1012. { type: ElementRef },
  1013. { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [CONTEXT_MENU_OPTIONS,] }] }
  1014. ];
  1015. ContextMenuComponent.propDecorators = {
  1016. menuClass: [{ type: Input }],
  1017. autoFocus: [{ type: Input }],
  1018. useBootstrap4: [{ type: Input }],
  1019. disabled: [{ type: Input }],
  1020. close: [{ type: Output }],
  1021. open: [{ type: Output }],
  1022. menuItems: [{ type: ContentChildren, args: [ContextMenuItemDirective,] }],
  1023. menuElement: [{ type: ViewChild, args: ['menu', { static: false },] }]
  1024. };
  1025. if (false) {
  1026. /** @type {?} */
  1027. ContextMenuComponent.prototype.menuClass;
  1028. /** @type {?} */
  1029. ContextMenuComponent.prototype.autoFocus;
  1030. /** @type {?} */
  1031. ContextMenuComponent.prototype.useBootstrap4;
  1032. /** @type {?} */
  1033. ContextMenuComponent.prototype.disabled;
  1034. /** @type {?} */
  1035. ContextMenuComponent.prototype.close;
  1036. /** @type {?} */
  1037. ContextMenuComponent.prototype.open;
  1038. /** @type {?} */
  1039. ContextMenuComponent.prototype.menuItems;
  1040. /** @type {?} */
  1041. ContextMenuComponent.prototype.menuElement;
  1042. /** @type {?} */
  1043. ContextMenuComponent.prototype.visibleMenuItems;
  1044. /** @type {?} */
  1045. ContextMenuComponent.prototype.links;
  1046. /** @type {?} */
  1047. ContextMenuComponent.prototype.item;
  1048. /** @type {?} */
  1049. ContextMenuComponent.prototype.event;
  1050. /**
  1051. * @type {?}
  1052. * @private
  1053. */
  1054. ContextMenuComponent.prototype.subscription;
  1055. /**
  1056. * @type {?}
  1057. * @private
  1058. */
  1059. ContextMenuComponent.prototype._contextMenuService;
  1060. /**
  1061. * @type {?}
  1062. * @private
  1063. */
  1064. ContextMenuComponent.prototype.changeDetector;
  1065. /**
  1066. * @type {?}
  1067. * @private
  1068. */
  1069. ContextMenuComponent.prototype.elementRef;
  1070. /**
  1071. * @type {?}
  1072. * @private
  1073. */
  1074. ContextMenuComponent.prototype.options;
  1075. }
  1076. /**
  1077. * @fileoverview added by tsickle
  1078. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1079. */
  1080. class ContextMenuAttachDirective {
  1081. /**
  1082. * @param {?} contextMenuService
  1083. */
  1084. constructor(contextMenuService) {
  1085. this.contextMenuService = contextMenuService;
  1086. }
  1087. /**
  1088. * @param {?} event
  1089. * @return {?}
  1090. */
  1091. onContextMenu(event) {
  1092. if (!this.contextMenu.disabled) {
  1093. this.contextMenuService.show.next({
  1094. contextMenu: this.contextMenu,
  1095. event,
  1096. item: this.contextMenuSubject,
  1097. });
  1098. event.preventDefault();
  1099. event.stopPropagation();
  1100. }
  1101. }
  1102. }
  1103. ContextMenuAttachDirective.decorators = [
  1104. { type: Directive, args: [{
  1105. selector: '[contextMenu]',
  1106. },] }
  1107. ];
  1108. /** @nocollapse */
  1109. ContextMenuAttachDirective.ctorParameters = () => [
  1110. { type: ContextMenuService }
  1111. ];
  1112. ContextMenuAttachDirective.propDecorators = {
  1113. contextMenuSubject: [{ type: Input }],
  1114. contextMenu: [{ type: Input }],
  1115. onContextMenu: [{ type: HostListener, args: ['contextmenu', ['$event'],] }]
  1116. };
  1117. if (false) {
  1118. /** @type {?} */
  1119. ContextMenuAttachDirective.prototype.contextMenuSubject;
  1120. /** @type {?} */
  1121. ContextMenuAttachDirective.prototype.contextMenu;
  1122. /**
  1123. * @type {?}
  1124. * @private
  1125. */
  1126. ContextMenuAttachDirective.prototype.contextMenuService;
  1127. }
  1128. /**
  1129. * @fileoverview added by tsickle
  1130. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1131. */
  1132. class ContextMenuModule {
  1133. /**
  1134. * @param {?=} options
  1135. * @return {?}
  1136. */
  1137. static forRoot(options) {
  1138. return {
  1139. ngModule: ContextMenuModule,
  1140. providers: [
  1141. ContextMenuService,
  1142. {
  1143. provide: CONTEXT_MENU_OPTIONS,
  1144. useValue: options,
  1145. },
  1146. ],
  1147. };
  1148. }
  1149. }
  1150. ContextMenuModule.decorators = [
  1151. { type: NgModule, args: [{
  1152. declarations: [
  1153. ContextMenuAttachDirective,
  1154. ContextMenuComponent,
  1155. ContextMenuContentComponent,
  1156. ContextMenuItemDirective,
  1157. ],
  1158. entryComponents: [
  1159. ContextMenuContentComponent,
  1160. ],
  1161. exports: [
  1162. ContextMenuAttachDirective,
  1163. ContextMenuComponent,
  1164. ContextMenuItemDirective,
  1165. ],
  1166. imports: [
  1167. CommonModule,
  1168. OverlayModule,
  1169. ],
  1170. },] }
  1171. ];
  1172. /**
  1173. * @fileoverview added by tsickle
  1174. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1175. */
  1176. /**
  1177. * @record
  1178. */
  1179. function IContextMenuOptions() { }
  1180. if (false) {
  1181. /** @type {?|undefined} */
  1182. IContextMenuOptions.prototype.useBootstrap4;
  1183. /** @type {?|undefined} */
  1184. IContextMenuOptions.prototype.autoFocus;
  1185. }
  1186. /**
  1187. * @fileoverview added by tsickle
  1188. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1189. */
  1190. /**
  1191. * @fileoverview added by tsickle
  1192. * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1193. */
  1194. export { ContextMenuComponent, ContextMenuModule, ContextMenuService, ContextMenuAttachDirective as ɵa, ContextMenuItemDirective as ɵb, CONTEXT_MENU_OPTIONS as ɵc, ContextMenuContentComponent as ɵd };
  1195. //# sourceMappingURL=ngx-contextmenu.js.map