Compare commits

...

61 Commits

Author SHA1 Message Date
github-actions[bot]
d604b82fb3 chore(release): version packages (#644)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-22 23:08:57 +04:00
shadcn
658c710bce fix: schema keys validation (#641)
* fix: schema keys validation

* chore: add changeset

* fix: update registry base url
2023-06-22 23:05:54 +04:00
github-actions[bot]
4ca9619efa chore(release): version packages (#640)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-22 22:49:27 +04:00
shadcn
eeb17545a1 feat: new CLI, Styles and more (#637) 2023-06-22 22:44:52 +04:00
Tat Tran
3d717f992b fix: class name typo (#398)
* fix: class name

* fix: class names order

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:26:44 +04:00
moshyfawn
042554ad07 fix(www): register field array input once (#466)
Closes: #465

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:20:43 +04:00
Hajime Nakagawa
71f496d41f fix(command): modify incorrect className (#473)
text-foreground-muted => text-muted-foreground

Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:16:04 +04:00
puneet-sarhali
0a5df3ac85 docs(www): broken link to React Hook Form's useController hook (#479)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-29 11:10:34 +04:00
shadcn
eb27529f50 feat(next-template): update template and dependencies (#483)
* feat(next-template): update template and deps

* docs(next-template): update README

* fix(www): update deps

* chore: remove console.log
2023-05-29 11:02:02 +04:00
Prince Hernandez
dffbe89f7d feat(cli): add support for config file (#245)
* feat(cli): add config files and examples in docs

* docs(www): remove cli advanced options

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-28 10:21:11 +04:00
Alexander Kachkaev
22f23b7db3 chore: update pnpm version in package.json (#314)
* Update pnpm version in package.json

* Update package.json

* 8.5.1

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 22:05:55 +04:00
Brad Adams
d6d4017b95 fix: tailwind-indicator classnames (#453)
+ Remove unnecessary `hidden` breakpoint classnames

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 21:05:41 +04:00
Sammy Hass
00ecdfbb17 fix(docs): command menu unresponsive after no results (#455) 2023-05-25 20:57:49 +04:00
Oliver Schneider
065ba02ae5 feat(sheet): expose close trigger for Sheet (#438)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 16:10:22 +04:00
Spastic
5dfc2020aa fix typings (#287)
* fix typings

* change Toast interface to type

* ran prettier
2023-05-25 15:51:13 +04:00
Daniel Rotärmel
5aecccc586 feat(cli): use https_proxy and consider previous install locations (#430)
* feat(cli): use system proxy

* feat(cli): adds default installation directory

* style: formatting

* feat: update lockfile

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-25 14:37:37 +04:00
shadcn
97a444b210 feat(www): add link to view code examples 2023-05-25 13:32:05 +04:00
Raí Siqueira
060e896183 fix(www): fix language placeholder (#433)
Signed-off-by: Rai Siqueira <rai93siqueira@gmail.com>
2023-05-24 18:21:27 +04:00
Dev
c4da22ffe9 docs(www): Fix unmatched closing brace typo (#423)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-23 11:31:32 +04:00
dong.huo
f6b2d0c5dd fix(www): add key to list map components (#413)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-23 11:22:35 +04:00
Zwyx
3f01388389 docs(www): fix inconsistency in forms docs + add note about uncontrolled components (#417)
* fix(forms): correct inconsistency in forms example

* doc(forms): add note about uncontrolled components
2023-05-22 17:00:59 +04:00
shadcn
c584f01163 docs(www): update defaultValues for useForm 2023-05-22 13:43:29 +04:00
shadcn
588ebd79e4 fix(www): filter out disabled links 2023-05-20 21:44:26 +04:00
shadcn
36881682cf feat: switch to stable next 2023-05-20 20:02:42 +04:00
jeremy
d1363515eb fix: incorrect example in form docs (#387) 2023-05-20 12:13:06 +04:00
Usman S. (Max Programming)
5afb8d530f docs(www): fix typo (#375)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 10:09:42 +04:00
Piros
6a5195498f fix(radio-group): add aspect-square on radio button (#379)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 10:03:52 +04:00
Piros
7d8be94a01 docs(www): update react-hook-form.mdx (#378)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 09:58:25 +04:00
moshyfawn
a3c904dcc9 fix(www): refactor controlled inputs API examples (#385)
* refactor: remove usage of uncontrolled input apis within controlled inputs

Closes: #384

* docs: display FormField controlled input usage example

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 08:58:44 +04:00
moshyfawn
11447c9bff fix(form): avoid unnecessary state subscriptions (#383)
* perf: avoid unnecessary state subscriptions

Closes: #382

* fix(form): code format

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-20 08:52:53 +04:00
moshyfawn
f9d399172c fix(www): display account form name input error (#381)
* docs: display account form name input error

Closes: #380

* style: remove callback return statement
2023-05-20 08:40:01 +04:00
shadcn
4ccff13f9c feat: react-hook-form (#377)
* feat(form): add form component

* feat(www): update site styles

* feat: add form examples

* docs(www): add docs for forms

* docs(www): hide tabs for docs demo
2023-05-19 22:56:49 +04:00
Quentin
dbbdbe618f fix(www): missing ']' in className (#358)
* fix(code-block-wrapper): missing ']' in className

* fix(code-block-wrapper): re-order tailwind className

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-16 15:41:35 +04:00
Yiyang
b09cff40ae fix(textarea): replace h-20 with min-h-[80px] (#344)
Co-authored-by: shadcn <m@shadcn.com>
2023-05-16 15:04:17 +04:00
github-actions[bot]
df9369762a chore(release): version packages (#352)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-13 13:35:46 +04:00
Nirmalya Ghosh
87ad14cb2a fix(shadcn-ui): add missing deps for Button (#259)
* fix: add missing deps for Button

* fix: use correct dep

Co-authored-by: Pablo <its.monotype@gmail.com>

* chore: add changeset

---------

Co-authored-by: Pablo <its.monotype@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-13 13:07:39 +04:00
Jack
5a2ce61e2e fix(data-table): uncontrolled input error on data-table (#335) 2023-05-11 10:07:39 +04:00
Bilal Afzal
4a7c07e754 fix(next-template): remove unused import Laptop in icons.tsx (#310)
Remove unused import 'Laptop' in icons.tsx (nextjs-example)

Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 21:48:46 +04:00
shadcn
8eb3e1e160 fix(www): update padding for row actions 2023-05-10 20:51:00 +04:00
shadcn
5bc68894b8 fix(www): filter display for lg 2023-05-10 16:32:57 +04:00
Connor Stevens
21890afa80 docs(www): fix typo
Co-authored-by: shadcn <m@shadcn.com>
2023-05-10 07:49:17 +04:00
Ben Jacobson
84b7200178 docs(www): fix link to DataTable (#322) 2023-05-10 00:09:15 +04:00
shadcn
f8272baf07 feat: add table and data table (#321) 2023-05-09 23:25:26 +04:00
Mike Robinson
9a6b934421 feat(table): add table component (#248)
* Add table.tsx

* Update table.tsx

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:49:19 +04:00
Mike Robinson
b3247d90a6 fix(checkbox,radio-group): Increase contrast ratio of checkboxes and radio buttons (#224)
* Update checkbox.tsx

* Update radio-group.tsx

* fix(radio-group): update colors for contrast

* fix(checkbox): update colors for contrast

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-05-08 15:43:10 +04:00
Danazumi
0c31f60bb0 fix(next-template): strict type check
Co-authored-by: Danazumi <granzin98@gmail.com>
Co-authored-by: shadcn <m@shadcn.com>
2023-05-03 18:47:49 +04:00
James
be64c55901 fix(dropdown-menu): dropdown sub menu using wrong text color (#285) 2023-05-03 17:20:18 +04:00
shadcn
b19199a35d docs: move combobox and date picker to their own page (#283) 2023-05-03 15:59:00 +04:00
shadcn
6e67107170 fix(www): update to canary with scrolling fix (#279) 2023-05-03 10:53:59 +04:00
github-actions[bot]
71c631891f chore(release): version packages (#272)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-02 19:25:02 +04:00
shadcn
888a5ad6f6 fix(shadcn-ui): update color of cards in dark mode (#271) 2023-05-02 15:39:16 +04:00
Evan Rosenfeld
d99b992e27 feat(button): support render delegation with asChild parameter (#174) 2023-04-30 12:01:41 +04:00
Elliot Anderson
7dc67be1ad fix(www): set icon fill to current currentColor (#239) 2023-04-29 18:52:25 +04:00
Aron Hafner
d528e9a45e docs(www): set correct colors for card in darkmode (#213) 2023-04-20 16:36:38 +04:00
Jack B
2a61b54096 fix(www): component selection in copy-button (#220)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 16:20:14 +04:00
Esteban De la Rosa
5b004f7565 docs(www): Fix typo in alert component usage (#196)
Co-authored-by: shadcn <m@shadcn.com>
2023-04-20 12:49:15 +04:00
shadcn
5e915756d5 feat(www): implement event tracking (#218)
* feat(www): implement event tracking

* fix(www): always show copy button

* fix(www): update align props for copy button
2023-04-20 12:21:50 +04:00
Elia Maino
bfc66148ea fix(badge): update destructive bg color on hover (#189) 2023-04-20 09:34:32 +04:00
Vincent Taneri
2fa78133c8 docs(www): fix a typo in FAQ (#198) 2023-04-19 19:56:04 +04:00
Seo Jun Hyung
3a60500d54 fix(www): remove styles for preventing scroll when modal is open (#209) 2023-04-19 19:43:33 +04:00
shadcn
a5b313ab13 chore: update template 2023-04-19 15:01:29 +04:00
685 changed files with 31449 additions and 6295 deletions

View File

@@ -10,13 +10,12 @@
"plugins": ["tailwindcss"],
"rules": {
"@next/next/no-html-link-for-pages": "off",
"react/jsx-key": "off",
"tailwindcss/no-custom-classname": "off",
"tailwindcss/classnames-order": "error"
},
"settings": {
"tailwindcss": {
"callees": ["cn"],
"callees": ["cn", "cva"],
"config": "tailwind.config.cjs"
},
"next": {

View File

@@ -22,6 +22,7 @@ jobs:
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
@@ -57,6 +58,7 @@ jobs:
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
@@ -94,6 +96,7 @@ jobs:
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory

View File

@@ -24,6 +24,8 @@ jobs:
- name: Use PNPM
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.1
- name: Use Node.js 18
uses: actions/setup-node@v3

View File

@@ -20,10 +20,13 @@ jobs:
- name: Use PNPM
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.1
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
version: 8.6.1
node-version: 18
cache: "pnpm"

42
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Test
on:
pull_request:
branches: ["*"]
jobs:
test:
runs-on: ubuntu-latest
name: Test
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.2.4
name: Install pnpm
id: pnpm-install
with:
version: 8.6.1
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- run: pnpm test

View File

@@ -3,4 +3,4 @@ node_modules
.next
build
.contentlayer
apps/www/pages/api/components.json
apps/www/pages/api/registry.json

View File

@@ -2,5 +2,9 @@
"eslint.workingDirectories": [
{ "pattern": "apps/*/" },
{ "pattern": "packages/*/" }
],
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}

View File

@@ -2,4 +2,5 @@ dist
node_modules
.next
build
.contentlayer
.contentlayer
__registry__/index.tsx

View File

@@ -0,0 +1 @@
// The content of this directory is autogenerated by the registry server.

View File

View File

@@ -0,0 +1 @@
> Files inside this directory is autogenerated by `./scripts/build-registry.ts`. **Do not edit them manually.** - shadcn

File diff suppressed because it is too large Load Diff

View File

@@ -10,12 +10,13 @@ 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-components"
import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc"
import { badgeVariants } from "@/registry/new-york/ui/badge"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
import { Separator } from "@/registry/new-york/ui/separator"
interface DocPageProps {
params: {
@@ -132,16 +133,20 @@ export default async function DocPage({ params }: DocPageProps) {
)}
</div>
) : null}
<Separator className="my-4 md:my-6" />
<Mdx code={doc.body.code} />
<Separator className="my-4 md:my-6" />
<div className="pb-12 pt-8">
<Mdx code={doc.body.code} />
</div>
<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-6">
<DashboardTableOfContents toc={toc} />
{doc.toc && (
<div className="hidden text-sm xl:block">
<div className="sticky top-16 -mt-10 h-[calc(100vh-3.5rem)] overflow-hidden pt-6">
<ScrollArea className="pb-10">
<DashboardTableOfContents toc={toc} />
</ScrollArea>
</div>
</div>
</div>
)}
</main>
)
}

View File

@@ -1,6 +1,6 @@
import { docsConfig } from "@/config/docs"
import { ScrollArea } from "@/components/ui/scroll-area"
import { DocsSidebarNav } from "@/components/sidebar-nav"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
interface DocsLayoutProps {
children: React.ReactNode
@@ -8,13 +8,15 @@ interface DocsLayoutProps {
export default function DocsLayout({ children }: DocsLayoutProps) {
return (
<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>
{children}
<div className="border-b">
<div className="container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10">
<aside className="fixed top-14 z-30 -ml-2 hidden h-[calc(100vh-3.5rem)] w-full shrink-0 md:sticky md:block">
<ScrollArea className="h-full py-6 pl-8 pr-6 lg:py-8">
<DocsSidebarNav items={docsConfig.sidebarNav} />
</ScrollArea>
</aside>
{children}
</div>
</div>
)
}

View File

@@ -3,10 +3,10 @@
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"
import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}

View File

@@ -4,7 +4,7 @@ import Link from "next/link"
import { Command } from "lucide-react"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { UserAuthForm } from "@/app/examples/authentication/components/user-auth-form"
export const metadata: Metadata = {
@@ -35,20 +35,14 @@ export default function AuthenticationPage() {
<Link
href="/examples/authentication"
className={cn(
buttonVariants({ variant: "ghost", size: "sm" }),
buttonVariants({ variant: "ghost" }),
"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="absolute inset-0 bg-zinc-900" />
<div className="relative z-20 flex items-center text-lg font-medium">
<Command className="mr-2 h-6 w-6" /> Acme Inc
</div>
@@ -57,7 +51,7 @@ export default function AuthenticationPage() {
<p className="text-lg">
&ldquo;This library has saved me countless hours of work and
helped me deliver stunning designs to my clients faster than
ever before. Highly recommended!&rdquo;
ever before.&rdquo;
</p>
<footer className="text-sm">Sofia Davis</footer>
</blockquote>

View File

@@ -1,6 +1,6 @@
"use client"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
@@ -8,9 +8,9 @@ import {
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
} from "@/registry/new-york/ui/card"
import { Label } from "@/registry/new-york/ui/label"
import { Switch } from "@/registry/new-york/ui/switch"
export function DemoCookieSettings() {
return (

View File

@@ -1,6 +1,7 @@
"use client"
import { Button } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
@@ -8,10 +9,9 @@ import {
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Icons } from "@/components/icons"
} from "@/registry/new-york/ui/card"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
export function DemoCreateAccount() {
return (

View File

@@ -1,6 +1,6 @@
import { Card, CardContent } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { CalendarDateRangePicker } from "@/components/examples/calendar/date-range-picker"
import DatePickerWithRange from "@/registry/default/example/date-picker-with-range"
import { Card, CardContent } from "@/registry/new-york/ui/card"
import { Label } from "@/registry/new-york/ui/label"
export function DemoDatePicker() {
return (
@@ -10,7 +10,7 @@ export function DemoDatePicker() {
<Label htmlFor="date" className="shrink-0">
Pick a date
</Label>
<CalendarDateRangePicker className="[&>button]:w-[260px]" />
<DatePickerWithRange className="[&>button]:w-[260px]" />
</div>
</CardContent>
</Card>

View File

@@ -1,13 +1,13 @@
import { ChevronDown, Circle, Plus, Star } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
} from "@/registry/new-york/ui/card"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
@@ -16,8 +16,8 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Separator } from "@/components/ui/separator"
} from "@/registry/new-york/ui/dropdown-menu"
import { Separator } from "@/registry/new-york/ui/separator"
export function DemoGithub() {
return (
@@ -71,7 +71,7 @@ export function DemoGithub() {
</div>
<div className="flex items-center">
<Star className="mr-1 h-3 w-3" />
10k
20k
</div>
<div>Updated April 2023</div>
</div>

View File

@@ -6,20 +6,20 @@ import {
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
} from "@/registry/new-york/ui/card"
export function DemoNotifications() {
return (
<Card>
<CardHeader>
<CardHeader className="pb-3">
<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" />
<CardContent className="grid gap-1">
<div className="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
<Bell className="mt-px 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">
@@ -27,8 +27,8 @@ export function DemoNotifications() {
</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="-mx-2 flex items-start space-x-4 rounded-md bg-accent p-2 text-accent-foreground transition-all">
<AtSign className="mt-px 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">
@@ -36,8 +36,8 @@ export function DemoNotifications() {
</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="-mx-2 flex items-start space-x-4 rounded-md p-2 transition-all hover:bg-accent hover:text-accent-foreground">
<BellOff className="mt-px 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">

View File

@@ -1,6 +1,7 @@
import { CreditCard } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Icons } from "@/components/icons"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
@@ -8,18 +9,17 @@ import {
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"
} from "@/registry/new-york/ui/card"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Icons } from "@/components/icons"
} from "@/registry/new-york/ui/select"
export function DemoPaymentMethod() {
return (

View File

@@ -1,6 +1,6 @@
"use client"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
@@ -8,17 +8,17 @@ import {
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
} from "@/registry/new-york/ui/card"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
} from "@/registry/new-york/ui/select"
import { Textarea } from "@/registry/new-york/ui/textarea"
export function DemoReportAnIssue() {
return (

View File

@@ -1,23 +1,27 @@
"use client"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
} from "@/registry/new-york/ui/card"
import { Input } from "@/registry/new-york/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
} from "@/registry/new-york/ui/select"
import { Separator } from "@/registry/new-york/ui/separator"
export function DemoShareDocument() {
return (

View File

@@ -1,14 +1,18 @@
import { ChevronDown } from "lucide-react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
import { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
} from "@/registry/new-york/ui/card"
import {
Command,
CommandEmpty,
@@ -16,12 +20,12 @@ import {
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
} from "@/registry/new-york/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
export function DemoTeamMembers() {
return (

View File

@@ -6,13 +6,13 @@ 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 { Button } from "@/registry/new-york/ui/button"
import { Calendar } from "@/registry/new-york/ui/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
export function CalendarDateRangePicker({
className,
@@ -29,9 +29,8 @@ export function CalendarDateRangePicker({
<Button
id="date"
variant={"outline"}
size="sm"
className={cn(
"w-[240px] justify-start text-left font-normal",
"w-[260px] justify-start text-left font-normal",
!date && "text-muted-foreground"
)}
>

View File

@@ -1,4 +1,8 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
export function RecentSales() {
return (

View File

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

View File

@@ -4,8 +4,12 @@ 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 {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
import { Button } from "@/registry/new-york/ui/button"
import {
Command,
CommandEmpty,
@@ -14,7 +18,7 @@ import {
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command"
} from "@/registry/new-york/ui/command"
import {
Dialog,
DialogContent,
@@ -23,21 +27,21 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
} from "@/registry/new-york/ui/dialog"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/registry/new-york/ui/select"
const groups = [
{
@@ -82,8 +86,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
variant="outline"
role="combobox"
aria-expanded={open}
aria-label="Select a team"
@@ -93,6 +96,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<AvatarImage
src={`https://avatar.vercel.sh/${selectedTeam.value}.png`}
alt={selectedTeam.label}
className="grayscale"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
@@ -120,6 +124,7 @@ export default function TeamSwitcher({ className }: TeamSwitcherProps) {
<AvatarImage
src={`https://avatar.vercel.sh/${team.value}.png`}
alt={team.label}
className="grayscale"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>

View File

@@ -1,7 +1,11 @@
import { CreditCard, LogOut, PlusCircle, Settings, User } from "lucide-react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/registry/new-york/ui/avatar"
import { Button } from "@/registry/new-york/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
@@ -11,7 +15,7 @@ import {
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
} from "@/registry/new-york/ui/dropdown-menu"
export function UserNav() {
return (

View File

@@ -2,15 +2,20 @@ 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 { Button } from "@/registry/new-york/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
} from "@/registry/new-york/ui/card"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/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"
@@ -59,7 +64,7 @@ export default function DashboardPage() {
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
<div className="flex items-center space-x-2">
<CalendarDateRangePicker />
<Button size="sm">
<Button>
<Download className="mr-2 h-4 w-4" />
Download
</Button>

View File

@@ -0,0 +1,219 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { format } from "date-fns"
import { CalendarIcon, Check, ChevronsUpDown } from "lucide-react"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button"
import { Calendar } from "@/registry/new-york/ui/calendar"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/registry/new-york/ui/command"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { Input } from "@/registry/new-york/ui/input"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york/ui/popover"
import { toast } from "@/registry/new-york/ui/use-toast"
const languages = [
{ label: "English", value: "en" },
{ label: "French", value: "fr" },
{ label: "German", value: "de" },
{ label: "Spanish", value: "es" },
{ label: "Portuguese", value: "pt" },
{ label: "Russian", value: "ru" },
{ label: "Japanese", value: "ja" },
{ label: "Korean", value: "ko" },
{ label: "Chinese", value: "zh" },
] as const
const accountFormSchema = z.object({
name: z
.string()
.min(2, {
message: "Name must be at least 2 characters.",
})
.max(30, {
message: "Name must not be longer than 30 characters.",
}),
dob: z.date({
required_error: "A date of birth is required.",
}),
language: z.string({
required_error: "Please select a language.",
}),
})
type AccountFormValues = z.infer<typeof accountFormSchema>
// This can come from your database or API.
const defaultValues: Partial<AccountFormValues> = {
// name: "Your name",
// dob: new Date("2023-01-23"),
}
export function AccountForm() {
const form = useForm<AccountFormValues>({
resolver: zodResolver(accountFormSchema),
defaultValues,
})
function onSubmit(data: AccountFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Your name" {...field} />
</FormControl>
<FormDescription>
This is the name that will be displayed on your profile and in
emails.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="dob"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Date of birth</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-[240px] pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>
Your date of birth is used to calculate your age.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="language"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Language</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
"w-[200px] justify-between",
!field.value && "text-muted-foreground"
)}
>
{field.value
? languages.find(
(language) => language.value === field.value
)?.label
: "Select language"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandInput placeholder="Search language..." />
<CommandEmpty>No language found.</CommandEmpty>
<CommandGroup>
{languages.map((language) => (
<CommandItem
value={language.value}
key={language.value}
onSelect={(value) => {
form.setValue("language", value)
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
language.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
{language.label}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
<FormDescription>
This is the language that will be used in the dashboard.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Update account</Button>
</form>
</Form>
)
}

View File

@@ -0,0 +1,18 @@
import { Separator } from "@/registry/new-york/ui/separator"
import { AccountForm } from "@/app/examples/forms/account/account-form"
export default function SettingsAccountPage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">Account</h3>
<p className="text-sm text-muted-foreground">
Update your account settings. Set your preferred language and
timezone.
</p>
</div>
<Separator />
<AccountForm />
</div>
)
}

View File

@@ -0,0 +1,164 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { ChevronDown } from "lucide-react"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/registry/new-york/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
import { toast } from "@/registry/new-york/ui/use-toast"
const appearanceFormSchema = z.object({
theme: z.enum(["light", "dark"], {
required_error: "Please select a theme.",
}),
font: z.enum(["inter", "manrope", "system"], {
invalid_type_error: "Select a font",
required_error: "Please select a font.",
}),
})
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>
// This can come from your database or API.
const defaultValues: Partial<AppearanceFormValues> = {
theme: "light",
}
export function AppearanceForm() {
const form = useForm<AppearanceFormValues>({
resolver: zodResolver(appearanceFormSchema),
defaultValues,
})
function onSubmit(data: AppearanceFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="font"
render={({ field }) => (
<FormItem>
<FormLabel>Font</FormLabel>
<div className="relative w-max">
<FormControl>
<select
className={cn(
buttonVariants({ variant: "outline" }),
"w-[200px] appearance-none bg-transparent font-normal"
)}
{...field}
>
<option value="inter">Inter</option>
<option value="manrope">Manrope</option>
<option value="system">System</option>
</select>
</FormControl>
<ChevronDown className="absolute right-3 top-3 h-4 w-4 opacity-50" />
</div>
<FormDescription>
Set the font you want to use in the dashboard.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="theme"
render={({ field }) => (
<FormItem className="space-y-1">
<FormLabel>Theme</FormLabel>
<FormDescription>
Select the theme for the dashboard.
</FormDescription>
<FormMessage />
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="grid max-w-md grid-cols-2 gap-8 pt-2"
>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="light" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent">
<div className="space-y-2 rounded-sm bg-[#ecedef] p-2">
<div className="space-y-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-[#ecedef]" />
<div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" />
</div>
</div>
</div>
<span className="block w-full p-2 text-center font-normal">
Light
</span>
</FormLabel>
</FormItem>
<FormItem>
<FormLabel className="[&:has([data-state=checked])>div]:border-primary">
<FormControl>
<RadioGroupItem value="dark" className="sr-only" />
</FormControl>
<div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground">
<div className="space-y-2 rounded-sm bg-slate-950 p-2">
<div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-2 w-[80px] rounded-lg bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
<div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm">
<div className="h-4 w-4 rounded-full bg-slate-400" />
<div className="h-2 w-[100px] rounded-lg bg-slate-400" />
</div>
</div>
</div>
<span className="block w-full p-2 text-center font-normal">
Dark
</span>
</FormLabel>
</FormItem>
</RadioGroup>
</FormItem>
)}
/>
<Button type="submit">Update preferences</Button>
</form>
</Form>
)
}

View File

@@ -0,0 +1,18 @@
import { Separator } from "@/registry/new-york/ui/separator"
import { AppearanceForm } from "@/app/examples/forms/appearance/appearance-form"
export default function SettingsAppearancePage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">Appearance</h3>
<p className="text-sm text-muted-foreground">
Customize the appearance of the app. Automatically switch between day
and night themes.
</p>
</div>
<Separator />
<AppearanceForm />
</div>
)
}

View File

@@ -0,0 +1,44 @@
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/registry/new-york/ui/button"
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: {
href: string
title: string
}[]
}
export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
const pathname = usePathname()
return (
<nav
className={cn(
"flex space-x-2 lg:flex-col lg:space-x-0 lg:space-y-1",
className
)}
{...props}
>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href
? "bg-muted hover:bg-muted"
: "hover:bg-transparent hover:underline",
"justify-start"
)}
>
{item.title}
</Link>
))}
</nav>
)
}

View File

@@ -0,0 +1,132 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { Button } from "@/registry/new-york/ui/button"
import { Checkbox } from "@/registry/new-york/ui/checkbox"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { toast } from "@/registry/new-york/ui/use-toast"
const items = [
{
id: "recents",
label: "Recents",
},
{
id: "home",
label: "Home",
},
{
id: "applications",
label: "Applications",
},
{
id: "desktop",
label: "Desktop",
},
{
id: "downloads",
label: "Downloads",
},
{
id: "documents",
label: "Documents",
},
] as const
const displayFormSchema = z.object({
items: z.array(z.string()).refine((value) => value.some((item) => item), {
message: "You have to select at least one item.",
}),
})
type DisplayFormValues = z.infer<typeof displayFormSchema>
// This can come from your database or API.
const defaultValues: Partial<DisplayFormValues> = {
items: ["recents", "home"],
}
export function DisplayForm() {
const form = useForm<DisplayFormValues>({
resolver: zodResolver(displayFormSchema),
defaultValues,
})
function onSubmit(data: DisplayFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="items"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-base">Sidebar</FormLabel>
<FormDescription>
Select the items you want to display in the sidebar.
</FormDescription>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="items"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([...field.value, item.id])
: field.onChange(
field.value?.filter(
(value) => value !== item.id
)
)
}}
/>
</FormControl>
<FormLabel className="font-normal">
{item.label}
</FormLabel>
</FormItem>
)
}}
/>
))}
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Update display</Button>
</form>
</Form>
)
}

View File

@@ -0,0 +1,17 @@
import { Separator } from "@/registry/new-york/ui/separator"
import { DisplayForm } from "@/app/examples/forms/display/display-form"
export default function SettingsDisplayPage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">Display</h3>
<p className="text-sm text-muted-foreground">
Turn items on or off to control what&apos;s displayed in the app.
</p>
</div>
<Separator />
<DisplayForm />
</div>
)
}

View File

@@ -0,0 +1,75 @@
import { Metadata } from "next"
import Image from "next/image"
import { Separator } from "@/registry/new-york/ui/separator"
import { SidebarNav } from "@/app/examples/forms/components/sidebar-nav"
export const metadata: Metadata = {
title: "Forms",
description: "Advanced form example using react-hook-form and Zod.",
}
const sidebarNavItems = [
{
title: "Profile",
href: "/examples/forms",
},
{
title: "Account",
href: "/examples/forms/account",
},
{
title: "Appearance",
href: "/examples/forms/appearance",
},
{
title: "Notifications",
href: "/examples/forms/notifications",
},
{
title: "Display",
href: "/examples/forms/display",
},
]
interface SettingsLayoutProps {
children: React.ReactNode
}
export default function SettingsLayout({ children }: SettingsLayoutProps) {
return (
<>
<div className="md:hidden">
<Image
src="/examples/forms-light.png"
width={1280}
height={791}
alt="Forms"
className="block dark:hidden"
/>
<Image
src="/examples/forms-dark.png"
width={1280}
height={791}
alt="Forms"
className="hidden dark:block"
/>
</div>
<div className="hidden space-y-6 p-10 pb-16 md:block">
<div className="space-y-0.5">
<h2 className="text-2xl font-bold tracking-tight">Settings</h2>
<p className="text-muted-foreground">
Manage your account settings and set e-mail preferences.
</p>
</div>
<Separator className="my-6" />
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<aside className="-mx-4 lg:w-1/5">
<SidebarNav items={sidebarNavItems} />
</aside>
<div className="flex-1 lg:max-w-2xl">{children}</div>
</div>
</div>
</>
)
}

View File

@@ -0,0 +1,222 @@
"use client"
import Link from "next/link"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { Button } from "@/registry/new-york/ui/button"
import { Checkbox } from "@/registry/new-york/ui/checkbox"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { RadioGroup, RadioGroupItem } from "@/registry/new-york/ui/radio-group"
import { Switch } from "@/registry/new-york/ui/switch"
import { toast } from "@/registry/new-york/ui/use-toast"
const notificationsFormSchema = z.object({
type: z.enum(["all", "mentions", "none"], {
required_error: "You need to select a notification type.",
}),
mobile: z.boolean().default(false).optional(),
communication_emails: z.boolean().default(false).optional(),
social_emails: z.boolean().default(false).optional(),
marketing_emails: z.boolean().default(false).optional(),
security_emails: z.boolean(),
})
type NotificationsFormValues = z.infer<typeof notificationsFormSchema>
// This can come from your database or API.
const defaultValues: Partial<NotificationsFormValues> = {
communication_emails: false,
marketing_emails: false,
social_emails: true,
security_emails: true,
}
export function NotificationsForm() {
const form = useForm<NotificationsFormValues>({
resolver: zodResolver(notificationsFormSchema),
defaultValues,
})
function onSubmit(data: NotificationsFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Notify me about...</FormLabel>
<FormControl>
<RadioGroup
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-col space-y-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="all" />
</FormControl>
<FormLabel className="font-normal">
All new messages
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="mentions" />
</FormControl>
<FormLabel className="font-normal">
Direct messages and mentions
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="none" />
</FormControl>
<FormLabel className="font-normal">Nothing</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div>
<h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
<div className="space-y-4">
<FormField
control={form.control}
name="communication_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">
Communication emails
</FormLabel>
<FormDescription>
Receive emails about your account activity.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="marketing_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">
Marketing emails
</FormLabel>
<FormDescription>
Receive emails about new products, features, and more.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="social_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">Social emails</FormLabel>
<FormDescription>
Receive emails for friend requests, follows, and more.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="security_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<FormLabel className="text-base">Security emails</FormLabel>
<FormDescription>
Receive emails about your account activity and security.
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<FormField
control={form.control}
name="mobile"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
Use different settings for my mobile devices
</FormLabel>
<FormDescription>
You can manage your mobile notifications in the{" "}
<Link href="/examples/forms">mobile settings</Link> page.
</FormDescription>
</div>
</FormItem>
)}
/>
<Button type="submit">Update notifications</Button>
</form>
</Form>
)
}

View File

@@ -0,0 +1,18 @@
import { Separator } from "@/registry/new-york/ui/separator"
import { AccountForm } from "@/app/examples/forms/account/account-form"
import { NotificationsForm } from "@/app/examples/forms/notifications/notifications-form"
export default function SettingsNotificationsPage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">Notifications</h3>
<p className="text-sm text-muted-foreground">
Configure how you receive notifications.
</p>
</div>
<Separator />
<NotificationsForm />
</div>
)
}

View File

@@ -0,0 +1,17 @@
import { Separator } from "@/registry/new-york/ui/separator"
import { ProfileForm } from "@/app/examples/forms/profile-form"
export default function SettingsProfilePage() {
return (
<div className="space-y-6">
<div>
<h3 className="text-lg font-medium">Profile</h3>
<p className="text-sm text-muted-foreground">
This is how others will see you on the site.
</p>
</div>
<Separator />
<ProfileForm />
</div>
)
}

View File

@@ -0,0 +1,191 @@
"use client"
import Link from "next/link"
import { zodResolver } from "@hookform/resolvers/zod"
import { useFieldArray, useForm } from "react-hook-form"
import * as z from "zod"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/registry/new-york/ui/form"
import { Input } from "@/registry/new-york/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york/ui/select"
import { Textarea } from "@/registry/new-york/ui/textarea"
import { toast } from "@/registry/new-york/ui/use-toast"
const profileFormSchema = z.object({
username: z
.string()
.min(2, {
message: "Username must be at least 2 characters.",
})
.max(30, {
message: "Username must not be longer than 30 characters.",
}),
email: z
.string({
required_error: "Please select an email to display.",
})
.email(),
bio: z.string().max(160).min(4),
urls: z
.array(
z.object({
value: z.string().url({ message: "Please enter a valid URL." }),
})
)
.optional(),
})
type ProfileFormValues = z.infer<typeof profileFormSchema>
// This can come from your database or API.
const defaultValues: Partial<ProfileFormValues> = {
bio: "I own a computer.",
urls: [
{ value: "https://shadcn.com" },
{ value: "http://twitter.com/shadcn" },
],
}
export function ProfileForm() {
const form = useForm<ProfileFormValues>({
resolver: zodResolver(profileFormSchema),
defaultValues,
mode: "onChange",
})
const { fields, append } = useFieldArray({
name: "urls",
control: form.control,
})
function onSubmit(data: ProfileFormValues) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name. It can be your real name or a
pseudonym. You can only change this once every 30 days.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="m@example.com">m@example.com</SelectItem>
<SelectItem value="m@google.com">m@google.com</SelectItem>
<SelectItem value="m@support.com">m@support.com</SelectItem>
</SelectContent>
</Select>
<FormDescription>
You can manage verified email addresses in your{" "}
<Link href="/examples/forms">email settings</Link>.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us a little bit about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
You can <span>@mention</span> other users and organizations to
link to them.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<div>
{fields.map((field, index) => (
<FormField
control={form.control}
key={field.id}
name={`urls.${index}.value`}
render={({ field }) => (
<FormItem>
<FormLabel className={cn(index !== 0 && "sr-only")}>
URLs
</FormLabel>
<FormDescription className={cn(index !== 0 && "sr-only")}>
Add links to your website, blog, or social media profiles.
</FormDescription>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
))}
<Button
type="button"
variant="outline"
size="sm"
className="mt-2"
onClick={() => append({ value: "" })}
>
Add URL
</Button>
</div>
<Button type="submit">Update profile</Button>
</form>
</Form>
)
}

View File

@@ -1,14 +1,16 @@
import { Metadata } from "next"
import Link from "next/link"
import { ChevronRight } from "lucide-react"
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"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
export const metadata: Metadata = {
title: "Examples",
@@ -22,8 +24,19 @@ interface ExamplesLayoutProps {
export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
return (
<>
<div className="container relative pb-10">
<PageHeader className="page-header">
<div className="container relative">
<PageHeader className="page-header pb-8">
<Link
href="/docs/changelog"
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="sm:hidden">Style, a new CLI and more.</span>
<span className="hidden sm:inline">
Introducing Style, a new CLI and more.
</span>
<ChevronRight className="ml-1 h-4 w-4" />
</Link>
<PageHeaderHeading className="hidden md:block">
Check out some examples.
</PageHeaderHeading>
@@ -32,34 +45,27 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
Dashboard, cards, authentication. Some examples built using the
components. Use this as a guide to build your own.
</PageHeaderDescription>
<section className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<Link
href="/docs"
className={cn(buttonVariants(), "rounded-[6px]")}
>
Get Started
</Link>
<Link
href="/components"
className={cn(
buttonVariants({ variant: "outline" }),
"rounded-[6px]"
)}
>
Components
</Link>
</section>
</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">
<div className="overflow-hidden rounded-[0.5rem] border bg-background shadow">
{children}
</div>
</section>

View File

@@ -2,7 +2,7 @@ import Image from "next/image"
import { ListMusic, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { AspectRatio } from "@/registry/new-york/ui/aspect-ratio"
import {
ContextMenu,
ContextMenuContent,
@@ -12,7 +12,7 @@ import {
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
} from "@/registry/new-york/ui/context-menu"
import { Album } from "../data/albums"
import { playlists } from "../data/playlists"

View File

@@ -15,7 +15,7 @@ import {
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar"
} from "@/registry/new-york/ui/menubar"
export function Menu() {
return (

View File

@@ -1,6 +1,6 @@
import { Plus, Podcast } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Dialog,
DialogContent,
@@ -9,9 +9,9 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
} from "@/registry/new-york/ui/dialog"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
export function PodcastEmptyPlaceholder() {
return (

View File

@@ -11,8 +11,8 @@ import {
} from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Button } from "@/registry/new-york/ui/button"
import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
import { Playlist } from "../data/playlists"
@@ -24,66 +24,62 @@ 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">
<div className="px-3 py-2">
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
Discover
</h2>
<div className="space-y-1">
<Button
variant="secondary"
size="sm"
className="w-full justify-start"
>
<Button variant="secondary" 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">
<Button variant="ghost" 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">
<Button variant="ghost" 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">
<div className="px-3 py-2">
<h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
Library
</h2>
<div className="space-y-1">
<Button variant="ghost" size="sm" className="w-full justify-start">
<Button variant="ghost" 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">
<Button variant="ghost" 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">
<Button variant="ghost" 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">
<Button variant="ghost" 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">
<Button variant="ghost" 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">
<h2 className="relative px-7 text-lg font-semibold tracking-tight">
Playlists
</h2>
<ScrollArea className="h-[300px] px-2">
<ScrollArea className="h-[300px] px-1">
<div className="space-y-1 p-2">
{playlists?.map((playlist) => (
{playlists?.map((playlist, i) => (
<Button
key={`${playlist}-${i}`}
variant="ghost"
size="sm"
className="w-full justify-start font-normal"
>
<ListMusic className="mr-2 h-4 w-4" />

View File

@@ -1,8 +1,13 @@
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 { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
import { Separator } from "@/registry/new-york/ui/separator"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
import { AlbumArtwork } from "./components/album-artwork"
import { Menu } from "./components/menu"
@@ -14,7 +19,7 @@ import "./styles.css"
import Image from "next/image"
import { PlusCircle } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
export const metadata: Metadata = {
title: "Music App",

View File

@@ -1,4 +1,4 @@
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Dialog,
DialogContent,
@@ -6,7 +6,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
} from "@/registry/new-york/ui/dialog"
export function CodeViewer() {
return (

View File

@@ -7,9 +7,9 @@ import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
} from "@/registry/new-york/ui/hover-card"
import { Label } from "@/registry/new-york/ui/label"
import { Slider } from "@/registry/new-york/ui/slider"
interface MaxLengthSelectorProps {
defaultValue: SliderProps["defaultValue"]

View File

@@ -6,7 +6,7 @@ import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils"
import { useMutationObserver } from "@/hooks/use-mutation-observer"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Command,
CommandEmpty,
@@ -14,18 +14,18 @@ import {
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
} from "@/registry/new-york/ui/command"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
} from "@/registry/new-york/ui/hover-card"
import { Label } from "@/registry/new-york/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
import { Model, ModelType } from "../data/models"

View File

@@ -12,25 +12,25 @@ import {
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
} from "@/registry/new-york/ui/alert-dialog"
import { Button } from "@/registry/new-york/ui/button"
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
} from "@/registry/new-york/ui/dialog"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { toast } from "@/components/ui/use-toast"
} from "@/registry/new-york/ui/dropdown-menu"
import { Label } from "@/registry/new-york/ui/label"
import { Switch } from "@/registry/new-york/ui/switch"
import { toast } from "@/registry/new-york/ui/use-toast"
export function PresetActions() {
const [open, setIsOpen] = React.useState(false)

View File

@@ -1,4 +1,4 @@
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Dialog,
DialogContent,
@@ -7,9 +7,9 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
} from "@/registry/new-york/ui/dialog"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
export function PresetSave() {
return (

View File

@@ -6,19 +6,19 @@ import { PopoverProps } from "@radix-ui/react-popover"
import { Check, ChevronsUpDown } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command"
} from "@/registry/new-york/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
import { Preset } from "../data/presets"

View File

@@ -1,13 +1,13 @@
import { Copy } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input"
import { Label } from "@/registry/new-york/ui/label"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
} from "@/registry/new-york/ui/popover"
export function PresetShare() {
return (

View File

@@ -7,9 +7,9 @@ import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
} from "@/registry/new-york/ui/hover-card"
import { Label } from "@/registry/new-york/ui/label"
import { Slider } from "@/registry/new-york/ui/slider"
interface TemperatureSelectorProps {
defaultValue: SliderProps["defaultValue"]

View File

@@ -7,9 +7,9 @@ import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
} from "@/registry/new-york/ui/hover-card"
import { Label } from "@/registry/new-york/ui/label"
import { Slider } from "@/registry/new-york/ui/slider"
interface TopPSelectorProps {
defaultValue: SliderProps["defaultValue"]

View File

@@ -1,16 +1,21 @@
import { Metadata } from "next"
import { History } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/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"
} from "@/registry/new-york/ui/hover-card"
import { Label } from "@/registry/new-york/ui/label"
import { Separator } from "@/registry/new-york/ui/separator"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
import { Textarea } from "@/registry/new-york/ui/textarea"
import { CodeViewer } from "./components/code-viewer"
import { Icons } from "./components/icons"

View File

@@ -0,0 +1,120 @@
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { Badge } from "@/registry/new-york/ui/badge"
import { Checkbox } from "@/registry/new-york/ui/checkbox"
import { labels, priorities, statuses } from "../data/data"
import { Task } from "../data/schema"
import { DataTableColumnHeader } from "./data-table-column-header"
import { DataTableRowActions } from "./data-table-row-actions"
export const columns: ColumnDef<Task>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
className="translate-y-[2px]"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
className="translate-y-[2px]"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "id",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Task" />
),
cell: ({ row }) => <div className="w-[80px]">{row.getValue("id")}</div>,
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "title",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Title" />
),
cell: ({ row }) => {
const label = labels.find((label) => label.value === row.original.label)
return (
<div className="flex space-x-2">
{label && <Badge variant="outline">{label.label}</Badge>}
<span className="max-w-[500px] truncate font-medium">
{row.getValue("title")}
</span>
</div>
)
},
},
{
accessorKey: "status",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Status" />
),
cell: ({ row }) => {
const status = statuses.find(
(status) => status.value === row.getValue("status")
)
if (!status) {
return null
}
return (
<div className="flex w-[100px] items-center">
{status.icon && (
<status.icon className="mr-2 h-4 w-4 text-muted-foreground" />
)}
<span>{status.label}</span>
</div>
)
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
accessorKey: "priority",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Priority" />
),
cell: ({ row }) => {
const priority = priorities.find(
(priority) => priority.value === row.getValue("priority")
)
if (!priority) {
return null
}
return (
<div className="flex items-center">
{priority.icon && (
<priority.icon className="mr-2 h-4 w-4 text-muted-foreground" />
)}
<span>{priority.label}</span>
</div>
)
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id))
},
},
{
id: "actions",
cell: ({ row }) => <DataTableRowActions row={row} />,
},
]

View File

@@ -0,0 +1,66 @@
import { Column } from "@tanstack/react-table"
import { ChevronsUpDown, EyeOff, SortAsc, SortDesc } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/new-york/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
interface DataTableColumnHeaderProps<TData, TValue>
extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
title: string
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>
}
return (
<div className={cn("flex items-center space-x-2", className)}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="-ml-3 h-8 data-[state=open]:bg-accent"
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<SortDesc className="ml-2 h-4 w-4" />
) : column.getIsSorted() === "asc" ? (
<SortAsc className="ml-2 h-4 w-4" />
) : (
<ChevronsUpDown className="ml-2 h-4 w-4" />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<SortAsc className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<SortDesc className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeOff className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}

View File

@@ -0,0 +1,147 @@
import * as React from "react"
import { Column } from "@tanstack/react-table"
import { Check, LucideIcon, PlusCircle } from "lucide-react"
import { cn } from "@/lib/utils"
import { Badge } from "@/registry/new-york/ui/badge"
import { Button } from "@/registry/new-york/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/registry/new-york/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/registry/new-york/ui/popover"
import { Separator } from "@/registry/new-york/ui/separator"
interface DataTableFacetedFilter<TData, TValue> {
column?: Column<TData, TValue>
title?: string
options: {
label: string
value: string
icon?: LucideIcon
}[]
}
export function DataTableFacetedFilter<TData, TValue>({
column,
title,
options,
}: DataTableFacetedFilter<TData, TValue>) {
const facets = column?.getFacetedUniqueValues()
const selectedValues = new Set(column?.getFilterValue() as string[])
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className="h-8 border-dashed">
<PlusCircle className="mr-2 h-4 w-4" />
{title}
{selectedValues?.size > 0 && (
<>
<Separator orientation="vertical" className="mx-2 h-4" />
<Badge
variant="secondary"
className="rounded-sm px-1 font-normal lg:hidden"
>
{selectedValues.size}
</Badge>
<div className="hidden space-x-1 lg:flex">
{selectedValues.size > 2 ? (
<Badge
variant="secondary"
className="rounded-sm px-1 font-normal"
>
{selectedValues.size} selected
</Badge>
) : (
options
.filter((option) => selectedValues.has(option.value))
.map((option) => (
<Badge
variant="secondary"
key={option.value}
className="rounded-sm px-1 font-normal"
>
{option.label}
</Badge>
))
)}
</div>
</>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0" align="start">
<Command>
<CommandInput placeholder={title} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
{options.map((option) => {
const isSelected = selectedValues.has(option.value)
return (
<CommandItem
key={option.value}
onSelect={() => {
if (isSelected) {
selectedValues.delete(option.value)
} else {
selectedValues.add(option.value)
}
const filterValues = Array.from(selectedValues)
column?.setFilterValue(
filterValues.length ? filterValues : undefined
)
}}
>
<div
className={cn(
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
isSelected
? "bg-primary text-primary-foreground"
: "opacity-50 [&_svg]:invisible"
)}
>
<Check className={cn("h-4 w-4")} />
</div>
{option.icon && (
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
)}
<span>{option.label}</span>
{facets?.get(option.value) && (
<span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
{facets.get(option.value)}
</span>
)}
</CommandItem>
)
})}
</CommandGroup>
{selectedValues.size > 0 && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
onSelect={() => column?.setFilterValue(undefined)}
className="justify-center text-center"
>
Clear filters
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}

View File

@@ -0,0 +1,97 @@
import { Table } from "@tanstack/react-table"
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york/ui/select"
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronsLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRight className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronsRight className="h-4 w-4" />
</Button>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,82 @@
"use client"
import { Row } from "@tanstack/react-table"
import { Copy, MoreHorizontal, Pen, Star, Tags, Trash } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/registry/new-york/ui/dropdown-menu"
import { labels } from "../data/data"
import { taskSchema } from "../data/schema"
interface DataTableRowActionsProps<TData> {
row: Row<TData>
}
export function DataTableRowActions<TData>({
row,
}: DataTableRowActionsProps<TData>) {
const task = taskSchema.parse(row.original)
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[160px]">
<DropdownMenuItem>
<Pen className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Edit
</DropdownMenuItem>
<DropdownMenuItem>
<Copy className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Make a copy
</DropdownMenuItem>
<DropdownMenuItem>
<Star className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Favorite
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<Tags className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Labels
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuRadioGroup value={task.label}>
{labels.map((label) => (
<DropdownMenuRadioItem key={label.value} value={label.value}>
{label.label}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Trash className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Delete
<DropdownMenuShortcut></DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -0,0 +1,63 @@
"use client"
import { Table } from "@tanstack/react-table"
import { X } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import { Input } from "@/registry/new-york/ui/input"
import { DataTableViewOptions } from "@/app/examples/tasks/components/data-table-view-options"
import { priorities, statuses } from "../data/data"
import { DataTableFacetedFilter } from "./data-table-faceted-filter"
interface DataTableToolbarProps<TData> {
table: Table<TData>
}
export function DataTableToolbar<TData>({
table,
}: DataTableToolbarProps<TData>) {
const isFiltered =
table.getPreFilteredRowModel().rows.length >
table.getFilteredRowModel().rows.length
return (
<div className="flex items-center justify-between">
<div className="flex flex-1 items-center space-x-2">
<Input
placeholder="Filter tasks..."
value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("title")?.setFilterValue(event.target.value)
}
className="h-8 w-[150px] lg:w-[250px]"
/>
{table.getColumn("status") && (
<DataTableFacetedFilter
column={table.getColumn("status")}
title="Status"
options={statuses}
/>
)}
{table.getColumn("priority") && (
<DataTableFacetedFilter
column={table.getColumn("priority")}
title="Priority"
options={priorities}
/>
)}
{isFiltered && (
<Button
variant="ghost"
onClick={() => table.resetColumnFilters()}
className="h-8 px-2 lg:px-3"
>
Reset
<X className="ml-2 h-4 w-4" />
</Button>
)}
</div>
<DataTableViewOptions table={table} />
</div>
)
}

View File

@@ -0,0 +1,59 @@
"use client"
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
import { Table } from "@tanstack/react-table"
import { SlidersHorizontal } from "lucide-react"
import { Button } from "@/registry/new-york/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@/registry/new-york/ui/dropdown-menu"
interface DataTableViewOptionsProps<TData> {
table: Table<TData>
}
export function DataTableViewOptions<TData>({
table,
}: DataTableViewOptionsProps<TData>) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
>
<SlidersHorizontal className="mr-2 h-4 w-4" />
View
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== "undefined" && column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@@ -0,0 +1,126 @@
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/registry/new-york/ui/table"
import { DataTablePagination } from "../components/data-table-pagination"
import { DataTableToolbar } from "../components/data-table-toolbar"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [rowSelection, setRowSelection] = React.useState({})
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [sorting, setSorting] = React.useState<SortingState>([])
const table = useReactTable({
data,
columns,
state: {
sorting,
columnVisibility,
rowSelection,
columnFilters,
},
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
})
return (
<div className="space-y-4">
<DataTableToolbar table={table} />
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<DataTablePagination table={table} />
</div>
)
}

View File

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

View File

@@ -0,0 +1,71 @@
import {
ArrowDownToLine,
ArrowRightToLine,
ArrowUpCircle,
ArrowUpToLine,
CheckCircle2,
Circle,
HelpCircle,
XCircle,
} from "lucide-react"
export const labels = [
{
value: "bug",
label: "Bug",
},
{
value: "feature",
label: "Feature",
},
{
value: "documentation",
label: "Documentation",
},
]
export const statuses = [
{
value: "backlog",
label: "Backlog",
icon: HelpCircle,
},
{
value: "todo",
label: "Todo",
icon: Circle,
},
{
value: "in progress",
label: "In Progress",
icon: ArrowUpCircle,
},
{
value: "done",
label: "Done",
icon: CheckCircle2,
},
{
value: "canceled",
label: "Canceled",
icon: XCircle,
},
]
export const priorities = [
{
label: "Low",
value: "low",
icon: ArrowDownToLine,
},
{
label: "Medium",
value: "medium",
icon: ArrowRightToLine,
},
{
label: "High",
value: "high",
icon: ArrowUpToLine,
},
]

View File

@@ -0,0 +1,13 @@
import { z } from "zod"
// We're keeping a simple non-relational schema here.
// IRL, you will have a schema for your data models.
export const taskSchema = z.object({
id: z.string(),
title: z.string(),
status: z.string(),
label: z.string(),
priority: z.string(),
})
export type Task = z.infer<typeof taskSchema>

View File

@@ -0,0 +1,20 @@
import fs from "fs"
import path from "path"
import { faker } from "@faker-js/faker"
import { labels, priorities, statuses } from "./data"
const tasks = Array.from({ length: 100 }, () => ({
id: `TASK-${faker.datatype.number({ min: 1000, max: 9999 })}`,
title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()),
status: faker.helpers.arrayElement(statuses).value,
label: faker.helpers.arrayElement(labels).value,
priority: faker.helpers.arrayElement(priorities).value,
}))
fs.writeFileSync(
path.join(__dirname, "tasks.json"),
JSON.stringify(tasks, null, 2)
)
console.log("✅ Tasks data generated.")

View File

@@ -0,0 +1,702 @@
[
{
"id": "TASK-8782",
"title": "You can't compress the program without quantifying the open-source SSD pixel!",
"status": "in progress",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-7878",
"title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-7839",
"title": "We need to bypass the neural TCP card!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5562",
"title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!",
"status": "backlog",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-8686",
"title": "I'll parse the wireless SSL protocol, that should driver the API panel!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-1280",
"title": "Use the digital TLS panel, then you can transmit the haptic system!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-7262",
"title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!",
"status": "done",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1138",
"title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7184",
"title": "We need to program the back-end THX pixel!",
"status": "todo",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-5160",
"title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!",
"status": "in progress",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-5618",
"title": "Generating the driver won't do anything, we need to index the online SSL application!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-6699",
"title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-2858",
"title": "We need to override the online UDP bus!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-9864",
"title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-8404",
"title": "We need to generate the virtual HEX alarm!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5365",
"title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!",
"status": "in progress",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1780",
"title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-6938",
"title": "Use the redundant SCSI application, then you can hack the optical alarm!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-9885",
"title": "We need to compress the auxiliary VGA driver!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-3216",
"title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!",
"status": "backlog",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-9285",
"title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-1024",
"title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!",
"status": "in progress",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-7068",
"title": "You can't generate the capacitor without indexing the wireless HEX pixel!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-6502",
"title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!",
"status": "todo",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5326",
"title": "We need to hack the redundant UTF8 transmitter!",
"status": "todo",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-6274",
"title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1571",
"title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9518",
"title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!",
"status": "canceled",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-5581",
"title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!",
"status": "backlog",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-2197",
"title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8484",
"title": "We need to parse the solid state UDP firewall!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-9892",
"title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!",
"status": "done",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-9616",
"title": "We need to synthesize the cross-platform ASCII pixel!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9744",
"title": "Use the back-end IP card, then you can input the solid state hard drive!",
"status": "done",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1376",
"title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-7382",
"title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!",
"status": "todo",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-2290",
"title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!",
"status": "canceled",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-1533",
"title": "You can't input the firewall without overriding the wireless TCP firewall!",
"status": "done",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-4920",
"title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!",
"status": "in progress",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-5168",
"title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7103",
"title": "We need to parse the multi-byte EXE bandwidth!",
"status": "canceled",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-4314",
"title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!",
"status": "in progress",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-3415",
"title": "Use the cross-platform XML application, then you can quantify the solid state feed!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-8339",
"title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6995",
"title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-8053",
"title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!",
"status": "todo",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-4336",
"title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8790",
"title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!",
"status": "done",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-8980",
"title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-7342",
"title": "Use the neural CLI card, then you can parse the online port!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-5608",
"title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-1606",
"title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7872",
"title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-4167",
"title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!",
"status": "canceled",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-9581",
"title": "You can't index the port without hacking the cross-platform XSS monitor!",
"status": "backlog",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-8806",
"title": "We need to bypass the back-end SSL panel!",
"status": "done",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-6542",
"title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!",
"status": "done",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6806",
"title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-9549",
"title": "You can't bypass the bus without connecting the neural JBOD bus!",
"status": "todo",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1075",
"title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-1427",
"title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!",
"status": "done",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-1907",
"title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!",
"status": "todo",
"label": "documentation",
"priority": "high"
},
{
"id": "TASK-4309",
"title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3973",
"title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!",
"status": "todo",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-7962",
"title": "Use the wireless RAM program, then you can hack the cross-platform feed!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-3360",
"title": "You can't quantify the program without synthesizing the neural OCR interface!",
"status": "done",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-9887",
"title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!",
"status": "backlog",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3649",
"title": "I'll input the virtual USB system, that should circuit the DNS monitor!",
"status": "in progress",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-3586",
"title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!",
"status": "in progress",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5150",
"title": "I'll hack the wireless XSS port, that should transmitter the IP interface!",
"status": "canceled",
"label": "feature",
"priority": "medium"
},
{
"id": "TASK-3652",
"title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!",
"status": "backlog",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6884",
"title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!",
"status": "canceled",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-1591",
"title": "We need to connect the mobile XSS driver!",
"status": "in progress",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-3802",
"title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7253",
"title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-9739",
"title": "We need to hack the multi-byte HDD bus!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4424",
"title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!",
"status": "in progress",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-3922",
"title": "You can't back up the capacitor without generating the wireless PCI program!",
"status": "backlog",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-4921",
"title": "I'll index the open-source IP feed, that should system the GB application!",
"status": "canceled",
"label": "bug",
"priority": "low"
},
{
"id": "TASK-5814",
"title": "We need to calculate the 1080p AGP feed!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-2645",
"title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!",
"status": "todo",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4535",
"title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!",
"status": "in progress",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-4463",
"title": "We need to copy the solid state AGP monitor!",
"status": "done",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-9745",
"title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!",
"status": "canceled",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-2080",
"title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!",
"status": "todo",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3838",
"title": "I'll bypass the online TCP application, that should panel the AGP system!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-1340",
"title": "We need to navigate the virtual PNG circuit!",
"status": "todo",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-6665",
"title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!",
"status": "canceled",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-7585",
"title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!",
"status": "backlog",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-6319",
"title": "We need to copy the multi-byte SCSI program!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-4369",
"title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-9035",
"title": "We need to override the solid state PNG array!",
"status": "canceled",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-3970",
"title": "You can't index the transmitter without quantifying the haptic ASCII card!",
"status": "todo",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-4473",
"title": "You can't bypass the protocol without overriding the neural RSS program!",
"status": "todo",
"label": "documentation",
"priority": "low"
},
{
"id": "TASK-4136",
"title": "You can't hack the hard drive without hacking the primary JSON program!",
"status": "canceled",
"label": "bug",
"priority": "medium"
},
{
"id": "TASK-3939",
"title": "Use the back-end SQL firewall, then you can connect the neural hard drive!",
"status": "done",
"label": "feature",
"priority": "low"
},
{
"id": "TASK-2007",
"title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!",
"status": "backlog",
"label": "bug",
"priority": "high"
},
{
"id": "TASK-7516",
"title": "Use the primary SQL program, then you can generate the auxiliary transmitter!",
"status": "done",
"label": "documentation",
"priority": "medium"
},
{
"id": "TASK-6906",
"title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!",
"status": "done",
"label": "feature",
"priority": "high"
},
{
"id": "TASK-5207",
"title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!",
"status": "in progress",
"label": "bug",
"priority": "low"
}
]

View File

@@ -0,0 +1,65 @@
import { promises as fs } from "fs"
import path from "path"
import { Metadata } from "next"
import Image from "next/image"
import { z } from "zod"
import { columns } from "./components/columns"
import { DataTable } from "./components/data-table"
import { UserNav } from "./components/user-nav"
import { taskSchema } from "./data/schema"
export const metadata: Metadata = {
title: "Tasks",
description: "A task and issue tracker build using Tanstack Table.",
}
// Simulate a database read for tasks.
async function getTasks() {
const data = await fs.readFile(
path.join(process.cwd(), "app/examples/tasks/data/tasks.json")
)
const tasks = JSON.parse(data.toString())
return z.array(taskSchema).parse(tasks)
}
export default async function TaskPage() {
const tasks = await getTasks()
return (
<>
<div className="md:hidden">
<Image
src="/examples/tasks-light.png"
width={1280}
height={998}
alt="Playground"
className="block dark:hidden"
/>
<Image
src="/examples/tasks-dark.png"
width={1280}
height={998}
alt="Playground"
className="hidden dark:block"
/>
</div>
<div className="hidden h-full flex-1 flex-col space-y-8 p-8 md:flex">
<div className="flex items-center justify-between space-y-2">
<div>
<h2 className="text-2xl font-bold tracking-tight">Welcome back!</h2>
<p className="text-muted-foreground">
Here&apos;s a list of your tasks for this month!
</p>
</div>
<div className="flex items-center space-x-2">
<UserNav />
</div>
</div>
<DataTable data={tasks} columns={columns} />
</div>
</>
)
}

View File

@@ -1,71 +0,0 @@
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 (
<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" })}
target="_blank"
rel="noreferrer"
>
Get a copy
</Link>
<Link
href="https://twitter.com/skirano"
className={cn(
buttonVariants({ size: "lg", variant: "outline" }),
"px-5"
)}
target="_blank"
rel="noreferrer"
>
<Heart className="mr-2 h-4 w-4 fill-current" />
Follow Pietro
</Link>
</div>
</section>
<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 bg-muted"
/>
</AspectRatio>
</div>
)
}

View File

@@ -4,13 +4,13 @@ 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 { ThemeProvider } from "@/components/providers"
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 as DefaultToaster } from "@/registry/default/ui/toaster"
import { Toaster as NewYorkToaster } from "@/registry/new-york/ui/toaster"
export const metadata: Metadata = {
title: {
@@ -90,9 +90,9 @@ export default function RootLayout({ children }: RootLayoutProps) {
</div>
<TailwindIndicator />
</ThemeProvider>
<StyleSwitcher />
<Analytics />
<Toaster />
<NewYorkToaster />
<DefaultToaster />
</body>
</html>
</>

View File

@@ -1,9 +1,9 @@
import Image from "next/image"
import Link from "next/link"
import { ChevronRight } from "lucide-react"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { ExamplesNav } from "@/components/examples-nav"
import { Icons } from "@/components/icons"
import {
@@ -11,42 +11,45 @@ import {
PageHeaderDescription,
PageHeaderHeading,
} from "@/components/page-header"
import { PromoVideo } from "@/components/promo-video"
import { StyleSwitcher } from "@/components/style-switcher"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
import DashboardPage from "@/app/examples/dashboard/page"
export default function IndexPage() {
return (
<div className="container relative pb-10">
<StyleSwitcher />
<PageHeader>
<div className="container relative">
<PageHeader className="pb-8">
<Link
href="/docs/changelog"
className="inline-flex items-center rounded-lg bg-muted px-3 py-1 text-sm font-medium"
>
🎉 <Separator className="mx-2 h-4" orientation="vertical" />{" "}
<span className="sm:hidden">Style, a new CLI and more.</span>
<span className="hidden sm:inline">
Introducing Style, a new CLI and more.
</span>
<ChevronRight className="ml-1 h-4 w-4" />
</Link>
<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>
<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 className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<Link href="/docs" className={cn(buttonVariants())}>
Get Started
</Link>
<Link
target="_blank"
rel="noreferrer"
href={siteConfig.links.github}
className={cn(buttonVariants({ variant: "outline" }))}
>
<Icons.gitHub className="mr-2 h-4 w-4" />
GitHub
</Link>
</div>
</section>
</PageHeader>
<ExamplesNav className="[&>a:first-child]:text-primary" />
<section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
<Image
@@ -65,7 +68,7 @@ export default function IndexPage() {
/>
</section>
<section className="hidden md:block">
<div className="overflow-hidden rounded-lg border bg-background shadow-xl">
<div className="overflow-hidden rounded-lg border bg-background shadow">
<DashboardPage />
</div>
</section>

View File

@@ -0,0 +1,27 @@
import Link from "next/link"
import { ThemeWrapper } from "@/components/theme-wrapper"
import { styles } from "@/registry/styles"
interface SinkLayoutProps {
children: React.ReactNode
}
export default function SinkLayout({ children }: SinkLayoutProps) {
return (
<div className="flex flex-col">
<div className="container">
<div className="flex space-x-2 px-2 py-4">
{styles.map((style) => (
<Link href={`/sink/${style.name}`} key={style.name}>
{style.label}
</Link>
))}
</div>
</div>
<div className="flex-1">
<ThemeWrapper>{children}</ThemeWrapper>
</div>
</div>
)
}

View File

@@ -0,0 +1,206 @@
import * as React from "react"
import Link from "next/link"
import { cn } from "@/lib/utils"
import AccordionDemo from "@/registry/new-york/example/accordion-demo"
import AlertDialogDemo from "@/registry/new-york/example/alert-dialog-demo"
import AspectRatioDemo from "@/registry/new-york/example/aspect-ratio-demo"
import AvatarDemo from "@/registry/new-york/example/avatar-demo"
import BadgeDemo from "@/registry/new-york/example/badge-demo"
import BadgeDestructive from "@/registry/new-york/example/badge-destructive"
import BadgeOutline from "@/registry/new-york/example/badge-outline"
import BadgeSecondary from "@/registry/new-york/example/badge-secondary"
import ButtonDemo from "@/registry/new-york/example/button-demo"
import ButtonDestructive from "@/registry/new-york/example/button-destructive"
import ButtonGhost from "@/registry/new-york/example/button-ghost"
import ButtonLink from "@/registry/new-york/example/button-link"
import ButtonLoading from "@/registry/new-york/example/button-loading"
import ButtonOutline from "@/registry/new-york/example/button-outline"
import ButtonSecondary from "@/registry/new-york/example/button-secondary"
import ButtonWithIcon from "@/registry/new-york/example/button-with-icon"
import CardDemo from "@/registry/new-york/example/card-demo"
import CheckboxDemo from "@/registry/new-york/example/checkbox-demo"
import CollapsibleDemo from "@/registry/new-york/example/collapsible-demo"
import CommandDemo from "@/registry/new-york/example/command-demo"
import ContextMenuDemo from "@/registry/new-york/example/context-menu-demo"
import DatePickerDemo from "@/registry/new-york/example/date-picker-demo"
import DialogDemo from "@/registry/new-york/example/dialog-demo"
import DropdownMenuDemo from "@/registry/new-york/example/dropdown-menu-demo"
import HoverCardDemo from "@/registry/new-york/example/hover-card-demo"
import MenubarDemo from "@/registry/new-york/example/menubar-demo"
import NavigationMenuDemo from "@/registry/new-york/example/navigation-menu-demo"
import PopoverDemo from "@/registry/new-york/example/popover-demo"
import ProgressDemo from "@/registry/new-york/example/progress-demo"
import RadioGroupDemo from "@/registry/new-york/example/radio-group-demo"
import ScrollAreaDemo from "@/registry/new-york/example/scroll-area-demo"
import SelectDemo from "@/registry/new-york/example/select-demo"
import SeparatorDemo from "@/registry/new-york/example/separator-demo"
import SheetDemo from "@/registry/new-york/example/sheet-demo"
import SkeletonDemo from "@/registry/new-york/example/skeleton-demo"
import SliderDemo from "@/registry/new-york/example/slider-demo"
import SwitchDemo from "@/registry/new-york/example/switch-demo"
import TabsDemo from "@/registry/new-york/example/tabs-demo"
import ToastDemo from "@/registry/new-york/example/toast-demo"
import ToggleDemo from "@/registry/new-york/example/toggle-demo"
import ToggleDisabled from "@/registry/new-york/example/toggle-disabled"
import ToggleOutline from "@/registry/new-york/example/toggle-outline"
import ToggleWithText from "@/registry/new-york/example/toggle-with-text"
import TooltipDemo from "@/registry/new-york/example/tooltip-demo"
import { Button } from "@/registry/new-york/ui/button"
export default function KitchenSinkPage() {
return (
<div className="container">
<div className="grid gap-4">
<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-sm text-muted-foreground">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>
<div className="flex space-x-2">
<Button size="lg">Large</Button>
<Button size="sm">Small</Button>
</div>
</ComponentWrapper>
<ComponentWrapper>
<DatePickerDemo />
</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 p-4",
className
)}
>
{children}
</div>
)
}

View File

@@ -2,55 +2,56 @@ 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"
import AccordionDemo from "@/registry/default/example/accordion-demo"
import AlertDialogDemo from "@/registry/default/example/alert-dialog-demo"
import AspectRatioDemo from "@/registry/default/example/aspect-ratio-demo"
import AvatarDemo from "@/registry/default/example/avatar-demo"
import BadgeDemo from "@/registry/default/example/badge-demo"
import BadgeDestructive from "@/registry/default/example/badge-destructive"
import BadgeOutline from "@/registry/default/example/badge-outline"
import BadgeSecondary from "@/registry/default/example/badge-secondary"
import ButtonDemo from "@/registry/default/example/button-demo"
import ButtonDestructive from "@/registry/default/example/button-destructive"
import ButtonGhost from "@/registry/default/example/button-ghost"
import ButtonLink from "@/registry/default/example/button-link"
import ButtonLoading from "@/registry/default/example/button-loading"
import ButtonOutline from "@/registry/default/example/button-outline"
import ButtonSecondary from "@/registry/default/example/button-secondary"
import ButtonWithIcon from "@/registry/default/example/button-with-icon"
import CardDemo from "@/registry/default/example/card-demo"
import CheckboxDemo from "@/registry/default/example/checkbox-demo"
import CollapsibleDemo from "@/registry/default/example/collapsible-demo"
import CommandDemo from "@/registry/default/example/command-demo"
import ContextMenuDemo from "@/registry/default/example/context-menu-demo"
import DatePickerDemo from "@/registry/default/example/date-picker-demo"
import DialogDemo from "@/registry/default/example/dialog-demo"
import DropdownMenuDemo from "@/registry/default/example/dropdown-menu-demo"
import HoverCardDemo from "@/registry/default/example/hover-card-demo"
import MenubarDemo from "@/registry/default/example/menubar-demo"
import NavigationMenuDemo from "@/registry/default/example/navigation-menu-demo"
import PopoverDemo from "@/registry/default/example/popover-demo"
import ProgressDemo from "@/registry/default/example/progress-demo"
import RadioGroupDemo from "@/registry/default/example/radio-group-demo"
import ScrollAreaDemo from "@/registry/default/example/scroll-area-demo"
import SelectDemo from "@/registry/default/example/select-demo"
import SeparatorDemo from "@/registry/default/example/separator-demo"
import SheetDemo from "@/registry/default/example/sheet-demo"
import SkeletonDemo from "@/registry/default/example/skeleton-demo"
import SliderDemo from "@/registry/default/example/slider-demo"
import SwitchDemo from "@/registry/default/example/switch-demo"
import TabsDemo from "@/registry/default/example/tabs-demo"
import ToastDemo from "@/registry/default/example/toast-demo"
import ToggleDemo from "@/registry/default/example/toggle-demo"
import ToggleDisabled from "@/registry/default/example/toggle-disabled"
import ToggleOutline from "@/registry/default/example/toggle-outline"
import ToggleWithText from "@/registry/default/example/toggle-with-text"
import TooltipDemo from "@/registry/default/example/tooltip-demo"
import { Button } from "@/registry/default/ui/button"
export default function KitchenSinkPage() {
return (
<div className="container">
<div className="grid gap-4 py-10">
<div className="grid gap-4">
<div className="grid grid-cols-3 items-start gap-4">
<div className="grid gap-4">
<ComponentWrapper>
@@ -63,7 +64,7 @@ export default function KitchenSinkPage() {
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 text-muted-foreground">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">
@@ -107,9 +108,13 @@ export default function KitchenSinkPage() {
<ButtonWithIcon />
<ButtonLoading />
</div>
<div className="flex space-x-2">
<Button size="lg">Large</Button>
<Button size="sm">Small</Button>
</div>
</ComponentWrapper>
<ComponentWrapper>
<CalendarDatePicker />
<DatePickerDemo />
</ComponentWrapper>
<ComponentWrapper>
<AccordionDemo />
@@ -191,7 +196,7 @@ function ComponentWrapper({
return (
<div
className={cn(
"flex items-center justify-between space-x-4 rounded-md border p-4",
"flex items-center justify-between space-x-4 rounded-md p-4",
className
)}
>

View File

@@ -1,5 +1,9 @@
import { cn } from "@/lib/utils"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import {
Alert,
AlertDescription,
AlertTitle,
} from "@/registry/new-york/ui/alert"
interface CalloutProps {
icon?: string

View File

@@ -3,12 +3,12 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
} from "@/registry/new-york/ui/collapsible"
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
expandButtonTitle?: string
@@ -31,7 +31,7 @@ export function CodeBlockWrapper({
>
<div
className={cn(
"[&_pre]:max-h-[650px [&_pre]:my-0 [&_pre]:pb-[100px]",
"[&_pre]:my-0 [&_pre]:max-h-[650px] [&_pre]:pb-[100px]",
!isOpened ? "[&_pre]:overflow-hidden" : "[&_pre]:overflow-auto]"
)}
>

View File

@@ -3,13 +3,12 @@
import * as React from "react"
import { useRouter } from "next/navigation"
import { DialogProps } from "@radix-ui/react-alert-dialog"
import { allDocs } from "contentlayer/generated"
import { Circle, File, Laptop, Moon, SunMedium } from "lucide-react"
import { useTheme } from "next-themes"
import { docsConfig } from "@/config/docs"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Button } from "@/registry/new-york/ui/button"
import {
CommandDialog,
CommandEmpty,
@@ -18,7 +17,7 @@ import {
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command"
} from "@/registry/new-york/ui/command"
export function CommandMenu({ ...props }: DialogProps) {
const router = useRouter()
@@ -47,14 +46,14 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button
variant="outline"
className={cn(
"relative h-9 w-full justify-start rounded-[0.5rem] text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64"
"relative w-full justify-start 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 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">
<kbd className="pointer-events-none absolute right-1.5 top-1.5 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex">
<span className="text-xs"></span>K
</kbd>
</Button>
@@ -68,6 +67,7 @@ export function CommandMenu({ ...props }: DialogProps) {
.map((navItem) => (
<CommandItem
key={navItem.href}
value={navItem.title}
onSelect={() => {
runCommand(() => router.push(navItem.href as string))
}}
@@ -82,6 +82,7 @@ export function CommandMenu({ ...props }: DialogProps) {
{group.items.map((navItem) => (
<CommandItem
key={navItem.href}
value={navItem.title}
onSelect={() => {
runCommand(() => router.push(navItem.href as string))
}}

View File

@@ -1,7 +1,7 @@
import React from "react"
import { cn } from "@/lib/utils"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { AspectRatio } from "@/registry/new-york/ui/aspect-ratio"
export function ComponentCard({
className,

View File

@@ -3,8 +3,13 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
interface ComponentExampleProps extends React.HTMLAttributes<HTMLDivElement> {
extractClassname?: boolean
@@ -33,7 +38,7 @@ export function ComponentExample({
const [, Button] = React.Children.toArray(
Code.props.children
) as React.ReactElement[]
return Button?.props?.value || null
return Button?.props?.value || Button?.props?.__rawString__ || null
}
}, [Code])
@@ -42,7 +47,7 @@ export function ComponentExample({
className={cn("group relative my-4 flex flex-col space-y-2", className)}
{...props}
>
<Tabs defaultValue="preview" className="mr-auto w-full">
<Tabs defaultValue="preview" className="relative mr-auto w-full">
<div className="flex items-center justify-between pb-3">
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
<TabsTrigger
@@ -62,9 +67,15 @@ export function ComponentExample({
<CopyWithClassNames
value={codeString}
classNames={extractedClassNames}
className="absolute right-4 top-20"
/>
) : (
codeString && <CopyButton value={codeString} />
codeString && (
<CopyButton
value={codeString}
className="absolute right-4 top-20"
/>
)
)}
</div>
<TabsContent value="preview" className="rounded-md border">

View File

@@ -0,0 +1,139 @@
"use client"
import * as React from "react"
import { Index } from "@/__registry__"
import { Loader2 } from "lucide-react"
import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { CopyButton, CopyWithClassNames } from "@/components/copy-button"
import { StyleSwitcher } from "@/components/style-switcher"
import { ThemeWrapper } from "@/components/theme-wrapper"
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
import { styles } from "@/registry/styles"
interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
name: string
extractClassname?: boolean
extractedClassNames?: string
align?: "center" | "start" | "end"
}
export function ComponentPreview({
name,
children,
className,
extractClassname,
extractedClassNames,
align = "center",
...props
}: ComponentPreviewProps) {
const [config] = useConfig()
const index = styles.findIndex((style) => style.name === config.style)
const Codes = React.Children.toArray(children) as React.ReactElement[]
const Code = Codes[index]
const Preview = React.useMemo(() => {
const Component = Index[config.style][name]?.component
if (!Component) {
return (
<p className="text-sm text-muted-foreground">
Component{" "}
<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm">
{name}
</code>{" "}
not found in registry.
</p>
)
}
return <Component />
}, [name, config.style])
const codeString = React.useMemo(() => {
if (
typeof Code?.props["data-rehype-pretty-code-fragment"] !== "undefined"
) {
const [, Button] = React.Children.toArray(
Code.props.children
) as React.ReactElement[]
return Button?.props?.value || Button?.props?.__rawString__ || null
}
}, [Code])
return (
<div
className={cn("group relative my-4 flex flex-col space-y-2", className)}
{...props}
>
<Tabs defaultValue="preview" className="relative mr-auto w-full">
<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 h-9 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 h-9 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>
</div>
<TabsContent value="preview" className="relative rounded-md border">
<div className="flex items-center justify-between p-4">
<StyleSwitcher />
{extractedClassNames ? (
<CopyWithClassNames
value={codeString}
classNames={extractedClassNames}
/>
) : (
codeString && <CopyButton value={codeString} />
)}
</div>
<ThemeWrapper>
<div
className={cn(
"preview flex min-h-[350px] w-full justify-center p-10",
{
"items-center": align === "center",
"items-start": align === "start",
"items-end": align === "end",
}
)}
>
<React.Suspense
fallback={
<div className="flex items-center text-sm text-muted-foreground">
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Loading...
</div>
}
>
{Preview}
</React.Suspense>
</div>
</ThemeWrapper>
</TabsContent>
<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">
{Code}
</div>
</div>
</TabsContent>
</Tabs>
</div>
)
}

View File

@@ -12,7 +12,7 @@ interface ComponentSourceProps extends React.HTMLAttributes<HTMLDivElement> {
export function ComponentSource({ children, className }: ComponentSourceProps) {
return (
<CodeBlockWrapper
expandButtonTitle="View Primitive"
expandButtonTitle="Expand"
className={cn("my-6 overflow-hidden rounded-md", className)}
>
{children}

View File

@@ -4,31 +4,35 @@ import * as React from "react"
import { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
import { NpmCommands } from "types/unist"
import { Event, trackEvent } from "@/lib/events"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Button } from "@/registry/new-york/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Icons } from "@/components/icons"
} from "@/registry/new-york/ui/dropdown-menu"
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string
src?: string
event?: Event["name"]
}
async function copyToClipboardWithMeta(
value: string,
meta?: Record<string, unknown>
) {
async function copyToClipboardWithMeta(value: string, event?: Event) {
navigator.clipboard.writeText(value)
if (event) {
trackEvent(event)
}
}
export function CopyButton({
value,
className,
src,
event,
...props
}: CopyButtonProps) {
const [hasCopied, setHasCopied] = React.useState(false)
@@ -40,15 +44,25 @@ export function CopyButton({
}, [hasCopied])
return (
<button
<Button
size="icon"
variant="ghost"
className={cn(
"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",
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
className
)}
onClick={() => {
copyToClipboardWithMeta(value, {
component: src,
})
copyToClipboardWithMeta(
value,
event
? {
name: event,
properties: {
code: value,
},
}
: undefined
)
setHasCopied(true)
}}
{...props}
@@ -59,7 +73,7 @@ export function CopyButton({
) : (
<Icons.copy className="h-3 w-3" />
)}
</button>
</Button>
)
}
@@ -90,21 +104,24 @@ export function CopyWithClassNames({
return (
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
"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-3 w-3" />
) : (
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
<DropdownMenuTrigger asChild>
<Button
size="icon"
variant="ghost"
className={cn(
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
className
)}
>
{hasCopied ? (
<Icons.check className="h-3 w-3" />
) : (
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => copyToClipboard(value)}>
<Icons.react className="mr-2 h-4 w-4" />
<span>Component</span>
@@ -135,39 +152,54 @@ export function CopyNpmCommandButton({
}, 2000)
}, [hasCopied])
const copyCommand = React.useCallback((value: string) => {
copyToClipboardWithMeta(value)
setHasCopied(true)
}, [])
const copyCommand = React.useCallback(
(value: string, pm: "npm" | "pnpm" | "yarn") => {
copyToClipboardWithMeta(value, {
name: "copy_npm_command",
properties: {
command: value,
pm,
},
})
setHasCopied(true)
},
[]
)
return (
<DropdownMenu>
<DropdownMenuTrigger
className={cn(
"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-3 w-3" />
) : (
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
<DropdownMenuTrigger asChild>
<Button
size="icon"
variant="ghost"
className={cn(
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
className
)}
>
{hasCopied ? (
<Icons.check className="h-3 w-3" />
) : (
<Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => copyCommand(commands.__npmCommand__)}>
<Icons.npm className="mr-2 h-4 w-4" />
<span>npm</span>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => copyCommand(commands.__npmCommand__, "npm")}
>
npm
</DropdownMenuItem>
<DropdownMenuItem onClick={() => copyCommand(commands.__yarnCommand__)}>
<Icons.yarn className="mr-2 h-4 w-4" />
<span>yarn</span>
<DropdownMenuItem
onClick={() => copyCommand(commands.__yarnCommand__, "yarn")}
>
yarn
</DropdownMenuItem>
<DropdownMenuItem onClick={() => copyCommand(commands.__pnpmCommand__)}>
<Icons.pnpm className="mr-2 h-4 w-4" />
<span>pnpm</span>
<DropdownMenuItem
onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")}
>
pnpm
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

View File

@@ -2,30 +2,46 @@
import Link from "next/link"
import { usePathname } from "next/navigation"
import { ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
import { ScrollArea, ScrollBar } from "@/registry/new-york/ui/scroll-area"
const examples = [
{
name: "Dashboard",
href: "/examples/dashboard",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/dashboard",
},
{
name: "Cards",
href: "/examples/cards",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/cards",
},
{
name: "Tasks",
href: "/examples/tasks",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/tasks",
},
{
name: "Playground",
href: "/examples/playground",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/playground",
},
{
name: "Forms",
href: "/examples/forms",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/forms",
},
{
name: "Music",
href: "/examples/music",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/music",
},
{
name: "Authentication",
href: "/examples/authentication",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/authentication",
},
]
@@ -35,24 +51,53 @@ 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>
<div className="relative">
<ScrollArea className="max-w-[600px] lg:max-w-none">
<div className={cn("mb-4 flex items-center", className)} {...props}>
{examples.map((example) => (
<Link
href={example.href}
key={example.href}
className={cn(
"flex items-center px-4",
pathname?.startsWith(example.href)
? "font-bold text-primary"
: "font-medium text-muted-foreground"
)}
>
{example.name}
</Link>
))}
</div>
<ScrollBar orientation="horizontal" className="invisible" />
</ScrollArea>
<ExampleCodeLink
pathname={pathname === "/" ? "/examples/dashboard" : pathname}
/>
</div>
)
}
interface ExampleCodeLinkProps {
pathname: string | null
}
export function ExampleCodeLink({ pathname }: ExampleCodeLinkProps) {
const example = examples.find((example) => pathname?.startsWith(example.href))
if (!example?.code) {
return null
}
return (
<Link
href={example?.code}
target="_blank"
rel="nofollow"
className="absolute right-0 top-0 hidden items-center rounded-[0.5rem] text-sm font-medium md:flex"
>
View code
<ArrowRight className="ml-1 h-4 w-4" />
</Link>
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,187 +0,0 @@
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 { 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"
import { CollapsibleDemo } from "@/components/examples/collapsible/demo"
import { CommandCombobox } from "@/components/examples/command/combobox"
import { CommandDemo } from "@/components/examples/command/demo"
import { CommandDialogDemo } from "@/components/examples/command/dialog"
import { CommandDropdownMenu } from "@/components/examples/command/dropdown-menu"
import { CommandPopover } from "@/components/examples/command/popover"
import { ContextMenuDemo } from "@/components/examples/context-menu/demo"
import { DialogDemo } from "@/components/examples/dialog/demo"
import { DropdownMenuCheckboxes } from "@/components/examples/dropdown-menu/checkboxes"
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo"
import { DropdownMenuRadioGroupDemo } from "@/components/examples/dropdown-menu/radio-group"
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"
import { LabelDemo } from "@/components/examples/label/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 { 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"
import { TextareaDemo } from "@/components/examples/textarea/demo"
import { TextareaDisabled } from "@/components/examples/textarea/disabled"
import { TextareaWithButton } from "@/components/examples/textarea/with-button"
import { TextareaWithLabel } from "@/components/examples/textarea/with-label"
import { TextareaWithText } from "@/components/examples/textarea/with-text"
import { ToastDemo } from "@/components/examples/toast/demo"
import { ToastDestructive } from "@/components/examples/toast/destructive"
import { ToastSimple } from "@/components/examples/toast/simple"
import { ToastWithAction } from "@/components/examples/toast/with-action"
import { ToastWithTitle } from "@/components/examples/toast/with-title"
import { ToggleDemo } from "@/components/examples/toggle/demo"
import { ToggleDisabled } from "@/components/examples/toggle/disabled"
import { ToggleLg } from "@/components/examples/toggle/lg"
import { ToggleOutline } from "@/components/examples/toggle/outline"
import { ToggleSm } from "@/components/examples/toggle/sm"
import { ToggleWithText } from "@/components/examples/toggle/with-text"
import { TooltipDemo } from "@/components/examples/tooltip/demo"
import { TypographyBlockquote } from "@/components/examples/typography/blockquote"
import { TypographyDemo } from "@/components/examples/typography/demo"
import { TypographyH1 } from "@/components/examples/typography/h1"
import { TypographyH2 } from "@/components/examples/typography/h2"
import { TypographyH3 } from "@/components/examples/typography/h3"
import { TypographyH4 } from "@/components/examples/typography/h4"
import { TypographyInlineCode } from "@/components/examples/typography/inline-code"
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 { 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,
ButtonSecondary,
ButtonWithIcon,
CalendarDemo,
CalendarDatePicker,
CalendarDateRangePicker,
CalendarDatePickerWithPresets,
CardDemo,
CardWithForm,
CheckboxDemo,
CheckboxDisabled,
CheckboxWithText,
CollapsibleDemo,
CommandDemo,
CommandDialogDemo,
CommandCombobox,
CommandPopover,
CommandDropdownMenu,
ContextMenuDemo,
DialogDemo,
DropdownMenuCheckboxes,
DropdownMenuDemo,
DropdownMenuRadioGroupDemo,
HoverCardDemo,
InputDemo,
InputDisabled,
InputFile,
InputWithButton,
InputWithLabel,
InputWithText,
LabelDemo,
MenubarDemo,
NavigationMenuDemo,
PopoverDemo,
ProgressDemo,
RadioGroupDemo,
ScrollAreaDemo,
SelectDemo,
SeparatorDemo,
SheetDemo,
SheetSize,
SheetPosition,
SkeletonDemo,
SliderDemo,
SwitchDemo,
TabsDemo,
TextareaDemo,
TextareaDisabled,
TextareaWithButton,
TextareaWithLabel,
TextareaWithText,
ToastDemo,
ToastDestructive,
ToastSimple,
ToastWithTitle,
ToastWithAction,
TooltipDemo,
TypographyBlockquote,
TypographyDemo,
TypographyH1,
TypographyH2,
TypographyH3,
TypographyH4,
TypographyInlineCode,
TypographyLarge,
TypographyLead,
TypographyList,
TypographyP,
TypographySmall,
TypographyMuted,
TypographyTable,
ToggleDemo,
ToggleSm,
ToggleLg,
ToggleOutline,
ToggleDisabled,
ToggleWithText,
}

View File

@@ -1,5 +0,0 @@
import { Input } from "@/components/ui/input"
export function InputDemo() {
return <Input type="email" placeholder="Email" />
}

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