2024-05-19 21:20:42 +08:00
|
|
|
|
<script lang="ts" setup>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
import { computed, useSlots } from 'vue';
|
|
|
|
|
|
|
2024-10-06 17:27:32 +08:00
|
|
|
|
import { useRefresh } from '@vben/hooks';
|
2026-06-06 23:24:54 +08:00
|
|
|
|
import { LockKeyhole, LogOut, RotateCw } from '@vben/icons';
|
|
|
|
|
|
import { $t } from '@vben/locales';
|
2024-07-23 00:03:59 +08:00
|
|
|
|
import { preferences, usePreferences } from '@vben/preferences';
|
2024-07-30 21:10:28 +08:00
|
|
|
|
import { useAccessStore } from '@vben/stores';
|
2025-01-01 11:39:49 +08:00
|
|
|
|
|
2026-06-06 23:24:54 +08:00
|
|
|
|
import { useVbenModal } from '@vben-core/popup-ui';
|
2026-07-21 22:36:42 +08:00
|
|
|
|
import {
|
|
|
|
|
|
VbenFullScreen,
|
|
|
|
|
|
VbenIconButton,
|
|
|
|
|
|
VbenTooltip,
|
|
|
|
|
|
} from '@vben-core/shadcn-ui';
|
2024-05-19 21:20:42 +08:00
|
|
|
|
|
2026-06-06 23:24:54 +08:00
|
|
|
|
import { useMagicKeys, whenever } from '@vueuse/core';
|
|
|
|
|
|
|
2024-08-14 23:02:39 +08:00
|
|
|
|
import {
|
|
|
|
|
|
GlobalSearch,
|
|
|
|
|
|
LanguageToggle,
|
2026-06-06 23:24:54 +08:00
|
|
|
|
LockScreenModal,
|
2026-07-21 22:36:42 +08:00
|
|
|
|
Notification,
|
2024-08-14 23:02:39 +08:00
|
|
|
|
PreferencesButton,
|
|
|
|
|
|
ThemeToggle,
|
2025-10-29 19:47:10 +08:00
|
|
|
|
TimezoneButton,
|
2024-08-14 23:02:39 +08:00
|
|
|
|
} from '../../widgets';
|
2024-07-09 22:49:36 +08:00
|
|
|
|
|
2024-05-19 21:20:42 +08:00
|
|
|
|
interface Props {
|
2026-06-06 23:24:54 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 头像
|
|
|
|
|
|
*/
|
|
|
|
|
|
avatar?: string;
|
2024-05-19 21:20:42 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Logo 主题
|
|
|
|
|
|
*/
|
|
|
|
|
|
theme?: string;
|
2026-06-06 23:24:54 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 用户文本
|
|
|
|
|
|
*/
|
|
|
|
|
|
text?: string;
|
2024-05-19 21:20:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
defineOptions({
|
|
|
|
|
|
name: 'LayoutHeader',
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
withDefaults(defineProps<Props>(), {
|
2026-06-06 23:24:54 +08:00
|
|
|
|
avatar: '',
|
2024-05-19 21:20:42 +08:00
|
|
|
|
theme: 'light',
|
2026-06-06 23:24:54 +08:00
|
|
|
|
text: '',
|
2024-05-19 21:20:42 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-06 23:24:54 +08:00
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
clearPreferencesAndLogout: [];
|
|
|
|
|
|
logout: [];
|
|
|
|
|
|
openLockScreen: [];
|
|
|
|
|
|
}>();
|
2024-08-28 09:58:59 +08:00
|
|
|
|
|
2026-05-20 17:24:18 +08:00
|
|
|
|
const REFERENCE_VALUE = 100;
|
2024-10-06 17:27:32 +08:00
|
|
|
|
|
2024-07-30 21:10:28 +08:00
|
|
|
|
const accessStore = useAccessStore();
|
2026-06-06 23:24:54 +08:00
|
|
|
|
const {
|
|
|
|
|
|
globalLockScreenShortcutKey,
|
|
|
|
|
|
globalLogoutShortcutKey,
|
|
|
|
|
|
globalSearchShortcutKey,
|
|
|
|
|
|
preferencesButtonPosition,
|
|
|
|
|
|
} = usePreferences();
|
2024-08-06 22:32:51 +08:00
|
|
|
|
const slots = useSlots();
|
2024-10-06 17:27:32 +08:00
|
|
|
|
const { refresh } = useRefresh();
|
|
|
|
|
|
|
2026-06-06 23:24:54 +08:00
|
|
|
|
const [LockModal, lockModalApi] = useVbenModal({
|
|
|
|
|
|
connectedComponent: LockScreenModal,
|
|
|
|
|
|
});
|
|
|
|
|
|
const [LogoutModal, logoutModalApi] = useVbenModal({
|
|
|
|
|
|
onConfirm() {
|
|
|
|
|
|
handleSubmitLogout();
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function handleOpenLock() {
|
|
|
|
|
|
lockModalApi.open();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSubmitLock(lockScreenPassword: string) {
|
|
|
|
|
|
lockModalApi.close();
|
|
|
|
|
|
accessStore.lockScreen(lockScreenPassword);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleLogout() {
|
|
|
|
|
|
logoutModalApi.open();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleSubmitLogout() {
|
|
|
|
|
|
emit('logout');
|
|
|
|
|
|
logoutModalApi.close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 快捷键
|
|
|
|
|
|
if (preferences.shortcutKeys.enable) {
|
|
|
|
|
|
const keys = useMagicKeys();
|
|
|
|
|
|
const lockKey = keys['Alt+KeyL'];
|
|
|
|
|
|
const logoutKey = keys['Alt+KeyQ'];
|
|
|
|
|
|
|
|
|
|
|
|
if (lockKey) {
|
|
|
|
|
|
whenever(lockKey, () => {
|
|
|
|
|
|
if (globalLockScreenShortcutKey?.value) {
|
|
|
|
|
|
handleOpenLock();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (logoutKey) {
|
|
|
|
|
|
whenever(logoutKey, () => {
|
|
|
|
|
|
if (globalLogoutShortcutKey?.value) {
|
|
|
|
|
|
handleLogout();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-20 17:24:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 插槽列表类型
|
|
|
|
|
|
*/
|
|
|
|
|
|
type SlotItem = { index: number; name: string };
|
|
|
|
|
|
|
2024-08-06 22:32:51 +08:00
|
|
|
|
const rightSlots = computed(() => {
|
2026-05-20 17:24:18 +08:00
|
|
|
|
const list: Array<SlotItem> = [];
|
2026-07-19 20:38:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 按 widget.order 顺序遍历,检查每个部件是否应在 header 中显示
|
|
|
|
|
|
const widgetChecks: Record<string, { slotName: string; visible: boolean }> = {
|
|
|
|
|
|
globalSearch: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.globalSearch &&
|
|
|
|
|
|
preferences.widget.globalSearchButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'global-search',
|
|
|
|
|
|
},
|
|
|
|
|
|
preferences: {
|
|
|
|
|
|
visible: preferencesButtonPosition.value.header,
|
|
|
|
|
|
slotName: 'preferences',
|
|
|
|
|
|
},
|
|
|
|
|
|
themeToggle: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.themeToggle &&
|
|
|
|
|
|
preferences.widget.themeToggleButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'theme-toggle',
|
|
|
|
|
|
},
|
|
|
|
|
|
languageToggle: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.languageToggle &&
|
|
|
|
|
|
preferences.widget.languageToggleButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'language-toggle',
|
|
|
|
|
|
},
|
|
|
|
|
|
timezone: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.timezone &&
|
|
|
|
|
|
preferences.widget.timezoneButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'timezone',
|
|
|
|
|
|
},
|
|
|
|
|
|
fullscreen: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.fullscreen &&
|
|
|
|
|
|
preferences.widget.fullscreenButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'fullscreen',
|
|
|
|
|
|
},
|
|
|
|
|
|
notification: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.notification &&
|
|
|
|
|
|
preferences.widget.notificationButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'notification',
|
|
|
|
|
|
},
|
|
|
|
|
|
lockScreenBtn: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.lockScreen &&
|
|
|
|
|
|
preferences.widget.lockScreenButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'lock-screen-btn',
|
|
|
|
|
|
},
|
|
|
|
|
|
logoutBtn: {
|
|
|
|
|
|
visible: preferences.widget.logoutButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'logout-btn',
|
|
|
|
|
|
},
|
|
|
|
|
|
refresh: {
|
|
|
|
|
|
visible:
|
|
|
|
|
|
preferences.widget.refresh &&
|
|
|
|
|
|
preferences.widget.refreshButtonPosition === 'header',
|
|
|
|
|
|
slotName: 'refresh',
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (const key of preferences.widget.order) {
|
|
|
|
|
|
const check = widgetChecks[key];
|
|
|
|
|
|
if (check?.visible) {
|
|
|
|
|
|
list.push({ index: REFERENCE_VALUE + list.length, name: check.slotName });
|
|
|
|
|
|
}
|
2026-07-18 22:46:31 +08:00
|
|
|
|
}
|
2024-08-06 22:32:51 +08:00
|
|
|
|
|
2026-07-19 20:38:30 +08:00
|
|
|
|
// 用户插槽(header-right-N)追加在后面
|
2024-08-06 22:32:51 +08:00
|
|
|
|
Object.keys(slots).forEach((key) => {
|
|
|
|
|
|
if (key.startsWith('header-right')) {
|
2026-05-20 17:24:18 +08:00
|
|
|
|
const slotIndex = Number(key.split('-')[2]);
|
|
|
|
|
|
const index = Number.isNaN(slotIndex) ? nextIndex(list) : slotIndex;
|
|
|
|
|
|
list.push({ index, name: key });
|
2024-08-06 22:32:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-05-20 17:24:18 +08:00
|
|
|
|
// 最后追加用户下拉框,若是索引值超过1000时则固定在1000(适配用户按钮不在最后的场景)
|
|
|
|
|
|
const userDropdownIndex = Math.min(1000, nextIndex(list));
|
|
|
|
|
|
list.push({ index: userDropdownIndex, name: 'user-dropdown' });
|
|
|
|
|
|
// 按照索引排序,保证插槽顺序
|
2025-11-17 12:15:14 +08:00
|
|
|
|
return list.toSorted((a, b) => a.index - b.index);
|
2024-08-06 22:32:51 +08:00
|
|
|
|
});
|
2024-08-14 23:02:39 +08:00
|
|
|
|
|
2024-08-06 22:32:51 +08:00
|
|
|
|
const leftSlots = computed(() => {
|
2026-05-20 17:24:18 +08:00
|
|
|
|
const list: Array<SlotItem> = [];
|
2024-08-06 22:32:51 +08:00
|
|
|
|
|
|
|
|
|
|
Object.keys(slots).forEach((key) => {
|
2026-05-20 17:24:18 +08:00
|
|
|
|
// 适配插槽名称,例如第一个插槽名:header-left-1
|
2024-08-06 22:32:51 +08:00
|
|
|
|
if (key.startsWith('header-left')) {
|
2026-05-20 17:24:18 +08:00
|
|
|
|
// 取第三个占位的数字,若是第三个占位不是数字,则自动分配排序索引
|
|
|
|
|
|
const slotIndex = Number(key.split('-')[2]);
|
|
|
|
|
|
const index = Number.isNaN(slotIndex) ? nextIndex(list) : slotIndex;
|
|
|
|
|
|
list.push({ index, name: key });
|
2024-08-06 22:32:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-05-20 17:24:18 +08:00
|
|
|
|
// 按照索引排序,保证插槽顺序
|
2025-11-17 12:15:14 +08:00
|
|
|
|
return list.toSorted((a, b) => a.index - b.index);
|
2024-08-06 22:32:51 +08:00
|
|
|
|
});
|
2024-08-28 09:58:59 +08:00
|
|
|
|
|
2026-05-20 17:24:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取列表下一个索引值(用于排序)
|
|
|
|
|
|
* @param list 列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
function nextIndex(list: Array<SlotItem>) {
|
|
|
|
|
|
const index =
|
|
|
|
|
|
list.length > 0 ? Math.max(...list.map((item) => item.index)) : 0;
|
|
|
|
|
|
return index + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-28 09:58:59 +08:00
|
|
|
|
function clearPreferencesAndLogout() {
|
|
|
|
|
|
emit('clearPreferencesAndLogout');
|
|
|
|
|
|
}
|
2024-05-19 21:20:42 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-06-06 23:24:54 +08:00
|
|
|
|
<LockModal
|
|
|
|
|
|
v-if="preferences.widget.lockScreen"
|
|
|
|
|
|
:avatar="avatar"
|
|
|
|
|
|
:text="text"
|
|
|
|
|
|
@submit="handleSubmitLock"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<LogoutModal
|
|
|
|
|
|
:cancel-text="$t('common.cancel')"
|
|
|
|
|
|
:confirm-text="$t('common.confirm')"
|
|
|
|
|
|
:fullscreen-button="false"
|
|
|
|
|
|
:title="$t('common.prompt')"
|
|
|
|
|
|
centered
|
|
|
|
|
|
content-class="px-8 min-h-10"
|
|
|
|
|
|
footer-class="border-none mb-3 mr-3"
|
|
|
|
|
|
header-class="border-none"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ $t('ui.widgets.logoutTip') }}
|
|
|
|
|
|
</LogoutModal>
|
|
|
|
|
|
|
2024-08-06 22:32:51 +08:00
|
|
|
|
<template
|
2024-10-06 17:27:32 +08:00
|
|
|
|
v-for="slot in leftSlots.filter((item) => item.index < REFERENCE_VALUE)"
|
2024-08-06 22:32:51 +08:00
|
|
|
|
:key="slot.name"
|
|
|
|
|
|
>
|
2024-10-06 17:27:32 +08:00
|
|
|
|
<slot :name="slot.name">
|
|
|
|
|
|
<template v-if="slot.name === 'refresh'">
|
2024-10-09 22:25:37 +08:00
|
|
|
|
<VbenIconButton class="my-0 mr-1 rounded-md" @click="refresh">
|
2024-10-06 17:27:32 +08:00
|
|
|
|
<RotateCw class="size-4" />
|
|
|
|
|
|
</VbenIconButton>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</slot>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
</template>
|
2024-05-19 21:20:42 +08:00
|
|
|
|
<div class="flex-center hidden lg:block">
|
|
|
|
|
|
<slot name="breadcrumb"></slot>
|
|
|
|
|
|
</div>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
<template
|
2024-10-06 17:27:32 +08:00
|
|
|
|
v-for="slot in leftSlots.filter((item) => item.index > REFERENCE_VALUE)"
|
2024-08-06 22:32:51 +08:00
|
|
|
|
:key="slot.name"
|
|
|
|
|
|
>
|
|
|
|
|
|
<slot :name="slot.name"></slot>
|
|
|
|
|
|
</template>
|
2024-12-28 16:16:48 +08:00
|
|
|
|
<div
|
|
|
|
|
|
:class="`menu-align-${preferences.header.menuAlign}`"
|
|
|
|
|
|
class="flex h-full min-w-0 flex-1 items-center"
|
|
|
|
|
|
>
|
2024-05-19 21:20:42 +08:00
|
|
|
|
<slot name="menu"></slot>
|
|
|
|
|
|
</div>
|
2026-03-10 05:08:45 +08:00
|
|
|
|
<div class="flex h-full min-w-0 shrink-0 items-center">
|
2024-08-06 22:32:51 +08:00
|
|
|
|
<template v-for="slot in rightSlots" :key="slot.name">
|
|
|
|
|
|
<slot :name="slot.name">
|
|
|
|
|
|
<template v-if="slot.name === 'global-search'">
|
|
|
|
|
|
<GlobalSearch
|
|
|
|
|
|
:enable-shortcut-key="globalSearchShortcutKey"
|
|
|
|
|
|
:menus="accessStore.accessMenus"
|
2024-08-26 23:17:27 +08:00
|
|
|
|
class="mr-1 sm:mr-4"
|
2024-08-06 22:32:51 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
2024-08-14 23:02:39 +08:00
|
|
|
|
|
|
|
|
|
|
<template v-else-if="slot.name === 'preferences'">
|
2026-07-21 22:36:42 +08:00
|
|
|
|
<VbenTooltip side="bottom">
|
|
|
|
|
|
<template #trigger>
|
|
|
|
|
|
<PreferencesButton
|
|
|
|
|
|
class="mr-1"
|
|
|
|
|
|
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
{{ $t('preferences.title') }}
|
|
|
|
|
|
</VbenTooltip>
|
2024-08-14 23:02:39 +08:00
|
|
|
|
</template>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
<template v-else-if="slot.name === 'theme-toggle'">
|
2026-07-21 22:36:42 +08:00
|
|
|
|
<VbenTooltip side="bottom">
|
|
|
|
|
|
<template #trigger>
|
|
|
|
|
|
<ThemeToggle class="mt-0.5 mr-1" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
{{ $t('preferences.theme.title') }}
|
|
|
|
|
|
</VbenTooltip>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="slot.name === 'language-toggle'">
|
2026-07-21 22:36:42 +08:00
|
|
|
|
<VbenTooltip side="bottom">
|
|
|
|
|
|
<template #trigger>
|
|
|
|
|
|
<LanguageToggle class="mr-1" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
{{ $t('preferences.widget.languageToggle') }}
|
|
|
|
|
|
</VbenTooltip>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="slot.name === 'fullscreen'">
|
2026-07-21 22:36:42 +08:00
|
|
|
|
<VbenFullScreen
|
|
|
|
|
|
class="mr-1"
|
|
|
|
|
|
:tooltip="$t('preferences.widget.fullscreen')"
|
|
|
|
|
|
/>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
</template>
|
2025-10-29 19:47:10 +08:00
|
|
|
|
<template v-else-if="slot.name === 'timezone'">
|
2026-03-14 18:11:08 +08:00
|
|
|
|
<TimezoneButton class="mt-0.5 mr-1" />
|
2025-10-29 19:47:10 +08:00
|
|
|
|
</template>
|
2026-06-06 23:24:54 +08:00
|
|
|
|
<template v-else-if="slot.name === 'lock-screen-btn'">
|
|
|
|
|
|
<VbenIconButton
|
|
|
|
|
|
class="mr-1"
|
|
|
|
|
|
:tooltip="$t('ui.widgets.lockScreen.title')"
|
|
|
|
|
|
@click="handleOpenLock"
|
|
|
|
|
|
>
|
|
|
|
|
|
<LockKeyhole class="size-4" />
|
|
|
|
|
|
</VbenIconButton>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="slot.name === 'logout-btn'">
|
|
|
|
|
|
<VbenIconButton
|
|
|
|
|
|
class="mr-1"
|
|
|
|
|
|
:tooltip="$t('common.logout')"
|
|
|
|
|
|
@click="handleLogout"
|
|
|
|
|
|
>
|
|
|
|
|
|
<LogOut class="size-4" />
|
|
|
|
|
|
</VbenIconButton>
|
|
|
|
|
|
</template>
|
2026-07-21 22:36:42 +08:00
|
|
|
|
<template v-else-if="slot.name === 'notification'">
|
|
|
|
|
|
<VbenTooltip side="bottom">
|
|
|
|
|
|
<template #trigger>
|
|
|
|
|
|
<Notification class="mr-1" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
{{ $t('preferences.widget.notification') }}
|
|
|
|
|
|
</VbenTooltip>
|
|
|
|
|
|
</template>
|
2026-07-19 20:38:30 +08:00
|
|
|
|
<template v-else-if="slot.name === 'refresh'">
|
|
|
|
|
|
<VbenIconButton
|
|
|
|
|
|
class="my-0 mr-1 rounded-md"
|
|
|
|
|
|
:tooltip="$t('preferences.widget.refresh')"
|
|
|
|
|
|
@click="refresh"
|
|
|
|
|
|
>
|
|
|
|
|
|
<RotateCw class="size-4" />
|
|
|
|
|
|
</VbenIconButton>
|
|
|
|
|
|
</template>
|
2024-08-06 22:32:51 +08:00
|
|
|
|
</slot>
|
|
|
|
|
|
</template>
|
2024-05-19 21:20:42 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
2024-12-28 16:16:48 +08:00
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.menu-align-start {
|
|
|
|
|
|
--menu-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.menu-align-center {
|
|
|
|
|
|
--menu-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.menu-align-end {
|
|
|
|
|
|
--menu-align: end;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|