From 1196e50602f9159f9756b0a5418b21d40c35df7b Mon Sep 17 00:00:00 2001 From: ngc2207 Date: Thu, 21 Nov 2024 14:51:48 +0800 Subject: [PATCH] feat: add shadcn sidebar-08 --- package.json | 8 +- src/app/dashboard/layout.tsx | 33 ++ src/app/dashboard/page.tsx | 3 + src/app/globals.css | 16 + src/components/app-sidebar.tsx | 185 +++++++ src/components/nav-main.tsx | 78 +++ src/components/nav-projects.tsx | 89 ++++ src/components/nav-secondary.tsx | 40 ++ src/components/nav-user.tsx | 114 +++++ src/components/ui/avatar.tsx | 50 ++ src/components/ui/breadcrumb.tsx | 115 +++++ src/components/ui/collapsible.tsx | 11 + src/components/ui/dropdown-menu.tsx | 201 ++++++++ src/components/ui/input.tsx | 22 + src/components/ui/separator.tsx | 31 ++ src/components/ui/sheet.tsx | 140 +++++ src/components/ui/sidebar.tsx | 763 ++++++++++++++++++++++++++++ src/components/ui/skeleton.tsx | 15 + src/components/ui/tooltip.tsx | 32 ++ src/hooks/use-mobile.tsx | 19 + tailwind.config.ts | 10 + 21 files changed, 1974 insertions(+), 1 deletion(-) create mode 100644 src/app/dashboard/layout.tsx create mode 100644 src/app/dashboard/page.tsx create mode 100644 src/components/app-sidebar.tsx create mode 100644 src/components/nav-main.tsx create mode 100644 src/components/nav-projects.tsx create mode 100644 src/components/nav-secondary.tsx create mode 100644 src/components/nav-user.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/hooks/use-mobile.tsx diff --git a/package.json b/package.json index 089587e..c8b3664 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "lint": "next lint" }, "dependencies": { + "@radix-ui/react-avatar": "^1.1.1", + "@radix-ui/react-collapsible": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.4", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.460.0", @@ -32,4 +38,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx new file mode 100644 index 0000000..ba214c2 --- /dev/null +++ b/src/app/dashboard/layout.tsx @@ -0,0 +1,33 @@ +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar"; +import { cookies } from "next/headers"; +import { AppSidebar } from "@/components/app-sidebar"; +import { Separator } from "@/components/ui/separator"; +import { ThemeToggle } from "@/components/theme-toggle"; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + const cookieStore = await cookies(); + const defaultOpen = cookieStore.get("sidebar:state")?.value === "true"; + return ( + + + +
+
+ + + +
+
+ {children} +
+
+ ); +} diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx new file mode 100644 index 0000000..8bf79c1 --- /dev/null +++ b/src/app/dashboard/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardPage() { + return null; +} diff --git a/src/app/globals.css b/src/app/globals.css index d906525..d07d384 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -28,6 +28,14 @@ --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; } .dark { --background: 240 10% 3.9%; @@ -54,6 +62,14 @@ --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; } } @layer base { diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx new file mode 100644 index 0000000..d24396e --- /dev/null +++ b/src/components/app-sidebar.tsx @@ -0,0 +1,185 @@ +"use client" + +import * as React from "react" +import { + BookOpen, + Bot, + Command, + Frame, + LifeBuoy, + Map, + PieChart, + Send, + Settings2, + SquareTerminal, +} from "lucide-react" + +import { NavMain } from "@/components/nav-main" +import { NavProjects } from "@/components/nav-projects" +import { NavSecondary } from "@/components/nav-secondary" +import { NavUser } from "@/components/nav-user" +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + navMain: [ + { + title: "Playground", + url: "#", + icon: SquareTerminal, + isActive: true, + items: [ + { + title: "History", + url: "#", + }, + { + title: "Starred", + url: "#", + }, + { + title: "Settings", + url: "#", + }, + ], + }, + { + title: "Models", + url: "#", + icon: Bot, + items: [ + { + title: "Genesis", + url: "#", + }, + { + title: "Explorer", + url: "#", + }, + { + title: "Quantum", + url: "#", + }, + ], + }, + { + title: "Documentation", + url: "#", + icon: BookOpen, + items: [ + { + title: "Introduction", + url: "#", + }, + { + title: "Get Started", + url: "#", + }, + { + title: "Tutorials", + url: "#", + }, + { + title: "Changelog", + url: "#", + }, + ], + }, + { + title: "Settings", + url: "#", + icon: Settings2, + items: [ + { + title: "General", + url: "#", + }, + { + title: "Team", + url: "#", + }, + { + title: "Billing", + url: "#", + }, + { + title: "Limits", + url: "#", + }, + ], + }, + ], + navSecondary: [ + { + title: "Support", + url: "#", + icon: LifeBuoy, + }, + { + title: "Feedback", + url: "#", + icon: Send, + }, + ], + projects: [ + { + name: "Design Engineering", + url: "#", + icon: Frame, + }, + { + name: "Sales & Marketing", + url: "#", + icon: PieChart, + }, + { + name: "Travel", + url: "#", + icon: Map, + }, + ], +} + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + + + + + +
+ +
+
+ Acme Inc + Enterprise +
+
+
+
+
+
+ + + + + + + + +
+ ) +} diff --git a/src/components/nav-main.tsx b/src/components/nav-main.tsx new file mode 100644 index 0000000..3481f96 --- /dev/null +++ b/src/components/nav-main.tsx @@ -0,0 +1,78 @@ +"use client" + +import { ChevronRight, type LucideIcon } from "lucide-react" + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible" +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + SidebarMenuSub, + SidebarMenuSubButton, + SidebarMenuSubItem, +} from "@/components/ui/sidebar" + +export function NavMain({ + items, +}: { + items: { + title: string + url: string + icon: LucideIcon + isActive?: boolean + items?: { + title: string + url: string + }[] + }[] +}) { + return ( + + Platform + + {items.map((item) => ( + + + + + + {item.title} + + + {item.items?.length ? ( + <> + + + + Toggle + + + + + {item.items?.map((subItem) => ( + + + + {subItem.title} + + + + ))} + + + + ) : null} + + + ))} + + + ) +} diff --git a/src/components/nav-projects.tsx b/src/components/nav-projects.tsx new file mode 100644 index 0000000..81d5805 --- /dev/null +++ b/src/components/nav-projects.tsx @@ -0,0 +1,89 @@ +"use client" + +import { + Folder, + MoreHorizontal, + Share, + Trash2, + type LucideIcon, +} from "lucide-react" + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarGroup, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuAction, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/components/ui/sidebar" + +export function NavProjects({ + projects, +}: { + projects: { + name: string + url: string + icon: LucideIcon + }[] +}) { + const { isMobile } = useSidebar() + + return ( + + Projects + + {projects.map((item) => ( + + + + + {item.name} + + + + + + + More + + + + + + View Project + + + + Share Project + + + + + Delete Project + + + + + ))} + + + + More + + + + + ) +} diff --git a/src/components/nav-secondary.tsx b/src/components/nav-secondary.tsx new file mode 100644 index 0000000..a931a7e --- /dev/null +++ b/src/components/nav-secondary.tsx @@ -0,0 +1,40 @@ +import * as React from "react" +import { type LucideIcon } from "lucide-react" + +import { + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar" + +export function NavSecondary({ + items, + ...props +}: { + items: { + title: string + url: string + icon: LucideIcon + }[] +} & React.ComponentPropsWithoutRef) { + return ( + + + + {items.map((item) => ( + + + + + {item.title} + + + + ))} + + + + ) +} diff --git a/src/components/nav-user.tsx b/src/components/nav-user.tsx new file mode 100644 index 0000000..d12ef78 --- /dev/null +++ b/src/components/nav-user.tsx @@ -0,0 +1,114 @@ +"use client" + +import { + BadgeCheck, + Bell, + ChevronsUpDown, + CreditCard, + LogOut, + Sparkles, +} from "lucide-react" + +import { + Avatar, + AvatarFallback, + AvatarImage, +} from "@/components/ui/avatar" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@/components/ui/sidebar" + +export function NavUser({ + user, +}: { + user: { + name: string + email: string + avatar: string + } +}) { + const { isMobile } = useSidebar() + + return ( + + + + + + + + CN + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + CN + +
+ {user.name} + {user.email} +
+
+
+ + + + + Upgrade to Pro + + + + + + + Account + + + + Billing + + + + Notifications + + + + + + Log out + +
+
+
+
+ ) +} diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>