mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-27 06:34:12 +00:00
docs
This commit is contained in:
104
apps/www/__registry__/default/block/demo-sidebar-rsc.tsx
Normal file
104
apps/www/__registry__/default/block/demo-sidebar-rsc.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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,
|
||||
|
||||
104
apps/www/__registry__/new-york/block/demo-sidebar-rsc.tsx
Normal file
104
apps/www/__registry__/new-york/block/demo-sidebar-rsc.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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%",
|
||||
|
||||
12
apps/www/public/r/styles/default/demo-sidebar-rsc.json
Normal file
12
apps/www/public/r/styles/default/demo-sidebar-rsc.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
]
|
||||
|
||||
12
apps/www/public/r/styles/new-york/demo-sidebar-rsc.json
Normal file
12
apps/www/public/r/styles/new-york/demo-sidebar-rsc.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
104
apps/www/registry/default/block/demo-sidebar-rsc.tsx
Normal file
104
apps/www/registry/default/block/demo-sidebar-rsc.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
104
apps/www/registry/new-york/block/demo-sidebar-rsc.tsx
Normal file
104
apps/www/registry/new-york/block/demo-sidebar-rsc.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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%",
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user