mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
feat: CSS variables and CLI (#180)
This commit is contained in:
@@ -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": [],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
37
README.md
37
README.md
@@ -4,42 +4,9 @@ Accessible and customizable components that you can copy and paste into your app
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# App
|
||||
# -----------------------------------------------------------------------------
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3001
|
||||
|
||||
@@ -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" }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
99
apps/www/app/examples/authentication/page.tsx
Normal file
99
apps/www/app/examples/authentication/page.tsx
Normal 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">
|
||||
“This library has saved me countless hours of work and
|
||||
helped me deliver stunning designs to my clients faster than
|
||||
ever before. Highly recommended!”
|
||||
</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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
60
apps/www/app/examples/cards/components/cookie-settings.tsx
Normal file
60
apps/www/app/examples/cards/components/cookie-settings.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
60
apps/www/app/examples/cards/components/create-account.tsx
Normal file
60
apps/www/app/examples/cards/components/create-account.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
18
apps/www/app/examples/cards/components/date-picker.tsx
Normal file
18
apps/www/app/examples/cards/components/date-picker.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
81
apps/www/app/examples/cards/components/github-card.tsx
Normal file
81
apps/www/app/examples/cards/components/github-card.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
51
apps/www/app/examples/cards/components/notifications.tsx
Normal file
51
apps/www/app/examples/cards/components/notifications.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
117
apps/www/app/examples/cards/components/payment-method.tsx
Normal file
117
apps/www/app/examples/cards/components/payment-method.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
82
apps/www/app/examples/cards/components/report-an-issue.tsx
Normal file
82
apps/www/app/examples/cards/components/report-an-issue.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
116
apps/www/app/examples/cards/components/share-document.tsx
Normal file
116
apps/www/app/examples/cards/components/share-document.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
147
apps/www/app/examples/cards/components/team-members.tsx
Normal file
147
apps/www/app/examples/cards/components/team-members.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
93
apps/www/app/examples/cards/page.tsx
Normal file
93
apps/www/app/examples/cards/page.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
63
apps/www/app/examples/cards/styles.css
Normal file
63
apps/www/app/examples/cards/styles.css
Normal 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%;
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
40
apps/www/app/examples/dashboard/components/main-nav.tsx
Normal file
40
apps/www/app/examples/dashboard/components/main-nav.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
78
apps/www/app/examples/dashboard/components/overview.tsx
Normal file
78
apps/www/app/examples/dashboard/components/overview.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
67
apps/www/app/examples/dashboard/components/recent-sales.tsx
Normal file
67
apps/www/app/examples/dashboard/components/recent-sales.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
13
apps/www/app/examples/dashboard/components/search.tsx
Normal file
13
apps/www/app/examples/dashboard/components/search.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
205
apps/www/app/examples/dashboard/components/team-switcher.tsx
Normal file
205
apps/www/app/examples/dashboard/components/team-switcher.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
67
apps/www/app/examples/dashboard/components/user-nav.tsx
Normal file
67
apps/www/app/examples/dashboard/components/user-nav.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
165
apps/www/app/examples/dashboard/page.tsx
Normal file
165
apps/www/app/examples/dashboard/page.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
69
apps/www/app/examples/layout.tsx
Normal file
69
apps/www/app/examples/layout.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
84
apps/www/app/examples/music/components/album-artwork.tsx
Normal file
84
apps/www/app/examples/music/components/album-artwork.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
178
apps/www/app/examples/music/components/menu.tsx
Normal file
178
apps/www/app/examples/music/components/menu.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
99
apps/www/app/examples/music/components/sidebar.tsx
Normal file
99
apps/www/app/examples/music/components/sidebar.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
71
apps/www/app/examples/music/data/albums.ts
Normal file
71
apps/www/app/examples/music/data/albums.ts
Normal 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",
|
||||
},
|
||||
]
|
||||
16
apps/www/app/examples/music/data/playlists.ts
Normal file
16
apps/www/app/examples/music/data/playlists.ts
Normal 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",
|
||||
]
|
||||
154
apps/www/app/examples/music/page.tsx
Normal file
154
apps/www/app/examples/music/page.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
65
apps/www/app/examples/music/styles.css
Normal file
65
apps/www/app/examples/music/styles.css
Normal 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;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
ClipboardCheck,
|
||||
Copy,
|
||||
CreditCard,
|
||||
EggFried,
|
||||
Fingerprint,
|
||||
HelpCircle,
|
||||
Laptop,
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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" />
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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",
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Preset } from "@/types"
|
||||
export interface Preset {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export const presets: Preset[] = [
|
||||
{
|
||||
178
apps/www/app/examples/playground/page.tsx
Normal file
178
apps/www/app/examples/playground/page.tsx
Normal 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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
65
apps/www/app/examples/playground/styles.css
Normal file
65
apps/www/app/examples/playground/styles.css
Normal 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;
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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()} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
201
apps/www/app/sink/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
BIN
apps/www/assets/fonts/CalSans-SemiBold.ttf
Normal file
BIN
apps/www/assets/fonts/CalSans-SemiBold.ttf
Normal file
Binary file not shown.
BIN
apps/www/assets/fonts/CalSans-SemiBold.woff
Normal file
BIN
apps/www/assets/fonts/CalSans-SemiBold.woff
Normal file
Binary file not shown.
BIN
apps/www/assets/fonts/CalSans-SemiBold.woff2
Normal file
BIN
apps/www/assets/fonts/CalSans-SemiBold.woff2
Normal file
Binary file not shown.
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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"))}>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
58
apps/www/components/examples-nav.tsx
Normal file
58
apps/www/components/examples-nav.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
15
apps/www/components/examples/alert/demo.tsx
Normal file
15
apps/www/components/examples/alert/demo.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
15
apps/www/components/examples/alert/destructive.tsx
Normal file
15
apps/www/components/examples/alert/destructive.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client"
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
|
||||
export function AvatarDemo() {
|
||||
|
||||
5
apps/www/components/examples/badge/demo.tsx
Normal file
5
apps/www/components/examples/badge/demo.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export function BadgeDemo() {
|
||||
return <Badge>Badge</Badge>
|
||||
}
|
||||
5
apps/www/components/examples/badge/destructive.tsx
Normal file
5
apps/www/components/examples/badge/destructive.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export function BadgeDestructive() {
|
||||
return <Badge variant="desctructive">Destructive</Badge>
|
||||
}
|
||||
5
apps/www/components/examples/badge/outline.tsx
Normal file
5
apps/www/components/examples/badge/outline.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export function BadgeOutline() {
|
||||
return <Badge variant="outline">Outline</Badge>
|
||||
}
|
||||
5
apps/www/components/examples/badge/secondary.tsx
Normal file
5
apps/www/components/examples/badge/secondary.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
|
||||
export function BadgeSecondary() {
|
||||
return <Badge variant="secondary">Secondary</Badge>
|
||||
}
|
||||
5
apps/www/components/examples/button/secondary.tsx
Normal file
5
apps/www/components/examples/button/secondary.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function ButtonSecondary() {
|
||||
return <Button variant="secondary">Secondary</Button>
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export function ButtonSubtle() {
|
||||
return <Button variant="subtle">Subtle</Button>
|
||||
}
|
||||
43
apps/www/components/examples/calendar/date-picker.tsx
Normal file
43
apps/www/components/examples/calendar/date-picker.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
65
apps/www/components/examples/calendar/date-range-picker.tsx
Normal file
65
apps/www/components/examples/calendar/date-range-picker.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
18
apps/www/components/examples/calendar/demo.tsx
Normal file
18
apps/www/components/examples/calendar/demo.tsx
Normal 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"
|
||||
/>
|
||||
)
|
||||
}
|
||||
62
apps/www/components/examples/calendar/with-presets.tsx
Normal file
62
apps/www/components/examples/calendar/with-presets.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
79
apps/www/components/examples/card/demo.tsx
Normal file
79
apps/www/components/examples/card/demo.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
59
apps/www/components/examples/card/with-form.tsx
Normal file
59
apps/www/components/examples/card/with-form.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client"
|
||||
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
|
||||
export function CheckboxDisabled() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 ? (
|
||||
<>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
Cloud,
|
||||
CreditCard,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
11
apps/www/components/examples/input/file.tsx
Normal file
11
apps/www/components/examples/input/file.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user