Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/sidebar #30

Merged
merged 12 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 91 additions & 51 deletions app/App.vue
Original file line number Diff line number Diff line change
@@ -1,63 +1,103 @@
<script lang="ts" setup>
import router from './router'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { RouterLink, RouterView } from 'vue-router'
import { ChevronDown, PanelLeft } from 'lucide-vue-next'
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from 'radix-vue'
import router from './router'
import { LumuixModeToggle } from '@/components/lumuix'
import { SidebarProvider, Sidebar, SidebarContent, SidebarTrigger } from '@/components/sidebar'
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarFooter,
SidebarMenuSub,
SidebarMenuSubItem,
SidebarGroupAction
} from '@/components/sidebar'
import { SidebarRail } from '@'

const route = useRoute()
const currentRoute = computed(() => route.path)

const routes = router.getRoutes().filter((route) => route.children.length > 0)
</script>

<template>
<div>
<nav
id="nav-bar"
class="sticky top-0 z-10 flex h-fit w-full justify-between border-b border-slate-400 bg-background backdrop-blur dark:border-white">
<RouterLink to="/">
<img
src="/public/images/logo.png"
class="w-32"
alt="Lumuix Logo" />
</RouterLink>
<div class="text-primary my-auto mr-6 flex gap-2 font-bold">
<a href="https://github.com/SethSharp/lumuix"> 1.0.0-alpha.10.3 </a>
<LumuixModeToggle />
</div>
</nav>
<main>
<div class="flex">
<div class="h-screen w-1/4 bg-background">
<nav class="space-y-4 border-r border-slate-400 p-4 dark:border-white">
<div
v-for="group in routes"
:key="group.name">
<h3 class="text-lg font-medium text-heading">{{ group.name }}</h3>
<div class="flex">
<SidebarProvider>
<Sidebar side="left" variant="sidebar">
<SidebarHeader>
<RouterLink to="/">
<img
src="/public/images/logo.png"
class="w-full"
alt="Lumuix Logo" />
</RouterLink>
</SidebarHeader>
<SidebarContent>
<SidebarGroup
v-for="group in routes"
:key="group.name">
<SidebarGroupContent>
<SidebarMenu>
<CollapsibleRoot default-open class="group/collapsible">
<SidebarMenuItem>
<SidebarGroupLabel as-child class="text-heading">
<CollapsibleTrigger>
<SidebarMenuButton>
{{ group.name }}
<ChevronDown />
</SidebarMenuButton>
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarMenuSub role="list">
<SidebarMenuSubItem
v-for="route in group.children"
:key="route.name"
class="hover:underline">
<SidebarMenuButton :is-active="currentRoute === route.path" as-child>
<RouterLink
:to="route.path"
class="text-text">
{{ route.name }}
</RouterLink>
</SidebarMenuButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</CollapsibleRoot>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>

<SidebarRail>
<div class="pl-10 py-3">
<PanelLeft />
</div>
</SidebarRail>

<SidebarFooter class="h-12 text-center">
<div class="text-primary m-auto flex gap-2 font-bold">
<a href="https://github.com/SethSharp/lumuix" class="my-auto"> 1.0.0-alpha.10.3 </a>
<LumuixModeToggle />
</div>
</SidebarFooter>
</Sidebar>
</SidebarProvider>

