mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-23 04:35:46 +00:00
Compare commits
42 Commits
fix/cli-va
...
shadcn/cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2d894b2e8 | ||
|
|
bfcf2b2de9 | ||
|
|
ab10b70a90 | ||
|
|
4fe731d3e7 | ||
|
|
02cf3bc3f7 | ||
|
|
3621240f4f | ||
|
|
879b6eec90 | ||
|
|
61fd90fa1c | ||
|
|
9de91380f3 | ||
|
|
b5e5d2b91d | ||
|
|
9171932081 | ||
|
|
326cb3e6ad | ||
|
|
b7fc3167d8 | ||
|
|
cd66578d67 | ||
|
|
7fc999d284 | ||
|
|
8f86b9995e | ||
|
|
2f28d1c816 | ||
|
|
3a6eca12c0 | ||
|
|
d2081862b6 | ||
|
|
52cf7acaed | ||
|
|
75e08c1bc1 | ||
|
|
6184716dcd | ||
|
|
0251da8cc9 | ||
|
|
d97764bb55 | ||
|
|
f15a22073f | ||
|
|
2744218d71 | ||
|
|
b95ffc2168 | ||
|
|
30d47cab2f | ||
|
|
119bc7b044 | ||
|
|
0711a3711e | ||
|
|
33595c7d21 | ||
|
|
76738a9187 | ||
|
|
757b715aa5 | ||
|
|
fa4448fc6e | ||
|
|
d86676ae2a | ||
|
|
f4080fbdaf | ||
|
|
87b6d0c312 | ||
|
|
665d4e8f93 | ||
|
|
99eb4a2df7 | ||
|
|
3b8376b687 | ||
|
|
72719d663e | ||
|
|
6684b40e4c |
@@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
|
||||||
"changelog": ["@changesets/changelog-github", { "repo": "shadcn-ui/ui" }],
|
"changelog": [
|
||||||
|
"@changesets/changelog-github",
|
||||||
|
{ "repo": "shadcn-ui/ui-private" }
|
||||||
|
],
|
||||||
"commit": false,
|
"commit": false,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
"linked": [],
|
"linked": [],
|
||||||
|
|||||||
5
.changeset/large-phones-poke.md
Normal file
5
.changeset/large-phones-poke.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"shadcn-ui": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add support for frameworks
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
"plugin:tailwindcss/recommended"
|
"plugin:tailwindcss/recommended"
|
||||||
],
|
],
|
||||||
"plugins": ["tailwindcss"],
|
"plugins": ["tailwindcss"],
|
||||||
|
"ignorePatterns": ["**/fixtures/**"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-html-link-for-pages": "off",
|
"@next/next/no-html-link-for-pages": "off",
|
||||||
"tailwindcss/no-custom-classname": "off",
|
"tailwindcss/no-custom-classname": "off",
|
||||||
|
|||||||
6
.github/workflows/prerelease-comment.yml
vendored
6
.github/workflows/prerelease-comment.yml
vendored
@@ -28,8 +28,8 @@ jobs:
|
|||||||
|
|
||||||
for (const artifact of allArtifacts.data.artifacts) {
|
for (const artifact of allArtifacts.data.artifacts) {
|
||||||
// Extract the PR number and package version from the artifact name
|
// Extract the PR number and package version from the artifact name
|
||||||
const match = /^npm-package-shadcn-ui@(.*?)-pr-(\d+)/.exec(artifact.name);
|
const match = /^npm-package-shadcn@(.*?)-pr-(\d+)/.exec(artifact.name);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
require("fs").appendFileSync(
|
require("fs").appendFileSync(
|
||||||
process.env.GITHUB_ENV,
|
process.env.GITHUB_ENV,
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
A new prerelease is available for testing:
|
A new prerelease is available for testing:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npx shadcn-ui@${{ env.BETA_PACKAGE_VERSION }}
|
npx shadcn@${{ env.BETA_PACKAGE_VERSION }}
|
||||||
```
|
```
|
||||||
|
|
||||||
- name: "Remove the autorelease label once published"
|
- name: "Remove the autorelease label once published"
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ node_modules
|
|||||||
.next
|
.next
|
||||||
build
|
build
|
||||||
.contentlayer
|
.contentlayer
|
||||||
apps/www/pages/api/registry.json
|
apps/www/pages/api/registry.json
|
||||||
|
**/fixtures
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -6,5 +6,10 @@
|
|||||||
"tailwindCSS.experimental.classRegex": [
|
"tailwindCSS.experimental.classRegex": [
|
||||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||||
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||||
|
],
|
||||||
|
"vitest.debugExclude": [
|
||||||
|
"<node_internals>/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/fixtures/**"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
26
apps/www/__registry__/default/block/hello-block.tsx
Normal file
26
apps/www/__registry__/default/block/hello-block.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { HelloCard } from "@/registry/default/block/hello-block/components/hello-card"
|
||||||
|
import { useMediaQuery } from "@/registry/default/hooks/use-media-query"
|
||||||
|
import { cn } from "@/registry/default/lib/utils"
|
||||||
|
import { Button } from "@/registry/default/ui/button"
|
||||||
|
|
||||||
|
export default function HelloBlock() {
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HelloCard
|
||||||
|
title="Hello Block"
|
||||||
|
className={cn(
|
||||||
|
"p-12",
|
||||||
|
isDesktop
|
||||||
|
? "bg-muted text-muted-foreground"
|
||||||
|
: "bg-primary text-primary-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p>Hello. This is a component inside a block.</p>
|
||||||
|
<p>You are currently on {isDesktop ? "desktop" : "mobile"}</p>
|
||||||
|
<Button>Click me</Button>
|
||||||
|
</HelloCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
68
apps/www/__registry__/default/block/login-01.tsx
Normal file
68
apps/www/__registry__/default/block/login-01.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/registry/new-york/ui/card"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export const iframeHeight = "870px"
|
||||||
|
|
||||||
|
export const containerClassName = "w-full h-full"
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-full items-center justify-center px-4">
|
||||||
|
<Card className="mx-auto max-w-sm">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-2xl">Login</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Enter your email below to login to your account
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="m@example.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Label htmlFor="password">Password</Label>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="ml-auto inline-block text-sm underline"
|
||||||
|
>
|
||||||
|
Forgot your password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<Input id="password" type="password" required />
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="w-full">
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" className="w-full">
|
||||||
|
Login with Google
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-center text-sm">
|
||||||
|
Don't have an account?{" "}
|
||||||
|
<Link href="#" className="underline">
|
||||||
|
Sign up
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
apps/www/__registry__/default/block/sidebar-01.tsx
Normal file
25
apps/www/__registry__/default/block/sidebar-01.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { AppSidebar } from "@/registry/new-york/block/sidebar-01/components/app-sidebar"
|
||||||
|
import {
|
||||||
|
SidebarLayout,
|
||||||
|
SidebarTrigger,
|
||||||
|
} from "@/registry/new-york/block/sidebar-01/ui/sidebar"
|
||||||
|
|
||||||
|
export const iframeHeight = "870px"
|
||||||
|
|
||||||
|
export const containerClassName = "w-full h-full"
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const { cookies } = await import("next/headers")
|
||||||
|
return (
|
||||||
|
<SidebarLayout
|
||||||
|
defaultOpen={cookies().get("sidebar:state")?.value === "true"}
|
||||||
|
>
|
||||||
|
<AppSidebar />
|
||||||
|
<main className="flex flex-1 flex-col p-2 transition-all duration-300 ease-in-out">
|
||||||
|
<div className="h-full rounded-md border-2 border-dashed p-2">
|
||||||
|
<SidebarTrigger />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</SidebarLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
26
apps/www/__registry__/new-york/block/hello-block.tsx
Normal file
26
apps/www/__registry__/new-york/block/hello-block.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { HelloCard } from "@/registry/new-york/block/hello-block/components/hello-card"
|
||||||
|
import { useMediaQuery } from "@/registry/new-york/hooks/use-media-query"
|
||||||
|
import { cn } from "@/registry/new-york/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function HelloBlock() {
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HelloCard
|
||||||
|
title="Hello Block"
|
||||||
|
className={cn(
|
||||||
|
"p-12",
|
||||||
|
isDesktop
|
||||||
|
? "bg-muted text-muted-foreground"
|
||||||
|
: "bg-primary text-primary-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p>Hello. This is a component inside a block.</p>
|
||||||
|
<p>You are currently on {isDesktop ? "desktop" : "mobile"}</p>
|
||||||
|
<Button>Click me</Button>
|
||||||
|
</HelloCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
68
apps/www/__registry__/new-york/block/login-01.tsx
Normal file
68
apps/www/__registry__/new-york/block/login-01.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/registry/new-york/ui/card"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export const iframeHeight = "870px"
|
||||||
|
|
||||||
|
export const containerClassName = "w-full h-full"
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen w-full items-center justify-center px-4">
|
||||||
|
<Card className="mx-auto max-w-sm">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-2xl">Login</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Enter your email below to login to your account
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid gap-4">
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="m@example.com"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Label htmlFor="password">Password</Label>
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
className="ml-auto inline-block text-sm underline"
|
||||||
|
>
|
||||||
|
Forgot your password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<Input id="password" type="password" required />
|
||||||
|
</div>
|
||||||
|
<Button type="submit" className="w-full">
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" className="w-full">
|
||||||
|
Login with Google
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-center text-sm">
|
||||||
|
Don't have an account?{" "}
|
||||||
|
<Link href="#" className="underline">
|
||||||
|
Sign up
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
25
apps/www/__registry__/new-york/block/sidebar-01.tsx
Normal file
25
apps/www/__registry__/new-york/block/sidebar-01.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { AppSidebar } from "@/registry/new-york/block/sidebar-01/components/app-sidebar"
|
||||||
|
import {
|
||||||
|
SidebarLayout,
|
||||||
|
SidebarTrigger,
|
||||||
|
} from "@/registry/new-york/block/sidebar-01/ui/sidebar"
|
||||||
|
|
||||||
|
export const iframeHeight = "870px"
|
||||||
|
|
||||||
|
export const containerClassName = "w-full h-full"
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const { cookies } = await import("next/headers")
|
||||||
|
return (
|
||||||
|
<SidebarLayout
|
||||||
|
defaultOpen={cookies().get("sidebar:state")?.value === "true"}
|
||||||
|
>
|
||||||
|
<AppSidebar />
|
||||||
|
<main className="flex flex-1 flex-col p-2 transition-all duration-300 ease-in-out">
|
||||||
|
<div className="h-full rounded-md border-2 border-dashed p-2">
|
||||||
|
<SidebarTrigger />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</SidebarLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
apps/www/__registry__/new-york/component/accordion-demo.tsx
Normal file
32
apps/www/__registry__/new-york/component/accordion-demo.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/registry/new-york/ui/accordion"
|
||||||
|
|
||||||
|
export default function AccordionDemo() {
|
||||||
|
return (
|
||||||
|
<Accordion type="single" collapsible className="w-full">
|
||||||
|
<AccordionItem value="item-1">
|
||||||
|
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
Yes. It adheres to the WAI-ARIA design pattern.
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem value="item-2">
|
||||||
|
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
Yes. It comes with default styles that matches the other
|
||||||
|
components' aesthetic.
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem value="item-3">
|
||||||
|
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
Yes. It's animated by default, but you can disable it if you prefer.
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
)
|
||||||
|
}
|
||||||
19
apps/www/__registry__/new-york/component/alert-demo.tsx
Normal file
19
apps/www/__registry__/new-york/component/alert-demo.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { RocketIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
|
AlertTitle,
|
||||||
|
} from "@/registry/new-york/ui/alert"
|
||||||
|
|
||||||
|
export default function AlertDemo() {
|
||||||
|
return (
|
||||||
|
<Alert>
|
||||||
|
<RocketIcon className="h-4 w-4" />
|
||||||
|
<AlertTitle>Heads up!</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
You can add components to your app using the cli.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ExclamationTriangleIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
|
AlertTitle,
|
||||||
|
} from "@/registry/new-york/ui/alert"
|
||||||
|
|
||||||
|
export default function AlertDestructive() {
|
||||||
|
return (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<ExclamationTriangleIcon className="h-4 w-4" />
|
||||||
|
<AlertTitle>Error</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
Your session has expired. Please log in again.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
} from "@/registry/new-york/ui/alert-dialog"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function AlertDialogDemo() {
|
||||||
|
return (
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button variant="outline">Show Dialog</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
This action cannot be undone. This will permanently delete your
|
||||||
|
account and remove your data from our servers.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction>Continue</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Image from "next/image"
|
||||||
|
|
||||||
|
import { AspectRatio } from "@/registry/new-york/ui/aspect-ratio"
|
||||||
|
|
||||||
|
export default function AspectRatioDemo() {
|
||||||
|
return (
|
||||||
|
<AspectRatio ratio={16 / 9} className="bg-muted">
|
||||||
|
<Image
|
||||||
|
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
|
||||||
|
alt="Photo by Drew Beamer"
|
||||||
|
fill
|
||||||
|
className="rounded-md object-cover"
|
||||||
|
/>
|
||||||
|
</AspectRatio>
|
||||||
|
)
|
||||||
|
}
|
||||||
14
apps/www/__registry__/new-york/component/avatar-demo.tsx
Normal file
14
apps/www/__registry__/new-york/component/avatar-demo.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
|
||||||
|
export default function AvatarDemo() {
|
||||||
|
return (
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||||
|
<AvatarFallback>CN</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
apps/www/__registry__/new-york/component/badge-demo.tsx
Normal file
5
apps/www/__registry__/new-york/component/badge-demo.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
|
||||||
|
export default function BadgeDemo() {
|
||||||
|
return <Badge>Badge</Badge>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
|
||||||
|
export default function BadgeDestructive() {
|
||||||
|
return <Badge variant="destructive">Destructive</Badge>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
|
||||||
|
export default function BadgeOutline() {
|
||||||
|
return <Badge variant="outline">Outline</Badge>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Badge } from "@/registry/new-york/ui/badge"
|
||||||
|
|
||||||
|
export default function BadgeSecondary() {
|
||||||
|
return <Badge variant="secondary">Secondary</Badge>
|
||||||
|
}
|
||||||
49
apps/www/__registry__/new-york/component/breadcrumb-demo.tsx
Normal file
49
apps/www/__registry__/new-york/component/breadcrumb-demo.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbEllipsis,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export default function BreadcrumbDemo() {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||||
|
<BreadcrumbEllipsis className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Toggle menu</span>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
<DropdownMenuItem>Documentation</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Themes</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="/docs/components">Components</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { ChevronDownIcon, SlashIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export default function BreadcrumbWithDropdown() {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator>
|
||||||
|
<SlashIcon />
|
||||||
|
</BreadcrumbSeparator>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||||
|
Components
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
<DropdownMenuItem>Documentation</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Themes</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator>
|
||||||
|
<SlashIcon />
|
||||||
|
</BreadcrumbSeparator>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbEllipsis,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
|
||||||
|
export default function BreadcrumbCollapsed() {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink asChild>
|
||||||
|
<Link href="/">Home</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbEllipsis />
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink asChild>
|
||||||
|
<Link href="/docs/components">Components</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
34
apps/www/__registry__/new-york/component/breadcrumb-link.tsx
Normal file
34
apps/www/__registry__/new-york/component/breadcrumb-link.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
|
||||||
|
export default function BreadcrumbWithCustomSeparator() {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink>
|
||||||
|
<Link href="/">Home</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink>
|
||||||
|
<Link href="/components">Components</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbEllipsis,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/registry/new-york/ui/drawer"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ href: "#", label: "Home" },
|
||||||
|
{ href: "#", label: "Documentation" },
|
||||||
|
{ href: "#", label: "Building Your Application" },
|
||||||
|
{ href: "#", label: "Data Fetching" },
|
||||||
|
{ label: "Caching and Revalidating" },
|
||||||
|
]
|
||||||
|
|
||||||
|
const ITEMS_TO_DISPLAY = 3
|
||||||
|
|
||||||
|
export default function BreadcrumbResponsive() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href={items[0].href}>{items[0].label}</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
{items.length > ITEMS_TO_DISPLAY ? (
|
||||||
|
<>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
{isDesktop ? (
|
||||||
|
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||||
|
<DropdownMenuTrigger
|
||||||
|
className="flex items-center gap-1"
|
||||||
|
aria-label="Toggle menu"
|
||||||
|
>
|
||||||
|
<BreadcrumbEllipsis className="h-4 w-4" />
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start">
|
||||||
|
{items.slice(1, -2).map((item, index) => (
|
||||||
|
<DropdownMenuItem key={index}>
|
||||||
|
<Link href={item.href ? item.href : "#"}>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
) : (
|
||||||
|
<Drawer open={open} onOpenChange={setOpen}>
|
||||||
|
<DrawerTrigger aria-label="Toggle Menu">
|
||||||
|
<BreadcrumbEllipsis className="h-4 w-4" />
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerHeader className="text-left">
|
||||||
|
<DrawerTitle>Navigate to</DrawerTitle>
|
||||||
|
<DrawerDescription>
|
||||||
|
Select a page to navigate to.
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
<div className="grid gap-1 px-4">
|
||||||
|
{items.slice(1, -2).map((item, index) => (
|
||||||
|
<Link
|
||||||
|
key={index}
|
||||||
|
href={item.href ? item.href : "#"}
|
||||||
|
className="py-1 text-sm"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<DrawerFooter className="pt-4">
|
||||||
|
<DrawerClose asChild>
|
||||||
|
<Button variant="outline">Close</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</DrawerFooter>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
)}
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
{items.slice(-ITEMS_TO_DISPLAY + 1).map((item, index) => (
|
||||||
|
<BreadcrumbItem key={index}>
|
||||||
|
{item.href ? (
|
||||||
|
<>
|
||||||
|
<BreadcrumbLink
|
||||||
|
asChild
|
||||||
|
className="max-w-20 truncate md:max-w-none"
|
||||||
|
>
|
||||||
|
<Link href={item.href}>{item.label}</Link>
|
||||||
|
</BreadcrumbLink>
|
||||||
|
<BreadcrumbSeparator />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<BreadcrumbPage className="max-w-20 truncate md:max-w-none">
|
||||||
|
{item.label}
|
||||||
|
</BreadcrumbPage>
|
||||||
|
)}
|
||||||
|
</BreadcrumbItem>
|
||||||
|
))}
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { SlashIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbLink,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from "@/registry/new-york/ui/breadcrumb"
|
||||||
|
|
||||||
|
export default function BreadcrumbWithCustomSeparator() {
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator>
|
||||||
|
<SlashIcon />
|
||||||
|
</BreadcrumbSeparator>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbLink href="/components">Components</BreadcrumbLink>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
<BreadcrumbSeparator>
|
||||||
|
<SlashIcon />
|
||||||
|
</BreadcrumbSeparator>
|
||||||
|
<BreadcrumbItem>
|
||||||
|
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
)
|
||||||
|
}
|
||||||
11
apps/www/__registry__/new-york/component/button-as-child.tsx
Normal file
11
apps/www/__registry__/new-york/component/button-as-child.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonAsChild() {
|
||||||
|
return (
|
||||||
|
<Button asChild>
|
||||||
|
<Link href="/login">Login</Link>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
apps/www/__registry__/new-york/component/button-demo.tsx
Normal file
5
apps/www/__registry__/new-york/component/button-demo.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonDemo() {
|
||||||
|
return <Button>Button</Button>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonDestructive() {
|
||||||
|
return <Button variant="destructive">Destructive</Button>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonGhost() {
|
||||||
|
return <Button variant="ghost">Ghost</Button>
|
||||||
|
}
|
||||||
11
apps/www/__registry__/new-york/component/button-icon.tsx
Normal file
11
apps/www/__registry__/new-york/component/button-icon.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { ChevronRightIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonIcon() {
|
||||||
|
return (
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<ChevronRightIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
apps/www/__registry__/new-york/component/button-link.tsx
Normal file
5
apps/www/__registry__/new-york/component/button-link.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonLink() {
|
||||||
|
return <Button variant="link">Link</Button>
|
||||||
|
}
|
||||||
12
apps/www/__registry__/new-york/component/button-loading.tsx
Normal file
12
apps/www/__registry__/new-york/component/button-loading.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { ReloadIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonLoading() {
|
||||||
|
return (
|
||||||
|
<Button disabled>
|
||||||
|
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Please wait
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonOutline() {
|
||||||
|
return <Button variant="outline">Outline</Button>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonSecondary() {
|
||||||
|
return <Button variant="secondary">Secondary</Button>
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { EnvelopeOpenIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ButtonWithIcon() {
|
||||||
|
return (
|
||||||
|
<Button>
|
||||||
|
<EnvelopeOpenIcon className="mr-2 h-4 w-4" /> Login with Email
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
18
apps/www/__registry__/new-york/component/calendar-demo.tsx
Normal file
18
apps/www/__registry__/new-york/component/calendar-demo.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
|
|
||||||
|
export default function CalendarDemo() {
|
||||||
|
const [date, setDate] = React.useState<Date | undefined>(new Date())
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={date}
|
||||||
|
onSelect={setDate}
|
||||||
|
className="rounded-md border shadow"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
101
apps/www/__registry__/new-york/component/calendar-form.tsx
Normal file
101
apps/www/__registry__/new-york/component/calendar-form.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
import { format } from "date-fns"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { 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 {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
dob: z.date({
|
||||||
|
required_error: "A date of birth is required.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function CalendarForm() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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="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>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
78
apps/www/__registry__/new-york/component/card-demo.tsx
Normal file
78
apps/www/__registry__/new-york/component/card-demo.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { BellIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/registry/new-york/ui/card"
|
||||||
|
import { Switch } from "@/registry/new-york/ui/switch"
|
||||||
|
|
||||||
|
const notifications = [
|
||||||
|
{
|
||||||
|
title: "Your call has been confirmed.",
|
||||||
|
description: "1 hour ago",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "You have a new message!",
|
||||||
|
description: "1 hour ago",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Your subscription is expiring soon!",
|
||||||
|
description: "2 hours ago",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
type CardProps = React.ComponentProps<typeof Card>
|
||||||
|
|
||||||
|
export default function CardDemo({ className, ...props }: CardProps) {
|
||||||
|
return (
|
||||||
|
<Card className={cn("w-[380px]", className)} {...props}>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Notifications</CardTitle>
|
||||||
|
<CardDescription>You have 3 unread messages.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="grid gap-4">
|
||||||
|
<div className=" flex items-center space-x-4 rounded-md border p-4">
|
||||||
|
<BellIcon />
|
||||||
|
<div className="flex-1 space-y-1">
|
||||||
|
<p className="text-sm font-medium leading-none">
|
||||||
|
Push Notifications
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Send notifications to device.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{notifications.map((notification, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0"
|
||||||
|
>
|
||||||
|
<span className="flex h-2 w-2 translate-y-1 rounded-full bg-sky-500" />
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-sm font-medium leading-none">
|
||||||
|
{notification.title}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{notification.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button className="w-full">
|
||||||
|
<CheckIcon className="mr-2 h-4 w-4" /> Mark all as read
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
59
apps/www/__registry__/new-york/component/card-with-form.tsx
Normal file
59
apps/www/__registry__/new-york/component/card-with-form.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/registry/new-york/ui/card"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/registry/new-york/ui/select"
|
||||||
|
|
||||||
|
export default function CardWithForm() {
|
||||||
|
return (
|
||||||
|
<Card className="w-[350px]">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Create project</CardTitle>
|
||||||
|
<CardDescription>Deploy your new project in one-click.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form>
|
||||||
|
<div className="grid w-full items-center gap-4">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="name">Name</Label>
|
||||||
|
<Input id="name" placeholder="Name of your project" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="framework">Framework</Label>
|
||||||
|
<Select>
|
||||||
|
<SelectTrigger id="framework">
|
||||||
|
<SelectValue placeholder="Select" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent position="popper">
|
||||||
|
<SelectItem value="next">Next.js</SelectItem>
|
||||||
|
<SelectItem value="sveltekit">SvelteKit</SelectItem>
|
||||||
|
<SelectItem value="astro">Astro</SelectItem>
|
||||||
|
<SelectItem value="nuxt">Nuxt.js</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-between">
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
<Button>Deploy</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
53
apps/www/__registry__/new-york/component/carousel-api.tsx
Normal file
53
apps/www/__registry__/new-york/component/carousel-api.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
type CarouselApi,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselDApiDemo() {
|
||||||
|
const [api, setApi] = React.useState<CarouselApi>()
|
||||||
|
const [current, setCurrent] = React.useState(0)
|
||||||
|
const [count, setCount] = React.useState(0)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setCount(api.scrollSnapList().length)
|
||||||
|
setCurrent(api.selectedScrollSnap() + 1)
|
||||||
|
|
||||||
|
api.on("select", () => {
|
||||||
|
setCurrent(api.selectedScrollSnap() + 1)
|
||||||
|
})
|
||||||
|
}, [api])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Carousel setApi={setApi} className="w-full max-w-xs">
|
||||||
|
<CarouselContent>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-4xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
<div className="py-2 text-center text-sm text-muted-foreground">
|
||||||
|
Slide {current} of {count}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
apps/www/__registry__/new-york/component/carousel-demo.tsx
Normal file
32
apps/www/__registry__/new-york/component/carousel-demo.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselDemo() {
|
||||||
|
return (
|
||||||
|
<Carousel className="w-full max-w-xs">
|
||||||
|
<CarouselContent>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-4xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselOrientation() {
|
||||||
|
return (
|
||||||
|
<Carousel
|
||||||
|
opts={{
|
||||||
|
align: "start",
|
||||||
|
}}
|
||||||
|
orientation="vertical"
|
||||||
|
className="w-full max-w-xs"
|
||||||
|
>
|
||||||
|
<CarouselContent className="-mt-1 h-[200px]">
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index} className="pt-1 md:basis-1/2">
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex items-center justify-center p-6">
|
||||||
|
<span className="text-3xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
42
apps/www/__registry__/new-york/component/carousel-plugin.tsx
Normal file
42
apps/www/__registry__/new-york/component/carousel-plugin.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import Autoplay from "embla-carousel-autoplay"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselPlugin() {
|
||||||
|
const plugin = React.useRef(
|
||||||
|
Autoplay({ delay: 2000, stopOnInteraction: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Carousel
|
||||||
|
plugins={[plugin.current]}
|
||||||
|
className="w-full max-w-xs"
|
||||||
|
onMouseEnter={plugin.current.stop}
|
||||||
|
onMouseLeave={plugin.current.reset}
|
||||||
|
>
|
||||||
|
<CarouselContent>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-4xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
37
apps/www/__registry__/new-york/component/carousel-size.tsx
Normal file
37
apps/www/__registry__/new-york/component/carousel-size.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselSize() {
|
||||||
|
return (
|
||||||
|
<Carousel
|
||||||
|
opts={{
|
||||||
|
align: "start",
|
||||||
|
}}
|
||||||
|
className="w-full max-w-sm"
|
||||||
|
>
|
||||||
|
<CarouselContent>
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-3xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Card, CardContent } from "@/registry/new-york/ui/card"
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/registry/new-york/ui/carousel"
|
||||||
|
|
||||||
|
export default function CarouselSpacing() {
|
||||||
|
return (
|
||||||
|
<Carousel className="w-full max-w-sm">
|
||||||
|
<CarouselContent className="-ml-1">
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index} className="pl-1 md:basis-1/2 lg:basis-1/3">
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-2xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||||
|
|
||||||
|
import { ChartConfig, ChartContainer } from "@/registry/new-york/ui/chart"
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ month: "January", desktop: 186, mobile: 80 },
|
||||||
|
{ month: "February", desktop: 305, mobile: 200 },
|
||||||
|
{ month: "March", desktop: 237, mobile: 120 },
|
||||||
|
{ month: "April", desktop: 73, mobile: 190 },
|
||||||
|
{ month: "May", desktop: 209, mobile: 130 },
|
||||||
|
{ month: "June", desktop: 214, mobile: 140 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const chartConfig = {
|
||||||
|
desktop: {
|
||||||
|
label: "Desktop",
|
||||||
|
color: "#2563eb",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
label: "Mobile",
|
||||||
|
color: "#60a5fa",
|
||||||
|
},
|
||||||
|
} satisfies ChartConfig
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
|
||||||
|
<BarChart accessibilityLayer data={chartData}>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<XAxis
|
||||||
|
dataKey="month"
|
||||||
|
tickLine={false}
|
||||||
|
tickMargin={10}
|
||||||
|
axisLine={false}
|
||||||
|
tickFormatter={(value) => value.slice(0, 3)}
|
||||||
|
/>
|
||||||
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
||||||
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
||||||
|
</BarChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Bar, BarChart, CartesianGrid } from "recharts"
|
||||||
|
|
||||||
|
import { ChartConfig, ChartContainer } from "@/registry/new-york/ui/chart"
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ month: "January", desktop: 186, mobile: 80 },
|
||||||
|
{ month: "February", desktop: 305, mobile: 200 },
|
||||||
|
{ month: "March", desktop: 237, mobile: 120 },
|
||||||
|
{ month: "April", desktop: 73, mobile: 190 },
|
||||||
|
{ month: "May", desktop: 209, mobile: 130 },
|
||||||
|
{ month: "June", desktop: 214, mobile: 140 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const chartConfig = {
|
||||||
|
desktop: {
|
||||||
|
label: "Desktop",
|
||||||
|
color: "#2563eb",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
label: "Mobile",
|
||||||
|
color: "#60a5fa",
|
||||||
|
},
|
||||||
|
} satisfies ChartConfig
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
|
||||||
|
<BarChart accessibilityLayer data={chartData}>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
||||||
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
||||||
|
</BarChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChartConfig,
|
||||||
|
ChartContainer,
|
||||||
|
ChartLegend,
|
||||||
|
ChartLegendContent,
|
||||||
|
ChartTooltip,
|
||||||
|
ChartTooltipContent,
|
||||||
|
} from "@/registry/new-york/ui/chart"
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ month: "January", desktop: 186, mobile: 80 },
|
||||||
|
{ month: "February", desktop: 305, mobile: 200 },
|
||||||
|
{ month: "March", desktop: 237, mobile: 120 },
|
||||||
|
{ month: "April", desktop: 73, mobile: 190 },
|
||||||
|
{ month: "May", desktop: 209, mobile: 130 },
|
||||||
|
{ month: "June", desktop: 214, mobile: 140 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const chartConfig = {
|
||||||
|
desktop: {
|
||||||
|
label: "Desktop",
|
||||||
|
color: "#2563eb",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
label: "Mobile",
|
||||||
|
color: "#60a5fa",
|
||||||
|
},
|
||||||
|
} satisfies ChartConfig
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
|
||||||
|
<BarChart accessibilityLayer data={chartData}>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<XAxis
|
||||||
|
dataKey="month"
|
||||||
|
tickLine={false}
|
||||||
|
tickMargin={10}
|
||||||
|
axisLine={false}
|
||||||
|
tickFormatter={(value) => value.slice(0, 3)}
|
||||||
|
/>
|
||||||
|
<ChartTooltip content={<ChartTooltipContent />} />
|
||||||
|
<ChartLegend content={<ChartLegendContent />} />
|
||||||
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
||||||
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
||||||
|
</BarChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChartConfig,
|
||||||
|
ChartContainer,
|
||||||
|
ChartTooltip,
|
||||||
|
ChartTooltipContent,
|
||||||
|
} from "@/registry/new-york/ui/chart"
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ month: "January", desktop: 186, mobile: 80 },
|
||||||
|
{ month: "February", desktop: 305, mobile: 200 },
|
||||||
|
{ month: "March", desktop: 237, mobile: 120 },
|
||||||
|
{ month: "April", desktop: 73, mobile: 190 },
|
||||||
|
{ month: "May", desktop: 209, mobile: 130 },
|
||||||
|
{ month: "June", desktop: 214, mobile: 140 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const chartConfig = {
|
||||||
|
desktop: {
|
||||||
|
label: "Desktop",
|
||||||
|
color: "#2563eb",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
label: "Mobile",
|
||||||
|
color: "#60a5fa",
|
||||||
|
},
|
||||||
|
} satisfies ChartConfig
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
|
||||||
|
<BarChart accessibilityLayer data={chartData}>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<XAxis
|
||||||
|
dataKey="month"
|
||||||
|
tickLine={false}
|
||||||
|
tickMargin={10}
|
||||||
|
axisLine={false}
|
||||||
|
tickFormatter={(value) => value.slice(0, 3)}
|
||||||
|
/>
|
||||||
|
<ChartTooltip content={<ChartTooltipContent />} />
|
||||||
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
||||||
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
||||||
|
</BarChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
36
apps/www/__registry__/new-york/component/chart-bar-demo.tsx
Normal file
36
apps/www/__registry__/new-york/component/chart-bar-demo.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Bar, BarChart } from "recharts"
|
||||||
|
|
||||||
|
import { ChartConfig, ChartContainer } from "@/registry/new-york/ui/chart"
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ month: "January", desktop: 186, mobile: 80 },
|
||||||
|
{ month: "February", desktop: 305, mobile: 200 },
|
||||||
|
{ month: "March", desktop: 237, mobile: 120 },
|
||||||
|
{ month: "April", desktop: 73, mobile: 190 },
|
||||||
|
{ month: "May", desktop: 209, mobile: 130 },
|
||||||
|
{ month: "June", desktop: 214, mobile: 140 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const chartConfig = {
|
||||||
|
desktop: {
|
||||||
|
label: "Desktop",
|
||||||
|
color: "#2563eb",
|
||||||
|
},
|
||||||
|
mobile: {
|
||||||
|
label: "Mobile",
|
||||||
|
color: "#60a5fa",
|
||||||
|
},
|
||||||
|
} satisfies ChartConfig
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
|
||||||
|
<BarChart accessibilityLayer data={chartData}>
|
||||||
|
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
|
||||||
|
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
|
||||||
|
</BarChart>
|
||||||
|
</ChartContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
218
apps/www/__registry__/new-york/component/chart-tooltip-demo.tsx
Normal file
218
apps/www/__registry__/new-york/component/chart-tooltip-demo.tsx
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export default function Component() {
|
||||||
|
return (
|
||||||
|
<div className="grid aspect-video w-full max-w-md justify-center text-foreground md:grid-cols-2 [&>div]:relative [&>div]:flex [&>div]:h-[137px] [&>div]:w-[224px] [&>div]:items-center [&>div]:justify-center [&>div]:p-4">
|
||||||
|
<div>
|
||||||
|
<div className="absolute left-[-35px] top-[45px] z-10 text-sm font-medium">
|
||||||
|
Label
|
||||||
|
</div>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 193 40"
|
||||||
|
width="50"
|
||||||
|
height="12"
|
||||||
|
fill="none"
|
||||||
|
className="absolute left-[5px] top-[50px] z-10"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M173.928 21.13C115.811 44.938 58.751 45.773 0 26.141c4.227-4.386 7.82-2.715 10.567-1.88 21.133 5.64 42.9 6.266 64.457 7.101 31.066 1.253 60.441-5.848 89.183-17.335 1.268-.418 2.325-1.253 4.861-2.924-14.582-2.924-29.165 2.089-41.845-3.76.212-.835.212-1.879.423-2.714 9.51-.627 19.231-1.253 28.742-2.089 9.51-.835 18.808-1.88 28.318-2.506 6.974-.418 9.933 2.924 7.397 9.19-3.17 8.145-7.608 15.664-11.623 23.391-.423.836-1.057 1.88-1.902 2.298-2.325.835-4.65 1.044-7.186 1.67-.422-2.088-1.479-4.386-1.268-6.265.423-2.506 1.902-4.595 3.804-9.19Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="currentColor" d="M0 0h193v40H0z" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
<TooltipDemo
|
||||||
|
label="Page Views"
|
||||||
|
payload={[
|
||||||
|
{ name: "Desktop", value: 186, fill: "hsl(var(--chart-1))" },
|
||||||
|
{ name: "Mobile", value: 80, fill: "hsl(var(--chart-2))" },
|
||||||
|
]}
|
||||||
|
className="w-[8rem]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="items-end">
|
||||||
|
<div className="absolute left-[122px] top-[0px] z-10 text-sm font-medium">
|
||||||
|
Name
|
||||||
|
</div>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="35"
|
||||||
|
height="42"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 122 148"
|
||||||
|
className="absolute left-[85px] top-[10px] z-10 -scale-x-100"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#ab)">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M0 2.65c6.15-4.024 12.299-2.753 17.812-.847a115.56 115.56 0 0 1 21.84 10.59C70.4 32.727 88.849 61.744 96.483 97.54c1.908 9.108 2.544 18.639 3.817 29.017 8.481-4.871 12.934-14.402 21.416-19.909 1.061 4.236-1.06 6.989-2.756 9.319-6.998 9.531-14.207 19.062-21.63 28.382-3.604 4.448-6.36 4.871-10.177 1.059-8.058-7.837-12.935-17.368-14.42-28.382 0-.424.636-1.059 1.485-2.118 9.118 2.33 6.997 13.979 14.843 18.215 3.393-14.614.848-28.593-2.969-42.149-4.029-14.19-9.33-27.746-17.812-39.82-8.27-11.86-18.66-21.392-30.11-30.287C26.93 11.758 14.207 6.039 0 2.65Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="ab">
|
||||||
|
<path fill="currentColor" d="M0 0h122v148H0z" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
<TooltipDemo
|
||||||
|
label="Browser"
|
||||||
|
hideLabel
|
||||||
|
payload={[
|
||||||
|
{ name: "Chrome", value: 1286, fill: "hsl(var(--chart-3))" },
|
||||||
|
{ name: "Firefox", value: 1000, fill: "hsl(var(--chart-4))" },
|
||||||
|
]}
|
||||||
|
indicator="dashed"
|
||||||
|
className="w-[8rem]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="!hidden md:!flex">
|
||||||
|
<TooltipDemo
|
||||||
|
label="Page Views"
|
||||||
|
payload={[
|
||||||
|
{ name: "Desktop", value: 12486, fill: "hsl(var(--chart-3))" },
|
||||||
|
]}
|
||||||
|
className="w-[9rem]"
|
||||||
|
indicator="line"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="!items-start !justify-start">
|
||||||
|
<div className="absolute left-[50px] top-[60px] z-10 text-sm font-medium">
|
||||||
|
Indicator
|
||||||
|
</div>
|
||||||
|
<TooltipDemo
|
||||||
|
label="Browser"
|
||||||
|
hideLabel
|
||||||
|
payload={[
|
||||||
|
{ name: "Chrome", value: 1286, fill: "hsl(var(--chart-1))" },
|
||||||
|
]}
|
||||||
|
indicator="dot"
|
||||||
|
className="w-[8rem]"
|
||||||
|
/>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="15"
|
||||||
|
height="34"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 75 175"
|
||||||
|
className="absolute left-[30px] top-[38px] z-10 rotate-[-40deg]"
|
||||||
|
>
|
||||||
|
<g clip-path="url(#abc)">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M20.187 175c-4.439-2.109-7.186-2.531-8.032-4.008-3.17-5.484-6.763-10.968-8.454-17.084-5.073-16.242-4.439-32.694-1.057-49.146 5.707-28.053 18.388-52.942 34.24-76.565 1.692-2.531 3.171-5.063 4.862-7.805 0-.21-.211-.632-.634-1.265-4.65 1.265-9.511 2.53-14.161 3.585-2.537.422-5.496.422-8.032-.421-1.48-.422-3.593-2.742-3.593-4.219 0-1.898 1.48-4.218 2.747-5.906 1.057-1.054 2.96-1.265 4.65-1.687C35.406 7.315 48.088 3.729 60.98.776c10.99-2.53 14.584 1.055 13.95 11.812-.634 11.18-.846 22.358-1.268 33.326-.212 3.375-.846 6.96-1.268 10.757-8.878-4.007-8.878-4.007-12.048-38.177C47.03 33.259 38.153 49.289 29.91 65.741 21.667 82.193 16.17 99.49 13.212 117.84c-2.959 18.984.634 36.912 6.975 57.161Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="abc">
|
||||||
|
<path fill="currentColor" d="M0 0h75v175H0z" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TooltipDemo({
|
||||||
|
indicator = "dot",
|
||||||
|
label,
|
||||||
|
payload,
|
||||||
|
hideLabel,
|
||||||
|
hideIndicator,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
hideLabel?: boolean
|
||||||
|
hideIndicator?: boolean
|
||||||
|
indicator?: "line" | "dot" | "dashed"
|
||||||
|
payload: {
|
||||||
|
name: string
|
||||||
|
value: number
|
||||||
|
fill: string
|
||||||
|
}[]
|
||||||
|
nameKey?: string
|
||||||
|
labelKey?: string
|
||||||
|
} & React.ComponentProps<"div">) {
|
||||||
|
const tooltipLabel = hideLabel ? null : (
|
||||||
|
<div className="font-medium">{label}</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!payload?.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const nestLabel = payload.length === 1 && indicator !== "dot"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl transition-all ease-in-out hover:-translate-y-0.5",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{!nestLabel ? tooltipLabel : null}
|
||||||
|
<div className="grid gap-1.5">
|
||||||
|
{payload.map((item, index) => {
|
||||||
|
const indicatorColor = item.fill
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={cn(
|
||||||
|
"flex w-full items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
||||||
|
indicator === "dot" && "items-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<>
|
||||||
|
{!hideIndicator && (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
|
||||||
|
{
|
||||||
|
"h-2.5 w-2.5": indicator === "dot",
|
||||||
|
"w-1": indicator === "line",
|
||||||
|
"w-0 border-[1.5px] border-dashed bg-transparent":
|
||||||
|
indicator === "dashed",
|
||||||
|
"my-0.5": nestLabel && indicator === "dashed",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--color-bg": indicatorColor,
|
||||||
|
"--color-border": indicatorColor,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-1 justify-between leading-none",
|
||||||
|
nestLabel ? "items-end" : "items-center"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="grid gap-1.5">
|
||||||
|
{nestLabel ? tooltipLabel : null}
|
||||||
|
<span className="text-muted-foreground">{item.name}</span>
|
||||||
|
</div>
|
||||||
|
<span className="font-mono font-medium tabular-nums text-foreground">
|
||||||
|
{item.value.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
apps/www/__registry__/new-york/component/checkbox-demo.tsx
Normal file
17
apps/www/__registry__/new-york/component/checkbox-demo.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
|
||||||
|
export default function CheckboxDemo() {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox id="terms" />
|
||||||
|
<label
|
||||||
|
htmlFor="terms"
|
||||||
|
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Accept terms and conditions
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
|
||||||
|
export default function CheckboxDisabled() {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox id="terms2" disabled />
|
||||||
|
<label
|
||||||
|
htmlFor="terms2"
|
||||||
|
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Accept terms and conditions
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { 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 FormSchema = z.object({
|
||||||
|
items: z.array(z.string()).refine((value) => value.some((item) => item), {
|
||||||
|
message: "You have to select at least one item.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function CheckboxReactHookFormMultiple() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
items: ["recents", "home"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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="text-sm font-normal">
|
||||||
|
{item.label}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { 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,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
mobile: z.boolean().default(false).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function CheckboxReactHookFormSingle() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
mobile: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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-6">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="mobile"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4 shadow">
|
||||||
|
<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">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
|
||||||
|
export default function CheckboxWithText() {
|
||||||
|
return (
|
||||||
|
<div className="items-top flex space-x-2">
|
||||||
|
<Checkbox id="terms1" />
|
||||||
|
<div className="grid gap-1.5 leading-none">
|
||||||
|
<label
|
||||||
|
htmlFor="terms1"
|
||||||
|
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
>
|
||||||
|
Accept terms and conditions
|
||||||
|
</label>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
You agree to our Terms of Service and Privacy Policy.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CaretSortIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "@/registry/new-york/ui/collapsible"
|
||||||
|
|
||||||
|
export default function CollapsibleDemo() {
|
||||||
|
const [isOpen, setIsOpen] = React.useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Collapsible
|
||||||
|
open={isOpen}
|
||||||
|
onOpenChange={setIsOpen}
|
||||||
|
className="w-[350px] space-y-2"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between space-x-4 px-4">
|
||||||
|
<h4 className="text-sm font-semibold">
|
||||||
|
@peduarte starred 3 repositories
|
||||||
|
</h4>
|
||||||
|
<CollapsibleTrigger asChild>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<CaretSortIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Toggle</span>
|
||||||
|
</Button>
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-sm">
|
||||||
|
@radix-ui/primitives
|
||||||
|
</div>
|
||||||
|
<CollapsibleContent className="space-y-2">
|
||||||
|
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-sm">
|
||||||
|
@radix-ui/colors
|
||||||
|
</div>
|
||||||
|
<div className="rounded-md border px-4 py-2 font-mono text-sm shadow-sm">
|
||||||
|
@stitches/react
|
||||||
|
</div>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
)
|
||||||
|
}
|
||||||
94
apps/www/__registry__/new-york/component/combobox-demo.tsx
Normal file
94
apps/www/__registry__/new-york/component/combobox-demo.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
|
const frameworks = [
|
||||||
|
{
|
||||||
|
value: "next.js",
|
||||||
|
label: "Next.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "sveltekit",
|
||||||
|
label: "SvelteKit",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "nuxt.js",
|
||||||
|
label: "Nuxt.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "remix",
|
||||||
|
label: "Remix",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "astro",
|
||||||
|
label: "Astro",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComboboxDemo() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const [value, setValue] = React.useState("")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
aria-expanded={open}
|
||||||
|
className="w-[200px] justify-between"
|
||||||
|
>
|
||||||
|
{value
|
||||||
|
? frameworks.find((framework) => framework.value === value)?.label
|
||||||
|
: "Select framework..."}
|
||||||
|
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Search framework..." className="h-9" />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{frameworks.map((framework) => (
|
||||||
|
<CommandItem
|
||||||
|
key={framework.value}
|
||||||
|
value={framework.value}
|
||||||
|
onSelect={(currentValue) => {
|
||||||
|
setValue(currentValue === value ? "" : currentValue)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{framework.label}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
value === framework.value ? "opacity-100" : "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { DotsHorizontalIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
const labels = [
|
||||||
|
"feature",
|
||||||
|
"bug",
|
||||||
|
"enhancement",
|
||||||
|
"documentation",
|
||||||
|
"design",
|
||||||
|
"question",
|
||||||
|
"maintenance",
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComboboxDropdownMenu() {
|
||||||
|
const [label, setLabel] = React.useState("feature")
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col items-start justify-between rounded-md border px-4 py-3 sm:flex-row sm:items-center">
|
||||||
|
<p className="text-sm font-medium leading-none">
|
||||||
|
<span className="mr-2 rounded-lg bg-primary px-2 py-1 text-xs text-primary-foreground">
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground">Create a new project</span>
|
||||||
|
</p>
|
||||||
|
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" size="sm">
|
||||||
|
<DotsHorizontalIcon />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-[200px]">
|
||||||
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>Assign to...</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Set due date...</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>Apply label</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent className="p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Filter label..."
|
||||||
|
autoFocus={true}
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No label found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{labels.map((label) => (
|
||||||
|
<CommandItem
|
||||||
|
key={label}
|
||||||
|
value={label}
|
||||||
|
onSelect={(value) => {
|
||||||
|
setLabel(value)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem className="text-red-600">
|
||||||
|
Delete
|
||||||
|
<DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
141
apps/www/__registry__/new-york/component/combobox-form.tsx
Normal file
141
apps/www/__registry__/new-york/component/combobox-form.tsx
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
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 FormSchema = z.object({
|
||||||
|
language: z.string({
|
||||||
|
required_error: "Please select a language.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function ComboboxForm() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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-6">
|
||||||
|
<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"}
|
||||||
|
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</FormControl>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="Search framework..."
|
||||||
|
className="h-9"
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No framework found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{languages.map((language) => (
|
||||||
|
<CommandItem
|
||||||
|
value={language.label}
|
||||||
|
key={language.value}
|
||||||
|
onSelect={() => {
|
||||||
|
form.setValue("language", language.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{language.label}
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"ml-auto h-4 w-4",
|
||||||
|
language.value === field.value
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
<FormDescription>
|
||||||
|
This is the language that will be used in the dashboard.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
|
type Status = {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses: Status[] = [
|
||||||
|
{
|
||||||
|
value: "backlog",
|
||||||
|
label: "Backlog",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "todo",
|
||||||
|
label: "Todo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "in progress",
|
||||||
|
label: "In Progress",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "done",
|
||||||
|
label: "Done",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "canceled",
|
||||||
|
label: "Canceled",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComboboxPopover() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const [selectedStatus, setSelectedStatus] = React.useState<Status | null>(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<p className="text-sm text-muted-foreground">Status</p>
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="outline" className="w-[150px] justify-start">
|
||||||
|
{selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0" side="right" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Change status..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{statuses.map((status) => (
|
||||||
|
<CommandItem
|
||||||
|
key={status.value}
|
||||||
|
value={status.value}
|
||||||
|
onSelect={(value) => {
|
||||||
|
setSelectedStatus(
|
||||||
|
statuses.find((priority) => priority.value === value) ||
|
||||||
|
null
|
||||||
|
)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status.label}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
123
apps/www/__registry__/new-york/component/combobox-responsive.tsx
Normal file
123
apps/www/__registry__/new-york/component/combobox-responsive.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/registry/new-york/ui/drawer"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
|
type Status = {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const statuses: Status[] = [
|
||||||
|
{
|
||||||
|
value: "backlog",
|
||||||
|
label: "Backlog",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "todo",
|
||||||
|
label: "Todo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "in progress",
|
||||||
|
label: "In Progress",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "done",
|
||||||
|
label: "Done",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "canceled",
|
||||||
|
label: "Canceled",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ComboBoxResponsive() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
const [selectedStatus, setSelectedStatus] = React.useState<Status | null>(
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="outline" className="w-[150px] justify-start">
|
||||||
|
{selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0" align="start">
|
||||||
|
<StatusList setOpen={setOpen} setSelectedStatus={setSelectedStatus} />
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer open={open} onOpenChange={setOpen}>
|
||||||
|
<DrawerTrigger asChild>
|
||||||
|
<Button variant="outline" className="w-[150px] justify-start">
|
||||||
|
{selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>}
|
||||||
|
</Button>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent>
|
||||||
|
<div className="mt-4 border-t">
|
||||||
|
<StatusList setOpen={setOpen} setSelectedStatus={setSelectedStatus} />
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusList({
|
||||||
|
setOpen,
|
||||||
|
setSelectedStatus,
|
||||||
|
}: {
|
||||||
|
setOpen: (open: boolean) => void
|
||||||
|
setSelectedStatus: (status: Status | null) => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Command>
|
||||||
|
<CommandInput placeholder="Filter status..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{statuses.map((status) => (
|
||||||
|
<CommandItem
|
||||||
|
key={status.value}
|
||||||
|
value={status.value}
|
||||||
|
onSelect={(value) => {
|
||||||
|
setSelectedStatus(
|
||||||
|
statuses.find((priority) => priority.value === value) || null
|
||||||
|
)
|
||||||
|
setOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{status.label}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
)
|
||||||
|
}
|
||||||
62
apps/www/__registry__/new-york/component/command-demo.tsx
Normal file
62
apps/www/__registry__/new-york/component/command-demo.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import {
|
||||||
|
CalendarIcon,
|
||||||
|
EnvelopeClosedIcon,
|
||||||
|
FaceIcon,
|
||||||
|
GearIcon,
|
||||||
|
PersonIcon,
|
||||||
|
RocketIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
|
||||||
|
export default function CommandDemo() {
|
||||||
|
return (
|
||||||
|
<Command className="rounded-lg border shadow-md">
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Calendar</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<FaceIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Search Emoji</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem disabled>
|
||||||
|
<RocketIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Launch</span>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem>
|
||||||
|
<PersonIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<CommandShortcut>⌘P</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<EnvelopeClosedIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Mail</span>
|
||||||
|
<CommandShortcut>⌘B</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<GearIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Settings</span>
|
||||||
|
<CommandShortcut>⌘S</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
)
|
||||||
|
}
|
||||||
87
apps/www/__registry__/new-york/component/command-dialog.tsx
Normal file
87
apps/www/__registry__/new-york/component/command-dialog.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
CalendarIcon,
|
||||||
|
EnvelopeClosedIcon,
|
||||||
|
FaceIcon,
|
||||||
|
GearIcon,
|
||||||
|
PersonIcon,
|
||||||
|
RocketIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
CommandDialog,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
CommandSeparator,
|
||||||
|
CommandShortcut,
|
||||||
|
} from "@/registry/new-york/ui/command"
|
||||||
|
|
||||||
|
export default function CommandDialogDemo() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const down = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
|
||||||
|
e.preventDefault()
|
||||||
|
setOpen((open) => !open)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("keydown", down)
|
||||||
|
return () => document.removeEventListener("keydown", down)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Press{" "}
|
||||||
|
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
|
||||||
|
<span className="text-xs">⌘</span>J
|
||||||
|
</kbd>
|
||||||
|
</p>
|
||||||
|
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||||
|
<CommandInput placeholder="Type a command or search..." />
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>No results found.</CommandEmpty>
|
||||||
|
<CommandGroup heading="Suggestions">
|
||||||
|
<CommandItem>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Calendar</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<FaceIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Search Emoji</span>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<RocketIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Launch</span>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
<CommandSeparator />
|
||||||
|
<CommandGroup heading="Settings">
|
||||||
|
<CommandItem>
|
||||||
|
<PersonIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<CommandShortcut>⌘P</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<EnvelopeClosedIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Mail</span>
|
||||||
|
<CommandShortcut>⌘B</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
<CommandItem>
|
||||||
|
<GearIcon className="mr-2 h-4 w-4" />
|
||||||
|
<span>Settings</span>
|
||||||
|
<CommandShortcut>⌘S</CommandShortcut>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</CommandDialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
ContextMenu,
|
||||||
|
ContextMenuCheckboxItem,
|
||||||
|
ContextMenuContent,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuLabel,
|
||||||
|
ContextMenuRadioGroup,
|
||||||
|
ContextMenuRadioItem,
|
||||||
|
ContextMenuSeparator,
|
||||||
|
ContextMenuShortcut,
|
||||||
|
ContextMenuSub,
|
||||||
|
ContextMenuSubContent,
|
||||||
|
ContextMenuSubTrigger,
|
||||||
|
ContextMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/context-menu"
|
||||||
|
|
||||||
|
export default function ContextMenuDemo() {
|
||||||
|
return (
|
||||||
|
<ContextMenu>
|
||||||
|
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">
|
||||||
|
Right click here
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
<ContextMenuContent className="w-64">
|
||||||
|
<ContextMenuItem inset>
|
||||||
|
Back
|
||||||
|
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem inset disabled>
|
||||||
|
Forward
|
||||||
|
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem inset>
|
||||||
|
Reload
|
||||||
|
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuSub>
|
||||||
|
<ContextMenuSubTrigger inset>More Tools</ContextMenuSubTrigger>
|
||||||
|
<ContextMenuSubContent className="w-48">
|
||||||
|
<ContextMenuItem>
|
||||||
|
Save Page As...
|
||||||
|
<ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
|
||||||
|
</ContextMenuItem>
|
||||||
|
<ContextMenuItem>Create Shortcut...</ContextMenuItem>
|
||||||
|
<ContextMenuItem>Name Window...</ContextMenuItem>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuItem>Developer Tools</ContextMenuItem>
|
||||||
|
</ContextMenuSubContent>
|
||||||
|
</ContextMenuSub>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuCheckboxItem checked>
|
||||||
|
Show Bookmarks Bar
|
||||||
|
<ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>
|
||||||
|
</ContextMenuCheckboxItem>
|
||||||
|
<ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuRadioGroup value="pedro">
|
||||||
|
<ContextMenuLabel inset>People</ContextMenuLabel>
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuRadioItem value="pedro">
|
||||||
|
Pedro Duarte
|
||||||
|
</ContextMenuRadioItem>
|
||||||
|
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
|
||||||
|
</ContextMenuRadioGroup>
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
316
apps/www/__registry__/new-york/component/data-table-demo.tsx
Normal file
316
apps/www/__registry__/new-york/component/data-table-demo.tsx
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
DotsHorizontalIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
ColumnFiltersState,
|
||||||
|
SortingState,
|
||||||
|
VisibilityState,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getFilteredRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
getSortedRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/registry/new-york/ui/table"
|
||||||
|
|
||||||
|
const data: Payment[] = [
|
||||||
|
{
|
||||||
|
id: "m5gr84i9",
|
||||||
|
amount: 316,
|
||||||
|
status: "success",
|
||||||
|
email: "ken99@yahoo.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3u1reuv4",
|
||||||
|
amount: 242,
|
||||||
|
status: "success",
|
||||||
|
email: "Abe45@gmail.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "derv1ws0",
|
||||||
|
amount: 837,
|
||||||
|
status: "processing",
|
||||||
|
email: "Monserrat44@gmail.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "5kma53ae",
|
||||||
|
amount: 874,
|
||||||
|
status: "success",
|
||||||
|
email: "Silas22@gmail.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "bhqecj4p",
|
||||||
|
amount: 721,
|
||||||
|
status: "failed",
|
||||||
|
email: "carmella@hotmail.com",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export type Payment = {
|
||||||
|
id: string
|
||||||
|
amount: number
|
||||||
|
status: "pending" | "processing" | "success" | "failed"
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const columns: ColumnDef<Payment>[] = [
|
||||||
|
{
|
||||||
|
id: "select",
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsAllPageRowsSelected() ||
|
||||||
|
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
aria-label="Select all"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
aria-label="Select row"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "status",
|
||||||
|
header: "Status",
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="capitalize">{row.getValue("status")}</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "email",
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
<CaretSortIcon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
cell: ({ row }) => <div className="lowercase">{row.getValue("email")}</div>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "amount",
|
||||||
|
header: () => <div className="text-right">Amount</div>,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const amount = parseFloat(row.getValue("amount"))
|
||||||
|
|
||||||
|
// Format the amount as a dollar amount
|
||||||
|
const formatted = new Intl.NumberFormat("en-US", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "USD",
|
||||||
|
}).format(amount)
|
||||||
|
|
||||||
|
return <div className="text-right font-medium">{formatted}</div>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "actions",
|
||||||
|
enableHiding: false,
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const payment = row.original
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||||
|
<span className="sr-only">Open menu</span>
|
||||||
|
<DotsHorizontalIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||||
|
>
|
||||||
|
Copy payment ID
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function DataTableDemo() {
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||||
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
React.useState<VisibilityState>({})
|
||||||
|
const [rowSelection, setRowSelection] = React.useState({})
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
onColumnFiltersChange: setColumnFilters,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
getSortedRowModel: getSortedRowModel(),
|
||||||
|
getFilteredRowModel: getFilteredRowModel(),
|
||||||
|
onColumnVisibilityChange: setColumnVisibility,
|
||||||
|
onRowSelectionChange: setRowSelection,
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
columnFilters,
|
||||||
|
columnVisibility,
|
||||||
|
rowSelection,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
<div className="flex items-center py-4">
|
||||||
|
<Input
|
||||||
|
placeholder="Filter emails..."
|
||||||
|
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
|
||||||
|
onChange={(event) =>
|
||||||
|
table.getColumn("email")?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" className="ml-auto">
|
||||||
|
Columns <ChevronDownIcon className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{table
|
||||||
|
.getAllColumns()
|
||||||
|
.filter((column) => column.getCanHide())
|
||||||
|
.map((column) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
key={column.id}
|
||||||
|
className="capitalize"
|
||||||
|
checked={column.getIsVisible()}
|
||||||
|
onCheckedChange={(value) =>
|
||||||
|
column.toggleVisibility(!!value)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{column.id}
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
<div className="flex items-center justify-end space-x-2 py-4">
|
||||||
|
<div className="flex-1 text-sm text-muted-foreground">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||||
|
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
import { format } from "date-fns"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
|
export default function DatePickerDemo() {
|
||||||
|
const [date, setDate] = React.useState<Date>()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"w-[240px] justify-start text-left font-normal",
|
||||||
|
!date && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
selected={date}
|
||||||
|
onSelect={setDate}
|
||||||
|
initialFocus
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
101
apps/www/__registry__/new-york/component/date-picker-form.tsx
Normal file
101
apps/www/__registry__/new-york/component/date-picker-form.tsx
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
import { format } from "date-fns"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { 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 {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
dob: z.date({
|
||||||
|
required_error: "A date of birth is required.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function DatePickerForm() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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="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>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
import { addDays, format } from "date-fns"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/registry/new-york/ui/select"
|
||||||
|
|
||||||
|
export default function DatePickerWithPresets() {
|
||||||
|
const [date, setDate] = React.useState<Date>()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"w-[240px] justify-start text-left font-normal",
|
||||||
|
!date && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
align="start"
|
||||||
|
className="flex w-auto flex-col space-y-2 p-2"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setDate(addDays(new Date(), parseInt(value)))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent position="popper">
|
||||||
|
<SelectItem value="0">Today</SelectItem>
|
||||||
|
<SelectItem value="1">Tomorrow</SelectItem>
|
||||||
|
<SelectItem value="3">In 3 days</SelectItem>
|
||||||
|
<SelectItem value="7">In a week</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<Calendar mode="single" selected={date} onSelect={setDate} />
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
import { addDays, format } from "date-fns"
|
||||||
|
import { DateRange } from "react-day-picker"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Calendar } from "@/registry/new-york/ui/calendar"
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from "@/registry/new-york/ui/popover"
|
||||||
|
|
||||||
|
export default function DatePickerWithRange({
|
||||||
|
className,
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
const [date, setDate] = React.useState<DateRange | undefined>({
|
||||||
|
from: new Date(2022, 0, 20),
|
||||||
|
to: addDays(new Date(2022, 0, 20), 20),
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("grid gap-2", className)}>
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
id="date"
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"w-[300px] justify-start text-left font-normal",
|
||||||
|
!date && "text-muted-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||||
|
{date?.from ? (
|
||||||
|
date.to ? (
|
||||||
|
<>
|
||||||
|
{format(date.from, "LLL dd, y")} -{" "}
|
||||||
|
{format(date.to, "LLL dd, y")}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
format(date.from, "LLL dd, y")
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<span>Pick a date</span>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-auto p-0" align="start">
|
||||||
|
<Calendar
|
||||||
|
initialFocus
|
||||||
|
mode="range"
|
||||||
|
defaultMonth={date?.from}
|
||||||
|
selected={date}
|
||||||
|
onSelect={setDate}
|
||||||
|
numberOfMonths={2}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { CopyIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dialog"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function DialogCloseButton() {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline">Share</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Share link</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Anyone who has this link will be able to view this.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="grid flex-1 gap-2">
|
||||||
|
<Label htmlFor="link" className="sr-only">
|
||||||
|
Link
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="link"
|
||||||
|
defaultValue="https://ui.shadcn.com/docs/installation"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button type="submit" size="sm" className="px-3">
|
||||||
|
<span className="sr-only">Copy</span>
|
||||||
|
<CopyIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="sm:justify-start">
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button type="button" variant="secondary">
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
47
apps/www/__registry__/new-york/component/dialog-demo.tsx
Normal file
47
apps/www/__registry__/new-york/component/dialog-demo.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dialog"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function DialogDemo() {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline">Edit Profile</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit profile</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're done.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="grid gap-4 py-4">
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="name" className="text-right">
|
||||||
|
Name
|
||||||
|
</Label>
|
||||||
|
<Input id="name" value="Pedro Duarte" className="col-span-3" />
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
|
<Label htmlFor="username" className="text-right">
|
||||||
|
Username
|
||||||
|
</Label>
|
||||||
|
<Input id="username" value="@peduarte" className="col-span-3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit">Save changes</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
134
apps/www/__registry__/new-york/component/drawer-demo.tsx
Normal file
134
apps/www/__registry__/new-york/component/drawer-demo.tsx
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { MinusIcon, PlusIcon } from "@radix-ui/react-icons"
|
||||||
|
import { Bar, BarChart, ResponsiveContainer } from "recharts"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/registry/new-york/ui/drawer"
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
goal: 400,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 278,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 189,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 239,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 278,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 189,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
goal: 349,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function DrawerDemo() {
|
||||||
|
const [goal, setGoal] = React.useState(350)
|
||||||
|
|
||||||
|
function onClick(adjustment: number) {
|
||||||
|
setGoal(Math.max(200, Math.min(400, goal + adjustment)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer>
|
||||||
|
<DrawerTrigger asChild>
|
||||||
|
<Button variant="outline">Open Drawer</Button>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent>
|
||||||
|
<div className="mx-auto w-full max-w-sm">
|
||||||
|
<DrawerHeader>
|
||||||
|
<DrawerTitle>Move Goal</DrawerTitle>
|
||||||
|
<DrawerDescription>Set your daily activity goal.</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
<div className="p-4 pb-0">
|
||||||
|
<div className="flex items-center justify-center space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 rounded-full"
|
||||||
|
onClick={() => onClick(-10)}
|
||||||
|
disabled={goal <= 200}
|
||||||
|
>
|
||||||
|
<MinusIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Decrease</span>
|
||||||
|
</Button>
|
||||||
|
<div className="flex-1 text-center">
|
||||||
|
<div className="text-7xl font-bold tracking-tighter">
|
||||||
|
{goal}
|
||||||
|
</div>
|
||||||
|
<div className="text-[0.70rem] uppercase text-muted-foreground">
|
||||||
|
Calories/day
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8 shrink-0 rounded-full"
|
||||||
|
onClick={() => onClick(10)}
|
||||||
|
disabled={goal >= 400}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Increase</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 h-[120px]">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<BarChart data={data}>
|
||||||
|
<Bar
|
||||||
|
dataKey="goal"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
fill: "hsl(var(--foreground))",
|
||||||
|
opacity: 0.9,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DrawerFooter>
|
||||||
|
<Button>Submit</Button>
|
||||||
|
<DrawerClose asChild>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</DrawerFooter>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
87
apps/www/__registry__/new-york/component/drawer-dialog.tsx
Normal file
87
apps/www/__registry__/new-york/component/drawer-dialog.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dialog"
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/registry/new-york/ui/drawer"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function DrawerDialogDemo() {
|
||||||
|
const [open, setOpen] = React.useState(false)
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline">Edit Profile</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Edit profile</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Make changes to your profile here. Click save when you're done.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<ProfileForm />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer open={open} onOpenChange={setOpen}>
|
||||||
|
<DrawerTrigger asChild>
|
||||||
|
<Button variant="outline">Edit Profile</Button>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent>
|
||||||
|
<DrawerHeader className="text-left">
|
||||||
|
<DrawerTitle>Edit profile</DrawerTitle>
|
||||||
|
<DrawerDescription>
|
||||||
|
Make changes to your profile here. Click save when you're done.
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
<ProfileForm className="px-4" />
|
||||||
|
<DrawerFooter className="pt-2">
|
||||||
|
<DrawerClose asChild>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</DrawerFooter>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProfileForm({ className }: React.ComponentProps<"form">) {
|
||||||
|
return (
|
||||||
|
<form className={cn("grid items-start gap-4", className)}>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input type="email" id="email" defaultValue="shadcn@example.com" />
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Label htmlFor="username">Username</Label>
|
||||||
|
<Input id="username" defaultValue="@shadcn" />
|
||||||
|
</div>
|
||||||
|
<Button type="submit">Save changes</Button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { DropdownMenuCheckboxItemProps } from "@radix-ui/react-dropdown-menu"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuCheckboxItem,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
type Checked = DropdownMenuCheckboxItemProps["checked"]
|
||||||
|
|
||||||
|
export default function DropdownMenuCheckboxes() {
|
||||||
|
const [showStatusBar, setShowStatusBar] = React.useState<Checked>(true)
|
||||||
|
const [showActivityBar, setShowActivityBar] = React.useState<Checked>(false)
|
||||||
|
const [showPanel, setShowPanel] = React.useState<Checked>(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline">Open</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={showStatusBar}
|
||||||
|
onCheckedChange={setShowStatusBar}
|
||||||
|
>
|
||||||
|
Status Bar
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={showActivityBar}
|
||||||
|
onCheckedChange={setShowActivityBar}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
Activity Bar
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
<DropdownMenuCheckboxItem
|
||||||
|
checked={showPanel}
|
||||||
|
onCheckedChange={setShowPanel}
|
||||||
|
>
|
||||||
|
Panel
|
||||||
|
</DropdownMenuCheckboxItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuPortal,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuSub,
|
||||||
|
DropdownMenuSubContent,
|
||||||
|
DropdownMenuSubTrigger,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export default function DropdownMenuDemo() {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline">Open</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Profile
|
||||||
|
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Billing
|
||||||
|
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Settings
|
||||||
|
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Keyboard shortcuts
|
||||||
|
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<DropdownMenuItem>Team</DropdownMenuItem>
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger>Invite users</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuPortal>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
|
<DropdownMenuItem>Email</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Message</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>More...</DropdownMenuItem>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
New Team
|
||||||
|
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Support</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem disabled>API</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Log out
|
||||||
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuRadioGroup,
|
||||||
|
DropdownMenuRadioItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export default function DropdownMenuRadioGroupDemo() {
|
||||||
|
const [position, setPosition] = React.useState("bottom")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline">Open</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
||||||
|
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
||||||
|
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
||||||
|
</DropdownMenuRadioGroup>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
22
apps/www/__registry__/new-york/component/hello-block.tsx
Normal file
22
apps/www/__registry__/new-york/component/hello-block.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useMediaQuery } from "@/registry/new-york/hooks/use-media-query"
|
||||||
|
import { cn } from "@/registry/new-york/lib/utils"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
|
||||||
|
export default function ExampleBlock() {
|
||||||
|
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"p-12",
|
||||||
|
isDesktop
|
||||||
|
? "bg-muted text-muted-foreground"
|
||||||
|
: "bg-primary text-primary-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button>A button</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
43
apps/www/__registry__/new-york/component/hover-card-demo.tsx
Normal file
43
apps/www/__registry__/new-york/component/hover-card-demo.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { CalendarIcon } from "@radix-ui/react-icons"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarImage,
|
||||||
|
} from "@/registry/new-york/ui/avatar"
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
HoverCard,
|
||||||
|
HoverCardContent,
|
||||||
|
HoverCardTrigger,
|
||||||
|
} from "@/registry/new-york/ui/hover-card"
|
||||||
|
|
||||||
|
export default function HoverCardDemo() {
|
||||||
|
return (
|
||||||
|
<HoverCard>
|
||||||
|
<HoverCardTrigger asChild>
|
||||||
|
<Button variant="link">@nextjs</Button>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent className="w-80">
|
||||||
|
<div className="flex justify-between space-x-4">
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage src="https://github.com/vercel.png" />
|
||||||
|
<AvatarFallback>VC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<h4 className="text-sm font-semibold">@nextjs</h4>
|
||||||
|
<p className="text-sm">
|
||||||
|
The React Framework – created and maintained by @vercel.
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center pt-2">
|
||||||
|
<CalendarIcon className="mr-2 h-4 w-4 opacity-70" />{" "}
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Joined December 2021
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
apps/www/__registry__/new-york/component/input-demo.tsx
Normal file
5
apps/www/__registry__/new-york/component/input-demo.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
|
||||||
|
export default function InputDemo() {
|
||||||
|
return <Input type="email" placeholder="Email" />
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
|
||||||
|
export default function InputDisabled() {
|
||||||
|
return <Input disabled type="email" placeholder="Email" />
|
||||||
|
}
|
||||||
11
apps/www/__registry__/new-york/component/input-file.tsx
Normal file
11
apps/www/__registry__/new-york/component/input-file.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function InputFile() {
|
||||||
|
return (
|
||||||
|
<div className="grid w-full max-w-sm items-center gap-1.5">
|
||||||
|
<Label htmlFor="picture">Picture</Label>
|
||||||
|
<Input id="picture" type="file" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
68
apps/www/__registry__/new-york/component/input-form.tsx
Normal file
68
apps/www/__registry__/new-york/component/input-form.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
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 { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
username: z.string().min(2, {
|
||||||
|
message: "Username must be at least 2 characters.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function InputForm() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
username: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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="w-2/3 space-y-6">
|
||||||
|
<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.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
InputOTP,
|
||||||
|
InputOTPGroup,
|
||||||
|
InputOTPSlot,
|
||||||
|
} from "@/registry/new-york/ui/input-otp"
|
||||||
|
|
||||||
|
export default function InputOTPControlled() {
|
||||||
|
const [value, setValue] = React.useState("")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<InputOTP
|
||||||
|
maxLength={6}
|
||||||
|
value={value}
|
||||||
|
onChange={(value) => setValue(value)}
|
||||||
|
>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
|
<div className="text-center text-sm">
|
||||||
|
{value === "" ? (
|
||||||
|
<>Enter your one-time password.</>
|
||||||
|
) : (
|
||||||
|
<>You entered: {value}</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
24
apps/www/__registry__/new-york/component/input-otp-demo.tsx
Normal file
24
apps/www/__registry__/new-york/component/input-otp-demo.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
InputOTP,
|
||||||
|
InputOTPGroup,
|
||||||
|
InputOTPSeparator,
|
||||||
|
InputOTPSlot,
|
||||||
|
} from "@/registry/new-york/ui/input-otp"
|
||||||
|
|
||||||
|
export default function InputOTPDemo() {
|
||||||
|
return (
|
||||||
|
<InputOTP maxLength={6}>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
<InputOTPSeparator />
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
|
)
|
||||||
|
}
|
||||||
82
apps/www/__registry__/new-york/component/input-otp-form.tsx
Normal file
82
apps/www/__registry__/new-york/component/input-otp-form.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useForm } from "react-hook-form"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/registry/new-york/ui/form"
|
||||||
|
import {
|
||||||
|
InputOTP,
|
||||||
|
InputOTPGroup,
|
||||||
|
InputOTPSlot,
|
||||||
|
} from "@/registry/new-york/ui/input-otp"
|
||||||
|
import { toast } from "@/registry/new-york/ui/use-toast"
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
pin: z.string().min(6, {
|
||||||
|
message: "Your one-time password must be 6 characters.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default function InputOTPForm() {
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: {
|
||||||
|
pin: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
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="w-2/3 space-y-6">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="pin"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>One-Time Password</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<InputOTP maxLength={6} {...field}>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>
|
||||||
|
Please enter the one-time password sent to your phone.
|
||||||
|
</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
|
||||||
|
|
||||||
|
import {
|
||||||
|
InputOTP,
|
||||||
|
InputOTPGroup,
|
||||||
|
InputOTPSlot,
|
||||||
|
} from "@/registry/new-york/ui/input-otp"
|
||||||
|
|
||||||
|
export default function InputOTPPattern() {
|
||||||
|
return (
|
||||||
|
<InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import React from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
InputOTP,
|
||||||
|
InputOTPGroup,
|
||||||
|
InputOTPSeparator,
|
||||||
|
InputOTPSlot,
|
||||||
|
} from "@/registry/new-york/ui/input-otp"
|
||||||
|
|
||||||
|
export default function InputOTPWithSeparator() {
|
||||||
|
return (
|
||||||
|
<InputOTP maxLength={6}>
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={0} />
|
||||||
|
<InputOTPSlot index={1} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
<InputOTPSeparator />
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={2} />
|
||||||
|
<InputOTPSlot index={3} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
<InputOTPSeparator />
|
||||||
|
<InputOTPGroup>
|
||||||
|
<InputOTPSlot index={4} />
|
||||||
|
<InputOTPSlot index={5} />
|
||||||
|
</InputOTPGroup>
|
||||||
|
</InputOTP>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
|
||||||
|
export default function InputWithButton() {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full max-w-sm items-center space-x-2">
|
||||||
|
<Input type="email" placeholder="Email" />
|
||||||
|
<Button type="submit">Subscribe</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function InputWithLabel() {
|
||||||
|
return (
|
||||||
|
<div className="grid w-full max-w-sm items-center gap-1.5">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input type="email" id="email" placeholder="Email" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
12
apps/www/__registry__/new-york/component/input-with-text.tsx
Normal file
12
apps/www/__registry__/new-york/component/input-with-text.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Input } from "@/registry/new-york/ui/input"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function InputWithText() {
|
||||||
|
return (
|
||||||
|
<div className="grid w-full max-w-sm items-center gap-1.5">
|
||||||
|
<Label htmlFor="email-2">Email</Label>
|
||||||
|
<Input type="email" id="email-2" placeholder="Email" />
|
||||||
|
<p className="text-sm text-muted-foreground">Enter your email address.</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
13
apps/www/__registry__/new-york/component/label-demo.tsx
Normal file
13
apps/www/__registry__/new-york/component/label-demo.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Checkbox } from "@/registry/new-york/ui/checkbox"
|
||||||
|
import { Label } from "@/registry/new-york/ui/label"
|
||||||
|
|
||||||
|
export default function LabelDemo() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Checkbox id="terms" />
|
||||||
|
<Label htmlFor="terms">Accept terms and conditions</Label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
107
apps/www/__registry__/new-york/component/menubar-demo.tsx
Normal file
107
apps/www/__registry__/new-york/component/menubar-demo.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
Menubar,
|
||||||
|
MenubarCheckboxItem,
|
||||||
|
MenubarContent,
|
||||||
|
MenubarItem,
|
||||||
|
MenubarMenu,
|
||||||
|
MenubarRadioGroup,
|
||||||
|
MenubarRadioItem,
|
||||||
|
MenubarSeparator,
|
||||||
|
MenubarShortcut,
|
||||||
|
MenubarSub,
|
||||||
|
MenubarSubContent,
|
||||||
|
MenubarSubTrigger,
|
||||||
|
MenubarTrigger,
|
||||||
|
} from "@/registry/new-york/ui/menubar"
|
||||||
|
|
||||||
|
export default function MenubarDemo() {
|
||||||
|
return (
|
||||||
|
<Menubar>
|
||||||
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>File</MenubarTrigger>
|
||||||
|
<MenubarContent>
|
||||||
|
<MenubarItem>
|
||||||
|
New Tab <MenubarShortcut>⌘T</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarItem>
|
||||||
|
New Window <MenubarShortcut>⌘N</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarItem disabled>New Incognito Window</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarSub>
|
||||||
|
<MenubarSubTrigger>Share</MenubarSubTrigger>
|
||||||
|
<MenubarSubContent>
|
||||||
|
<MenubarItem>Email link</MenubarItem>
|
||||||
|
<MenubarItem>Messages</MenubarItem>
|
||||||
|
<MenubarItem>Notes</MenubarItem>
|
||||||
|
</MenubarSubContent>
|
||||||
|
</MenubarSub>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem>
|
||||||
|
Print... <MenubarShortcut>⌘P</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>Edit</MenubarTrigger>
|
||||||
|
<MenubarContent>
|
||||||
|
<MenubarItem>
|
||||||
|
Undo <MenubarShortcut>⌘Z</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarItem>
|
||||||
|
Redo <MenubarShortcut>⇧⌘Z</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarSub>
|
||||||
|
<MenubarSubTrigger>Find</MenubarSubTrigger>
|
||||||
|
<MenubarSubContent>
|
||||||
|
<MenubarItem>Search the web</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem>Find...</MenubarItem>
|
||||||
|
<MenubarItem>Find Next</MenubarItem>
|
||||||
|
<MenubarItem>Find Previous</MenubarItem>
|
||||||
|
</MenubarSubContent>
|
||||||
|
</MenubarSub>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem>Cut</MenubarItem>
|
||||||
|
<MenubarItem>Copy</MenubarItem>
|
||||||
|
<MenubarItem>Paste</MenubarItem>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>View</MenubarTrigger>
|
||||||
|
<MenubarContent>
|
||||||
|
<MenubarCheckboxItem>Always Show Bookmarks Bar</MenubarCheckboxItem>
|
||||||
|
<MenubarCheckboxItem checked>
|
||||||
|
Always Show Full URLs
|
||||||
|
</MenubarCheckboxItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem inset>
|
||||||
|
Reload <MenubarShortcut>⌘R</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarItem disabled inset>
|
||||||
|
Force Reload <MenubarShortcut>⇧⌘R</MenubarShortcut>
|
||||||
|
</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem inset>Toggle Fullscreen</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem inset>Hide Sidebar</MenubarItem>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
<MenubarMenu>
|
||||||
|
<MenubarTrigger>Profiles</MenubarTrigger>
|
||||||
|
<MenubarContent>
|
||||||
|
<MenubarRadioGroup value="benoit">
|
||||||
|
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
|
||||||
|
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
|
||||||
|
</MenubarRadioGroup>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem inset>Edit...</MenubarItem>
|
||||||
|
<MenubarSeparator />
|
||||||
|
<MenubarItem inset>Add Profile...</MenubarItem>
|
||||||
|
</MenubarContent>
|
||||||
|
</MenubarMenu>
|
||||||
|
</Menubar>
|
||||||
|
)
|
||||||
|
}
|
||||||
40
apps/www/__registry__/new-york/component/mode-toggle.tsx
Normal file
40
apps/www/__registry__/new-york/component/mode-toggle.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import { MoonIcon, SunIcon } from "@radix-ui/react-icons"
|
||||||
|
import { useTheme } from "next-themes"
|
||||||
|
|
||||||
|
import { Button } from "@/registry/new-york/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/registry/new-york/ui/dropdown-menu"
|
||||||
|
|
||||||
|
export default function ModeToggle() {
|
||||||
|
const { setTheme } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<SunIcon className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||||
|
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||||
|
<span className="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||||
|
Light
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||||
|
Dark
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||||
|
System
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Icons } from "@/components/icons"
|
||||||
|
import {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
navigationMenuTriggerStyle,
|
||||||
|
} from "@/registry/new-york/ui/navigation-menu"
|
||||||
|
|
||||||
|
const components: { title: string; href: string; description: string }[] = [
|
||||||
|
{
|
||||||
|
title: "Alert Dialog",
|
||||||
|
href: "/docs/primitives/alert-dialog",
|
||||||
|
description:
|
||||||
|
"A modal dialog that interrupts the user with important content and expects a response.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Hover Card",
|
||||||
|
href: "/docs/primitives/hover-card",
|
||||||
|
description:
|
||||||
|
"For sighted users to preview content available behind a link.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Progress",
|
||||||
|
href: "/docs/primitives/progress",
|
||||||
|
description:
|
||||||
|
"Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Scroll-area",
|
||||||
|
href: "/docs/primitives/scroll-area",
|
||||||
|
description: "Visually or semantically separates content.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tabs",
|
||||||
|
href: "/docs/primitives/tabs",
|
||||||
|
description:
|
||||||
|
"A set of layered sections of content—known as tab panels—that are displayed one at a time.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tooltip",
|
||||||
|
href: "/docs/primitives/tooltip",
|
||||||
|
description:
|
||||||
|
"A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function NavigationMenuDemo() {
|
||||||
|
return (
|
||||||
|
<NavigationMenu>
|
||||||
|
<NavigationMenuList>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
|
||||||
|
<NavigationMenuContent>
|
||||||
|
<ul className="grid gap-3 p-4 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||||
|
<li className="row-span-3">
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<a
|
||||||
|
className="flex h-full w-full select-none flex-col justify-end rounded-md bg-gradient-to-b from-muted/50 to-muted p-6 no-underline outline-none focus:shadow-md"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
<Icons.logo className="h-6 w-6" />
|
||||||
|
<div className="mb-2 mt-4 text-lg font-medium">
|
||||||
|
shadcn/ui
|
||||||
|
</div>
|
||||||
|
<p className="text-sm leading-tight text-muted-foreground">
|
||||||
|
Beautifully designed components built with Radix UI and
|
||||||
|
Tailwind CSS.
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
<ListItem href="/docs" title="Introduction">
|
||||||
|
Re-usable components built using Radix UI and Tailwind CSS.
|
||||||
|
</ListItem>
|
||||||
|
<ListItem href="/docs/installation" title="Installation">
|
||||||
|
How to install dependencies and structure your app.
|
||||||
|
</ListItem>
|
||||||
|
<ListItem href="/docs/primitives/typography" title="Typography">
|
||||||
|
Styles for headings, paragraphs, lists...etc
|
||||||
|
</ListItem>
|
||||||
|
</ul>
|
||||||
|
</NavigationMenuContent>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
|
||||||
|
<NavigationMenuContent>
|
||||||
|
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||||
|
{components.map((component) => (
|
||||||
|
<ListItem
|
||||||
|
key={component.title}
|
||||||
|
title={component.title}
|
||||||
|
href={component.href}
|
||||||
|
>
|
||||||
|
{component.description}
|
||||||
|
</ListItem>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</NavigationMenuContent>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<Link href="/docs" legacyBehavior passHref>
|
||||||
|
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||||
|
Documentation
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</Link>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
</NavigationMenuList>
|
||||||
|
</NavigationMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListItem = React.forwardRef<
|
||||||
|
React.ElementRef<"a">,
|
||||||
|
React.ComponentPropsWithoutRef<"a">
|
||||||
|
>(({ className, title, children, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<NavigationMenuLink asChild>
|
||||||
|
<a
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="text-sm font-medium leading-none">{title}</div>
|
||||||
|
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
ListItem.displayName = "ListItem"
|
||||||
38
apps/www/__registry__/new-york/component/pagination-demo.tsx
Normal file
38
apps/www/__registry__/new-york/component/pagination-demo.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
PaginationContent,
|
||||||
|
PaginationEllipsis,
|
||||||
|
PaginationItem,
|
||||||
|
PaginationLink,
|
||||||
|
PaginationNext,
|
||||||
|
PaginationPrevious,
|
||||||
|
} from "@/registry/new-york/ui/pagination"
|
||||||
|
|
||||||
|
export default function PaginationDemo() {
|
||||||
|
return (
|
||||||
|
<Pagination>
|
||||||
|
<PaginationContent>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationPrevious href="#" />
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationLink href="#">1</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationLink href="#" isActive>
|
||||||
|
2
|
||||||
|
</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationLink href="#">3</PaginationLink>
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationEllipsis />
|
||||||
|
</PaginationItem>
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationNext href="#" />
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user