mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-30 16:14:13 +00:00
fix
This commit is contained in:
@@ -3,7 +3,7 @@ import path from "node:path"
|
||||
import * as React from "react"
|
||||
|
||||
import { highlightCode } from "@/lib/highlight-code"
|
||||
import { getRegistryItem } from "@/lib/registry"
|
||||
import { getDemoItem, getRegistryItem } from "@/lib/registry"
|
||||
import { transformForDisplay } from "@/lib/rehype"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { CodeCollapsibleWrapper } from "@/components/code-collapsible-wrapper"
|
||||
@@ -33,7 +33,10 @@ export async function ComponentSource({
|
||||
let code: string | undefined
|
||||
|
||||
if (name) {
|
||||
const item = await getRegistryItem(name, styleName)
|
||||
// Try demo item first, then fall back to registry item.
|
||||
const item =
|
||||
(await getDemoItem(name, styleName)) ??
|
||||
(await getRegistryItem(name, styleName))
|
||||
code = item?.files?.[0]?.content
|
||||
}
|
||||
|
||||
|
||||
53
apps/v4/examples/README.md
Normal file
53
apps/v4/examples/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Examples
|
||||
|
||||
This directory contains demo components for the component documentation.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
examples
|
||||
├── base
|
||||
│ ├── ui # Auto-generated by build-registry.mts
|
||||
│ ├── lib # Auto-generated by build-registry.mts
|
||||
│ ├── hooks # Auto-generated by build-registry.mts
|
||||
│ ├── button-demo.tsx
|
||||
│ └── ...
|
||||
├── radix
|
||||
│ ├── ui # Auto-generated by build-registry.mts
|
||||
│ ├── lib # Auto-generated by build-registry.mts
|
||||
│ ├── hooks # Auto-generated by build-registry.mts
|
||||
│ └── ...
|
||||
└── __index__.tsx # Auto-generated by build-registry.mts
|
||||
```
|
||||
|
||||
## Adding a New Example
|
||||
|
||||
1. Create a new `.tsx` file in `examples/base` or `examples/radix`:
|
||||
|
||||
```tsx
|
||||
// examples/base/button-loading.tsx
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export function ButtonLoading() {
|
||||
return <Button disabled>Loading...</Button>
|
||||
}
|
||||
```
|
||||
|
||||
2. Run the examples build to regenerate the index:
|
||||
|
||||
```bash
|
||||
pnpm examples:build
|
||||
```
|
||||
|
||||
3. Use the example in documentation by referencing its name (filename without `.tsx`):
|
||||
|
||||
```tsx
|
||||
<ComponentPreview name="button-loading" />
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The `ui`, `lib`, and `hooks` directories are auto-generated during `pnpm registry:build`. Do not edit files in these directories directly.
|
||||
- Example files should be placed directly in `examples/base` or `examples/radix`, not in subdirectories.
|
||||
- Both named exports and default exports are supported.
|
||||
- After adding or removing examples, run `pnpm examples:build` to update the index.
|
||||
3809
apps/v4/examples/__index__.tsx
Normal file
3809
apps/v4/examples/__index__.tsx
Normal file
File diff suppressed because it is too large
Load Diff
46
apps/v4/examples/base/accordion-borders.tsx
Normal file
46
apps/v4/examples/base/accordion-borders.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/examples/base/ui/accordion"
|
||||
|
||||
export default function AccordionBorders() {
|
||||
return (
|
||||
<Accordion className="w-full">
|
||||
<AccordionItem
|
||||
value="billing"
|
||||
className="border px-4 first:rounded-t-lg last:rounded-b-lg"
|
||||
>
|
||||
<AccordionTrigger>How does billing work?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
We offer monthly and annual subscription plans. Billing is charged at
|
||||
the beginning of each cycle, and you can cancel anytime. All plans
|
||||
include automatic backups, 24/7 support, and unlimited team members.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
value="security"
|
||||
className="border-x border-b px-4 last:rounded-b-lg"
|
||||
>
|
||||
<AccordionTrigger>Is my data secure?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Yes. We use end-to-end encryption, SOC 2 Type II compliance, and
|
||||
regular third-party security audits. All data is encrypted at rest and
|
||||
in transit using industry-standard protocols.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem
|
||||
value="integration"
|
||||
className="border-x border-b px-4 last:rounded-b-lg"
|
||||
>
|
||||
<AccordionTrigger>What integrations do you support?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
We integrate with 500+ popular tools including Slack, Zapier,
|
||||
Salesforce, HubSpot, and more. You can also build custom integrations
|
||||
using our REST API and webhooks.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
60
apps/v4/examples/base/accordion-card.tsx
Normal file
60
apps/v4/examples/base/accordion-card.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/examples/base/ui/accordion"
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/examples/base/ui/card"
|
||||
|
||||
export default function AccordionCard() {
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Subscription & Billing</CardTitle>
|
||||
<CardDescription>
|
||||
Common questions about your account, plans, and payments
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Accordion defaultValue={["plans"]}>
|
||||
<AccordionItem value="plans">
|
||||
<AccordionTrigger>
|
||||
What subscription plans do you offer?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
We offer three subscription tiers: Starter ($9/month),
|
||||
Professional ($29/month), and Enterprise ($99/month). Each plan
|
||||
includes increasing storage limits, API access, priority support,
|
||||
and team collaboration features.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="billing">
|
||||
<AccordionTrigger>How does billing work?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Billing occurs automatically at the start of each billing cycle.
|
||||
We accept all major credit cards, PayPal, and ACH transfers for
|
||||
enterprise customers. You'll receive an invoice via email
|
||||
after each payment.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="cancel">
|
||||
<AccordionTrigger>
|
||||
How do I cancel my subscription?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
You can cancel your subscription anytime from your account
|
||||
settings. There are no cancellation fees or penalties. Your access
|
||||
will continue until the end of your current billing period.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
33
apps/v4/examples/base/accordion-demo.tsx
Normal file
33
apps/v4/examples/base/accordion-demo.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/examples/base/ui/accordion"
|
||||
|
||||
export default function AccordionDemo() {
|
||||
return (
|
||||
<Accordion className="w-full" defaultValue={["item-1"]}>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/accordion-disabled.tsx
Normal file
36
apps/v4/examples/base/accordion-disabled.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/examples/base/ui/accordion"
|
||||
|
||||
export default function AccordionDisabled() {
|
||||
return (
|
||||
<Accordion className="w-full">
|
||||
<AccordionItem value="item-1">
|
||||
<AccordionTrigger>Can I access my account history?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Yes, you can view your complete account history including all
|
||||
transactions, plan changes, and support tickets in the Account History
|
||||
section of your dashboard.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-2" disabled>
|
||||
<AccordionTrigger>Premium feature information</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
This section contains information about premium features. Upgrade your
|
||||
plan to access this content.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-3">
|
||||
<AccordionTrigger>How do I update my email address?</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
You can update your email address in your account settings.
|
||||
You'll receive a verification email at your new address to
|
||||
confirm the change.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
46
apps/v4/examples/base/accordion-multiple.tsx
Normal file
46
apps/v4/examples/base/accordion-multiple.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/examples/base/ui/accordion"
|
||||
|
||||
export default function AccordionMultiple() {
|
||||
return (
|
||||
<Accordion multiple className="w-full">
|
||||
<AccordionItem value="item-1">
|
||||
<AccordionTrigger>
|
||||
What are the key considerations when implementing a comprehensive
|
||||
enterprise-level authentication system?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Implementing a robust enterprise authentication system requires
|
||||
careful consideration of multiple factors. This includes secure
|
||||
password hashing and storage, multi-factor authentication (MFA)
|
||||
implementation, session management, OAuth2 and SSO integration,
|
||||
regular security audits, rate limiting to prevent brute force attacks,
|
||||
and maintaining detailed audit logs. Additionally, you'll need to
|
||||
consider scalability, performance impact, and compliance with relevant
|
||||
data protection regulations such as GDPR or HIPAA.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
<AccordionItem value="item-2">
|
||||
<AccordionTrigger>
|
||||
How does modern distributed system architecture handle eventual
|
||||
consistency and data synchronization across multiple regions?
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
Modern distributed systems employ various strategies to maintain data
|
||||
consistency across regions. This often involves using techniques like
|
||||
CRDT (Conflict-Free Replicated Data Types), vector clocks, and gossip
|
||||
protocols. Systems might implement event sourcing patterns, utilize
|
||||
message queues for asynchronous updates, and employ sophisticated
|
||||
conflict resolution strategies. Popular solutions like Amazon's
|
||||
DynamoDB and Google's Spanner demonstrate different approaches to
|
||||
solving these challenges, balancing between consistency, availability,
|
||||
and partition tolerance as described in the CAP theorem.
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
34
apps/v4/examples/base/alert-demo.tsx
Normal file
34
apps/v4/examples/base/alert-demo.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/examples/base/ui/alert"
|
||||
import { AlertCircleIcon, CheckCircle2Icon, PopcornIcon } from "lucide-react"
|
||||
|
||||
export default function AlertDemo() {
|
||||
return (
|
||||
<div className="grid w-full max-w-xl items-start gap-4">
|
||||
<Alert>
|
||||
<CheckCircle2Icon />
|
||||
<AlertTitle>Success! Your changes have been saved</AlertTitle>
|
||||
<AlertDescription>
|
||||
This is an alert with icon, title and description.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<Alert>
|
||||
<PopcornIcon />
|
||||
<AlertTitle>
|
||||
This Alert has a title and an icon. No description.
|
||||
</AlertTitle>
|
||||
</Alert>
|
||||
<Alert variant="destructive">
|
||||
<AlertCircleIcon />
|
||||
<AlertTitle>Unable to process your payment.</AlertTitle>
|
||||
<AlertDescription>
|
||||
<p>Please verify your billing information and try again.</p>
|
||||
<ul className="list-inside list-disc text-sm">
|
||||
<li>Check your card details</li>
|
||||
<li>Ensure sufficient funds</li>
|
||||
<li>Verify billing address</li>
|
||||
</ul>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
apps/v4/examples/base/alert-destructive.tsx
Normal file
14
apps/v4/examples/base/alert-destructive.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/examples/base/ui/alert"
|
||||
import { AlertCircleIcon } from "lucide-react"
|
||||
|
||||
export default function AlertDestructive() {
|
||||
return (
|
||||
<Alert variant="destructive">
|
||||
<AlertCircleIcon />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>
|
||||
Your session has expired. Please log in again.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
35
apps/v4/examples/base/alert-dialog-demo.tsx
Normal file
35
apps/v4/examples/base/alert-dialog-demo.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/examples/base/ui/alert-dialog"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function AlertDialogDemo() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger render={<Button variant="outline" />}>
|
||||
Show Dialog
|
||||
</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>
|
||||
)
|
||||
}
|
||||
15
apps/v4/examples/base/aspect-ratio-demo.tsx
Normal file
15
apps/v4/examples/base/aspect-ratio-demo.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Image from "next/image"
|
||||
import { AspectRatio } from "@/examples/base/ui/aspect-ratio"
|
||||
|
||||
export default function AspectRatioDemo() {
|
||||
return (
|
||||
<AspectRatio ratio={16 / 9} className="bg-muted rounded-lg">
|
||||
<Image
|
||||
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
|
||||
alt="Photo by Drew Beamer"
|
||||
fill
|
||||
className="h-full w-full rounded-lg object-cover dark:brightness-[0.2] dark:grayscale"
|
||||
/>
|
||||
</AspectRatio>
|
||||
)
|
||||
}
|
||||
39
apps/v4/examples/base/avatar-demo.tsx
Normal file
39
apps/v4/examples/base/avatar-demo.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/examples/base/ui/avatar"
|
||||
|
||||
export default function AvatarDemo() {
|
||||
return (
|
||||
<div className="flex flex-row flex-wrap items-center gap-12">
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar className="rounded-lg">
|
||||
<AvatarImage
|
||||
src="https://github.com/evilrabbit.png"
|
||||
alt="@evilrabbit"
|
||||
/>
|
||||
<AvatarFallback>ER</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/maxleiter.png"
|
||||
alt="@maxleiter"
|
||||
/>
|
||||
<AvatarFallback>LR</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/evilrabbit.png"
|
||||
alt="@evilrabbit"
|
||||
/>
|
||||
<AvatarFallback>ER</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
39
apps/v4/examples/base/badge-demo.tsx
Normal file
39
apps/v4/examples/base/badge-demo.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Badge } from "@/examples/base/ui/badge"
|
||||
import { BadgeCheckIcon } from "lucide-react"
|
||||
|
||||
export default function BadgeDemo() {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="flex w-full flex-wrap gap-2">
|
||||
<Badge>Badge</Badge>
|
||||
<Badge variant="secondary">Secondary</Badge>
|
||||
<Badge variant="destructive">Destructive</Badge>
|
||||
<Badge variant="outline">Outline</Badge>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap gap-2">
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-blue-500 text-white dark:bg-blue-600"
|
||||
>
|
||||
<BadgeCheckIcon />
|
||||
Verified
|
||||
</Badge>
|
||||
<Badge className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums">
|
||||
8
|
||||
</Badge>
|
||||
<Badge
|
||||
className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums"
|
||||
variant="destructive"
|
||||
>
|
||||
99
|
||||
</Badge>
|
||||
<Badge
|
||||
className="h-5 min-w-5 rounded-full px-1 font-mono tabular-nums"
|
||||
variant="outline"
|
||||
>
|
||||
20+
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
5
apps/v4/examples/base/badge-destructive.tsx
Normal file
5
apps/v4/examples/base/badge-destructive.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/examples/base/ui/badge"
|
||||
|
||||
export default function BadgeDestructive() {
|
||||
return <Badge variant="destructive">Destructive</Badge>
|
||||
}
|
||||
5
apps/v4/examples/base/badge-outline.tsx
Normal file
5
apps/v4/examples/base/badge-outline.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/examples/base/ui/badge"
|
||||
|
||||
export default function BadgeOutline() {
|
||||
return <Badge variant="outline">Outline</Badge>
|
||||
}
|
||||
5
apps/v4/examples/base/badge-secondary.tsx
Normal file
5
apps/v4/examples/base/badge-secondary.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Badge } from "@/examples/base/ui/badge"
|
||||
|
||||
export default function BadgeSecondary() {
|
||||
return <Badge variant="secondary">Secondary</Badge>
|
||||
}
|
||||
55
apps/v4/examples/base/breadcrumb-demo.tsx
Normal file
55
apps/v4/examples/base/breadcrumb-demo.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
|
||||
export function BreadcrumbDemo() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/" />}>Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<BreadcrumbEllipsis className="size-4" />
|
||||
<span className="sr-only">Toggle menu</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>Documentation</DropdownMenuItem>
|
||||
<DropdownMenuItem>Themes</DropdownMenuItem>
|
||||
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/docs/components" />}>
|
||||
Components
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
53
apps/v4/examples/base/breadcrumb-dropdown.tsx
Normal file
53
apps/v4/examples/base/breadcrumb-dropdown.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import { ChevronDownIcon, SlashIcon } from "lucide-react"
|
||||
|
||||
export function BreadcrumbDropdown() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/" />}>Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>
|
||||
<SlashIcon />
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5">
|
||||
Components
|
||||
<ChevronDownIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>Documentation</DropdownMenuItem>
|
||||
<DropdownMenuItem>Themes</DropdownMenuItem>
|
||||
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>
|
||||
<SlashIcon />
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/breadcrumb-ellipsis.tsx
Normal file
36
apps/v4/examples/base/breadcrumb-ellipsis.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
|
||||
export function BreadcrumbEllipsisDemo() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/" />}>Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbEllipsis />
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/docs/components" />}>
|
||||
Components
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
31
apps/v4/examples/base/breadcrumb-link.tsx
Normal file
31
apps/v4/examples/base/breadcrumb-link.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
|
||||
export function BreadcrumbLinkDemo() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/" />}>Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/components" />}>
|
||||
Components
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
139
apps/v4/examples/base/breadcrumb-responsive.tsx
Normal file
139
apps/v4/examples/base/breadcrumb-responsive.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbEllipsis,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/examples/base/ui/drawer"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
|
||||
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||
|
||||
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 function BreadcrumbResponsive() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link 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="size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuGroup>
|
||||
{items.slice(1, -2).map((item, index) => (
|
||||
<DropdownMenuItem key={index}>
|
||||
<Link href={item.href ? item.href : "#"}>
|
||||
{item.label}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuGroup>
|
||||
</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>
|
||||
Close
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
</>
|
||||
) : null}
|
||||
{items.slice(-ITEMS_TO_DISPLAY + 1).map((item, index) => (
|
||||
<BreadcrumbItem key={index}>
|
||||
{item.href ? (
|
||||
<>
|
||||
<BreadcrumbLink
|
||||
render={<Link href={item.href} />}
|
||||
className="max-w-20 truncate md:max-w-none"
|
||||
>
|
||||
{item.label}
|
||||
</BreadcrumbLink>
|
||||
<BreadcrumbSeparator />
|
||||
</>
|
||||
) : (
|
||||
<BreadcrumbPage className="max-w-20 truncate md:max-w-none">
|
||||
{item.label}
|
||||
</BreadcrumbPage>
|
||||
)}
|
||||
</BreadcrumbItem>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/breadcrumb-separator.tsx
Normal file
36
apps/v4/examples/base/breadcrumb-separator.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import Link from "next/link"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/examples/base/ui/breadcrumb"
|
||||
import { SlashIcon } from "lucide-react"
|
||||
|
||||
export function BreadcrumbSeparatorDemo() {
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/" />}>Home</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>
|
||||
<SlashIcon />
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink render={<Link href="/components" />}>
|
||||
Components
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator>
|
||||
<SlashIcon />
|
||||
</BreadcrumbSeparator>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>Breadcrumb</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
)
|
||||
}
|
||||
6
apps/v4/examples/base/button-as-child.tsx
Normal file
6
apps/v4/examples/base/button-as-child.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import Link from "next/link"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonAsChild() {
|
||||
return <Button render={<Link href="/login" />}>Login</Button>
|
||||
}
|
||||
5
apps/v4/examples/base/button-default.tsx
Normal file
5
apps/v4/examples/base/button-default.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonDefault() {
|
||||
return <Button>Button</Button>
|
||||
}
|
||||
13
apps/v4/examples/base/button-demo.tsx
Normal file
13
apps/v4/examples/base/button-demo.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ArrowUpIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonDemo() {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center gap-2 md:flex-row">
|
||||
<Button variant="outline">Button</Button>
|
||||
<Button variant="outline" size="icon" aria-label="Submit">
|
||||
<ArrowUpIcon />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
5
apps/v4/examples/base/button-destructive.tsx
Normal file
5
apps/v4/examples/base/button-destructive.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonDestructive() {
|
||||
return <Button variant="destructive">Destructive</Button>
|
||||
}
|
||||
5
apps/v4/examples/base/button-ghost.tsx
Normal file
5
apps/v4/examples/base/button-ghost.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonGhost() {
|
||||
return <Button variant="ghost">Ghost</Button>
|
||||
}
|
||||
115
apps/v4/examples/base/button-group-demo.tsx
Normal file
115
apps/v4/examples/base/button-group-demo.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import {
|
||||
ArchiveIcon,
|
||||
ArrowLeftIcon,
|
||||
CalendarPlusIcon,
|
||||
ClockIcon,
|
||||
ListFilterIcon,
|
||||
MailCheckIcon,
|
||||
MoreHorizontalIcon,
|
||||
TagIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react"
|
||||
|
||||
export default function ButtonGroupDemo() {
|
||||
const [label, setLabel] = React.useState("personal")
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ButtonGroup className="hidden sm:flex">
|
||||
<Button variant="outline" size="icon" aria-label="Go Back">
|
||||
<ArrowLeftIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">Archive</Button>
|
||||
<Button variant="outline">Report</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">Snooze</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="outline" size="icon" aria-label="More Options" />
|
||||
}
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<MailCheckIcon />
|
||||
Mark as Read
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<ArchiveIcon />
|
||||
Archive
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<ClockIcon />
|
||||
Snooze
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CalendarPlusIcon />
|
||||
Add to Calendar
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<ListFilterIcon />
|
||||
Add to List
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<TagIcon />
|
||||
Label As...
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuRadioGroup
|
||||
value={label}
|
||||
onValueChange={setLabel}
|
||||
>
|
||||
<DropdownMenuRadioItem value="personal">
|
||||
Personal
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="work">
|
||||
Work
|
||||
</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="other">
|
||||
Other
|
||||
</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem variant="destructive">
|
||||
<Trash2Icon />
|
||||
Trash
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
72
apps/v4/examples/base/button-group-dropdown.tsx
Normal file
72
apps/v4/examples/base/button-group-dropdown.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
CheckIcon,
|
||||
ChevronDownIcon,
|
||||
CopyIcon,
|
||||
ShareIcon,
|
||||
TrashIcon,
|
||||
UserRoundXIcon,
|
||||
VolumeOffIcon,
|
||||
} from "lucide-react"
|
||||
|
||||
export default function ButtonGroupDropdown() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">Follow</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={<Button variant="outline" className="!pl-2" />}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="[--radius:1rem]">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
<VolumeOffIcon />
|
||||
Mute Conversation
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CheckIcon />
|
||||
Mark as Read
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<AlertTriangleIcon />
|
||||
Report Conversation
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<UserRoundXIcon />
|
||||
Block User
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<ShareIcon />
|
||||
Share Conversation
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<CopyIcon />
|
||||
Copy Conversation
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem variant="destructive">
|
||||
<TrashIcon />
|
||||
Delete Conversation
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
59
apps/v4/examples/base/button-group-input-group.tsx
Normal file
59
apps/v4/examples/base/button-group-input-group.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
} from "@/examples/base/ui/input-group"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/examples/base/ui/tooltip"
|
||||
import { AudioLinesIcon, PlusIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupInputGroup() {
|
||||
const [voiceEnabled, setVoiceEnabled] = React.useState(false)
|
||||
|
||||
return (
|
||||
<ButtonGroup className="[--radius:9999rem]">
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" size="icon">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<InputGroup>
|
||||
<InputGroupInput
|
||||
placeholder={
|
||||
voiceEnabled ? "Record and send audio..." : "Send a message..."
|
||||
}
|
||||
disabled={voiceEnabled}
|
||||
/>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
render={
|
||||
<InputGroupButton
|
||||
onClick={() => setVoiceEnabled(!voiceEnabled)}
|
||||
size="icon-xs"
|
||||
data-active={voiceEnabled}
|
||||
className="data-[active=true]:bg-orange-100 data-[active=true]:text-orange-700 dark:data-[active=true]:bg-orange-800 dark:data-[active=true]:text-orange-100"
|
||||
aria-pressed={voiceEnabled}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AudioLinesIcon />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Voice Mode</TooltipContent>
|
||||
</Tooltip>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
15
apps/v4/examples/base/button-group-input.tsx
Normal file
15
apps/v4/examples/base/button-group-input.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { SearchIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupInput() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Input placeholder="Search..." />
|
||||
<Button variant="outline" aria-label="Search">
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
37
apps/v4/examples/base/button-group-nested.tsx
Normal file
37
apps/v4/examples/base/button-group-nested.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupNested() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" size="sm">
|
||||
1
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
2
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
3
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
4
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
5
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" size="icon-sm" aria-label="Previous">
|
||||
<ArrowLeftIcon />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon-sm" aria-label="Next">
|
||||
<ArrowRightIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
20
apps/v4/examples/base/button-group-orientation.tsx
Normal file
20
apps/v4/examples/base/button-group-orientation.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import { MinusIcon, PlusIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupOrientation() {
|
||||
return (
|
||||
<ButtonGroup
|
||||
orientation="vertical"
|
||||
aria-label="Media controls"
|
||||
className="h-fit"
|
||||
>
|
||||
<Button variant="outline" size="icon">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon">
|
||||
<MinusIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
46
apps/v4/examples/base/button-group-popover.tsx
Normal file
46
apps/v4/examples/base/button-group-popover.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
import { Separator } from "@/examples/base/ui/separator"
|
||||
import { Textarea } from "@/examples/base/ui/textarea"
|
||||
import { BotIcon, ChevronDownIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupPopover() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">
|
||||
<BotIcon /> Copilot
|
||||
</Button>
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button variant="outline" size="icon" aria-label="Open Popover" />
|
||||
}
|
||||
>
|
||||
<ChevronDownIcon />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="rounded-xl p-0 text-sm">
|
||||
<div className="px-4 py-3">
|
||||
<div className="text-sm font-medium">Agent Tasks</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="p-4 text-sm *:[p:not(:last-child)]:mb-2">
|
||||
<Textarea
|
||||
placeholder="Describe your task in natural language."
|
||||
className="mb-4 resize-none"
|
||||
/>
|
||||
<p className="font-medium">Start a new task with Copilot</p>
|
||||
<p className="text-muted-foreground">
|
||||
Describe your task in natural language. Copilot will work in the
|
||||
background and open a pull request for your review.
|
||||
</p>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
54
apps/v4/examples/base/button-group-select.tsx
Normal file
54
apps/v4/examples/base/button-group-select.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "@/examples/base/ui/select"
|
||||
import { ArrowRightIcon } from "lucide-react"
|
||||
|
||||
const CURRENCIES = [
|
||||
{ label: "US Dollar", value: "$" },
|
||||
{ label: "Euro", value: "€" },
|
||||
{ label: "British Pound", value: "£" },
|
||||
]
|
||||
|
||||
export default function ButtonGroupSelect() {
|
||||
const [currency, setCurrency] = React.useState("$")
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Select
|
||||
items={CURRENCIES}
|
||||
value={currency}
|
||||
onValueChange={(value) => setCurrency(value as string)}
|
||||
>
|
||||
<SelectTrigger className="font-mono">{currency}</SelectTrigger>
|
||||
<SelectContent className="min-w-24">
|
||||
<SelectGroup>
|
||||
{CURRENCIES.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.value}{" "}
|
||||
<span className="text-muted-foreground">{item.label}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Input placeholder="10.00" pattern="[0-9]*" />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button aria-label="Send" size="icon" variant="outline">
|
||||
<ArrowRightIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
19
apps/v4/examples/base/button-group-separator.tsx
Normal file
19
apps/v4/examples/base/button-group-separator.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
ButtonGroup,
|
||||
ButtonGroupSeparator,
|
||||
} from "@/examples/base/ui/button-group"
|
||||
|
||||
export default function ButtonGroupSeparatorDemo() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button variant="secondary" size="sm">
|
||||
Copy
|
||||
</Button>
|
||||
<ButtonGroupSeparator />
|
||||
<Button variant="secondary" size="sm">
|
||||
Paste
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
46
apps/v4/examples/base/button-group-size.tsx
Normal file
46
apps/v4/examples/base/button-group-size.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ButtonGroup } from "@/examples/base/ui/button-group"
|
||||
import { PlusIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonGroupSize() {
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-8">
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" size="sm">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="outline" size="sm">
|
||||
Group
|
||||
</Button>
|
||||
<Button variant="outline" size="icon-sm">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">Default</Button>
|
||||
<Button variant="outline">Button</Button>
|
||||
<Button variant="outline">Group</Button>
|
||||
<Button variant="outline" size="icon">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup>
|
||||
<Button variant="outline" size="lg">
|
||||
Large
|
||||
</Button>
|
||||
<Button variant="outline" size="lg">
|
||||
Button
|
||||
</Button>
|
||||
<Button variant="outline" size="lg">
|
||||
Group
|
||||
</Button>
|
||||
<Button variant="outline" size="icon-lg">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
18
apps/v4/examples/base/button-group-split.tsx
Normal file
18
apps/v4/examples/base/button-group-split.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
ButtonGroup,
|
||||
ButtonGroupSeparator,
|
||||
} from "@/examples/base/ui/button-group"
|
||||
import { IconPlus } from "@tabler/icons-react"
|
||||
|
||||
export default function ButtonGroupSplit() {
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button variant="secondary">Button</Button>
|
||||
<ButtonGroupSeparator />
|
||||
<Button size="icon" variant="secondary">
|
||||
<IconPlus />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
10
apps/v4/examples/base/button-icon.tsx
Normal file
10
apps/v4/examples/base/button-icon.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { CircleFadingArrowUpIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonIcon() {
|
||||
return (
|
||||
<Button variant="outline" size="icon">
|
||||
<CircleFadingArrowUpIcon />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
5
apps/v4/examples/base/button-link.tsx
Normal file
5
apps/v4/examples/base/button-link.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonLink() {
|
||||
return <Button variant="link">Link</Button>
|
||||
}
|
||||
11
apps/v4/examples/base/button-loading.tsx
Normal file
11
apps/v4/examples/base/button-loading.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Spinner } from "@/examples/base/ui/spinner"
|
||||
|
||||
export default function ButtonLoading() {
|
||||
return (
|
||||
<Button size="sm" variant="outline" disabled>
|
||||
<Spinner />
|
||||
Submit
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
5
apps/v4/examples/base/button-outline.tsx
Normal file
5
apps/v4/examples/base/button-outline.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonOutline() {
|
||||
return <Button variant="outline">Outline</Button>
|
||||
}
|
||||
12
apps/v4/examples/base/button-rounded.tsx
Normal file
12
apps/v4/examples/base/button-rounded.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ArrowUpIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonRounded() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8">
|
||||
<Button variant="outline" size="icon" className="rounded-full">
|
||||
<ArrowUpIcon />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
5
apps/v4/examples/base/button-secondary.tsx
Normal file
5
apps/v4/examples/base/button-secondary.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
|
||||
export default function ButtonSecondary() {
|
||||
return <Button variant="secondary">Secondary</Button>
|
||||
}
|
||||
31
apps/v4/examples/base/button-size.tsx
Normal file
31
apps/v4/examples/base/button-size.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { ArrowUpRightIcon } from "lucide-react"
|
||||
|
||||
export default function ButtonSize() {
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-8 sm:flex-row">
|
||||
<div className="flex items-start gap-2">
|
||||
<Button size="sm" variant="outline">
|
||||
Small
|
||||
</Button>
|
||||
<Button size="icon-sm" aria-label="Submit" variant="outline">
|
||||
<ArrowUpRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<Button variant="outline">Default</Button>
|
||||
<Button size="icon" aria-label="Submit" variant="outline">
|
||||
<ArrowUpRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<Button variant="outline" size="lg">
|
||||
Large
|
||||
</Button>
|
||||
<Button size="icon-lg" aria-label="Submit" variant="outline">
|
||||
<ArrowUpRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
10
apps/v4/examples/base/button-with-icon.tsx
Normal file
10
apps/v4/examples/base/button-with-icon.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { IconGitBranch } from "@tabler/icons-react"
|
||||
|
||||
export default function ButtonWithIcon() {
|
||||
return (
|
||||
<Button variant="outline" size="sm">
|
||||
<IconGitBranch /> New Branch
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
18
apps/v4/examples/base/calendar-demo.tsx
Normal file
18
apps/v4/examples/base/calendar-demo.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Calendar } from "@/examples/base/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-sm"
|
||||
captionLayout="dropdown"
|
||||
/>
|
||||
)
|
||||
}
|
||||
228
apps/v4/examples/base/calendar-hijri.tsx
Normal file
228
apps/v4/examples/base/calendar-hijri.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button, buttonVariants } from "@/examples/base/ui/button"
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
} from "lucide-react"
|
||||
import { getDefaultClassNames, type DayButton } from "react-day-picker"
|
||||
import { DayPicker } from "react-day-picker/persian"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export default function CalendarHijri() {
|
||||
const [date, setDate] = React.useState<Date | undefined>(
|
||||
new Date(2025, 5, 12)
|
||||
)
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
mode="single"
|
||||
defaultMonth={date}
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
className="rounded-lg border shadow-sm"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The code below is for this example only.
|
||||
// For your own calendar, you would edit the calendar.tsx component directly.
|
||||
// ----------------------------------------------------------------------------
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
captionLayout = "label",
|
||||
buttonVariant = "ghost",
|
||||
formatters,
|
||||
components,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayPicker> & {
|
||||
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
|
||||
}) {
|
||||
const defaultClassNames = getDefaultClassNames()
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||
className
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString("default", { month: "short" }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn("w-fit", defaultClassNames.root),
|
||||
months: cn(
|
||||
"flex gap-4 flex-col md:flex-row relative",
|
||||
defaultClassNames.months
|
||||
),
|
||||
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
|
||||
nav: cn(
|
||||
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
|
||||
defaultClassNames.nav
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||
defaultClassNames.button_previous
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||
defaultClassNames.button_next
|
||||
),
|
||||
month_caption: cn(
|
||||
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
|
||||
defaultClassNames.month_caption
|
||||
),
|
||||
dropdowns: cn(
|
||||
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
|
||||
defaultClassNames.dropdowns
|
||||
),
|
||||
dropdown_root: cn(
|
||||
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
|
||||
defaultClassNames.dropdown_root
|
||||
),
|
||||
dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
|
||||
caption_label: cn(
|
||||
"select-none font-medium",
|
||||
captionLayout === "label"
|
||||
? "text-sm"
|
||||
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
table: "w-full border-collapse",
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
|
||||
defaultClassNames.weekday
|
||||
),
|
||||
week: cn("flex w-full mt-2", defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
"select-none w-(--cell-size)",
|
||||
defaultClassNames.week_number_header
|
||||
),
|
||||
week_number: cn(
|
||||
"text-[0.8rem] select-none text-muted-foreground",
|
||||
defaultClassNames.week_number
|
||||
),
|
||||
day: cn(
|
||||
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
|
||||
defaultClassNames.day
|
||||
),
|
||||
range_start: cn(
|
||||
"rounded-l-md bg-accent",
|
||||
defaultClassNames.range_start
|
||||
),
|
||||
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
||||
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
||||
today: cn(
|
||||
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
|
||||
defaultClassNames.today
|
||||
),
|
||||
outside: cn(
|
||||
"text-muted-foreground aria-selected:text-muted-foreground",
|
||||
defaultClassNames.outside
|
||||
),
|
||||
disabled: cn(
|
||||
"text-muted-foreground opacity-50",
|
||||
defaultClassNames.disabled
|
||||
),
|
||||
hidden: cn("invisible", defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
Root: ({ className, rootRef, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
data-slot="calendar"
|
||||
ref={rootRef}
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
Chevron: ({ className, orientation, ...props }) => {
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn("size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
||||
)
|
||||
},
|
||||
DayButton: CalendarDayButton,
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
},
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function CalendarDayButton({
|
||||
className,
|
||||
day,
|
||||
modifiers,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayButton>) {
|
||||
const defaultClassNames = getDefaultClassNames()
|
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null)
|
||||
React.useEffect(() => {
|
||||
if (modifiers.focused) ref.current?.focus()
|
||||
}, [modifiers.focused])
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
data-day={day.date.toLocaleDateString()}
|
||||
data-selected-single={
|
||||
modifiers.selected &&
|
||||
!modifiers.range_start &&
|
||||
!modifiers.range_end &&
|
||||
!modifiers.range_middle
|
||||
}
|
||||
data-range-start={modifiers.range_start}
|
||||
data-range-end={modifiers.range_end}
|
||||
data-range-middle={modifiers.range_middle}
|
||||
className={cn(
|
||||
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
defaultClassNames.day,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
63
apps/v4/examples/base/card-demo.tsx
Normal file
63
apps/v4/examples/base/card-demo.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/examples/base/ui/card"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
|
||||
export default function CardDemo() {
|
||||
return (
|
||||
<Card className="w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Login to your account</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your email below to login to your account
|
||||
</CardDescription>
|
||||
<CardAction>
|
||||
<Button variant="link">Sign Up</Button>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form>
|
||||
<div className="flex flex-col gap-6">
|
||||
<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>
|
||||
<a
|
||||
href="#"
|
||||
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
<Input id="password" type="password" required />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2">
|
||||
<Button type="submit" className="w-full">
|
||||
Login
|
||||
</Button>
|
||||
<Button variant="outline" className="w-full">
|
||||
Login with Google
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
54
apps/v4/examples/base/carousel-api.tsx
Normal file
54
apps/v4/examples/base/carousel-api.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
type CarouselApi,
|
||||
} from "@/examples/base/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 className="mx-auto max-w-xs">
|
||||
<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="text-muted-foreground py-2 text-center text-sm">
|
||||
Slide {current} of {count}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
31
apps/v4/examples/base/carousel-demo.tsx
Normal file
31
apps/v4/examples/base/carousel-demo.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/examples/base/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>
|
||||
)
|
||||
}
|
||||
37
apps/v4/examples/base/carousel-orientation.tsx
Normal file
37
apps/v4/examples/base/carousel-orientation.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/examples/base/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>
|
||||
)
|
||||
}
|
||||
43
apps/v4/examples/base/carousel-plugin.tsx
Normal file
43
apps/v4/examples/base/carousel-plugin.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/examples/base/ui/carousel"
|
||||
import Autoplay from "embla-carousel-autoplay"
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/carousel-size.tsx
Normal file
36
apps/v4/examples/base/carousel-size.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/examples/base/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>
|
||||
)
|
||||
}
|
||||
31
apps/v4/examples/base/carousel-spacing.tsx
Normal file
31
apps/v4/examples/base/carousel-spacing.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import * as React from "react"
|
||||
import { Card, CardContent } from "@/examples/base/ui/card"
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
CarouselNext,
|
||||
CarouselPrevious,
|
||||
} from "@/examples/base/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>
|
||||
)
|
||||
}
|
||||
43
apps/v4/examples/base/chart-bar-demo-axis.tsx
Normal file
43
apps/v4/examples/base/chart-bar-demo-axis.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { ChartContainer, type ChartConfig } from "@/examples/base/ui/chart"
|
||||
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||
|
||||
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 function ChartBarDemoAxis() {
|
||||
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>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/chart-bar-demo-grid.tsx
Normal file
36
apps/v4/examples/base/chart-bar-demo-grid.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
"use client"
|
||||
|
||||
import { ChartContainer, type ChartConfig } from "@/examples/base/ui/chart"
|
||||
import { Bar, BarChart, CartesianGrid } from "recharts"
|
||||
|
||||
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 function ChartBarDemoGrid() {
|
||||
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>
|
||||
)
|
||||
}
|
||||
52
apps/v4/examples/base/chart-bar-demo-legend.tsx
Normal file
52
apps/v4/examples/base/chart-bar-demo-legend.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ChartContainer,
|
||||
ChartLegend,
|
||||
ChartLegendContent,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
type ChartConfig,
|
||||
} from "@/examples/base/ui/chart"
|
||||
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||
|
||||
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 function ChartBarDemoLegend() {
|
||||
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>
|
||||
)
|
||||
}
|
||||
49
apps/v4/examples/base/chart-bar-demo-tooltip.tsx
Normal file
49
apps/v4/examples/base/chart-bar-demo-tooltip.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
type ChartConfig,
|
||||
} from "@/examples/base/ui/chart"
|
||||
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
|
||||
|
||||
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 function ChartBarDemoTooltip() {
|
||||
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>
|
||||
)
|
||||
}
|
||||
35
apps/v4/examples/base/chart-bar-demo.tsx
Normal file
35
apps/v4/examples/base/chart-bar-demo.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import { ChartContainer, type ChartConfig } from "@/examples/base/ui/chart"
|
||||
import { Bar, BarChart } from "recharts"
|
||||
|
||||
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 function ChartBarDemo() {
|
||||
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>
|
||||
)
|
||||
}
|
||||
43
apps/v4/examples/base/checkbox-demo.tsx
Normal file
43
apps/v4/examples/base/checkbox-demo.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
|
||||
export default function CheckboxDemo() {
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox id="terms" />
|
||||
<Label htmlFor="terms">Accept terms and conditions</Label>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox id="terms-2" defaultChecked />
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="terms-2">Accept terms and conditions</Label>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
By clicking this checkbox, you agree to the terms and conditions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox id="toggle" disabled />
|
||||
<Label htmlFor="toggle">Enable notifications</Label>
|
||||
</div>
|
||||
<Label className="hover:bg-accent/50 flex items-start gap-3 rounded-lg border p-3 has-[[aria-checked=true]]:border-blue-600 has-[[aria-checked=true]]:bg-blue-50 dark:has-[[aria-checked=true]]:border-blue-900 dark:has-[[aria-checked=true]]:bg-blue-950">
|
||||
<Checkbox
|
||||
id="toggle-2"
|
||||
defaultChecked
|
||||
className="data-[state=checked]:border-blue-600 data-[state=checked]:bg-blue-600 data-[state=checked]:text-white dark:data-[state=checked]:border-blue-700 dark:data-[state=checked]:bg-blue-700"
|
||||
/>
|
||||
<div className="grid gap-1.5 font-normal">
|
||||
<p className="text-sm leading-none font-medium">
|
||||
Enable notifications
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
You can enable or disable notifications at any time.
|
||||
</p>
|
||||
</div>
|
||||
</Label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
15
apps/v4/examples/base/checkbox-disabled.tsx
Normal file
15
apps/v4/examples/base/checkbox-disabled.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Checkbox } from "@/examples/base/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 leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Accept terms and conditions
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
22
apps/v4/examples/base/checkbox-with-text.tsx
Normal file
22
apps/v4/examples/base/checkbox-with-text.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
"use client"
|
||||
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
|
||||
export default function CheckboxWithText() {
|
||||
return (
|
||||
<div className="items-top flex gap-2">
|
||||
<Checkbox id="terms1" />
|
||||
<div className="grid gap-1.5 leading-none">
|
||||
<label
|
||||
htmlFor="terms1"
|
||||
className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
Accept terms and conditions
|
||||
</label>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
You agree to our Terms of Service and Privacy Policy.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
45
apps/v4/examples/base/collapsible-demo.tsx
Normal file
45
apps/v4/examples/base/collapsible-demo.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/examples/base/ui/collapsible"
|
||||
import { ChevronsUpDown } from "lucide-react"
|
||||
|
||||
export default function CollapsibleDemo() {
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Collapsible
|
||||
open={isOpen}
|
||||
onOpenChange={setIsOpen}
|
||||
className="flex w-[350px] flex-col gap-2"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-4 px-4">
|
||||
<h4 className="text-sm font-semibold">
|
||||
@peduarte starred 3 repositories
|
||||
</h4>
|
||||
<CollapsibleTrigger
|
||||
render={<Button variant="ghost" size="icon" className="size-8" />}
|
||||
>
|
||||
<ChevronsUpDown />
|
||||
<span className="sr-only">Toggle</span>
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<div className="rounded-md border px-4 py-2 font-mono text-sm">
|
||||
@radix-ui/primitives
|
||||
</div>
|
||||
<CollapsibleContent className="flex flex-col gap-2">
|
||||
<div className="rounded-md border px-4 py-2 font-mono text-sm">
|
||||
@radix-ui/colors
|
||||
</div>
|
||||
<div className="rounded-md border px-4 py-2 font-mono text-sm">
|
||||
@stitches/react
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
96
apps/v4/examples/base/combobox-demo.tsx
Normal file
96
apps/v4/examples/base/combobox-demo.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/examples/base/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
import { Check, ChevronsUpDown } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
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
|
||||
render={
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-[200px] justify-between"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{value
|
||||
? frameworks.find((framework) => framework.value === value)?.label
|
||||
: "Select framework..."}
|
||||
<ChevronsUpDown className="opacity-50" />
|
||||
</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}
|
||||
<Check
|
||||
className={cn(
|
||||
"ml-auto",
|
||||
value === framework.value ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
99
apps/v4/examples/base/combobox-dropdown-menu.tsx
Normal file
99
apps/v4/examples/base/combobox-dropdown-menu.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/examples/base/ui/command"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import { MoreHorizontal } from "lucide-react"
|
||||
|
||||
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 leading-none font-medium">
|
||||
<span className="bg-primary text-primary-foreground mr-2 rounded-lg px-2 py-1 text-xs">
|
||||
{label}
|
||||
</span>
|
||||
<span className="text-muted-foreground">Create a new project</span>
|
||||
</p>
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger render={<Button variant="ghost" size="sm" />}>
|
||||
<MoreHorizontal />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[200px]">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
92
apps/v4/examples/base/combobox-popover.tsx
Normal file
92
apps/v4/examples/base/combobox-popover.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/examples/base/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/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-muted-foreground text-sm">Status</p>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button variant="outline" className="w-[150px] justify-start" />
|
||||
}
|
||||
>
|
||||
{selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
121
apps/v4/examples/base/combobox-responsive.tsx
Normal file
121
apps/v4/examples/base/combobox-responsive.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/examples/base/ui/command"
|
||||
import { Drawer, DrawerContent, DrawerTrigger } from "@/examples/base/ui/drawer"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
|
||||
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||
|
||||
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
|
||||
render={
|
||||
<Button variant="outline" className="w-[150px] justify-start" />
|
||||
}
|
||||
>
|
||||
{selectedStatus ? <>{selectedStatus.label}</> : <>+ Set status</>}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
61
apps/v4/examples/base/command-demo.tsx
Normal file
61
apps/v4/examples/base/command-demo.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
CommandShortcut,
|
||||
} from "@/examples/base/ui/command"
|
||||
import {
|
||||
Calculator,
|
||||
Calendar,
|
||||
CreditCard,
|
||||
Settings,
|
||||
Smile,
|
||||
User,
|
||||
} from "lucide-react"
|
||||
|
||||
export function CommandDemo() {
|
||||
return (
|
||||
<Command className="rounded-lg border shadow-md md:min-w-[450px]">
|
||||
<CommandInput placeholder="Type a command or search..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup heading="Suggestions">
|
||||
<CommandItem>
|
||||
<Calendar />
|
||||
<span>Calendar</span>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<Smile />
|
||||
<span>Search Emoji</span>
|
||||
</CommandItem>
|
||||
<CommandItem disabled>
|
||||
<Calculator />
|
||||
<span>Calculator</span>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Settings">
|
||||
<CommandItem>
|
||||
<User />
|
||||
<span>Profile</span>
|
||||
<CommandShortcut>⌘P</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<CreditCard />
|
||||
<span>Billing</span>
|
||||
<CommandShortcut>⌘B</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<Settings />
|
||||
<span>Settings</span>
|
||||
<CommandShortcut>⌘S</CommandShortcut>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
)
|
||||
}
|
||||
86
apps/v4/examples/base/command-dialog.tsx
Normal file
86
apps/v4/examples/base/command-dialog.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator,
|
||||
CommandShortcut,
|
||||
} from "@/examples/base/ui/command"
|
||||
import {
|
||||
Calculator,
|
||||
Calendar,
|
||||
CreditCard,
|
||||
Settings,
|
||||
Smile,
|
||||
User,
|
||||
} from "lucide-react"
|
||||
|
||||
export 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-muted-foreground text-sm">
|
||||
Press{" "}
|
||||
<kbd className="bg-muted text-muted-foreground pointer-events-none inline-flex h-5 items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100 select-none">
|
||||
<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>
|
||||
<Calendar />
|
||||
<span>Calendar</span>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<Smile />
|
||||
<span>Search Emoji</span>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<Calculator />
|
||||
<span>Calculator</span>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
<CommandSeparator />
|
||||
<CommandGroup heading="Settings">
|
||||
<CommandItem>
|
||||
<User />
|
||||
<span>Profile</span>
|
||||
<CommandShortcut>⌘P</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<CreditCard />
|
||||
<span>Billing</span>
|
||||
<CommandShortcut>⌘B</CommandShortcut>
|
||||
</CommandItem>
|
||||
<CommandItem>
|
||||
<Settings />
|
||||
<span>Settings</span>
|
||||
<CommandShortcut>⌘S</CommandShortcut>
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
77
apps/v4/examples/base/context-menu-demo.tsx
Normal file
77
apps/v4/examples/base/context-menu-demo.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuCheckboxItem,
|
||||
ContextMenuContent,
|
||||
ContextMenuGroup,
|
||||
ContextMenuItem,
|
||||
ContextMenuLabel,
|
||||
ContextMenuRadioGroup,
|
||||
ContextMenuRadioItem,
|
||||
ContextMenuSeparator,
|
||||
ContextMenuShortcut,
|
||||
ContextMenuSub,
|
||||
ContextMenuSubContent,
|
||||
ContextMenuSubTrigger,
|
||||
ContextMenuTrigger,
|
||||
} from "@/examples/base/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-52">
|
||||
<ContextMenuGroup>
|
||||
<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-44">
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuItem>Save Page...</ContextMenuItem>
|
||||
<ContextMenuItem>Create Shortcut...</ContextMenuItem>
|
||||
<ContextMenuItem>Name Window...</ContextMenuItem>
|
||||
</ContextMenuGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuItem>Developer Tools</ContextMenuItem>
|
||||
</ContextMenuGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuItem variant="destructive">Delete</ContextMenuItem>
|
||||
</ContextMenuGroup>
|
||||
</ContextMenuSubContent>
|
||||
</ContextMenuSub>
|
||||
</ContextMenuGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuCheckboxItem checked>
|
||||
Show Bookmarks
|
||||
</ContextMenuCheckboxItem>
|
||||
<ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>
|
||||
</ContextMenuGroup>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuGroup>
|
||||
<ContextMenuRadioGroup value="pedro">
|
||||
<ContextMenuLabel inset>People</ContextMenuLabel>
|
||||
<ContextMenuRadioItem value="pedro">
|
||||
Pedro Duarte
|
||||
</ContextMenuRadioItem>
|
||||
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
|
||||
</ContextMenuRadioGroup>
|
||||
</ContextMenuGroup>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
)
|
||||
}
|
||||
318
apps/v4/examples/base/data-table-demo.tsx
Normal file
318
apps/v4/examples/base/data-table-demo.tsx
Normal file
@@ -0,0 +1,318 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/examples/base/ui/table"
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
type ColumnDef,
|
||||
type ColumnFiltersState,
|
||||
type SortingState,
|
||||
type VisibilityState,
|
||||
} from "@tanstack/react-table"
|
||||
import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
|
||||
|
||||
const data: Payment[] = [
|
||||
{
|
||||
id: "m5gr84i9",
|
||||
amount: 316,
|
||||
status: "success",
|
||||
email: "ken99@example.com",
|
||||
},
|
||||
{
|
||||
id: "3u1reuv4",
|
||||
amount: 242,
|
||||
status: "success",
|
||||
email: "Abe45@example.com",
|
||||
},
|
||||
{
|
||||
id: "derv1ws0",
|
||||
amount: 837,
|
||||
status: "processing",
|
||||
email: "Monserrat44@example.com",
|
||||
},
|
||||
{
|
||||
id: "5kma53ae",
|
||||
amount: 874,
|
||||
status: "success",
|
||||
email: "Silas22@example.com",
|
||||
},
|
||||
{
|
||||
id: "bhqecj4p",
|
||||
amount: 721,
|
||||
status: "failed",
|
||||
email: "carmella@example.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() ? true : false)
|
||||
}
|
||||
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
|
||||
<ArrowUpDown />
|
||||
</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
|
||||
render={<Button variant="ghost" className="h-8 w-8 p-0" />}
|
||||
>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<MoreHorizontal />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export 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
|
||||
render={<Button variant="outline" className="ml-auto" />}
|
||||
>
|
||||
Columns <ChevronDown />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuGroup>
|
||||
{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>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<div className="overflow-hidden 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="text-muted-foreground flex-1 text-sm">
|
||||
{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>
|
||||
)
|
||||
}
|
||||
45
apps/v4/examples/base/date-picker-demo.tsx
Normal file
45
apps/v4/examples/base/date-picker-demo.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Calendar } from "@/examples/base/ui/calendar"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
import { format } from "date-fns"
|
||||
import { CalendarIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export function DatePickerDemo() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[240px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
80
apps/v4/examples/base/date-picker-with-presets.tsx
Normal file
80
apps/v4/examples/base/date-picker-with-presets.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Calendar } from "@/examples/base/ui/calendar"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/examples/base/ui/select"
|
||||
import { addDays, format } from "date-fns"
|
||||
import { CalendarIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const items = [
|
||||
{ label: "Select", value: null },
|
||||
{ label: "Today", value: "0" },
|
||||
{ label: "Tomorrow", value: "1" },
|
||||
{ label: "In 3 days", value: "3" },
|
||||
{ label: "In a week", value: "7" },
|
||||
]
|
||||
|
||||
export function DatePickerWithPresets() {
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
render={
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[240px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="start"
|
||||
className="flex w-auto flex-col space-y-2 p-2"
|
||||
>
|
||||
<Select
|
||||
items={items}
|
||||
onValueChange={(value) =>
|
||||
setDate(addDays(new Date(), parseInt(value as string)))
|
||||
}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{items.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="rounded-md border">
|
||||
<Calendar mode="single" selected={date} onSelect={setDate} />
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
67
apps/v4/examples/base/date-picker-with-range.tsx
Normal file
67
apps/v4/examples/base/date-picker-with-range.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Calendar } from "@/examples/base/ui/calendar"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/examples/base/ui/popover"
|
||||
import { addDays, format } from "date-fns"
|
||||
import { CalendarIcon } from "lucide-react"
|
||||
import { type DateRange } from "react-day-picker"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export 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
|
||||
render={
|
||||
<Button
|
||||
id="date"
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-[300px] justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<CalendarIcon />
|
||||
{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>
|
||||
)}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
46
apps/v4/examples/base/dialog-close-button.tsx
Normal file
46
apps/v4/examples/base/dialog-close-button.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/examples/base/ui/dialog"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
|
||||
export function DialogCloseButton() {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger render={<Button variant="outline" />}>Share</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 gap-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>
|
||||
</div>
|
||||
<DialogFooter className="sm:justify-start">
|
||||
<DialogClose render={<Button type="button" variant="secondary" />}>
|
||||
Close
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
50
apps/v4/examples/base/dialog-demo.tsx
Normal file
50
apps/v4/examples/base/dialog-demo.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/examples/base/ui/dialog"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
|
||||
export function DialogDemo() {
|
||||
return (
|
||||
<Dialog>
|
||||
<form>
|
||||
<DialogTrigger render={<Button variant="outline" />}>
|
||||
Open Dialog
|
||||
</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">
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="name-1">Name</Label>
|
||||
<Input id="name-1" name="name" defaultValue="Pedro Duarte" />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="username-1">Username</Label>
|
||||
<Input id="username-1" name="username" defaultValue="@peduarte" />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<DialogClose render={<Button variant="outline" />}>
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<Button type="submit">Save changes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</form>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
135
apps/v4/examples/base/drawer-demo.tsx
Normal file
135
apps/v4/examples/base/drawer-demo.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/examples/base/ui/drawer"
|
||||
import { Minus, Plus } from "lucide-react"
|
||||
import { Bar, BarChart, ResponsiveContainer } from "recharts"
|
||||
|
||||
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 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}
|
||||
>
|
||||
<Minus />
|
||||
<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-muted-foreground text-[0.70rem] uppercase">
|
||||
Calories/day
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0 rounded-full"
|
||||
onClick={() => onClick(10)}
|
||||
disabled={goal >= 400}
|
||||
>
|
||||
<Plus />
|
||||
<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>
|
||||
)
|
||||
}
|
||||
90
apps/v4/examples/base/drawer-dialog.tsx
Normal file
90
apps/v4/examples/base/drawer-dialog.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/examples/base/ui/dialog"
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
} from "@/examples/base/ui/drawer"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useMediaQuery } from "@/hooks/use-media-query"
|
||||
|
||||
export function DrawerDialogDemo() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||
|
||||
if (isDesktop) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger render={<Button variant="outline" />}>
|
||||
Edit Profile
|
||||
</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-6", className)}>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input type="email" id="email" defaultValue="shadcn@example.com" />
|
||||
</div>
|
||||
<div className="grid gap-3">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input id="username" defaultValue="@shadcn" />
|
||||
</div>
|
||||
<Button type="submit">Save changes</Button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
50
apps/v4/examples/base/dropdown-menu-checkboxes.tsx
Normal file
50
apps/v4/examples/base/dropdown-menu-checkboxes.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
|
||||
export function DropdownMenuCheckboxes() {
|
||||
const [showStatusBar, setShowStatusBar] = React.useState(true)
|
||||
const [showActivityBar, setShowActivityBar] = React.useState(false)
|
||||
const [showPanel, setShowPanel] = React.useState(false)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
||||
Open
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={showStatusBar ?? false}
|
||||
onCheckedChange={setShowStatusBar}
|
||||
>
|
||||
Status Bar
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={showActivityBar}
|
||||
onCheckedChange={setShowActivityBar}
|
||||
disabled
|
||||
>
|
||||
Activity Bar
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={showPanel}
|
||||
onCheckedChange={setShowPanel}
|
||||
>
|
||||
Panel
|
||||
</DropdownMenuCheckboxItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
78
apps/v4/examples/base/dropdown-menu-demo.tsx
Normal file
78
apps/v4/examples/base/dropdown-menu-demo.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
|
||||
export function DropdownMenuDemo() {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
||||
Open
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="start">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
<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 />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||
<DropdownMenuItem>Support</DropdownMenuItem>
|
||||
<DropdownMenuItem disabled>API</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuItem>
|
||||
Log out
|
||||
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
116
apps/v4/examples/base/dropdown-menu-dialog.tsx
Normal file
116
apps/v4/examples/base/dropdown-menu-dialog.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/examples/base/ui/dialog"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
import { Field, FieldGroup, FieldLabel } from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Label } from "@/examples/base/ui/label"
|
||||
import { Textarea } from "@/examples/base/ui/textarea"
|
||||
import { MoreHorizontalIcon } from "lucide-react"
|
||||
|
||||
export function DropdownMenuDialog() {
|
||||
const [showNewDialog, setShowNewDialog] = useState(false)
|
||||
const [showShareDialog, setShowShareDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="outline" aria-label="Open menu" size="icon-sm" />
|
||||
}
|
||||
>
|
||||
<MoreHorizontalIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-40" align="end">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>File Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem onSelect={() => setShowNewDialog(true)}>
|
||||
New File...
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={() => setShowShareDialog(true)}>
|
||||
Share...
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem disabled>Download</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Dialog open={showNewDialog} onOpenChange={setShowNewDialog}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New File</DialogTitle>
|
||||
<DialogDescription>
|
||||
Provide a name for your new file. Click create when you're
|
||||
done.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FieldGroup className="pb-3">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="filename">File Name</FieldLabel>
|
||||
<Input id="filename" name="filename" placeholder="document.txt" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter>
|
||||
<DialogClose render={<Button variant="outline" />}>
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<Button type="submit">Create</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Dialog open={showShareDialog} onOpenChange={setShowShareDialog}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Share File</DialogTitle>
|
||||
<DialogDescription>
|
||||
Anyone with the link will be able to view this file.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<FieldGroup className="py-3">
|
||||
<Field>
|
||||
<Label htmlFor="email">Email Address</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="shadcn@vercel.com"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="message">Message (Optional)</FieldLabel>
|
||||
<Textarea
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Check out this file"
|
||||
/>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter>
|
||||
<DialogClose render={<Button variant="outline" />}>
|
||||
Cancel
|
||||
</DialogClose>
|
||||
<Button type="submit">Send Invite</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
35
apps/v4/examples/base/dropdown-menu-radio-group.tsx
Normal file
35
apps/v4/examples/base/dropdown-menu-radio-group.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/examples/base/ui/dropdown-menu"
|
||||
|
||||
export function DropdownMenuRadioGroupDemo() {
|
||||
const [position, setPosition] = React.useState("bottom")
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger render={<Button variant="outline" />}>
|
||||
Open
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
||||
<DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
|
||||
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
||||
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
52
apps/v4/examples/base/empty-avatar-group.tsx
Normal file
52
apps/v4/examples/base/empty-avatar-group.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/examples/base/ui/avatar"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import { PlusIcon } from "lucide-react"
|
||||
|
||||
export default function EmptyAvatarGroup() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia>
|
||||
<div className="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:size-12 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale">
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/maxleiter.png"
|
||||
alt="@maxleiter"
|
||||
/>
|
||||
<AvatarFallback>LR</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/evilrabbit.png"
|
||||
alt="@evilrabbit"
|
||||
/>
|
||||
<AvatarFallback>ER</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No Team Members</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Invite your team to collaborate on this project.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button size="sm">
|
||||
<PlusIcon />
|
||||
Invite Members
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
36
apps/v4/examples/base/empty-avatar.tsx
Normal file
36
apps/v4/examples/base/empty-avatar.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/examples/base/ui/avatar"
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
|
||||
export default function EmptyAvatar() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="default">
|
||||
<Avatar className="size-12">
|
||||
<AvatarImage
|
||||
src="https://github.com/shadcn.png"
|
||||
className="grayscale"
|
||||
/>
|
||||
<AvatarFallback>LR</AvatarFallback>
|
||||
</Avatar>
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>User Offline</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
This user is currently offline. You can leave a message to notify them
|
||||
or try again later.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button size="sm">Leave Message</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
33
apps/v4/examples/base/empty-background.tsx
Normal file
33
apps/v4/examples/base/empty-background.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import { IconBell } from "@tabler/icons-react"
|
||||
import { RefreshCcwIcon } from "lucide-react"
|
||||
|
||||
export default function EmptyMuted() {
|
||||
return (
|
||||
<Empty className="from-muted/50 to-background h-full bg-gradient-to-b from-30%">
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconBell />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No Notifications</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
You're all caught up. New notifications will appear here.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button variant="outline" size="sm">
|
||||
<RefreshCcwIcon />
|
||||
Refresh
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
42
apps/v4/examples/base/empty-demo.tsx
Normal file
42
apps/v4/examples/base/empty-demo.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import { IconFolderCode } from "@tabler/icons-react"
|
||||
import { ArrowUpRightIcon } from "lucide-react"
|
||||
|
||||
export default function EmptyDemo() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconFolderCode />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No Projects Yet</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
You haven't created any projects yet. Get started by creating
|
||||
your first project.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<div className="flex gap-2">
|
||||
<Button>Create Project</Button>
|
||||
<Button variant="outline">Import Project</Button>
|
||||
</div>
|
||||
</EmptyContent>
|
||||
<Button
|
||||
variant="link"
|
||||
render={<a href="#" />}
|
||||
className="text-muted-foreground"
|
||||
size="sm"
|
||||
>
|
||||
Learn More <ArrowUpRightIcon />
|
||||
</Button>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
67
apps/v4/examples/base/empty-icon.tsx
Normal file
67
apps/v4/examples/base/empty-icon.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
Empty,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import {
|
||||
IconBookmark,
|
||||
IconHeart,
|
||||
IconInbox,
|
||||
IconStar,
|
||||
} from "@tabler/icons-react"
|
||||
|
||||
export default function EmptyIcon() {
|
||||
return (
|
||||
<div className="grid gap-8 md:grid-cols-2">
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconInbox />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No messages</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Your inbox is empty. New messages will appear here.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconStar />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No favorites</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Items you mark as favorites will appear here.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconHeart />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No likes yet</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Content you like will be saved here for easy access.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconBookmark />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>No bookmarks</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Save interesting content by bookmarking it.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
</Empty>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
42
apps/v4/examples/base/empty-input-group.tsx
Normal file
42
apps/v4/examples/base/empty-input-group.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupInput,
|
||||
} from "@/examples/base/ui/input-group"
|
||||
import { Kbd } from "@/examples/base/ui/kbd"
|
||||
import { SearchIcon } from "lucide-react"
|
||||
|
||||
export default function EmptyInputGroup() {
|
||||
return (
|
||||
<Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyTitle>404 - Not Found</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
The page you're looking for doesn't exist. Try searching for
|
||||
what you need below.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<InputGroup className="sm:w-3/4">
|
||||
<InputGroupInput placeholder="Try searching for pages..." />
|
||||
<InputGroupAddon>
|
||||
<SearchIcon />
|
||||
</InputGroupAddon>
|
||||
<InputGroupAddon align="inline-end">
|
||||
<Kbd>/</Kbd>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
<EmptyDescription>
|
||||
Need help? <a href="#">Contact support</a>
|
||||
</EmptyDescription>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
31
apps/v4/examples/base/empty-outline.tsx
Normal file
31
apps/v4/examples/base/empty-outline.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Empty,
|
||||
EmptyContent,
|
||||
EmptyDescription,
|
||||
EmptyHeader,
|
||||
EmptyMedia,
|
||||
EmptyTitle,
|
||||
} from "@/examples/base/ui/empty"
|
||||
import { IconCloud } from "@tabler/icons-react"
|
||||
|
||||
export default function EmptyOutline() {
|
||||
return (
|
||||
<Empty className="border border-dashed">
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<IconCloud />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>Cloud Storage Empty</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
Upload files to your cloud storage to access them anywhere.
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button variant="outline" size="sm">
|
||||
Upload Files
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
)
|
||||
}
|
||||
80
apps/v4/examples/base/field-checkbox.tsx
Normal file
80
apps/v4/examples/base/field-checkbox.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
import {
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldLegend,
|
||||
FieldSeparator,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
|
||||
export default function FieldCheckbox() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<FieldGroup>
|
||||
<FieldSet>
|
||||
<FieldLegend variant="label">
|
||||
Show these items on the desktop
|
||||
</FieldLegend>
|
||||
<FieldDescription>
|
||||
Select the items you want to show on the desktop.
|
||||
</FieldDescription>
|
||||
<FieldGroup className="gap-3">
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="finder-pref-9k2-hard-disks-ljj" />
|
||||
<FieldLabel
|
||||
htmlFor="finder-pref-9k2-hard-disks-ljj"
|
||||
className="font-normal"
|
||||
defaultChecked
|
||||
>
|
||||
Hard disks
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="finder-pref-9k2-external-disks-1yg" />
|
||||
<FieldLabel
|
||||
htmlFor="finder-pref-9k2-external-disks-1yg"
|
||||
className="font-normal"
|
||||
>
|
||||
External disks
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="finder-pref-9k2-cds-dvds-fzt" />
|
||||
<FieldLabel
|
||||
htmlFor="finder-pref-9k2-cds-dvds-fzt"
|
||||
className="font-normal"
|
||||
>
|
||||
CDs, DVDs, and iPods
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="finder-pref-9k2-connected-servers-6l2" />
|
||||
<FieldLabel
|
||||
htmlFor="finder-pref-9k2-connected-servers-6l2"
|
||||
className="font-normal"
|
||||
>
|
||||
Connected servers
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
<FieldSeparator />
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="finder-pref-9k2-sync-folders-nep" defaultChecked />
|
||||
<FieldContent>
|
||||
<FieldLabel htmlFor="finder-pref-9k2-sync-folders-nep">
|
||||
Sync Desktop & Documents folders
|
||||
</FieldLabel>
|
||||
<FieldDescription>
|
||||
Your Desktop & Documents folders are being synced with iCloud
|
||||
Drive. You can access them from other devices.
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
51
apps/v4/examples/base/field-choice-card.tsx
Normal file
51
apps/v4/examples/base/field-choice-card.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldSet,
|
||||
FieldTitle,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { RadioGroup, RadioGroupItem } from "@/examples/base/ui/radio-group"
|
||||
|
||||
export default function FieldChoiceCard() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<FieldGroup>
|
||||
<FieldSet>
|
||||
<FieldLabel htmlFor="compute-environment-p8w">
|
||||
Compute Environment
|
||||
</FieldLabel>
|
||||
<FieldDescription>
|
||||
Select the compute environment for your cluster.
|
||||
</FieldDescription>
|
||||
<RadioGroup defaultValue="kubernetes">
|
||||
<FieldLabel htmlFor="kubernetes-r2h">
|
||||
<Field orientation="horizontal">
|
||||
<FieldContent>
|
||||
<FieldTitle>Kubernetes</FieldTitle>
|
||||
<FieldDescription>
|
||||
Run GPU workloads on a K8s configured cluster.
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<RadioGroupItem value="kubernetes" id="kubernetes-r2h" />
|
||||
</Field>
|
||||
</FieldLabel>
|
||||
<FieldLabel htmlFor="vm-z4k">
|
||||
<Field orientation="horizontal">
|
||||
<FieldContent>
|
||||
<FieldTitle>Virtual Machine</FieldTitle>
|
||||
<FieldDescription>
|
||||
Access a VM configured cluster to run GPU workloads.
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<RadioGroupItem value="vm" id="vm-z4k" />
|
||||
</Field>
|
||||
</FieldLabel>
|
||||
</RadioGroup>
|
||||
</FieldSet>
|
||||
</FieldGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
174
apps/v4/examples/base/field-demo.tsx
Normal file
174
apps/v4/examples/base/field-demo.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldLegend,
|
||||
FieldSeparator,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/examples/base/ui/select"
|
||||
import { Textarea } from "@/examples/base/ui/textarea"
|
||||
|
||||
const months = [
|
||||
{ label: "MM", value: null },
|
||||
{ label: "01", value: "01" },
|
||||
{ label: "02", value: "02" },
|
||||
{ label: "03", value: "03" },
|
||||
{ label: "04", value: "04" },
|
||||
{ label: "05", value: "05" },
|
||||
{ label: "06", value: "06" },
|
||||
{ label: "07", value: "07" },
|
||||
{ label: "08", value: "08" },
|
||||
{ label: "09", value: "09" },
|
||||
{ label: "10", value: "10" },
|
||||
{ label: "11", value: "11" },
|
||||
{ label: "12", value: "12" },
|
||||
]
|
||||
|
||||
const years = [
|
||||
{ label: "YYYY", value: null },
|
||||
{ label: "2024", value: "2024" },
|
||||
{ label: "2025", value: "2025" },
|
||||
{ label: "2026", value: "2026" },
|
||||
{ label: "2027", value: "2027" },
|
||||
{ label: "2028", value: "2028" },
|
||||
{ label: "2029", value: "2029" },
|
||||
]
|
||||
|
||||
export default function FieldDemo() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<form>
|
||||
<FieldGroup>
|
||||
<FieldSet>
|
||||
<FieldLegend>Payment Method</FieldLegend>
|
||||
<FieldDescription>
|
||||
All transactions are secure and encrypted
|
||||
</FieldDescription>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-7j9-card-name-43j">
|
||||
Name on Card
|
||||
</FieldLabel>
|
||||
<Input
|
||||
id="checkout-7j9-card-name-43j"
|
||||
placeholder="Evil Rabbit"
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-7j9-card-number-uw1">
|
||||
Card Number
|
||||
</FieldLabel>
|
||||
<Input
|
||||
id="checkout-7j9-card-number-uw1"
|
||||
placeholder="1234 5678 9012 3456"
|
||||
required
|
||||
/>
|
||||
<FieldDescription>
|
||||
Enter your 16-digit card number
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-exp-month-ts6">
|
||||
Month
|
||||
</FieldLabel>
|
||||
<Select items={months}>
|
||||
<SelectTrigger id="checkout-exp-month-ts6">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{months.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-7j9-exp-year-f59">
|
||||
Year
|
||||
</FieldLabel>
|
||||
<Select items={years}>
|
||||
<SelectTrigger id="checkout-7j9-exp-year-f59">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{years.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-7j9-cvv">CVV</FieldLabel>
|
||||
<Input id="checkout-7j9-cvv" placeholder="123" required />
|
||||
</Field>
|
||||
</div>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
<FieldSeparator />
|
||||
<FieldSet>
|
||||
<FieldLegend>Billing Address</FieldLegend>
|
||||
<FieldDescription>
|
||||
The billing address associated with your payment method
|
||||
</FieldDescription>
|
||||
<FieldGroup>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox
|
||||
id="checkout-7j9-same-as-shipping-wgm"
|
||||
defaultChecked
|
||||
/>
|
||||
<FieldLabel
|
||||
htmlFor="checkout-7j9-same-as-shipping-wgm"
|
||||
className="font-normal"
|
||||
>
|
||||
Same as shipping address
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
<FieldSet>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="checkout-7j9-optional-comments">
|
||||
Comments
|
||||
</FieldLabel>
|
||||
<Textarea
|
||||
id="checkout-7j9-optional-comments"
|
||||
placeholder="Add any additional comments"
|
||||
className="resize-none"
|
||||
/>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
<Field orientation="horizontal">
|
||||
<Button type="submit">Submit</Button>
|
||||
<Button variant="outline" type="button">
|
||||
Cancel
|
||||
</Button>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
38
apps/v4/examples/base/field-fieldset.tsx
Normal file
38
apps/v4/examples/base/field-fieldset.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldLegend,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
|
||||
export default function FieldFieldset() {
|
||||
return (
|
||||
<div className="w-full max-w-md space-y-6">
|
||||
<FieldSet>
|
||||
<FieldLegend>Address Information</FieldLegend>
|
||||
<FieldDescription>
|
||||
We need your address to deliver your order.
|
||||
</FieldDescription>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="street">Street Address</FieldLabel>
|
||||
<Input id="street" type="text" placeholder="123 Main St" />
|
||||
</Field>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Field>
|
||||
<FieldLabel htmlFor="city">City</FieldLabel>
|
||||
<Input id="city" type="text" placeholder="New York" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="zip">Postal Code</FieldLabel>
|
||||
<Input id="zip" type="text" placeholder="90502" />
|
||||
</Field>
|
||||
</div>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
55
apps/v4/examples/base/field-group.tsx
Normal file
55
apps/v4/examples/base/field-group.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Checkbox } from "@/examples/base/ui/checkbox"
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldSeparator,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
|
||||
export default function FieldGroupExample() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<FieldGroup>
|
||||
<FieldSet>
|
||||
<FieldLabel>Responses</FieldLabel>
|
||||
<FieldDescription>
|
||||
Get notified when ChatGPT responds to requests that take time, like
|
||||
research or image generation.
|
||||
</FieldDescription>
|
||||
<FieldGroup data-slot="checkbox-group">
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="push" defaultChecked disabled />
|
||||
<FieldLabel htmlFor="push" className="font-normal">
|
||||
Push notifications
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
<FieldSeparator />
|
||||
<FieldSet>
|
||||
<FieldLabel>Tasks</FieldLabel>
|
||||
<FieldDescription>
|
||||
Get notified when tasks you've created have updates.{" "}
|
||||
<a href="#">Manage tasks</a>
|
||||
</FieldDescription>
|
||||
<FieldGroup data-slot="checkbox-group">
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="push-tasks" />
|
||||
<FieldLabel htmlFor="push-tasks" className="font-normal">
|
||||
Push notifications
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<Checkbox id="email-tasks" />
|
||||
<FieldLabel htmlFor="email-tasks" className="font-normal">
|
||||
Email notifications
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
</FieldGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
33
apps/v4/examples/base/field-input.tsx
Normal file
33
apps/v4/examples/base/field-input.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
|
||||
export default function FieldInput() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<FieldSet>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="username">Username</FieldLabel>
|
||||
<Input id="username" type="text" placeholder="Max Leiter" />
|
||||
<FieldDescription>
|
||||
Choose a unique username for your account.
|
||||
</FieldDescription>
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="password">Password</FieldLabel>
|
||||
<FieldDescription>
|
||||
Must be at least 8 characters long.
|
||||
</FieldDescription>
|
||||
<Input id="password" type="password" placeholder="••••••••" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
40
apps/v4/examples/base/field-radio.tsx
Normal file
40
apps/v4/examples/base/field-radio.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
Field,
|
||||
FieldDescription,
|
||||
FieldLabel,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { RadioGroup, RadioGroupItem } from "@/examples/base/ui/radio-group"
|
||||
|
||||
export default function FieldRadio() {
|
||||
return (
|
||||
<div className="w-full max-w-md">
|
||||
<FieldSet>
|
||||
<FieldLabel>Subscription Plan</FieldLabel>
|
||||
<FieldDescription>
|
||||
Yearly and lifetime plans offer significant savings.
|
||||
</FieldDescription>
|
||||
<RadioGroup defaultValue="monthly">
|
||||
<Field orientation="horizontal">
|
||||
<RadioGroupItem value="monthly" id="plan-monthly" />
|
||||
<FieldLabel htmlFor="plan-monthly" className="font-normal">
|
||||
Monthly ($9.99/month)
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<RadioGroupItem value="yearly" id="plan-yearly" />
|
||||
<FieldLabel htmlFor="plan-yearly" className="font-normal">
|
||||
Yearly ($99.99/year)
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
<Field orientation="horizontal">
|
||||
<RadioGroupItem value="lifetime" id="plan-lifetime" />
|
||||
<FieldLabel htmlFor="plan-lifetime" className="font-normal">
|
||||
Lifetime ($299.99)
|
||||
</FieldLabel>
|
||||
</Field>
|
||||
</RadioGroup>
|
||||
</FieldSet>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
61
apps/v4/examples/base/field-responsive.tsx
Normal file
61
apps/v4/examples/base/field-responsive.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Button } from "@/examples/base/ui/button"
|
||||
import {
|
||||
Field,
|
||||
FieldContent,
|
||||
FieldDescription,
|
||||
FieldGroup,
|
||||
FieldLabel,
|
||||
FieldLegend,
|
||||
FieldSeparator,
|
||||
FieldSet,
|
||||
} from "@/examples/base/ui/field"
|
||||
import { Input } from "@/examples/base/ui/input"
|
||||
import { Textarea } from "@/examples/base/ui/textarea"
|
||||
|
||||
export default function FieldResponsive() {
|
||||
return (
|
||||
<div className="w-full max-w-4xl">
|
||||
<form>
|
||||
<FieldSet>
|
||||
<FieldLegend>Profile</FieldLegend>
|
||||
<FieldDescription>Fill in your profile information.</FieldDescription>
|
||||
<FieldSeparator />
|
||||
<FieldGroup>
|
||||
<Field orientation="responsive">
|
||||
<FieldContent>
|
||||
<FieldLabel htmlFor="name">Name</FieldLabel>
|
||||
<FieldDescription>
|
||||
Provide your full name for identification
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<Input id="name" placeholder="Evil Rabbit" required />
|
||||
</Field>
|
||||
<FieldSeparator />
|
||||
<Field orientation="responsive">
|
||||
<FieldContent>
|
||||
<FieldLabel htmlFor="lastName">Message</FieldLabel>
|
||||
<FieldDescription>
|
||||
You can write your message here. Keep it short, preferably
|
||||
under 100 characters.
|
||||
</FieldDescription>
|
||||
</FieldContent>
|
||||
<Textarea
|
||||
id="message"
|
||||
placeholder="Hello, world!"
|
||||
required
|
||||
className="min-h-[100px] resize-none sm:min-w-[300px]"
|
||||
/>
|
||||
</Field>
|
||||
<FieldSeparator />
|
||||
<Field orientation="responsive">
|
||||
<Button type="submit">Submit</Button>
|
||||
<Button type="button" variant="outline">
|
||||
Cancel
|
||||
</Button>
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</FieldSet>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user