diff --git a/virtual-desktop/src/app/authentication-manager/login/login.component.html b/virtual-desktop/src/app/authentication-manager/login/login.component.html index d76765937..128638fa7 100644 --- a/virtual-desktop/src/app/authentication-manager/login/login.component.html +++ b/virtual-desktop/src/app/authentication-manager/login/login.component.html @@ -31,14 +31,14 @@ -

{{errorMessage || " "}}

+

{{this.translation.translate(errorMessage)}}

-
-
+
{{translation.translate("Refresh Applications")}}
-
-
- -

{{item.label}}

-
+
+
+ +

{{item.label}}

+
diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar-menu/launchbar-menu.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar-menu/launchbar-menu.component.ts index cd65570db..3ea7becc6 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar-menu/launchbar-menu.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/launchbar-menu/launchbar-menu.component.ts @@ -10,7 +10,7 @@ Copyright Contributors to the Zowe Project. */ -import { Component, ElementRef, HostListener, Input, Output, EventEmitter, Injector } from '@angular/core'; +import { Component, ElementRef, HostListener, Input, Output, EventEmitter, Injector, ViewChild } from '@angular/core'; import { Subject } from 'rxjs/Subject'; import { PluginsDataService } from '../../services/plugins-data.service'; import { LaunchbarItem } from '../shared/launchbar-item'; @@ -37,6 +37,9 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ this.filterMenuItems(); } + @ViewChild('searchapp') searchAppInputRef: ElementRef; + @ViewChild('menudiv') menuDivRef: ElementRef; + @Output() refreshClicked: EventEmitter; @Output() itemClicked: EventEmitter; @Output() menuStateChanged: EventEmitter; @@ -47,6 +50,8 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ propertyWindowPluginDef : DesktopPluginDefinitionImpl; public authenticationManager : MVDHosting.AuthenticationManagerInterface; public appFilter:string=""; + public activeIndex:number; + private isContextMenuPresent:boolean; constructor( private elementRef: ElementRef, @@ -55,7 +60,7 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ private injector: Injector, private translation: TranslationService, private desktopComponent: DesktopComponent, - private appKeyboard: KeybindingService + private appKeyboard: KeybindingService, ) { // Workaround for AoT problem with namespaces (see angular/angular#15613) this.applicationManager = this.injector.get(MVDHosting.Tokens.ApplicationManagerToken); @@ -65,6 +70,9 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ this.refreshClicked = new EventEmitter(); this.menuStateChanged = new EventEmitter(); this.authenticationManager.registerPostLoginAction(this); + + this.activeIndex = 0; + this.isContextMenuPresent = false; } onLogin(plugins:any): boolean { @@ -106,16 +114,31 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ activeToggle(): void { this.isActive = !this.isActive; + // gain focus and clear on toggle when active + if(this.isActive) { + setTimeout(() => { + this.searchAppInputRef.nativeElement.focus(); + },0); + } this.emitState(); } + setSearchFocus() { + this.searchAppInputRef.nativeElement.focus(); + } + refresh(): void { + this.resetMenu(); + this.refreshClicked.emit(); + } + + resetMenu(): void { this.appFilter = ''; this.displayItems = this._menuItems; - this.refreshClicked.emit(); } filterMenuItems(): void { + this.activeIndex = 0; if (this.appFilter) { let filter = this.appFilter.toLowerCase(); this.displayItems = this._menuItems.filter((item)=> { @@ -146,9 +169,108 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent): void { if (this.isActive && event && !this.elementRef.nativeElement.contains(event.target)) { - this.isActive = false; - this.emitState(); + this.activeToggle(); + } + } + + + + @HostListener('keydown', ['$event']) + onKeyDown(event: KeyboardEvent) { + if(this.isContextMenuInDom()) { + this.keepSearchCursor(); + return; } + + // eating one render cycle + if(this.isContextMenuPresent) { + this.isContextMenuPresent = false; + return; + } + + if(!this.isSearchFocus()) return; + + switch(event.which) { + case KeyCode.ESCAPE: { + // if(this.appFilter>'') { + // this.resetMenu(); + // } else { + this.activeToggle(); + //} + break; + } + case KeyCode.ENTER: { + if(this.activeIndex0) { + this.activeIndex--; + } else { + this.activeIndex=0; + } + this.scrollToActiveMenuItem(); + break; + } + case KeyCode.DOWN_ARROW: { + if(this.activeIndex < this.displayItems.length-1) { + this.activeIndex++; + } + this.scrollToActiveMenuItem(); + break; + } + } + } + + private getActiveMenuItem():any { + return this.menuDivRef.nativeElement.querySelectorAll('.launch-widget-row')[this.activeIndex]; + } + + private getContextMenu(item:LaunchbarItem):void { + const elm = this.getActiveMenuItem(); + if(elm) { + const pos = this.getElementPosition(elm); + let menuItems: ContextMenuItem[] = generateInstanceActions(item, this.pluginsDataService, this.translation, this.applicationManager, this.windowManager); + this.windowManager.contextMenuRequested.next({ xPos: pos.x, yPos: pos.y - 20, items: menuItems }); + this.isContextMenuPresent = true; + } + } + + private getElementPosition(elm: any): any { + let x = window.scrollX + elm.getBoundingClientRect().left + 40; + let y = window.scrollY + elm.getBoundingClientRect().top + 50; + return {x:x, y:y}; + } + + private scrollToActiveMenuItem(): void { + const elm = this.getActiveMenuItem(); + if(elm) { + elm.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' }); + } + } + + private keepSearchCursor(): void { + const el = this.searchAppInputRef.nativeElement; + if (typeof el.selectionStart == "number") { + el.selectionStart = el.selectionEnd = el.value.length; + } + } + + private isContextMenuInDom(): boolean { + return document.querySelector('com-rs-mvd-context-menu') !== null; + } + + private isSearchFocus(): boolean { + return document.activeElement === this.searchAppInputRef.nativeElement; } private emitState(): void { @@ -158,6 +280,7 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{ 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 - 20, items: menuItems }); + this.isContextMenuPresent = true; return false; } diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.html b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.html index 9c2ba446d..151120508 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.html +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.html @@ -20,7 +20,7 @@
- +
diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.ts index c4a035fc4..339f83c1a 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/launchbar/shared/snackbar/snackbar.component.ts @@ -12,6 +12,7 @@ import { Component, Inject } from '@angular/core'; import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material'; +import { TranslationService } from 'angular-l10n'; @Component({ selector: 'app-snackbar', @@ -20,6 +21,7 @@ import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material'; }) export class SnackbarComponent { constructor( + public translation: TranslationService, public snackBarRef: MatSnackBarRef, @Inject(MAT_SNACK_BAR_DATA) public data: any ) { } diff --git a/virtual-desktop/src/assets/i18n/messages.de.json b/virtual-desktop/src/assets/i18n/messages.de.json index fae41fd05..276feea49 100644 --- a/virtual-desktop/src/assets/i18n/messages.de.json +++ b/virtual-desktop/src/assets/i18n/messages.de.json @@ -1,8 +1,8 @@ { "Account Password": "Konto Passwort", - "Back": "Zurück", "Apply": "Hinzufügen", "AuthenticationFailed": "Authentifizierung für {{numTypes}} Arten fehlgeschlagen. Arten sind: {{types}}", + "Back": "Zurück", "BringToFront": "In den Vordergrund holen", "Change Password": "Passwort ändern", "Chinese": "Chinesisch", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "Damit die Änderung der Sprache übernommen wird, ist ein Neustart von Zowe erforderlich.", "French": "Französisch", "German": "Deutsch", + "Incorrect password or account is locked. Please contact your administrator.": "Falsches Passwort oder falsches Konto ist gesperrt. Bitte wenden Sie sich an Ihren Administrator.", "Japanese": "Japanisch", "Language Changes": "Sprache ändern", "Language Selected": "Ausgewählte Sprache", "Languages": "Sprachen", + "Multiple password reset is not available.": "Das Zurücksetzen mehrerer Passwörter ist nicht verfügbar.", + "New passwords do not match. Please try again.": "Neue Passwörter stimmen nicht überein. Bitte versuche es erneut.", + "No new password provided": "Kein neues Passwort angegeben.", + "No password provided": "Kein Passwort angegeben", + "No password reset service available.": "Kein Dienst zum Zurücksetzen des Passworts verfügbar.", + "No username provided": "Kein Benutzername angegeben", "Open New": "Neu Öffnen", "Open": "Öffnen", "Open in New Browser Tab": "In neuem Browser Tab öffnen", @@ -38,6 +45,8 @@ "Session Expiring Soon": "Sitzung läuft bald ab", "Session Renewal Error": "Fehler beim Erneuern der Sitzung", "Session will expire unless renewed.": "Sitzung wird in {{expirationInMS}} Sekunden ablaufen, falls nicht erneuert.", + "The new password format is incorrect. Please try again.": "Das neue Passwortformat ist falsch. Bitte versuche es erneut.", + "Username or password is incorrect. Please try again.": "Benutzername oder Passwort ist falsch. Bitte versuche es erneut.", "UsernameRequired": "Benutzername erforderlich", "Would you like to restart the desktop?": "Möchten Sie den Desktop neu starten?", "UnpinFromTaskbar": "Anwendung von Taskbar lösen" diff --git a/virtual-desktop/src/assets/i18n/messages.en.json b/virtual-desktop/src/assets/i18n/messages.en.json index 1b979bd56..2cb2bc451 100644 --- a/virtual-desktop/src/assets/i18n/messages.en.json +++ b/virtual-desktop/src/assets/i18n/messages.en.json @@ -1,8 +1,8 @@ { "Account Password": "Account Password", - "Back": "Back", "Apply": "Apply", "AuthenticationFailed": "Authentication failed for {{numTypes}} types. Types: {{types}}", + "Back": "Back", "BringToFront": "Bring to Front", "Change Password": "Change Password", "Chinese": "Chinese", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "For language changes to take effect, Zowe must be restarted.", "French": "French", "German": "German", + "Incorrect password or account is locked. Please contact your administrator.": "Incorrect password or account is locked. Please contact your administrator.", "Japanese": "Japanese", "Language Changes": "Language Changes", "Language Selected": "Language Selected", "Languages": "Languages", + "Multiple password reset is not available.": "Multiple password reset is not available.", + "New passwords do not match. Please try again.": "New passwords do not match. Please try again.", + "No new password provided": "No new password provided.", + "No password provided": "No password provided", + "No password reset service available.": "No password reset service available.", + "No username provided": "No username provided", "Open New": "Open New", "Open": "Open", "Open in New Browser Tab": "Open In New Browser Tab", @@ -37,6 +44,8 @@ "Session Expiring Soon": "Session Expiring Soon", "Session Renewal Error": "Session Renewal Error", "Session will expire unless renewed.": "Session will expire in {{expirationInMS}} seconds unless renewed.", + "The new password format is incorrect. Please try again.": "The new password format is incorrect. Please try again.", + "Username or password is incorrect. Please try again.": "Username or password is incorrect. Please try again.", "UnpinFromTaskbar": "Unpin from taskbar", "UsernameRequired": "Username required", "Would you like to restart the desktop?": "Would you like to restart the desktop?" diff --git a/virtual-desktop/src/assets/i18n/messages.fr.json b/virtual-desktop/src/assets/i18n/messages.fr.json index 4aee4b100..81e703d5b 100644 --- a/virtual-desktop/src/assets/i18n/messages.fr.json +++ b/virtual-desktop/src/assets/i18n/messages.fr.json @@ -1,8 +1,8 @@ { "Account Password": "Mot de passe du compte", "Apply": "Appliquer", - "Back": "Retourner", "AuthenticationFailed": "L'authentification a échoué pour {{numTypes}} types. Les types: {{types}}", + "Back": "Retourner", "BringToFront": "Mettre au premier plan", "Change Password": "Changer de mot de Passe", "Chinese": "Chinois", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "Pour que le changement de langue prenne effet, Zowe doit être redémarré.", "French": "Français", "German": "Allemand", + "Incorrect password or account is locked. Please contact your administrator.": "Un mot de passe ou un compte incorrect est verrouillé. Veuillez contacter votre administrateur.", "Japanese": "Japonais", "Language Changes": "Changement de Langue", "Language Selected": "Langue Sélectionnée", "Languages": "Langues", + "Multiple password reset is not available.": "La réinitialisation de plusieurs mots de passe n'est pas disponible.", + "New passwords do not match. Please try again.": "Les nouveaux mots de passe ne correspondent pas. Veuillez réessayer.", + "No new password provided": "Aucun nouveau mot de passe fourni.", + "No password provided": "Aucun mot de passe fourni", + "No password reset service available.": "Aucun service de réinitialisation de mot de passe disponible.", + "No username provided": "Aucun nom d'utilisateur fourni", "Open New": "Nouvelle Fenêtre", "Open": "Ouvrir", "Open in New Browser Tab": "Ouvrir dans un nouvel onglet", @@ -38,6 +45,8 @@ "Session Expiring Soon": "Votre session va bientôt expirer", "Session Renewal Error": "Erreur de renouvellement de session", "Session will expire unless renewed.": "Votre session va expirer dans {{expirationInMS}} secondes sauf si elle est renouvelée.", + "The new password format is incorrect. Please try again.": "Le nouveau format de mot de passe est incorrect. Veuillez réessayer.", + "Username or password is incorrect. Please try again.": "L'identifiant ou le mot de passe est incorrect. Veuillez réessayer.", "UsernameRequired": "Nom d'utilisateur requis", "Would you like to restart the desktop?": "Voulez-vous redémarrer votre session?", "UnpinFromTaskbar": "Désépingler" diff --git a/virtual-desktop/src/assets/i18n/messages.ja.json b/virtual-desktop/src/assets/i18n/messages.ja.json index bf8bbc4e6..b8497f4f6 100644 --- a/virtual-desktop/src/assets/i18n/messages.ja.json +++ b/virtual-desktop/src/assets/i18n/messages.ja.json @@ -1,8 +1,8 @@ { "Account Password": "アカウントパスワード", "Apply": "適用", - "Back": "戻る", "AuthenticationFailed": "{{numTypes}}個の認証プロバイダー({{types}})に対して認証が失敗しました", + "Back": "戻る", "BringToFront": "最前面に移動", "Change Password": "パスワードを変更する", "Chinese": "中国語", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "言語の変更を反映するには、Zoweを再起動する必要があります。", "French": "フランス語", "German": "ドイツ語", + "Incorrect password or account is locked. Please contact your administrator.": "パスワードが間違っているか、アカウントがロックされているため、ログインに失敗しました。 管理者に連絡してください。", "Japanese": "日本語", "Language Changes": "言語変更", "Language Selected": "選択された言語", "Languages": "言語", + "Multiple password reset is not available.": "複数のパスワードをリセットできません。", + "New passwords do not match. Please try again.": "新しいパスワードが一致しません。 もう一度やり直してください。", + "No new password provided": "パスワードがありません。", + "No password provided": "パスワードが提供されていません", + "No password reset service available.": "パスワードリセットのサービスがありません。", + "No username provided": "ユーザー名がありません。", "Open New": "新しく開く", "Open": "開く", "Open in New Browser Tab": "新しいブラウザー・タブで開く", @@ -39,6 +46,8 @@ "Session Expiring Soon": "セッションの期限がもうすぐ切れます。", "Session Renewal Error": "セッション更新エラー。", "Session will expire unless renewed.": "セッションを更新しないと {{expirationInMS}} 秒で切れます。", + "The new password format is incorrect. Please try again.": "新しいパスワードの形式が正しくありません。もう一度やり直してください。", + "Username or password is incorrect. Please try again.": "ユーザー名かパスワードが間違っています。もう一度やり直してください。", "UsernameRequired": "ユーザー名が必要です", "UnpinFromTaskbar": "アプリケーションのピン留めをはずす", "Would you like to restart the desktop?": "デスクトップを再起動しますか?" diff --git a/virtual-desktop/src/assets/i18n/messages.ru.json b/virtual-desktop/src/assets/i18n/messages.ru.json index ada62ae84..01c1f8a41 100644 --- a/virtual-desktop/src/assets/i18n/messages.ru.json +++ b/virtual-desktop/src/assets/i18n/messages.ru.json @@ -1,8 +1,8 @@ { "Account Password": "Пароль от аккаунта", "Apply": "Применить", - "Back": "Назад", "AuthenticationFailed": "Ошибка аутентификации {{numTypes}} для {{types}}", + "Back": "Назад", "BringToFront": "На передний план", "Change Password": "Изменить пароль", "Chinese": "Китайский", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "Чтобы изменить язык, необходимо перезагрузить Zowe.", "French": "Французский", "German": "Немецкий", + "Incorrect password or account is locked. Please contact your administrator.": "Неверный пароль или аккаунт заблокирован. Пожалуйста, свяжитесь с вашим администратором.", "Japanese": "Японский", "Language Changes": "Смена языка", "Language Selected": "Выбранный язык", "Languages": "Языки", + "Multiple password reset is not available.": "Многократный сброс пароля недоступен.", + "New passwords do not match. Please try again.": "Новый пароль и его подтверждение не совпадают. Пожалуйста, попробуйте еще раз", + "No new password provided": "Новый пароль не указан.", + "No password provided": "Пароль не указан", + "No password reset service available.": "Служба сброса пароля недоступна.", + "No username provided": "Имя пользователя не указано", "Open New": "Открыть новое окно", "Open": "Открыть", "Open in New Browser Tab": "Открыть в новой вкладке браузера", @@ -39,6 +46,8 @@ "Session Expiring Soon": "Сессия скоро истекает", "Session Renewal Error": "Ошибка обновления сессии", "Session will expire unless renewed.": "Сессия истекает через {{expirationInMS}} секунд если не обновлено.", + "The new password format is incorrect. Please try again.": "Новый пароль имеет неверный формат. Пожалуйста, попробуйте еще раз", + "Username or password is incorrect. Please try again.": "Имя пользователя или пароль неверны. Пожалуйста, попробуйте еще раз.", "UsernameRequired": "Введите имя пользователя", "UnpinFromTaskbar": "Открепить", "Would you like to restart the desktop?": "Перезагрузить рабочий стол?" diff --git a/virtual-desktop/src/assets/i18n/messages.zh.json b/virtual-desktop/src/assets/i18n/messages.zh.json index 25adc999c..6f3e22f7f 100644 --- a/virtual-desktop/src/assets/i18n/messages.zh.json +++ b/virtual-desktop/src/assets/i18n/messages.zh.json @@ -1,8 +1,8 @@ { - "Account Password": "户口密码", + "Account Password": "用户密码", "Apply": "应用", - "Back": "回去", "AuthenticationFailed": "{{numTypes}} 个类型验证失败 类型: {{types}}", + "Back": "后退", "BringToFront": "置顶此程序", "Change Password": "更改密码", "Chinese": "中文", @@ -15,10 +15,17 @@ "For language changes to take effect, Zowe must be restarted.": "为使语言变更生效,Zowe必须重新启动。", "French": "法语", "German": "德语", + "Incorrect password or account is locked. Please contact your administrator.": "错误的密码或帐户已被锁定。请与您的管理员联系。", "Japanese": "日语", "Language Changes": "语言变更", "Language Selected": "当前语言", "Languages": "语言", + "Multiple password reset is not available.": "多个密码重置不可用。", + "New passwords do not match. Please try again.": "新密码不匹配。请再试一遍。", + "No new password provided": "没有提供新密码。", + "No password provided": "没有提供密码", + "No password reset service available.": "没有可用的密码重置服务。", + "No username provided": "没有提供用户名", "Open New": "打开新会话", "Open": "打开", "Open in New Browser Tab": "在新的浏览器标签页打开", @@ -38,6 +45,8 @@ "Session Expiring Soon": "会话即将过期", "Session Renewal Error": "会话续订发生错误", "Session will expire unless renewed.": "会话将在{{expirationInMS}}秒后过期", + "The new password format is incorrect. Please try again.": "新密码格式不正确。请再试一遍。", + "Username or password is incorrect. Please try again.": "用户名或密码不正确。请再试一遍。", "UsernameRequired": "用户名不能为空", "UnpinFromTaskbar": "将此程序从任务栏移除", "Would you like to restart the desktop?": "是否立即重新启动?"