refactor: update breadcrumb bar visibility handling across components
This commit is contained in:
@@ -120,7 +120,7 @@
|
||||
:logout-label="logoutLabel"
|
||||
:search-config="searchConfig"
|
||||
:search-value="searchValue"
|
||||
:show-breadcrumb-bar="showBreadcrumbBar"
|
||||
:breadcrumb-bar-visible="breadcrumbBarVisible"
|
||||
:show-favorites-bar="showFavoritesBar"
|
||||
:theme-toggle-label="themeToggleLabel"
|
||||
:toolbar-actions="toolbarActions"
|
||||
@@ -131,7 +131,7 @@
|
||||
@toggle-drawer="drawer = !drawer"
|
||||
@toggle-theme="toggleTheme"
|
||||
@update:search-value="searchValue = $event"
|
||||
@update:show-breadcrumb-bar="showBreadcrumbBar = $event"
|
||||
@update:breadcrumb-bar-visible="breadcrumbBarVisible = $event"
|
||||
@update:show-favorites-bar="showFavoritesBar = $event"
|
||||
>
|
||||
<template v-if="$slots.actions" #actions>
|
||||
@@ -155,7 +155,7 @@
|
||||
:breadcrumb-items="breadcrumbItems"
|
||||
:features="features"
|
||||
:is-mobile="isMobile"
|
||||
:show-breadcrumb-bar="showBreadcrumbBar"
|
||||
:breadcrumb-bar-visible="breadcrumbBarVisible"
|
||||
:show-favorites-bar="showFavoritesBar"
|
||||
@toggle-favorites-bar="toggleFavoritesBar"
|
||||
>
|
||||
@@ -389,7 +389,7 @@ const {
|
||||
mobileMenuLevels,
|
||||
openMobileFavoritesPanel,
|
||||
opened,
|
||||
showBreadcrumbBar,
|
||||
breadcrumbBarVisible,
|
||||
showFavoritesBar,
|
||||
toggleFavoritesBar,
|
||||
toggleSidebar,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<v-col
|
||||
v-if="features.showBreadcrumb && showBreadcrumbBar && !isMobile"
|
||||
v-if="features.showBreadcrumb && breadcrumbBarVisible && !isMobile"
|
||||
class="d-flex align-center justify-space-between pr-2 pl-3 py-1 bg-surface">
|
||||
<v-breadcrumbs class="pa-0" density="compact" :items="breadcrumbItems">
|
||||
<template #prepend>
|
||||
@@ -32,7 +32,7 @@ import { mdiChevronRight } from '@mdi/js'
|
||||
|
||||
interface Props {
|
||||
features?: AdminLayoutFeatures
|
||||
showBreadcrumbBar?: boolean
|
||||
breadcrumbBarVisible?: boolean
|
||||
isMobile?: boolean
|
||||
breadcrumbItems?: AdminLayoutBreadcrumbItem[]
|
||||
showFavoritesBar?: boolean
|
||||
@@ -47,7 +47,7 @@ withDefaults(defineProps<Props>(), {
|
||||
showToolbarActions: true,
|
||||
showUserInfo: true,
|
||||
}),
|
||||
showBreadcrumbBar: true,
|
||||
breadcrumbBarVisible: true,
|
||||
isMobile: false,
|
||||
breadcrumbItems: () => [],
|
||||
showFavoritesBar: true,
|
||||
@@ -60,8 +60,12 @@ const emit = defineEmits<{
|
||||
function getBreadcrumbItem (item: unknown): AdminLayoutBreadcrumbItem | null {
|
||||
if (typeof item !== 'object' || item === null) return null
|
||||
|
||||
if ('title' in item) {
|
||||
return item as AdminLayoutBreadcrumbItem
|
||||
}
|
||||
|
||||
const raw = (item as { raw?: unknown }).raw
|
||||
if (typeof raw !== 'object' || raw === null) return null
|
||||
if (typeof raw !== 'object' || raw === null || !('title' in raw)) return null
|
||||
|
||||
return raw as AdminLayoutBreadcrumbItem
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
<v-btn v-if="isMobile" :icon="mdiMenu" size="small" variant="text" @click="emit('toggle-drawer')"></v-btn>
|
||||
|
||||
<div v-if="features.showSearch" class="search-input-wrapper">
|
||||
<span id="admin-layout-search-label" class="sr-only">{{ searchConfig.label }}</span>
|
||||
<v-text-field
|
||||
v-model="searchValueModel" :aria-label="searchConfig.label" class="search-input" density="compact"
|
||||
hide-details :placeholder="searchConfig.placeholder" variant="outlined"
|
||||
v-model="searchValueModel" aria-labelledby="admin-layout-search-label" :aria-label="searchConfig.label"
|
||||
class="search-input" density="compact" hide-details name="layout-search"
|
||||
:placeholder="searchConfig.placeholder" variant="outlined"
|
||||
@keyup.enter="triggerSearch">
|
||||
<template v-if="!isMobile" #prepend-inner>
|
||||
<v-icon size="small" :icon="mdiMagnify" />
|
||||
@@ -87,7 +89,7 @@ v-bind="{ ...menuProps, ...tooltipProps }" :aria-label="toolbarActions.settingsL
|
||||
</v-switch>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-switch v-model="showBreadcrumbBarModel" color="primary" density="comfortable" hide-details>
|
||||
<v-switch v-model="breadcrumbBarVisibleModel" color="primary" density="comfortable" hide-details>
|
||||
<template #label>
|
||||
<span class="text-body-2" style="width: 8ch;">路徑</span>
|
||||
</template>
|
||||
@@ -132,7 +134,7 @@ interface Props {
|
||||
logoutLabel?: string
|
||||
themeToggleLabel?: string
|
||||
showFavoritesBar?: boolean
|
||||
showBreadcrumbBar?: boolean
|
||||
breadcrumbBarVisible?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -163,7 +165,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
logoutLabel: '',
|
||||
themeToggleLabel: '',
|
||||
showFavoritesBar: true,
|
||||
showBreadcrumbBar: true,
|
||||
breadcrumbBarVisible: true,
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -174,7 +176,7 @@ const emit = defineEmits<{
|
||||
logout: []
|
||||
'toggle-theme': []
|
||||
'update:showFavoritesBar': [value: boolean]
|
||||
'update:showBreadcrumbBar': [value: boolean]
|
||||
'update:breadcrumbBarVisible': [value: boolean]
|
||||
}>()
|
||||
|
||||
const searchValueModel = computed({
|
||||
@@ -187,9 +189,9 @@ const showFavoritesBarModel = computed({
|
||||
set: (value) => emit('update:showFavoritesBar', value),
|
||||
})
|
||||
|
||||
const showBreadcrumbBarModel = computed({
|
||||
get: () => props.showBreadcrumbBar,
|
||||
set: (value) => emit('update:showBreadcrumbBar', value),
|
||||
const breadcrumbBarVisibleModel = computed({
|
||||
get: () => props.breadcrumbBarVisible,
|
||||
set: (value) => emit('update:breadcrumbBarVisible', value),
|
||||
})
|
||||
|
||||
function triggerSearch () {
|
||||
@@ -214,4 +216,16 @@ function triggerSearch () {
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<v-list v-model:opened="openedModel" color="primary" density="compact" prepend-gap="8">
|
||||
<template v-for="item in menuItems" :key="item.path ?? item.title">
|
||||
<v-list-group v-if="item.subItems?.length" class="menu-group" :value="`menu:${item.path ?? item.title}`">
|
||||
<v-list-group v-if="item.subItems?.length" :id="getGroupId(item)" :value="getGroupValue(item)">
|
||||
<template #activator="{ props: activatorProps }">
|
||||
<v-list-item
|
||||
v-bind="isShrink ? undefined : activatorProps" :class="{ 'px-0': isShrink }"
|
||||
@@ -27,7 +27,8 @@ v-if="!isShrink && getItemCount(item) > 0" class="menu-count" color="secondary"
|
||||
<template v-for="subItem in item.subItems" :key="subItem.path ?? subItem.title">
|
||||
<v-list-group
|
||||
v-if="subItem.subItems?.length"
|
||||
class="menu-group" :value="`menu:${item.path ?? item.title}::${subItem.path ?? subItem.title}`">
|
||||
:id="getGroupId(subItem, getGroupId(item))"
|
||||
:value="getGroupValue(subItem, getGroupValue(item))">
|
||||
<template #activator="{ props: subProps }">
|
||||
<v-list-item
|
||||
v-bind="subProps" :link="isNavigable(subItem)"
|
||||
@@ -151,6 +152,37 @@ function getItemCount (item: AdminLayoutMenuItem) {
|
||||
}, 0)
|
||||
return countLeaf(item.subItems)
|
||||
}
|
||||
|
||||
function getGroupValue(item: AdminLayoutMenuItem, parentKey?: string) {
|
||||
const rawKey = item.path ?? item.title ?? 'group'
|
||||
const normalizedKey = Array.from(rawKey.trim())
|
||||
.map((char) => {
|
||||
if (/[a-z0-9]/i.test(char)) {
|
||||
return char.toLowerCase()
|
||||
}
|
||||
|
||||
return `u${char.codePointAt(0)?.toString(16)}`
|
||||
})
|
||||
.join('-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
|
||||
if (!parentKey) {
|
||||
return `menu-${normalizedKey || 'group'}`
|
||||
}
|
||||
|
||||
return `${parentKey}__${normalizedKey || 'group'}`
|
||||
}
|
||||
|
||||
function getGroupId(item: AdminLayoutMenuItem, parentId?: string) {
|
||||
const groupId = getGroupValue(item).replace(/^menu-/, 'group-')
|
||||
|
||||
if (!parentId) {
|
||||
return `nav-${groupId}`
|
||||
}
|
||||
|
||||
return `${parentId}__${groupId}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user