11<script setup lang="ts">
22import { computed , ref } from ' vue' ;
3+
4+ import {
5+ UBadge ,
6+ UButton ,
7+ UCheckbox ,
8+ UDropdownMenu ,
9+ UIcon ,
10+ UNavigationMenu ,
11+ USwitch ,
12+ UTabs ,
13+ } from ' #components' ;
14+
315import type { Component } from ' vue' ;
4- import { UIcon , UBadge , UTabs } from ' #components' ;
516
617interface NavigationItem {
718 id: string ;
819 label: string ;
920 icon? : string ;
1021 badge? : string | number ;
22+ slot? : string ;
23+ status? : {
24+ label: string ;
25+ dotColor: string ; // Tailwind color class like 'bg-green-500'
26+ }[];
27+ children? : NavigationItem []; // For grouped/nested items
28+ isGroup? : boolean ; // Indicates if this is a group/folder
29+ }
30+
31+ interface NavigationMenuItem extends NavigationItem {
32+ to? : string ;
33+ slot? : string ;
34+ onClick? : () => void ;
1135}
1236
1337interface TabItem {
@@ -34,11 +58,58 @@ const props = withDefaults(defineProps<Props>(), {
3458
3559const selectedNavigationId = ref (props .defaultNavigationId || props .navigationItems [0 ]?.id || ' ' );
3660const selectedTab = ref (props .defaultTabKey || ' 0' );
61+ const selectedItems = ref <string []>([]);
62+
63+ const selectedNavigationItem = computed (() => {
64+ // First check top-level items
65+ const topLevel = props .navigationItems .find ((item ) => item .id === selectedNavigationId .value );
66+ if (topLevel ) return topLevel ;
3767
38- const selectedNavigationItem = computed (() =>
39- props .navigationItems .find ((item ) => item .id === selectedNavigationId .value )
68+ // Then check nested items
69+ for (const item of props .navigationItems ) {
70+ if (item .children ) {
71+ const nested = item .children .find ((child ) => child .id === selectedNavigationId .value );
72+ if (nested ) return nested ;
73+ }
74+ }
75+
76+ return undefined ;
77+ });
78+
79+ const navigationMenuItems = computed ((): NavigationMenuItem [] =>
80+ props .navigationItems .map ((item ) => ({
81+ label: item .label ,
82+ icon: item .icon ,
83+ id: item .id ,
84+ badge: item .badge ,
85+ slot: item .slot ,
86+ onClick : () => selectNavigationItem (item .id ),
87+ children: item .children ?.map ((child ) => ({
88+ label: child .label ,
89+ icon: child .icon ,
90+ id: child .id ,
91+ badge: child .badge ,
92+ slot: child .slot ,
93+ onClick : () => selectNavigationItem (child .id ),
94+ status: child .status ,
95+ })),
96+ isGroup: item .isGroup ,
97+ }))
4098);
4199
100+ const toggleItemSelection = (itemId : string ) => {
101+ const index = selectedItems .value .indexOf (itemId );
102+ if (index > - 1 ) {
103+ selectedItems .value .splice (index , 1 );
104+ } else {
105+ selectedItems .value .push (itemId );
106+ }
107+ };
108+
109+ const isItemSelected = (itemId : string ) => {
110+ return selectedItems .value .includes (itemId );
111+ };
112+
42113const tabItems = computed (() =>
43114 props .tabs .map ((tab ) => ({
44115 label: tab .label ,
@@ -74,28 +145,75 @@ const getCurrentTabProps = () => {
74145 <div class =" flex h-full gap-6" >
75146 <!-- Left Navigation Section -->
76147 <div class =" w-64 flex-shrink-0" >
77- <nav class =" space-y-1" >
78- <button
79- v-for =" item in navigationItems"
80- :key =" item.id"
81- :class =" [
82- 'group flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
83- selectedNavigationId === item.id
84- ? 'bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-white'
85- : 'text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-white',
86- ]"
87- @click =" selectNavigationItem(item.id)"
88- >
89- <UIcon v-if =" item.icon" :name =" item.icon" class =" h-5 w-5 flex-shrink-0" />
90- <span class =" truncate" >{{ item.label }}</span >
91- <UBadge v-if =" item.badge" size =" xs" :label =" String(item.badge)" class =" ml-auto" />
92- </button >
93- </nav >
148+ <UNavigationMenu :items =" navigationMenuItems" orientation =" vertical" >
149+ <template v-for =" navItem in navigationMenuItems " :key =" navItem .id " #[navItem .slot ]>
150+ <div class =" flex items-center gap-3" >
151+ <UCheckbox
152+ :model-value =" isItemSelected(navItem.id)"
153+ class =" flex-shrink-0"
154+ @update:model-value =" toggleItemSelection(navItem.id)"
155+ @click.stop
156+ />
157+ <UIcon v-if =" navItem.icon" :name =" navItem.icon" class =" h-5 w-5 flex-shrink-0" />
158+ <span class =" truncate flex-1" >{{ navItem.label }}</span >
159+ <UBadge v-if =" navItem.badge" size =" xs" :label =" String(navItem.badge)" />
160+ </div >
161+ </template >
162+ </UNavigationMenu >
94163 </div >
95164
96165 <!-- Right Content Section -->
97166 <div class =" flex-1 min-w-0" >
98- <UTabs v-model =" selectedTab" :items =" tabItems" class =" w-full" />
167+ <div v-if =" selectedNavigationItem" class =" mb-6 flex items-center justify-between" >
168+ <div class =" flex items-center gap-3" >
169+ <UIcon
170+ v-if =" selectedNavigationItem.icon"
171+ :name =" selectedNavigationItem.icon"
172+ class =" h-8 w-8"
173+ />
174+ <h1 class =" text-2xl font-semibold text-gray-900 dark:text-white" >
175+ {{ selectedNavigationItem.label }}
176+ </h1 >
177+
178+ <!-- Status Indicators -->
179+ <div v-if =" selectedNavigationItem.status" class =" flex items-center gap-4" >
180+ <UBadge
181+ v-for =" (statusItem, index) in selectedNavigationItem.status"
182+ :key =" index"
183+ variant =" subtle"
184+ color =" neutral"
185+ size =" sm"
186+ >
187+ <div :class =" ['h-2 w-2 rounded-full mr-2', statusItem.dotColor]" />
188+ {{ statusItem.label }}
189+ </UBadge >
190+ </div >
191+ </div >
192+
193+ <!-- Right Side Controls -->
194+ <div class =" flex items-center gap-4" >
195+ <div class =" flex items-center gap-3" >
196+ <span class =" text-sm font-medium" >Autostart</span >
197+ <USwitch :model-value =" true" />
198+ </div >
199+
200+ <!-- Manage Dropdown -->
201+ <UDropdownMenu
202+ :items =" [
203+ [{ label: 'Edit', icon: 'i-lucide-edit' }],
204+ [{ label: 'Remove', icon: 'i-lucide-trash-2' }],
205+ [{ label: 'Restart', icon: 'i-lucide-refresh-cw' }],
206+ [{ label: 'Force Update', icon: 'i-lucide-download' }],
207+ ]"
208+ >
209+ <UButton variant =" outline" color =" primary" trailing-icon =" i-lucide-chevron-down" >
210+ Manage
211+ </UButton >
212+ </UDropdownMenu >
213+ </div >
214+ </div >
215+
216+ <UTabs v-model =" selectedTab" variant =" link" :items =" tabItems" class =" w-full" />
99217
100218 <!-- Tab Content -->
101219 <div class =" mt-6" >
0 commit comments