feat: Enhance navigation with collapsible groups and improved NavTitle component

This commit is contained in:
Johnpaul
2025-09-17 04:24:46 +01:00
parent d48ed607c7
commit 632f53ab45
3 changed files with 68 additions and 16 deletions

View File

@@ -1,13 +1,55 @@
<template>
<h3
class="m-0 px-3 py-0 pt-5 text-xs font-bold uppercase text-neutral-400 dark-theme:text-neutral-400"
<div
:class="
cn(
'flex items-center justify-between m-0 px-3 py-0 pt-5',
collapsible && 'cursor-pointer select-none'
)
"
@click="collapsible && toggleCollapse()"
>
{{ title }}
</h3>
<h3
class="text-xs font-bold uppercase text-neutral-400 dark-theme:text-neutral-400"
>
{{ title }}
</h3>
<i
v-if="collapsible"
:class="
cn(
'pi transition-transform duration-200 text-xs text-neutral-400 dark-theme:text-neutral-400',
isCollapsed ? 'pi-chevron-right' : 'pi-chevron-down'
)
"
/>
</div>
</template>
<script setup lang="ts">
const { title } = defineProps<{
import { computed } from 'vue'
import { cn } from '@/utils/tailwindUtil'
const {
title,
modelValue = false,
collapsible = false
} = defineProps<{
title: string
modelValue?: boolean
collapsible?: boolean
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
}>()
const isCollapsed = computed({
get: () => modelValue,
set: (value: boolean) => emit('update:modelValue', value)
})
const toggleCollapse = () => {
isCollapsed.value = !isCollapsed.value
}
</script>

View File

@@ -10,16 +10,22 @@
<nav class="flex-1 px-3 py-4 flex flex-col gap-1">
<template v-for="(item, index) in navItems" :key="index">
<div v-if="'items' in item" class="flex flex-col gap-2">
<NavTitle :title="item.title" />
<NavItem
v-for="subItem in item.items"
:key="subItem.id"
:icon="subItem.icon"
:active="activeItem === subItem.id"
@click="activeItem = subItem.id"
>
{{ subItem.label }}
</NavItem>
<NavTitle
v-model="collapsedGroups[item.title]"
:title="item.title"
:collapsible="item.collapsible"
/>
<template v-if="!item.collapsible || !collapsedGroups[item.title]">
<NavItem
v-for="subItem in item.items"
:key="subItem.id"
:icon="subItem.icon"
:active="activeItem === subItem.id"
@click="activeItem = subItem.id"
>
{{ subItem.label }}
</NavItem>
</template>
</div>
<div v-else class="flex flex-col gap-2">
<NavItem
@@ -36,7 +42,7 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import NavItem from '@/components/widget/nav/NavItem.vue'
import NavTitle from '@/components/widget/nav/NavTitle.vue'
@@ -53,6 +59,9 @@ const emit = defineEmits<{
'update:modelValue': [value: string | null]
}>()
// Track collapsed state for each group
const collapsedGroups = ref<Record<string, boolean>>({})
const getFirstItemId = () => {
if (!navItems || navItems.length === 0) {
return null

View File

@@ -8,4 +8,5 @@ export interface NavGroupData {
title: string
items: NavItemData[]
icon?: string
collapsible?: boolean
}