Compare commits

...

62 Commits

Author SHA1 Message Date
github-actions[bot]
2846b2ea9a chore(release): version packages (#696)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-24 21:12:15 +04:00
Samuel Corsi-House
0f84973b4d fix(shadcn-ui): use @antfu/ni to detect package manager (#677)
* fix(cli): use @antfu/ni to detect package manager

* chore(cli): cleanup imports

* Create cyan-houses-dress.md

---------

Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 21:01:22 +04:00
Caio Borghi
f8348621f4 docs(www): fix TypeScript typo (#645)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:38:10 +04:00
Daniele Luisetto
fbed50f4e8 fix(shadcn-ui): cssVars template typo causing missing value (#682)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:10:01 +04:00
Daniele Luisetto
1971fa7511 fix(shadcn-ui): use slash for alpha values in colors (#681)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 20:09:22 +04:00
Daniele Luisetto
7b5582e5d0 fix(docs): remove duplicate install command (#679)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-24 15:51:23 +04:00
Bryce Kalow
2ca7476c9b docs(www): directory -> directive (#685) 2023-06-24 15:49:47 +04:00
Reda
aea12e9762 docs(www): missing RSC anchor link (#664)
adda a missing # for the RSC section anchor link, resulting in 404.

Co-authored-by: shadcn <m@shadcn.com>
2023-06-23 20:08:18 +04:00
shadcn
343acb3a51 docs: update toast.mdx (#670) 2023-06-23 19:13:29 +04:00
shadcn
cf139e5fa1 feat: add manual installation for components (#666) 2023-06-23 14:28:34 +04:00
Ahmed Abdelbaset
38fb9693d0 style(shadcn-ui): remove unused variable in index.ts (#654) 2023-06-23 10:58:33 +04:00
Amruth Pillai
f1de3401a2 fix: update schema.json (#651)
Co-authored-by: shadcn <m@shadcn.com>
2023-06-23 00:27:28 +04:00
shadcn
379d1560c3 docs: add latest to cli (#649) 2023-06-23 00:07:19 +04:00
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
683 changed files with 32446 additions and 6082 deletions

View File

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

View File

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

View File

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

View File

@@ -20,10 +20,13 @@ jobs:
- name: Use PNPM - name: Use PNPM
uses: pnpm/action-setup@v2.2.4 uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.1
- name: Use Node.js 18 - name: Use Node.js 18
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
version: 8.6.1
node-version: 18 node-version: 18
cache: "pnpm" 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 .next
build build
.contentlayer .contentlayer
apps/www/pages/api/components.json apps/www/pages/api/registry.json

View File

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

View File

@@ -2,4 +2,5 @@ dist
node_modules node_modules
.next .next
build 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 { siteConfig } from "@/config/site"
import { getTableOfContents } from "@/lib/toc" import { getTableOfContents } from "@/lib/toc"
import { absoluteUrl, cn } from "@/lib/utils" import { absoluteUrl, cn } from "@/lib/utils"
import { badgeVariants } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Icons } from "@/components/icons" import { Icons } from "@/components/icons"
import { Mdx } from "@/components/mdx-components" import { Mdx } from "@/components/mdx-components"
import { DocsPager } from "@/components/pager" import { DocsPager } from "@/components/pager"
import { DashboardTableOfContents } from "@/components/toc" 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 { interface DocPageProps {
params: { params: {
@@ -132,16 +133,20 @@ export default async function DocPage({ params }: DocPageProps) {
)} )}
</div> </div>
) : null} ) : null}
<Separator className="my-4 md:my-6" /> <div className="pb-12 pt-8">
<Mdx code={doc.body.code} /> <Mdx code={doc.body.code} />
<Separator className="my-4 md:my-6" /> </div>
<DocsPager doc={doc} /> <DocsPager doc={doc} />
</div> </div>
<div className="hidden text-sm xl:block"> {doc.toc && (
<div className="sticky top-16 -mt-10 max-h-[calc(var(--vh)-4rem)] overflow-y-auto pt-6"> <div className="hidden text-sm xl:block">
<DashboardTableOfContents toc={toc} /> <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>
</div> )}
</main> </main>
) )
} }

View File

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

View File

@@ -3,10 +3,10 @@
import * as React from "react" import * as React from "react"
import { cn } from "@/lib/utils" 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 { 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> {} interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}

View File

@@ -4,7 +4,7 @@ import Link from "next/link"
import { Command } from "lucide-react" import { Command } from "lucide-react"
import { cn } from "@/lib/utils" 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" import { UserAuthForm } from "@/app/examples/authentication/components/user-auth-form"
export const metadata: Metadata = { export const metadata: Metadata = {
@@ -35,20 +35,14 @@ export default function AuthenticationPage() {
<Link <Link
href="/examples/authentication" href="/examples/authentication"
className={cn( className={cn(
buttonVariants({ variant: "ghost", size: "sm" }), buttonVariants({ variant: "ghost" }),
"absolute right-4 top-4 md:right-8 md:top-8" "absolute right-4 top-4 md:right-8 md:top-8"
)} )}
> >
Login Login
</Link> </Link>
<div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex"> <div className="relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex">
<div <div className="absolute inset-0 bg-zinc-900" />
className="absolute inset-0 bg-cover"
style={{
backgroundImage:
"url(https://images.unsplash.com/photo-1590069261209-f8e9b8642343?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1376&q=80)",
}}
/>
<div className="relative z-20 flex items-center text-lg font-medium"> <div className="relative z-20 flex items-center text-lg font-medium">
<Command className="mr-2 h-6 w-6" /> Acme Inc <Command className="mr-2 h-6 w-6" /> Acme Inc
</div> </div>
@@ -57,7 +51,7 @@ export default function AuthenticationPage() {
<p className="text-lg"> <p className="text-lg">
&ldquo;This library has saved me countless hours of work and &ldquo;This library has saved me countless hours of work and
helped me deliver stunning designs to my clients faster than helped me deliver stunning designs to my clients faster than
ever before. Highly recommended!&rdquo; ever before.&rdquo;
</p> </p>
<footer className="text-sm">Sofia Davis</footer> <footer className="text-sm">Sofia Davis</footer>
</blockquote> </blockquote>

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,20 +6,20 @@ import {
CardDescription, CardDescription,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card" } from "@/registry/new-york/ui/card"
export function DemoNotifications() { export function DemoNotifications() {
return ( return (
<Card> <Card>
<CardHeader> <CardHeader className="pb-3">
<CardTitle>Notifications</CardTitle> <CardTitle>Notifications</CardTitle>
<CardDescription> <CardDescription>
Choose what you want to be notified about. Choose what you want to be notified about.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="grid gap-1 p-1.5"> <CardContent className="grid gap-1">
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground"> <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="h-5 w-5" /> <Bell className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Everything</p> <p className="text-sm font-medium leading-none">Everything</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
@@ -27,8 +27,8 @@ export function DemoNotifications() {
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-center space-x-4 rounded-md bg-accent p-2 text-accent-foreground"> <div className="-mx-2 flex items-start space-x-4 rounded-md bg-accent p-2 text-accent-foreground transition-all">
<AtSign className="h-5 w-5" /> <AtSign className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Available</p> <p className="text-sm font-medium leading-none">Available</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">
@@ -36,8 +36,8 @@ export function DemoNotifications() {
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-center space-x-4 rounded-md p-2 hover:bg-accent hover:text-accent-foreground"> <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="h-5 w-5" /> <BellOff className="mt-px h-5 w-5" />
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none">Ignoring</p> <p className="text-sm font-medium leading-none">Ignoring</p>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,15 +2,20 @@ import { Metadata } from "next"
import Image from "next/image" import Image from "next/image"
import { Activity, CreditCard, DollarSign, Download, Users } from "lucide-react" import { Activity, CreditCard, DollarSign, Download, Users } from "lucide-react"
import { Button } from "@/components/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card" } from "@/registry/new-york/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/registry/new-york/ui/tabs"
import { CalendarDateRangePicker } from "@/app/examples/dashboard/components/date-range-picker" import { CalendarDateRangePicker } from "@/app/examples/dashboard/components/date-range-picker"
import { MainNav } from "@/app/examples/dashboard/components/main-nav" import { MainNav } from "@/app/examples/dashboard/components/main-nav"
import { Overview } from "@/app/examples/dashboard/components/overview" 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> <h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<CalendarDateRangePicker /> <CalendarDateRangePicker />
<Button size="sm"> <Button>
<Download className="mr-2 h-4 w-4" /> <Download className="mr-2 h-4 w-4" />
Download Download
</Button> </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 { Metadata } from "next"
import Link from "next/link" import Link from "next/link"
import { ChevronRight } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { ExamplesNav } from "@/components/examples-nav" import { ExamplesNav } from "@/components/examples-nav"
import { import {
PageHeader, PageHeader,
PageHeaderDescription, PageHeaderDescription,
PageHeaderHeading, PageHeaderHeading,
} from "@/components/page-header" } from "@/components/page-header"
import { buttonVariants } from "@/registry/new-york/ui/button"
import { Separator } from "@/registry/new-york/ui/separator"
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Examples", title: "Examples",
@@ -22,8 +24,19 @@ interface ExamplesLayoutProps {
export default function ExamplesLayout({ children }: ExamplesLayoutProps) { export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
return ( return (
<> <>
<div className="container relative pb-10"> <div className="container relative">
<PageHeader className="page-header"> <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"> <PageHeaderHeading className="hidden md:block">
Check out some examples. Check out some examples.
</PageHeaderHeading> </PageHeaderHeading>
@@ -32,34 +45,27 @@ export default function ExamplesLayout({ children }: ExamplesLayoutProps) {
Dashboard, cards, authentication. Some examples built using the Dashboard, cards, authentication. Some examples built using the
components. Use this as a guide to build your own. components. Use this as a guide to build your own.
</PageHeaderDescription> </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> </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> <section>
<ExamplesNav /> <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} {children}
</div> </div>
</section> </section>

View File

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

View File

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

View File

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

View File

@@ -11,8 +11,8 @@ import {
} from "lucide-react" } from "lucide-react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { ScrollArea } from "@/components/ui/scroll-area" import { ScrollArea } from "@/registry/new-york/ui/scroll-area"
import { Playlist } from "../data/playlists" import { Playlist } from "../data/playlists"
@@ -24,66 +24,62 @@ export function Sidebar({ className, playlists }: SidebarProps) {
return ( return (
<div className={cn("pb-12", className)}> <div className={cn("pb-12", className)}>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div className="px-4 py-2"> <div className="px-3 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight"> <h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
Discover Discover
</h2> </h2>
<div className="space-y-1"> <div className="space-y-1">
<Button <Button variant="secondary" className="w-full justify-start">
variant="secondary"
size="sm"
className="w-full justify-start"
>
<PlayCircle className="mr-2 h-4 w-4" /> <PlayCircle className="mr-2 h-4 w-4" />
Listen Now Listen Now
</Button> </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" /> <LayoutGrid className="mr-2 h-4 w-4" />
Browse Browse
</Button> </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 className="mr-2 h-4 w-4" />
Radio Radio
</Button> </Button>
</div> </div>
</div> </div>
<div className="px-4 py-2"> <div className="px-3 py-2">
<h2 className="mb-2 px-2 text-lg font-semibold tracking-tight"> <h2 className="mb-2 px-4 text-lg font-semibold tracking-tight">
Library Library
</h2> </h2>
<div className="space-y-1"> <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" /> <ListMusic className="mr-2 h-4 w-4" />
Playlists Playlists
</Button> </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" /> <Music2 className="mr-2 h-4 w-4" />
Songs Songs
</Button> </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" /> <User className="mr-2 h-4 w-4" />
Made for You Made for You
</Button> </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" /> <Mic2 className="mr-2 h-4 w-4" />
Artists Artists
</Button> </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" /> <Library className="mr-2 h-4 w-4" />
Albums Albums
</Button> </Button>
</div> </div>
</div> </div>
<div className="py-2"> <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 Playlists
</h2> </h2>
<ScrollArea className="h-[300px] px-2"> <ScrollArea className="h-[300px] px-1">
<div className="space-y-1 p-2"> <div className="space-y-1 p-2">
{playlists?.map((playlist) => ( {playlists?.map((playlist, i) => (
<Button <Button
key={`${playlist}-${i}`}
variant="ghost" variant="ghost"
size="sm"
className="w-full justify-start font-normal" className="w-full justify-start font-normal"
> >
<ListMusic className="mr-2 h-4 w-4" /> <ListMusic className="mr-2 h-4 w-4" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,21 @@
import { Metadata } from "next" import { Metadata } from "next"
import { History } from "lucide-react" import { History } from "lucide-react"
import { Button } from "@/components/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
HoverCard, HoverCard,
HoverCardContent, HoverCardContent,
HoverCardTrigger, HoverCardTrigger,
} from "@/components/ui/hover-card" } from "@/registry/new-york/ui/hover-card"
import { Label } from "@/components/ui/label" import { Label } from "@/registry/new-york/ui/label"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/registry/new-york/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import {
import { Textarea } from "@/components/ui/textarea" 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 { CodeViewer } from "./components/code-viewer"
import { Icons } from "./components/icons" 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 { siteConfig } from "@/config/site"
import { fontSans } from "@/lib/fonts" import { fontSans } from "@/lib/fonts"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Toaster } from "@/components/ui/toaster"
import { Analytics } from "@/components/analytics" import { Analytics } from "@/components/analytics"
import { ThemeProvider } from "@/components/providers"
import { SiteFooter } from "@/components/site-footer" import { SiteFooter } from "@/components/site-footer"
import { SiteHeader } from "@/components/site-header" import { SiteHeader } from "@/components/site-header"
import { StyleSwitcher } from "@/components/style-switcher"
import { TailwindIndicator } from "@/components/tailwind-indicator" 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 = { export const metadata: Metadata = {
title: { title: {
@@ -90,9 +90,9 @@ export default function RootLayout({ children }: RootLayoutProps) {
</div> </div>
<TailwindIndicator /> <TailwindIndicator />
</ThemeProvider> </ThemeProvider>
<StyleSwitcher />
<Analytics /> <Analytics />
<Toaster /> <NewYorkToaster />
<DefaultToaster />
</body> </body>
</html> </html>
</> </>

View File

@@ -1,9 +1,9 @@
import Image from "next/image" import Image from "next/image"
import Link from "next/link" import Link from "next/link"
import { ChevronRight } from "lucide-react"
import { siteConfig } from "@/config/site" import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { ExamplesNav } from "@/components/examples-nav" import { ExamplesNav } from "@/components/examples-nav"
import { Icons } from "@/components/icons" import { Icons } from "@/components/icons"
import { import {
@@ -11,42 +11,45 @@ import {
PageHeaderDescription, PageHeaderDescription,
PageHeaderHeading, PageHeaderHeading,
} from "@/components/page-header" } from "@/components/page-header"
import { PromoVideo } from "@/components/promo-video" import { buttonVariants } from "@/registry/new-york/ui/button"
import { StyleSwitcher } from "@/components/style-switcher" import { Separator } from "@/registry/new-york/ui/separator"
import DashboardPage from "@/app/examples/dashboard/page" import DashboardPage from "@/app/examples/dashboard/page"
export default function IndexPage() { export default function IndexPage() {
return ( return (
<div className="container relative pb-10"> <div className="container relative">
<StyleSwitcher /> <PageHeader className="pb-8">
<PageHeader> <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> <PageHeaderHeading>Build your component library.</PageHeaderHeading>
<PageHeaderDescription> <PageHeaderDescription>
Beautifully designed components that you can copy and paste into your Beautifully designed components that you can copy and paste into your
apps. Accessible. Customizable. Open Source. apps. Accessible. Customizable. Open Source.
</PageHeaderDescription> </PageHeaderDescription>
</PageHeader> <div className="flex w-full items-center space-x-4 pb-8 pt-4 md:pb-10">
<section className="pb-8 md:pb-10"> <Link href="/docs" className={cn(buttonVariants())}>
<div className="flex w-full items-center justify-between"> Get Started
<div className="flex space-x-4"> </Link>
<Link href="/docs" className={cn(buttonVariants({ size: "lg" }))}> <Link
Get Started target="_blank"
</Link> rel="noreferrer"
<Link href={siteConfig.links.github}
target="_blank" className={cn(buttonVariants({ variant: "outline" }))}
rel="noreferrer" >
href={siteConfig.links.github} <Icons.gitHub className="mr-2 h-4 w-4" />
className={cn( GitHub
buttonVariants({ variant: "outline", size: "lg" }), </Link>
"pl-6"
)}
>
<Icons.gitHub className="mr-2 h-4 w-4" />
GitHub
</Link>
</div>
</div> </div>
</section> </PageHeader>
<ExamplesNav className="[&>a:first-child]:text-primary" /> <ExamplesNav className="[&>a:first-child]:text-primary" />
<section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden"> <section className="space-y-8 overflow-hidden rounded-lg border-2 border-primary dark:border-muted md:hidden">
<Image <Image
@@ -65,7 +68,7 @@ export default function IndexPage() {
/> />
</section> </section>
<section className="hidden md:block"> <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 /> <DashboardPage />
</div> </div>
</section> </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 Link from "next/link"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { AccordionDemo } from "@/components/examples/accordion/demo" import AccordionDemo from "@/registry/default/example/accordion-demo"
import { AlertDialogDemo } from "@/components/examples/alert-dialog/demo" import AlertDialogDemo from "@/registry/default/example/alert-dialog-demo"
import { AspectRatioDemo } from "@/components/examples/aspect-ratio/demo" import AspectRatioDemo from "@/registry/default/example/aspect-ratio-demo"
import { AvatarDemo } from "@/components/examples/avatar/demo" import AvatarDemo from "@/registry/default/example/avatar-demo"
import { BadgeDemo } from "@/components/examples/badge/demo" import BadgeDemo from "@/registry/default/example/badge-demo"
import { BadgeDestructive } from "@/components/examples/badge/destructive" import BadgeDestructive from "@/registry/default/example/badge-destructive"
import { BadgeOutline } from "@/components/examples/badge/outline" import BadgeOutline from "@/registry/default/example/badge-outline"
import { BadgeSecondary } from "@/components/examples/badge/secondary" import BadgeSecondary from "@/registry/default/example/badge-secondary"
import { ButtonDemo } from "@/components/examples/button/demo" import ButtonDemo from "@/registry/default/example/button-demo"
import { ButtonDestructive } from "@/components/examples/button/destructive" import ButtonDestructive from "@/registry/default/example/button-destructive"
import { ButtonGhost } from "@/components/examples/button/ghost" import ButtonGhost from "@/registry/default/example/button-ghost"
import { ButtonLink } from "@/components/examples/button/link" import ButtonLink from "@/registry/default/example/button-link"
import { ButtonLoading } from "@/components/examples/button/loading" import ButtonLoading from "@/registry/default/example/button-loading"
import { ButtonOutline } from "@/components/examples/button/outline" import ButtonOutline from "@/registry/default/example/button-outline"
import { ButtonSecondary } from "@/components/examples/button/secondary" import ButtonSecondary from "@/registry/default/example/button-secondary"
import { ButtonWithIcon } from "@/components/examples/button/with-icon" import ButtonWithIcon from "@/registry/default/example/button-with-icon"
import { CalendarDatePicker } from "@/components/examples/calendar/date-picker" import CardDemo from "@/registry/default/example/card-demo"
import { CardDemo } from "@/components/examples/card/demo" import CheckboxDemo from "@/registry/default/example/checkbox-demo"
import { CheckboxDemo } from "@/components/examples/checkbox/demo" import CollapsibleDemo from "@/registry/default/example/collapsible-demo"
import { CollapsibleDemo } from "@/components/examples/collapsible/demo" import CommandDemo from "@/registry/default/example/command-demo"
import { CommandDemo } from "@/components/examples/command/demo" import ContextMenuDemo from "@/registry/default/example/context-menu-demo"
import { ContextMenuDemo } from "@/components/examples/context-menu/demo" import DatePickerDemo from "@/registry/default/example/date-picker-demo"
import { DialogDemo } from "@/components/examples/dialog/demo" import DialogDemo from "@/registry/default/example/dialog-demo"
import { DropdownMenuDemo } from "@/components/examples/dropdown-menu/demo" import DropdownMenuDemo from "@/registry/default/example/dropdown-menu-demo"
import { HoverCardDemo } from "@/components/examples/hover-card/demo" import HoverCardDemo from "@/registry/default/example/hover-card-demo"
import { MenubarDemo } from "@/components/examples/menubar/demo" import MenubarDemo from "@/registry/default/example/menubar-demo"
import { NavigationMenuDemo } from "@/components/examples/navigation-menu/demo" import NavigationMenuDemo from "@/registry/default/example/navigation-menu-demo"
import { PopoverDemo } from "@/components/examples/popover/demo" import PopoverDemo from "@/registry/default/example/popover-demo"
import { ProgressDemo } from "@/components/examples/progress/demo" import ProgressDemo from "@/registry/default/example/progress-demo"
import { RadioGroupDemo } from "@/components/examples/radio-group/demo" import RadioGroupDemo from "@/registry/default/example/radio-group-demo"
import { ScrollAreaDemo } from "@/components/examples/scroll-area/demo" import ScrollAreaDemo from "@/registry/default/example/scroll-area-demo"
import { SelectDemo } from "@/components/examples/select/demo" import SelectDemo from "@/registry/default/example/select-demo"
import { SeparatorDemo } from "@/components/examples/separator/demo" import SeparatorDemo from "@/registry/default/example/separator-demo"
import { SheetDemo } from "@/components/examples/sheet/demo" import SheetDemo from "@/registry/default/example/sheet-demo"
import { SkeletonDemo } from "@/components/examples/skeleton/demo" import SkeletonDemo from "@/registry/default/example/skeleton-demo"
import { SliderDemo } from "@/components/examples/slider/demo" import SliderDemo from "@/registry/default/example/slider-demo"
import { SwitchDemo } from "@/components/examples/switch/demo" import SwitchDemo from "@/registry/default/example/switch-demo"
import { TabsDemo } from "@/components/examples/tabs/demo" import TabsDemo from "@/registry/default/example/tabs-demo"
import { ToastDemo } from "@/components/examples/toast/demo" import ToastDemo from "@/registry/default/example/toast-demo"
import { ToggleDemo } from "@/components/examples/toggle/demo" import ToggleDemo from "@/registry/default/example/toggle-demo"
import { ToggleDisabled } from "@/components/examples/toggle/disabled" import ToggleDisabled from "@/registry/default/example/toggle-disabled"
import { ToggleOutline } from "@/components/examples/toggle/outline" import ToggleOutline from "@/registry/default/example/toggle-outline"
import { ToggleWithText } from "@/components/examples/toggle/with-text" import ToggleWithText from "@/registry/default/example/toggle-with-text"
import { TooltipDemo } from "@/components/examples/tooltip/demo" import TooltipDemo from "@/registry/default/example/tooltip-demo"
import { Button } from "@/registry/default/ui/button"
export default function KitchenSinkPage() { export default function KitchenSinkPage() {
return ( return (
<div className="container"> <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 grid-cols-3 items-start gap-4">
<div className="grid gap-4"> <div className="grid gap-4">
<ComponentWrapper> <ComponentWrapper>
@@ -63,7 +64,7 @@ export default function KitchenSinkPage() {
className="spa flex-col items-start space-x-0 className="spa flex-col items-start space-x-0
space-y-2" 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"> <p className="text-sm font-medium leading-none">
You can customize the theme using{" "} 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"> <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 /> <ButtonWithIcon />
<ButtonLoading /> <ButtonLoading />
</div> </div>
<div className="flex space-x-2">
<Button size="lg">Large</Button>
<Button size="sm">Small</Button>
</div>
</ComponentWrapper> </ComponentWrapper>
<ComponentWrapper> <ComponentWrapper>
<CalendarDatePicker /> <DatePickerDemo />
</ComponentWrapper> </ComponentWrapper>
<ComponentWrapper> <ComponentWrapper>
<AccordionDemo /> <AccordionDemo />
@@ -191,7 +196,7 @@ function ComponentWrapper({
return ( return (
<div <div
className={cn( 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 className
)} )}
> >

View File

@@ -1,5 +1,9 @@
import { cn } from "@/lib/utils" 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 { interface CalloutProps {
icon?: string icon?: string

View File

@@ -3,12 +3,12 @@
import * as React from "react" import * as React from "react"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
Collapsible, Collapsible,
CollapsibleContent, CollapsibleContent,
CollapsibleTrigger, CollapsibleTrigger,
} from "@/components/ui/collapsible" } from "@/registry/new-york/ui/collapsible"
interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> { interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
expandButtonTitle?: string expandButtonTitle?: string
@@ -31,7 +31,7 @@ export function CodeBlockWrapper({
> >
<div <div
className={cn( 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]" !isOpened ? "[&_pre]:overflow-hidden" : "[&_pre]:overflow-auto]"
)} )}
> >
@@ -40,7 +40,7 @@ export function CodeBlockWrapper({
</CollapsibleContent> </CollapsibleContent>
<div <div
className={cn( className={cn(
"absolute flex items-center justify-center bg-gradient-to-b from-background/30 to-muted/90 p-2", "absolute flex items-center justify-center bg-gradient-to-b from-zinc-700/30 to-zinc-950/90 p-2",
isOpened ? "inset-x-0 bottom-0 h-12" : "inset-0" isOpened ? "inset-x-0 bottom-0 h-12" : "inset-0"
)} )}
> >

View File

@@ -3,13 +3,12 @@
import * as React from "react" import * as React from "react"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { DialogProps } from "@radix-ui/react-alert-dialog" import { DialogProps } from "@radix-ui/react-alert-dialog"
import { allDocs } from "contentlayer/generated"
import { Circle, File, Laptop, Moon, SunMedium } from "lucide-react" import { Circle, File, Laptop, Moon, SunMedium } from "lucide-react"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { docsConfig } from "@/config/docs" import { docsConfig } from "@/config/docs"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button" import { Button } from "@/registry/new-york/ui/button"
import { import {
CommandDialog, CommandDialog,
CommandEmpty, CommandEmpty,
@@ -18,7 +17,7 @@ import {
CommandItem, CommandItem,
CommandList, CommandList,
CommandSeparator, CommandSeparator,
} from "@/components/ui/command" } from "@/registry/new-york/ui/command"
export function CommandMenu({ ...props }: DialogProps) { export function CommandMenu({ ...props }: DialogProps) {
const router = useRouter() const router = useRouter()
@@ -47,14 +46,14 @@ export function CommandMenu({ ...props }: DialogProps) {
<Button <Button
variant="outline" variant="outline"
className={cn( 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)} onClick={() => setOpen(true)}
{...props} {...props}
> >
<span className="hidden lg:inline-flex">Search documentation...</span> <span className="hidden lg:inline-flex">Search documentation...</span>
<span className="inline-flex lg:hidden">Search...</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 <span className="text-xs"></span>K
</kbd> </kbd>
</Button> </Button>
@@ -68,6 +67,7 @@ export function CommandMenu({ ...props }: DialogProps) {
.map((navItem) => ( .map((navItem) => (
<CommandItem <CommandItem
key={navItem.href} key={navItem.href}
value={navItem.title}
onSelect={() => { onSelect={() => {
runCommand(() => router.push(navItem.href as string)) runCommand(() => router.push(navItem.href as string))
}} }}
@@ -82,6 +82,7 @@ export function CommandMenu({ ...props }: DialogProps) {
{group.items.map((navItem) => ( {group.items.map((navItem) => (
<CommandItem <CommandItem
key={navItem.href} key={navItem.href}
value={navItem.title}
onSelect={() => { onSelect={() => {
runCommand(() => router.push(navItem.href as string)) runCommand(() => router.push(navItem.href as string))
}} }}

View File

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

View File

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

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

View File

@@ -6,13 +6,14 @@ import { NpmCommands } from "types/unist"
import { Event, trackEvent } from "@/lib/events" import { Event, trackEvent } from "@/lib/events"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { Button } from "@/registry/new-york/ui/button"
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu" } from "@/registry/new-york/ui/dropdown-menu"
import { Icons } from "@/components/icons"
interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> { interface CopyButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
value: string value: string
@@ -43,9 +44,11 @@ export function CopyButton({
}, [hasCopied]) }, [hasCopied])
return ( return (
<button <Button
size="icon"
variant="ghost"
className={cn( 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 className
)} )}
onClick={() => { onClick={() => {
@@ -70,7 +73,7 @@ export function CopyButton({
) : ( ) : (
<Icons.copy className="h-3 w-3" /> <Icons.copy className="h-3 w-3" />
)} )}
</button> </Button>
) )
} }
@@ -101,21 +104,24 @@ export function CopyWithClassNames({
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger <DropdownMenuTrigger asChild>
className={cn( <Button
"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", size="icon"
className variant="ghost"
)} className={cn(
{...props} "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" /> >
) : ( {hasCopied ? (
<Icons.copy className="h-3 w-3" /> <Icons.check className="h-3 w-3" />
)} ) : (
<span className="sr-only">Copy</span> <Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => copyToClipboard(value)}> <DropdownMenuItem onClick={() => copyToClipboard(value)}>
<Icons.react className="mr-2 h-4 w-4" /> <Icons.react className="mr-2 h-4 w-4" />
<span>Component</span> <span>Component</span>
@@ -162,38 +168,38 @@ export function CopyNpmCommandButton({
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger <DropdownMenuTrigger asChild>
className={cn( <Button
"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", size="icon"
className variant="ghost"
)} className={cn(
{...props} "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" /> >
) : ( {hasCopied ? (
<Icons.copy className="h-3 w-3" /> <Icons.check className="h-3 w-3" />
)} ) : (
<span className="sr-only">Copy</span> <Icons.copy className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem <DropdownMenuItem
onClick={() => copyCommand(commands.__npmCommand__, "npm")} onClick={() => copyCommand(commands.__npmCommand__, "npm")}
> >
<Icons.npm className="mr-2 h-4 w-4" /> npm
<span>npm</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => copyCommand(commands.__yarnCommand__, "yarn")} onClick={() => copyCommand(commands.__yarnCommand__, "yarn")}
> >
<Icons.yarn className="mr-2 h-4 w-4" /> yarn
<span>yarn</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")} onClick={() => copyCommand(commands.__pnpmCommand__, "pnpm")}
> >
<Icons.pnpm className="mr-2 h-4 w-4" /> pnpm
<span>pnpm</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@@ -2,30 +2,46 @@
import Link from "next/link" import Link from "next/link"
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils" 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 = [ const examples = [
{ {
name: "Dashboard", name: "Dashboard",
href: "/examples/dashboard", href: "/examples/dashboard",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/dashboard",
}, },
{ {
name: "Cards", name: "Cards",
href: "/examples/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", name: "Playground",
href: "/examples/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", name: "Music",
href: "/examples/music", href: "/examples/music",
code: "https://github.com/shadcn/ui/tree/main/apps/www/app/examples/music",
}, },
{ {
name: "Authentication", name: "Authentication",
href: "/examples/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() const pathname = usePathname()
return ( return (
<ScrollArea> <div className="relative">
<div className={cn("mb-4 flex items-center", className)} {...props}> <ScrollArea className="max-w-[600px] lg:max-w-none">
{examples.map((example) => ( <div className={cn("mb-4 flex items-center", className)} {...props}>
<Link {examples.map((example) => (
href={example.href} <Link
key={example.href} href={example.href}
className={cn( key={example.href}
"flex px-4 font-medium", className={cn(
pathname === example.href "flex items-center px-4",
? "text-primary" pathname?.startsWith(example.href)
: "text-muted-foreground" ? "font-bold text-primary"
)} : "font-medium text-muted-foreground"
> )}
{example.name} >
</Link> {example.name}
))} </Link>
</div> ))}
<ScrollBar orientation="horizontal" className="invisible" /> </div>
</ScrollArea> <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,189 +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 { ButtonAsChild } from "@/components/examples/button/as-child"
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,
ButtonAsChild,
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