mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-02 04:02:20 +00:00
feat(ui): Implement experimental UI layer with versioned architecture
- Reorganize components into v1/v2 versioned structure - Add common components for shared UI elements - Introduce composables for reusable logic - Restructure views into v1/v2 directories - Remove old component structure in favor of versioned approach - Update router and UI store for new architecture
This commit is contained in:
174
ComfyUI_vibe/src/components/v2/layout/WorkspaceSidebar.vue
Normal file
174
ComfyUI_vibe/src/components/v2/layout/WorkspaceSidebar.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
interface MenuItem {
|
||||
label: string
|
||||
icon: string
|
||||
route?: string
|
||||
badge?: number
|
||||
}
|
||||
|
||||
interface MenuGroup {
|
||||
label: string
|
||||
items: MenuItem[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
workspaceId: string
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const isTeam = computed(() => props.workspaceId === 'team')
|
||||
|
||||
const userMenuGroups = computed<MenuGroup[]>(() => [
|
||||
{
|
||||
label: 'Overview',
|
||||
items: [
|
||||
{ label: 'Dashboard', icon: 'pi pi-home', route: `/${props.workspaceId}` },
|
||||
{ label: 'Projects', icon: 'pi pi-folder', route: `/${props.workspaceId}/projects` },
|
||||
{ label: 'Canvases', icon: 'pi pi-objects-column', route: `/${props.workspaceId}/canvases` }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Library',
|
||||
items: [
|
||||
{ label: 'Workflows', icon: 'pi pi-sitemap', route: `/${props.workspaceId}/workflows` },
|
||||
{ label: 'Assets', icon: 'pi pi-images', route: `/${props.workspaceId}/assets` },
|
||||
{ label: 'Models', icon: 'pi pi-box', route: `/${props.workspaceId}/models` }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Account',
|
||||
items: [
|
||||
{ label: 'Settings', icon: 'pi pi-cog', route: `/${props.workspaceId}/settings` },
|
||||
{ label: 'API Keys', icon: 'pi pi-key', route: `/${props.workspaceId}/api-keys` }
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const teamMenuGroups = computed<MenuGroup[]>(() => [
|
||||
{
|
||||
label: 'Overview',
|
||||
items: [
|
||||
{ label: 'Dashboard', icon: 'pi pi-home', route: `/${props.workspaceId}` },
|
||||
{ label: 'Projects', icon: 'pi pi-folder', route: `/${props.workspaceId}/projects` },
|
||||
{ label: 'Canvases', icon: 'pi pi-objects-column', route: `/${props.workspaceId}/canvases` }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Library',
|
||||
items: [
|
||||
{ label: 'Workflows', icon: 'pi pi-sitemap', route: `/${props.workspaceId}/workflows` },
|
||||
{ label: 'Assets', icon: 'pi pi-images', route: `/${props.workspaceId}/assets` },
|
||||
{ label: 'Models', icon: 'pi pi-box', route: `/${props.workspaceId}/models` }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Team',
|
||||
items: [
|
||||
{ label: 'Members', icon: 'pi pi-users', route: `/${props.workspaceId}/members`, badge: 8 },
|
||||
{ label: 'Activity', icon: 'pi pi-history', route: `/${props.workspaceId}/activity` }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
items: [
|
||||
{ label: 'General', icon: 'pi pi-cog', route: `/${props.workspaceId}/settings` },
|
||||
{ label: 'Billing', icon: 'pi pi-credit-card', route: `/${props.workspaceId}/billing` }
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
const menuGroups = computed(() => (isTeam.value ? teamMenuGroups.value : userMenuGroups.value))
|
||||
|
||||
function isActive(itemRoute?: string): boolean {
|
||||
if (!itemRoute) return false
|
||||
if (itemRoute === `/${props.workspaceId}`) {
|
||||
return route.path === itemRoute
|
||||
}
|
||||
return route.path.startsWith(itemRoute)
|
||||
}
|
||||
|
||||
function signOut(): void {
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside
|
||||
class="flex h-full w-60 flex-col border-r border-zinc-200 bg-zinc-50/50 dark:border-zinc-800 dark:bg-zinc-950"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex h-14 items-center gap-3 border-b border-zinc-200 px-4 dark:border-zinc-800">
|
||||
<div
|
||||
:class="[
|
||||
'flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md text-sm font-semibold',
|
||||
isTeam ? 'bg-blue-600 text-white' : 'bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900'
|
||||
]"
|
||||
>
|
||||
{{ workspaceId.charAt(0).toUpperCase() }}
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<p class="truncate text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{{ workspaceId }}
|
||||
</p>
|
||||
<p class="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{{ isTeam ? 'Team' : 'Personal' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Menu Groups -->
|
||||
<nav class="flex-1 overflow-y-auto px-3 py-4">
|
||||
<template v-for="(group, groupIndex) in menuGroups" :key="group.label">
|
||||
<div v-if="groupIndex > 0" class="my-4" />
|
||||
|
||||
<p class="mb-2 px-2 text-xs font-medium text-zinc-500 dark:text-zinc-400">
|
||||
{{ group.label }}
|
||||
</p>
|
||||
|
||||
<ul class="flex flex-col gap-0.5">
|
||||
<li v-for="item in group.items" :key="item.label">
|
||||
<RouterLink
|
||||
:to="item.route ?? '#'"
|
||||
:class="[
|
||||
'flex items-center gap-3 rounded-md px-2 py-1.5 text-sm transition-colors',
|
||||
isActive(item.route)
|
||||
? 'bg-zinc-900 font-medium text-white dark:bg-zinc-100 dark:text-zinc-900'
|
||||
: 'text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100'
|
||||
]"
|
||||
>
|
||||
<i :class="[item.icon, 'text-base']" />
|
||||
<span class="flex-1">{{ item.label }}</span>
|
||||
<span
|
||||
v-if="item.badge"
|
||||
:class="[
|
||||
'rounded-full px-1.5 py-0.5 text-xs font-medium',
|
||||
isActive(item.route)
|
||||
? 'bg-white/20 dark:bg-zinc-900/20'
|
||||
: 'bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300'
|
||||
]"
|
||||
>
|
||||
{{ item.badge }}
|
||||
</span>
|
||||
</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</nav>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="border-t border-zinc-200 p-3 dark:border-zinc-800">
|
||||
<button
|
||||
class="flex w-full items-center gap-3 rounded-md px-2 py-1.5 text-sm text-zinc-600 transition-colors hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100"
|
||||
@click="signOut"
|
||||
>
|
||||
<i class="pi pi-sign-out text-base" />
|
||||
<span>Sign out</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</template>
|
||||
Reference in New Issue
Block a user