Skip to content

Commit 06dc6ae

Browse files
committed
refactor: updated layouts, new popup menu button
1 parent 0e2e620 commit 06dc6ae

File tree

6 files changed

+142
-126
lines changed

6 files changed

+142
-126
lines changed

src/components/PopupMenuButton.vue

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<script setup lang="ts">
2+
import { computed, useTemplateRef } from 'vue';
3+
import Menu from '@/components/primevue/menu/Menu.vue';
4+
import { ChevronDown } from 'lucide-vue-next';
5+
import { MenuItem } from '@/types';
6+
7+
const props = withDefaults(defineProps<{
8+
name: string,
9+
menuItems: MenuItem[],
10+
buttonLabel?: string,
11+
buttonSeverity?: 'secondary' | 'success' | 'info' | 'warn' | 'help' | 'danger' | 'contrast' | undefined,
12+
buttonVariant?: 'default' | 'outlined' | 'text' | 'link' | undefined,
13+
fixedPosition?: 'left' | 'right',
14+
}>(), {
15+
buttonSeverity: 'secondary',
16+
buttonVariant: 'default',
17+
});
18+
19+
const appendToId = computed(() => {
20+
return props.name.replace(/[^a-zA-Z0-9]/g, '') + '_append';
21+
});
22+
23+
type MenuType = InstanceType<typeof Menu>;
24+
const dropdownMenu = useTemplateRef<MenuType>(props.name);
25+
const toggleDropdownMenu = (event: Event) => {
26+
if (dropdownMenu.value) {
27+
dropdownMenu.value.toggle(event);
28+
}
29+
};
30+
31+
const menuPositionClasses = computed(() => {
32+
let classes = '';
33+
if (props?.fixedPosition) {
34+
switch (props?.fixedPosition) {
35+
case 'left':
36+
classes = 'left-auto! top-0! left-0';
37+
break;
38+
case 'right':
39+
classes = 'left-auto! top-0! right-0';
40+
break;
41+
default:
42+
break;
43+
}
44+
}
45+
46+
return classes;
47+
});
48+
</script>
49+
50+
<template>
51+
<div class="flex flex-col">
52+
<Button
53+
id="instances-menu-btn"
54+
:label="props?.buttonLabel"
55+
:pt:root:class="{ 'flex flex-row-reverse justify-between': props?.buttonLabel }"
56+
:severity="props.buttonSeverity"
57+
:variant="props.buttonVariant === 'default' ? undefined : props.buttonVariant"
58+
@click="toggleDropdownMenu($event)"
59+
>
60+
<template #icon>
61+
<slot
62+
v-if="$slots.toggleIcon"
63+
name="toggleIcon"
64+
/>
65+
<ChevronDown v-else />
66+
</template>
67+
</Button>
68+
<div
69+
v-if="props?.fixedPosition"
70+
:id="appendToId"
71+
class="relative"
72+
/>
73+
<Menu
74+
:ref="props.name"
75+
:appendTo="props?.fixedPosition ? `#${appendToId}` : 'body'"
76+
:model="props.menuItems"
77+
:pt:root:class="['z-1200 w-[12.5rem] min-w-max', menuPositionClasses]"
78+
popup
79+
/>
80+
</div>
81+
</template>

