feat: new components (#2144)

This commit is contained in:
shadcn
2023-12-22 23:36:59 +04:00
committed by GitHub
parent 6145dd8118
commit 57d404b5d3
165 changed files with 5738 additions and 356 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
@@ -52,7 +52,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
@@ -90,7 +90,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm

View File

@@ -16,7 +16,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm

2
.nvmrc
View File

@@ -1 +1 @@
v16.18.0
v18.17.0

View File

@@ -3,4 +3,5 @@ node_modules
.next
build
.contentlayer
__registry__/index.tsx
__registry__/index.tsx
app/examples/mail/components/mail.tsx

View File

@@ -68,6 +68,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/ui/card")),
files: ["registry/default/ui/card.tsx"],
},
"carousel": {
name: "carousel",
type: "components:ui",
registryDependencies: ["button"],
component: React.lazy(() => import("@/registry/default/ui/carousel")),
files: ["registry/default/ui/carousel.tsx"],
},
"checkbox": {
name: "checkbox",
type: "components:ui",
@@ -103,6 +110,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/ui/dialog")),
files: ["registry/default/ui/dialog.tsx"],
},
"drawer": {
name: "drawer",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/drawer")),
files: ["registry/default/ui/drawer.tsx"],
},
"dropdown-menu": {
name: "dropdown-menu",
type: "components:ui",
@@ -152,6 +166,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/ui/navigation-menu")),
files: ["registry/default/ui/navigation-menu.tsx"],
},
"pagination": {
name: "pagination",
type: "components:ui",
registryDependencies: ["button"],
component: React.lazy(() => import("@/registry/default/ui/pagination")),
files: ["registry/default/ui/pagination.tsx"],
},
"popover": {
name: "popover",
type: "components:ui",
@@ -173,6 +194,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/ui/radio-group")),
files: ["registry/default/ui/radio-group.tsx"],
},
"resizable": {
name: "resizable",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/resizable")),
files: ["registry/default/ui/resizable.tsx"],
},
"scroll-area": {
name: "scroll-area",
type: "components:ui",
@@ -215,6 +243,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/ui/slider")),
files: ["registry/default/ui/slider.tsx"],
},
"sonner": {
name: "sonner",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/ui/sonner")),
files: ["registry/default/ui/sonner.tsx"],
},
"switch": {
name: "switch",
type: "components:ui",
@@ -439,6 +474,48 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/card-with-form")),
files: ["registry/default/example/card-with-form.tsx"],
},
"carousel-demo": {
name: "carousel-demo",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-demo")),
files: ["registry/default/example/carousel-demo.tsx"],
},
"carousel-size": {
name: "carousel-size",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-size")),
files: ["registry/default/example/carousel-size.tsx"],
},
"carousel-spacing": {
name: "carousel-spacing",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-spacing")),
files: ["registry/default/example/carousel-spacing.tsx"],
},
"carousel-orientation": {
name: "carousel-orientation",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-orientation")),
files: ["registry/default/example/carousel-orientation.tsx"],
},
"carousel-api": {
name: "carousel-api",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-api")),
files: ["registry/default/example/carousel-api.tsx"],
},
"carousel-plugin": {
name: "carousel-plugin",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/default/example/carousel-plugin")),
files: ["registry/default/example/carousel-plugin.tsx"],
},
"checkbox-demo": {
name: "checkbox-demo",
type: "components:example",
@@ -509,6 +586,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/combobox-popover")),
files: ["registry/default/example/combobox-popover.tsx"],
},
"combobox-responsive": {
name: "combobox-responsive",
type: "components:example",
registryDependencies: ["combobox","popover","drawer"],
component: React.lazy(() => import("@/registry/default/example/combobox-responsive")),
files: ["registry/default/example/combobox-responsive.tsx"],
},
"command-demo": {
name: "command-demo",
type: "components:example",
@@ -579,6 +663,20 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/dialog-close-button")),
files: ["registry/default/example/dialog-close-button.tsx"],
},
"drawer-demo": {
name: "drawer-demo",
type: "components:example",
registryDependencies: ["drawer"],
component: React.lazy(() => import("@/registry/default/example/drawer-demo")),
files: ["registry/default/example/drawer-demo.tsx"],
},
"drawer-dialog": {
name: "drawer-dialog",
type: "components:example",
registryDependencies: ["drawer","dialog"],
component: React.lazy(() => import("@/registry/default/example/drawer-dialog")),
files: ["registry/default/example/drawer-dialog.tsx"],
},
"dropdown-menu-demo": {
name: "dropdown-menu-demo",
type: "components:example",
@@ -677,6 +775,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/navigation-menu-demo")),
files: ["registry/default/example/navigation-menu-demo.tsx"],
},
"pagination-demo": {
name: "pagination-demo",
type: "components:example",
registryDependencies: ["pagination"],
component: React.lazy(() => import("@/registry/default/example/pagination-demo")),
files: ["registry/default/example/pagination-demo.tsx"],
},
"popover-demo": {
name: "popover-demo",
type: "components:example",
@@ -705,6 +810,34 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/radio-group-form")),
files: ["registry/default/example/radio-group-form.tsx"],
},
"resizable-demo": {
name: "resizable-demo",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/default/example/resizable-demo")),
files: ["registry/default/example/resizable-demo.tsx"],
},
"resizable-demo-with-handle": {
name: "resizable-demo-with-handle",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/default/example/resizable-demo-with-handle")),
files: ["registry/default/example/resizable-demo-with-handle.tsx"],
},
"resizable-vertical": {
name: "resizable-vertical",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/default/example/resizable-vertical")),
files: ["registry/default/example/resizable-vertical.tsx"],
},
"resizable-handle": {
name: "resizable-handle",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/default/example/resizable-handle")),
files: ["registry/default/example/resizable-handle.tsx"],
},
"scroll-area-demo": {
name: "scroll-area-demo",
type: "components:example",
@@ -775,6 +908,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/default/example/slider-demo")),
files: ["registry/default/example/slider-demo.tsx"],
},
"sonner-demo": {
name: "sonner-demo",
type: "components:example",
registryDependencies: ["sonner"],
component: React.lazy(() => import("@/registry/default/example/sonner-demo")),
files: ["registry/default/example/sonner-demo.tsx"],
},
"switch-demo": {
name: "switch-demo",
type: "components:example",
@@ -1147,6 +1287,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/ui/card")),
files: ["registry/new-york/ui/card.tsx"],
},
"carousel": {
name: "carousel",
type: "components:ui",
registryDependencies: ["button"],
component: React.lazy(() => import("@/registry/new-york/ui/carousel")),
files: ["registry/new-york/ui/carousel.tsx"],
},
"checkbox": {
name: "checkbox",
type: "components:ui",
@@ -1182,6 +1329,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/ui/dialog")),
files: ["registry/new-york/ui/dialog.tsx"],
},
"drawer": {
name: "drawer",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/new-york/ui/drawer")),
files: ["registry/new-york/ui/drawer.tsx"],
},
"dropdown-menu": {
name: "dropdown-menu",
type: "components:ui",
@@ -1231,6 +1385,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/ui/navigation-menu")),
files: ["registry/new-york/ui/navigation-menu.tsx"],
},
"pagination": {
name: "pagination",
type: "components:ui",
registryDependencies: ["button"],
component: React.lazy(() => import("@/registry/new-york/ui/pagination")),
files: ["registry/new-york/ui/pagination.tsx"],
},
"popover": {
name: "popover",
type: "components:ui",
@@ -1252,6 +1413,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/ui/radio-group")),
files: ["registry/new-york/ui/radio-group.tsx"],
},
"resizable": {
name: "resizable",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/new-york/ui/resizable")),
files: ["registry/new-york/ui/resizable.tsx"],
},
"scroll-area": {
name: "scroll-area",
type: "components:ui",
@@ -1294,6 +1462,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/ui/slider")),
files: ["registry/new-york/ui/slider.tsx"],
},
"sonner": {
name: "sonner",
type: "components:ui",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/new-york/ui/sonner")),
files: ["registry/new-york/ui/sonner.tsx"],
},
"switch": {
name: "switch",
type: "components:ui",
@@ -1518,6 +1693,48 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/card-with-form")),
files: ["registry/new-york/example/card-with-form.tsx"],
},
"carousel-demo": {
name: "carousel-demo",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-demo")),
files: ["registry/new-york/example/carousel-demo.tsx"],
},
"carousel-size": {
name: "carousel-size",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-size")),
files: ["registry/new-york/example/carousel-size.tsx"],
},
"carousel-spacing": {
name: "carousel-spacing",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-spacing")),
files: ["registry/new-york/example/carousel-spacing.tsx"],
},
"carousel-orientation": {
name: "carousel-orientation",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-orientation")),
files: ["registry/new-york/example/carousel-orientation.tsx"],
},
"carousel-api": {
name: "carousel-api",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-api")),
files: ["registry/new-york/example/carousel-api.tsx"],
},
"carousel-plugin": {
name: "carousel-plugin",
type: "components:example",
registryDependencies: ["carousel"],
component: React.lazy(() => import("@/registry/new-york/example/carousel-plugin")),
files: ["registry/new-york/example/carousel-plugin.tsx"],
},
"checkbox-demo": {
name: "checkbox-demo",
type: "components:example",
@@ -1588,6 +1805,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/combobox-popover")),
files: ["registry/new-york/example/combobox-popover.tsx"],
},
"combobox-responsive": {
name: "combobox-responsive",
type: "components:example",
registryDependencies: ["combobox","popover","drawer"],
component: React.lazy(() => import("@/registry/new-york/example/combobox-responsive")),
files: ["registry/new-york/example/combobox-responsive.tsx"],
},
"command-demo": {
name: "command-demo",
type: "components:example",
@@ -1658,6 +1882,20 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/dialog-close-button")),
files: ["registry/new-york/example/dialog-close-button.tsx"],
},
"drawer-demo": {
name: "drawer-demo",
type: "components:example",
registryDependencies: ["drawer"],
component: React.lazy(() => import("@/registry/new-york/example/drawer-demo")),
files: ["registry/new-york/example/drawer-demo.tsx"],
},
"drawer-dialog": {
name: "drawer-dialog",
type: "components:example",
registryDependencies: ["drawer","dialog"],
component: React.lazy(() => import("@/registry/new-york/example/drawer-dialog")),
files: ["registry/new-york/example/drawer-dialog.tsx"],
},
"dropdown-menu-demo": {
name: "dropdown-menu-demo",
type: "components:example",
@@ -1756,6 +1994,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/navigation-menu-demo")),
files: ["registry/new-york/example/navigation-menu-demo.tsx"],
},
"pagination-demo": {
name: "pagination-demo",
type: "components:example",
registryDependencies: ["pagination"],
component: React.lazy(() => import("@/registry/new-york/example/pagination-demo")),
files: ["registry/new-york/example/pagination-demo.tsx"],
},
"popover-demo": {
name: "popover-demo",
type: "components:example",
@@ -1784,6 +2029,34 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/radio-group-form")),
files: ["registry/new-york/example/radio-group-form.tsx"],
},
"resizable-demo": {
name: "resizable-demo",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/new-york/example/resizable-demo")),
files: ["registry/new-york/example/resizable-demo.tsx"],
},
"resizable-demo-with-handle": {
name: "resizable-demo-with-handle",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/new-york/example/resizable-demo-with-handle")),
files: ["registry/new-york/example/resizable-demo-with-handle.tsx"],
},
"resizable-vertical": {
name: "resizable-vertical",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/new-york/example/resizable-vertical")),
files: ["registry/new-york/example/resizable-vertical.tsx"],
},
"resizable-handle": {
name: "resizable-handle",
type: "components:example",
registryDependencies: ["resizable"],
component: React.lazy(() => import("@/registry/new-york/example/resizable-handle")),
files: ["registry/new-york/example/resizable-handle.tsx"],
},
"scroll-area-demo": {
name: "scroll-area-demo",
type: "components:example",
@@ -1854,6 +2127,13 @@ export const Index: Record<string, any> = {
component: React.lazy(() => import("@/registry/new-york/example/slider-demo")),
files: ["registry/new-york/example/slider-demo.tsx"],
},
"sonner-demo": {
name: "sonner-demo",
type: "components:example",
registryDependencies: ["sonner"],
component: React.lazy(() => import("@/registry/new-york/example/sonner-demo")),
files: ["registry/new-york/example/sonner-demo.tsx"],
},
"switch-demo": {
name: "switch-demo",
type: "components:example",

View File

@@ -4,13 +4,12 @@ import { allDocs } from "contentlayer/generated"
import "@/styles/mdx.css"
import type { Metadata } from "next"
import Link from "next/link"
import { ChevronRightIcon } from "@radix-ui/react-icons"
import { ChevronRightIcon, ExternalLinkIcon } from "@radix-ui/react-icons"
import Balancer from "react-wrap-balancer"
import { siteConfig } from "@/config/site"
import { getTableOfContents } from "@/lib/toc"
import { absoluteUrl, cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Mdx } from "@/components/mdx-components"
import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc"
@@ -107,27 +106,28 @@ export default async function DocPage({ params }: DocPageProps) {
</p>
)}
</div>
{doc.radix ? (
{doc.links ? (
<div className="flex items-center space-x-2 pt-4">
{doc.radix?.link && (
{doc.links?.doc && (
<Link
href={doc.radix.link}
href={doc.links.doc}
target="_blank"
rel="noreferrer"
className={cn(badgeVariants({ variant: "secondary" }))}
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
>
<Icons.radix className="mr-1 h-3 w-3" />
Radix UI
Docs
<ExternalLinkIcon className="h-3 w-3" />
</Link>
)}
{doc.radix?.api && (
{doc.links?.api && (
<Link
href={doc.radix.api}
href={doc.links.api}
target="_blank"
rel="noreferrer"
className={cn(badgeVariants({ variant: "secondary" }))}
className={cn(badgeVariants({ variant: "secondary" }), "gap-1")}
>
API Reference
<ExternalLinkIcon className="h-3 w-3" />
</Link>
)}
</div>

View File

@@ -11,7 +11,7 @@ export default function DocsLayout({ children }: DocsLayoutProps) {
<div className="border-b">
<div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block">
<ScrollArea className="h-full py-6 pl-8 pr-6 lg:py-8">
<ScrollArea className="h-full py-6 pr-6 lg:py-8">
<DocsSidebarNav items={docsConfig.sidebarNav} />
</ScrollArea>
</aside>

View File

@@ -40,7 +40,7 @@ export default function AuthenticationPage() {
>
Login
</Link>
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
<div className="relative hidden h-full flex-col bg-muted p-10 text-white lg:flex dark:border-r">
<div className="absolute inset-0 bg-zinc-900" />
<div className="relative z-20 flex items-center text-lg font-medium">
<svg

View File

@@ -31,8 +31,8 @@ export function DemoGithub() {
<div className="space-y-1">
<CardTitle>shadcn/ui</CardTitle>
<CardDescription>
Beautifully designed components built with Radix UI and Tailwind
CSS.
Beautifully designed components that you can copy and paste into
your apps. Accessible. Customizable. Open Source.
</CardDescription>
</div>
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">

View File

@@ -71,7 +71,12 @@ export function Overview() {
axisLine={false}
tickFormatter={(value) => `$${value}`}
/>
<Bar dataKey="total" fill="#adfa1d" radius={[4, 4, 0, 0]} />
<Bar
dataKey="total"
fill="currentColor"
radius={[4, 4, 0, 0]}
className="fill-primary"
/>
</BarChart>
</ResponsiveContainer>
)

View File

@@ -100,6 +100,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<AvatarImage
src={`https://avatar.vercel.sh/${selectedTeam.value}.png`}
alt={selectedTeam.label}
className="grayscale"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>

View File

@@ -1,16 +1,16 @@
import { Metadata } from "next"
import Link from "next/link"
import { ArrowRightIcon } from "@radix-ui/react-icons"
import { cn } from "@/lib/utils"
import { Announcement } from "@/components/announcement"
import { ExamplesNav } from "@/components/examples-nav"
import {
PageActions,
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
export const metadata: Metadata = {
title: "Examples",
@@ -25,27 +25,17 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
return (
<>
<div className="container relative">
<PageHeader className="page-header pb-8">
<Link
href="/docs/changelog"
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="sm:hidden">Style, a new CLI and more.</span>
<span className="hidden sm:inline">
Introducing Style, a new CLI and more.
</span>
<ArrowRightIcon className="ml-1 h-4 w-4" />
</Link>
<PageHeader>
<Announcement />
<PageHeaderHeading className="hidden md:block">
Check out some examples.
Check out some examples
</PageHeaderHeading>
<PageHeaderHeading className="md:hidden">Examples</PageHeaderHeading>
<PageHeaderDescription>
Dashboard, cards, authentication. Some examples built using the
components. Use this as a guide to build your own.
</PageHeaderDescription>
<section className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<PageActions>
<Link
href="/docs"
className={cn(buttonVariants(), "rounded-[6px]")}
@@ -61,11 +51,11 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
>
Components
</Link>
</section>
</PageActions>
</PageHeader>
<section>
<ExamplesNav />
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow-md md:shadow-xl">
{children}
</div>
</section>

View File

@@ -0,0 +1,63 @@
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york/ui/select"
interface AccountSwitcherProps {
isCollapsed: boolean
accounts: {
label: string
email: string
icon: React.ReactNode
}[]
}
export function AccountSwitcher({
isCollapsed,
accounts,
}: AccountSwitcherProps) {
const [selectedAccount, setSelectedAccount] = React.useState<string>(
accounts[0].email
)
return (
<Select defaultValue={selectedAccount} onValueChange={setSelectedAccount}>
<SelectTrigger
className={cn(
"flex flex-1 items-center gap-2 [&>span]:line-clamp-1 [&>span]:flex [&>span]:w-full [&>span]:items-center [&>span]:gap-1 [&>span]:truncate [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0",
isCollapsed &&
"flex h-8 w-8 items-center justify-center p-0 [&>span]:w-auto [&>svg]:hidden"
)}
aria-label="Select account"
>
<SelectValue placeholder="Select an account">
{accounts.find((account) => account.email === selectedAccount)?.icon}
<span className={cn("ml-2", isCollapsed && "hidden")}>
{
accounts.find((account) => account.email === selectedAccount)
?.label
}
</span>
</SelectValue>
</SelectTrigger>
<SelectContent>
{accounts.map((account) => (
<SelectItem key={account.email} value={account.email}>
<div className="flex items-center gap-3 [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg]:text-foreground">
{account.icon}
{account.email}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
)
}

View File

@@ -0,0 +1,254 @@
import addDays from "date-fns/addDays"
import addHours from "date-fns/addHours"
import format from "date-fns/format"
import nextSaturday from "date-fns/nextSaturday"
import {
Archive,
ArchiveX,
Clock,
Forward,
MoreVertical,
Reply,
ReplyAll,
Trash2,
} from "lucide-react"
import {
DropdownMenuContent,
DropdownMenuItem,
} from "@/registry/default/ui/dropdown-menu"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
import { Button } from "@/registry/new-york/ui/button"
import { Calendar } from "@/registry/new-york/ui/calendar"
import {
DropdownMenu,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
import { Label } from "@/registry/new-york/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york/ui/popover"
import { Separator } from "@/registry/new-york/ui/separator"
import { Switch } from "@/registry/new-york/ui/switch"
import { Textarea } from "@/registry/new-york/ui/textarea"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york/ui/tooltip"
import { Mail } from "@/app/examples/mail/data"
interface MailDisplayProps {
mail: Mail | null
}
export function MailDisplay({ mail }: MailDisplayProps) {
const today = new Date()
return (
<div className="flex h-full flex-col">
<div className="flex items-center p-2">
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<Archive className="h-4 w-4" />
<span className="sr-only">Archive</span>
</Button>
</TooltipTrigger>
<TooltipContent>Archive</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<ArchiveX className="h-4 w-4" />
<span className="sr-only">Move to junk</span>
</Button>
</TooltipTrigger>
<TooltipContent>Move to junk</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<Trash2 className="h-4 w-4" />
<span className="sr-only">Move to trash</span>
</Button>
</TooltipTrigger>
<TooltipContent>Move to trash</TooltipContent>
</Tooltip>
<Separator orientation="vertical" className="mx-1 h-6" />
<Tooltip>
<Popover>
<PopoverTrigger asChild>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<Clock className="h-4 w-4" />
<span className="sr-only">Snooze</span>
</Button>
</TooltipTrigger>
</PopoverTrigger>
<PopoverContent className="flex w-[535px] p-0">
<div className="flex flex-col gap-2 border-r px-2 py-4">
<div className="px-4 text-sm font-medium">Snooze until</div>
<div className="grid min-w-[250px] gap-1">
<Button
variant="ghost"
className="justify-start font-normal"
>
Later today{" "}
<span className="ml-auto text-muted-foreground">
{format(addHours(today, 4), "E, h:m b")}
</span>
</Button>
<Button
variant="ghost"
className="justify-start font-normal"
>
Tomorrow
<span className="ml-auto text-muted-foreground">
{format(addDays(today, 1), "E, h:m b")}
</span>
</Button>
<Button
variant="ghost"
className="justify-start font-normal"
>
This weekend
<span className="ml-auto text-muted-foreground">
{format(nextSaturday(today), "E, h:m b")}
</span>
</Button>
<Button
variant="ghost"
className="justify-start font-normal"
>
Next week
<span className="ml-auto text-muted-foreground">
{format(addDays(today, 7), "E, h:m b")}
</span>
</Button>
</div>
</div>
<div className="p-2">
<Calendar />
</div>
</PopoverContent>
</Popover>
<TooltipContent>Snooze</TooltipContent>
</Tooltip>
</div>
<div className="ml-auto flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<Reply className="h-4 w-4" />
<span className="sr-only">Reply</span>
</Button>
</TooltipTrigger>
<TooltipContent>Reply</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<ReplyAll className="h-4 w-4" />
<span className="sr-only">Reply all</span>
</Button>
</TooltipTrigger>
<TooltipContent>Reply all</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<Forward className="h-4 w-4" />
<span className="sr-only">Forward</span>
</Button>
</TooltipTrigger>
<TooltipContent>Forward</TooltipContent>
</Tooltip>
</div>
<Separator orientation="vertical" className="mx-2 h-6" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" disabled={!mail}>
<MoreVertical className="h-4 w-4" />
<span className="sr-only">More</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Mark as unread</DropdownMenuItem>
<DropdownMenuItem>Star thread</DropdownMenuItem>
<DropdownMenuItem>Add label</DropdownMenuItem>
<DropdownMenuItem>Mute thread</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<Separator />
{mail ? (
<div className="flex flex-1 flex-col">
<div className="flex items-start p-4">
<div className="flex items-start gap-4 text-sm">
<Avatar>
<AvatarImage alt={mail.name} />
<AvatarFallback>
{mail.name
.split(" ")
.map((chunk) => chunk[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="grid gap-1">
<div className="font-semibold">{mail.name}</div>
<div className="line-clamp-1 text-xs">{mail.subject}</div>
<div className="line-clamp-1 text-xs">
<span className="font-medium">Reply-To:</span> {mail.email}
</div>
</div>
</div>
{mail.date && (
<div className="ml-auto text-xs text-muted-foreground">
{format(new Date(mail.date), "PPpp")}
</div>
)}
</div>
<Separator />
<div className="flex-1 whitespace-pre-wrap p-4 text-sm">
{mail.text}
</div>
<Separator className="mt-auto" />
<div className="p-4">
<form>
<div className="grid gap-4">
<Textarea
className="p-4"
placeholder={`Reply ${mail.name}...`}
/>
<div className="flex items-center">
<Label
htmlFor="mute"
className="flex items-center gap-2 text-xs font-normal"
>
<Switch id="mute" aria-label="Mute thread" /> Mute this
thread
</Label>
<Button size="sm" className="ml-auto">
Send
</Button>
</div>
</div>
</form>
</div>
</div>
) : (
<div className="p-8 text-center text-muted-foreground">
No message selected
</div>
)}
</div>
)
}

View File

@@ -0,0 +1,89 @@
import { ComponentProps } from "react"
import formatDistanceToNow from "date-fns/formatDistanceToNow"
import { cn } from "@/lib/utils"
import { Badge } from "@/registry/new-york/ui/badge"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
import { Separator } from "@/registry/new-york/ui/separator"
import { Mail } from "@/app/examples/mail/data"
import { useMail } from "@/app/examples/mail/use-mail"
interface MailListProps {
items: Mail[]
}
export function MailList({ items }: MailListProps) {
const [mail, setMail] = useMail()
return (
<ScrollArea className="h-screen">
<div className="flex flex-col gap-2 p-4 pt-0">
{items.map((item) => (
<button
key={item.id}
className={cn(
"flex flex-col items-start gap-2 rounded-lg border p-3 text-left text-sm transition-all hover:bg-accent",
mail.selected === item.id && "bg-muted"
)}
onClick={() =>
setMail({
...mail,
selected: item.id,
})
}
>
<div className="flex w-full flex-col gap-1">
<div className="flex items-center">
<div className="flex items-center gap-2">
<div className="font-semibold">{item.name}</div>
{!item.read && (
<span className="flex h-2 w-2 rounded-full bg-blue-600" />
)}
</div>
<div
className={cn(
"ml-auto text-xs",
mail.selected === item.id
? "text-foreground"
: "text-muted-foreground"
)}
>
{formatDistanceToNow(new Date(item.date), {
addSuffix: true,
})}
</div>
</div>
<div className="text-xs font-medium">{item.subject}</div>
</div>
<div className="line-clamp-2 text-xs text-muted-foreground">
{item.text.substring(0, 300)}
</div>
{item.labels.length ? (
<div className="flex items-center gap-2">
{item.labels.map((label) => (
<Badge key={label} variant={getBadgeVariantFromLabel(label)}>
{label}
</Badge>
))}
</div>
) : null}
</button>
))}
</div>
</ScrollArea>
)
}
function getBadgeVariantFromLabel(
label: string
): ComponentProps<typeof Badge>["variant"] {
if (["work"].includes(label.toLowerCase())) {
return "default"
}
if (["personal"].includes(label.toLowerCase())) {
return "outline"
}
return "secondary"
}

View File

@@ -0,0 +1,219 @@
"use client"
import * as React from "react"
import {
AlertCircle,
Archive,
ArchiveX,
File,
Inbox,
MessagesSquare,
PenBox,
Search,
Send,
ShoppingCart,
Trash2,
Users2,
} from "lucide-react"
import { AccountSwitcher } from "@/app/examples/mail/components/account-switcher"
import { MailDisplay } from "@/app/examples/mail/components/mail-display"
import { MailList } from "@/app/examples/mail/components/mail-list"
import { Nav } from "@/app/examples/mail/components/nav"
import { Mail } from "@/app/examples/mail/data"
import { useMail } from "@/app/examples/mail/use-mail"
import { cn } from "@/lib/utils"
import { Separator } from "@/registry/new-york/ui/separator"
import { Input } from "@/registry/new-york/ui/input"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
import { TooltipProvider } from "@/registry/new-york/ui/tooltip"
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/registry/new-york/ui/resizable"
interface MailProps {
accounts: {
label: string
email: string
icon: React.ReactNode
}[]
mails: Mail[]
defaultLayout: number[] | undefined
defaultCollapsed?: boolean
navCollapsedSize: number
}
export function Mail({
accounts,
mails,
defaultLayout = [265, 440, 655],
defaultCollapsed = false,
navCollapsedSize,
}: MailProps) {
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)
const [mail] = useMail()
return (
<TooltipProvider delayDuration={0}>
<ResizablePanelGroup
direction="horizontal"
onLayout={(sizes: number[]) => {
document.cookie = `react-resizable-panels:layout=${JSON.stringify(
sizes
)}`
}}
className="h-full max-h-[800px] items-stretch"
>
<ResizablePanel
defaultSize={defaultLayout[0]}
collapsedSize={navCollapsedSize}
collapsible={true}
minSize={15}
maxSize={20}
onCollapse={(collapsed) => {
setIsCollapsed(collapsed)
document.cookie = `react-resizable-panels:collapsed=${JSON.stringify(
collapsed
)}`
}}
className={cn(isCollapsed && "transition-all duration-300 ease-in-out")}
>
<div className="flex items-center p-2">
<div
className="w-full"
// className={cn("w-full flex-1", isCollapsed ? "w-full" : "w-[80%]")}
>
<AccountSwitcher isCollapsed={isCollapsed} accounts={accounts} />
</div>
</div>
<Separator />
<div className={cn(isCollapsed ? "block" : "hidden")}>
<Nav
isCollapsed={isCollapsed}
links={[
{
title: "Compose",
label: "",
icon: PenBox,
variant: "ghost",
},
]}
/>
</div>
<Nav
isCollapsed={isCollapsed}
links={[
{
title: "Inbox",
label: "128",
icon: Inbox,
variant: "default",
},
{
title: "Drafts",
label: "9",
icon: File,
variant: "ghost",
},
{
title: "Sent",
label: "",
icon: Send,
variant: "ghost",
},
{
title: "Junk",
label: "23",
icon: ArchiveX,
variant: "ghost",
},
{
title: "Trash",
label: "",
icon: Trash2,
variant: "ghost",
},
{
title: "Archive",
label: "",
icon: Archive,
variant: "ghost",
},
]}
/>
<Separator />
<Nav
isCollapsed={isCollapsed}
links={[
{
title: "Social",
label: "972",
icon: Users2,
variant: "ghost",
},
{
title: "Updates",
label: "342",
icon: AlertCircle,
variant: "ghost",
},
{
title: "Forums",
label: "128",
icon: MessagesSquare,
variant: "ghost",
},
{
title: "Shopping",
label: "8",
icon: ShoppingCart,
variant: "ghost",
},
{
title: "Promotions",
label: "21",
icon: Archive,
variant: "ghost",
},
]}
/>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={defaultLayout[1]} minSize={30}>
<Tabs defaultValue="all">
<div className="flex items-center px-4 py-2">
<h1 className="text-xl font-bold">Inbox</h1>
<TabsList className="ml-auto">
<TabsTrigger value="all" className="text-zinc-600 dark:text-zinc-200">All mail</TabsTrigger>
<TabsTrigger value="unread" className="text-zinc-600 dark:text-zinc-200">Unread</TabsTrigger>
</TabsList>
</div>
<Separator />
<div className="bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<form>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input placeholder="Search" className="pl-8" />
</div>
</form>
</div>
<TabsContent value="all" className="m-0">
<MailList items={mails} />
</TabsContent>
<TabsContent value="unread" className="m-0">
<MailList items={mails.filter((item) => !item.read)} />
</TabsContent>
</Tabs>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={defaultLayout[2]}>
<MailDisplay
mail={mails.find((item) => item.id === mail.selected) || null}
/>
</ResizablePanel>
</ResizablePanelGroup>
</TooltipProvider>
)
}

View File

@@ -0,0 +1,86 @@
"use client"
import Link from "next/link"
import { LucideIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/registry/default/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/registry/new-york/ui/tooltip"
interface NavProps {
isCollapsed: boolean
links: {
title: string
label?: string
icon: LucideIcon
variant: "default" | "ghost"
}[]
}
export function Nav({ links, isCollapsed }: NavProps) {
return (
<div
data-collapsed={isCollapsed}
className="group flex flex-col gap-4 py-2 data-[collapsed=true]:py-2"
>
<nav className="grid gap-1 px-2 group-[[data-collapsed=true]]:px-2">
{links.map((link, index) =>
isCollapsed ? (
<Tooltip key={index} delayDuration={0}>
<TooltipTrigger asChild>
<Link
href="#"
className={cn(
buttonVariants({ variant: link.variant, size: "icon" }),
"h-8 w-8",
link.variant === "default" &&
"dark:bg-muted dark:text-muted-foreground"
)}
>
<link.icon className="h-4 w-4" />
<span className="sr-only">{link.title}</span>
</Link>
</TooltipTrigger>
<TooltipContent side="right" className="flex items-center gap-4">
{link.title}
{link.label && (
<span className="ml-auto text-muted-foreground">
{link.label}
</span>
)}
</TooltipContent>
</Tooltip>
) : (
<Link
key={index}
href="#"
className={cn(
buttonVariants({ variant: link.variant, size: "sm" }),
link.variant === "default" && "dark:bg-muted dark:text-white",
"justify-start"
)}
>
<link.icon className="mr-2 h-4 w-4" />
{link.title}
{link.label && (
<span
className={cn(
"ml-auto",
link.variant === "default" &&
"text-background dark:text-white"
)}
>
{link.label}
</span>
)}
</Link>
)
)}
</nav>
</div>
)
}

View File

@@ -0,0 +1,300 @@
export const mails = [
{
id: "6c84fb90-12c4-11e1-840d-7b25c5ee775a",
name: "William Smith",
email: "williamsmith@example.com",
subject: "Meeting Tomorrow",
text: "Hi, let's have a meeting tomorrow to discuss the project. I've been reviewing the project details and have some ideas I'd like to share. It's crucial that we align on our next steps to ensure the project's success.\n\nPlease come prepared with any questions or insights you may have. Looking forward to our meeting!\n\nBest regards, William",
date: "2023-10-22T09:00:00",
read: true,
labels: ["meeting", "work", "important"],
},
{
id: "110e8400-e29b-11d4-a716-446655440000",
name: "Alice Smith",
email: "alicesmith@example.com",
subject: "Re: Project Update",
text: "Thank you for the project update. It looks great! I've gone through the report, and the progress is impressive. The team has done a fantastic job, and I appreciate the hard work everyone has put in.\n\nI have a few minor suggestions that I'll include in the attached document.\n\nLet's discuss these during our next meeting. Keep up the excellent work!\n\nBest regards, Alice",
date: "2023-10-22T10:30:00",
read: true,
labels: ["work", "important"],
},
{
id: "3e7c3f6d-bdf5-46ae-8d90-171300f27ae2",
name: "Bob Johnson",
email: "bobjohnson@example.com",
subject: "Weekend Plans",
text: "Any plans for the weekend? I was thinking of going hiking in the nearby mountains. It's been a while since we had some outdoor fun.\n\nIf you're interested, let me know, and we can plan the details. It'll be a great way to unwind and enjoy nature.\n\nLooking forward to your response!\n\nBest, Bob",
date: "2023-04-10T11:45:00",
read: true,
labels: ["personal"],
},
{
id: "61c35085-72d7-42b4-8d62-738f700d4b92",
name: "Emily Davis",
email: "emilydavis@example.com",
subject: "Re: Question about Budget",
text: "I have a question about the budget for the upcoming project. It seems like there's a discrepancy in the allocation of resources.\n\nI've reviewed the budget report and identified a few areas where we might be able to optimize our spending without compromising the project's quality.\n\nI've attached a detailed analysis for your reference. Let's discuss this further in our next meeting.\n\nThanks, Emily",
date: "2023-03-25T13:15:00",
read: false,
labels: ["work", "budget"],
},
{
id: "8f7b5db9-d935-4e42-8e05-1f1d0a3dfb97",
name: "Michael Wilson",
email: "michaelwilson@example.com",
subject: "Important Announcement",
text: "I have an important announcement to make during our team meeting. It pertains to a strategic shift in our approach to the upcoming product launch. We've received valuable feedback from our beta testers, and I believe it's time to make some adjustments to better meet our customers' needs.\n\nThis change is crucial to our success, and I look forward to discussing it with the team. Please be prepared to share your insights during the meeting.\n\nRegards, Michael",
date: "2023-03-10T15:00:00",
read: false,
labels: ["meeting", "work", "important"],
},
{
id: "1f0f2c02-e299-40de-9b1d-86ef9e42126b",
name: "Sarah Brown",
email: "sarahbrown@example.com",
subject: "Re: Feedback on Proposal",
text: "Thank you for your feedback on the proposal. It looks great! I'm pleased to hear that you found it promising. The team worked diligently to address all the key points you raised, and I believe we now have a strong foundation for the project.\n\nI've attached the revised proposal for your review.\n\nPlease let me know if you have any further comments or suggestions. Looking forward to your response.\n\nBest regards, Sarah",
date: "2023-02-15T16:30:00",
read: true,
labels: ["work"],
},
{
id: "17c0a96d-4415-42b1-8b4f-764efab57f66",
name: "David Lee",
email: "davidlee@example.com",
subject: "New Project Idea",
text: "I have an exciting new project idea to discuss with you. It involves expanding our services to target a niche market that has shown considerable growth in recent months.\n\nI've prepared a detailed proposal outlining the potential benefits and the strategy for execution.\n\nThis project has the potential to significantly impact our business positively. Let's set up a meeting to dive into the details and determine if it aligns with our current goals.\n\nBest regards, David",
date: "2023-01-28T17:45:00",
read: false,
labels: ["meeting", "work", "important"],
},
{
id: "2f0130cb-39fc-44c4-bb3c-0a4337edaaab",
name: "Olivia Wilson",
email: "oliviawilson@example.com",
subject: "Vacation Plans",
text: "Let's plan our vacation for next month. What do you think? I've been thinking of visiting a tropical paradise, and I've put together some destination options.\n\nI believe it's time for us to unwind and recharge. Please take a look at the options and let me know your preferences.\n\nWe can start making arrangements to ensure a smooth and enjoyable trip.\n\nExcited to hear your thoughts! Olivia",
date: "2022-12-20T18:30:00",
read: true,
labels: ["personal"],
},
{
id: "de305d54-75b4-431b-adb2-eb6b9e546014",
name: "James Martin",
email: "jamesmartin@example.com",
subject: "Re: Conference Registration",
text: "I've completed the registration for the conference next month. The event promises to be a great networking opportunity, and I'm looking forward to attending the various sessions and connecting with industry experts.\n\nI've also attached the conference schedule for your reference.\n\nIf there are any specific topics or sessions you'd like me to explore, please let me know. It's an exciting event, and I'll make the most of it.\n\nBest regards, James",
date: "2022-11-30T19:15:00",
read: true,
labels: ["work", "conference"],
},
{
id: "7dd90c63-00f6-40f3-bd87-5060a24e8ee7",
name: "Sophia White",
email: "sophiawhite@example.com",
subject: "Team Dinner",
text: "Let's have a team dinner next week to celebrate our success. We've achieved some significant milestones, and it's time to acknowledge our hard work and dedication.\n\nI've made reservations at a lovely restaurant, and I'm sure it'll be an enjoyable evening.\n\nPlease confirm your availability and any dietary preferences. Looking forward to a fun and memorable dinner with the team!\n\nBest, Sophia",
date: "2022-11-05T20:30:00",
read: false,
labels: ["meeting", "work"],
},
{
id: "99a88f78-3eb4-4d87-87b7-7b15a49a0a05",
name: "Daniel Johnson",
email: "danieljohnson@example.com",
subject: "Feedback Request",
text: "I'd like your feedback on the latest project deliverables. We've made significant progress, and I value your input to ensure we're on the right track.\n\nI've attached the deliverables for your review, and I'm particularly interested in any areas where you think we can further enhance the quality or efficiency.\n\nYour feedback is invaluable, and I appreciate your time and expertise. Let's work together to make this project a success.\n\nRegards, Daniel",
date: "2022-10-22T09:30:00",
read: false,
labels: ["work"],
},
{
id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
name: "Ava Taylor",
email: "avataylor@example.com",
subject: "Re: Meeting Agenda",
text: "Here's the agenda for our meeting next week. I've included all the topics we need to cover, as well as time allocations for each.\n\nIf you have any additional items to discuss or any specific points to address, please let me know, and we can integrate them into the agenda.\n\nIt's essential that our meeting is productive and addresses all relevant matters.\n\nLooking forward to our meeting! Ava",
date: "2022-10-10T10:45:00",
read: true,
labels: ["meeting", "work"],
},
{
id: "c1a0ecb4-2540-49c5-86f8-21e5ce79e4e6",
name: "William Anderson",
email: "williamanderson@example.com",
subject: "Product Launch Update",
text: "The product launch is on track. I'll provide an update during our call. We've made substantial progress in the development and marketing of our new product.\n\nI'm excited to share the latest updates with you during our upcoming call. It's crucial that we coordinate our efforts to ensure a successful launch. Please come prepared with any questions or insights you may have.\n\nLet's make this product launch a resounding success!\n\nBest regards, William",
date: "2022-09-20T12:00:00",
read: false,
labels: ["meeting", "work", "important"],
},
{
id: "ba54eefd-4097-4949-99f2-2a9ae4d1a836",
name: "Mia Harris",
email: "miaharris@example.com",
subject: "Re: Travel Itinerary",
text: "I've received the travel itinerary. It looks great! Thank you for your prompt assistance in arranging the details. I've reviewed the schedule and the accommodations, and everything seems to be in order. I'm looking forward to the trip, and I'm confident it'll be a smooth and enjoyable experience.\n\nIf there are any specific activities or attractions you recommend at our destination, please feel free to share your suggestions.\n\nExcited for the trip! Mia",
date: "2022-09-10T13:15:00",
read: true,
labels: ["personal", "travel"],
},
{
id: "df09b6ed-28bd-4e0c-85a9-9320ec5179aa",
name: "Ethan Clark",
email: "ethanclark@example.com",
subject: "Team Building Event",
text: "Let's plan a team-building event for our department. Team cohesion and morale are vital to our success, and I believe a well-organized team-building event can be incredibly beneficial. I've done some research and have a few ideas for fun and engaging activities.\n\nPlease let me know your thoughts and availability. We want this event to be both enjoyable and productive.\n\nTogether, we'll strengthen our team and boost our performance.\n\nRegards, Ethan",
date: "2022-08-25T15:30:00",
read: false,
labels: ["meeting", "work"],
},
{
id: "d67c1842-7f8b-4b4b-9be1-1b3b1ab4611d",
name: "Chloe Hall",
email: "chloehall@example.com",
subject: "Re: Budget Approval",
text: "The budget has been approved. We can proceed with the project. I'm delighted to inform you that our budget proposal has received the green light from the finance department. This is a significant milestone, and it means we can move forward with the project as planned.\n\nI've attached the finalized budget for your reference. Let's ensure that we stay on track and deliver the project on time and within budget.\n\nIt's an exciting time for us! Chloe",
date: "2022-08-10T16:45:00",
read: true,
labels: ["work", "budget"],
},
{
id: "6c9a7f94-8329-4d70-95d3-51f68c186ae1",
name: "Samuel Turner",
email: "samuelturner@example.com",
subject: "Weekend Hike",
text: "Who's up for a weekend hike in the mountains? I've been craving some outdoor adventure, and a hike in the mountains sounds like the perfect escape. If you're up for the challenge, we can explore some scenic trails and enjoy the beauty of nature.\n\nI've done some research and have a few routes in mind.\n\nLet me know if you're interested, and we can plan the details.\n\nIt's sure to be a memorable experience! Samuel",
date: "2022-07-28T17:30:00",
read: false,
labels: ["personal"],
},
]
export type Mail = (typeof mails)[number]
export const accounts = [
{
label: "Alicia Koch",
email: "alicia@example.com",
icon: (
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Vercel</title>
<path d="M24 22.525H0l12-21.05 12 21.05z" fill="currentColor" />
</svg>
),
},
{
label: "Alicia Koch",
email: "alicia@gmail.com",
icon: (
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>Gmail</title>
<path
d="M24 5.457v13.909c0 .904-.732 1.636-1.636 1.636h-3.819V11.73L12 16.64l-6.545-4.91v9.273H1.636A1.636 1.636 0 0 1 0 19.366V5.457c0-2.023 2.309-3.178 3.927-1.964L5.455 4.64 12 9.548l6.545-4.91 1.528-1.145C21.69 2.28 24 3.434 24 5.457z"
fill="currentColor"
/>
</svg>
),
},
{
label: "Alicia Koch",
email: "alicia@me.com",
icon: (
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>iCloud</title>
<path
d="M13.762 4.29a6.51 6.51 0 0 0-5.669 3.332 3.571 3.571 0 0 0-1.558-.36 3.571 3.571 0 0 0-3.516 3A4.918 4.918 0 0 0 0 14.796a4.918 4.918 0 0 0 4.92 4.914 4.93 4.93 0 0 0 .617-.045h14.42c2.305-.272 4.041-2.258 4.043-4.589v-.009a4.594 4.594 0 0 0-3.727-4.508 6.51 6.51 0 0 0-6.511-6.27z"
fill="currentColor"
/>
</svg>
),
},
]
export type Account = (typeof accounts)[number]
export const contacts = [
{
name: "Emma Johnson",
email: "emma.johnson@example.com",
},
{
name: "Liam Wilson",
email: "liam.wilson@example.com",
},
{
name: "Olivia Davis",
email: "olivia.davis@example.com",
},
{
name: "Noah Martinez",
email: "noah.martinez@example.com",
},
{
name: "Ava Taylor",
email: "ava.taylor@example.com",
},
{
name: "Lucas Brown",
email: "lucas.brown@example.com",
},
{
name: "Sophia Smith",
email: "sophia.smith@example.com",
},
{
name: "Ethan Wilson",
email: "ethan.wilson@example.com",
},
{
name: "Isabella Jackson",
email: "isabella.jackson@example.com",
},
{
name: "Mia Clark",
email: "mia.clark@example.com",
},
{
name: "Mason Lee",
email: "mason.lee@example.com",
},
{
name: "Layla Harris",
email: "layla.harris@example.com",
},
{
name: "William Anderson",
email: "william.anderson@example.com",
},
{
name: "Ella White",
email: "ella.white@example.com",
},
{
name: "James Thomas",
email: "james.thomas@example.com",
},
{
name: "Harper Lewis",
email: "harper.lewis@example.com",
},
{
name: "Benjamin Moore",
email: "benjamin.moore@example.com",
},
{
name: "Aria Hall",
email: "aria.hall@example.com",
},
{
name: "Henry Turner",
email: "henry.turner@example.com",
},
{
name: "Scarlett Adams",
email: "scarlett.adams@example.com",
},
]
export type Contact = (typeof contacts)[number]

View File

@@ -0,0 +1,43 @@
import { cookies } from "next/headers"
import Image from "next/image"
import { Mail } from "@/app/examples/mail/components/mail"
import { accounts, mails } from "@/app/examples/mail/data"
export default function MailPage() {
const layout = cookies().get("react-resizable-panels:layout")
const collapsed = cookies().get("react-resizable-panels:collapsed")
const defaultLayout = layout ? JSON.parse(layout.value) : undefined
const defaultCollapsed = collapsed ? JSON.parse(collapsed.value) : undefined
return (
<>
<div className="md:hidden">
<Image
src="/examples/mail-dark.png"
width={1280}
height={727}
alt="Mail"
className="hidden dark:block"
/>
<Image
src="/examples/mail-light.png"
width={1280}
height={727}
alt="Mail"
className="block dark:hidden"
/>
</div>
<div className="hidden flex-col md:flex">
<Mail
accounts={accounts}
mails={mails}
defaultLayout={defaultLayout}
defaultCollapsed={defaultCollapsed}
navCollapsedSize={4}
/>
</div>
</>
)
}

View File

@@ -0,0 +1,15 @@
import { atom, useAtom } from "jotai"
import { Mail, mails } from "@/app/examples/mail/data"
type Config = {
selected: Mail["id"] | null
}
const configAtom = atom<Config>({
selected: mails[0].id,
})
export function useMail() {
return useAtom(configAtom)
}

View File

@@ -1,5 +1,5 @@
import "@/styles/globals.css"
import { Metadata } from "next"
import { Metadata, Viewport } from "next"
import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
@@ -11,6 +11,7 @@ import { SiteHeader } from "@/components/site-header"
import { TailwindIndicator } from "@/components/tailwind-indicator"
import { ThemeSwitcher } from "@/components/theme-switcher"
import { Toaster as DefaultToaster } from "@/registry/default/ui/toaster"
import { Toaster as NewYorkSonner } from "@/registry/new-york/ui/sonner"
import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"
export const metadata: Metadata = {
@@ -18,6 +19,7 @@ export const metadata: Metadata = {
default: siteConfig.name,
template: `%s - ${siteConfig.name}`,
},
metadataBase: new URL(siteConfig.url),
description: siteConfig.description,
keywords: [
"Next.js",
@@ -33,10 +35,6 @@ export const metadata: Metadata = {
},
],
creator: "shadcn",
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
openGraph: {
type: "website",
locale: "en_US",
@@ -68,6 +66,13 @@ export const metadata: Metadata = {
manifest: `${siteConfig.url}/site.webmanifest`,
}
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
}
interface RootLayoutProps {
children: React.ReactNode
}
@@ -80,7 +85,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
fontSans.className
)}
>
<ThemeProvider
@@ -89,17 +94,20 @@ export default function RootLayout({ children }: RootLayoutProps) {
enableSystem
disableTransitionOnChange
>
<div className="relative flex min-h-screen flex-col">
<SiteHeader />
<div className="flex-1">{children}</div>
<SiteFooter />
<div vaul-drawer-wrapper="">
<div className="relative flex min-h-screen flex-col bg-background">
<SiteHeader />
<main className="flex-1">{children}</main>
<SiteFooter />
</div>
</div>
<TailwindIndicator />
<ThemeSwitcher />
<Analytics />
<NewYorkToaster />
<DefaultToaster />
<NewYorkSonner />
</ThemeProvider>
<ThemeSwitcher />
<Analytics />
<NewYorkToaster />
<DefaultToaster />
</body>
</html>
</>

View File

@@ -1,41 +1,31 @@
import Image from "next/image"
import Link from "next/link"
import { ArrowRightIcon } from "@radix-ui/react-icons"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Announcement } from "@/components/announcement"
import { ExamplesNav } from "@/components/examples-nav"
import { Icons } from "@/components/icons"
import {
PageActions,
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
import DashboardPage from "@/app/examples/dashboard/page"
import MailPage from "@/app/examples/mail/page"
export default function IndexPage() {
return (
<div className="container relative">
<PageHeader className="pb-8">
<Link
href="/docs/changelog"
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="sm:hidden">Style, a new CLI and more.</span>
<span className="hidden sm:inline">
Introducing Style, a new CLI and more.
</span>
<ArrowRightIcon className="ml-1 h-4 w-4" />
</Link>
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
<PageHeader>
<Announcement />
<PageHeaderHeading>Build your component library</PageHeaderHeading>
<PageHeaderDescription>
Beautifully designed components that you can copy and paste into your
apps. Accessible. Customizable. Open Source.
</PageHeaderDescription>
<div className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<PageActions>
<Link href="/docs" className={cn(buttonVariants())}>
Get Started
</Link>
@@ -48,28 +38,28 @@ export default function IndexPage() {
<Icons.gitHub className="mr-2 h-4 w-4" />
GitHub
</Link>
</div>
</PageActions>
</PageHeader>
<ExamplesNav className="[&>a:first-child]:text-primary" />
<section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
<section className="overflow-hidden rounded-lg border bg-background shadow-md md:hidden md:shadow-xl">
<Image
src="/examples/dashboard-light.png"
src="/examples/mail-dark.png"
width={1280}
height={866}
alt="Dashboard"
className="block dark:hidden"
height={727}
alt="Mail"
className="hidden dark:block"
/>
<Image
src="/examples/dashboard-dark.png"
src="/examples/mail-light.png"
width={1280}
height={866}
alt="Dashboard"
className="hidden dark:block"
height={727}
alt="Mail"
className="block dark:hidden"
/>
</section>
<section className="hidden md:block">
<div className="overflow-hidden rounded-lg border bg-background shadow">
<DashboardPage />
<div className="overflow-hidden rounded-lg border bg-background shadow-lg">
<MailPage />
</div>
</section>
</div>

View File

@@ -1,7 +1,9 @@
import { Metadata } from "next"
import "public/registry/themes.css"
import { Announcement } from "@/components/announcement"
import {
PageActions,
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
@@ -22,15 +24,21 @@ export default function ThemesPage() {
defaultTheme="zinc"
className="relative flex flex-col items-start md:flex-row md:items-center"
>
<PageHeader className="relative pb-4 md:pb-8 lg:pb-12">
<PageHeaderHeading>Make it yours.</PageHeaderHeading>
<PageHeader>
<Announcement />
<PageHeaderHeading className="hidden md:block">
Add colors. Make it yours.
</PageHeaderHeading>
<PageHeaderHeading className="md:hidden">
Make it yours
</PageHeaderHeading>
<PageHeaderDescription>
Hand-picked themes that you can copy and paste into your apps.
</PageHeaderDescription>
<PageActions>
<ThemeCustomizer />
</PageActions>
</PageHeader>
<div className="px-4 pb-8 md:ml-auto md:pb-0">
<ThemeCustomizer />
</div>
</ThemeWrapper>
<ThemesTabs />
</div>

View File

@@ -0,0 +1,20 @@
import Link from "next/link"
import { ArrowRightIcon } from "@radix-ui/react-icons"
import { Separator } from "@/registry/new-york/ui/separator"
export function Announcement() {
return (
<Link
href="/docs/changelog"
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="sm:hidden">New components and more.</span>
<span className="hidden sm:inline">
New components, cli updates and more.
</span>
<ArrowRightIcon className="ml-1 h-4 w-4" />
</Link>
)
}

View File

@@ -52,14 +52,14 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button
variant="outline"
className={cn(
"relative w-full justify-start text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64"
"relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64"
)}
onClick={() => setOpen(true)}
{...props}
>
<span className="hidden lg:inline-flex">Search documentation...</span>
<span className="inline-flex lg:hidden">Search...</span>
<kbd className="pointer-events-none absolute right-1.5 top-1.5 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<kbd className="pointer-events-none absolute right-[0.3rem] top-[0.3rem] hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span className="text-xs"></span>K
</kbd>
</Button>

View File

@@ -12,7 +12,7 @@ const DrawerContent = forwardRef<
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DrawerPrimitive.Portal>
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-zinc-950/60" />
<DrawerPrimitive.Overlay className="fixed inset-0 z-50 bg-black/80" />
<DrawerPrimitive.Content
ref={ref}
className={cn(

View File

@@ -8,6 +8,11 @@ import { cn } from "@/lib/utils"
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
const examples = [
{
name: "Mail",
href: "/examples/mail",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/mail",
},
{
name: "Dashboard",
href: "/examples/dashboard",
@@ -54,15 +59,16 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
<div className="relative">
<ScrollArea className="max-w-[600px] lg:max-w-none">
<div className={cn("mb-4 flex items-center", className)} {...props}>
{examples.map((example) => (
{examples.map((example, index) => (
<Link
href={example.href}
key={example.href}
className={cn(
"flex items-center px-4",
pathname?.startsWith(example.href)
? "font-bold text-primary"
: "font-medium text-muted-foreground"
"flex h-7 items-center justify-center rounded-full px-4 text-center text-sm transition-colors hover:text-primary",
pathname?.startsWith(example.href) ||
(index === 0 && pathname === "/")
? "bg-muted font-medium text-primary"
: "text-muted-foreground"
)}
>
{example.name}
@@ -71,9 +77,6 @@ export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
</div>
<ScrollBar orientation="horizontal" className="invisible" />
</ScrollArea>
<ExampleCodeLink
pathname={pathname === "/" ? "/examples/dashboard" : pathname}
/>
</div>
)
}

View File

@@ -20,7 +20,7 @@ export function MainNav() {
{siteConfig.name}
</span>
</Link>
<nav className="flex items-center space-x-6 text-sm font-medium">
<nav className="flex items-center gap-6 text-sm">
<Link
href="/docs"
className={cn(
@@ -28,7 +28,7 @@ export function MainNav() {
pathname === "/docs" ? "text-foreground" : "text-foreground/60"
)}
>
Documentation
Docs
</Link>
<Link
href="/docs/components"

View File

@@ -23,7 +23,35 @@ export function MobileNav() {
variant="ghost"
className="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
>
<ViewVerticalIcon className="h-5 w-5" />
<svg
strokeWidth="1.5"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
>
<path
d="M3 5H11"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
<path
d="M3 12H16"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
<path
d="M3 19H21"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
></path>
</svg>
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>

View File

@@ -10,7 +10,7 @@ function PageHeader({
return (
<section
className={cn(
"flex max-w-[980px] flex-col items-start gap-2 px-4 pt-8 md:pt-12",
"mx-auto flex max-w-[980px] flex-col items-center gap-2 py-8 md:py-12 md:pb-8 lg:py-24 lg:pb-20",
className
)}
{...props}
@@ -27,7 +27,7 @@ function PageHeaderHeading({
return (
<h1
className={cn(
"text-3xl font-bold leading-tight tracking-tighter md:text-5xl lg:leading-[1.1]",
"text-center text-3xl font-bold leading-tight tracking-tighter md:text-6xl lg:leading-[1.1]",
className
)}
{...props}
@@ -42,7 +42,7 @@ function PageHeaderDescription({
return (
<Balance
className={cn(
"max-w-[750px] text-lg text-muted-foreground sm:text-xl",
"max-w-[750px] text-center text-lg text-muted-foreground sm:text-xl",
className
)}
{...props}
@@ -50,4 +50,19 @@ function PageHeaderDescription({
)
}
export { PageHeader, PageHeaderHeading, PageHeaderDescription }
function PageActions({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"flex w-full items-center justify-center space-x-4 py-4 md:pb-10",
className
)}
{...props}
/>
)
}
export { PageHeader, PageHeaderHeading, PageHeaderDescription, PageActions }

View File

@@ -4,7 +4,7 @@ export function SiteFooter() {
return (
<footer className="py-6 md:px-8 md:py-0">
<div className="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
<p className="text-center text-sm leading-loose text-muted-foreground md:text-left">
<p className="text-balance text-center text-sm leading-loose text-muted-foreground md:text-left">
Built by{" "}
<a
href={siteConfig.links.twitter}

View File

@@ -11,8 +11,8 @@ import { buttonVariants } from "@/registry/new-york/ui/button"
export function SiteHeader() {
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-14 items-center">
<header className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-14 max-w-screen-2xl items-center">
<MainNav />
<MobileNav />
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end">

View File

@@ -16,7 +16,6 @@ import { useTheme } from "next-themes"
import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { copyToClipboardWithMeta } from "@/components/copy-button"
import { DrawerContent, DrawerTrigger } from "@/components/drawer"
import { ThemeWrapper } from "@/components/theme-wrapper"
import { Button } from "@/registry/new-york/ui/button"
import {
@@ -27,6 +26,11 @@ import {
DialogTitle,
DialogTrigger,
} from "@/registry/new-york/ui/dialog"
import {
Drawer,
DrawerContent,
DrawerTrigger,
} from "@/registry/new-york/ui/drawer"
import { Label } from "@/registry/new-york/ui/label"
import {
Popover,
@@ -37,8 +41,6 @@ import { Skeleton } from "@/registry/new-york/ui/skeleton"
import { Theme, themes } from "@/registry/themes"
import "@/styles/mdx.css"
import { Drawer } from "vaul"
import {
Tooltip,
TooltipContent,
@@ -56,17 +58,17 @@ export function ThemeCustomizer() {
return (
<div className="flex items-center space-x-2">
<Drawer.Root>
<Drawer>
<DrawerTrigger asChild>
<Button variant="outline" className="md:hidden">
<Paintbrush className="mr-2 h-4 w-4" />
Customize
</Button>
</DrawerTrigger>
<DrawerContent className="h-[85%] p-6 pt-10">
<DrawerContent className="p-6 pt-0">
<Customizer />
</DrawerContent>
</Drawer.Root>
</Drawer>
<div className="hidden md:flex">
<div className="mr-2 hidden items-center space-x-0.5 lg:flex">
{mounted ? (
@@ -128,7 +130,7 @@ export function ThemeCustomizer() {
})}
</>
) : (
<div className="mr-1 flex items-center space-x-3">
<div className="mr-1 flex items-center gap-4">
<Skeleton className="h-6 w-6 rounded-full" />
<Skeleton className="h-6 w-6 rounded-full" />
<Skeleton className="h-6 w-6 rounded-full" />
@@ -145,7 +147,7 @@ export function ThemeCustomizer() {
</Button>
</PopoverTrigger>
<PopoverContent
align="end"
align="center"
className="z-40 w-[340px] rounded-[0.5rem] bg-white p-6 dark:bg-zinc-950"
>
<Customizer />
@@ -171,7 +173,7 @@ function Customizer() {
defaultTheme="zinc"
className="flex flex-col space-y-4 md:space-y-6"
>
<div className="flex items-start">
<div className="flex items-start pt-4 md:pt-0">
<div className="space-y-1 pr-2">
<div className="font-semibold leading-none tracking-tight">
Customize
@@ -603,7 +605,7 @@ const BASE_STYLES_WITH_VARIABLES = `
--ring: <%- colors.light["ring"] %>;
--radius: <%- radius %>rem;
}
.dark {
--background: <%- colors.dark["background"] %>;
--foreground: <%- colors.dark["foreground"] %>;

View File

@@ -87,76 +87,6 @@ export const docsConfig: DocsConfig = {
href: "/docs/changelog",
items: [],
},
{
title: "About",
href: "/docs/about",
items: [],
},
],
},
{
title: "Installation",
items: [
{
title: "Next.js",
href: "/docs/installation/next",
items: [],
},
{
title: "Vite",
href: "/docs/installation/vite",
items: [],
},
{
title: "Remix",
href: "/docs/installation/remix",
items: [],
},
{
title: "Gatsby",
href: "/docs/installation/gatsby",
items: [],
},
{
title: "Astro",
href: "/docs/installation/astro",
items: [],
},
{
title: "Laravel",
href: "/docs/installation/laravel",
items: [],
},
{
title: "Manual",
href: "/docs/installation/manual",
items: [],
},
],
},
{
title: "Dark Mode",
items: [
{
title: "Next.js",
href: "/docs/dark-mode/next",
items: [],
},
{
title: "Vite",
href: "/docs/dark-mode/vite",
items: [],
},
{
title: "Astro",
href: "/docs/dark-mode/astro",
items: [],
},
{
title: "Remix",
href: "/docs/dark-mode/remix",
items: [],
},
],
},
{
@@ -207,6 +137,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/card",
items: [],
},
{
title: "Carousel",
href: "/docs/components/carousel",
items: [],
label: "New",
},
{
title: "Checkbox",
href: "/docs/components/checkbox",
@@ -247,6 +183,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/dialog",
items: [],
},
{
title: "Drawer",
href: "/docs/components/drawer",
items: [],
label: "New",
},
{
title: "Dropdown Menu",
href: "/docs/components/dropdown-menu",
@@ -282,6 +224,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/navigation-menu",
items: [],
},
{
title: "Pagination",
href: "/docs/components/pagination",
items: [],
label: "New",
},
{
title: "Popover",
href: "/docs/components/popover",
@@ -297,6 +245,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/radio-group",
items: [],
},
{
title: "Resizable",
href: "/docs/components/resizable",
items: [],
label: "New",
},
{
title: "Scroll Area",
href: "/docs/components/scroll-area",
@@ -327,6 +281,12 @@ export const docsConfig: DocsConfig = {
href: "/docs/components/slider",
items: [],
},
{
title: "Sonner",
href: "/docs/components/sonner",
items: [],
label: "New",
},
{
title: "Switch",
href: "/docs/components/switch",

View File

@@ -3,7 +3,7 @@ export const siteConfig = {
url: "https://ui.shadcn.com",
ogImage: "https://ui.shadcn.com/og.jpg",
description:
"Beautifully designed components built with Radix UI and Tailwind CSS.",
"Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.",
links: {
twitter: "https://twitter.com/shadcn",
github: "https://github.com/shadcn-ui/ui",

View File

@@ -4,6 +4,71 @@ description: Latest updates and announcements.
toc: false
---
## December 2023 - New components, CLI and more
We've added new components to shadcn/ui and made a lot of improvements to the CLI.
Here's a quick overview of what's new:
- [**Carousel**](#carousel) - A carousel component with motion, swipe gestures and keyboard support.
- [**Drawer**](#drawer) - A drawer component that looks amazing on mobile.
- [**Pagination**](#pagination) - A pagination component with page navigation, previous and next buttons.
- [**Resizable**](#resizable) - A resizable component for building resizable panel groups and layouts.
- [**Sonner**](#sonner) - The last toast component you'll ever need.
- [**CLI updates**](#cli-updates) - Support for custom **Tailwind prefix** and `tailwind.config.ts`.
### Carousel
We've added a fully featured carousel component with motion, swipe gestures and keyboard support. Built on top of [Embla Carousel](https://www.embla-carousel.com).
It has support for infinite looping, autoplay, vertical orientation, and more.
<ComponentPreview name="carousel-demo" />
### Drawer
Oh the drawer component 😍. Built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_).
Try opening the following drawer on mobile. It looks amazing!
<ComponentPreview name="drawer-demo" />
### Pagination
We've added a pagination component with page navigation, previous and next buttons. Simple, flexible and works with your framework's `<Link />` component.
<ComponentPreview name="pagination-demo" />
### Resizable
Build resizable panel groups and layouts with this `<Resizable />` component.
<ComponentPreview name="resizable-demo-with-handle" />
`<Resizable />` is built using [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) by [bvaughn](https://github.com/bvaughn). It has support for mouse, touch and keyboard.
### Sonner
Another one by [emilkowalski\_](https://twitter.com/emilkowalski_). The last toast component you'll ever need. Sonner is now availabe in shadcn/ui.
<ComponentPreview name="sonner-demo" />
### CLI updates
This has been one of the most requested features. You can now configure a custom Tailwind prefix and the cli will automatically prefix your utility classes when adding components.
This means you can now easily add shadcn/ui components to existing projects like Docusaurus, Nextra...etc. A drop-in for your existing design system with no conflict. 🔥
```tsx /tw-/
<AlertDialog className="tw-grid tw-gap-4 tw-border tw-bg-background tw-shadow-lg" />
```
It works with `cn`, `cva` and CSS variables.
The cli can now also detect `tailwind.config.ts` and add the TypeScript version of the config for you.
That's it. Happy Holidays.
## July 2023 - JavaScript
This project and the components are written in TypeScript. **We recommend using TypeScript for your project as well**.

View File

@@ -103,6 +103,18 @@ For more information, see the <Link href="/docs/theming">theming docs</Link>.
**This cannot be changed after initialization.** To switch between CSS variables and utility classes, you'll have to delete and re-install your components.
### tailwind.prefix
The prefix to use for your Tailwind CSS utility classes. Components will be added with this prefix.
```json title="components.json"
{
"tailwind": {
"prefix": "tw-"
}
}
```
## rsc
Whether or not to enable support for React Server Components.

View File

@@ -2,8 +2,8 @@
title: Accordion
description: A vertically stacked set of interactive headings that each reveal a section of content.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/accordion
links:
doc: https://www.radix-ui.com/docs/primitives/components/accordion
api: https://www.radix-ui.com/docs/primitives/components/accordion#api-reference
---

View File

@@ -3,8 +3,8 @@ title: Alert Dialog
description: A modal dialog that interrupts the user with important content and expects a response.
featured: true
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/alert-dialog
links:
doc: https://www.radix-ui.com/docs/primitives/components/alert-dialog
api: https://www.radix-ui.com/docs/primitives/components/alert-dialog#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Aspect Ratio
description: Displays content within a desired ratio.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/aspect-ratio
links:
doc: https://www.radix-ui.com/docs/primitives/components/aspect-ratio
api: https://www.radix-ui.com/docs/primitives/components/aspect-ratio#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Avatar
description: An image element with a fallback for representing the user.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/avatar
links:
doc: https://www.radix-ui.com/docs/primitives/components/avatar
api: https://www.radix-ui.com/docs/primitives/components/avatar#api-reference
---

View File

@@ -2,6 +2,8 @@
title: Calendar
description: A date field component that allows users to enter and edit date.
component: true
links:
doc: https://react-day-picker.js.org
---
<ComponentPreview name="calendar-demo" />

View File

@@ -0,0 +1,277 @@
---
title: Carousel
description: A carousel with motion and swipe built using Embla.
component: true
links:
doc: https://www.embla-carousel.com/get-started/react
api: https://www.embla-carousel.com/api
---
<ComponentPreview name="carousel-demo" />
## About
The carousel component is built using the [Embla Carousel](https://www.embla-carousel.com/) library.
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn-ui@latest add carousel
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install embla-carousel-react
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="carousel" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/registry/new-york/ui/carousel"
```
```tsx
<Carousel>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
```
## Examples
### Sizes
To set the size of the items, you can use the `basis` utility class on the `<CarouselItem />`.
<ComponentPreview name="carousel-size" />
```tsx title="Example" showLineNumbers {4-6}
// 33% of the carousel width.
<Carousel>
<CarouselContent>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
<CarouselItem className="basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>
```
```tsx title="Responsive" showLineNumbers {4-6}
// 50% on small screens and 33% on larger screens.
<Carousel>
<CarouselContent>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
<CarouselItem className="md:basis-1/2 lg:basis-1/3">...</CarouselItem>
</CarouselContent>
</Carousel>
```
### Spacing
To set the spacing between the items, we use a `pl-[VALUE]` utility on the `<CarouselItem />` and a negative `-ml-[VALUE]` on the `<CarouselContent />`.
<Callout className="mt-6">
**Why:** I tried to use the `gap` property or a `grid` layout on the `
<CarouselContent />` but it required a lot of math and mental effort to get the
spacing right. I found `pl-[VALUE]` and `-ml-[VALUE]` utilities much easier to
use.
You can always adjust this in your own project if you need to.
</Callout>
<ComponentPreview name="carousel-spacing" />
```tsx title="Example" showLineNumbers /-ml-4/ /pl-4/
<Carousel>
<CarouselContent className="-ml-4">
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
<CarouselItem className="pl-4">...</CarouselItem>
</CarouselContent>
</Carousel>
```
```tsx title="Responsive" showLineNumbers /-ml-2/ /pl-2/ /md:-ml-4/ /md:pl-4/
<Carousel>
<CarouselContent className="-ml-2 md:-ml-4">
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
<CarouselItem className="pl-2 md:pl-4">...</CarouselItem>
</CarouselContent>
</Carousel>
```
### Orientation
Use the `orientation` prop to set the orientation of the carousel.
<ComponentPreview name="carousel-orientation" />
```tsx showLineNumbers /vertical | horizontal/
<Carousel orientation="vertical | horizontal">
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
```
## Options
You can pass options to the carousel using the `opts` prop. See the [Embla Carousel docs](https://www.embla-carousel.com/api/options/) for more information.
```tsx showLineNumbers {2-5}
<Carousel
opts={{
align: "start",
loop: true,
}}
>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
```
## API
Use a state and the `setApi` props to get an instance of the carousel API.
<ComponentPreview name="carousel-api" />
```tsx showLineNumbers {1,4,22}
import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
const [current, setCurrent] = React.useState(0)
const [count, setCount] = React.useState(0)
React.useEffect(() => {
if (!api) {
return
}
setCount(api.scrollSnapList().length)
setCurrent(api.selectedScrollSnap() + 1)
api.on("select", () => {
setCurrent(api.selectedScrollSnap() + 1)
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
)
}
```
## Events
You can listen to events using the api instance from `setApi`.
```tsx showLineNumbers {1,4-14,16}
import { type CarouselApi } from "@/components/ui/carousel"
export function Example() {
const [api, setApi] = React.useState<CarouselApi>()
React.useEffect(() => {
if (!api) {
return
}
api.on("select", () => {
// Do something on select.
})
}, [api])
return (
<Carousel setApi={setApi}>
<CarouselContent>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
<CarouselItem>...</CarouselItem>
</CarouselContent>
</Carousel>
)
}
```
See the [Embla Carousel docs](https://www.embla-carousel.com/api/events/) for more information on using events.
## Plugins
You can use the `plugins` prop to add plugins to the carousel.
```ts showLineNumbers {1,6-10}
import Autoplay from "embla-carousel-autoplay"
export function Example() {
return (
<Carousel
plugins={[
Autoplay({
delay: 2000,
}),
]}
>
// ...
</Carousel>
)
}
```
<ComponentPreview name="carousel-plugin" />
See the [Embla Carousel docs](https://www.embla-carousel.com/api/plugins/) for more information on using plugins.

View File

@@ -2,8 +2,8 @@
title: Checkbox
description: A control that allows the user to toggle between checked and not checked.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/checkbox
links:
doc: https://www.radix-ui.com/docs/primitives/components/checkbox
api: https://www.radix-ui.com/docs/primitives/components/checkbox#api-reference
---

View File

@@ -3,8 +3,8 @@ title: Collapsible
description: An interactive component which expands/collapses a panel.
component: true
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/collapsible
links:
doc: https://www.radix-ui.com/docs/primitives/components/collapsible
api: https://www.radix-ui.com/docs/primitives/components/collapsible#api-reference
---

View File

@@ -122,6 +122,12 @@ export function ComboboxDemo() {
<ComponentPreview name="combobox-dropdown-menu" />
### Responsive
You can create a responsive combobox by using the `<Popover />` on desktop and the `<Drawer />` components on mobile.
<ComponentPreview name="combobox-responsive" />
### Form
<ComponentPreview name="combobox-form" />

View File

@@ -2,6 +2,8 @@
title: Command
description: Fast, composable, unstyled command menu for React.
component: true
links:
doc: https://cmdk.paco.me
---
<ComponentPreview

View File

@@ -2,8 +2,8 @@
title: Context Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/context-menu
links:
doc: https://www.radix-ui.com/docs/primitives/components/context-menu
api: https://www.radix-ui.com/docs/primitives/components/context-menu#api-reference
---

View File

@@ -2,6 +2,8 @@
title: Data Table
description: Powerful table and datagrids built using TanStack Table.
component: true
links:
doc: https://tanstack.com/table/v8/docs/guide/introduction
---
<ComponentPreview name="data-table-demo" />

View File

@@ -3,8 +3,8 @@ title: Dialog
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
featured: true
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/dialog
links:
doc: https://www.radix-ui.com/docs/primitives/components/dialog
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
---

View File

@@ -0,0 +1,91 @@
---
title: Drawer
description: An drawer component for React.
component: true
links:
doc: https://github.com/emilkowalski/vaul
---
<ComponentPreview name="drawer-demo" />
## About
Drawer is built on top of [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski\_](https://twitter.com/emilkowalski_).
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn-ui@latest add drawer
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install vaul
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="drawer" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx showLineNumbers
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
```
```tsx showLineNumbers
<Drawer>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Are you sure absolutely sure?</DrawerTitle>
<DrawerDescription>This action cannot be undone.</DrawerDescription>
</DrawerHeader>
<DrawerFooter>
<Button>Submit</Button>
<DrawerClose>
<Button variant="outline">Cancel</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
```
## Examples
### Responsive Dialog
You can combine the `Dialog` and `Drawer` components to create a responsive dialog. This renders a `Dialog` component on desktop and a `Drawer` on mobile.
<ComponentPreview name="drawer-dialog" />

View File

@@ -3,8 +3,8 @@ title: Dropdown Menu
description: Displays a menu to the user — such as a set of actions or functions — triggered by a button.
featured: true
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/dropdown-menu
links:
doc: https://www.radix-ui.com/docs/primitives/components/dropdown-menu
api: https://www.radix-ui.com/docs/primitives/components/dropdown-menu#api-reference
---

View File

@@ -1,6 +1,8 @@
---
title: React Hook Form
description: Building forms with React Hook Form and Zod.
links:
doc: https://react-hook-form.com
---
Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.

View File

@@ -2,8 +2,8 @@
title: Hover Card
description: For sighted users to preview content available behind a link.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/hover-card
links:
doc: https://www.radix-ui.com/docs/primitives/components/hover-card
api: https://www.radix-ui.com/docs/primitives/components/hover-card#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Label
description: Renders an accessible label associated with controls.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/label
links:
doc: https://www.radix-ui.com/docs/primitives/components/label
api: https://www.radix-ui.com/docs/primitives/components/label#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Menubar
description: A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/menubar
links:
doc: https://www.radix-ui.com/docs/primitives/components/menubar
api: https://www.radix-ui.com/docs/primitives/components/menubar#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Navigation Menu
description: A collection of links for navigating websites.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/navigation-menu
links:
doc: https://www.radix-ui.com/docs/primitives/components/navigation-menu
api: https://www.radix-ui.com/docs/primitives/components/navigation-menu#api-reference
---

View File

@@ -0,0 +1,102 @@
---
title: Pagination
description: Pagination with page navigation, next and previous links.
component: true
---
<ComponentPreview name="pagination-demo" />
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn-ui@latest add pagination
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="pagination" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "@/components/ui/resizable"
```
```tsx
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
```
### Next.js
By default the `<PaginationLink />` component will render an `<a />` tag.
To use the Next.js `<Link />` component, make the following updates to `pagination.tsx`.
```diff showLineNumbers /typeof Link/ {1}
+ import { Link } from "next/link"
- type PaginationLinkProps = ... & React.ComponentProps<"a">
+ type PaginationLinkProps = ... & React.ComponentProps<typeof Link>
const PaginationLink = ({...props }: ) => (
<PaginationItem>
- <a>
+ <Link>
// ...
- </a>
+ </Link>
</PaginationItem>
)
```
<Callout className="mt-6">
**Note:** We are making updates to the cli to automatically do this for you.
</Callout>

View File

@@ -2,8 +2,8 @@
title: Popover
description: Displays rich content in a portal, triggered by a button.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/popover
links:
doc: https://www.radix-ui.com/docs/primitives/components/popover
api: https://www.radix-ui.com/docs/primitives/components/popover#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Progress
description: Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/progress
links:
doc: https://www.radix-ui.com/docs/primitives/components/progress
api: https://www.radix-ui.com/docs/primitives/components/progress#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Radio Group
description: A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/radio-group
links:
doc: https://www.radix-ui.com/docs/primitives/components/radio-group
api: https://www.radix-ui.com/docs/primitives/components/radio-group#api-reference
---

View File

@@ -0,0 +1,120 @@
---
title: Resizable
description: Accessible resizable panel groups and layouts with keyboard support.
component: true
links:
doc: https://github.com/bvaughn/react-resizable-panels
api: https://github.com/bvaughn/react-resizable-panels/tree/main/packages/react-resizable-panels
---
<ComponentPreview name="resizable-demo" />
## About
The `Resizable` component is built on top of [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels) by [bvaughn](https://github.com/bvaughn).
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
```bash
npx shadcn-ui@latest add resizable
```
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install react-resizable-panels
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="resizable" />
<Step>Update the import paths to match your project setup.</Step>
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
```
```tsx
<ResizableGroup direction="horizontal">
<ResizablePanel>One</ResizablePanel>
<ResizableHandle />
<ResizablePanel>Two</ResizablePanel>
</ResizableGroup>
```
## Examples
### Vertical
Use the `direction` prop to set the direction of the resizable panels.
<ComponentPreview name="resizable-vertical" />
```tsx showLineNumbers {9}
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
export default function Example() {
return (
<ResizableGroup direction="vertical">
<ResizablePanel>One</ResizablePanel>
<ResizableHandle />
<ResizablePanel>Two</ResizablePanel>
</ResizableGroup>
)
}
```
### Handle
You can set or hide the handle by using the `withHandle` prop on the `ResizableHandle` component.
<ComponentPreview name="resizable-handle" />
```tsx showLineNumbers {11}
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
export default function Example() {
return (
<ResizableGroup direction="vertical">
<ResizablePanel>One</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>Two</ResizablePanel>
</ResizableGroup>
)
}
```

View File

@@ -2,8 +2,8 @@
title: Scroll-area
description: Augments native scroll functionality for custom, cross-browser styling.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/scroll-area
links:
doc: https://www.radix-ui.com/docs/primitives/components/scroll-area
api: https://www.radix-ui.com/docs/primitives/components/scroll-area#api-reference
---

View File

@@ -3,8 +3,8 @@ title: Select
description: Displays a list of options for the user to pick from—triggered by a button.
component: true
featured: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/select
links:
doc: https://www.radix-ui.com/docs/primitives/components/select
api: https://www.radix-ui.com/docs/primitives/components/select#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Separator
description: Visually or semantically separates content.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/separator
links:
doc: https://www.radix-ui.com/docs/primitives/components/separator
api: https://www.radix-ui.com/docs/primitives/components/separator#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Sheet
description: Extends the Dialog component to display content that complements the main content of the screen.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/dialog
links:
doc: https://www.radix-ui.com/docs/primitives/components/dialog
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Slider
description: An input where the user selects a value from within a given range.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/slider
links:
doc: https://www.radix-ui.com/docs/primitives/components/slider
api: https://www.radix-ui.com/docs/primitives/components/slider#api-reference
---

View File

@@ -0,0 +1,101 @@
---
title: Sonner
description: An opinionated toast component for React.
component: true
links:
doc: https://sonner.emilkowal.ski
---
<ComponentPreview name="sonner-demo" />
## About
Sonner is built and maintained by [emilkowalski\_](https://twitter.com/emilkowalski_).
## Installation
<Tabs defaultValue="cli">
<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">
<Steps>
<Step>Run the following command:</Step>
```bash
npx shadcn-ui@latest add sonner
```
<Step>Add the Toaster component</Step>
```tsx title="app/layout.tsx" {1,9}
import { Toaster } from "@/components/ui/sonner"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<main>{children}</main>
<Toaster />
</body>
</html>
)
}
```
</Steps>
</TabsContent>
<TabsContent value="manual">
<Steps>
<Step>Install the following dependencies:</Step>
```bash
npm install sonner next-themes
```
<Step>Copy and paste the following code into your project.</Step>
<ComponentSource name="sonner" />
<Step>Add the Toaster component</Step>
```tsx title="app/layout.tsx" {1,9}
import { Toaster } from "@/components/ui/sonner"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<main>{children}</main>
<Toaster />
</body>
</html>
)
}
```
</Steps>
</TabsContent>
</Tabs>
## Usage
```tsx
import { toast } from "sonner"
```
```tsx
toast("Event has been created.")
```

View File

@@ -2,8 +2,8 @@
title: Switch
description: A control that allows the user to toggle between checked and not checked.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/switch
links:
doc: https://www.radix-ui.com/docs/primitives/components/switch
api: https://www.radix-ui.com/docs/primitives/components/switch#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Tabs
description: A set of layered sections of content—known as tab panels—that are displayed one at a time.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/tabs
links:
doc: https://www.radix-ui.com/docs/primitives/components/tabs
api: https://www.radix-ui.com/docs/primitives/components/tabs#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Toast
description: A succinct message that is displayed temporarily.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/toast
links:
doc: https://www.radix-ui.com/docs/primitives/components/toast
api: https://www.radix-ui.com/docs/primitives/components/toast#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Toggle Group
description: A set of two-state buttons that can be toggled on or off.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/toggle-group
links:
doc: https://www.radix-ui.com/docs/primitives/components/toggle-group
api: https://www.radix-ui.com/docs/primitives/components/toggle-group#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Toggle
description: A two-state button that can be either on or off.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/toggle
links:
doc: https://www.radix-ui.com/docs/primitives/components/toggle
api: https://www.radix-ui.com/docs/primitives/components/toggle#api-reference
---

View File

@@ -2,8 +2,8 @@
title: Tooltip
description: A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
component: true
radix:
link: https://www.radix-ui.com/docs/primitives/components/tooltip
links:
doc: https://www.radix-ui.com/docs/primitives/components/tooltip
api: https://www.radix-ui.com/docs/primitives/components/tooltip#api-reference
---

View File

@@ -57,7 +57,3 @@ description: Adding dark mode to your site.
<p className="font-medium mt-2">Remix</p>
</LinkedCard>
</div>
## Other frameworks
I'm looking for help writing guides for other frameworks. Help me write guides for Remix, Astro and Vite by [opening an PR](https://github.com/shadcn/ui).

View File

@@ -1,6 +1,6 @@
---
title: Introduction
description: Re-usable components built using Radix UI and Tailwind CSS.
description: Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
---
This is **NOT** a component library. It's a collection of re-usable components that you can copy and paste into your apps.

View File

@@ -1,4 +1,3 @@
import fs from "fs"
import path from "path"
import {
defineDocumentType,
@@ -28,10 +27,10 @@ const computedFields = {
},
}
const RadixProperties = defineNestedType(() => ({
name: "RadixProperties",
const LinksProperties = defineNestedType(() => ({
name: "LinksProperties",
fields: {
link: {
doc: {
type: "string",
},
api: {
@@ -57,9 +56,9 @@ export const Doc = defineDocumentType(() => ({
type: "boolean",
default: true,
},
radix: {
links: {
type: "nested",
of: RadixProperties,
of: LinksProperties,
},
featured: {
type: "boolean",

View File

@@ -11,7 +11,7 @@ type Config = {
}
const configAtom = atomWithStorage<Config>("config", {
style: "default",
style: "new-york",
theme: "zinc",
radius: 0.5,
})

View File

@@ -0,0 +1,19 @@
import * as React from "react"
export function useMediaQuery(query: string) {
const [value, setValue] = React.useState(false)
React.useEffect(() => {
function onChange(event: MediaQueryListEvent) {
setValue(event.matches)
}
const result = matchMedia(query)
result.addEventListener("change", onChange)
setValue(result.matches)
return () => result.removeEventListener("change", onChange)
}, [query])
return value
}

View File

@@ -1,9 +1,13 @@
import { JetBrains_Mono as FontMono, Inter as FontSans } from "next/font/google"
// import { JetBrains_Mono as FontMono, Inter as FontSans } from "next/font/google"
import { JetBrains_Mono as FontMono } from "next/font/google"
// import { GeistMono } from "geist/font/mono"
import { GeistSans } from "geist/font/sans"
export const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
})
// export const fontSans = FontSans({
// subsets: ["latin"],
// variable: "--font-sans",
// })
export const fontSans = GeistSans
export const fontMono = FontMono({
subsets: ["latin"],

View File

@@ -21,7 +21,7 @@ const nextConfig = {
},
{
source: "/examples",
destination: "/examples/dashboard",
destination: "/examples/mail",
permanent: false,
},
{

View File

@@ -55,20 +55,25 @@
"cmdk": "^0.2.0",
"contentlayer": "0.3.4",
"date-fns": "^2.30.0",
"embla-carousel-autoplay": "8.0.0-rc15",
"embla-carousel-react": "8.0.0-rc15",
"geist": "^1.1.0",
"jotai": "^2.1.0",
"lodash.template": "^4.5.0",
"lucide-react": "0.214.0",
"lucide-react": "0.288.0",
"markdown-wasm": "^1.2.0",
"next": "13.4.8",
"next": "14.0.4",
"next-contentlayer": "0.3.4",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-day-picker": "^8.7.1",
"react-dom": "^18.2.0",
"react-hook-form": "^7.44.2",
"react-resizable-panels": "^0.0.55",
"react-wrap-balancer": "^0.4.1",
"recharts": "^2.6.2",
"sharp": "^0.31.3",
"sonner": "^1.2.3",
"tailwind-merge": "^1.12.0",
"vaul": "^0.2.0",
"zod": "^3.21.4"
@@ -92,7 +97,7 @@
"remark-gfm": "^3.0.1",
"rimraf": "^4.1.3",
"shiki": "^0.12.1",
"tailwindcss": "^3.3.2",
"tailwindcss": "^3.4.0",
"typescript": "^4.9.5",
"unist-builder": "3.0.0",
"unist-util-visit": "^4.1.2"

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

View File

@@ -87,6 +87,19 @@
],
"type": "components:ui"
},
{
"name": "carousel",
"dependencies": [
"embla-carousel-react"
],
"registryDependencies": [
"button"
],
"files": [
"ui/carousel.tsx"
],
"type": "components:ui"
},
{
"name": "checkbox",
"dependencies": [
@@ -140,6 +153,17 @@
],
"type": "components:ui"
},
{
"name": "drawer",
"dependencies": [
"vaul",
"@radix-ui/react-dialog"
],
"files": [
"ui/drawer.tsx"
],
"type": "components:ui"
},
{
"name": "dropdown-menu",
"dependencies": [
@@ -215,6 +239,16 @@
],
"type": "components:ui"
},
{
"name": "pagination",
"registryDependencies": [
"button"
],
"files": [
"ui/pagination.tsx"
],
"type": "components:ui"
},
{
"name": "popover",
"dependencies": [
@@ -245,6 +279,16 @@
],
"type": "components:ui"
},
{
"name": "resizable",
"dependencies": [
"react-resizable-panels"
],
"files": [
"ui/resizable.tsx"
],
"type": "components:ui"
},
{
"name": "scroll-area",
"dependencies": [
@@ -302,6 +346,17 @@
],
"type": "components:ui"
},
{
"name": "sonner",
"dependencies": [
"sonner",
"next-themes"
],
"files": [
"ui/sonner.tsx"
],
"type": "components:ui"
},
{
"name": "switch",
"dependencies": [

View File

@@ -9,7 +9,7 @@
"files": [
{
"name": "alert-dialog.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/default/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/default/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n}\n"
}
],
"type": "components:ui"

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
"files": [
{
"name": "dialog.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = DialogPrimitive.Portal\n\nconst DialogClose = DialogPrimitive.Close\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Dialog = DialogPrimitive.Root\n\nconst DialogTrigger = DialogPrimitive.Trigger\n\nconst DialogPortal = DialogPrimitive.Portal\n\nconst DialogClose = DialogPrimitive.Close\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n))\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nDialogHeader.displayName = \"DialogHeader\"\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"
}
],
"type": "components:ui"

View File

@@ -0,0 +1,14 @@
{
"name": "drawer",
"dependencies": [
"vaul",
"@radix-ui/react-dialog"
],
"files": [
{
"name": "drawer.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Drawer = ({\n shouldScaleBackground = true,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (\n <DrawerPrimitive.Root\n shouldScaleBackground={shouldScaleBackground}\n {...props}\n />\n)\nDrawer.displayName = \"Drawer\"\n\nconst DrawerTrigger = DrawerPrimitive.Trigger\n\nconst DrawerPortal = DrawerPrimitive.Portal\n\nconst DrawerClose = DrawerPrimitive.Close\n\nconst DrawerOverlay = React.forwardRef<\n React.ElementRef<typeof DrawerPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DrawerPrimitive.Overlay\n ref={ref}\n className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n {...props}\n />\n))\nDrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName\n\nconst DrawerContent = React.forwardRef<\n React.ElementRef<typeof DrawerPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background\",\n className\n )}\n {...props}\n >\n <div className=\"mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n))\nDrawerContent.displayName = \"DrawerContent\"\n\nconst DrawerHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n {...props}\n />\n)\nDrawerHeader.displayName = \"DrawerHeader\"\n\nconst DrawerFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n)\nDrawerFooter.displayName = \"DrawerFooter\"\n\nconst DrawerTitle = React.forwardRef<\n React.ElementRef<typeof DrawerPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DrawerPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n))\nDrawerTitle.displayName = DrawerPrimitive.Title.displayName\n\nconst DrawerDescription = React.forwardRef<\n React.ElementRef<typeof DrawerPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DrawerPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nDrawerDescription.displayName = DrawerPrimitive.Description.displayName\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n}\n"
}
],
"type": "components:ui"
}

View File

@@ -0,0 +1,13 @@
{
"name": "pagination",
"registryDependencies": [
"button"
],
"files": [
{
"name": "pagination.tsx",
"content": "import * as React from \"react\"\nimport { ChevronLeft, ChevronRight, MoreHorizontal } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ButtonProps, buttonVariants } from \"@/registry/default/ui/button\"\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n role=\"navigation\"\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n)\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n))\nPaginationContent.displayName = \"PaginationContent\"\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n))\nPaginationItem.displayName = \"PaginationItem\"\n\ntype PaginationLinkProps = {\n isActive?: boolean\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <PaginationItem>\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n </PaginationItem>\n)\nPaginationLink.displayName = \"PaginationLink\"\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span>Previous</span>\n </PaginationLink>\n)\nPaginationPrevious.displayName = \"PaginationPrevious\"\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </PaginationLink>\n)\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n <span className=\"sr-only\">More pages</span>\n </span>\n)\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n}\n"
}
],
"type": "components:ui"
}

View File

@@ -0,0 +1,13 @@
{
"name": "resizable",
"dependencies": [
"react-resizable-panels"
],
"files": [
{
"name": "resizable.tsx",
"content": "import { GripVertical } from \"lucide-react\"\nimport * as ResizablePrimitive from \"react-resizable-panels\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ResizablePanelGroup = ({\n className,\n ...props\n}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (\n <ResizablePrimitive.PanelGroup\n className={cn(\n \"flex h-full w-full data-[panel-group-direction=vertical]:flex-col\",\n className\n )}\n {...props}\n />\n)\n\nconst ResizablePanel = ResizablePrimitive.Panel\n\nconst ResizableHandle = ({\n withHandle,\n className,\n ...props\n}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {\n withHandle?: boolean\n}) => (\n <ResizablePrimitive.PanelResizeHandle\n className={cn(\n \"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90\",\n className\n )}\n {...props}\n >\n {withHandle && (\n <div className=\"z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border\">\n <GripVertical className=\"h-2.5 w-2.5\" />\n </div>\n )}\n </ResizablePrimitive.PanelResizeHandle>\n)\n\nexport { ResizablePanelGroup, ResizablePanel, ResizableHandle }\n"
}
],
"type": "components:ui"
}

View File

@@ -6,7 +6,7 @@
"files": [
{
"name": "sheet.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = SheetPrimitive.Portal\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\nconst sheetVariants = cva(\n \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n)\n\ninterface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n </SheetPrimitive.Content>\n </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\nconst SheetHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nSheetHeader.displayName = \"SheetHeader\"\n\nconst SheetFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = SheetPrimitive.Portal\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\nconst sheetVariants = cva(\n \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n)\n\ninterface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n </SheetPrimitive.Content>\n </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\nconst SheetHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nSheetHeader.displayName = \"SheetHeader\"\n\nconst SheetFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n"
}
],
"type": "components:ui"

View File

@@ -0,0 +1,14 @@
{
"name": "sonner",
"dependencies": [
"sonner",
"next-themes"
],
"files": [
{
"name": "sonner.tsx",
"content": "\"use client\"\n\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner } from \"sonner\"\n\ntype ToasterProps = React.ComponentProps<typeof Sonner>\n\nconst Toaster = ({ ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n <Sonner\n theme={theme as ToasterProps[\"theme\"]}\n className=\"toaster group\"\n toastOptions={{\n classNames: {\n toast:\n \"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg\",\n description: \"group-[.toast]:text-muted-foreground\",\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n cancelButton:\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground\",\n },\n }}\n {...props}\n />\n )\n}\n\nexport { Toaster }\n"
}
],
"type": "components:ui"
}

View File

@@ -10,7 +10,7 @@
},
{
"name": "use-toast.ts",
"content": "// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport type {\n ToastActionElement,\n ToastProps,\n} from \"@/registry/default/ui/toast\"\n\nconst TOAST_LIMIT = 1\nconst TOAST_REMOVE_DELAY = 1000000\n\ntype ToasterToast = ToastProps & {\n id: string\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionElement\n}\n\nconst actionTypes = {\n ADD_TOAST: \"ADD_TOAST\",\n UPDATE_TOAST: \"UPDATE_TOAST\",\n DISMISS_TOAST: \"DISMISS_TOAST\",\n REMOVE_TOAST: \"REMOVE_TOAST\",\n} as const\n\nlet count = 0\n\nfunction genId() {\n count = (count + 1) % Number.MAX_VALUE\n return count.toString()\n}\n\ntype ActionType = typeof actionTypes\n\ntype Action =\n | {\n type: ActionType[\"ADD_TOAST\"]\n toast: ToasterToast\n }\n | {\n type: ActionType[\"UPDATE_TOAST\"]\n toast: Partial<ToasterToast>\n }\n | {\n type: ActionType[\"DISMISS_TOAST\"]\n toastId?: ToasterToast[\"id\"]\n }\n | {\n type: ActionType[\"REMOVE_TOAST\"]\n toastId?: ToasterToast[\"id\"]\n }\n\ninterface State {\n toasts: ToasterToast[]\n}\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n\nconst addToRemoveQueue = (toastId: string) => {\n if (toastTimeouts.has(toastId)) {\n return\n }\n\n const timeout = setTimeout(() => {\n toastTimeouts.delete(toastId)\n dispatch({\n type: \"REMOVE_TOAST\",\n toastId: toastId,\n })\n }, TOAST_REMOVE_DELAY)\n\n toastTimeouts.set(toastId, timeout)\n}\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"ADD_TOAST\":\n return {\n ...state,\n toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n }\n\n case \"UPDATE_TOAST\":\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === action.toast.id ? { ...t, ...action.toast } : t\n ),\n }\n\n case \"DISMISS_TOAST\": {\n const { toastId } = action\n\n // ! Side effects ! - This could be extracted into a dismissToast() action,\n // but I'll keep it here for simplicity\n if (toastId) {\n addToRemoveQueue(toastId)\n } else {\n state.toasts.forEach((toast) => {\n addToRemoveQueue(toast.id)\n })\n }\n\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === toastId || toastId === undefined\n ? {\n ...t,\n open: false,\n }\n : t\n ),\n }\n }\n case \"REMOVE_TOAST\":\n if (action.toastId === undefined) {\n return {\n ...state,\n toasts: [],\n }\n }\n return {\n ...state,\n toasts: state.toasts.filter((t) => t.id !== action.toastId),\n }\n }\n}\n\nconst listeners: Array<(state: State) => void> = []\n\nlet memoryState: State = { toasts: [] }\n\nfunction dispatch(action: Action) {\n memoryState = reducer(memoryState, action)\n listeners.forEach((listener) => {\n listener(memoryState)\n })\n}\n\ntype Toast = Omit<ToasterToast, \"id\">\n\nfunction toast({ ...props }: Toast) {\n const id = genId()\n\n const update = (props: ToasterToast) =>\n dispatch({\n type: \"UPDATE_TOAST\",\n toast: { ...props, id },\n })\n const dismiss = () => dispatch({ type: \"DISMISS_TOAST\", toastId: id })\n\n dispatch({\n type: \"ADD_TOAST\",\n toast: {\n ...props,\n id,\n open: true,\n onOpenChange: (open) => {\n if (!open) dismiss()\n },\n },\n })\n\n return {\n id: id,\n dismiss,\n update,\n }\n}\n\nfunction useToast() {\n const [state, setState] = React.useState<State>(memoryState)\n\n React.useEffect(() => {\n listeners.push(setState)\n return () => {\n const index = listeners.indexOf(setState)\n if (index > -1) {\n listeners.splice(index, 1)\n }\n }\n }, [state])\n\n return {\n ...state,\n toast,\n dismiss: (toastId?: string) => dispatch({ type: \"DISMISS_TOAST\", toastId }),\n }\n}\n\nexport { useToast, toast }\n"
"content": "// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport type {\n ToastActionElement,\n ToastProps,\n} from \"@/registry/default/ui/toast\"\n\nconst TOAST_LIMIT = 1\nconst TOAST_REMOVE_DELAY = 1000000\n\ntype ToasterToast = ToastProps & {\n id: string\n title?: React.ReactNode\n description?: React.ReactNode\n action?: ToastActionElement\n}\n\nconst actionTypes = {\n ADD_TOAST: \"ADD_TOAST\",\n UPDATE_TOAST: \"UPDATE_TOAST\",\n DISMISS_TOAST: \"DISMISS_TOAST\",\n REMOVE_TOAST: \"REMOVE_TOAST\",\n} as const\n\nlet count = 0\n\nfunction genId() {\n count = (count + 1) % Number.MAX_SAFE_INTEGER\n return count.toString()\n}\n\ntype ActionType = typeof actionTypes\n\ntype Action =\n | {\n type: ActionType[\"ADD_TOAST\"]\n toast: ToasterToast\n }\n | {\n type: ActionType[\"UPDATE_TOAST\"]\n toast: Partial<ToasterToast>\n }\n | {\n type: ActionType[\"DISMISS_TOAST\"]\n toastId?: ToasterToast[\"id\"]\n }\n | {\n type: ActionType[\"REMOVE_TOAST\"]\n toastId?: ToasterToast[\"id\"]\n }\n\ninterface State {\n toasts: ToasterToast[]\n}\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n\nconst addToRemoveQueue = (toastId: string) => {\n if (toastTimeouts.has(toastId)) {\n return\n }\n\n const timeout = setTimeout(() => {\n toastTimeouts.delete(toastId)\n dispatch({\n type: \"REMOVE_TOAST\",\n toastId: toastId,\n })\n }, TOAST_REMOVE_DELAY)\n\n toastTimeouts.set(toastId, timeout)\n}\n\nexport const reducer = (state: State, action: Action): State => {\n switch (action.type) {\n case \"ADD_TOAST\":\n return {\n ...state,\n toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),\n }\n\n case \"UPDATE_TOAST\":\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === action.toast.id ? { ...t, ...action.toast } : t\n ),\n }\n\n case \"DISMISS_TOAST\": {\n const { toastId } = action\n\n // ! Side effects ! - This could be extracted into a dismissToast() action,\n // but I'll keep it here for simplicity\n if (toastId) {\n addToRemoveQueue(toastId)\n } else {\n state.toasts.forEach((toast) => {\n addToRemoveQueue(toast.id)\n })\n }\n\n return {\n ...state,\n toasts: state.toasts.map((t) =>\n t.id === toastId || toastId === undefined\n ? {\n ...t,\n open: false,\n }\n : t\n ),\n }\n }\n case \"REMOVE_TOAST\":\n if (action.toastId === undefined) {\n return {\n ...state,\n toasts: [],\n }\n }\n return {\n ...state,\n toasts: state.toasts.filter((t) => t.id !== action.toastId),\n }\n }\n}\n\nconst listeners: Array<(state: State) => void> = []\n\nlet memoryState: State = { toasts: [] }\n\nfunction dispatch(action: Action) {\n memoryState = reducer(memoryState, action)\n listeners.forEach((listener) => {\n listener(memoryState)\n })\n}\n\ntype Toast = Omit<ToasterToast, \"id\">\n\nfunction toast({ ...props }: Toast) {\n const id = genId()\n\n const update = (props: ToasterToast) =>\n dispatch({\n type: \"UPDATE_TOAST\",\n toast: { ...props, id },\n })\n const dismiss = () => dispatch({ type: \"DISMISS_TOAST\", toastId: id })\n\n dispatch({\n type: \"ADD_TOAST\",\n toast: {\n ...props,\n id,\n open: true,\n onOpenChange: (open) => {\n if (!open) dismiss()\n },\n },\n })\n\n return {\n id: id,\n dismiss,\n update,\n }\n}\n\nfunction useToast() {\n const [state, setState] = React.useState<State>(memoryState)\n\n React.useEffect(() => {\n listeners.push(setState)\n return () => {\n const index = listeners.indexOf(setState)\n if (index > -1) {\n listeners.splice(index, 1)\n }\n }\n }, [state])\n\n return {\n ...state,\n toast,\n dismiss: (toastId?: string) => dispatch({ type: \"DISMISS_TOAST\", toastId }),\n }\n}\n\nexport { useToast, toast }\n"
},
{
"name": "toaster.tsx",

View File

@@ -9,7 +9,7 @@
"files": [
{
"name": "alert-dialog.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/new-york/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n}\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/new-york/ui/button\"\n\nconst AlertDialog = AlertDialogPrimitive.Root\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n))\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n)\nAlertDialogHeader.displayName = \"AlertDialogHeader\"\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n)\nAlertDialogFooter.displayName = \"AlertDialogFooter\"\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n))\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n))\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n))\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n))\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n}\n"
}
],
"type": "components:ui"

View File

@@ -6,7 +6,7 @@
"files": [
{
"name": "button.tsx",
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90\",\n outline:\n \"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
"content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n )\n }\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n"
}
],
"type": "components:ui"

View File

@@ -10,7 +10,7 @@
"files": [
{
"name": "calendar.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronLeftIcon, ChevronRightIcon } from \"@radix-ui/react-icons\"\nimport { DayPicker } from \"react-day-picker\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/default/ui/button\"\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker>\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n ...props\n}: CalendarProps) {\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\"p-3\", className)}\n classNames={{\n months: \"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0\",\n month: \"space-y-4\",\n caption: \"flex justify-center pt-1 relative items-center\",\n caption_label: \"text-sm font-medium\",\n nav: \"space-x-1 flex items-center\",\n nav_button: cn(\n buttonVariants({ variant: \"outline\" }),\n \"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100\"\n ),\n nav_button_previous: \"absolute left-1\",\n nav_button_next: \"absolute right-1\",\n table: \"w-full border-collapse space-y-1\",\n head_row: \"flex\",\n head_cell:\n \"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]\",\n row: \"flex w-full mt-2\",\n cell: cn(\n \"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50\",\n props.mode === \"range\"\n ? \"[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md\"\n : \"[&:has([aria-selected])]:rounded-md\"\n ),\n day: cn(\n buttonVariants({ variant: \"ghost\" }),\n \"h-8 w-8 p-0 font-normal aria-selected:opacity-100\"\n ),\n day_range_start: \"day-range-start\",\n day_range_end: \"day-range-end\",\n day_selected:\n \"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground\",\n day_today: \"bg-accent text-accent-foreground\",\n day_outside:\n \"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30\",\n day_disabled: \"text-muted-foreground opacity-50\",\n day_range_middle:\n \"aria-selected:bg-accent aria-selected:text-accent-foreground\",\n day_hidden: \"invisible\",\n ...classNames,\n }}\n components={{\n IconLeft: ({ ...props }) => <ChevronLeftIcon className=\"h-4 w-4\" />,\n IconRight: ({ ...props }) => <ChevronRightIcon className=\"h-4 w-4\" />,\n }}\n {...props}\n />\n )\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n"
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronLeftIcon, ChevronRightIcon } from \"@radix-ui/react-icons\"\nimport { DayPicker } from \"react-day-picker\"\n\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/registry/default/ui/button\"\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker>\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n ...props\n}: CalendarProps) {\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\"p-3\", className)}\n classNames={{\n months: \"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0\",\n month: \"space-y-4\",\n caption: \"flex justify-center pt-1 relative items-center\",\n caption_label: \"text-sm font-medium\",\n nav: \"space-x-1 flex items-center\",\n nav_button: cn(\n buttonVariants({ variant: \"outline\" }),\n \"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100\"\n ),\n nav_button_previous: \"absolute left-1\",\n nav_button_next: \"absolute right-1\",\n table: \"w-full border-collapse space-y-1\",\n head_row: \"flex\",\n head_cell:\n \"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]\",\n row: \"flex w-full mt-2\",\n cell: cn(\n \"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md\",\n props.mode === \"range\"\n ? \"[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md\"\n : \"[&:has([aria-selected])]:rounded-md\"\n ),\n day: cn(\n buttonVariants({ variant: \"ghost\" }),\n \"h-8 w-8 p-0 font-normal aria-selected:opacity-100\"\n ),\n day_range_start: \"day-range-start\",\n day_range_end: \"day-range-end\",\n day_selected:\n \"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground\",\n day_today: \"bg-accent text-accent-foreground\",\n day_outside:\n \"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30\",\n day_disabled: \"text-muted-foreground opacity-50\",\n day_range_middle:\n \"aria-selected:bg-accent aria-selected:text-accent-foreground\",\n day_hidden: \"invisible\",\n ...classNames,\n }}\n components={{\n IconLeft: ({ ...props }) => <ChevronLeftIcon className=\"h-4 w-4\" />,\n IconRight: ({ ...props }) => <ChevronRightIcon className=\"h-4 w-4\" />,\n }}\n {...props}\n />\n )\n}\nCalendar.displayName = \"Calendar\"\n\nexport { Calendar }\n"
}
],
"type": "components:ui"

Some files were not shown because too many files have changed in this diff Show More