<ul
role="list"
class="flex flex-1 flex-col py-2">
<li>
<ul class="space-y-2">
<li
v-for="route in group.children"
:key="route.name"
class="flex items-center hover:underline">
<RouterLink
:to="route.path"
class="px-2 text-text">
{{ route.name }}
</RouterLink>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</div>
<div class="w-3/4">
<component :is="$route.meta.layout">
<RouterView />
</component>
</div>
</div>
<main class="w-full px-8 p-4">
<component :is="$route.meta.layout">
<RouterView />
</component>
</main>
</div>
</template>
2 changes: 1 addition & 1 deletion app/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defineProps<{
</script>

<template>
<div class="space-y-2 p-6 leading-loose bg-background h-screen">
<div class="space-y-2 leading-loose bg-background pt-6 pr-12 h-screen w-full">
<slot name="breadcrumbs" />

<h1 class="text-3xl font-bold text-heading">{{ $route.name }}</h1>
Expand Down
12 changes: 11 additions & 1 deletion app/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Select,
Separator,
Sheet,
Sidebar,
Skeleton,
Slider,
Table,
Expand Down Expand Up @@ -228,6 +229,15 @@ const routes = [
shadcn: true,
},
},
{
path: '/components/sidebar',
name: 'Sidebar',
component: Sidebar,
meta: {
layout: ComponentLayout,
shadcn: true,
},
},
{
path: '/components/skeleton',
name: 'Skeleton',
Expand Down Expand Up @@ -298,7 +308,7 @@ const routes = [

const router = createRouter({
history: createWebHistory(),
linkActiveClass: '!font-medium underline',
linkActiveClass: '',
routes,
})

Expand Down
74 changes: 74 additions & 0 deletions app/views/components/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { BlocksIcon, MoreHorizontal, PersonStanding, CheckCircle } from 'lucide-vue-next'
// Sidebar is currently a WIP with 1/2 parts merged
// https://github.com/unovue/shadcn-vue/pull/827
// https://github.com/unovue/shadcn-vue/issues/805
import {
SidebarProvider,
Sidebar,
SidebarContent,
SidebarTrigger,
SidebarMenuAction,
SidebarMenuItem,
SidebarMenuButton,
SidebarGroup, SidebarMenu, SidebarGroupContent, SidebarGroupLabel
} from '@/components/sidebar'
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@/components/dropdown-menu'

const projects = [
{
name: 'Lumuix',
icon: BlocksIcon
},
{
name: 'Portfolio',
icon: PersonStanding
},
{
name: 'Habit Tracker',
icon: CheckCircle
}
]
</script>

<template>
<SidebarProvider :default-open="false">
<SidebarTrigger />
<Sidebar collapsible="icon" side="right" variant="sidebar">
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>
Projects
</SidebarGroupLabel>
<SidebarMenu>
<SidebarGroupContent>
<SidebarMenuItem v-for="project in projects">
<SidebarMenuButton asChild>
<a href="#">
<component :is="project.icon" />
<span>{{ project.name }} </span>
</a>
</SidebarMenuButton>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuAction>
<MoreHorizontal />
</SidebarMenuAction>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuItem>
<span>Edit Project</span>
</DropdownMenuItem>
<DropdownMenuItem>
<span>Delete Project</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarGroupContent>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
</template>
1 change: 1 addition & 0 deletions app/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { default as RangeCalendar } from './components/RangeCalendar.vue'
export { default as Select } from './components/Select.vue'
export { default as Separator } from './components/Separator.vue'
export { default as Sheet } from './components/Sheet.vue'
export { default as Sidebar} from './components/Sidebar.vue'
export { default as Skeleton } from './components/Skeleton.vue'
export { default as Slider } from './components/Slider.vue'
export { default as Table } from './components/Table.vue'
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

98 changes: 98 additions & 0 deletions src/components/sidebar/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import Sheet from '@/components/sheet/Sheet.vue'
import SheetContent from '@/components/sheet/SheetContent.vue'
import { cn } from '@/lib/utils'
import { SIDEBAR_WIDTH_MOBILE, useSidebar } from './utils'

defineOptions({
inheritAttrs: false,
})

const props = withDefaults(
defineProps<{
side?: 'left' | 'right'
variant?: 'sidebar' | 'floating' | 'inset'
collapsible?: 'offcanvas' | 'icon' | 'none'
class?: HTMLAttributes['class']
}>(),
{
side: 'left',
variant: 'sidebar',
collapsible: 'offcanvas',
},
)

const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
</script>

<template>
<div
v-if="collapsible === 'none'"
:class="
cn('flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground', props.class)
"
v-bind="$attrs">
<slot />
</div>

<Sheet
v-else-if="isMobile"
:open="openMobile"
v-bind="$attrs"
@update:open="setOpenMobile">
<SheetContent
data-sidebar="sidebar"
data-mobile="true"
class="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
:style="{
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
}">
<div class="flex h-full w-full flex-col">
<slot />
</div>
</SheetContent>
</Sheet>

<div
v-else
class="group peer hidden md:block"
:data-state="state"
:data-collapsible="state === 'collapsed' ? collapsible : ''"
:data-variant="variant"
:data-side="side">
<!-- This is what handles the sidebar gap on desktop -->
<div
:class="
cn(
'relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear',
'group-data-[collapsible=offcanvas]:w-0',
'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset'
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]',
)
" />
<div
:class="
cn(
'fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex',
side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
// Adjust the padding for floating and inset variants.
variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
props.class,
)
"
v-bind="$attrs">
<div
data-sidebar="sidebar"
class="group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow">
<slot />
</div>
</div>
</div>
</template>
Loading