This commit is contained in:
shadcn
2024-10-16 12:54:21 +04:00
parent d0a308cc64
commit 8fc80836ff
38 changed files with 1066 additions and 192 deletions

View File

@@ -0,0 +1,104 @@
import * as React from "react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarProvider,
} from "@/registry/default/ui/sidebar"
const projects = [
{
name: "Design Engineering",
url: "#",
icon: Frame,
badge: "24",
},
{
name: "Sales & Marketing",
url: "#",
icon: PieChart,
badge: "12",
},
{
name: "Travel",
url: "#",
icon: Map,
badge: "3",
},
{
name: "Support",
url: "#",
icon: LifeBuoy,
badge: "21",
},
{
name: "Feedback",
url: "#",
icon: Send,
badge: "8",
},
]
// Dummy fetch function
async function fetchProjects() {
await new Promise((resolve) => setTimeout(resolve, 3000))
return projects
}
export default function AppSidebar() {
return (
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
)
}
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}

View File

@@ -23,7 +23,7 @@ export default function Page() {
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<header className="flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>

View File

@@ -429,7 +429,7 @@ export const Index: Record<string, any> = {
name: "sidebar",
description: "",
type: "registry:ui",
registryDependencies: ["button","separator","sheet","tooltip","input","use-mobile"],
registryDependencies: ["button","separator","sheet","tooltip","input","use-mobile","skeleton"],
files: ["registry/new-york/ui/sidebar.tsx"],
component: React.lazy(() => import("@/registry/new-york/ui/sidebar.tsx")),
source: "",
@@ -2477,6 +2477,18 @@ export const Index: Record<string, any> = {
subcategory: "",
chunks: []
},
"demo-sidebar-rsc": {
name: "demo-sidebar-rsc",
description: "",
type: "registry:block",
registryDependencies: undefined,
files: ["registry/new-york/block/demo-sidebar-rsc.tsx"],
component: React.lazy(() => import("@/registry/new-york/block/demo-sidebar-rsc.tsx")),
source: "__registry__/new-york/block/demo-sidebar-rsc.tsx",
category: "",
subcategory: "",
chunks: []
},
"sidebar-01": {
name: "sidebar-01",
description: "A simple sidebar with navigation grouped by section.",
@@ -3970,7 +3982,7 @@ export const Index: Record<string, any> = {
name: "sidebar",
description: "",
type: "registry:ui",
registryDependencies: ["button","separator","sheet","tooltip","input","use-mobile"],
registryDependencies: ["button","separator","sheet","tooltip","input","use-mobile","skeleton"],
files: ["registry/default/ui/sidebar.tsx"],
component: React.lazy(() => import("@/registry/default/ui/sidebar.tsx")),
source: "",
@@ -6018,6 +6030,18 @@ export const Index: Record<string, any> = {
subcategory: "",
chunks: []
},
"demo-sidebar-rsc": {
name: "demo-sidebar-rsc",
description: "",
type: "registry:block",
registryDependencies: undefined,
files: ["registry/default/block/demo-sidebar-rsc.tsx"],
component: React.lazy(() => import("@/registry/default/block/demo-sidebar-rsc.tsx")),
source: "__registry__/default/block/demo-sidebar-rsc.tsx",
category: "",
subcategory: "",
chunks: []
},
"sidebar-01": {
name: "sidebar-01",
description: "A simple sidebar with navigation grouped by section.",

View File

@@ -1,20 +1,7 @@
"use client"
import {
Frame,
LifeBuoy,
Map,
MoreHorizontal,
PieChart,
Send,
} from "lucide-react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
import {
Sidebar,
SidebarContent,
@@ -22,7 +9,6 @@ import {
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,

View File

@@ -0,0 +1,104 @@
import * as React from "react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarProvider,
} from "@/registry/new-york/ui/sidebar"
const projects = [
{
name: "Design Engineering",
url: "#",
icon: Frame,
badge: "24",
},
{
name: "Sales & Marketing",
url: "#",
icon: PieChart,
badge: "12",
},
{
name: "Travel",
url: "#",
icon: Map,
badge: "3",
},
{
name: "Support",
url: "#",
icon: LifeBuoy,
badge: "21",
},
{
name: "Feedback",
url: "#",
icon: Send,
badge: "8",
},
]
// Dummy fetch function
async function fetchProjects() {
await new Promise((resolve) => setTimeout(resolve, 3000))
return projects
}
export default function AppSidebar() {
return (
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
)
}
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}

View File

@@ -23,7 +23,7 @@ export default function Page() {
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<header className="flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>

View File

@@ -13,7 +13,7 @@ component: true
className="w-full"
/>
<figcaption className="text-center text-sm text-gray-500">
A simple collapsible sidebar.
A sidebar that collapses to icons.
</figcaption>
</figure>
@@ -50,15 +50,15 @@ npx shadcn@latest add sidebar
The command above should install the colors for you. It not, copy and paste the following in your CSS file.
We'll go over the colors later in the theming section.
We'll go over the colors later in the [theming section](/docs/components/sidebar#theming).
```css
@layer base {
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--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%;
@@ -68,8 +68,8 @@ We'll go over the colors later in the theming section.
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
@@ -101,8 +101,8 @@ We'll go over the colors later in the theming section.
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--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%;
@@ -112,8 +112,8 @@ We'll go over the colors later in the theming section.
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
@@ -156,7 +156,7 @@ A `Sidebar` component is composed of the following parts:
## Usage
```tsx showLineNumbers {12-20,22}
```tsx showLineNumbers title="app/layout.tsx"
import {
Sidebar,
SidebarContent,
@@ -164,6 +164,7 @@ import {
SidebarGroup,
SidebarHeader,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
export default function Layout({ children }: { children: React.ReactNode }) {
@@ -177,7 +178,10 @@ export default function Layout({ children }: { children: React.ReactNode }) {
</SidebarContent>
<SidebarFooter />
</Sidebar>
<main>{children}</main>
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
)
}
@@ -238,8 +242,6 @@ export default function Layout({ children }: { children: React.ReactNode }) {
}
```
If you click the trigger, the sidebar should slide in and out from the left.
<Step>Now, let's add a `SidebarMenu` to the sidebar.</Step>
We'll use the `SidebarMenu` component in a `SidebarGroup`.
@@ -317,6 +319,8 @@ export function AppSidebar() {
}
```
<Step>You've created your first sidebar.</Step>
</Steps>
<figure className="flex flex-col gap-4">
@@ -1021,6 +1025,24 @@ The `SidebarMenuBadge` component is used to render a badge within a `SidebarMenu
</figcaption>
</figure>
## SidebarMenuSkeleton
The `SidebarMenuSkeleton` component is used to render a skeleton for a `SidebarMenu`. You can use this to show a loading state when using React Server Components or swr or react-query.
```tsx showLineNumbers
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
```
## SidebarSeparator
The `SidebarSeparator` component is used to render a separator within a `Sidebar`.
@@ -1081,6 +1103,157 @@ The `SidebarRail` component is used to render a rail within a `Sidebar`. This ra
</Sidebar>
```
## Data Fetching
### React Server Components
Here's an example of a `SidebarMenu` component rendering a list of projects using React Server Components.
```tsx showLineNumbers {6} title="Skeleton to show loading state."
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
```
```tsx showLineNumbers {2} title="Server component fetching data."
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
```
```tsx showLineNumbers {8-10} title="Usage with React Suspense."
function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
)
}
```
<figure className="flex flex-col gap-4">
<ComponentPreview
name="demo-sidebar-rsc"
title="Sidebar Menu RSC"
type="block"
description="A sidebar menu using React Server Components."
className="w-full"
/>
<figcaption className="text-center text-sm text-gray-500">
Right-click and reload frame to see the skeleton.
</figcaption>
</figure>
### SWR and React Query
You can use the same approach with [SWR](https://swr.vercel.app/) or [react-query](https://tanstack.com/query/latest/docs/framework/react/overview).
```tsx showLineNumbers title="SWR"
function NavProjects() {
const { data, isLoading } = useSWR("/api/projects", fetcher)
if (isLoading) {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
if (!data) {
return ...
}
return (
<SidebarMenu>
{data.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
```
```tsx showLineNumbers title="React Query"
function NavProjects() {
const { data, isLoading } = useQuery()
if (isLoading) {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
if (!data) {
return ...
}
return (
<SidebarMenu>
{data.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
```
## Theming
We use the following CSS variables to theme the sidebar.
@@ -1090,8 +1263,8 @@ We use the following CSS variables to theme the sidebar.
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--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%;
@@ -1101,8 +1274,8 @@ We use the following CSS variables to theme the sidebar.
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;

View File

@@ -1,5 +1,4 @@
import path from "path"
import { getHighlighter, loadTheme } from "@shikijs/compat"
import { getHighlighter } from "@shikijs/compat"
import {
defineDocumentType,
defineNestedType,
@@ -114,12 +113,8 @@ export default makeSource({
[
rehypePrettyCode,
{
getHighlighter: async () => {
const theme = await loadTheme(
path.join(process.cwd(), "/lib/highlighter-theme.json")
)
return await getHighlighter({ theme })
},
theme: "github-dark",
getHighlighter,
onVisitLine(node) {
// Prevent lines from collapsing in `display: grid` mode, and allow empty
// lines to be copy/pasted

View File

@@ -512,7 +512,8 @@
"sheet",
"tooltip",
"input",
"use-mobile"
"use-mobile",
"skeleton"
],
"files": [
{
@@ -524,8 +525,8 @@
"light": {
"sidebar-background": "0 0% 98%",
"sidebar-foreground": "240 5.3% 26.1%",
"sidebar-primary": "220 100% 50%",
"sidebar-primary-foreground": "0 0% 100%",
"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%",
@@ -534,8 +535,8 @@
"dark": {
"sidebar-background": "240 5.9% 10%",
"sidebar-foreground": "240 4.8% 95.9%",
"sidebar-primary": "220 100% 50%",
"sidebar-primary-foreground": "0 0% 100%",
"sidebar-primary": "0 0% 98%",
"sidebar-primary-foreground": "240 5.9% 10%",
"sidebar-accent": "240 3.7% 15.9%",
"sidebar-accent-foreground": "240 4.8% 95.9%",
"sidebar-border": "240 3.7% 15.9%",

View File

@@ -0,0 +1,12 @@
{
"name": "demo-sidebar-rsc",
"type": "registry:block",
"description": "",
"files": [
{
"path": "block/demo-sidebar-rsc.tsx",
"content": "import * as React from \"react\"\nimport { Frame, LifeBuoy, Map, PieChart, Send } from \"lucide-react\"\n\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarProvider,\n} from \"@/registry/default/ui/sidebar\"\n\nconst projects = [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n badge: \"24\",\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n badge: \"12\",\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n badge: \"3\",\n },\n {\n name: \"Support\",\n url: \"#\",\n icon: LifeBuoy,\n badge: \"21\",\n },\n {\n name: \"Feedback\",\n url: \"#\",\n icon: Send,\n badge: \"8\",\n },\n]\n\n// Dummy fetch function\nasync function fetchProjects() {\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return projects\n}\n\nexport default function AppSidebar() {\n return (\n <SidebarProvider>\n <Sidebar>\n <SidebarContent>\n <SidebarGroup>\n <SidebarGroupLabel>Projects</SidebarGroupLabel>\n <SidebarGroupContent>\n <React.Suspense fallback={<NavProjectsSkeleton />}>\n <NavProjects />\n </React.Suspense>\n </SidebarGroupContent>\n </SidebarGroup>\n </SidebarContent>\n </Sidebar>\n </SidebarProvider>\n )\n}\n\nfunction NavProjectsSkeleton() {\n return (\n <SidebarMenu>\n {Array.from({ length: 5 }).map((_, index) => (\n <SidebarMenuItem key={index}>\n <SidebarMenuSkeleton showIcon />\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n )\n}\n\nasync function NavProjects() {\n const projects = await fetchProjects()\n\n return (\n <SidebarMenu>\n {projects.map((project) => (\n <SidebarMenuItem key={project.name}>\n <SidebarMenuButton asChild>\n <a href={project.url}>\n <project.icon />\n <span>{project.name}</span>\n </a>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n )\n}\n",
"type": "registry:component"
}
]
}

View File

@@ -12,7 +12,7 @@
"files": [
{
"path": "block/sidebar-02/page.tsx",
"content": "import { AppSidebar } from \"@/registry/default/block/sidebar-02/components/app-sidebar\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/registry/default/ui/breadcrumb\"\nimport { Separator } from \"@/registry/default/ui/separator\"\nimport {\n SidebarInset,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/registry/default/ui/sidebar\"\n\nexport default function Page() {\n return (\n <SidebarProvider>\n <AppSidebar />\n <SidebarInset>\n <header className=\"flex h-16 shrink-0 items-center gap-2 border-b px-4\">\n <SidebarTrigger className=\"-ml-1\" />\n <Separator orientation=\"vertical\" className=\"mr-2 h-4\" />\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem className=\"hidden md:block\">\n <BreadcrumbLink href=\"#\">\n Building Your Application\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator className=\"hidden md:block\" />\n <BreadcrumbItem>\n <BreadcrumbPage>Data Fetching</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </header>\n <div className=\"flex flex-1 flex-col gap-4 p-4\">\n {Array.from({ length: 24 }).map((_, index) => (\n <div\n key={index}\n className=\"aspect-video h-12 w-full rounded-lg bg-muted/50\"\n />\n ))}\n </div>\n </SidebarInset>\n </SidebarProvider>\n )\n}\n",
"content": "import { AppSidebar } from \"@/registry/default/block/sidebar-02/components/app-sidebar\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/registry/default/ui/breadcrumb\"\nimport { Separator } from \"@/registry/default/ui/separator\"\nimport {\n SidebarInset,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/registry/default/ui/sidebar\"\n\nexport default function Page() {\n return (\n <SidebarProvider>\n <AppSidebar />\n <SidebarInset>\n <header className=\"flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4\">\n <SidebarTrigger className=\"-ml-1\" />\n <Separator orientation=\"vertical\" className=\"mr-2 h-4\" />\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem className=\"hidden md:block\">\n <BreadcrumbLink href=\"#\">\n Building Your Application\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator className=\"hidden md:block\" />\n <BreadcrumbItem>\n <BreadcrumbPage>Data Fetching</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </header>\n <div className=\"flex flex-1 flex-col gap-4 p-4\">\n {Array.from({ length: 24 }).map((_, index) => (\n <div\n key={index}\n className=\"aspect-video h-12 w-full rounded-lg bg-muted/50\"\n />\n ))}\n </div>\n </SidebarInset>\n </SidebarProvider>\n )\n}\n",
"type": "registry:page",
"target": "app/dashboard/page.tsx"
},

View File

@@ -19,12 +19,12 @@
},
{
"path": "block/sidebar-07/components/app-sidebar.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n BookOpen,\n Bot,\n Command,\n Frame,\n Map,\n PieChart,\n Settings2,\n SquareTerminal,\n} from \"lucide-react\"\n\nimport { NavMain } from \"@/registry/default/block/sidebar-07/components/nav-main\"\nimport { NavProjects } from \"@/registry/default/block/sidebar-07/components/nav-projects\"\nimport { NavUser } from \"@/registry/default/block/sidebar-07/components/nav-user\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarRail,\n} from \"@/registry/default/ui/sidebar\"\n\n// This is sample data.\nconst data = {\n user: {\n name: \"shadcn\",\n email: \"m@example.com\",\n avatar: \"/avatars/shadcn.jpg\",\n },\n navMain: [\n {\n title: \"Playground\",\n url: \"#\",\n icon: SquareTerminal,\n isActive: true,\n items: [\n {\n title: \"History\",\n url: \"#\",\n },\n {\n title: \"Starred\",\n url: \"#\",\n },\n {\n title: \"Settings\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Models\",\n url: \"#\",\n icon: Bot,\n items: [\n {\n title: \"Genesis\",\n url: \"#\",\n },\n {\n title: \"Explorer\",\n url: \"#\",\n },\n {\n title: \"Quantum\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Documentation\",\n url: \"#\",\n icon: BookOpen,\n items: [\n {\n title: \"Introduction\",\n url: \"#\",\n },\n {\n title: \"Get Started\",\n url: \"#\",\n },\n {\n title: \"Tutorials\",\n url: \"#\",\n },\n {\n title: \"Changelog\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Settings\",\n url: \"#\",\n icon: Settings2,\n items: [\n {\n title: \"General\",\n url: \"#\",\n },\n {\n title: \"Team\",\n url: \"#\",\n },\n {\n title: \"Billing\",\n url: \"#\",\n },\n {\n title: \"Limits\",\n url: \"#\",\n },\n ],\n },\n ],\n projects: [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n },\n ],\n}\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar collapsible=\"icon\" {...props}>\n <SidebarHeader>\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton size=\"lg\" asChild>\n <a href=\"#\">\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground\">\n <Command className=\"size-4\" />\n </div>\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-semibold\">Acme Inc</span>\n <span className=\"truncate text-xs\">Enterprise</span>\n </div>\n </a>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n </SidebarHeader>\n <SidebarContent>\n <NavMain items={data.navMain} />\n <NavProjects projects={data.projects} />\n </SidebarContent>\n <SidebarFooter>\n <NavUser user={data.user} />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n AudioWaveform,\n BookOpen,\n Bot,\n Command,\n Frame,\n GalleryVerticalEnd,\n Map,\n PieChart,\n Settings2,\n SquareTerminal,\n} from \"lucide-react\"\n\nimport { NavMain } from \"@/registry/default/block/sidebar-07/components/nav-main\"\nimport { NavProjects } from \"@/registry/default/block/sidebar-07/components/nav-projects\"\nimport { NavUser } from \"@/registry/default/block/sidebar-07/components/nav-user\"\nimport { TeamSwitcher } from \"@/registry/default/block/sidebar-07/components/team-switcher\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarRail,\n} from \"@/registry/default/ui/sidebar\"\n\n// This is sample data.\nconst data = {\n user: {\n name: \"shadcn\",\n email: \"m@example.com\",\n avatar: \"/avatars/shadcn.jpg\",\n },\n teams: [\n {\n name: \"Acme Inc\",\n logo: GalleryVerticalEnd,\n plan: \"Enterprise\",\n },\n {\n name: \"Acme Corp.\",\n logo: AudioWaveform,\n plan: \"Startup\",\n },\n {\n name: \"Evil Corp.\",\n logo: Command,\n plan: \"Free\",\n },\n ],\n navMain: [\n {\n title: \"Playground\",\n url: \"#\",\n icon: SquareTerminal,\n isActive: true,\n items: [\n {\n title: \"History\",\n url: \"#\",\n },\n {\n title: \"Starred\",\n url: \"#\",\n },\n {\n title: \"Settings\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Models\",\n url: \"#\",\n icon: Bot,\n items: [\n {\n title: \"Genesis\",\n url: \"#\",\n },\n {\n title: \"Explorer\",\n url: \"#\",\n },\n {\n title: \"Quantum\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Documentation\",\n url: \"#\",\n icon: BookOpen,\n items: [\n {\n title: \"Introduction\",\n url: \"#\",\n },\n {\n title: \"Get Started\",\n url: \"#\",\n },\n {\n title: \"Tutorials\",\n url: \"#\",\n },\n {\n title: \"Changelog\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Settings\",\n url: \"#\",\n icon: Settings2,\n items: [\n {\n title: \"General\",\n url: \"#\",\n },\n {\n title: \"Team\",\n url: \"#\",\n },\n {\n title: \"Billing\",\n url: \"#\",\n },\n {\n title: \"Limits\",\n url: \"#\",\n },\n ],\n },\n ],\n projects: [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n },\n ],\n}\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar collapsible=\"icon\" {...props}>\n <SidebarHeader>\n <TeamSwitcher teams={data.teams} />\n </SidebarHeader>\n <SidebarContent>\n <NavMain items={data.navMain} />\n <NavProjects projects={data.projects} />\n </SidebarContent>\n <SidebarFooter>\n <NavUser user={data.user} />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n",
"type": "registry:component"
},
{
"path": "block/sidebar-07/components/nav-main.tsx",
"content": "\"use client\"\n\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/default/ui/collapsible\"\nimport {\n SidebarGroup,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n} from \"@/registry/default/ui/sidebar\"\n\nexport function NavMain({\n items,\n}: {\n items: {\n title: string\n url: string\n icon?: LucideIcon\n isActive?: boolean\n items?: {\n title: string\n url: string\n }[]\n }[]\n}) {\n return (\n <SidebarGroup>\n <SidebarGroupLabel>Platform</SidebarGroupLabel>\n <SidebarMenu>\n {items.map((item) => (\n <Collapsible key={item.title} asChild defaultOpen={item.isActive}>\n <SidebarMenuItem>\n <SidebarMenuButton asChild tooltip={item.title}>\n <a href={item.url}>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n </a>\n </SidebarMenuButton>\n {item.items?.length ? (\n <>\n <CollapsibleTrigger asChild>\n <SidebarMenuAction className=\"data-[state=open]:rotate-90\">\n <ChevronRight />\n <span className=\"sr-only\">Toggle</span>\n </SidebarMenuAction>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.items?.map((subItem) => (\n <SidebarMenuSubItem key={subItem.title}>\n <SidebarMenuSubButton asChild>\n <a href={subItem.url}>\n <span>{subItem.title}</span>\n </a>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </>\n ) : null}\n </SidebarMenuItem>\n </Collapsible>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n )\n}\n",
"content": "\"use client\"\n\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/default/ui/collapsible\"\nimport {\n SidebarGroup,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n} from \"@/registry/default/ui/sidebar\"\n\nexport function NavMain({\n items,\n}: {\n items: {\n title: string\n url: string\n icon?: LucideIcon\n isActive?: boolean\n items?: {\n title: string\n url: string\n }[]\n }[]\n}) {\n return (\n <SidebarGroup>\n <SidebarGroupLabel>Platform</SidebarGroupLabel>\n <SidebarMenu>\n {items.map((item) => (\n <Collapsible\n key={item.title}\n asChild\n defaultOpen={item.isActive}\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton tooltip={item.title}>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n <ChevronRight className=\"ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.items?.map((subItem) => (\n <SidebarMenuSubItem key={subItem.title}>\n <SidebarMenuSubButton asChild>\n <a href={subItem.url}>\n <span>{subItem.title}</span>\n </a>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n )\n}\n",
"type": "registry:component"
},
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
"files": [
{
"path": "block/demo-sidebar-menu-badge.tsx",
"content": "\"use client\"\n\nimport {\n Frame,\n LifeBuoy,\n Map,\n MoreHorizontal,\n PieChart,\n Send,\n} from \"lucide-react\"\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@/registry/new-york/ui/dropdown-menu\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarProvider,\n} from \"@/registry/new-york/ui/sidebar\"\n\nconst projects = [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n badge: \"24\",\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n badge: \"12\",\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n badge: \"3\",\n },\n {\n name: \"Support\",\n url: \"#\",\n icon: LifeBuoy,\n badge: \"21\",\n },\n {\n name: \"Feedback\",\n url: \"#\",\n icon: Send,\n badge: \"8\",\n },\n]\n\nexport default function AppSidebar() {\n return (\n <SidebarProvider>\n <Sidebar>\n <SidebarContent>\n <SidebarGroup>\n <SidebarGroupLabel>Projects</SidebarGroupLabel>\n <SidebarGroupContent>\n <SidebarMenu>\n {projects.map((project) => (\n <SidebarMenuItem key={project.name}>\n <SidebarMenuButton\n asChild\n className=\"group-has-[[data-state=open]]/menu-item:bg-sidebar-accent\"\n >\n <a href={project.url}>\n <project.icon />\n <span>{project.name}</span>\n </a>\n </SidebarMenuButton>\n <SidebarMenuBadge>{project.badge}</SidebarMenuBadge>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n </SidebarContent>\n </Sidebar>\n </SidebarProvider>\n )\n}\n",
"content": "\"use client\"\n\nimport { Frame, LifeBuoy, Map, PieChart, Send } from \"lucide-react\"\n\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarProvider,\n} from \"@/registry/new-york/ui/sidebar\"\n\nconst projects = [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n badge: \"24\",\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n badge: \"12\",\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n badge: \"3\",\n },\n {\n name: \"Support\",\n url: \"#\",\n icon: LifeBuoy,\n badge: \"21\",\n },\n {\n name: \"Feedback\",\n url: \"#\",\n icon: Send,\n badge: \"8\",\n },\n]\n\nexport default function AppSidebar() {\n return (\n <SidebarProvider>\n <Sidebar>\n <SidebarContent>\n <SidebarGroup>\n <SidebarGroupLabel>Projects</SidebarGroupLabel>\n <SidebarGroupContent>\n <SidebarMenu>\n {projects.map((project) => (\n <SidebarMenuItem key={project.name}>\n <SidebarMenuButton\n asChild\n className=\"group-has-[[data-state=open]]/menu-item:bg-sidebar-accent\"\n >\n <a href={project.url}>\n <project.icon />\n <span>{project.name}</span>\n </a>\n </SidebarMenuButton>\n <SidebarMenuBadge>{project.badge}</SidebarMenuBadge>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n </SidebarGroupContent>\n </SidebarGroup>\n </SidebarContent>\n </Sidebar>\n </SidebarProvider>\n )\n}\n",
"type": "registry:component"
}
]

View File

@@ -0,0 +1,12 @@
{
"name": "demo-sidebar-rsc",
"type": "registry:block",
"description": "",
"files": [
{
"path": "block/demo-sidebar-rsc.tsx",
"content": "import * as React from \"react\"\nimport { Frame, LifeBuoy, Map, PieChart, Send } from \"lucide-react\"\n\nimport {\n Sidebar,\n SidebarContent,\n SidebarGroup,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarProvider,\n} from \"@/registry/new-york/ui/sidebar\"\n\nconst projects = [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n badge: \"24\",\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n badge: \"12\",\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n badge: \"3\",\n },\n {\n name: \"Support\",\n url: \"#\",\n icon: LifeBuoy,\n badge: \"21\",\n },\n {\n name: \"Feedback\",\n url: \"#\",\n icon: Send,\n badge: \"8\",\n },\n]\n\n// Dummy fetch function\nasync function fetchProjects() {\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return projects\n}\n\nexport default function AppSidebar() {\n return (\n <SidebarProvider>\n <Sidebar>\n <SidebarContent>\n <SidebarGroup>\n <SidebarGroupLabel>Projects</SidebarGroupLabel>\n <SidebarGroupContent>\n <React.Suspense fallback={<NavProjectsSkeleton />}>\n <NavProjects />\n </React.Suspense>\n </SidebarGroupContent>\n </SidebarGroup>\n </SidebarContent>\n </Sidebar>\n </SidebarProvider>\n )\n}\n\nfunction NavProjectsSkeleton() {\n return (\n <SidebarMenu>\n {Array.from({ length: 5 }).map((_, index) => (\n <SidebarMenuItem key={index}>\n <SidebarMenuSkeleton showIcon />\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n )\n}\n\nasync function NavProjects() {\n const projects = await fetchProjects()\n\n return (\n <SidebarMenu>\n {projects.map((project) => (\n <SidebarMenuItem key={project.name}>\n <SidebarMenuButton asChild>\n <a href={project.url}>\n <project.icon />\n <span>{project.name}</span>\n </a>\n </SidebarMenuButton>\n </SidebarMenuItem>\n ))}\n </SidebarMenu>\n )\n}\n",
"type": "registry:component"
}
]
}

View File

@@ -12,7 +12,7 @@
"files": [
{
"path": "block/sidebar-02/page.tsx",
"content": "import { AppSidebar } from \"@/registry/new-york/block/sidebar-02/components/app-sidebar\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/registry/new-york/ui/breadcrumb\"\nimport { Separator } from \"@/registry/new-york/ui/separator\"\nimport {\n SidebarInset,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/registry/new-york/ui/sidebar\"\n\nexport default function Page() {\n return (\n <SidebarProvider>\n <AppSidebar />\n <SidebarInset>\n <header className=\"flex h-16 shrink-0 items-center gap-2 border-b px-4\">\n <SidebarTrigger className=\"-ml-1\" />\n <Separator orientation=\"vertical\" className=\"mr-2 h-4\" />\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem className=\"hidden md:block\">\n <BreadcrumbLink href=\"#\">\n Building Your Application\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator className=\"hidden md:block\" />\n <BreadcrumbItem>\n <BreadcrumbPage>Data Fetching</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </header>\n <div className=\"flex flex-1 flex-col gap-4 p-4\">\n {Array.from({ length: 24 }).map((_, index) => (\n <div\n key={index}\n className=\"aspect-video h-12 w-full rounded-lg bg-muted/50\"\n />\n ))}\n </div>\n </SidebarInset>\n </SidebarProvider>\n )\n}\n",
"content": "import { AppSidebar } from \"@/registry/new-york/block/sidebar-02/components/app-sidebar\"\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"@/registry/new-york/ui/breadcrumb\"\nimport { Separator } from \"@/registry/new-york/ui/separator\"\nimport {\n SidebarInset,\n SidebarProvider,\n SidebarTrigger,\n} from \"@/registry/new-york/ui/sidebar\"\n\nexport default function Page() {\n return (\n <SidebarProvider>\n <AppSidebar />\n <SidebarInset>\n <header className=\"flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4\">\n <SidebarTrigger className=\"-ml-1\" />\n <Separator orientation=\"vertical\" className=\"mr-2 h-4\" />\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem className=\"hidden md:block\">\n <BreadcrumbLink href=\"#\">\n Building Your Application\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator className=\"hidden md:block\" />\n <BreadcrumbItem>\n <BreadcrumbPage>Data Fetching</BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n </header>\n <div className=\"flex flex-1 flex-col gap-4 p-4\">\n {Array.from({ length: 24 }).map((_, index) => (\n <div\n key={index}\n className=\"aspect-video h-12 w-full rounded-lg bg-muted/50\"\n />\n ))}\n </div>\n </SidebarInset>\n </SidebarProvider>\n )\n}\n",
"type": "registry:page",
"target": "app/dashboard/page.tsx"
},

View File

@@ -19,12 +19,12 @@
},
{
"path": "block/sidebar-07/components/app-sidebar.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n BookOpen,\n Bot,\n Command,\n Frame,\n Map,\n PieChart,\n Settings2,\n SquareTerminal,\n} from \"lucide-react\"\n\nimport { NavMain } from \"@/registry/new-york/block/sidebar-07/components/nav-main\"\nimport { NavProjects } from \"@/registry/new-york/block/sidebar-07/components/nav-projects\"\nimport { NavUser } from \"@/registry/new-york/block/sidebar-07/components/nav-user\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarRail,\n} from \"@/registry/new-york/ui/sidebar\"\n\n// This is sample data.\nconst data = {\n user: {\n name: \"shadcn\",\n email: \"m@example.com\",\n avatar: \"/avatars/shadcn.jpg\",\n },\n navMain: [\n {\n title: \"Playground\",\n url: \"#\",\n icon: SquareTerminal,\n isActive: true,\n items: [\n {\n title: \"History\",\n url: \"#\",\n },\n {\n title: \"Starred\",\n url: \"#\",\n },\n {\n title: \"Settings\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Models\",\n url: \"#\",\n icon: Bot,\n items: [\n {\n title: \"Genesis\",\n url: \"#\",\n },\n {\n title: \"Explorer\",\n url: \"#\",\n },\n {\n title: \"Quantum\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Documentation\",\n url: \"#\",\n icon: BookOpen,\n items: [\n {\n title: \"Introduction\",\n url: \"#\",\n },\n {\n title: \"Get Started\",\n url: \"#\",\n },\n {\n title: \"Tutorials\",\n url: \"#\",\n },\n {\n title: \"Changelog\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Settings\",\n url: \"#\",\n icon: Settings2,\n items: [\n {\n title: \"General\",\n url: \"#\",\n },\n {\n title: \"Team\",\n url: \"#\",\n },\n {\n title: \"Billing\",\n url: \"#\",\n },\n {\n title: \"Limits\",\n url: \"#\",\n },\n ],\n },\n ],\n projects: [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n },\n ],\n}\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar collapsible=\"icon\" {...props}>\n <SidebarHeader>\n <SidebarMenu>\n <SidebarMenuItem>\n <SidebarMenuButton size=\"lg\" asChild>\n <a href=\"#\">\n <div className=\"flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground\">\n <Command className=\"size-4\" />\n </div>\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-semibold\">Acme Inc</span>\n <span className=\"truncate text-xs\">Enterprise</span>\n </div>\n </a>\n </SidebarMenuButton>\n </SidebarMenuItem>\n </SidebarMenu>\n </SidebarHeader>\n <SidebarContent>\n <NavMain items={data.navMain} />\n <NavProjects projects={data.projects} />\n </SidebarContent>\n <SidebarFooter>\n <NavUser user={data.user} />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n AudioWaveform,\n BookOpen,\n Bot,\n Command,\n Frame,\n GalleryVerticalEnd,\n Map,\n PieChart,\n Settings2,\n SquareTerminal,\n} from \"lucide-react\"\n\nimport { NavMain } from \"@/registry/new-york/block/sidebar-07/components/nav-main\"\nimport { NavProjects } from \"@/registry/new-york/block/sidebar-07/components/nav-projects\"\nimport { NavUser } from \"@/registry/new-york/block/sidebar-07/components/nav-user\"\nimport { TeamSwitcher } from \"@/registry/new-york/block/sidebar-07/components/team-switcher\"\nimport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarHeader,\n SidebarRail,\n} from \"@/registry/new-york/ui/sidebar\"\n\n// This is sample data.\nconst data = {\n user: {\n name: \"shadcn\",\n email: \"m@example.com\",\n avatar: \"/avatars/shadcn.jpg\",\n },\n teams: [\n {\n name: \"Acme Inc\",\n logo: GalleryVerticalEnd,\n plan: \"Enterprise\",\n },\n {\n name: \"Acme Corp.\",\n logo: AudioWaveform,\n plan: \"Startup\",\n },\n {\n name: \"Evil Corp.\",\n logo: Command,\n plan: \"Free\",\n },\n ],\n navMain: [\n {\n title: \"Playground\",\n url: \"#\",\n icon: SquareTerminal,\n isActive: true,\n items: [\n {\n title: \"History\",\n url: \"#\",\n },\n {\n title: \"Starred\",\n url: \"#\",\n },\n {\n title: \"Settings\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Models\",\n url: \"#\",\n icon: Bot,\n items: [\n {\n title: \"Genesis\",\n url: \"#\",\n },\n {\n title: \"Explorer\",\n url: \"#\",\n },\n {\n title: \"Quantum\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Documentation\",\n url: \"#\",\n icon: BookOpen,\n items: [\n {\n title: \"Introduction\",\n url: \"#\",\n },\n {\n title: \"Get Started\",\n url: \"#\",\n },\n {\n title: \"Tutorials\",\n url: \"#\",\n },\n {\n title: \"Changelog\",\n url: \"#\",\n },\n ],\n },\n {\n title: \"Settings\",\n url: \"#\",\n icon: Settings2,\n items: [\n {\n title: \"General\",\n url: \"#\",\n },\n {\n title: \"Team\",\n url: \"#\",\n },\n {\n title: \"Billing\",\n url: \"#\",\n },\n {\n title: \"Limits\",\n url: \"#\",\n },\n ],\n },\n ],\n projects: [\n {\n name: \"Design Engineering\",\n url: \"#\",\n icon: Frame,\n },\n {\n name: \"Sales & Marketing\",\n url: \"#\",\n icon: PieChart,\n },\n {\n name: \"Travel\",\n url: \"#\",\n icon: Map,\n },\n ],\n}\n\nexport function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {\n return (\n <Sidebar collapsible=\"icon\" {...props}>\n <SidebarHeader>\n <TeamSwitcher teams={data.teams} />\n </SidebarHeader>\n <SidebarContent>\n <NavMain items={data.navMain} />\n <NavProjects projects={data.projects} />\n </SidebarContent>\n <SidebarFooter>\n <NavUser user={data.user} />\n </SidebarFooter>\n <SidebarRail />\n </Sidebar>\n )\n}\n",
"type": "registry:component"
},
{
"path": "block/sidebar-07/components/nav-main.tsx",
"content": "\"use client\"\n\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/new-york/ui/collapsible\"\nimport {\n SidebarGroup,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n} from \"@/registry/new-york/ui/sidebar\"\n\nexport function NavMain({\n items,\n}: {\n items: {\n title: string\n url: string\n icon?: LucideIcon\n isActive?: boolean\n items?: {\n title: string\n url: string\n }[]\n }[]\n}) {\n return (\n <SidebarGroup>\n <SidebarGroupLabel>Platform</SidebarGroupLabel>\n <SidebarMenu>\n {items.map((item) => (\n <Collapsible key={item.title} asChild defaultOpen={item.isActive}>\n <SidebarMenuItem>\n <SidebarMenuButton asChild tooltip={item.title}>\n <a href={item.url}>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n </a>\n </SidebarMenuButton>\n {item.items?.length ? (\n <>\n <CollapsibleTrigger asChild>\n <SidebarMenuAction className=\"data-[state=open]:rotate-90\">\n <ChevronRight />\n <span className=\"sr-only\">Toggle</span>\n </SidebarMenuAction>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.items?.map((subItem) => (\n <SidebarMenuSubItem key={subItem.title}>\n <SidebarMenuSubButton asChild>\n <a href={subItem.url}>\n <span>{subItem.title}</span>\n </a>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </>\n ) : null}\n </SidebarMenuItem>\n </Collapsible>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n )\n}\n",
"content": "\"use client\"\n\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@/registry/new-york/ui/collapsible\"\nimport {\n SidebarGroup,\n SidebarGroupLabel,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n} from \"@/registry/new-york/ui/sidebar\"\n\nexport function NavMain({\n items,\n}: {\n items: {\n title: string\n url: string\n icon?: LucideIcon\n isActive?: boolean\n items?: {\n title: string\n url: string\n }[]\n }[]\n}) {\n return (\n <SidebarGroup>\n <SidebarGroupLabel>Platform</SidebarGroupLabel>\n <SidebarMenu>\n {items.map((item) => (\n <Collapsible\n key={item.title}\n asChild\n defaultOpen={item.isActive}\n className=\"group/collapsible\"\n >\n <SidebarMenuItem>\n <CollapsibleTrigger asChild>\n <SidebarMenuButton tooltip={item.title}>\n {item.icon && <item.icon />}\n <span>{item.title}</span>\n <ChevronRight className=\"ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90\" />\n </SidebarMenuButton>\n </CollapsibleTrigger>\n <CollapsibleContent>\n <SidebarMenuSub>\n {item.items?.map((subItem) => (\n <SidebarMenuSubItem key={subItem.title}>\n <SidebarMenuSubButton asChild>\n <a href={subItem.url}>\n <span>{subItem.title}</span>\n </a>\n </SidebarMenuSubButton>\n </SidebarMenuSubItem>\n ))}\n </SidebarMenuSub>\n </CollapsibleContent>\n </SidebarMenuItem>\n </Collapsible>\n ))}\n </SidebarMenu>\n </SidebarGroup>\n )\n}\n",
"type": "registry:component"
},
{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,104 @@
import * as React from "react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarProvider,
} from "@/registry/default/ui/sidebar"
const projects = [
{
name: "Design Engineering",
url: "#",
icon: Frame,
badge: "24",
},
{
name: "Sales & Marketing",
url: "#",
icon: PieChart,
badge: "12",
},
{
name: "Travel",
url: "#",
icon: Map,
badge: "3",
},
{
name: "Support",
url: "#",
icon: LifeBuoy,
badge: "21",
},
{
name: "Feedback",
url: "#",
icon: Send,
badge: "8",
},
]
// Dummy fetch function
async function fetchProjects() {
await new Promise((resolve) => setTimeout(resolve, 3000))
return projects
}
export default function AppSidebar() {
return (
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
)
}
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}

View File

@@ -23,7 +23,7 @@ export default function Page() {
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<header className="flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>

View File

@@ -2,10 +2,12 @@
import * as React from "react"
import {
AudioWaveform,
BookOpen,
Bot,
Command,
Frame,
GalleryVerticalEnd,
Map,
PieChart,
Settings2,
@@ -15,14 +17,12 @@ import {
import { NavMain } from "@/registry/default/block/sidebar-07/components/nav-main"
import { NavProjects } from "@/registry/default/block/sidebar-07/components/nav-projects"
import { NavUser } from "@/registry/default/block/sidebar-07/components/nav-user"
import { TeamSwitcher } from "@/registry/default/block/sidebar-07/components/team-switcher"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@/registry/default/ui/sidebar"
@@ -33,6 +33,23 @@ const data = {
email: "m@example.com",
avatar: "/avatars/shadcn.jpg",
},
teams: [
{
name: "Acme Inc",
logo: GalleryVerticalEnd,
plan: "Enterprise",
},
{
name: "Acme Corp.",
logo: AudioWaveform,
plan: "Startup",
},
{
name: "Evil Corp.",
logo: Command,
plan: "Free",
},
],
navMain: [
{
title: "Playground",
@@ -143,21 +160,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<Command className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
<TeamSwitcher teams={data.teams} />
</SidebarHeader>
<SidebarContent>
<NavMain items={data.navMain} />

View File

@@ -38,37 +38,33 @@ export function NavMain({
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
<Collapsible key={item.title} asChild defaultOpen={item.isActive}>
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
<SidebarMenuButton asChild tooltip={item.title}>
<a href={item.url}>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
{item.icon && <item.icon />}
<span>{item.title}</span>
</a>
</SidebarMenuButton>
{item.items?.length ? (
<>
<CollapsibleTrigger asChild>
<SidebarMenuAction className="data-[state=open]:rotate-90">
<ChevronRight />
<span className="sr-only">Toggle</span>
</SidebarMenuAction>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
) : null}
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}

View File

@@ -0,0 +1,89 @@
"use client"
import * as React from "react"
import { ChevronsUpDown, Plus } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/registry/default/ui/dropdown-menu"
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/registry/default/ui/sidebar"
export function TeamSwitcher({
teams,
}: {
teams: {
name: string
logo: React.ElementType
plan: string
}[]
}) {
const { isMobile } = useSidebar()
const [activeTeam, setActiveTeam] = React.useState(teams[0])
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<activeTeam.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{activeTeam.name}
</span>
<span className="truncate text-xs">{activeTeam.plan}</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
align="start"
side={isMobile ? "bottom" : "right"}
sideOffset={4}
>
<DropdownMenuLabel className="text-xs text-muted-foreground">
Teams
</DropdownMenuLabel>
{teams.map((team, index) => (
<DropdownMenuItem
key={team.name}
onClick={() => setActiveTeam(team)}
className="gap-2 p-2"
>
<div className="flex size-6 items-center justify-center rounded-sm border">
<team.logo className="size-4 shrink-0" />
</div>
{team.name}
<DropdownMenuShortcut>{index + 1}</DropdownMenuShortcut>
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-2 p-2">
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
<Plus className="size-4" />
</div>
<div className="font-medium text-muted-foreground">Add team</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}

View File

@@ -161,7 +161,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
{/* This will make the sidebar appear as icons. */}
<Sidebar
collapsible="none"
className="!w-[--sidebar-width-icon] border-r"
className="!w-[calc(var(--sidebar-width-icon)_+_1px)] border-r"
>
<SidebarHeader>
<SidebarMenu>

View File

@@ -11,6 +11,7 @@ import { Button } from "@/registry/default/ui/button"
import { Input } from "@/registry/default/ui/input"
import { Separator } from "@/registry/default/ui/separator"
import { Sheet, SheetContent } from "@/registry/default/ui/sheet"
import { Skeleton } from "@/registry/default/ui/skeleton"
import {
Tooltip,
TooltipContent,
@@ -642,6 +643,44 @@ const SidebarMenuBadge = React.forwardRef<
))
SidebarMenuBadge.displayName = "SidebarMenuBadge"
const SidebarMenuSkeleton = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
showIcon?: boolean
}
>(({ className, showIcon = false, ...props }, ref) => {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])
return (
<div
ref={ref}
data-sidebar="menu-skeleton"
className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)}
{...props}
>
{showIcon && (
<Skeleton
className="size-4 rounded-md"
data-sidebar="menu-skeleton-icon"
/>
)}
<Skeleton
className="h-4 flex-1 max-w-[--skeleton-width]"
data-sidebar="menu-skeleton-text"
style={
{
"--skeleton-width": width,
} as React.CSSProperties
}
/>
</div>
)
})
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton"
const SidebarMenuSub = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
@@ -711,6 +750,7 @@ export {
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,

View File

@@ -1,20 +1,7 @@
"use client"
import {
Frame,
LifeBuoy,
Map,
MoreHorizontal,
PieChart,
Send,
} from "lucide-react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
import {
Sidebar,
SidebarContent,
@@ -22,7 +9,6 @@ import {
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,

View File

@@ -0,0 +1,104 @@
import * as React from "react"
import { Frame, LifeBuoy, Map, PieChart, Send } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarProvider,
} from "@/registry/new-york/ui/sidebar"
const projects = [
{
name: "Design Engineering",
url: "#",
icon: Frame,
badge: "24",
},
{
name: "Sales & Marketing",
url: "#",
icon: PieChart,
badge: "12",
},
{
name: "Travel",
url: "#",
icon: Map,
badge: "3",
},
{
name: "Support",
url: "#",
icon: LifeBuoy,
badge: "21",
},
{
name: "Feedback",
url: "#",
icon: Send,
badge: "8",
},
]
// Dummy fetch function
async function fetchProjects() {
await new Promise((resolve) => setTimeout(resolve, 3000))
return projects
}
export default function AppSidebar() {
return (
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
</SidebarProvider>
)
}
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}

View File

@@ -23,7 +23,7 @@ export default function Page() {
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<header className="flex sticky top-0 bg-background h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>

View File

@@ -2,10 +2,12 @@
import * as React from "react"
import {
AudioWaveform,
BookOpen,
Bot,
Command,
Frame,
GalleryVerticalEnd,
Map,
PieChart,
Settings2,
@@ -15,14 +17,12 @@ import {
import { NavMain } from "@/registry/new-york/block/sidebar-07/components/nav-main"
import { NavProjects } from "@/registry/new-york/block/sidebar-07/components/nav-projects"
import { NavUser } from "@/registry/new-york/block/sidebar-07/components/nav-user"
import { TeamSwitcher } from "@/registry/new-york/block/sidebar-07/components/team-switcher"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@/registry/new-york/ui/sidebar"
@@ -33,6 +33,23 @@ const data = {
email: "m@example.com",
avatar: "/avatars/shadcn.jpg",
},
teams: [
{
name: "Acme Inc",
logo: GalleryVerticalEnd,
plan: "Enterprise",
},
{
name: "Acme Corp.",
logo: AudioWaveform,
plan: "Startup",
},
{
name: "Evil Corp.",
logo: Command,
plan: "Free",
},
],
navMain: [
{
title: "Playground",
@@ -143,21 +160,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar collapsible="icon" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<Command className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
<TeamSwitcher teams={data.teams} />
</SidebarHeader>
<SidebarContent>
<NavMain items={data.navMain} />

View File

@@ -38,37 +38,33 @@ export function NavMain({
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
<Collapsible key={item.title} asChild defaultOpen={item.isActive}>
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
<SidebarMenuButton asChild tooltip={item.title}>
<a href={item.url}>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title}>
{item.icon && <item.icon />}
<span>{item.title}</span>
</a>
</SidebarMenuButton>
{item.items?.length ? (
<>
<CollapsibleTrigger asChild>
<SidebarMenuAction className="data-[state=open]:rotate-90">
<ChevronRight />
<span className="sr-only">Toggle</span>
</SidebarMenuAction>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
) : null}
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
))}

View File

@@ -0,0 +1,89 @@
"use client"
import * as React from "react"
import { ChevronsUpDown, Plus } from "lucide-react"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/registry/new-york/ui/sidebar"
export function TeamSwitcher({
teams,
}: {
teams: {
name: string
logo: React.ElementType
plan: string
}[]
}) {
const { isMobile } = useSidebar()
const [activeTeam, setActiveTeam] = React.useState(teams[0])
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<activeTeam.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{activeTeam.name}
</span>
<span className="truncate text-xs">{activeTeam.plan}</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
align="start"
side={isMobile ? "bottom" : "right"}
sideOffset={4}
>
<DropdownMenuLabel className="text-xs text-muted-foreground">
Teams
</DropdownMenuLabel>
{teams.map((team, index) => (
<DropdownMenuItem
key={team.name}
onClick={() => setActiveTeam(team)}
className="gap-2 p-2"
>
<div className="flex size-6 items-center justify-center rounded-sm border">
<team.logo className="size-4 shrink-0" />
</div>
{team.name}
<DropdownMenuShortcut>{index + 1}</DropdownMenuShortcut>
</DropdownMenuItem>
))}
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-2 p-2">
<div className="flex size-6 items-center justify-center rounded-md border bg-background">
<Plus className="size-4" />
</div>
<div className="font-medium text-muted-foreground">Add team</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
)
}

View File

@@ -161,7 +161,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
{/* This will make the sidebar appear as icons. */}
<Sidebar
collapsible="none"
className="!w-[--sidebar-width-icon] border-r"
className="!w-[calc(var(--sidebar-width-icon)_+_1px)] border-r"
>
<SidebarHeader>
<SidebarMenu>

View File

@@ -11,6 +11,7 @@ import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input"
import { Separator } from "@/registry/new-york/ui/separator"
import { Sheet, SheetContent } from "@/registry/new-york/ui/sheet"
import { Skeleton } from "@/registry/new-york/ui/skeleton"
import {
Tooltip,
TooltipContent,
@@ -642,6 +643,44 @@ const SidebarMenuBadge = React.forwardRef<
))
SidebarMenuBadge.displayName = "SidebarMenuBadge"
const SidebarMenuSkeleton = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
showIcon?: boolean
}
>(({ className, showIcon = false, ...props }, ref) => {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
}, [])
return (
<div
ref={ref}
data-sidebar="menu-skeleton"
className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)}
{...props}
>
{showIcon && (
<Skeleton
className="size-4 rounded-md"
data-sidebar="menu-skeleton-icon"
/>
)}
<Skeleton
className="h-4 flex-1 max-w-[--skeleton-width]"
data-sidebar="menu-skeleton-text"
style={
{
"--skeleton-width": width,
} as React.CSSProperties
}
/>
</div>
)
})
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton"
const SidebarMenuSub = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
@@ -711,6 +750,7 @@ export {
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,

View File

@@ -982,4 +982,14 @@ export const examples: Registry = [
},
],
},
{
name: "demo-sidebar-rsc",
type: "registry:block",
files: [
{
path: "block/demo-sidebar-rsc.tsx",
type: "registry:component",
},
],
},
]

View File

@@ -256,14 +256,15 @@ export const ui: Registry = [
"tooltip",
"input",
"use-mobile",
"skeleton",
],
files: ["ui/sidebar.tsx"],
cssVars: {
light: {
"sidebar-background": "0 0% 98%",
"sidebar-foreground": "240 5.3% 26.1%",
"sidebar-primary": "220 100% 50%",
"sidebar-primary-foreground": "0 0% 100%",
"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%",
@@ -272,8 +273,8 @@ export const ui: Registry = [
dark: {
"sidebar-background": "240 5.9% 10%",
"sidebar-foreground": "240 4.8% 95.9%",
"sidebar-primary": "220 100% 50%",
"sidebar-primary-foreground": "0 0% 100%",
"sidebar-primary": "0 0% 98%",
"sidebar-primary-foreground": "240 5.9% 10%",
"sidebar-accent": "240 3.7% 15.9%",
"sidebar-accent-foreground": "240 4.8% 95.9%",
"sidebar-border": "240 3.7% 15.9%",

View File

@@ -33,12 +33,12 @@
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--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%;
--sidebar-ring: 240 5% 64.9%;
}
.dark {
@@ -70,12 +70,12 @@
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 220 100% 50%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--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%;
--sidebar-ring: 240 4.9% 83.9%;
}
}