src/components/SelectColorModeButton.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
<script setup lang="ts">
22
import { ref, watchEffect, inject } from 'vue';
33
import { Sun, Moon, Monitor } from 'lucide-vue-next';
4+
import type { UseColorModeReturn } from '@vueuse/core';
45
5-
const colorMode = inject('colorMode');
6-
const selectedColorMode = ref(colorMode.value);
6+
const props = withDefaults(defineProps<{
7+
showLabel?: boolean,
8+
}>(), {
9+
showLabel: true,
10+
});
11+
12+
const colorMode = inject<UseColorModeReturn>('colorMode')!;
13+
const selectedColorMode = ref(colorMode?.value || 'auto');
714
815
const options = [
916
{ label: 'Light', value: 'light', icon: Sun },
@@ -23,7 +30,7 @@ watchEffect(() => colorMode.value = selectedColorMode.value);
2330
optionValue="value"
2431
>
2532
<template #option="{ option }">
26-
<component :is="option.icon" /> {{ option.label }}
33+
<component :is="option.icon" /> <span v-if="props.showLabel">{{ option.label }}</span>
2734
</template>
2835
</SelectButton>
2936
</template>

src/layouts/AppLayout.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script setup lang="ts">
2-
import AppLayout from '@/layouts/app/HeaderLayout.vue';
2+
import AppLayout from '@/layouts/app/SidebarLayout.vue';
3+
import { type MenuItem } from '@/types';
34
4-
const props = defineProps({
5-
breadcrumbs: {
6-
type: Array,
7-
default: () => [],
8-
},
5+
const props = withDefaults(defineProps<{
6+
breadcrumbs?: MenuItem[],
7+
}>(), {
8+
breadcrumbs: () => [],
99
});
1010
</script>
1111

src/layouts/UserSettingsLayout.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const sidebarNavItems = computed(() => [
4343
<nav class="flex flex-col space-x-0 space-y-1">
4444
<RouterLink
4545
v-for="item in sidebarNavItems"
46-
:key="item.route"
46+
:key="JSON.stringify(item.route)"
4747
:to="item.route"
4848
class="no-underline!"
4949
>

src/layouts/app/HeaderLayout.vue

Lines changed: 23 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
<script setup lang="ts">
2-
import { useTemplateRef } from 'vue';
32
import { useAppLayout } from '@/composables/useAppLayout';
4-
import { ChevronsUpDown, ChevronDown, Menu as MenuIcon } from 'lucide-vue-next';
3+
import { ChevronsUpDown, Menu as MenuIcon } from 'lucide-vue-next';
54
import Container from '@/components/Container.vue';
5+
import PopupMenuButton from '@/components/PopupMenuButton.vue';
66
import NavLogoLink from '@/components/NavLogoLink.vue';
7-
import Menu from '@/components/primevue/menu/Menu.vue';
87
import MenuBar from '@/components/primevue/menu/MenuBar.vue';
98
import PanelMenu from '@/components/primevue/menu/PanelMenu.vue';
109
import Breadcrumb from '@/components/primevue/menu/Breadcrumb.vue';
10+
import { MenuItem } from '@/types';
1111
12-
const props = defineProps({
13-
breadcrumbs: {
14-
type: Array,
15-
required: false,
16-
default: () => [],
17-
},
12+
const props = withDefaults(defineProps<{
13+
breadcrumbs?: MenuItem[],
14+
}>(), {
15+
breadcrumbs: () => [],
1816
});
1917
2018
const {
@@ -24,16 +22,6 @@ const {
2422
menuItems,
2523
userMenuItems,
2624
} = useAppLayout();
27-
28-
const userMenu = useTemplateRef('user-menu');
29-
const toggleUserMenu = (event) => {
30-
userMenu.value.$el.toggle(event);
31-
};
32-
33-
const mobileUserMenu = useTemplateRef('mobile-user-menu');
34-
const toggleMobileUserMenu = (event) => {
35-
mobileUserMenu.value.$el.toggle(event);
36-
};
3725
</script>
3826

3927
<template>
@@ -51,26 +39,15 @@ const toggleMobileUserMenu = (event) => {
5139
/>
5240
</div>
5341
<template #footer>
54-
<div class="flex flex-col">
55-
<Button
56-
id="mobile-user-menu-btn"
57-
:label="userName"
58-
severity="secondary"
59-
size="large"
60-
pt:root:class="flex flex-row-reverse justify-between"
61-
@click="toggleMobileUserMenu($event)"
62-
>
63-
<template #icon>
64-
<ChevronsUpDown />
65-
</template>
66-
</Button>
67-
<Menu
68-
ref="mobile-user-menu"
69-
:model="userMenuItems"
70-
pt:root:class="z-[1200]"
71-
popup
72-
/>
73-
</div>
42+
<PopupMenuButton
43+
name="mobile-user-menu-dd"
44+
:menu-items="userMenuItems"
45+
:button-label="userName"
46+
>
47+
<template #toggleIcon>
48+
<ChevronsUpDown />
49+
</template>
50+
</PopupMenuButton>
7451
</template>
7552
</Drawer>
7653
<ScrollTop
@@ -84,7 +61,7 @@ const toggleMobileUserMenu = (event) => {
8461
<MenuBar
8562
:key="currentRoute"
8663
:model="menuItems"
87-
pt:root:class="px-0 py-4 border-0 rounded-none dynamic-bg"
64+
pt:root:class="px-0 py-0 border-0 rounded-none bg-transparent h-[var(--header-height)]!"
8865
pt:button:class="hidden"
8966
>
9067
<template #start>
@@ -96,28 +73,12 @@ const toggleMobileUserMenu = (event) => {
9673
<!-- User Dropdown Menu -->
9774
<div class="hidden lg:flex items-center ms-6 space-x-3">
9875
<div class="flex flex-col">
99-
<Button
100-
id="user-menu-btn"
101-
:label="userName"
102-
pt:root:class="flex flex-row-reverse justify-between"
103-
severity="secondary"
104-
text
105-
@click="toggleUserMenu($event)"
106-
>
107-
<template #icon>
108-
<ChevronDown />
109-
</template>
110-
</Button>
111-
<div
112-
id="user-menu-append"
113-
class="relative"
114-
/>
115-
<Menu
116-
ref="user-menu"
117-
appendTo="#user-menu-append"
118-
:model="userMenuItems"
119-
pt:root:class="left-auto! top-0! right-0 z-[1200]"
120-
popup
76+
<PopupMenuButton
77+
name="desktop-user-menu-dd"
78+
button-variant="text"
79+
fixed-position="right"
80+
:menu-items="userMenuItems"
81+
:button-label="userName"
12182
/>
12283
</div>
12384
</div>
@@ -149,7 +110,6 @@ const toggleMobileUserMenu = (event) => {
149110
v-if="props.breadcrumbs.length"
150111
:model="props.breadcrumbs"
151112
/>
152-
153113
<!-- Page Content -->
154114
<slot />
155115
</Container>

src/layouts/app/SidebarLayout.vue

Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
<script setup lang="ts">
2-
import { useTemplateRef } from 'vue';
32
import { ChevronsUpDown, Menu as MenuIcon } from 'lucide-vue-next';
43
import { useAppLayout } from '@/composables/useAppLayout';
54
import Container from '@/components/Container.vue';
5+
import PopupMenuButton from '@/components/PopupMenuButton.vue';
66
import NavLogoLink from '@/components/NavLogoLink.vue';
7-
import Menu from '@/components/primevue/menu/Menu.vue';
87
import PanelMenu from '@/components/primevue/menu/PanelMenu.vue';
98
import Breadcrumb from '@/components/primevue/menu/Breadcrumb.vue';
9+
import { MenuItem } from 'primevue/menuitem';
1010
11-
const props = defineProps({
12-
breadcrumbs: {
13-
type: Array,
14-
required: false,
15-
default: () => [],
16-
},
11+
const props = withDefaults(defineProps<{
12+
breadcrumbs?: MenuItem[],
13+
}>(), {
14+
breadcrumbs: () => [],
1715
});
1816
1917
const {
@@ -22,16 +20,6 @@ const {
2220
menuItems,
2321
userMenuItems,
2422
} = useAppLayout();
25-
26-
const userMenu = useTemplateRef('user-menu');
27-
const toggleUserMenu = (event) => {
28-
userMenu.value.$el.toggle(event);
29-
};
30-
31-
const mobileUserMenu = useTemplateRef('mobile-user-menu');
32-
const toggleMobileUserMenu = (event) => {
33-
mobileUserMenu.value.$el.toggle(event);
34-
};
3523
</script>
3624

3725
<template>
@@ -49,26 +37,15 @@ const toggleMobileUserMenu = (event) => {
4937
/>
5038
</div>
5139
<template #footer>
52-
<div class="flex flex-col">
53-
<Button
54-
id="mobile-user-menu-btn"
55-
:label="userName"
56-
pt:root:class="flex flex-row-reverse justify-between"
57-
severity="secondary"
58-
size="large"
59-
@click="toggleMobileUserMenu($event)"
60-
>
61-
<template #icon>
62-
<ChevronsUpDown />
63-
</template>
64-
</Button>
65-
<Menu
66-
ref="mobile-user-menu"
67-
:model="userMenuItems"
68-
pt:root:class="z-[1200]"
69-
popup
70-
/>
71-
</div>
40+
<PopupMenuButton
41+
name="mobile-user-menu-dd"
42+
:menu-items="userMenuItems"
43+
:button-label="userName"
44+
>
45+
<template #toggleIcon>
46+
<ChevronsUpDown />
47+
</template>
48+
</PopupMenuButton>
7249
</template>
7350
</Drawer>
7451
<ScrollTop
@@ -118,24 +95,15 @@ const toggleMobileUserMenu = (event) => {
11895
</div>
11996
</div>
12097
<div>
121-
<Button
122-
id="user-menu-btn"
123-
:label="userName"
124-
pt:root:class="flex flex-row-reverse justify-between"
125-
severity="secondary"
126-
fluid
127-
@click="toggleUserMenu($event)"
98+
<PopupMenuButton
99+
name="desktop-user-menu-dd"
100+
:menu-items="userMenuItems"
101+
:button-label="userName"
128102
>
129-
<template #icon>
103+
<template #toggleIcon>
130104
<ChevronsUpDown />
131105
</template>
132-
</Button>
133-
<Menu
134-
ref="user-menu"
135-
:model="userMenuItems"
136-
pt:root:class="z-[1200]"
137-
popup
138-
/>
106+
</PopupMenuButton>
139107
</div>
140108
</div>
141109
</aside>

0 commit comments

Comments
 (0)