feat: CSS variables and CLI (#180)

This commit is contained in:
shadcn
2023-04-17 19:19:40 +04:00
committed by GitHub
parent d3cc7a3a6f
commit a703c6fb58
395 changed files with 8972 additions and 16156 deletions

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "shadcn/ui" }],
"changelog": ["@changesets/changelog-github", { "repo": "shadcn-ui" }],
"commit": false,
"fixed": [],
"linked": [],

View File

@@ -16,10 +16,17 @@
},
"settings": {
"tailwindcss": {
"callees": ["cn"]
"callees": ["cn"],
"config": "tailwind.config.cjs"
},
"next": {
"rootDir": ["apps/*/"]
}
}
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser"
}
]
}

View File

@@ -4,42 +4,9 @@ Accessible and customizable components that you can copy and paste into your app
![hero](apps/www/public/og.jpg)
## Roadmap
## Documentation
> **Warning**
> This is work in progress. I'm building this in public. You can follow the progress on Twitter [@shadcn](https://twitter.com/shadcn).
- [ ] Toggle Group
- [ ] Toolbar
- [x] ~Toast~
- [x] ~Toggle~
- [x] ~Navigation Menu~
- [x] ~Figma~
## Get Started
Starting a new project? Check out the Next.js template.
```bash
npx create-next-app -e https://github.com/shadcn/next-template
```
### Features
- Radix UI Primitives
- Tailwind CSS
- Fonts with `@next/font`
- Icons from [Lucide](https://lucide.dev)
- Dark mode with `next-themes`
- Automatic import sorting with `@ianvs/prettier-plugin-sort-imports`
### Tailwind CSS Features
- Class merging with `tailwind-merge`
- Animation with `tailwindcss-animate`
- Conditional classes with `clsx`
- Variants with `class-variance-authority`
- Automatic class sorting with `eslint-plugin-tailwindcss`
Visit http://ui.shadcn.com/docs to view the documentation.
## License

View File

@@ -1,4 +1,4 @@
# -----------------------------------------------------------------------------
# App
# -----------------------------------------------------------------------------
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_URL=http://localhost:3001

View File

@@ -1,25 +0,0 @@
import { allDocs } from "contentlayer/generated"
import MdxHead from "@/components/mdx-head"
interface HeadProps {
params: {
slug: string[]
}
}
export default function Head({ params }: HeadProps) {
const slug = params?.slug?.join("/") || ""
const doc = allDocs.find((doc) => doc.slugAsParams === slug)
if (!doc) {
return null
}
return (
<MdxHead
params={params}
og={{ heading: doc.title, type: doc.title, mode: "light" }}
/>
)
}

View File

@@ -2,15 +2,20 @@ import { notFound } from "next/navigation"
import { allDocs } from "contentlayer/generated"
import "@/styles/mdx.css"
import type { Metadata } from "next"
import Link from "next/link"
import { ChevronRight } from "lucide-react"
import Balancer from "react-wrap-balancer"
import { siteConfig } from "@/config/site"
import { getTableOfContents } from "@/lib/toc"
import { absoluteUrl, cn } from "@/lib/utils"
import { badgeVariants } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Icons } from "@/components/icons"
import { Mdx } from "@/components/mdx"
import { DocsPageHeader } from "@/components/page-header"
import { Mdx } from "@/components/mdx-components"
import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc"
import { Separator } from "@/components/ui/separator"
interface DocPageProps {
params: {
@@ -18,6 +23,53 @@ interface DocPageProps {
}
}
async function getDocFromParams({ params }: DocPageProps) {
const slug = params.slug?.join("/") || ""
const doc = allDocs.find((doc) => doc.slugAsParams === slug)
if (!doc) {
null
}
return doc
}
export async function generateMetadata({
params,
}: DocPageProps): Promise<Metadata> {
const doc = await getDocFromParams({ params })
if (!doc) {
return {}
}
return {
title: doc.title,
description: doc.description,
openGraph: {
title: doc.title,
description: doc.description,
type: "article",
url: absoluteUrl(doc.slug),
images: [
{
url: siteConfig.ogImage,
width: 1200,
height: 630,
alt: siteConfig.name,
},
],
},
twitter: {
card: "summary_large_image",
title: doc.title,
description: doc.description,
images: [siteConfig.ogImage],
creator: "@shadcn",
},
}
}
export async function generateStaticParams(): Promise<
DocPageProps["params"][]
> {
@@ -27,8 +79,7 @@ export async function generateStaticParams(): Promise<
}
export default async function DocPage({ params }: DocPageProps) {
const slug = params?.slug?.join("/") || ""
const doc = allDocs.find((doc) => doc.slugAsParams === slug)
const doc = await getDocFromParams({ params })
if (!doc) {
notFound()
@@ -37,41 +88,57 @@ export default async function DocPage({ params }: DocPageProps) {
const toc = await getTableOfContents(doc.body.raw)
return (
<main className="relative py-6 lg:gap-10 lg:py-10 xl:grid xl:grid-cols-[1fr_300px]">
<main className="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
<div className="mx-auto w-full min-w-0">
<DocsPageHeader heading={doc.title} text={doc.description}>
{doc.radix ? (
<div className="flex items-center space-x-2 pt-4">
{doc.radix?.link && (
<Link
href={doc.radix.link}
target="_blank"
rel="noreferrer"
className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-900 transition-colors hover:bg-slate-700 hover:text-slate-50"
>
<Icons.radix className="mr-1 h-3 w-3" />
Radix UI
</Link>
)}
{doc.radix?.api && (
<Link
href={doc.radix.api}
target="_blank"
rel="noreferrer"
className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-900 transition-colors hover:bg-slate-700 hover:text-slate-50"
>
API Reference
</Link>
)}
</div>
) : null}
</DocsPageHeader>
<div className="mb-4 flex items-center space-x-1 text-sm text-muted-foreground">
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
Docs
</div>
<ChevronRight className="h-4 w-4" />
<div className="font-medium text-foreground">{doc.title}</div>
</div>
<div className="space-y-2">
<h1 className={cn("scroll-m-20 text-4xl font-bold tracking-tight")}>
{doc.title}
</h1>
{doc.description && (
<p className="text-lg text-muted-foreground">
<Balancer>{doc.description}</Balancer>
</p>
)}
</div>
{doc.radix ? (
<div className="flex items-center space-x-2 pt-4">
{doc.radix?.link && (
<Link
href={doc.radix.link}
target="_blank"
rel="noreferrer"
className={cn(badgeVariants({ variant: "secondary" }))}
>
<Icons.radix className="mr-1 h-3 w-3" />
Radix UI
</Link>
)}
{doc.radix?.api && (
<Link
href={doc.radix.api}
target="_blank"
rel="noreferrer"
className={cn(badgeVariants({ variant: "secondary" }))}
>
API Reference
</Link>
)}
</div>
) : null}
<Separator className="my-4 md:my-6" />
<Mdx code={doc.body.code} />
<Separator className="my-4 md:my-6" />
<DocsPager doc={doc} />
</div>
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-10">
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-6">
<DashboardTableOfContents toc={toc} />
</div>
</div>

View File

@@ -8,9 +8,9 @@ interface DocsLayoutProps {
export default function DocsLayout({ children }: DocsLayoutProps) {
return (
<div className="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 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 overflow-y-auto border-r border-r-slate-100 dark:border-r-slate-700 md:sticky md:block">
<ScrollArea className="pr-6 lg:py-10">
<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 overflow-y-auto border-r md:sticky md:block">
<ScrollArea className="py-6 pr-6 lg:py-8">
<DocsSidebarNav items={docsConfig.sidebarNav} />
</ScrollArea>
</aside>

View File

@@ -0,0 +1,71 @@
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Icons } from "@/components/icons"
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}
export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
const [isLoading, setIsLoading] = React.useState<boolean>(false)
async function onSubmit(event: React.SyntheticEvent) {
event.preventDefault()
setIsLoading(true)
setTimeout(() => {
setIsLoading(false)
}, 3000)
}
return (
<div className={cn("grid gap-6", className)} {...props}>
<form onSubmit={onSubmit}>
<div className="grid gap-2">
<div className="grid gap-1">
<Label className="sr-only" htmlFor="email">
Email
</Label>
<Input
id="email"
placeholder="name@example.com"
type="email"
autoCapitalize="none"
autoComplete="email"
autoCorrect="off"
disabled={isLoading}
/>
</div>
<Button disabled={isLoading}>
{isLoading && (
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
)}
Sign In with Email
</Button>
</div>
</form>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-white px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>
<Button variant="outline" type="button" disabled={isLoading}>
{isLoading ? (
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
) : (
<Icons.gitHub className="mr-2 h-4 w-4" />
)}{" "}
Github
</Button>
</div>
)
}

View File

@@ -0,0 +1,99 @@
import { Metadata } from "next"
import Image from "next/image"
import Link from "next/link"
import { Command } from "lucide-react"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { UserAuthForm } from "@/app/examples/authentication/components/user-auth-form"
export const metadata: Metadata = {
title: "Authentication",
description: "Authentication forms built using the components.",
}
export default function AuthenticationPage() {
return (
<>
<div className="md:hidden">
<Image
src="/examples/authentication-light.png"
width={1280}
height={843}
alt="Authentication"
className="block dark:hidden"
/>
<Image
src="/examples/authentication-dark.png"
width={1280}
height={843}
alt="Authentication"
className="hidden dark:block"
/>
</div>
<div className="container relative hidden h-[800px] flex-col items-center justify-center md:grid lg:max-w-none lg:grid-cols-2 lg:px-0">
<Link
href="/examples/authentication"
className={cn(
buttonVariants({ variant: "ghost", size: "sm" }),
"absolute right-4 top-4 md:right-8 md:top-8"
)}
>
Login
</Link>
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
<div
className="absolute inset-0 bg-cover"
style={{
backgroundImage:
"url(https://images.unsplash.com/photo-1590069261209-f8e9b8642343?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1376&q=80)",
}}
/>
<div className="relative z-20 flex items-center text-lg font-medium">
<Command className="mr-2 h-6 w-6" /> Acme Inc
</div>
<div className="relative z-20 mt-auto">
<blockquote className="space-y-2">
<p className="text-lg">
&ldquo;This library has saved me countless hours of work and
helped me deliver stunning designs to my clients faster than
ever before. Highly recommended!&rdquo;
</p>
<footer className="text-sm">Sofia Davis</footer>
</blockquote>
</div>
</div>
<div className="lg:p-8">
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<div className="flex flex-col space-y-2 text-center">
<h1 className="text-2xl font-semibold tracking-tight">
Create an account
</h1>
<p className="text-sm text-muted-foreground">
Enter your email below to create your account
</p>
</div>
<UserAuthForm />
<p className="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
Privacy Policy
</Link>
.
</p>
</div>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,60 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
export function DemoCookieSettings() {
return (
<Card>
<CardHeader>
<CardTitle>Cookie Settings</CardTitle>
<CardDescription>Manage your cookie settings here.</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="flex items-center justify-between space-x-2">
<Label htmlFor="necessary" className="flex flex-col space-y-1">
<span>Strictly Necessary</span>
<span className="font-normal leading-snug text-muted-foreground">
These cookies are essential in order to use the website and use
its features.
</span>
</Label>
<Switch id="necessary" defaultChecked />
</div>
<div className="flex items-center justify-between space-x-2">
<Label htmlFor="functional" className="flex flex-col space-y-1">
<span>Functional Cookies</span>
<span className="font-normal leading-snug text-muted-foreground">
These cookies allow the website to provide personalized
functionality.
</span>
</Label>
<Switch id="functional" />
</div>
<div className="flex items-center justify-between space-x-2">
<Label htmlFor="performance" className="flex flex-col space-y-1">
<span>Performance Cookies</span>
<span className="font-normal leading-snug text-muted-foreground">
These cookies help to improve the performance of the website.
</span>
</Label>
<Switch id="performance" />
</div>
</CardContent>
<CardFooter>
<Button variant="outline" className="w-full">
Save preferences
</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,60 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Icons } from "@/components/icons"
export function DemoCreateAccount() {
return (
<Card>
<CardHeader className="space-y-1">
<CardTitle className="text-2xl">Create an account</CardTitle>
<CardDescription>
Enter your email below to create your account
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid grid-cols-2 gap-6">
<Button variant="outline">
<Icons.gitHub className="mr-2 h-4 w-4" />
Github
</Button>
<Button variant="outline">
<Icons.google className="mr-2 h-4 w-4" />
Google
</Button>
</div>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-background px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" />
</div>
<div className="grid gap-2">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" />
</div>
</CardContent>
<CardFooter>
<Button className="w-full">Create account</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,18 @@
import { Card, CardContent } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
export function DemoDatePicker() {
return (
<Card>
<CardContent className="pt-6">
<div className="space-y-2">
<Label htmlFor="date" className="shrink-0">
Pick a date
</Label>
<CalendarDateRangePicker className="[&>button]:w-[260px]" />
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,81 @@
import { ChevronDown, Circle, Plus, Star } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Separator } from "@/components/ui/separator"
export function DemoGithub() {
return (
<Card>
<CardHeader className="grid grid-cols-[1fr_110px] items-start gap-4 space-y-0">
<div className="space-y-1">
<CardTitle>shadcn/ui</CardTitle>
<CardDescription>
Beautifully designed components built with Radix UI and Tailwind
CSS.
</CardDescription>
</div>
<div className="flex items-center space-x-1 rounded-md bg-secondary text-secondary-foreground">
<Button variant="secondary" className="px-3">
<Star className="mr-2 h-4 w-4" />
Star
</Button>
<Separator orientation="vertical" className="h-[20px]" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" className="px-2">
<ChevronDown className="h-4 w-4 text-secondary-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
alignOffset={-5}
className="w-[200px]"
forceMount
>
<DropdownMenuLabel>Suggested Lists</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem checked>
Future Ideas
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>My Stack</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem>Inspiration</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Plus className="mr-2 h-4 w-4" /> Create List
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</CardHeader>
<CardContent>
<div className="flex space-x-4 text-sm text-muted-foreground">
<div className="flex items-center">
<Circle className="mr-1 h-3 w-3 fill-sky-400 text-sky-400" />
TypeScipt
</div>
<div className="flex items-center">
<Star className="mr-1 h-3 w-3" />
10k
</div>
<div>Updated April 2023</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,51 @@
import { AtSign, Bell, BellOff } from "lucide-react"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
export function DemoNotifications() {
return (
<Card>
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>
Choose what you want to be notified about.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-1 p-1.5">
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground">
<Bell className="h-5 w-5" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">Everything</p>
<p className="text-sm text-muted-foreground">
Email digest, mentions & all activity.
</p>
</div>
</div>
<div className="flex items-center space-x-4 rounded-md bg-accent p-2 text-accent-foreground">
<AtSign className="h-5 w-5" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">Available</p>
<p className="text-sm text-muted-foreground">
Only mentions and comments.
</p>
</div>
</div>
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground">
<BellOff className="h-5 w-5" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">Ignoring</p>
<p className="text-sm text-muted-foreground">
Turn off all notifications.
</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,117 @@
import { CreditCard } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Icons } from "@/components/icons"
export function DemoPaymentMethod() {
return (
<Card>
<CardHeader>
<CardTitle>Payment Method</CardTitle>
<CardDescription>
Add a new payment method to your account.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<RadioGroup defaultValue="card" className="grid grid-cols-3 gap-4">
<Label
htmlFor="card"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
>
<RadioGroupItem value="card" id="card" className="sr-only" />
<CreditCard className="mb-3 h-6 w-6" />
Card
</Label>
<Label
htmlFor="paypal"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
>
<RadioGroupItem value="paypal" id="paypal" className="sr-only" />
<Icons.paypal className="mb-3 h-6 w-6" />
Paypal
</Label>
<Label
htmlFor="apple"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground [&:has([data-state=checked])]:border-primary"
>
<RadioGroupItem value="apple" id="apple" className="sr-only" />
<Icons.apple className="mb-3 h-6 w-6" />
Apple
</Label>
</RadioGroup>
<div className="grid gap-2">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="First Last" />
</div>
<div className="grid gap-2">
<Label htmlFor="number">Card number</Label>
<Input id="number" placeholder="" />
</div>
<div className="grid grid-cols-3 gap-4">
<div className="grid gap-2">
<Label htmlFor="month">Expires</Label>
<Select>
<SelectTrigger id="month">
<SelectValue placeholder="Month" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">January</SelectItem>
<SelectItem value="2">February</SelectItem>
<SelectItem value="3">March</SelectItem>
<SelectItem value="4">April</SelectItem>
<SelectItem value="5">May</SelectItem>
<SelectItem value="6">June</SelectItem>
<SelectItem value="7">July</SelectItem>
<SelectItem value="8">August</SelectItem>
<SelectItem value="9">September</SelectItem>
<SelectItem value="10">October</SelectItem>
<SelectItem value="11">November</SelectItem>
<SelectItem value="12">December</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="year">Year</Label>
<Select>
<SelectTrigger id="year">
<SelectValue placeholder="Year" />
</SelectTrigger>
<SelectContent>
{Array.from({ length: 10 }, (_, i) => (
<SelectItem key={i} value={`${new Date().getFullYear() + i}`}>
{new Date().getFullYear() + i}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="cvc">CVC</Label>
<Input id="cvc" placeholder="CVC" />
</div>
</div>
</CardContent>
<CardFooter>
<Button className="w-full">Continue</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,82 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
export function DemoReportAnIssue() {
return (
<Card>
<CardHeader>
<CardTitle>Report an issue</CardTitle>
<CardDescription>
What area are you having problems with?
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="area">Area</Label>
<Select defaultValue="billing">
<SelectTrigger id="area">
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="team">Team</SelectItem>
<SelectItem value="billing">Billing</SelectItem>
<SelectItem value="account">Account</SelectItem>
<SelectItem value="deployments">Deployments</SelectItem>
<SelectItem value="support">Support</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="security-level">Security Level</Label>
<Select defaultValue="2">
<SelectTrigger id="security-level">
<SelectValue placeholder="Select level" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">Severity 1 (Highest)</SelectItem>
<SelectItem value="2">Severity 2</SelectItem>
<SelectItem value="3">Severity 3</SelectItem>
<SelectItem value="4">Severity 4 (Lowest)</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Subject</Label>
<Input id="subject" placeholder="I need help with..." />
</div>
<div className="grid gap-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
placeholder="Please include all information relevant to your issue."
/>
</div>
</CardContent>
<CardFooter className="justify-between space-x-2">
<Button variant="ghost">Cancel</Button>
<Button>Submit</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,116 @@
"use client"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
export function DemoShareDocument() {
return (
<Card>
<CardHeader>
<CardTitle>Share this document</CardTitle>
<CardDescription>
Anyone with the link can view this document.
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex space-x-2">
<Input value="http://example.com/link/to/document" readOnly />
<Button variant="secondary" className="shrink-0">
Copy Link
</Button>
</div>
<Separator className="my-4" />
<div className="space-y-4">
<h4 className="text-sm font-medium">People with access</h4>
<div className="grid gap-6">
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src="/avatars/03.png" />
<AvatarFallback>OM</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">
Olivia Martin
</p>
<p className="text-sm text-muted-foreground">m@example.com</p>
</div>
</div>
<Select defaultValue="edit">
<SelectTrigger className="ml-auto w-[110px]">
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="edit">Can edit</SelectItem>
<SelectItem value="view">Can view</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src="/avatars/05.png" />
<AvatarFallback>IN</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">
Isabella Nguyen
</p>
<p className="text-sm text-muted-foreground">b@example.com</p>
</div>
</div>
<Select defaultValue="view">
<SelectTrigger className="ml-auto w-[110px]">
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="edit">Can edit</SelectItem>
<SelectItem value="view">Can view</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src="/avatars/01.png" />
<AvatarFallback>SD</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">
Sofia Davis
</p>
<p className="text-sm text-muted-foreground">p@example.com</p>
</div>
</div>
<Select defaultValue="view">
<SelectTrigger className="ml-auto w-[110px]">
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="edit">Can edit</SelectItem>
<SelectItem value="view">Can view</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,147 @@
import { ChevronDown } from "lucide-react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function DemoTeamMembers() {
return (
<Card>
<CardHeader>
<CardTitle>Team Members</CardTitle>
<CardDescription>
Invite your team members to collaborate.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src="/avatars/01.png" />
<AvatarFallback>OM</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">Sofia Davis</p>
<p className="text-sm text-muted-foreground">m@example.com</p>
</div>
</div>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="ml-auto">
Owner{" "}
<ChevronDown className="ml-2 h-4 w-4 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="end">
<Command>
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p className="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p className="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p className="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p className="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src="/avatars/02.png" />
<AvatarFallback>JL</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">Jackson Lee</p>
<p className="text-sm text-muted-foreground">p@example.com</p>
</div>
</div>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="ml-auto">
Member{" "}
<ChevronDown className="ml-2 h-4 w-4 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" align="end">
<Command>
<CommandInput placeholder="Select new role..." />
<CommandList>
<CommandEmpty>No roles found.</CommandEmpty>
<CommandGroup className="p-1.5">
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Viewer</p>
<p className="text-sm text-muted-foreground">
Can view and comment.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Developer</p>
<p className="text-sm text-muted-foreground">
Can view, comment and edit.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Billing</p>
<p className="text-sm text-muted-foreground">
Can view, comment and manage billing.
</p>
</CommandItem>
<CommandItem className="teamaspace-y-1 flex flex-col items-start px-4 py-2">
<p>Owner</p>
<p className="text-sm text-muted-foreground">
Admin-level access to all resources.
</p>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
</CardContent>
</Card>
)
}

View File

@@ -0,0 +1,93 @@
import { Metadata } from "next"
import { cn } from "@/lib/utils"
import { DemoCookieSettings } from "./components/cookie-settings"
import { DemoCreateAccount } from "./components/create-account"
import { DemoDatePicker } from "./components/date-picker"
import { DemoGithub } from "./components/github-card"
import { DemoNotifications } from "./components/notifications"
import { DemoPaymentMethod } from "./components/payment-method"
import { DemoReportAnIssue } from "./components/report-an-issue"
import { DemoShareDocument } from "./components/share-document"
import { DemoTeamMembers } from "./components/team-members"
import "./styles.css"
import Image from "next/image"
export const metadata: Metadata = {
title: "Cards",
description: "Examples of cards built using the components.",
}
function DemoContainer({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"flex items-center justify-center [&>div]:w-full",
className
)}
{...props}
/>
)
}
export default function CardsPage() {
return (
<>
<div className="md:hidden">
<Image
src="/examples/cards-light.png"
width={1280}
height={1214}
alt="Cards"
className="block dark:hidden"
/>
<Image
src="/examples/cards-dark.png"
width={1280}
height={1214}
alt="Cards"
className="hidden dark:block"
/>
</div>
<div className="hidden items-start justify-center gap-6 rounded-lg p-8 md:grid lg:grid-cols-2 xl:grid-cols-3">
<div className="col-span-2 grid items-start gap-6 lg:col-span-1">
<DemoContainer>
<DemoCreateAccount />
</DemoContainer>
<DemoContainer>
<DemoPaymentMethod />
</DemoContainer>
</div>
<div className="col-span-2 grid items-start gap-6 lg:col-span-1">
<DemoContainer>
<DemoTeamMembers />
</DemoContainer>
<DemoContainer>
<DemoShareDocument />
</DemoContainer>
<DemoContainer>
<DemoDatePicker />
</DemoContainer>
<DemoContainer>
<DemoNotifications />
</DemoContainer>
</div>
<div className="col-span-2 grid items-start gap-6 lg:col-span-1 lg:grid-cols-2 xl:grid-cols-1">
<DemoContainer>
<DemoReportAnIssue />
</DemoContainer>
<DemoContainer>
<DemoGithub />
</DemoContainer>
<DemoContainer>
<DemoCookieSettings />
</DemoContainer>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,63 @@
[data-section="cards"] {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 214 95% 93%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 224 82% 56%;
--primary-foreground: 0 0% 100%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 216 100% 97%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 1.2rem;
}
.dark [data-section="cards"] {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 224 82% 56%;
--primary-foreground: 0 0% 100%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 226 57% 21%;
--accent-foreground: 0 0% 100%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
}

View File

@@ -0,0 +1,66 @@
"use client"
import * as React from "react"
import { addDays, format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function CalendarDateRangePicker({
className,
}: React.HTMLAttributes<HTMLDivElement>) {
const [date, setDate] = React.useState<DateRange | undefined>({
from: new Date(2023, 0, 20),
to: addDays(new Date(2023, 0, 20), 20),
})
return (
<div className={cn("grid gap-2", className)}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
size="sm"
className={cn(
"w-[240px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date?.from ? (
date.to ? (
<>
{format(date.from, "LLL dd, y")} -{" "}
{format(date.to, "LLL dd, y")}
</>
) : (
format(date.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="end">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
)
}

View File

@@ -0,0 +1,40 @@
import Link from "next/link"
import { cn } from "@/lib/utils"
export function MainNav({
className,
...props
}: React.HTMLAttributes<HTMLElement>) {
return (
<nav
className={cn("flex items-center space-x-4 lg:space-x-6", className)}
{...props}
>
<Link
href="/examples/dashboard"
className="text-sm font-medium transition-colors hover:text-primary"
>
Overview
</Link>
<Link
href="/examples/dashboard"
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
>
Customers
</Link>
<Link
href="/examples/dashboard"
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
>
Products
</Link>
<Link
href="/examples/dashboard"
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
>
Settings
</Link>
</nav>
)
}

View File

@@ -0,0 +1,78 @@
"use client"
import { Bar, BarChart, ResponsiveContainer, XAxis, YAxis } from "recharts"
const data = [
{
name: "Jan",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Feb",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Mar",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Apr",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "May",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Jun",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Jul",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Aug",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Sep",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Oct",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Nov",
total: Math.floor(Math.random() * 5000) + 1000,
},
{
name: "Dec",
total: Math.floor(Math.random() * 5000) + 1000,
},
]
export function Overview() {
return (
<ResponsiveContainer width="100%" height={350}>
<BarChart data={data}>
<XAxis
dataKey="name"
stroke="#888888"
fontSize={12}
tickLine={false}
axisLine={false}
/>
<YAxis
stroke="#888888"
fontSize={12}
tickLine={false}
axisLine={false}
tickFormatter={(value) => `$${value}`}
/>
<Bar dataKey="total" fill="#adfa1d" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
)
}

View File

@@ -0,0 +1,67 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
export function RecentSales() {
return (
<div className="space-y-8">
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src="/avatars/01.png" alt="Avatar" />
<AvatarFallback>OM</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">Olivia Martin</p>
<p className="text-sm text-muted-foreground">
olivia.martin@email.com
</p>
</div>
<div className="ml-auto font-medium">+$1,999.00</div>
</div>
<div className="flex items-center">
<Avatar className="flex h-9 w-9 items-center justify-center space-y-0 border">
<AvatarImage src="/avatars/02.png" alt="Avatar" />
<AvatarFallback>JL</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">Jackson Lee</p>
<p className="text-sm text-muted-foreground">jackson.lee@email.com</p>
</div>
<div className="ml-auto font-medium">+$39.00</div>
</div>
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src="/avatars/03.png" alt="Avatar" />
<AvatarFallback>IN</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">Isabella Nguyen</p>
<p className="text-sm text-muted-foreground">
isabella.nguyen@email.com
</p>
</div>
<div className="ml-auto font-medium">+$299.00</div>
</div>
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src="/avatars/04.png" alt="Avatar" />
<AvatarFallback>WK</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">William Kim</p>
<p className="text-sm text-muted-foreground">will@email.com</p>
</div>
<div className="ml-auto font-medium">+$99.00</div>
</div>
<div className="flex items-center">
<Avatar className="h-9 w-9">
<AvatarImage src="/avatars/05.png" alt="Avatar" />
<AvatarFallback>SD</AvatarFallback>
</Avatar>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">Sofia Davis</p>
<p className="text-sm text-muted-foreground">sofia.davis@email.com</p>
</div>
<div className="ml-auto font-medium">+$39.00</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,13 @@
import { Input } from "@/components/ui/input"
export function Search() {
return (
<div>
<Input
type="search"
placeholder="Search..."
className="h-9 md:w-[100px] lg:w-[300px]"
/>
</div>
)
}

View File

@@ -0,0 +1,205 @@
"use client"
import * as React from "react"
import { Check, ChevronsUpDown, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
const groups = [
{
label: "Personal Account",
teams: [
{
label: "Alicia Koch",
value: "personal",
},
],
},
{
label: "Teams",
teams: [
{
label: "Acme Inc.",
value: "acme-inc",
},
{
label: "Monsters Inc.",
value: "monsters",
},
],
},
]
type Team = typeof groups[number]["teams"][number]
type PopoverTriggerProps = React.ComponentPropsWithoutRef<typeof PopoverTrigger>
interface TeamSwitcherProps extends PopoverTriggerProps {}
export default function TeamSwitcher({ className }: TeamSwitcherProps) {
const [open, setOpen] = React.useState(false)
const [showNewTeamDialog, setShowNewTeamDialog] = React.useState(false)
const [selectedTeam, setSelectedTeam] = React.useState<Team>(
groups[0].teams[0]
)
return (
<Dialog open={showNewTeamDialog} onOpenChange={setShowNewTeamDialog}>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
role="combobox"
aria-expanded={open}
aria-label="Select a team"
className={cn("w-[200px] justify-between", className)}
>
<Avatar className="mr-2 h-5 w-5">
<AvatarImage
src={`https://avatar.vercel.sh/${selectedTeam.value}.png`}
alt={selectedTeam.label}
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
{selectedTeam.label}
<ChevronsUpDown className="ml-auto h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandList>
<CommandInput placeholder="Search team..." />
<CommandEmpty>No team found.</CommandEmpty>
{groups.map((group) => (
<CommandGroup key={group.label} heading={group.label}>
{group.teams.map((team) => (
<CommandItem
key={team.value}
onSelect={() => {
setSelectedTeam(team)
setOpen(false)
}}
className="text-sm"
>
<Avatar className="mr-2 h-5 w-5">
<AvatarImage
src={`https://avatar.vercel.sh/${team.value}.png`}
alt={team.label}
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
{team.label}
<Check
className={cn(
"ml-auto h-4 w-4",
selectedTeam.value === team.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
<CommandSeparator />
<CommandList>
<CommandGroup>
<DialogTrigger asChild>
<CommandItem
onSelect={() => {
setOpen(false)
setShowNewTeamDialog(true)
}}
>
<PlusCircle className="mr-2 h-5 w-5" />
Create Team
</CommandItem>
</DialogTrigger>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<DialogContent>
<DialogHeader>
<DialogTitle>Create team</DialogTitle>
<DialogDescription>
Add a new team to manage products and customers.
</DialogDescription>
</DialogHeader>
<div>
<div className="space-y-4 py-2 pb-4">
<div className="space-y-2">
<Label htmlFor="name">Team name</Label>
<Input id="name" placeholder="Acme Inc." />
</div>
<div className="space-y-2">
<Label htmlFor="plan">Subscription plan</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
<SelectItem value="free">
<span className="font-medium">Free</span> -{" "}
<span className="text-muted-foreground">
Trial for two weeks
</span>
</SelectItem>
<SelectItem value="pro">
<span className="font-medium">Pro</span> -{" "}
<span className="text-muted-foreground">
$9/month per user
</span>
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setShowNewTeamDialog(false)}>
Cancel
</Button>
<Button type="submit">Continue</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}

View File

@@ -0,0 +1,67 @@
import { CreditCard, LogOut, PlusCircle, Settings, User } from "lucide-react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function UserNav() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src="/avatars/01.png" alt="@shadcn" />
<AvatarFallback>SC</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end" forceMount>
<DropdownMenuLabel className="font-normal">
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">shadcn</p>
<p className="text-xs leading-none text-muted-foreground">
m@example.com
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
<span>New Team</span>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="mr-2 h-4 w-4" />
<span>Log out</span>
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -0,0 +1,165 @@
import { Metadata } from "next"
import Image from "next/image"
import { Activity, CreditCard, DollarSign, Download, Users } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { CalendarDateRangePicker } from "@/app/examples/dashboard/components/date-range-picker"
import { MainNav } from "@/app/examples/dashboard/components/main-nav"
import { Overview } from "@/app/examples/dashboard/components/overview"
import { RecentSales } from "@/app/examples/dashboard/components/recent-sales"
import { Search } from "@/app/examples/dashboard/components/search"
import TeamSwitcher from "@/app/examples/dashboard/components/team-switcher"
import { UserNav } from "@/app/examples/dashboard/components/user-nav"
export const metadata: Metadata = {
title: "Dashboard",
description: "Example dashboard app using the components.",
}
export default function DashboardPage() {
return (
<>
<div className="md:hidden">
<Image
src="/examples/dashboard-light.png"
width={1280}
height={866}
alt="Dashboard"
className="block dark:hidden"
/>
<Image
src="/examples/dashboard-dark.png"
width={1280}
height={866}
alt="Dashboard"
className="hidden dark:block"
/>
</div>
<div className="hidden flex-col md:flex">
<div className="border-b">
<div className="flex h-16 items-center px-4">
<TeamSwitcher />
<MainNav className="mx-6" />
<div className="ml-auto flex items-center space-x-4">
<Search />
<UserNav />
</div>
</div>
</div>
<div className="flex-1 space-y-4 p-8 pt-6">
<div className="flex items-center justify-between space-y-2">
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
<div className="flex items-center space-x-2">
<CalendarDateRangePicker />
<Button size="sm">
<Download className="mr-2 h-4 w-4" />
Download
</Button>
</div>
</div>
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="analytics" disabled>
Analytics
</TabsTrigger>
<TabsTrigger value="reports" disabled>
Reports
</TabsTrigger>
<TabsTrigger value="notifications" disabled>
Notifications
</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Total Revenue
</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">$45,231.89</div>
<p className="text-xs text-muted-foreground">
+20.1% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Subscriptions
</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+2350</div>
<p className="text-xs text-muted-foreground">
+180.1% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Sales</CardTitle>
<CreditCard className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+12,234</div>
<p className="text-xs text-muted-foreground">
+19% from last month
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
Active Now
</CardTitle>
<Activity className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">+573</div>
<p className="text-xs text-muted-foreground">
+201 since last hour
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
<Card className="col-span-4">
<CardHeader>
<CardTitle>Overview</CardTitle>
</CardHeader>
<CardContent className="pl-2">
<Overview />
</CardContent>
</Card>
<Card className="col-span-3">
<CardHeader>
<CardTitle>Recent Sales</CardTitle>
<CardDescription>
You made 265 sales this month.
</CardDescription>
</CardHeader>
<CardContent>
<RecentSales />
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,69 @@
import { Metadata } from "next"
import Link from "next/link"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { ExamplesNav } from "@/components/examples-nav"
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
export const metadata: Metadata = {
title: "Examples",
description: "Check out some examples app built using the components.",
}
interface ExamplesLayoutProps {
children: React.ReactNode
}
export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
return (
<>
<div className="container relative pb-10">
<PageHeader className="page-header">
<PageHeaderHeading className="hidden md:block">
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>
</PageHeader>
<section className="pb-6 md:pb-10">
<div className="flex w-full items-center justify-between">
<div className="flex space-x-4">
<Link
href="/docs"
className={cn(
buttonVariants({ size: "lg" }),
"rounded-[0.5rem]"
)}
>
Get Started
</Link>
<Link
href="/components"
className={cn(
buttonVariants({ variant: "outline", size: "lg" }),
"rounded-[0.5rem] pl-6"
)}
>
Components
</Link>
</div>
</div>
</section>
<section>
<ExamplesNav />
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow-xl">
{children}
</div>
</section>
</div>
</>
)
}

View File

@@ -0,0 +1,84 @@
import Image from "next/image"
import { ListMusic, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import { Album } from "../data/albums"
import { playlists } from "../data/playlists"
interface AlbumArtworkProps extends React.HTMLAttributes<HTMLDivElement> {
album: Album
aspectRatio?: "portrait" | "square"
width?: number
height?: number
}
export function AlbumArtwork({
album,
aspectRatio = "portrait",
width,
height,
className,
...props
}: AlbumArtworkProps) {
return (
<div className={cn("space-y-3", className)} {...props}>
<ContextMenu>
<ContextMenuTrigger>
<div className="overflow-hidden rounded-md">
<Image
src={album.cover}
alt={album.name}
width={width}
height={height}
className={cn(
"h-auto w-auto object-cover transition-all hover:scale-105",
aspectRatio === "portrait" ? "aspect-[3/4]" : "aspect-square"
)}
/>
</div>
</ContextMenuTrigger>
<ContextMenuContent className="w-40">
<ContextMenuItem>Add to Library</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
New Playlist
</ContextMenuItem>
<ContextMenuSeparator />
{playlists.map((playlist) => (
<ContextMenuItem key={playlist}>
<ListMusic className="mr-2 h-4 w-4" /> {playlist}
</ContextMenuItem>
))}
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>Play Next</ContextMenuItem>
<ContextMenuItem>Play Later</ContextMenuItem>
<ContextMenuItem>Create Station</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>Like</ContextMenuItem>
<ContextMenuItem>Share</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
<div className="space-y-1 text-sm">
<h3 className="font-medium leading-none">{album.name}</h3>
<p className="text-xs text-muted-foreground">{album.artist}</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,178 @@
import { Globe, Mic } from "lucide-react"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar"
export function Menu() {
return (
<Menubar className="rounded-none border-b border-none px-2 lg:px-4">
<MenubarMenu>
<MenubarTrigger className="font-bold">Music</MenubarTrigger>
<MenubarContent>
<MenubarItem>About Music</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Preferences... <MenubarShortcut>,</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Hide Music... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Hide Others... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarShortcut />
<MenubarItem>
Quit Music <MenubarShortcut>Q</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger className="relative">File</MenubarTrigger>
<MenubarContent>
<MenubarSub>
<MenubarSubTrigger>New</MenubarSubTrigger>
<MenubarSubContent className="w-[230px]">
<MenubarItem>
Playlist <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Playlist from Selection <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Smart Playlist... <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>Playlist Folder</MenubarItem>
<MenubarItem disabled>Genius Playlist</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Open Stream URL... <MenubarShortcut>U</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Close Window <MenubarShortcut>W</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Library</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Update Cloud Library</MenubarItem>
<MenubarItem>Update Genius</MenubarItem>
<MenubarSeparator />
<MenubarItem>Organize Library...</MenubarItem>
<MenubarItem>Export Library...</MenubarItem>
<MenubarSeparator />
<MenubarItem>Import Playlist...</MenubarItem>
<MenubarItem disabled>Export Playlist...</MenubarItem>
<MenubarItem>Show Duplicate Items</MenubarItem>
<MenubarSeparator />
<MenubarItem>Get Album Artwork</MenubarItem>
<MenubarItem disabled>Get Track Names</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Import... <MenubarShortcut>O</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>Burn Playlist to Disc...</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Show in Finder <MenubarShortcut>R</MenubarShortcut>{" "}
</MenubarItem>
<MenubarItem>Convert</MenubarItem>
<MenubarSeparator />
<MenubarItem>Page Setup...</MenubarItem>
<MenubarItem disabled>
Print... <MenubarShortcut>P</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem disabled>
Undo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Redo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled>
Cut <MenubarShortcut>X</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Copy <MenubarShortcut>C</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Paste <MenubarShortcut>V</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Select All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Deselect All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Smart Dictation...{" "}
<MenubarShortcut>
<Mic className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Emoji & Symbols{" "}
<MenubarShortcut>
<Globe className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem>Show Playing Next</MenubarCheckboxItem>
<MenubarCheckboxItem checked>Show Lyrics</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarItem inset disabled>
Show Status Bar
</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Hide Sidebar</MenubarItem>
<MenubarItem disabled inset>
Enter Full Screen
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger className="hidden md:block">Account</MenubarTrigger>
<MenubarContent forceMount>
<MenubarLabel inset>Switch Account</MenubarLabel>
<MenubarSeparator />
<MenubarRadioGroup value="benoit">
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarItem inset>Manage Famliy...</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Add Account...</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}

View File

@@ -0,0 +1,53 @@
import { Plus, Podcast } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export function PodcastEmptyPlaceholder() {
return (
<div className="flex h-[450px] shrink-0 items-center justify-center rounded-md border border-dashed">
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
<Podcast className="h-10 w-10 text-muted-foreground" />
<h3 className="mt-4 text-lg font-semibold">No episodes added</h3>
<p className="mb-4 mt-2 text-sm text-muted-foreground">
You have not added any podcasts. Add one below.
</p>
<Dialog>
<DialogTrigger>
<Button size="sm" className="relative">
<Plus className="mr-2 h-4 w-4" />
Add Podcast
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add Podcast</DialogTitle>
<DialogDescription>
Copy and paste the podcast feed URL to import.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="url">Podcast URL</Label>
<Input id="url" placeholder="https://example.com/feed.xml" />
</div>
</div>
<DialogFooter>
<Button>Import Podcast</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
)
}

View File

@@ -0,0 +1,99 @@
import {
LayoutGrid,
Library,
ListMusic,
Mic2,
Music,
Music2,
PlayCircle,
Radio,
User,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Playlist } from "../data/playlists"
interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
playlists: Playlist[]
}
export function Sidebar({ className, playlists }: SidebarProps) {
return (
<div className={cn("pb-12", className)}>
<div className="space-y-4 py-4">
<div className="px-4 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Discover
</h2>
<div className="space-y-1">
<Button
variant="secondary"
size="sm"
className="w-full justify-start"
>
<PlayCircle className="mr-2 h-4 w-4" />
Listen Now
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<LayoutGrid className="mr-2 h-4 w-4" />
Browse
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<Radio className="mr-2 h-4 w-4" />
Radio
</Button>
</div>
</div>
<div className="px-4 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Library
</h2>
<div className="space-y-1">
<Button variant="ghost" size="sm" className="w-full justify-start">
<ListMusic className="mr-2 h-4 w-4" />
Playlists
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<Music2 className="mr-2 h-4 w-4" />
Songs
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<User className="mr-2 h-4 w-4" />
Made for You
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<Mic2 className="mr-2 h-4 w-4" />
Artists
</Button>
<Button variant="ghost" size="sm" className="w-full justify-start">
<Library className="mr-2 h-4 w-4" />
Albums
</Button>
</div>
</div>
<div className="py-2">
<h2 className="relative px-6 text-lg font-semibold tracking-tight">
Playlists
</h2>
<ScrollArea className="h-[300px] px-2">
<div className="space-y-1 p-2">
{playlists?.map((playlist) => (
<Button
variant="ghost"
size="sm"
className="w-full justify-start font-normal"
>
<ListMusic className="mr-2 h-4 w-4" />
{playlist}
</Button>
))}
</div>
</ScrollArea>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,71 @@
export interface Album {
name: string
artist: string
cover: string
}
export const listenNowAlbums: Album[] = [
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1611348586804-61bf6c080437?w=300&dpr=2&q=80",
},
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1468817814611-b7edf94b5d60?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1528143358888-6d3c7f67bd5d?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1490300472339-79e4adc6be4a?w=300&dpr=2&q=80",
},
]
export const madeForYouAlbums: Album[] = [
{
name: "Thinking Components",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1615247001958-f4bc92fa6a4a?w=300&dpr=2&q=80",
},
{
name: "Functional Fury",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1513745405825-efaf9a49315f?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1614113489855-66422ad300a4?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1446185250204-f94591f7d702?w=300&dpr=2&q=80",
},
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1468817814611-b7edf94b5d60?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1490300472339-79e4adc6be4a?w=300&dpr=2&q=80",
},
]

View File

@@ -0,0 +1,16 @@
export type Playlist = typeof playlists[number]
export const playlists = [
"Recently Added",
"Recently Played",
"Top Songs",
"Top Albums",
"Top Artists",
"Logic Discography",
"Bedtime Beats",
"Feeling Happy",
"I miss Y2K Pop",
"Runtober",
"Mellow Days",
"Eminem Essentials",
]

View File

@@ -0,0 +1,154 @@
import { Metadata } from "next"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { Separator } from "@/components/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { AlbumArtwork } from "./components/album-artwork"
import { Menu } from "./components/menu"
import { PodcastEmptyPlaceholder } from "./components/podcast-empty-placeholder"
import { Sidebar } from "./components/sidebar"
import { listenNowAlbums, madeForYouAlbums } from "./data/albums"
import { playlists } from "./data/playlists"
import "./styles.css"
import Image from "next/image"
import { PlusCircle } from "lucide-react"
import { Button } from "@/components/ui/button"
export const metadata: Metadata = {
title: "Music App",
description: "Example music app using the components.",
}
export default function MusicPage() {
return (
<>
<div className="md:hidden">
<Image
src="/examples/music-light.png"
width={1280}
height={1114}
alt="Music"
className="block dark:hidden"
/>
<Image
src="/examples/music-dark.png"
width={1280}
height={1114}
alt="Music"
className="hidden dark:block"
/>
</div>
<div className="hidden md:block">
<Menu />
<div className="border-t">
<div className="bg-background">
<div className="grid lg:grid-cols-5">
<Sidebar playlists={playlists} className="hidden lg:block" />
<div className="col-span-3 lg:col-span-4 lg:border-l">
<div className="h-full px-4 py-6 lg:px-8">
<Tabs defaultValue="music" className="h-full space-y-6">
<div className="space-between flex items-center">
<TabsList>
<TabsTrigger value="music" className="relative">
Music
</TabsTrigger>
<TabsTrigger value="podcasts">Podcasts</TabsTrigger>
<TabsTrigger value="live" disabled>
Live
</TabsTrigger>
</TabsList>
<div className="ml-auto mr-4">
<Button>
<PlusCircle className="mr-2 h-4 w-4" />
Add music
</Button>
</div>
</div>
<TabsContent
value="music"
className="border-none p-0 outline-none"
>
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Listen Now
</h2>
<p className="text-sm text-muted-foreground">
Top picks for you. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<div className="relative">
<ScrollArea>
<div className="flex space-x-4 pb-4">
{listenNowAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[250px]"
aspectRatio="portrait"
width={250}
height={330}
/>
))}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
<div className="mt-6 space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Made for You
</h2>
<p className="text-sm text-muted-foreground">
Your personal playlists. Updated daily.
</p>
</div>
<Separator className="my-4" />
<div className="relative">
<ScrollArea>
<div className="flex space-x-4 pb-4">
{madeForYouAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[150px]"
aspectRatio="square"
width={150}
height={150}
/>
))}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
</TabsContent>
<TabsContent
value="podcasts"
className="h-full flex-col border-none p-0 data-[state=active]:flex"
>
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
New Episodes
</h2>
<p className="text-sm text-muted-foreground">
Your favorite podcasts. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<PodcastEmptyPlaceholder />
</TabsContent>
</Tabs>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,65 @@
[data-section="music"] {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 243 5% 96%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 349 89% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 243 11% 4%;
--secondary-foreground: 0 0% 98%;
--accent: 243 11% 4%;
--accent-foreground: 0 0% 100%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 349 89% 60%;
--radius: 0.5rem;
}
.dark [data-section="music"] {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 349 89% 60%;
--primary-foreground: 0 0% 100%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}

View File

@@ -1,5 +1,3 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Dialog,
@@ -14,7 +12,7 @@ export function CodeViewer() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="subtle">View code</Button>
<Button variant="secondary">View code</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[625px]">
<DialogHeader>
@@ -27,7 +25,7 @@ export function CodeViewer() {
<div className="grid gap-4">
<div className="rounded-md bg-black p-6">
<pre>
<code className="grid gap-1 text-sm text-slate-50 [&_span]:h-4">
<code className="grid gap-1 text-sm text-muted-foreground [&_span]:h-4">
<span>
<span className="text-sky-300">import</span> os
</span>
@@ -78,7 +76,7 @@ export function CodeViewer() {
</pre>
</div>
<div>
<p className="text-sm text-slate-500 dark:text-slate-400">
<p className="text-sm text-muted-foreground">
Your API Key can be found here. You should use environment
variables or a secret management tool to expose your key to your
applications.

View File

@@ -7,7 +7,6 @@ import {
ClipboardCheck,
Copy,
CreditCard,
EggFried,
Fingerprint,
HelpCircle,
Laptop,

View File

@@ -25,7 +25,7 @@ export function MaxLengthSelector({ defaultValue }: MaxLengthSelectorProps) {
<div className="grid gap-4">
<div className="flex items-center justify-between">
<Label htmlFor="maxlength">Maximum Length</Label>
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-slate-600 hover:border-slate-100 dark:text-slate-400 dark:hover:border-slate-800">
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-muted-foreground hover:border-border">
{value}
</span>
</div>

View File

@@ -1,13 +1,11 @@
"use client"
import * as React from "react"
import { useMutationObserver } from "@/hooks/use-mutation-observer"
import { Model } from "@/types"
import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { ModelType } from "@/data/models"
import { cn } from "@/lib/utils"
import { useMutationObserver } from "@/hooks/use-mutation-observer"
import { Button } from "@/components/ui/button"
import {
Command,
@@ -29,6 +27,8 @@ import {
PopoverTrigger,
} from "@/components/ui/popover"
import { Model, ModelType } from "../data/models"
interface ModelSelectorProps extends PopoverProps {
types: readonly ModelType[]
models: Model[]
@@ -77,7 +77,7 @@ export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
>
<div className="grid gap-2">
<h4 className="font-medium leading-none">{peekedModel.name}</h4>
<div className="text-sm text-slate-500 dark:text-slate-400">
<div className="text-sm text-muted-foreground">
{peekedModel.description}
</div>
{peekedModel.strengths ? (
@@ -85,7 +85,7 @@ export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
<h5 className="text-sm font-medium leading-none">
Strengths
</h5>
<ul className="text-sm text-slate-500 dark:text-slate-400">
<ul className="text-sm text-muted-foreground">
{peekedModel.strengths}
</ul>
</div>
@@ -135,12 +135,12 @@ function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
const ref = React.useRef<HTMLDivElement>(null)
useMutationObserver(ref, (mutations) => {
const mutation = mutations.find(
(mutation) => mutation.attributeName === "aria-selected"
)
if (mutation && mutation.target?.getAttribute("aria-selected")) {
onPeek(model)
for (const mutation of mutations) {
if (mutation.type === "attributes") {
if (mutation.attributeName === "aria-selected") {
onPeek(model)
}
}
}
})
@@ -149,7 +149,7 @@ function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
key={model.id}
onSelect={onSelect}
ref={ref}
className="aria-selected:bg-slate-900 aria-selected:text-slate-50"
className="aria-selected:bg-primary aria-selected:text-primary-foreground"
>
{model.name}
<Check

View File

@@ -1,7 +1,6 @@
"use client"
import * as React from "react"
import { toast } from "@/hooks/use-toast"
import { Dialog } from "@radix-ui/react-dialog"
import { Flag, MoreHorizontal, Trash } from "lucide-react"
@@ -31,6 +30,7 @@ import {
} from "@/components/ui/dropdown-menu"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { toast } from "@/components/ui/use-toast"
export function PresetActions() {
const [open, setIsOpen] = React.useState(false)
@@ -40,7 +40,7 @@ export function PresetActions() {
<>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="subtle">
<Button variant="secondary">
<span className="sr-only">Actions</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
@@ -71,7 +71,7 @@ export function PresetActions() {
</DialogDescription>
</DialogHeader>
<div className="py-6">
<h4 className="text-sm text-slate-500 dark:text-slate-400">
<h4 className="text-sm text-muted-foreground">
Playground Warnings
</h4>
<div className="flex items-start justify-between space-x-4 pt-3">
@@ -80,7 +80,7 @@ export function PresetActions() {
<span className="font-semibold">
Show a warning when content is flagged
</span>
<span className="text-sm text-slate-500 dark:text-slate-400">
<span className="text-sm text-muted-foreground">
A warning will be shown when sexual, hateful, violent or
self-harm content is detected.
</span>
@@ -88,7 +88,7 @@ export function PresetActions() {
</div>
</div>
<DialogFooter>
<Button variant="subtle" onClick={() => setIsOpen(false)}>
<Button variant="secondary" onClick={() => setIsOpen(false)}>
Close
</Button>
</DialogFooter>

View File

@@ -1,5 +1,3 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Dialog,
@@ -17,7 +15,7 @@ export function PresetSave() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="subtle">Save</Button>
<Button variant="secondary">Save</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[475px]">
<DialogHeader>

View File

@@ -1,9 +1,7 @@
"use client"
import * as React from "react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { Preset } from "@/types"
import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
@@ -22,6 +20,8 @@ import {
PopoverTrigger,
} from "@/components/ui/popover"
import { Preset } from "../data/presets"
interface PresetSelectorProps extends PopoverProps {
presets: Preset[]
}
@@ -39,7 +39,7 @@ export function PresetSelector({ presets, ...props }: PresetSelectorProps) {
role="combobox"
aria-label="Load a preset..."
aria-expanded={open}
className="max-w-[300px] flex-1 justify-between"
className="flex-1 justify-between md:max-w-[200px] lg:max-w-[300px]"
>
{selectedPreset ? selectedPreset.name : "Load a preset..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />

View File

@@ -1,18 +1,6 @@
"use client"
import { PopoverProps } from "@radix-ui/react-popover"
import { Clipboard, Copy } from "lucide-react"
import { Copy } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
@@ -25,14 +13,12 @@ export function PresetShare() {
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="subtle">Share</Button>
<Button variant="secondary">Share</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-[520px]">
<div className="flex flex-col space-y-2 text-center sm:text-left">
<h3 className="text-lg font-semibold text-slate-900 dark:text-slate-50">
Share preset
</h3>
<p className="text-sm text-slate-500 dark:text-slate-400">
<h3 className="text-lg font-semibold">Share preset</h3>
<p className="text-sm text-muted-foreground">
Anyone who has this link and an OpenAI account will be able to view
this.
</p>

View File

@@ -27,7 +27,7 @@ export function TemperatureSelector({
<div className="grid gap-4">
<div className="flex items-center justify-between">
<Label htmlFor="temperature">Temperature</Label>
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-slate-600 hover:border-slate-100 dark:text-slate-400 dark:hover:border-slate-800">
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-muted-foreground hover:border-border">
{value}
</span>
</div>

View File

@@ -25,7 +25,7 @@ export function TopPSelector({ defaultValue }: TopPSelectorProps) {
<div className="grid gap-4">
<div className="flex items-center justify-between">
<Label htmlFor="top-p">Top P</Label>
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-slate-600 hover:border-slate-100 dark:text-slate-400 dark:hover:border-slate-800">
<span className="w-12 rounded-md border border-transparent px-2 py-0.5 text-right text-sm text-muted-foreground hover:border-border">
{value}
</span>
</div>

View File

@@ -1,9 +1,15 @@
import { Model } from "@/types"
export const types = ["GPT-3", "Codex"] as const
export type ModelType = typeof types[number]
export interface Model<Type = string> {
id: string
name: string
description: string
strengths?: string
type: Type
}
export const models: Model<ModelType>[] = [
{
id: "c305f976-8e38-42b1-9fb7-d21b2e34f0da",

View File

@@ -1,4 +1,7 @@
import { Preset } from "@/types"
export interface Preset {
id: string
name: string
}
export const presets: Preset[] = [
{

View File

@@ -0,0 +1,178 @@
import { Metadata } from "next"
import { History } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Textarea } from "@/components/ui/textarea"
import { CodeViewer } from "./components/code-viewer"
import { Icons } from "./components/icons"
import { MaxLengthSelector } from "./components/maxlength-selector"
import { ModelSelector } from "./components/model-selector"
import { PresetActions } from "./components/preset-actions"
import { PresetSave } from "./components/preset-save"
import { PresetSelector } from "./components/preset-selector"
import { PresetShare } from "./components/preset-share"
import { TemperatureSelector } from "./components/temperature-selector"
import { TopPSelector } from "./components/top-p-selector"
import { models, types } from "./data/models"
import { presets } from "./data/presets"
import "./styles.css"
import Image from "next/image"
export const metadata: Metadata = {
title: "Playground",
description: "The OpenAI Playground built using the components.",
}
export default function PlaygroundPage() {
return (
<>
<div className="md:hidden">
<Image
src="/examples/playground-light.png"
width={1280}
height={916}
alt="Playground"
className="block dark:hidden"
/>
<Image
src="/examples/playground-dark.png"
width={1280}
height={916}
alt="Playground"
className="hidden dark:block"
/>
</div>
<div className="hidden h-full flex-col md:flex">
<div className="container flex flex-col items-start justify-between space-y-2 py-4 sm:flex-row sm:items-center sm:space-y-0 md:h-16">
<h2 className="text-lg font-semibold">Playground</h2>
<div className="ml-auto flex w-full space-x-2 sm:justify-end">
<PresetSelector presets={presets} />
<PresetSave />
<div className="hidden space-x-2 md:flex">
<CodeViewer />
<PresetShare />
</div>
<PresetActions />
</div>
</div>
<Separator />
<Tabs defaultValue="complete" className="flex-1">
<div className="container h-full py-6">
<div className="grid h-full items-stretch gap-6 md:grid-cols-[1fr_200px]">
<div className="hidden flex-col space-y-4 sm:flex md:order-2">
<div className="grid gap-2">
<HoverCard openDelay={200}>
<HoverCardTrigger asChild>
<span className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
Mode
</span>
</HoverCardTrigger>
<HoverCardContent className="w-[320px] text-sm" side="left">
Choose the interface that best suits your task. You can
provide: a simple prompt to complete, starting and ending
text to insert a completion within, or some text with
instructions to edit it.
</HoverCardContent>
</HoverCard>
<TabsList className="grid grid-cols-3">
<TabsTrigger value="complete">
<span className="sr-only">Complete</span>
<Icons.completeMode className="h-5 w-5" />
</TabsTrigger>
<TabsTrigger value="insert">
<span className="sr-only">Insert</span>
<Icons.insertMode className="h-5 w-5" />
</TabsTrigger>
<TabsTrigger value="edit">
<span className="sr-only">Edit</span>
<Icons.editMode className="h-5 w-5" />
</TabsTrigger>
</TabsList>
</div>
<ModelSelector types={types} models={models} />
<TemperatureSelector defaultValue={[0.56]} />
<MaxLengthSelector defaultValue={[256]} />
<TopPSelector defaultValue={[0.9]} />
</div>
<div className="md:order-1">
<TabsContent value="complete" className="mt-0 border-0 p-0">
<div className="flex h-full flex-col space-y-4">
<Textarea
placeholder="Write a tagline for an ice cream shop"
className="min-h-[400px] flex-1 p-4 md:min-h-[700px] lg:min-h-[700px]"
/>
<div className="flex items-center space-x-2">
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<History className="h-4 w-4" />
</Button>
</div>
</div>
</TabsContent>
<TabsContent value="insert" className="mt-0 border-0 p-0">
<div className="flex flex-col space-y-4">
<div className="grid h-full grid-rows-2 gap-6 lg:grid-cols-2 lg:grid-rows-1">
<Textarea
placeholder="We're writing to [inset]. Congrats from OpenAI!"
className="h-full min-h-[300px] lg:min-h-[700px] xl:min-h-[700px]"
/>
<div className="rounded-md border bg-muted"></div>
</div>
<div className="flex items-center space-x-2">
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<History className="h-4 w-4" />
</Button>
</div>
</div>
</TabsContent>
<TabsContent value="edit" className="mt-0 border-0 p-0">
<div className="flex flex-col space-y-4">
<div className="grid h-full gap-6 lg:grid-cols-2">
<div className="flex flex-col space-y-4">
<div className="flex flex-1 flex-col space-y-2">
<Label htmlFor="input">Input</Label>
<Textarea
id="input"
placeholder="We is going to the market."
className="flex-1 lg:min-h-[580px]"
/>
</div>
<div className="flex flex-col space-y-2">
<Label htmlFor="instructions">Instructions</Label>
<Textarea
id="instructions"
placeholder="Fix the grammar."
/>
</div>
</div>
<div className="mt-[21px] min-h-[400px] rounded-md border bg-muted lg:min-h-[700px]" />
</div>
<div className="flex items-center space-x-2">
<Button>Submit</Button>
<Button variant="secondary">
<span className="sr-only">Show history</span>
<History className="h-4 w-4" />
</Button>
</div>
</div>
</TabsContent>
</div>
</div>
</div>
</Tabs>
</div>
</>
)
}

View File

@@ -0,0 +1,65 @@
[data-section="playground"] {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 154 79% 96%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 143 72% 29%;
--primary-foreground: 141 75% 97%;
--secondary: 145 80% 10%;
--secondary-foreground: 141 75% 97%;
--accent: 154 79% 96%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 158 64% 52%;
--radius: 0rem;
}
.dark [data-section="playground"] {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 143 72% 29%;
--primary-foreground: 141 75% 97%;
--secondary: 145 80% 10%;
--secondary-foreground: 141 75% 97%;
--accent: 154 79% 96%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 158 64% 52%;
--radius: 0rem;
}

View File

@@ -1,33 +1,43 @@
import { Metadata } from "next"
import Link from "next/link"
import { Heart } from "lucide-react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { buttonVariants } from "@/components/ui/button"
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
export const metadata: Metadata = {
title: "Figma",
description:
"Every component recreated in Figma. With customizable props, typography and icons.",
}
export default function FigmaPage() {
return (
<>
<section className="grid items-center gap-6 pt-6 pb-8 md:py-10">
<div className="flex max-w-[980px] flex-col items-start gap-2">
<h1 className="text-3xl font-extrabold leading-tight tracking-tighter md:text-5xl lg:text-6xl lg:leading-[1.1]">
Figma UI Kit. Crafted to perfectly match the Radix UI components.
</h1>
<p className="max-w-[750px] text-lg text-slate-700 dark:text-slate-400 sm:text-xl">
Every component recreated in Figma. With customizable props,
typography and icons. Open sourced by{" "}
<Link
href="https://twitter.com/skirano"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-4"
>
Pietro Schirano
</Link>
.
</p>
</div>
<div className="flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4 md:flex-row">
<div className="container pb-10">
<PageHeader>
<PageHeaderHeading>Grab the free Figma UI Kit.</PageHeaderHeading>
<PageHeaderDescription>
Every component recreated in Figma. With customizable props,
typography and icons. Open sourced by{" "}
<Link
href="https://twitter.com/skirano"
target="_blank"
rel="noreferrer"
className="font-medium underline underline-offset-4"
>
Pietro Schirano
</Link>
.
</PageHeaderDescription>
</PageHeader>
<section className="mb-4 grid items-center gap-6">
<div className="flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0 md:flex-row">
<Link
href="https://www.figma.com/community/file/1203061493325953101"
className={buttonVariants({ size: "lg" })}
@@ -53,9 +63,9 @@ export default function FigmaPage() {
<AspectRatio ratio={16 / 9} className="w-full">
<iframe
src="https://embed.figma.com/file/1203061493325953101/hf_embed?community_viewer=true&embed_host=shadcn&hub_file_id=1203061493325953101&kind=&viewer=1"
className="h-full w-full overflow-hidden rounded-lg border border-slate-200 bg-slate-100 dark:border-slate-700 dark:bg-slate-800"
className="h-full w-full overflow-hidden rounded-lg border bg-muted"
/>
</AspectRatio>
</>
</div>
)
}

View File

@@ -1,43 +0,0 @@
import { siteConfig } from "@/config/site"
export default function Head() {
const url = process.env.NEXT_PUBLIC_APP_URL
const ogUrl = new URL(`${url}/og.jpg`)
return (
<>
<title>{`${siteConfig.name} - ${siteConfig.description}`}</title>
<meta charSet="utf-8" />
<meta name="description" content={siteConfig.description} />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<meta property="og:type" content="website" />
<meta property="og:title" content={siteConfig.name} />
<meta property="og:description" content={siteConfig.description} />
<meta property="og:url" content={url?.toString()} />
<meta property="og:image" content={ogUrl.toString()} />
<meta name="twitter:title" content={siteConfig.name} />
<meta name="twitter:description" content={siteConfig.description} />
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={url?.toString()} />
<meta name="twitter:image" content={ogUrl.toString()} />
</>
)
}

View File

@@ -1,19 +1,71 @@
import { Inter as FontSans } from "next/font/google"
import "@/styles/globals.css"
import { Metadata } from "next"
import { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils"
import { Toaster } from "@/components/ui/toaster"
import { Analytics } from "@/components/analytics"
import { SiteFooter } from "@/components/site-footer"
import { SiteHeader } from "@/components/site-header"
import { StyleSwitcher } from "@/components/style-switcher"
import { TailwindIndicator } from "@/components/tailwind-indicator"
import { ThemeProvider } from "@/components/theme-provider"
import { Toaster } from "@/components/ui/toaster"
const fontSans = FontSans({
subsets: ["latin"],
variable: "--font-sans",
display: "swap",
})
export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s - ${siteConfig.name}`,
},
description: siteConfig.description,
keywords: [
"Next.js",
"React",
"Tailwind CSS",
"Server Components",
"Radix UI",
],
authors: [
{
name: "shadcn",
url: "https://shadcn.com",
},
],
creator: "shadcn",
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "white" },
{ media: "(prefers-color-scheme: dark)", color: "black" },
],
openGraph: {
type: "website",
locale: "en_US",
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
siteName: siteConfig.name,
images: [
{
url: siteConfig.ogImage,
width: 1200,
height: 630,
alt: siteConfig.name,
},
],
},
twitter: {
card: "summary_large_image",
title: siteConfig.name,
description: siteConfig.description,
images: [siteConfig.ogImage],
creator: "@shadcn",
},
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png",
},
manifest: `${siteConfig.url}/site.webmanifest`,
}
interface RootLayoutProps {
children: React.ReactNode
@@ -26,18 +78,19 @@ export default function RootLayout({ children }: RootLayoutProps) {
<head />
<body
className={cn(
"min-h-screen bg-white font-sans text-slate-900 antialiased dark:bg-slate-900 dark:text-slate-50",
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<div className="flex min-h-screen flex-col">
<div className="relative flex min-h-screen flex-col">
<SiteHeader />
<div className="container flex-1">{children}</div>
<div className="flex-1">{children}</div>
<SiteFooter />
</div>
<TailwindIndicator />
</ThemeProvider>
<StyleSwitcher />
<Analytics />
<Toaster />
</body>

View File

@@ -1,67 +1,74 @@
import Image from "next/image"
import Link from "next/link"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { AppleMusicDemo } from "@/components/apple-music-demo"
import { CopyButton } from "@/components/copy-button"
import { Icons } from "@/components/icons"
import { PromoVideo } from "@/components/promo-video"
import { buttonVariants } from "@/components/ui/button"
import { ExamplesNav } from "@/components/examples-nav"
import { Icons } from "@/components/icons"
import {
PageHeader,
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { PromoVideo } from "@/components/promo-video"
import { StyleSwitcher } from "@/components/style-switcher"
import DashboardPage from "@/app/examples/dashboard/page"
export default function IndexPage() {
return (
<>
<section className="grid items-center gap-6 pt-6 pb-8 md:py-10">
<div className="flex max-w-[980px] flex-col items-start gap-2">
<h1 className="text-3xl font-extrabold leading-tight tracking-tighter md:text-5xl lg:text-6xl lg:leading-[1.1]">
Beautifully designed components <br className="hidden sm:inline" />
built with Radix UI and Tailwind CSS.
</h1>
<p className="max-w-[750px] text-lg text-slate-700 dark:text-slate-400 sm:text-xl">
Accessible and customizable components that you can copy and paste
into your apps. Free. Open Source.{" "}
<span className="font-semibold">
Use this to build your own component library
</span>
.
</p>
</div>
<div className="block lg:hidden">
<PromoVideo />
</div>
<div className="flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4 md:flex-row">
<Link href="/docs" className={buttonVariants({ size: "lg" })}>
Get Started
</Link>
<Link
target="_blank"
rel="noreferrer"
href={siteConfig.links.github}
className={cn(
buttonVariants({ variant: "outline", size: "lg" }),
"pl-6"
)}
>
<Icons.gitHub className="mr-2 h-4 w-4" />
GitHub
</Link>
</div>
<div>
<p className="text-sm text-slate-500 dark:text-slate-400">
You are looking at an early preview. You can follow the progress on{" "}
<Link
href={siteConfig.links.twitter}
className="font-medium underline underline-offset-4"
>
Twitter
<div className="container relative pb-10">
<StyleSwitcher />
<PageHeader>
<PageHeaderHeading>Build your component library.</PageHeaderHeading>
<PageHeaderDescription>
Beautifully designed components that you can copy and paste into your
apps. Accessible. Customizable. Open Source.
</PageHeaderDescription>
</PageHeader>
<section className="pb-8 md:pb-10">
<div className="flex w-full items-center justify-between">
<div className="flex space-x-4">
<Link href="/docs" className={cn(buttonVariants({ size: "lg" }))}>
Get Started
</Link>
.
</p>
<Link
target="_blank"
rel="noreferrer"
href={siteConfig.links.github}
className={cn(
buttonVariants({ variant: "outline", size: "lg" }),
"pl-6"
)}
>
<Icons.gitHub className="mr-2 h-4 w-4" />
GitHub
</Link>
</div>
</div>
</section>
<section className="hidden lg:block">
<AppleMusicDemo />
<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">
<Image
src="/examples/dashboard-light.png"
width={1280}
height={866}
alt="Dashboard"
className="block dark:hidden"
/>
<Image
src="/examples/dashboard-dark.png"
width={1280}
height={866}
alt="Dashboard"
className="hidden dark:block"
/>
</section>
</>
<section className="hidden md:block">
<div className="overflow-hidden rounded-lg border bg-background shadow-xl">
<DashboardPage />
</div>
</section>
</div>
)
}

201
apps/www/app/sink/page.tsx Normal file
View File

@@ -0,0 +1,201 @@
import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import { AccordionDemo } from "@/components/examples/accordion/demo"
import { AlertDialogDemo } from "@/components/examples/alert-dialog/demo"
import { AspectRatioDemo } from "@/components/examples/aspect-ratio/demo"
import { AvatarDemo } from "@/components/examples/avatar/demo"
import { BadgeDemo } from "@/components/examples/badge/demo"
import { BadgeDestructive } from "@/components/examples/badge/destructive"
import { BadgeOutline } from "@/components/examples/badge/outline"
import { BadgeSecondary } from "@/components/examples/badge/secondary"
import { ButtonDemo } from "@/components/examples/button/demo"
import { ButtonDestructive } from "@/components/examples/button/destructive"
import { ButtonGhost } from "@/components/examples/button/ghost"
import { ButtonLink } from "@/components/examples/button/link"
import { ButtonLoading } from "@/components/examples/button/loading"
import { ButtonOutline } from "@/components/examples/button/outline"
import { ButtonSecondary } from "@/components/examples/button/secondary"
import { ButtonWithIcon } from "@/components/examples/button/with-icon"
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker"
import { CardDemo } from "@/components/examples/card/demo"
import { CheckboxDemo } from "@/components/examples/checkbox/demo"
import { CollapsibleDemo } from "@/components/examples/collapsible/demo"
import { CommandDemo } from "@/components/examples/command/demo"
import { ContextMenuDemo } from "@/components/examples/context-menu/demo"
import { DialogDemo } from "@/components/examples/dialog/demo"
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo"
import { HoverCardDemo } from "@/components/examples/hover-card/demo"
import { MenubarDemo } from "@/components/examples/menubar/demo"
import { NavigationMenuDemo } from "@/components/examples/navigation-menu/demo"
import { PopoverDemo } from "@/components/examples/popover/demo"
import { ProgressDemo } from "@/components/examples/progress/demo"
import { RadioGroupDemo } from "@/components/examples/radio-group/demo"
import { ScrollAreaDemo } from "@/components/examples/scroll-area/demo"
import { SelectDemo } from "@/components/examples/select/demo"
import { SeparatorDemo } from "@/components/examples/separator/demo"
import { SheetDemo } from "@/components/examples/sheet/demo"
import { SkeletonDemo } from "@/components/examples/skeleton/demo"
import { SliderDemo } from "@/components/examples/slider/demo"
import { SwitchDemo } from "@/components/examples/switch/demo"
import { TabsDemo } from "@/components/examples/tabs/demo"
import { ToastDemo } from "@/components/examples/toast/demo"
import { ToggleDemo } from "@/components/examples/toggle/demo"
import { ToggleDisabled } from "@/components/examples/toggle/disabled"
import { ToggleOutline } from "@/components/examples/toggle/outline"
import { ToggleWithText } from "@/components/examples/toggle/with-text"
import { TooltipDemo } from "@/components/examples/tooltip/demo"
export default function KitchenSinkPage() {
return (
<div className="container">
<div className="grid gap-4 py-10">
<div className="grid grid-cols-3 items-start gap-4">
<div className="grid gap-4">
<ComponentWrapper>
<CardDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper>
<SliderDemo className="w-full" />
</ComponentWrapper>
<ComponentWrapper
className="spa flex-col items-start space-x-0
space-y-2"
>
<p className="text-foreground-muted text-sm">Documentation</p>
<p className="text-sm font-medium leading-none">
You can customize the theme using{" "}
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground">
CSS variables
</code>
.{" "}
<Link
href="#"
className="font-medium text-primary underline underline-offset-4"
>
Click here
</Link>{" "}
to learn more.
</p>
</ComponentWrapper>
<ComponentWrapper>
<CheckboxDemo />
<HoverCardDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<TabsDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<MenubarDemo />
<AvatarDemo />
</ComponentWrapper>
<ComponentWrapper className="flex-col items-start space-x-0 space-y-2">
<div className="flex space-x-2">
<ButtonDemo />
<ButtonSecondary />
<ButtonDestructive />
</div>
<div className="flex space-x-2">
<ButtonOutline />
<ButtonLink />
<ButtonGhost />
</div>
<div className="flex space-x-2">
<ButtonWithIcon />
<ButtonLoading />
</div>
</ComponentWrapper>
<ComponentWrapper>
<CalendarDatePicker />
</ComponentWrapper>
<ComponentWrapper>
<AccordionDemo />
</ComponentWrapper>
<ComponentWrapper className="[&_ul>li:last-child]:hidden">
<NavigationMenuDemo />
</ComponentWrapper>
<ComponentWrapper className="justify-between">
<SwitchDemo />
<SelectDemo />
</ComponentWrapper>
<ComponentWrapper>
<SeparatorDemo />
</ComponentWrapper>
<ComponentWrapper>
<AspectRatioDemo />
</ComponentWrapper>
<ComponentWrapper>
<PopoverDemo />
<ToastDemo />
</ComponentWrapper>
</div>
<div className="grid gap-4">
<ComponentWrapper>
<TooltipDemo />
<SheetDemo />
<ProgressDemo />
</ComponentWrapper>
<ComponentWrapper>
<CommandDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>span]:h-[80px] [&>span]:w-[200px]">
<RadioGroupDemo />
<ContextMenuDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<DropdownMenuDemo />
<AlertDialogDemo />
<DialogDemo />
</div>
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<BadgeDemo />
<BadgeSecondary />
<BadgeDestructive />
<BadgeOutline />
</div>
</ComponentWrapper>
<ComponentWrapper>
<SkeletonDemo />
</ComponentWrapper>
<ComponentWrapper className="[&>div]:w-full">
<CollapsibleDemo />
</ComponentWrapper>
<ComponentWrapper>
<div className="flex space-x-2">
<ToggleDemo />
<ToggleOutline />
<ToggleDisabled />
<ToggleWithText />
</div>
</ComponentWrapper>
<ComponentWrapper>
<ScrollAreaDemo />
</ComponentWrapper>
</div>
</div>
</div>
</div>
)
}
function ComponentWrapper({
className,
children,
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"flex items-center justify-between space-x-4 rounded-md border p-4",
className
)}
>
{children}
</div>
)
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,745 +0,0 @@
import * as React from "react"
import Image from "next/image"
import {
Album,
CreditCard,
Globe,
Keyboard,
LayoutGrid,
Library,
ListMusic,
LogOut,
Mail,
MessageSquare,
Mic,
Mic2,
Music,
Music2,
PlayCircle,
Plus,
PlusCircle,
Podcast,
Radio,
Settings,
User,
UserPlus,
Users,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { Separator } from "@/components/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
const playlists = [
"Recently Added",
"Recently Played",
"Top Songs",
"Top Albums",
"Top Artists",
"Logic Discography",
"Bedtime Beats",
"Feeling Happy",
"I miss Y2K Pop",
"Runtober",
"Mellow Days",
"Eminem Essentials",
]
interface Album {
name: string
artist: string
cover: string
}
const listenNowAlbums: Album[] = [
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1547355253-ff0740f6e8c1?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1598295893369-1918ffaf89a2?w=300&dpr=2&q=80",
},
]
const madeForYouAlbums: Album[] = [
{
name: "Async Awakenings",
artist: "Nina Netcode",
cover:
"https://images.unsplash.com/photo-1580428180098-24b353d7e9d9?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "Stateful Symphony",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1598062548091-a6fb6a052562?w=300&dpr=2&q=80",
},
{
name: "The Art of Reusability",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1626759486966-c067e3f79982?w=300&dpr=2&q=80",
},
{
name: "Thinking Components",
artist: "Lena Logic",
cover:
"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=300&dpr=2&q=80",
},
{
name: "Functional Fury",
artist: "Beth Binary",
cover:
"https://images.unsplash.com/photo-1606542758304-820b04394ac2?w=300&dpr=2&q=80",
},
{
name: "React Rendezvous",
artist: "Ethan Byte",
cover:
"https://images.unsplash.com/photo-1598295893369-1918ffaf89a2?w=300&dpr=2&q=80",
},
]
export function AppleMusicDemo() {
return (
<div className="overflow-hidden rounded-md border border-slate-200 bg-gradient-to-b from-rose-500 to-indigo-700 shadow-2xl dark:border-slate-800">
<Menubar className="rounded-none border-b border-none dark:bg-slate-900">
<MenubarMenu>
<MenubarTrigger className="font-bold">Music</MenubarTrigger>
<MenubarContent>
<MenubarItem>About Music</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Preferences... <MenubarShortcut>,</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Hide Music... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Hide Others... <MenubarShortcut>H</MenubarShortcut>
</MenubarItem>
<MenubarShortcut />
<MenubarItem>
Quit Music <MenubarShortcut>Q</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger className="relative">
File
<DemoIndicator />
</MenubarTrigger>
<MenubarContent>
<MenubarSub>
<MenubarSubTrigger>New</MenubarSubTrigger>
<MenubarSubContent className="w-[230px]">
<MenubarItem>
Playlist <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Playlist from Selection <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Smart Playlist... <MenubarShortcut>N</MenubarShortcut>
</MenubarItem>
<MenubarItem>Playlist Folder</MenubarItem>
<MenubarItem disabled>Genius Playlist</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Open Stream URL... <MenubarShortcut>U</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Close Window <MenubarShortcut>W</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Library</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Update Cloud Library</MenubarItem>
<MenubarItem>Update Genius</MenubarItem>
<MenubarSeparator />
<MenubarItem>Organize Library...</MenubarItem>
<MenubarItem>Export Library...</MenubarItem>
<MenubarSeparator />
<MenubarItem>Import Playlist...</MenubarItem>
<MenubarItem disabled>Export Playlist...</MenubarItem>
<MenubarItem>Show Duplicate Items</MenubarItem>
<MenubarSeparator />
<MenubarItem>Get Album Artwork</MenubarItem>
<MenubarItem disabled>Get Track Names</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarItem>
Import... <MenubarShortcut>O</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>Burn Playlist to Disc...</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Show in Finder <MenubarShortcut>R</MenubarShortcut>{" "}
</MenubarItem>
<MenubarItem>Convert</MenubarItem>
<MenubarSeparator />
<MenubarItem>Page Setup...</MenubarItem>
<MenubarItem disabled>
Print... <MenubarShortcut>P</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem disabled>
Undo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Redo <MenubarShortcut>Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled>
Cut <MenubarShortcut>X</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Copy <MenubarShortcut>C</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Paste <MenubarShortcut>V</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Select All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>
Deselect All <MenubarShortcut>A</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem>
Smart Dictation...{" "}
<MenubarShortcut>
<Mic className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Emoji & Symbols{" "}
<MenubarShortcut>
<Globe className="h-4 w-4" />
</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem>Show Playing Next</MenubarCheckboxItem>
<MenubarCheckboxItem checked>Show Lyrics</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarItem inset disabled>
Show Status Bar
</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Hide Sidebar</MenubarItem>
<MenubarItem disabled inset>
Enter Full Screen
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Account</MenubarTrigger>
<MenubarContent forceMount>
<MenubarLabel inset>Switch Account</MenubarLabel>
<MenubarSeparator />
<MenubarRadioGroup value="benoit">
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarItem inset>Manage Famliy...</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Add Account...</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
<div className="p-8">
<div className="rounded-md bg-white shadow-2xl transition-all dark:bg-slate-900">
<div className="grid grid-cols-4 xl:grid-cols-5">
<aside className="pb-12">
<div className="px-8 py-6">
<p className="flex items-center text-2xl font-semibold tracking-tight">
<Music className="mr-2" />
Music
</p>
</div>
<div className="space-y-4">
<div className="px-6 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Discover
</h2>
<div className="space-y-1">
<Button
variant="subtle"
size="sm"
className="w-full justify-start"
>
<PlayCircle className="mr-2 h-4 w-4" />
Listen Now
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<LayoutGrid className="mr-2 h-4 w-4" />
Browse
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Radio className="mr-2 h-4 w-4" />
Radio
</Button>
</div>
</div>
<div className="px-6 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight">
Library
</h2>
<div className="space-y-1">
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<ListMusic className="mr-2 h-4 w-4" />
Playlists
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Music2 className="mr-2 h-4 w-4" />
Songs
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<User className="mr-2 h-4 w-4" />
Made for You
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Mic2 className="mr-2 h-4 w-4" />
Artists
</Button>
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
>
<Library className="mr-2 h-4 w-4" />
Albums
</Button>
</div>
</div>
<div className="py-2">
<h2 className="relative px-8 text-lg font-semibold tracking-tight">
Playlists <DemoIndicator className="right-28" />
</h2>
<ScrollArea className="h-[230px] px-4">
<div className="space-y-1 p-2">
{playlists.map((playlist) => (
<Button
variant="ghost"
size="sm"
className="w-full justify-start font-normal"
>
<ListMusic className="mr-2 h-4 w-4" />
{playlist}
</Button>
))}
</div>
</ScrollArea>
</div>
</div>
</aside>
<div className="col-span-3 border-l border-l-slate-200 dark:border-l-slate-700 xl:col-span-4">
<div className="h-full px-8 py-6">
<Tabs defaultValue="music" className="h-full space-y-6">
<div className="space-between flex items-center">
<TabsList>
<TabsTrigger value="music" className="relative">
Music <DemoIndicator className="right-2" />
</TabsTrigger>
<TabsTrigger value="podcasts">Podcasts</TabsTrigger>
<TabsTrigger value="live" disabled>
Live
</TabsTrigger>
</TabsList>
<div className="ml-auto mr-4">
<h3 className="text-sm font-semibold">Welcome back</h3>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="relative h-10 w-10 rounded-full"
>
<Avatar>
<AvatarImage
src="https://github.com/shadcn.png"
alt="@shadcn"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
<DemoIndicator className="right-0 top-0" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-56"
align="end"
forceMount
>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
<DropdownMenuShortcut>P</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
<DropdownMenuShortcut>B</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
<DropdownMenuShortcut>S</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem>
<Keyboard className="mr-2 h-4 w-4" />
<span>Keyboard shortcuts</span>
<DropdownMenuShortcut>K</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Users className="mr-2 h-4 w-4" />
<span>Team</span>
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<UserPlus className="mr-2 h-4 w-4" />
<span>Invite users</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent forceMount>
<DropdownMenuItem>
<Mail className="mr-2 h-4 w-4" />
<span>Email</span>
</DropdownMenuItem>
<DropdownMenuItem>
<MessageSquare className="mr-2 h-4 w-4" />
<span>Message</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
<span>More...</span>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut className="mr-2 h-4 w-4" />
<span>Log out</span>
<DropdownMenuShortcut>Q</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<TabsContent value="music" className="border-none p-0">
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Listen Now
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Top picks for you. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<div className="relative">
<DemoIndicator className="right-auto left-24 top-32 z-30" />
<div className="relative flex space-x-4">
{listenNowAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[250px]"
/>
))}
</div>
</div>
<div className="mt-6 space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
Made for You
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Your personal playlists. Updated daily.
</p>
</div>
<Separator className="my-4" />
<div className="relative">
<DemoIndicator className="top-32 right-auto left-16 z-30" />
<ScrollArea>
<div className="flex space-x-4 pb-4">
{madeForYouAlbums.map((album) => (
<AlbumArtwork
key={album.name}
album={album}
className="w-[150px]"
aspectRatio={1 / 1}
/>
))}
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
</TabsContent>
<TabsContent
value="podcasts"
className="h-full flex-col border-none p-0 data-[state=active]:flex"
>
<div className="flex items-center justify-between">
<div className="space-y-1">
<h2 className="text-2xl font-semibold tracking-tight">
New Episodes
</h2>
<p className="text-sm text-slate-500 dark:text-slate-400">
Your favorite podcasts. Updated daily.
</p>
</div>
</div>
<Separator className="my-4" />
<div className="flex h-[450px] shrink-0 items-center justify-center rounded-md border border-dashed border-slate-200 dark:border-slate-700">
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
<Podcast className="h-10 w-10 text-slate-400" />
<h3 className="mt-4 text-lg font-semibold text-slate-900 dark:text-slate-50">
No episodes added
</h3>
<p className="mt-2 mb-4 text-sm text-slate-500 dark:text-slate-400">
You have not added any podcasts. Add one below.
</p>
<Dialog>
<DialogTrigger>
<Button size="sm" className="relative">
<Plus className="mr-2 h-4 w-4" />
Add Podcast
<DemoIndicator className="-top-1 -right-1 z-30" />
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Add Podcast</DialogTitle>
<DialogDescription>
Copy and paste the podcast feed URL to import.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="url">Podcast URL</Label>
<Input
id="url"
placeholder="https://example.com/feed.xml"
/>
</div>
</div>
<DialogFooter>
<Button>Import Podcast</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</TabsContent>
</Tabs>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
interface AlbumArtworkProps extends React.HTMLAttributes<HTMLDivElement> {
album: Album
aspectRatio?: number
}
function AlbumArtwork({
album,
aspectRatio = 3 / 4,
className,
...props
}: AlbumArtworkProps) {
return (
<div className={cn("space-y-3", className)} {...props}>
<ContextMenu>
<ContextMenuTrigger>
<AspectRatio
ratio={aspectRatio}
className="overflow-hidden rounded-md"
>
<Image
src={album.cover}
alt={album.name}
fill
className="object-cover transition-all hover:scale-105"
/>
</AspectRatio>
</ContextMenuTrigger>
<ContextMenuContent className="w-40">
<ContextMenuItem>Add to Library</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Add to Playlist</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem>
<PlusCircle className="mr-2 h-4 w-4" />
New Playlist
</ContextMenuItem>
<ContextMenuSeparator />
{playlists.map((playlist) => (
<ContextMenuItem key={playlist}>
<ListMusic className="mr-2 h-4 w-4" /> {playlist}
</ContextMenuItem>
))}
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem>Play Next</ContextMenuItem>
<ContextMenuItem>Play Later</ContextMenuItem>
<ContextMenuItem>Create Station</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>Like</ContextMenuItem>
<ContextMenuItem>Share</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
<div className="space-y-1 text-sm">
<h3 className="font-medium leading-none">{album.name}</h3>
<p className="text-xs text-slate-500 dark:text-slate-400">
{album.artist}
</p>
</div>
</div>
)
}
interface DemoIndicatorProps extends React.HTMLAttributes<HTMLSpanElement> {}
export function DemoIndicator({ className }: DemoIndicatorProps) {
return (
<span
className={cn(
"absolute top-1 right-0 flex h-5 w-5 animate-bounce items-center justify-center",
className
)}
>
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-400 opacity-75" />
<span className="relative inline-flex h-3 w-3 rounded-full bg-sky-500" />
</span>
)
}

View File

@@ -1,31 +1,18 @@
import { cn } from "@/lib/utils"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
interface CalloutProps {
icon?: string
title?: string
children?: React.ReactNode
type?: "default" | "warning" | "danger"
}
export function Callout({
children,
icon,
type = "default",
...props
}: CalloutProps) {
export function Callout({ title, children, icon, ...props }: CalloutProps) {
return (
<div
className={cn(
"my-6 flex items-start rounded-md border border-b-4 border-slate-900 p-4",
{
"border-slate-900 dark:border-slate-700": type === "default",
"border-red-600": type === "danger",
"border-yellow-500": type === "warning",
}
)}
{...props}
>
<Alert {...props}>
{icon && <span className="mr-4 text-2xl">{icon}</span>}
<div>{children}</div>
</div>
{title && <AlertTitle>{title}</AlertTitle>}
<AlertDescription>{children}</AlertDescription>
</Alert>
)
}

View File

@@ -1,38 +0,0 @@
import Link from "next/link"
import { cn } from "@/lib/utils"
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
href?: string
disabled?: boolean
}
export function Card({
href,
className,
children,
disabled,
...props
}: CardProps) {
return (
<div
className={cn(
"group relative rounded-lg border border-slate-200 bg-transparent p-6 text-slate-900 shadow-md transition-shadow hover:shadow-lg dark:border-slate-700 dark:text-slate-50",
disabled && "cursor-not-allowed opacity-60",
className
)}
{...props}
>
<div className="flex flex-col justify-between space-y-4">
<div className="space-y-2 [&>p]:text-slate-600 [&>p]:dark:text-slate-300 [&>h4]:!mt-0 [&>h3]:!mt-0">
{children}
</div>
</div>
{href && (
<Link href={disabled ? "#" : href} className="absolute inset-0">
<span className="sr-only">View</span>
</Link>
)}
</div>
)
}

View File

@@ -40,12 +40,12 @@ export function CodeBlockWrapper({
</CollapsibleContent>
<div
className={cn(
"absolute flex items-center justify-center bg-gradient-to-b from-slate-900/30 to-slate-900/90 p-2",
isOpened ? "inset-x-0 bottom-3 h-12" : "inset-0"
"absolute flex items-center justify-center bg-gradient-to-b from-background/30 to-muted/90 p-2",
isOpened ? "inset-x-0 bottom-0 h-12" : "inset-0"
)}
>
<CollapsibleTrigger asChild>
<Button variant="subtle" className="h-8 text-xs">
<Button variant="secondary" className="h-8 text-xs">
{isOpened ? "Collapse" : expandButtonTitle}
</Button>
</CollapsibleTrigger>

View File

@@ -47,14 +47,14 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button
variant="outline"
className={cn(
"relative h-9 w-full justify-start text-sm text-slate-500 dark:text-slate-400 sm:pr-12 md:w-40 lg:w-64"
"relative h-9 w-full justify-start rounded-[0.5rem] text-sm text-muted-foreground 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 top-2 right-1.5 hidden h-5 select-none items-center gap-1 rounded border border-slate-100 bg-slate-100 px-1.5 font-mono text-[10px] font-medium text-slate-600 opacity-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400 sm:flex">
<kbd className="pointer-events-none absolute right-1.5 top-2 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>
@@ -77,23 +77,23 @@ export function CommandMenu({ ...props }: DialogProps) {
</CommandItem>
))}
</CommandGroup>
<CommandGroup heading="Components">
{allDocs
.filter((doc) => doc.component)
.map((doc) => (
{docsConfig.sidebarNav.map((group) => (
<CommandGroup key={group.title} heading={group.title}>
{group.items.map((navItem) => (
<CommandItem
key={doc._id}
key={navItem.href}
onSelect={() => {
runCommand(() => router.push(doc.slug))
runCommand(() => router.push(navItem.href as string))
}}
>
<div className="mr-2 flex h-4 w-4 items-center justify-center">
<Circle className="h-3 w-3" />
</div>
{doc.title}
{navItem.title}
</CommandItem>
))}
</CommandGroup>
</CommandGroup>
))}
<CommandSeparator />
<CommandGroup heading="Theme">
<CommandItem onSelect={() => runCommand(() => setTheme("light"))}>

View File

@@ -11,7 +11,7 @@ export function ComponentCard({
<AspectRatio ratio={1 / 1} asChild>
<div
className={cn(
"flex items-center justify-center rounded-md border border-slate-200 p-8 dark:border-slate-700",
"flex items-center justify-center rounded-md border p-8",
className
)}
{...props}

View File

@@ -3,13 +3,14 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
extractClassname?: boolean
extractedClassNames?: string
align?: "center" | "start" | "end"
src?: string
}
export function ComponentExample({
@@ -18,6 +19,7 @@ export function ComponentExample({
extractClassname,
extractedClassNames,
align = "center",
src: _,
...props
}: ComponentExampleProps) {
const [Example, Code, ...Children] = React.Children.toArray(
@@ -41,22 +43,31 @@ export function ComponentExample({
{...props}
>
<Tabs defaultValue="preview" className="mr-auto w-full">
<div className="flex items-center justify-between">
<TabsList>
<TabsTrigger value="preview">Preview</TabsTrigger>
<TabsTrigger value="code">Code</TabsTrigger>
<div className="flex items-center justify-between pb-3">
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
<TabsTrigger
value="preview"
className="relative rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
Preview
</TabsTrigger>
<TabsTrigger
value="code"
className="relative rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
Code
</TabsTrigger>
</TabsList>
{extractedClassNames ? (
<CopyWithClassNames
value={codeString}
classNames={extractedClassNames}
className="border-none"
/>
) : (
codeString && <CopyButton value={codeString} />
)}
</div>
<TabsContent value="preview" className="p-0">
<TabsContent value="preview" className="rounded-md border">
<div
className={cn("flex min-h-[350px] justify-center p-10", {
"items-center": align === "center",
@@ -67,16 +78,16 @@ export function ComponentExample({
{Example}
</div>
</TabsContent>
<TabsContent value="code" className="border-none p-0">
<TabsContent value="code">
<div className="flex flex-col space-y-4">
<div className="w-full rounded-md [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto [&_button]:hidden">
<div className="w-full rounded-md [&_button]:hidden [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
{Code}
</div>
{Children && (
<div className="rounded-md [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto [&_button]:hidden">
{Children?.length ? (
<div className="rounded-md [&_button]:hidden [&_pre]:my-0 [&_pre]:max-h-[350px] [&_pre]:overflow-auto">
{Children}
</div>
)}
) : null}
</div>
</TabsContent>
</Tabs>

View File

@@ -5,13 +5,13 @@ import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
import { NpmCommands } from "types/unist"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Icons } from "@/components/icons"
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string
@@ -42,7 +42,7 @@ export function CopyButton({
return (
<button
className={cn(
"relative z-20 inline-flex h-8 items-center justify-center rounded-md border-slate-200 p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none dark:text-slate-100 dark:hover:bg-slate-800",
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
className
)}
onClick={() => {
@@ -55,9 +55,9 @@ export function CopyButton({
>
<span className="sr-only">Copy</span>
{hasCopied ? (
<Icons.check className="h-4 w-4" />
<Icons.check className="h-3 w-3" />
) : (
<Icons.copy className="h-4 w-4" />
<Icons.copy className="h-3 w-3" />
)}
</button>
)
@@ -66,7 +66,7 @@ export function CopyButton({
interface CopyWithClassNamesProps extends DropdownMenuTriggerProps {
value: string
classNames: string
className: string
className?: string
}
export function CopyWithClassNames({
@@ -92,15 +92,15 @@ export function CopyWithClassNames({
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
"relative z-20 inline-flex h-8 items-center justify-center rounded-md p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none dark:text-slate-100 dark:hover:bg-slate-800",
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
className
)}
{...props}
>
{hasCopied ? (
<Icons.check className="h-4 w-4" />
<Icons.check className="h-3 w-3" />
) : (
<Icons.copy className="h-4 w-4" />
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</DropdownMenuTrigger>
@@ -144,29 +144,29 @@ export function CopyNpmCommandButton({
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
"relative z-20 inline-flex h-8 items-center justify-center rounded-md p-2 text-sm font-medium text-slate-900 transition-all hover:bg-slate-100 focus:outline-none",
"relative z-20 inline-flex h-6 w-6 items-center justify-center rounded-md border bg-background text-sm font-medium transition-all hover:bg-muted focus:outline-none",
className
)}
{...props}
>
{hasCopied ? (
<Icons.check className="h-4 w-4" />
<Icons.check className="h-3 w-3" />
) : (
<Icons.copy className="h-4 w-4" />
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => copyCommand(commands.__npmCommand__)}>
<Icons.npm className="mr-2 h-4 w-4 fill-[#CB3837]" />
<Icons.npm className="mr-2 h-4 w-4" />
<span>npm</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => copyCommand(commands.__yarnCommand__)}>
<Icons.yarn className="mr-2 h-4 w-4 fill-[#2C8EBB]" />
<Icons.yarn className="mr-2 h-4 w-4" />
<span>yarn</span>
</DropdownMenuItem>
<DropdownMenuItem onClick={() => copyCommand(commands.__pnpmCommand__)}>
<Icons.pnpm className="mr-2 h-4 w-4 fill-[#F69220]" />
<Icons.pnpm className="mr-2 h-4 w-4" />
<span>pnpm</span>
</DropdownMenuItem>
</DropdownMenuContent>

View File

@@ -0,0 +1,58 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
const examples = [
{
name: "Dashboard",
href: "/examples/dashboard",
},
{
name: "Cards",
href: "/examples/cards",
},
{
name: "Playground",
href: "/examples/playground",
},
{
name: "Music",
href: "/examples/music",
},
{
name: "Authentication",
href: "/examples/authentication",
},
]
interface ExamplesNavProps extends React.HTMLAttributes<HTMLDivElement> {}
export function ExamplesNav({ className, ...props }: ExamplesNavProps) {
const pathname = usePathname()
return (
<ScrollArea>
<div className={cn("mb-4 flex items-center", className)} {...props}>
{examples.map((example) => (
<Link
href={example.href}
key={example.href}
className={cn(
"flex px-4 font-medium",
pathname === example.href
? "text-primary"
: "text-muted-foreground"
)}
>
{example.name}
</Link>
))}
</div>
<ScrollBar orientation="horizontal" className="invisible" />
</ScrollArea>
)
}

View File

@@ -7,7 +7,7 @@ import {
export function AccordionDemo() {
return (
<Accordion type="single" collapsible className="w-[450px]">
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>

View File

@@ -1,5 +1,3 @@
"use client"
import {
AlertDialog,
AlertDialogAction,
@@ -17,7 +15,7 @@ export function AlertDialogDemo() {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline">Open</Button>
<Button variant="outline">Show Dialog</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>

View File

@@ -0,0 +1,15 @@
import { Terminal, Waves } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
export function AlertDemo() {
return (
<Alert>
<Terminal className="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components to your app using the cli.
</AlertDescription>
</Alert>
)
}

View File

@@ -0,0 +1,15 @@
import { AlertCircle, FileWarning, Terminal } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
export function AlertDestructive() {
return (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Your session has expired. Please log in again.
</AlertDescription>
</Alert>
)
}

View File

@@ -1,15 +1,13 @@
"use client"
import Image from "next/image"
import { AspectRatio } from "@/components/ui/aspect-ratio"
export function AspectRatioDemo() {
return (
<AspectRatio ratio={16 / 9} className="bg-slate-50 dark:bg-slate-800">
<AspectRatio ratio={16 / 9} className="bg-muted">
<Image
src="https://images.unsplash.com/photo-1576075796033-848c2a5f3696?w=800&dpr=2&q=80"
alt="Photo by Alvaro Pinot"
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
fill
className="rounded-md object-cover"
/>

View File

@@ -1,5 +1,3 @@
"use client"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
export function AvatarDemo() {

View File

@@ -0,0 +1,5 @@
import { Badge } from "@/components/ui/badge"
export function BadgeDemo() {
return <Badge>Badge</Badge>
}

View File

@@ -0,0 +1,5 @@
import { Badge } from "@/components/ui/badge"
export function BadgeDestructive() {
return <Badge variant="desctructive">Destructive</Badge>
}

View File

@@ -0,0 +1,5 @@
import { Badge } from "@/components/ui/badge"
export function BadgeOutline() {
return <Badge variant="outline">Outline</Badge>
}

View File

@@ -0,0 +1,5 @@
import { Badge } from "@/components/ui/badge"
export function BadgeSecondary() {
return <Badge variant="secondary">Secondary</Badge>
}

View File

@@ -0,0 +1,5 @@
import { Button } from "@/components/ui/button"
export function ButtonSecondary() {
return <Button variant="secondary">Secondary</Button>
}

View File

@@ -1,5 +0,0 @@
import { Button } from "@/components/ui/button"
export function ButtonSubtle() {
return <Button variant="subtle">Subtle</Button>
}

View File

@@ -0,0 +1,43 @@
"use client"
import * as React from "react"
import { format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function CalendarDatePicker() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
initialFocus
/>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,65 @@
"use client"
import * as React from "react"
import { addDays, format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
export function CalendarDateRangePicker({
className,
}: React.HTMLAttributes<HTMLDivElement>) {
const [date, setDate] = React.useState<DateRange | undefined>({
from: new Date(2022, 0, 20),
to: addDays(new Date(2022, 0, 20), 20),
})
return (
<div className={cn("grid gap-2", className)}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"w-[300px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date?.from ? (
date.to ? (
<>
{format(date.from, "LLL dd, y")} -{" "}
{format(date.to, "LLL dd, y")}
</>
) : (
format(date.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
)
}

View File

@@ -0,0 +1,18 @@
"use client"
import * as React from "react"
import { Calendar } from "@/components/ui/calendar"
export function CalendarDemo() {
const [date, setDate] = React.useState<Date | undefined>(new Date())
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-md border"
/>
)
}

View File

@@ -0,0 +1,62 @@
"use client"
import * as React from "react"
import { addDays, format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export function CalendarDatePickerWithPresets() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-[280px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="flex w-auto flex-col space-y-2 p-2">
<Select
onValueChange={(value) =>
setDate(addDays(new Date(), parseInt(value)))
}
>
<SelectTrigger>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="0">Today</SelectItem>
<SelectItem value="1">Tomorrow</SelectItem>
<SelectItem value="3">In 3 days</SelectItem>
<SelectItem value="7">In a week</SelectItem>
</SelectContent>
</Select>
<div className="rounded-md border">
<Calendar mode="single" selected={date} onSelect={setDate} />
</div>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,79 @@
import { BellRing, Check } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"
const notifications = [
{
title: "Your call has been confirmed.",
description: "1 hour ago",
},
{
title: "You have a new message!",
description: "1 hour ago",
},
{
title: "Your subscription is expiring soon!",
description: "2 hours ago",
},
]
type CardProps = React.ComponentProps<typeof Card>
export function CardDemo({ className, ...props }: CardProps) {
return (
<Card className={cn("w-[380px]", className)} {...props}>
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>You have 3 unread messages.</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className=" flex items-center space-x-4 rounded-md border p-4">
<BellRing />
<div className="flex-1 space-y-1">
<p className="text-sm font-medium leading-none">
Push Notifications
</p>
<p className="text-sm text-muted-foreground">
Send notifications to device.
</p>
</div>
<Switch />
</div>
<div>
{notifications.map((notification, index) => (
<div
key={index}
className="mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0"
>
<span className="flex h-2 w-2 translate-y-1 rounded-full bg-sky-500" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">
{notification.title}
</p>
<p className="text-sm text-muted-foreground">
{notification.description}
</p>
</div>
</div>
))}
</div>
</CardContent>
<CardFooter>
<Button className="w-full">
<Check className="mr-2 h-4 w-4" /> Mark all as read
</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,59 @@
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export function CardWithForm() {
return (
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one-click.</CardDescription>
</CardHeader>
<CardContent>
<form>
<div className="grid w-full items-center gap-4">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Name of your project" />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Framework</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Select" />
<SelectContent position="popper">
<SelectItem value="next">Next.js</SelectItem>
<SelectItem value="sveltekit">SvelteKit</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
<SelectItem value="nuxt">Nuxt.js</SelectItem>
</SelectContent>
</SelectTrigger>
</Select>
</div>
</div>
</form>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="ghost">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
</Card>
)
}

View File

@@ -1,5 +1,3 @@
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function CheckboxDisabled() {

View File

@@ -13,7 +13,7 @@ export function CheckboxWithText() {
>
Accept terms and conditions
</label>
<p className="text-sm text-slate-500 dark:text-slate-400">
<p className="text-sm text-muted-foreground">
You agree to our Terms of Service and Privacy Policy.
</p>
</div>

View File

@@ -30,14 +30,14 @@ export function CollapsibleDemo() {
</Button>
</CollapsibleTrigger>
</div>
<div className="rounded-md border border-slate-200 px-4 py-3 font-mono text-sm dark:border-slate-700">
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/primitives
</div>
<CollapsibleContent className="space-y-2">
<div className="rounded-md border border-slate-200 px-4 py-3 font-mono text-sm dark:border-slate-700">
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@radix-ui/colors
</div>
<div className="rounded-md border border-slate-200 px-4 py-3 font-mono text-sm dark:border-slate-700">
<div className="rounded-md border px-4 py-3 font-mono text-sm">
@stitches/react
</div>
</CollapsibleContent>

View File

@@ -1,5 +1,3 @@
"use client"
import {
Calculator,
Calendar,
@@ -22,7 +20,7 @@ import {
export function CommandDemo() {
return (
<Command className="rounded-lg border border-slate-100 shadow-md dark:border-slate-800">
<Command className="rounded-lg border shadow-md">
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>

View File

@@ -37,9 +37,9 @@ export function CommandDialogDemo() {
return (
<>
<p className="text-sm text-slate-500 dark:text-slate-400">
<p className="text-sm text-muted-foreground">
Press{" "}
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border border-slate-100 bg-slate-100 px-1.5 font-mono text-[10px] font-medium text-slate-600 opacity-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400">
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
<span className="text-xs"></span>J
</kbd>
</p>

View File

@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
import { Calendar, MoreHorizontal, Pen, Tags, Trash, User } from "lucide-react"
import { Calendar, MoreHorizontal, Tags, Trash, User } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
@@ -41,12 +41,12 @@ export function CommandDropdownMenu() {
const [open, setOpen] = React.useState(false)
return (
<div className="flex w-full flex-col items-start justify-between rounded-md border border-slate-200 py-3 px-4 dark:border-slate-700 sm:flex-row sm:items-center">
<div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center">
<p className="text-sm font-medium leading-none">
<span className="mr-2 rounded-lg bg-slate-900 px-2 py-1 text-xs text-slate-50 dark:bg-slate-800 dark:text-slate-100">
<span className="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
{label}
</span>
<span className="text-slate-500">Create a new project</span>
<span className="text-muted-foreground">Create a new project</span>
</p>
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>

View File

@@ -68,13 +68,13 @@ export function CommandPopover() {
return (
<div className="flex items-center space-x-4">
<p className="text-sm text-slate-500 dark:text-slate-400">Status</p>
<p className="text-sm text-muted-foreground">Status</p>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
size="sm"
className="w-[120px] justify-start"
className="w-[150px] justify-start"
>
{selectedStatus ? (
<>

View File

@@ -1,31 +1,9 @@
"use client"
import {
Cloud,
CreditCard,
Github,
Keyboard,
LifeBuoy,
LogOut,
Mail,
MessageSquare,
Plus,
PlusCircle,
Settings,
User,
UserPlus,
Users,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
@@ -39,7 +17,7 @@ import {
export function ContextMenuDemo() {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed border-slate-200 text-sm dark:border-slate-700">
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-64">

View File

@@ -1,5 +1,3 @@
"use client"
import { Button } from "@/components/ui/button"
import {
Dialog,

View File

@@ -1,5 +1,3 @@
"use client"
import {
Cloud,
CreditCard,

View File

@@ -27,7 +27,7 @@ export function HoverCardDemo() {
</p>
<div className="flex items-center pt-2">
<CalendarDays className="mr-2 h-4 w-4 opacity-70" />{" "}
<span className="text-xs text-slate-500 dark:text-slate-400">
<span className="text-xs text-muted-foreground">
Joined December 2021
</span>
</div>

View File

@@ -1,15 +1,27 @@
import { AccordionDemo } from "@/components/examples/accordion/demo"
import { AlertDialogDemo } from "@/components/examples/alert-dialog/demo"
import { AlertDemo } from "@/components/examples/alert/demo"
import { AlertDestructive } from "@/components/examples/alert/destructive"
import { AspectRatioDemo } from "@/components/examples/aspect-ratio/demo"
import { AvatarDemo } from "@/components/examples/avatar/demo"
import { BadgeDemo } from "@/components/examples/badge/demo"
import { BadgeDestructive } from "@/components/examples/badge/destructive"
import { BadgeOutline } from "@/components/examples/badge/outline"
import { BadgeSecondary } from "@/components/examples/badge/secondary"
import { ButtonDemo } from "@/components/examples/button/demo"
import { ButtonDestructive } from "@/components/examples/button/destructive"
import { ButtonGhost } from "@/components/examples/button/ghost"
import { ButtonLink } from "@/components/examples/button/link"
import { ButtonLoading } from "@/components/examples/button/loading"
import { ButtonOutline } from "@/components/examples/button/outline"
import { ButtonSubtle } from "@/components/examples/button/subtle"
import { ButtonSecondary } from "@/components/examples/button/secondary"
import { ButtonWithIcon } from "@/components/examples/button/with-icon"
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker"
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
import { CalendarDemo } from "@/components/examples/calendar/demo"
import { CalendarDatePickerWithPresets } from "@/components/examples/calendar/with-presets"
import { CardDemo } from "@/components/examples/card/demo"
import { CardWithForm } from "@/components/examples/card/with-form"
import { CheckboxDemo } from "@/components/examples/checkbox/demo"
import { CheckboxDisabled } from "@/components/examples/checkbox/disabled"
import { CheckboxWithText } from "@/components/examples/checkbox/with-text"
@@ -27,6 +39,7 @@ import { DropdownMenuRadioGroupDemo } from "@/components/examples/dropdown-menu/
import { HoverCardDemo } from "@/components/examples/hover-card/demo"
import { InputDemo } from "@/components/examples/input/demo"
import { InputDisabled } from "@/components/examples/input/disabled"
import { InputFile } from "@/components/examples/input/file"
import { InputWithButton } from "@/components/examples/input/with-button"
import { InputWithLabel } from "@/components/examples/input/with-label"
import { InputWithText } from "@/components/examples/input/with-text"
@@ -42,6 +55,7 @@ import { SeparatorDemo } from "@/components/examples/separator/demo"
import { SheetDemo } from "@/components/examples/sheet/demo"
import { SheetPosition } from "@/components/examples/sheet/position"
import { SheetSize } from "@/components/examples/sheet/size"
import { SkeletonDemo } from "@/components/examples/skeleton/demo"
import { SliderDemo } from "@/components/examples/slider/demo"
import { SwitchDemo } from "@/components/examples/switch/demo"
import { TabsDemo } from "@/components/examples/tabs/demo"
@@ -72,24 +86,36 @@ import { TypographyInlineCode } from "@/components/examples/typography/inline-co
import { TypographyLarge } from "@/components/examples/typography/large"
import { TypographyLead } from "@/components/examples/typography/lead"
import { TypographyList } from "@/components/examples/typography/list"
import { TypographyMuted } from "@/components/examples/typography/muted"
import { TypographyP } from "@/components/examples/typography/p"
import { TypographySmall } from "@/components/examples/typography/small"
import { TypographySubtle } from "@/components/examples/typography/subtle"
import { TypographyTable } from "@/components/examples/typography/table"
export const examples = {
AccordionDemo,
AlertDemo,
AlertDialogDemo,
AlertDestructive,
AspectRatioDemo,
AvatarDemo,
BadgeDemo,
BadgeDestructive,
BadgeOutline,
BadgeSecondary,
ButtonDemo,
ButtonGhost,
ButtonDestructive,
ButtonLink,
ButtonLoading,
ButtonOutline,
ButtonSubtle,
ButtonSecondary,
ButtonWithIcon,
CalendarDemo,
CalendarDatePicker,
CalendarDateRangePicker,
CalendarDatePickerWithPresets,
CardDemo,
CardWithForm,
CheckboxDemo,
CheckboxDisabled,
CheckboxWithText,
@@ -107,6 +133,7 @@ export const examples = {
HoverCardDemo,
InputDemo,
InputDisabled,
InputFile,
InputWithButton,
InputWithLabel,
InputWithText,
@@ -122,6 +149,7 @@ export const examples = {
SheetDemo,
SheetSize,
SheetPosition,
SkeletonDemo,
SliderDemo,
SwitchDemo,
TabsDemo,
@@ -148,7 +176,7 @@ export const examples = {
TypographyList,
TypographyP,
TypographySmall,
TypographySubtle,
TypographyMuted,
TypographyTable,
ToggleDemo,
ToggleSm,

View File

@@ -0,0 +1,11 @@
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export function InputFile() {
return (
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor="picture">Picture</Label>
<Input id="picture" type="file" />
</div>
)
}

View File

@@ -6,7 +6,7 @@ export function InputWithText() {
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor="email-2">Email</Label>
<Input type="email" id="email-2" placeholder="Email" />
<p className="text-sm text-slate-500">Enter your email address.</p>
<p className="text-sm text-muted-foreground">Enter your email address.</p>
</div>
)
}

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