diff --git a/system-apps/system-settings-preferences/webClient/package-lock.json b/system-apps/system-settings-preferences/webClient/package-lock.json index 54831f7d9..f99127946 100644 --- a/system-apps/system-settings-preferences/webClient/package-lock.json +++ b/system-apps/system-settings-preferences/webClient/package-lock.json @@ -796,8 +796,8 @@ } }, "@zlux/widgets": { - "version": "git+ssh://git@github.com/zowe/zlux-widgets.git#4b988b7614cb3c4cd7b5cae264d62cb270bb3bd1", - "from": "git+ssh://git@github.com/zowe/zlux-widgets.git", + "version": "git+https://github.com/zowe/zlux-widgets.git#4b988b7614cb3c4cd7b5cae264d62cb270bb3bd1", + "from": "git+https://github.com/zowe/zlux-widgets.git", "dev": true, "requires": { "@angular/animations": "~6.0.9", diff --git a/system-apps/system-settings-preferences/webClient/package.json b/system-apps/system-settings-preferences/webClient/package.json index 58db93732..9603a8683 100644 --- a/system-apps/system-settings-preferences/webClient/package.json +++ b/system-apps/system-settings-preferences/webClient/package.json @@ -24,7 +24,7 @@ "@angular/platform-browser-dynamic": "~6.0.9", "@angular/router": "~6.0.9", "angular-l10n": "~5.2.0", - "@zlux/widgets": "git+ssh://git@github.com:zowe/zlux-widgets.git", + "@zlux/widgets": "git+https://github.com:zowe/zlux-widgets.git", "angular2-template-loader": "~0.6.2", "codelyzer": "~4.4.2", "copy-webpack-plugin": "~4.5.2", diff --git a/virtual-desktop/package-lock.json b/virtual-desktop/package-lock.json index f45db32b8..0f2129168 100644 --- a/virtual-desktop/package-lock.json +++ b/virtual-desktop/package-lock.json @@ -335,8 +335,8 @@ "dev": true }, "@zlux/widgets": { - "version": "git+ssh://git@github.com/zowe/zlux-widgets.git#da92205dedda0ed45ab69cfa098808bedd765221", - "from": "git+ssh://git@github.com/zowe/zlux-widgets.git", + "version": "git+https://github.com/zowe/zlux-widgets.git#da92205dedda0ed45ab69cfa098808bedd765221", + "from": "git+https://github.com/zowe/zlux-widgets.git", "dev": true, "requires": { "@angular/animations": "~6.0.9", diff --git a/virtual-desktop/package.json b/virtual-desktop/package.json index d005c7e39..b06b9c603 100644 --- a/virtual-desktop/package.json +++ b/virtual-desktop/package.json @@ -24,7 +24,7 @@ "@angular/platform-browser-dynamic": "~6.0.9", "@angular/router": "~6.0.9", "@types/html2canvas": "~0.0.35", - "@zlux/widgets": "git+ssh://git@github.com/zowe/zlux-widgets.git", + "@zlux/widgets": "git+https://github.com/zowe/zlux-widgets.git", "angular-l10n": "~5.2.0", "angular2-template-loader": "~0.6.2", "bootstrap": "~4.3.1", diff --git a/virtual-desktop/src/app/authentication-manager/authentication-manager.service.ts b/virtual-desktop/src/app/authentication-manager/authentication-manager.service.ts index eb1654e2e..a0732ffe5 100644 --- a/virtual-desktop/src/app/authentication-manager/authentication-manager.service.ts +++ b/virtual-desktop/src/app/authentication-manager/authentication-manager.service.ts @@ -133,10 +133,12 @@ export class AuthenticationManager { //requestLogin() used to exist here but it was counter-intuitive in behavior to requestLogout. //This was not documented and therefore has been removed to prevent misuse and confusion. - private doLoggoutInner(reason: LoginScreenChangeReason): void { + private doLogoutInner(reason: LoginScreenChangeReason): void { const windowManager: MVDWindowManagement.WindowManagerServiceInterface = this.injector.get(MVDWindowManagement.Tokens.WindowManagerToken); - windowManager.closeAllWindows(); + if (reason == LoginScreenChangeReason.UserLogout) { + windowManager.closeAllWindows(); + } this.performLogout().subscribe( response => { this.loginScreenVisibilityChanged.emit(reason); @@ -149,7 +151,7 @@ export class AuthenticationManager { } requestLogout(): void { - this.doLoggoutInner(LoginScreenChangeReason.UserLogout); + this.doLogoutInner(LoginScreenChangeReason.UserLogout); } private performPostLoginActions(): Observable { @@ -216,7 +218,7 @@ export class AuthenticationManager { expirationInMS: logoutAfterWarnTimer}); this.expirationWarning = setTimeout(()=> { this.log.warn(`Session timeout reached. Clearing desktop for new login.`); - this.doLoggoutInner(LoginScreenChangeReason.SessionExpired); + this.doLogoutInner(LoginScreenChangeReason.SessionExpired); },logoutAfterWarnTimer); },warnTimer); this.log.debug(`Set session timeout watcher to notify ${warnTimer}ms before expiration`); diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css index d017b544f..022c17d29 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css @@ -10,6 +10,14 @@ Copyright Contributors to the Zowe Project. */ +li.disabled { + pointer-events: none; +} + +.menuitem-content.disabled { + opacity: 0.3; +} + .context-menu { position: absolute; z-index: 20000; @@ -21,8 +29,17 @@ background-color:#a6a6a6; } +.context-menu .menu-item.top { + border-top-width: 1px; + border-radius: 4px 4px 0 0; +} + +.context-menu .menu-item.bottom { + border-bottom-width: 1px; + border-radius: 0 0 4px 4px; +} + .context-menu>ul { - background-color:#eaeaea; list-style: none; padding: 0; margin: 0; @@ -38,12 +55,14 @@ font-weight: 400; white-space:nowrap; color:#404040; - border-bottom: 1px solid #ccc; + border-style: solid; + border-width: 0 1px 1px 1px; + border-color: #ccc; cursor: pointer; position:relative; } -.context-menu .multi-menu-item>.menu-expand-caret { +.context-menu .multi-menu-item .menu-expand-caret { position: absolute; margin: auto; top: 0; @@ -56,6 +75,15 @@ border-left: 5px solid #666; } +.context-menu .multi-menu-item .menu-expand-caret.propagateChildLeft { + left: 5px; + margin: auto; + margin-left: 0; + margin-right: 0; + border-left: none; + border-right: 5px solid #666; +} + .context-menu .multi-menu-item.active>.menu-expand-caret { border-left: 5px solid #fff; } @@ -65,22 +93,56 @@ } .context-menu .multi-menu-item>.submenu { - width: 0; - height: 0; - right: 0; + right: 0px; top: 0; position: absolute; display: none; + z-index: 1; +} + +.context-menu .multi-menu-item>.submenu.propagateChildLeft { + left: 0px; } -.context-menu .multi-menu-item>.submenu>com-rs-mvd-context-menu { - margin: -10px; - padding: 10px; +.context-menu .multi-menu-item>.submenu>com-rs-mvd-context-menu>.context-menu { + left: 0; + top: 0; +} + +.context-menu .multi-menu-item>.submenu>com-rs-mvd-context-menu>.context-menu.propagateChildLeft>ul { + position: absolute; + right: 100%; } +.menuitem-content { + display: flex; + justify-content: space-between; +} + +.menuitem-content-left { + display: flex; + margin-right: 15px; +} -.context-menu .menu-item:last-child { - border-bottom: none; +.icon { + width: 19px; + height: 19px; + margin-right: 5px; + background-size: contain; +} + +.icon.propagateChildLeft { + margin-left: 25px; +} + +.shortcut { + margin-left: 15px; + right: 5px; + font-weight: bold; +} + +.hide-icon { + display: none; } /* diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html index 6b5b59723..f8889f845 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html @@ -10,21 +10,91 @@ Copyright Contributors to the Zowe Project. --> -
+
    - + - - diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts index 6b9173005..06b648bf4 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts @@ -10,94 +10,352 @@ Copyright Contributors to the Zowe Project. */ -import { Input, Component, EventEmitter, Output, HostListener, ElementRef, ViewChild } from '@angular/core'; +import { + Input, + Component, + EventEmitter, + Output, + HostListener, + ElementRef, + ViewChild, + ViewChildren, + AfterViewInit, + QueryList +} from '@angular/core'; import { ContextMenuItem } from 'pluginlib/inject-resources'; - -import { BaseLogger } from 'virtual-desktop-logger'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'com-rs-mvd-context-menu', templateUrl: './context-menu.component.html', - styleUrls: ['./context-menu.component.css'] + styleUrls: ['./context-menu.component.css'], }) -export class ContextMenuComponent { +export class ContextMenuComponent implements AfterViewInit { hovering: ContextMenuItem; newX: number; newY: number; + menuHeight: number; + menuWidth: number; + activeIndex: number; // Index of active item in menu. -1 if none active. + _isChildMenu: boolean; // True if menu is child of another menu, false otherwise. + _isParentActive: boolean; // True if parent item is active (ie. menu should be displayed), false otherwise. + isNavigable: boolean; // True if menu is keyboard-navigable, false otherwise. Only one menu can be navigable at a time. + _propagateChildLeft: boolean; // True if child menus will appear to the left of their parent. + _parentText: string; // Text of parent menu item + children: { [key: string]: any }; // Array of child elements @ViewChild('contextmenu') set menu(contextmenu: any) { contextmenu.nativeElement.style.opacity = 0; - setTimeout(() => { + setTimeout(() => { let menuHeight = contextmenu.nativeElement.clientHeight; let menuWidth = contextmenu.nativeElement.clientWidth; - this.newY = this.validateY(this.newY, menuHeight); - this.newX = this.validateX(this.newX, menuWidth); + // Apply specific styling to parent menu, and to parent menu that will propagate children to the left. + if (!this._isChildMenu) { + this.newY = this.validateY(this.newY, menuHeight); + this.newX = this.validateX(this.newX, menuWidth); + if (!this._propagateChildLeft) { + this.newX = this.newX + 3; + } + } contextmenu.nativeElement.style.opacity = 1; + this.menuHeight = menuHeight; + this.menuWidth = menuWidth; + this.activeIndex = -1; // By default, no item is selected. + this._isParentActive = false; // Since by default no item is selected, all parent items are inactive. + this.isNavigable = !this._isChildMenu; // By default, surface-depth menu is navigable, and all others are not. }, 0); } + @ViewChildren(ContextMenuComponent) _children: QueryList; + + // Set initial horizontal position of menu @Input() set xPos(xPos: number) { - this.newX = xPos; + this.newX = xPos + 2; }; + // Set initial vertical position of menu @Input() set yPos(yPos: number) { - this.newY = yPos; + this.newY = yPos + 4; }; - + + // Initialize array of menu items @Input() menuItems: ContextMenuItem[]; + + // Declare value of _isChildMenu based on input isChildMenu + // This is set on every child menu + @Input() set isChildMenu(isChildMenu: boolean) { + this._isChildMenu = isChildMenu; + }; + + // Set values of _isParentActive and activeIndex based on input isParentActive + // _isParentActive set to true by parent menu if activeIndex of parent menu corresponds to this child menu + // activeIndex set to existing value of activeIndex if parent is active (ie. child menu visible), -1 otherwise, so that any active item in child menu reset when child meny disappears, thus none selected when it reappears. + @Input() set isParentActive(isParentActive: boolean) { + this._isParentActive = isParentActive; // Set _isParentActive based on input + this.activeIndex = isParentActive ? this.activeIndex : -1; // If parent not active, reset this.activeIndex to -1 + }; + + // Set value of isNavigable based on input parentNavigable. + // If parent menu is navigable, child menu cannot be. + @Input() set parentNavigable(parentNavigable: boolean) { + if (parentNavigable) { + this.isNavigable = false; + } + }; + + // Set value of parentText based on input + @Input() set parentText(parentText: string) { + this._parentText = parentText; + }; + + // If parent propagated child left, child should also propagate its child left. + @Input() set propagateChildLeft(propagateChildLeft: boolean) { + this._propagateChildLeft = propagateChildLeft; + } @Output() complete: EventEmitter; - private readonly logger: ZLUX.ComponentLogger = BaseLogger; + @Output() makeParentUnnavigable = new EventEmitter(); + + @Output() makeParentNavigable = new EventEmitter(); + + @Output() sendRefToParent = new EventEmitter(); constructor( private elementRef: ElementRef, - //private logger: DesktopLogger - ) { + private sanitizer:DomSanitizer + ) { this.complete = new EventEmitter(); } + // Make menu navigable, and emit makeParentUnnavigable event. + // Emitting makeParentUnnavigable event will trigger any parent menu to call makeUnnavigable(). + makeNavigable = () => { + // Make navigable + this.isNavigable = true; + // If menu has parent, un-focus parent + if (this._isChildMenu) { + this.makeParentUnnavigable.emit() + } + } + + // Make menu unnavigable, and emit makeParentUnnavigable event. + // Emitting makeParentUnnavigable event will recursively make all subsequent parent menus call makeUnnavigable(). + makeUnnavigable() { + // Make un-navigable + this.isNavigable = false; + // If menu has parent, un-focus parent + if (this._isChildMenu) { + this.makeParentUnnavigable.emit() + } + } + + // After child elements retrieved from DOM, convert from array to object for faster access + ngAfterViewInit() { + this.elementRef.nativeElement.focus() + this.children = this._children.toArray().reduce((acc, curr) => ( + { + ...acc, + [curr._parentText]: curr + } + ), {}) + } + + // Recalculate horizontal position at which to spawn menu, correcting for proximity to edges of screen validateX(xPos: number, menuWidth: number): number { + let menuLeft = xPos; let menuRight = xPos + menuWidth; - let screenWidth = window.innerWidth - 10; /* Gave a 10 pixel buffer so isn't right on the edge */ + let screenWidth = window.innerWidth - 10; /* Give a 10 pixel buffer so isn't right on the edge */ + // If initial position or menu too close to right edge of screen, shift left until right edge of menu is at least 10px from right edge of screen if (menuRight > screenWidth) { - let difference = menuRight - screenWidth; - xPos = xPos - difference + this._propagateChildLeft = true; + let overshoot = menuLeft - screenWidth; + xPos = xPos - (menuWidth + (overshoot > 0 ? overshoot : 0)) + } + // If initial position or menu too close to left edge of screen, shift right until left edge of menu is at least 10px from left edge of screen + if (menuLeft < 10) { + let difference = 10 - menuLeft; + xPos = xPos + difference; } return xPos; } + // Recalculate vertical position at which to spawn menu, correcting for proximity to edges of screen validateY(yPos: number, menuHeight: number): number { - let menuBottom = menuHeight + yPos; - let screenHeight = window.innerHeight - 10; /* Gave a 10 pixel buffer so isn't right on the edge */ + let menuTop = yPos; + let menuBottom = yPos + menuHeight; + let screenHeight = window.innerHeight - 10; /* Give a 10 pixel buffer so isn't right on the edge */ + // If initial position of menu too close to bottom of screen, shift left until bottom edge of menu is at least 10px from bottom of screen if (menuBottom > screenHeight) { - let difference = menuBottom - screenHeight; - yPos = yPos - difference; + let overshoot = menuTop - screenHeight; + yPos = yPos - (menuHeight + (overshoot > 0 ? overshoot : 0)); + } + // If initial position of menu too close to top of screen, shift left until top edge of menu is at least 10px from top of screen + if (menuTop < 10) { + let difference = 10 - menuTop; + yPos = yPos + difference; } return yPos; } + // Triggered when menu item clicked. + // If item has associated action and is not disabled, execute action itemClicked(menuItem: ContextMenuItem): void { - this.logger.info(menuItem.text); - if (menuItem.action) { + if (menuItem.action && !menuItem.disabled) { menuItem.action(); } - - this.closeContextMenu(); + if (!menuItem.preventCloseMenu) { + this.closeContextMenu(); + } } - closeContextMenu(): void { + // Close context menu + closeContextMenu = (): void => { this.complete.emit(); } + // Set active index + setActiveIndex = (index: number) => { + this.activeIndex = index; + } + + computeShortcutString = (item: ContextMenuItem) => { + let shortcutString = ""; + if (item.shortcutProps) { + let props = item.shortcutProps; + if (props.altKey) { + shortcutString += "Alt "; + } + if (props.ctrlKey) { + shortcutString += "Ctrl "; + } + if (props.metaKey) { + shortcutString += "Cmd "; + } + if (props.shiftKey) { + shortcutString += "Shift "; + } + } + if (item.shortcutText) { + shortcutString += item.shortcutText; + } + return shortcutString; + } + + generateIcon = (base64Image: string) => { + return this.sanitizer.bypassSecurityTrustResourceUrl(base64Image); + } + + shortcutProps?: { + "code": string; + "altKey": boolean; + "ctrlKey": boolean; + "metaKey": boolean; + "shiftKey": boolean; + }; + + // When area of screen outside of menu is clicked, close menu @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent): void { - if (event && !this.elementRef.nativeElement.contains(event.target)) { + if (event && !this.elementRef.nativeElement.contains(event.target) && !this._isChildMenu) { this.closeContextMenu(); } } + + // When window resized, close menu + @HostListener('window:resize') + onResize(): void { + this.closeContextMenu(); + } + + // Handle key presses that occur while context menu is open + @HostListener('window:keydown', ['$event']) + onWindowKeyDown(event: KeyboardEvent) { + // console.log(event) + let mod = (x: number, n: number): number => ((x == -2 ? -1 : x % n) + n) % n; + let hasChildren; + if (this.activeIndex > -1) { + hasChildren = this.menuItems && this.menuItems[this.activeIndex].children ? true : false; + } + // Execute action of any visible item whose shortcut has been pressed + if (this.elementRef.nativeElement.firstChild.scrollWidth > 0) { + this.menuItems.forEach(item => { + if (!item.disabled && item.shortcutProps) { + // If all properties of shortcut associated with given item match those of key(s) clicked, execute action associated with item. + if (Object.keys(item.shortcutProps).every(shortcutProp => ((event)[shortcutProp] === (item.shortcutProps)[shortcutProp]))){ + event.preventDefault(); + item.action(); + if (!item.preventCloseMenu) { + this.closeContextMenu(); + } + } + } + }) + } + // Perform navigation of navigable menu + if (this.isNavigable) { + let newIndex; + switch (event.key){ + case 'ArrowUp': + newIndex = mod(this.activeIndex - 1, this.menuItems.length) + while (this.menuItems[newIndex].disabled) { + newIndex = mod(newIndex - 1, this.menuItems.length) + } + this.activeIndex = newIndex; + break; + case 'ArrowDown': + newIndex = mod(this.activeIndex + 1, this.menuItems.length) + while (this.menuItems[newIndex].disabled) { + newIndex = mod(newIndex + 1, this.menuItems.length) + } + this.activeIndex = newIndex; + break; + case 'ArrowRight': + if (this._propagateChildLeft) { + if (this.isNavigable && this._isChildMenu) { + this.makeParentNavigable.emit(); + this.setActiveIndex(-1); + } + } else { + if (hasChildren) { + this.makeUnnavigable(); // Make un-navigable because navigability will transfer to child menu + setTimeout(() => { + this.children[this.menuItems[this.activeIndex].text].makeNavigable(); + this.children[this.menuItems[this.activeIndex].text].setActiveIndex(0); + }, 0) + } + } + break; + case 'ArrowLeft': + if (this._propagateChildLeft) { + if (hasChildren) { + this.makeUnnavigable(); // Make un-navigable because navigability will transfer to child menu + setTimeout(() => { + this.children[this.menuItems[this.activeIndex].text].makeNavigable(); + this.children[this.menuItems[this.activeIndex].text].setActiveIndex(0); + }, 0) + } + } else { + if (this.isNavigable && this._isChildMenu) { + this.makeParentNavigable.emit(); + this.setActiveIndex(-1); + } + } + break; + case 'Enter': + let item = this.menuItems[this.activeIndex]; + if (item.action) { + item.action(); + } + if (!item.preventCloseMenu) { + this.closeContextMenu(); + } + break; + } + } + } } diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar/launchbar.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar/launchbar.component.ts index 869590ac5..9d2cc57bc 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar/launchbar.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar/launchbar.component.ts @@ -146,7 +146,7 @@ export class LaunchbarComponent { onRightClick(event: MouseEvent, item: LaunchbarItem): boolean { let menuItems: ContextMenuItem[] = generateInstanceActions(item, this.pluginsDataService, this.translation, this.applicationManager, this.windowManager); - this.windowManager.contextMenuRequested.next({xPos: event.clientX, yPos: event.clientY - 60, items: menuItems}); + this.windowManager.contextMenuRequested.next({xPos: event.clientX, yPos: event.clientY, items: menuItems}); return false; } diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/context-utils.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/context-utils.ts index 121baf5e5..d0b8c58a9 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/context-utils.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/context-utils.ts @@ -112,4 +112,4 @@ export function generateInstanceActions(item: LaunchbarItem, ] } return menuItems; -} +} \ No newline at end of file diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/shared/window-manager.service.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/shared/window-manager.service.ts index 1c238c3da..77b312f58 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/shared/window-manager.service.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/shared/window-manager.service.ts @@ -673,14 +673,8 @@ export class WindowManagerService implements MVDWindowManagement.WindowManagerSe const windowPos = desktopWindow.windowState.position; const newX = isAbsolutePos ? x : windowPos.left + x; const newY = isAbsolutePos ? y : windowPos.top + y + WindowManagerService.WINDOW_HEADER_HEIGHT; - if ((newX >= windowPos.left && newX <= (windowPos.left+windowPos.width)) - && (newY >= windowPos.top && newY <= (windowPos.top+windowPos.height))) { - this.contextMenuRequested.next({xPos: newX, yPos: newY, items: items}); - return true; - } else { - this.logger.warn(`Rejecting context menu due to invalid coord ${newX},${newY} for app at ${windowPos.left},${windowPos.top} w=${windowPos.width}, h=${windowPos.height}`); - return false; - } + this.contextMenuRequested.next({xPos: newX, yPos: newY, items: items}); + return true; } setDesktopTitle(title?:String) { diff --git a/virtual-desktop/src/pluginlib/inject-resources.ts b/virtual-desktop/src/pluginlib/inject-resources.ts index 6a5a6b754..7d5b8ddfc 100644 --- a/virtual-desktop/src/pluginlib/inject-resources.ts +++ b/virtual-desktop/src/pluginlib/inject-resources.ts @@ -70,8 +70,19 @@ export interface Angular2PluginEmbedActions { export interface ContextMenuItem { text: string; + icon?: string; + shortcutText?: string; + shortcutProps?: { + "code": string; + "altKey": boolean; + "ctrlKey": boolean; + "metaKey": boolean; + "shiftKey": boolean; + }; action: () => void; children?: ContextMenuItem[]; + disabled?: boolean; + preventCloseMenu?: boolean; } export interface Angular2L10nConfig { @@ -79,7 +90,6 @@ export interface Angular2L10nConfig { readonly providers: any[]; } - /* This program and the accompanying materials are made available under the terms of the Eclipse Public License v2.0 which accompanies