perf: reorganize the icons and reduce the volume

This commit is contained in:
vince
2024-07-17 22:25:27 +08:00
parent cb161eab89
commit 910a3553ac
54 changed files with 341 additions and 404 deletions

View File

@@ -4,7 +4,7 @@ import type { FallbackProps } from './fallback';
import { computed, defineAsyncComponent } from 'vue';
import { useRouter } from 'vue-router';
import { IcRoundArrowBackIosNew, IcRoundRefresh } from '@vben-core/icons';
import { ArrowLeft, RotateCw } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import { VbenButton } from '@vben-core/shadcn-ui';
@@ -151,11 +151,11 @@ function refresh() {
</p>
<slot v-if="$slots.action" name="action"></slot>
<VbenButton v-else-if="showBack" size="lg" @click="back">
<IcRoundArrowBackIosNew class="mr-2" />
<ArrowLeft class="mr-2 size-4" />
{{ $t('common.backToHome') }}
</VbenButton>
<VbenButton v-else-if="showRefresh" size="lg" @click="refresh">
<IcRoundRefresh class="mr-2" />
<RotateCw class="mr-2 size-4" />
{{ $t('common.refresh') }}
</VbenButton>
</div>

View File

@@ -30,7 +30,7 @@ defineOptions({ name: 'BasicLayout' });
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
const { isDark, isHeaderNav, isMixedNav, isSideMixedNav, layout } =
const { isDark, isHeaderNav, isMixedNav, isMobile, isSideMixedNav, layout } =
usePreferences();
const headerMenuTheme = computed(() => {
@@ -63,7 +63,6 @@ const logoCollapse = computed(() => {
return false;
}
const { isMobile } = preferences.app;
const { collapsed } = preferences.sidebar;
if (!collapsed && isMobile) {

View File

@@ -9,17 +9,17 @@ import { computed, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import {
IcRoundClose,
IcRoundFitScreen,
IcRoundMultipleStop,
IcRoundRefresh,
IcRoundTableView,
IcTwotoneFitScreen,
MdiArrowExpandHorizontal,
MdiFormatHorizontalAlignLeft,
MdiFormatHorizontalAlignRight,
ArrowLeftToLine,
ArrowRightLeft,
ArrowRightToLine,
ExternalLink,
FoldHorizontal,
Fullscreen,
MdiPin,
MdiPinOff,
Minimize2,
RotateCw,
X,
} from '@vben-core/icons';
import { $t, useI18n } from '@vben-core/locales';
import { updatePreferences, usePreferences } from '@vben-core/preferences';
@@ -131,7 +131,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.closeTab(tab, router);
},
icon: IcRoundClose,
icon: X,
key: 'close',
text: $t('preferences.tabbar.contextMenu.close'),
},
@@ -154,7 +154,7 @@ function useTabs() {
}
updateContentScreen(!contentIsMaximize.value);
},
icon: contentIsMaximize.value ? IcRoundFitScreen : IcTwotoneFitScreen,
icon: contentIsMaximize.value ? Minimize2 : Fullscreen,
key: contentIsMaximize.value ? 'restore-maximize' : 'maximize',
text: contentIsMaximize.value
? $t('preferences.tabbar.contextMenu.restoreMaximize')
@@ -165,7 +165,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.refresh(router);
},
icon: IcRoundRefresh,
icon: RotateCw,
key: 'reload',
text: $t('preferences.tabbar.contextMenu.reload'),
},
@@ -178,7 +178,7 @@ function useTabs() {
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
openWindow(url, { target: '_blank' });
},
icon: IcRoundTableView,
icon: ExternalLink,
key: 'open-in-new-window',
separator: true,
text: $t('preferences.tabbar.contextMenu.openInNewWindow'),
@@ -189,7 +189,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.closeLeftTabs(tab);
},
icon: MdiFormatHorizontalAlignLeft,
icon: ArrowLeftToLine,
key: 'close-left',
text: $t('preferences.tabbar.contextMenu.closeLeft'),
},
@@ -198,7 +198,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.closeRightTabs(tab);
},
icon: MdiFormatHorizontalAlignRight,
icon: ArrowRightToLine,
key: 'close-right',
separator: true,
text: $t('preferences.tabbar.contextMenu.closeRight'),
@@ -208,7 +208,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.closeOtherTabs(tab);
},
icon: MdiArrowExpandHorizontal,
icon: FoldHorizontal,
key: 'close-other',
text: $t('preferences.tabbar.contextMenu.closeOther'),
},
@@ -217,7 +217,7 @@ function useTabs() {
handler: async () => {
await coreTabbarStore.closeAllTabs(router);
},
icon: IcRoundMultipleStop,
icon: ArrowRightLeft,
key: 'close-all',
text: $t('preferences.tabbar.contextMenu.closeAll'),
},

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { BuiltinThemeType } from '@vben-core/typings';
import { IcRoundColorLens } from '@vben-core/icons';
import { Palette } from '@vben-core/icons';
import {
COLOR_PRESETS,
preferences,
@@ -56,7 +56,7 @@ function handleUpdate(value: BuiltinThemeType) {
</div>
<VbenIconButton>
<IcRoundColorLens class="text-primary size-5" />
<Palette class="text-primary size-4" />
</VbenIconButton>
</div>
</template>

View File

@@ -4,11 +4,11 @@ import type { MenuRecordRaw } from '@vben-core/typings';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import {
IcRoundArrowDownward,
IcRoundArrowUpward,
IcRoundSearch,
IcRoundSubdirectoryArrowLeft,
ArrowDown,
ArrowUp,
CornerDownLeft,
MdiKeyboardEsc,
Search,
} from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import {
@@ -87,8 +87,8 @@ onMounted(() => {
class="md:bg-accent group flex h-8 cursor-pointer items-center gap-3 rounded-2xl border-none bg-none px-2 py-0.5 outline-none"
@click="toggleOpen()"
>
<IcRoundSearch
class="text-muted-foreground group-hover:text-foreground size-4 group-hover:opacity-100"
<Search
class="text-muted-foreground group-hover:text-foreground size-3 group-hover:opacity-100"
/>
<span
class="text-muted-foreground group-hover:text-foreground hidden text-xs duration-300 md:block"
@@ -111,13 +111,13 @@ onMounted(() => {
>
<DialogHeader>
<DialogTitle
class="border-border flex h-12 items-center gap-5 border-b px-5 font-normal"
class="border-border flex h-12 items-center gap-3 border-b px-5 font-normal"
>
<IcRoundSearch class="mt-1 size-4" />
<Search class="text-muted-foreground size-4" />
<input
v-model="keyword"
:placeholder="$t('widgets.search.searchNavigate')"
class="ring-none placeholder:text-muted-foreground w-[80%] rounded-md border border-none bg-transparent p-2 text-sm outline-none ring-0 ring-offset-transparent focus-visible:ring-transparent"
class="ring-none placeholder:text-muted-foreground w-[80%] rounded-md border border-none bg-transparent p-2 pl-0 text-sm outline-none ring-0 ring-offset-transparent focus-visible:ring-transparent"
/>
</DialogTitle>
<DialogDescription />
@@ -127,16 +127,16 @@ onMounted(() => {
class="text-muted-foreground border-border hidden flex-row rounded-b-2xl border-t px-4 py-2 text-xs sm:flex sm:justify-start sm:gap-x-4"
>
<div class="flex items-center">
<IcRoundSubdirectoryArrowLeft class="mr-1" />
<CornerDownLeft class="mr-1 size-3" />
{{ $t('widgets.search.select') }}
</div>
<div class="flex items-center">
<IcRoundArrowUpward class="mr-2" />
<IcRoundArrowDownward class="mr-2" />
<ArrowUp class="mr-2 size-3" />
<ArrowDown class="mr-2 size-3" />
{{ $t('widgets.search.navigate') }}
</div>
<div class="flex items-center">
<MdiKeyboardEsc class="mr-1" />
<MdiKeyboardEsc class="mr-1 size-3" />
{{ $t('widgets.search.close') }}
</div>
</DialogFooter>

View File

@@ -4,7 +4,7 @@ import type { MenuRecordRaw } from '@vben-core/typings';
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
import { useRouter } from 'vue-router';
import { IcRoundClose, IcRoundSearchOff } from '@vben-core/icons';
import { SearchX, X } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import { VbenIcon, VbenScrollbar } from '@vben-core/shadcn-ui';
import { mapTree, traverseTreeValues, uniqueByField } from '@vben-core/toolkit';
@@ -221,7 +221,7 @@ onMounted(() => {
v-if="keyword && searchResults.length === 0"
class="text-muted-foreground text-center"
>
<IcRoundSearchOff class="size-12" />
<SearchX class="mx-auto size-12" />
<p class="my-10 text-xs">
{{ $t('widgets.search.noResults') }}
<span class="text-foreground text-sm font-medium">
@@ -271,7 +271,7 @@ onMounted(() => {
class="flex-center dark:hover:bg-accent hover:text-primary-foreground rounded-full p-1 hover:scale-110"
@click.stop="removeItem(index)"
>
<IcRoundClose />
<X class="size-4" />
</div>
</li>
</ul>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { SupportedLanguagesType } from '@vben-core/typings';
import { IcBaselineLanguage } from '@vben-core/icons';
import { Languages } from '@vben-core/icons';
import { loadLocaleMessages } from '@vben-core/locales';
import {
SUPPORT_LANGUAGES,
@@ -36,7 +36,7 @@ async function handleUpdate(value: string) {
@update:model-value="handleUpdate"
>
<VbenIconButton>
<IcBaselineLanguage class="size-5" />
<Languages class="size-4" />
</VbenIconButton>
</VbenDropdownRadioMenu>
</div>

View File

@@ -4,7 +4,7 @@ import type { AuthPageLayoutType } from '@vben-core/typings';
import { computed } from 'vue';
import { MdiDockBottom, MdiDockLeft, MdiDockRight } from '@vben-core/icons';
import { InspectionPanel, PanelLeft, PanelRight } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import {
preferences,
@@ -19,17 +19,17 @@ defineOptions({
const menus = computed((): VbenDropdownMenuItem[] => [
{
icon: MdiDockLeft,
icon: PanelLeft,
key: 'panel-left',
text: $t('authentication.layout.alignLeft'),
},
{
icon: MdiDockBottom,
icon: InspectionPanel,
key: 'panel-center',
text: $t('authentication.layout.center'),
},
{
icon: MdiDockRight,
icon: PanelRight,
key: 'panel-right',
text: $t('authentication.layout.alignRight'),
},
@@ -53,9 +53,9 @@ function handleUpdate(value: string) {
@update:model-value="handleUpdate"
>
<VbenIconButton>
<MdiDockRight v-if="authPanelRight" class="size-5" />
<MdiDockLeft v-if="authPanelLeft" class="size-5" />
<MdiDockBottom v-if="authPanelCenter" class="size-5" />
<PanelRight v-if="authPanelRight" class="size-4" />
<PanelLeft v-if="authPanelLeft" class="size-4" />
<InspectionPanel v-if="authPanelCenter" class="size-4" />
</VbenIconButton>
</VbenDropdownRadioMenu>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, reactive, ref, watchEffect } from 'vue';
import { IcRoundLock } from '@vben-core/icons';
import { LockKeyhole } from '@vben-core/icons';
import { $t, useI18n } from '@vben-core/locales';
import {
VbenAvatar,
@@ -92,7 +92,7 @@ function toggleUnlockForm() {
class="flex-col-center text-foreground/80 hover:text-foreground group my-4 cursor-pointer text-xl font-semibold"
@click="toggleUnlockForm"
>
<IcRoundLock
<LockKeyhole
class="size-5 transition-all duration-300 group-hover:scale-125"
/>
<span>{{ $t('widgets.lockScreen.unlock') }}</span>

View File

@@ -1,10 +1,7 @@
<script lang="ts" setup>
import type { NotificationItem } from './types';
import {
IcRoundMarkEmailRead,
IcRoundNotificationsNone,
} from '@vben-core/icons';
import { Bell, MailCheck } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import {
VbenButton,
@@ -75,7 +72,7 @@ function handleClick(item: NotificationItem) {
v-if="dot"
class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
></span>
<IcRoundNotificationsNone class="size-5" />
<Bell class="size-4" />
</VbenIconButton>
</div>
</template>
@@ -87,7 +84,7 @@ function handleClick(item: NotificationItem) {
:tooltip="$t('widgets.markAllAsRead')"
@click="handleMakeAll"
>
<IcRoundMarkEmailRead />
<MailCheck class="size-4" />
</VbenIconButton>
</div>
<VbenScrollbar v-if="notifications.length > 0">

View File

@@ -3,7 +3,7 @@ import type { SelectOption } from '@vben-core/typings';
import { useSlots } from 'vue';
import { MdiQuestionMarkCircleOutline } from '@vben-core/icons';
import { CircleHelp } from '@vben-core/icons';
import { Input, VbenTooltip } from '@vben-core/shadcn-ui';
defineOptions({
@@ -41,7 +41,7 @@ const slots = useSlots();
<VbenTooltip v-if="slots.tip" side="bottom">
<template #trigger>
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
<slot name="tip"></slot>
</VbenTooltip>

View File

@@ -3,7 +3,7 @@ import type { LayoutType } from '@vben-core/typings';
import { type Component, computed } from 'vue';
import { MdiQuestionMarkCircleOutline } from '@vben-core/icons';
import { CircleHelp } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import { VbenTooltip } from '@vben-core/shadcn-ui';
@@ -84,7 +84,7 @@ function activeClass(theme: string): string[] {
{{ theme.name }}
<VbenTooltip v-if="theme.tip" side="bottom">
<template #trigger>
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
{{ theme.tip }}
</VbenTooltip>

View File

@@ -3,7 +3,7 @@ import type { SelectOption } from '@vben-core/typings';
import { useSlots } from 'vue';
import { MdiQuestionMarkCircleOutline } from '@vben-core/icons';
import { CircleHelp } from '@vben-core/icons';
import {
NumberField,
NumberFieldContent,
@@ -48,7 +48,7 @@ const slots = useSlots();
<VbenTooltip v-if="slots.tip" side="bottom">
<template #trigger>
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
<slot name="tip"></slot>
</VbenTooltip>

View File

@@ -3,7 +3,7 @@ import type { SelectOption } from '@vben-core/typings';
import { useSlots } from 'vue';
import { MdiQuestionMarkCircleOutline } from '@vben-core/icons';
import { CircleHelp } from '@vben-core/icons';
import {
Select,
SelectContent,
@@ -48,7 +48,7 @@ const slots = useSlots();
<VbenTooltip v-if="slots.tip" side="bottom">
<template #trigger>
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
<slot name="tip"></slot>
</VbenTooltip>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { useSlots } from 'vue';
import { MdiQuestionMarkCircleOutline } from '@vben-core/icons';
import { CircleHelp } from '@vben-core/icons';
import { Switch, VbenTooltip } from '@vben-core/shadcn-ui';
defineOptions({
@@ -34,7 +34,7 @@ function handleClick() {
<VbenTooltip v-if="slots.tip" side="bottom">
<template #trigger>
<MdiQuestionMarkCircleOutline class="ml-1 cursor-help" />
<CircleHelp class="ml-1 size-3 cursor-help" />
</template>
<slot name="tip"></slot>
</VbenTooltip>

View File

@@ -3,7 +3,7 @@ import type { BuiltinThemeType } from '@vben-core/typings';
import { computed, ref } from 'vue';
import { MdiEditBoxOutline } from '@vben-core/icons';
import { UserRoundPen } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import {
BUILT_IN_THEME_PRESETS,
@@ -114,7 +114,7 @@ function selectColor() {
<template v-else>
<div class="size-full px-10 py-2" @click.stop="selectColor">
<div class="flex-center relative size-5 rounded-sm">
<MdiEditBoxOutline
<UserRoundPen
class="absolute z-10 size-5 opacity-60 group-hover:opacity-100"
/>
<input

View File

@@ -3,11 +3,7 @@ import type { ThemeModeType } from '@vben-core/typings';
import type { Component } from 'vue';
import {
IcRoundMotionPhotosAuto,
IcRoundWbSunny,
MdiMoonAndStars,
} from '@vben-core/icons';
import { MoonStar, Sun, SunMoon } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import SwitchItem from '../switch-item.vue';
@@ -23,15 +19,15 @@ const themeSemiDarkMenu = defineModel<boolean>('themeSemiDarkMenu', {
const THEME_PRESET: Array<{ icon: Component; name: ThemeModeType }> = [
{
icon: IcRoundWbSunny,
icon: Sun,
name: 'light',
},
{
icon: MdiMoonAndStars,
icon: MoonStar,
name: 'dark',
},
{
icon: IcRoundMotionPhotosAuto,
icon: SunMoon,
name: 'auto',
},
];

View File

@@ -13,7 +13,7 @@ import type {
import { computed, ref } from 'vue';
import { IcRoundFolderCopy, IcRoundRestartAlt } from '@vben-core/icons';
import { Copy, RotateCw, SwatchBook } from '@vben-core/icons';
import { $t, loadLocaleMessages } from '@vben-core/locales';
import {
clearPreferencesCache,
@@ -51,7 +51,6 @@ import {
Theme,
Widget,
} from './blocks';
import IconSetting from './icons/setting.vue';
import { useOpenPreferences } from './use-open-preferences';
const emit = defineEmits<{ clearPreferencesAndLogout: [] }>();
@@ -225,9 +224,9 @@ async function handleReset() {
<template #trigger>
<VbenButton
:title="$t('preferences.title')"
class="bg-primary flex-col-center h-12 w-12 cursor-pointer rounded-l-lg rounded-r-none border-none"
class="bg-primary flex-col-center h-10 w-10 cursor-pointer rounded-l-lg rounded-r-none border-none"
>
<IconSetting class="duration-3000 fill-primary-foreground text-2xl" />
<SwatchBook class="size-5" />
</VbenButton>
</template>
<template #extra>
@@ -241,7 +240,7 @@ async function handleReset() {
v-if="diffPreference"
class="bg-primary absolute right-0.5 top-0.5 h-2 w-2 rounded"
></span>
<IcRoundRestartAlt class="size-5" @click="handleReset" />
<RotateCw class="size-4" @click="handleReset" />
</VbenIconButton>
</div>
</template>
@@ -408,7 +407,7 @@ async function handleReset() {
variant="default"
@click="handleCopy"
>
<IcRoundFolderCopy class="mr-2 size-3" />
<Copy class="mr-2 size-3" />
{{ $t('preferences.copyPreferences') }}
</VbenButton>
<VbenButton
@@ -418,7 +417,7 @@ async function handleReset() {
variant="ghost"
@click="handleClearCache"
>
<!-- <IcRoundRestartAlt class="mr-2 size-4" /> -->
<!-- <RotateCw class="mr-2 size-4" /> -->
{{ $t('preferences.clearAndLogout') }}
</VbenButton>
</template>

View File

@@ -34,7 +34,7 @@ const bindProps = computed(() => {
: {
class: 'rounded-full',
size: 'icon' as const,
style: { padding: '6px' },
style: { padding: '7px' },
variant: 'icon' as const,
};
});
@@ -130,18 +130,18 @@ function toggleTheme(event: MouseEvent) {
}
&__sun {
@apply fill-foreground/80 stroke-none;
@apply fill-foreground/70 stroke-none;
transition: transform 1.6s cubic-bezier(0.25, 0, 0.2, 1);
transform-origin: center center;
&:hover > svg > & {
@apply fill-foreground/80;
@apply fill-foreground/70;
}
}
&__sun-beams {
@apply stroke-foreground/80 stroke-[2px];
@apply stroke-foreground/70 stroke-[2px];
transition:
transform 1.6s cubic-bezier(0.5, 1.5, 0.75, 1.25),

View File

@@ -1,11 +1,7 @@
<script lang="ts" setup>
import type { ThemeModeType } from '@vben-core/typings';
import {
IcRoundMotionPhotosAuto,
IcRoundWbSunny,
MdiMoonAndStars,
} from '@vben-core/icons';
import { MoonStar, Sun, SunMoon } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import {
preferences,
@@ -38,17 +34,17 @@ const { isDark } = usePreferences();
const PRESETS = [
{
icon: IcRoundWbSunny,
icon: Sun,
name: 'light',
title: $t('preferences.theme.light'),
},
{
icon: MdiMoonAndStars,
icon: MoonStar,
name: 'dark',
title: $t('preferences.theme.dark'),
},
{
icon: IcRoundMotionPhotosAuto,
icon: SunMoon,
name: 'auto',
title: $t('preferences.followSystem'),
},

View File

@@ -4,11 +4,7 @@ import type { AnyFunction } from '@vben-core/typings';
import type { Component } from 'vue';
import { computed, ref } from 'vue';
import {
IcRoundLock,
IcRoundLogout,
IcRoundSettingsSuggest,
} from '@vben-core/icons';
import { LockKeyhole, LogOut, SwatchBook } from '@vben-core/icons';
import { $t } from '@vben-core/locales';
import { preferences, usePreferences } from '@vben-core/preferences';
import {
@@ -203,7 +199,7 @@ if (enableShortcutKey.value) {
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="menu.handler"
>
<VbenIcon :icon="menu.icon" class="mr-2 size-5" />
<VbenIcon :icon="menu.icon" class="mr-2 size-4" />
{{ menu.text }}
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -211,7 +207,7 @@ if (enableShortcutKey.value) {
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleOpenPreference"
>
<IcRoundSettingsSuggest class="mr-2 size-5" />
<SwatchBook class="mr-2 size-4" />
{{ $t('preferences.title') }}
<DropdownMenuShortcut v-if="enablePreferencesShortcutKey">
{{ altView }} ,
@@ -222,7 +218,7 @@ if (enableShortcutKey.value) {
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleOpenLock"
>
<IcRoundLock class="mr-2 size-5" />
<LockKeyhole class="mr-2 size-4" />
{{ $t('widgets.lockScreen.title') }}
<DropdownMenuShortcut v-if="enableLockScreenShortcutKey">
{{ altView }} L
@@ -233,7 +229,7 @@ if (enableShortcutKey.value) {
class="mx-1 flex cursor-pointer items-center rounded-sm py-1 leading-8"
@click="handleLogout"
>
<IcRoundLogout class="mr-2 size-5" />
<LogOut class="mr-2 size-4" />
{{ $t('common.logout') }}
<DropdownMenuShortcut v-if="enableLogoutShortcutKey">
{{ altView }} Q