Skip to content

Commit 810a119

Browse files
committed
improvements: explicitly bind router menu component fallthrough attributes, TabMenu fixes, preserved expanded PanelMenu state for current route
1 parent 3357059 commit 810a119

File tree

10 files changed

+54
-21
lines changed

10 files changed

+54
-21
lines changed

resources/js/components/router-link-menus/Breadcrumb.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defineExpose({ $el: childRef })
2323
<template>
2424
<Breadcrumb
2525
ref="child-ref"
26-
v-bind="{ ...componentProps, pt: defaultPt, ptOptions: { mergeProps: ptViewMerge } }"
26+
v-bind="{ ...componentProps, ...$attrs, pt: defaultPt, ptOptions: { mergeProps: ptViewMerge } }"
2727
>
2828
<template #item="{ item, props }">
2929
<InertiaLink

resources/js/components/router-link-menus/ContextMenu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defineExpose({
2323
<template>
2424
<ContextMenu
2525
ref="child-ref"
26-
v-bind="{ ...componentProps, ptOptions: { mergeProps: ptViewMerge } }"
26+
v-bind="{ ...componentProps, ...$attrs, ptOptions: { mergeProps: ptViewMerge } }"
2727
>
2828
<template #item="{ item, props, hasSubmenu }">
2929
<InertiaLink

resources/js/components/router-link-menus/Menu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ defineExpose({
2222
<template>
2323
<Menu
2424
ref="child-ref"
25-
v-bind="{ ...componentProps, ptOptions: { mergeProps: ptViewMerge } }"
25+
v-bind="{ ...componentProps, ...$attrs, ptOptions: { mergeProps: ptViewMerge } }"
2626
>
2727
<template
2828
v-for="(_, slotName) in $slots"

resources/js/components/router-link-menus/Menubar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defineExpose({ $el: childRef })
2323
<template>
2424
<Menubar
2525
ref="child-ref"
26-
v-bind="{ ...componentProps, ptOptions: { mergeProps: ptViewMerge } }"
26+
v-bind="{ ...componentProps, ...$attrs, ptOptions: { mergeProps: ptViewMerge } }"
2727
>
2828
<template
2929
v-for="(_, slotName) in $slots"

resources/js/components/router-link-menus/PanelMenu.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ defineExpose({ $el: childRef })
2626
<template>
2727
<PanelMenu
2828
ref="child-ref"
29-
v-bind="{ ...componentProps, pt: defaultPt, ptOptions: { mergeProps: ptViewMerge } }"
29+
v-bind="{ ...componentProps, ...$attrs, pt: defaultPt, ptOptions: { mergeProps: ptViewMerge } }"
3030
>
3131
<template #item="{ item, root, active, props, hasSubmenu }">
3232
<Divider
@@ -91,4 +91,4 @@ defineExpose({ $el: childRef })
9191
</a>
9292
</template>
9393
</PanelMenu>
94-
</template>
94+
</template>

resources/js/components/router-link-menus/TabMenu.vue

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { useTemplateRef, computed } from 'vue'
33
import { usePage, Link as InertiaLink } from '@inertiajs/vue3'
44
import { route } from 'ziggy-js'
55
import Tabs from 'primevue/tabs'
6-
import TabList, { type TabListProps } from 'primevue/tablist'
7-
import Tab from 'primevue/tab'
6+
import Tab, { type TabsProps } from 'primevue/tabs'
7+
import TabList from 'primevue/tablist'
88
import type { MenuItem } from '@/types'
99
import { ptViewMerge } from '@/utils'
1010
11-
interface ExtendedTabListProps extends Omit<TabListProps, 'items'> {
12-
items?: MenuItem[] | undefined;
11+
interface ExtendedTabsProps extends Omit<TabsProps, 'value' | 'items'> {
12+
items: MenuItem[];
1313
}
14-
const componentProps = defineProps<ExtendedTabListProps>()
14+
const componentProps = defineProps<ExtendedTabsProps>()
1515
1616
const page = usePage()
1717
const currentRoute = computed(() => {
@@ -30,7 +30,7 @@ defineExpose({ $el: childRef })
3030
<template>
3131
<Tabs
3232
ref="child-ref"
33-
v-bind="{ ...componentProps, ptOptions: { mergeProps: ptViewMerge } }"
33+
v-bind="{ ...componentProps, ...$attrs, ptOptions: { mergeProps: ptViewMerge } }"
3434
:value="currentRoute ?? '/'"
3535
scrollable
3636
>
@@ -39,15 +39,14 @@ defineExpose({ $el: childRef })
3939
v-for="item in componentProps.items"
4040
:key="item.route"
4141
:href="item.route ?? ''"
42-
:class="['no-underline', { 'p-tab-active': item.active }]"
42+
:class="['p-tab no-underline', { 'p-tab-active': item.active }]"
4343
custom
4444
>
4545
<Tab
4646
v-if="item.route"
4747
:value="item.route"
4848
:class="[
49-
'flex items-center gap-2 hover:text-color',
50-
item.active ? 'p-tab-active' : 'text-muted-color',
49+
'flex flex-row items-center gap-2 hover:text-color',
5150
item.class
5251
]"
5352
:style="item.style"

resources/js/components/router-link-menus/TieredMenu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ defineExpose({ $el: childRef })
2020
<template>
2121
<TieredMenu
2222
ref="child-ref"
23-
v-bind="{ ...componentProps, ptOptions: { mergeProps: ptViewMerge } }"
23+
v-bind="{ ...componentProps, ...$attrs, ptOptions: { mergeProps: ptViewMerge } }"
2424
>
2525
<template
2626
v-for="(_, slotName) in $slots"

resources/js/composables/useAppLayout.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'
1+
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
22
import { usePage, useForm } from '@inertiajs/vue3'
33
import { LayoutGrid, House, Info, Settings, LogOut, ExternalLink, FileSearch, FolderGit2 } from 'lucide-vue-next'
44
import { MenuItem } from '@/types'
@@ -16,40 +16,47 @@ export function useAppLayout() {
1616
// Menu items
1717
const menuItems = computed<MenuItem[]>(() => [
1818
{
19+
key: 'home',
1920
label: 'Home',
2021
lucideIcon: House,
2122
route: route('welcome'),
2223
active: currentRoute.value == 'welcome',
2324
},
2425
{
26+
key: 'dashboard',
2527
label: 'Dashboard',
2628
lucideIcon: LayoutGrid,
2729
route: route('dashboard'),
2830
active: currentRoute.value == 'dashboard',
2931
},
3032
{
33+
key: 'resources',
3134
label: 'Resources',
3235
lucideIcon: Info,
3336
items: [
3437
{
38+
key: 'resources-laravel',
3539
label: 'Laravel Docs',
3640
url: 'https://laravel.com/docs/master',
3741
target: '_blank',
3842
lucideIcon: ExternalLink,
3943
},
4044
{
45+
key: 'resources-primevue',
4146
label: 'PrimeVue Docs',
4247
url: 'https://primevue.org/',
4348
target: '_blank',
4449
lucideIcon: ExternalLink,
4550
},
4651
{
52+
key: 'resources-starter-docs',
4753
label: 'Starter Kit Docs',
4854
url: 'https://connorabbas.github.io/laravel-primevue-starter-kit-docs/',
4955
target: '_blank',
5056
lucideIcon: FileSearch,
5157
},
5258
{
59+
key: 'resources-starter-repo',
5360
label: 'Starter Kit Repo',
5461
url: 'https://github.com/connorabbas/laravel-primevue-starter-kit',
5562
target: '_blank',
@@ -59,7 +66,28 @@ export function useAppLayout() {
5966
},
6067
])
6168

62-
// User menu and logout functionality.
69+
// Check/set expanded PanelMenu items based on active status, for non-persistent layouts
70+
const expandedKeys = ref<Record<string, boolean>>({})
71+
const updateExpandedKeys = () => {
72+
const keys: Record<string, boolean> = {}
73+
const hasActiveChild = (item: MenuItem): boolean => {
74+
if (item.items) {
75+
for (const child of item.items) {
76+
if (hasActiveChild(child)) {
77+
if (item.key) keys[item.key] = true
78+
return true
79+
}
80+
}
81+
}
82+
return !!item.active
83+
}
84+
menuItems.value.forEach(hasActiveChild)
85+
expandedKeys.value = keys
86+
}
87+
watch(currentRoute, () => {
88+
updateExpandedKeys()
89+
}, { immediate: true })
90+
6391
const logoutForm = useForm({})
6492
const logout = () => {
6593
logoutForm.post(route('logout'))
@@ -80,7 +108,7 @@ export function useAppLayout() {
80108
},
81109
]
82110

83-
// Mobile menu
111+
// Mobile drawer menu
84112
const mobileMenuOpen = ref(false)
85113
if (typeof window !== 'undefined') {
86114
const windowWidth = ref(window.innerWidth)
@@ -93,8 +121,8 @@ export function useAppLayout() {
93121
onUnmounted(() => {
94122
window.removeEventListener('resize', updateWidth)
95123
})
96-
watchEffect(() => {
97-
if (windowWidth.value > 1024) {
124+
watch(windowWidth, (newWidth) => {
125+
if (newWidth > 1024) {
98126
mobileMenuOpen.value = false
99127
}
100128
})
@@ -103,6 +131,7 @@ export function useAppLayout() {
103131
return {
104132
currentRoute,
105133
menuItems,
134+
expandedKeys,
106135
userMenuItems,
107136
mobileMenuOpen,
108137
logout,

resources/js/layouts/app/HeaderLayout.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const {
2323
currentRoute,
2424
mobileMenuOpen,
2525
menuItems,
26+
expandedKeys,
2627
userMenuItems,
2728
} = useAppLayout()
2829
</script>
@@ -38,6 +39,7 @@ const {
3839
>
3940
<div>
4041
<PanelMenu
42+
v-model:expandedKeys="expandedKeys"
4143
:model="menuItems"
4244
class="mt-1 w-full"
4345
/>

resources/js/layouts/app/SidebarLayout.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const page = usePage()
2121
const {
2222
mobileMenuOpen,
2323
menuItems,
24+
expandedKeys,
2425
userMenuItems,
2526
} = useAppLayout()
2627
</script>
@@ -36,6 +37,7 @@ const {
3637
>
3738
<div>
3839
<PanelMenu
40+
v-model:expandedKeys="expandedKeys"
3941
:model="menuItems"
4042
class="mt-1 w-full"
4143
/>
@@ -96,6 +98,7 @@ const {
9698
</div>
9799
<div>
98100
<PanelMenu
101+
v-model:expandedKeys="expandedKeys"
99102
:model="menuItems"
100103
class="mt-1 w-full"
101104
/>

0 commit comments

Comments
 (0)