glowing-fiesta/app/layouts/default/Sidebar.vue
2026-01-17 10:51:21 +01:00

167 lines
4.7 KiB
Vue

<script setup lang="ts">
import { computed } from "vue";
import { useRuntimeConfig } from "#app";
import { BookOpen, BookOpenText, ChevronRight, HandCoins, Settings2, SquareTerminal } from "lucide-vue-next";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
type SidebarProps,
} from "~/components/ui/sidebar";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "~/components/ui/collapsible";
import AppSidebarFooter from "~/layouts/default/SidebarFooter.vue";
import type { User } from "better-auth";
const data = {
user: {
name: "Liviu",
email: "x.liviu@gmail.com",
avatar: "https://git.burcusel.nl/avatars/bcc51c59d08174c798ba7db59631df6cb820e042679af9098cc03ef68330f486?size=200",
},
navMain: [
{
title: "Playground",
url: "#",
icon: SquareTerminal,
isActive: true,
items: [
{ title: "History", url: "#" },
{ title: "Starred", url: "#" },
{ title: "Settings", url: "#" },
],
},
{
title: "Documentation",
url: "#",
icon: BookOpen,
isActive: true,
items: [
{ title: "Introduction", url: "#" },
{ title: "Get Started", url: "#" },
{ title: "Tutorials", url: "#" },
{ title: "Changelog", url: "#" },
],
},
{
title: "Legal",
url: "#",
icon: BookOpenText,
isActive: true,
items: [
{ title: "Terms of Service", url: "/legal/terms-of-service" },
{ title: "Privacy Policy", url: "/legal/privacy-policy" },
],
},
{
title: "Settings",
url: "#",
icon: Settings2,
isActive: true,
items: [
{ title: "General", url: "#" },
{ title: "Billing", url: "#" },
{ title: "Limits", url: "#" },
],
},
],
};
interface NavItem {
title: string;
url: string;
icon?: any;
isActive?: boolean;
items?: { title: string; url: string }[];
}
interface SidebarLayoutProps extends /* @vue-ignore */ SidebarProps {
navItems?: NavItem[];
user?: User | null | undefined;
collapsible?: "offcanvas" | "icon" | "none" | undefined;
}
const props = withDefaults(defineProps<SidebarLayoutProps>(), {
collapsible: "icon",
});
const navMain = computed(() => props.navItems || data.navMain);
const config = useRuntimeConfig();
</script>
<template>
<Sidebar v-bind="props">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" as-child>
<NuxtLink to="/">
<div
class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"
>
<HandCoins class="size-4" />
</div>
<div class="flex flex-col gap-0.5 leading-none">
<span class="font-bold text-primary">Glowing Fiesta</span>
<span>v{{ config.public.appVersion }}</span>
</div>
</NuxtLink>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
<Collapsible
v-for="item in navMain"
:key="item.title"
as-child
:default-open="item.isActive"
class="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger as-child>
<SidebarMenuButton :tooltip="item.title">
<component :is="item.icon" v-if="item.icon" class="text-primary" />
<span class="font-bold text-primary">{{ item.title }}</span>
<ChevronRight
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 text-primary"
/>
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
<SidebarMenuSubItem v-for="subItem in item.items" :key="subItem.title">
<SidebarMenuSubButton as-child>
<NuxtLink :to="subItem.url">
<span>{{ subItem.title }}</span>
</NuxtLink>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<AppSidebarFooter :user="user" />
<SidebarRail></SidebarRail>
</Sidebar>
</template>