add stories

This commit is contained in:
Jonathan Summers-Muir committed 2023-06-25 19:22:39 +08:00
1 parent 92527c209b
commit 30931062bd
40 files changed
+3136 -7

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ const config: any = {
'../src/components/Dropdown/Dropdown.stories.tsx',
// '../src/components/Form/Form.stories.tsx',
'../src/components/shadcn/stories/**/*.tsx',
'../src/components/shadcn/stories/**/*.stories.tsx',
],
addons: [
'storybook-dark-mode',
@@ -1,4 +1,3 @@
import { Button } from './../../Button'
import {
Accordion,
AccordionContent,
@@ -9,13 +8,13 @@ import {
import type { Meta, StoryObj } from '@storybook/react'
const meta: Meta<typeof Button> = {
const meta: Meta<typeof Accordion> = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'shadcn/Accordian',
component: Button,
component: Accordion,
}
export const Neutral = (args: any) => (
@@ -41,7 +40,7 @@ export const Neutral = (args: any) => (
export default meta
type Story = StoryObj<typeof Button>
type Story = StoryObj<typeof Accordion>
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
@@ -0,0 +1,30 @@
import { Alert, AlertDescription, AlertTitle } from '@ui/components/shadcn/ui/alert'
import { AlertCircle, Terminal } from 'lucide-react'
import { Meta } from '@storybook/react'
const meta: Meta<typeof Alert> = {
title: 'shadcn/Alert',
component: Alert,
}
export const Default = () => (
<Alert>
<Terminal className="h-4 w-4" />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>You can add components to your app using the cli.</AlertDescription>
</Alert>
)
Default.storyName = 'Default'
export const Destructive = () => (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>Your session has expired. Please log in again.</AlertDescription>
</Alert>
)
Destructive.storyName = 'Destructive'
export default meta
@@ -0,0 +1,38 @@
import React from 'react'
import { Meta } from '@storybook/react'
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogAction,
AlertDialogCancel,
} from '@ui/components/shadcn/ui/alert-dialog'
const meta: Meta<typeof AlertDialog> = {
title: 'shadcn/AlertDialog',
component: AlertDialog,
}
export const Default = () => (
<AlertDialog>
<AlertDialogTrigger>Open Dialog</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Dialog Title</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogDescription>This is the dialog description.</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>OK</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
Default.storyName = 'Default'
export default meta
@@ -0,0 +1,32 @@
import React from 'react'
import { Meta } from '@storybook/react'
import { AspectRatio } from '@ui/components/shadcn/ui/aspect-ratio'
const meta: Meta<typeof AspectRatio> = {
title: 'shadcn/AspectRatio',
component: AspectRatio,
}
export const Default = () => (
<AspectRatio ratio={16 / 9} className="bg-overlay p-3 border-overlay rounded-lg">
<img
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
className="rounded-md"
/>
{/*
You could also use Next/Image in here like this:
<Image
src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
alt="Photo by Drew Beamer"
fill
className="rounded-md object-cover"
/>
*/}
</AspectRatio>
)
export default meta
@@ -0,0 +1,19 @@
import React from 'react'
import { Meta } from '@storybook/react'
import { Avatar, AvatarFallback, AvatarImage } from '@ui/components/shadcn/ui/avatar'
const meta: Meta<typeof Avatar> = {
title: 'Shadcn/Avatar',
component: Avatar,
}
export const Default = () => (
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
)
Default.storyName = 'Default'
export default meta
@@ -0,0 +1,25 @@
import { Badge } from '@ui/components/shadcn/ui/badge'
import { Meta } from '@storybook/react'
const meta: Meta = {
title: 'shadcn/Badge',
component: Badge,
}
export const Default = () => <Badge>Badge</Badge>
Default.storyName = 'Default'
export const Secondary = () => <Badge variant="secondary">Secondary</Badge>
Secondary.storyName = 'Secondary'
export const Outline = () => <Badge variant="outline">Outline</Badge>
Outline.storyName = 'Outline'
export const Destructive = () => <Badge variant="destructive">Destructive</Badge>
Destructive.storyName = 'Destructive'
export default meta
@@ -0,0 +1,81 @@
import { Button } from '@ui/components/shadcn/ui/button'
import { Meta } from '@storybook/react'
import { ChevronRight, Loader2, Mail } from 'lucide-react'
import Link from 'next/link'
const meta: Meta = {
title: 'Button',
component: Button,
}
export const Default = () => <Button>Button</Button>
Default.storyName = 'Default'
export const Secondary = () => <Button variant="secondary">Secondary</Button>
Secondary.storyName = 'Secondary'
export const Destructive = () => <Button variant="destructive">Destructive</Button>
Destructive.storyName = 'Destructive'
export const Outline = () => <Button variant="outline">Outline</Button>
Outline.storyName = 'Outline'
export const Ghost = () => <Button variant="ghost">Ghost</Button>
Ghost.storyName = 'Ghost'
export const LinkButton = () => (
<Link href="/">
<Button variant="link">Link</Button>
</Link>
)
LinkButton.storyName = 'Link'
export const Icon = () => (
<Button>
<ChevronRight />
</Button>
)
Icon.storyName = 'Icon'
export const IconOnly = () => (
<Button>
<Mail className="mr-2 h-4 w-4" /> Login with Email
</Button>
)
IconOnly.storyName = 'Icon Only'
export const Loading = () => (
<Button disabled>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Please wait
</Button>
)
Loading.storyName = 'Loading'
export const WithIcon = () => (
<Button>
<Mail className="mr-2" />
Login with Email
</Button>
)
WithIcon.storyName = 'With Icon'
export const AsChild = () => (
<Button>
<Link href="/">Login</Link>
</Button>
)
AsChild.storyName = 'As Child'
export default meta
@@ -0,0 +1,114 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Calendar } from '@ui/components/shadcn/ui/calendar'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { Popover, PopoverContent, PopoverTrigger } from '@ui/components/shadcn/ui/popover'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { cn } from '@ui/lib/utils'
import { format } from 'date-fns'
import { CalendarIcon } from 'lucide-react'
import * as React from 'react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/Calendar',
component: Calendar,
}
export const Default = () => {
const [date, setDate] = React.useState<Date | undefined>(new Date())
return (
<div className="flex items-center justify-center">
<Calendar mode="single" selected={date} onSelect={setDate} className="rounded-md border" />
</div>
)
}
// export const withForm = () => {
// const FormSchema = z.object({
// dob: z.date({
// required_error: 'A date of birth is required.',
// }),
// })
// const form = useForm<z.infer<typeof FormSchema>>({
// resolver: zodResolver(FormSchema),
// })
// function onSubmit(data: z.infer<typeof FormSchema>) {
// toast({
// title: 'You submitted the following values:',
// description: (
// <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
// <code className="text-white">{JSON.stringify(data, null, 2)}</code>
// </pre>
// ),
// })
// }
// return (
// <Form {...form}>
// <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
// <FormField
// control={form.control}
// name="dob"
// render={({ field }) => (
// <FormItem className="flex flex-col">
// <FormLabel>Date of birth</FormLabel>
// <Popover>
// <PopoverTrigger asChild>
// <FormControl>
// <Button
// variant={'outline'}
// className={cn(
// 'w-[240px] pl-3 text-left font-normal',
// !field.value && 'text-muted-foreground'
// )}
// >
// {field.value ? format(field.value, 'PPP') : <span>Pick a date</span>}
// <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
// </Button>
// </FormControl>
// </PopoverTrigger>
// <PopoverContent className="w-auto p-0" align="start">
// <Calendar
// mode="single"
// selected={field.value}
// onSelect={(e) => {
// // @mildtomato - guard in case returns undefined
// if (e) {
// field.onChange(e)
// }
// }}
// disabled={(date) => date > new Date() || date < new Date('1900-01-01')}
// initialFocus
// />
// </PopoverContent>
// </Popover>
// <FormDescription>Your date of birth is used to calculate your age.</FormDescription>
// <FormMessage />
// </FormItem>
// )}
// />
// <Button type="submit" variant={'secondary'}>
// Submit
// </Button>
// </form>
// </Form>
// )
// }
// withForm.storyName = 'With Form'
export default meta
@@ -0,0 +1,145 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@ui/components/shadcn/ui/card'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@ui/components/shadcn/ui/select'
import { Switch } from '@ui/components/shadcn/ui/switch'
import { cn } from '@ui/lib/utils'
import { BellRing, Check } from 'lucide-react'
const meta: Meta = {
title: 'shadcn/Card',
component: Card,
}
export const Default = () => {
return (
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
</CardHeader>
<CardContent>
<p>Card Content</p>
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>
)
}
export const CardWithForm = () => {
return (
<Card className="w-[350px]">
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one-click.</CardDescription>
</CardHeader>
<CardContent>
<form>
<div className="grid w-full items-center gap-4">
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Name of your project" />
</div>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="name">Framework</Label>
<Select>
<SelectTrigger>
<SelectValue placeholder="Select" />
<SelectContent position="popper">
<SelectItem value="next">Next.js</SelectItem>
<SelectItem value="sveltekit">SvelteKit</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
<SelectItem value="nuxt">Nuxt.js</SelectItem>
</SelectContent>
</SelectTrigger>
</Select>
</div>
</div>
</form>
</CardContent>
<CardFooter className="flex justify-between">
<Button variant="outline">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
</Card>
)
}
type CardProps = React.ComponentProps<typeof Card>
export const CardDemo = ({ className, ...props }: CardProps) => {
const notifications = [
{
title: 'Your call has been confirmed.',
description: '1 hour ago',
},
{
title: 'You have a new message!',
description: '1 hour ago',
},
{
title: 'Your subscription is expiring soon!',
description: '2 hours ago',
},
]
return (
<Card className={cn('w-[380px]', className)} {...props}>
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>You have 3 unread messages.</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className=" flex items-center space-x-4 rounded-md border p-4">
<BellRing />
<div className="flex-1 space-y-1">
<p className="text-sm font-medium leading-none">Push Notifications</p>
<p className="text-sm text-muted-foreground">Send notifications to device.</p>
</div>
<Switch />
</div>
<div>
{notifications.map((notification, index) => (
<div
key={index}
className="mb-4 grid grid-cols-[25px_1fr] items-start pb-4 last:mb-0 last:pb-0"
>
<span className="flex h-2 w-2 translate-y-1 rounded-full bg-sky-500" />
<div className="space-y-1">
<p className="text-sm font-medium leading-none">{notification.title}</p>
<p className="text-sm text-muted-foreground">{notification.description}</p>
</div>
</div>
))}
</div>
</CardContent>
<CardFooter>
<Button className="w-full">
<Check className="mr-2 h-4 w-4" /> Mark all as read
</Button>
</CardFooter>
</Card>
)
}
CardWithForm.storyName = 'Card with form'
CardDemo.storyName = 'Card demo'
export default meta
@@ -0,0 +1,233 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Checkbox } from '@ui/components/shadcn/ui/checkbox'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import Link from 'next/link'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/Checkbox',
component: Checkbox,
}
export const Default = () => {
return (
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label
htmlFor="terms"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Accept terms and conditions
</label>
</div>
)
}
export function CheckboxWithText() {
return (
<div className="items-top flex space-x-2">
<Checkbox id="terms1" />
<div className="grid gap-1.5 leading-none">
<label
htmlFor="terms1"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Accept terms and conditions
</label>
<p className="text-sm text-muted-foreground">
You agree to our Terms of Service and Privacy Policy.
</p>
</div>
</div>
)
}
export function CheckboxDisabled() {
return (
<div className="flex items-center space-x-2">
<Checkbox id="terms2" disabled />
<label
htmlFor="terms2"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Accept terms and conditions
</label>
</div>
)
}
export function CheckboxReactHookFormSingle() {
const FormSchema = z.object({
mobile: z.boolean().default(false).optional(),
})
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
mobile: true,
},
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="mobile"
render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={(e) => {
if (e) {
field.onChange
}
}}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>Use different settings for my mobile devices</FormLabel>
<FormDescription>
You can manage your mobile notifications in the{' '}
<Link href="/examples/forms">mobile settings</Link> page.
</FormDescription>
</div>
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export function CheckboxReactHookFormMultiple() {
const FormSchema = z.object({
items: z.array(z.string()).refine((value) => value.some((item) => item), {
message: 'You have to select at least one item.',
}),
})
const items = [
{
id: 'recents',
label: 'Recents',
},
{
id: 'home',
label: 'Home',
},
{
id: 'applications',
label: 'Applications',
},
{
id: 'desktop',
label: 'Desktop',
},
{
id: 'downloads',
label: 'Downloads',
},
{
id: 'documents',
label: 'Documents',
},
] as const
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
items: ['recents', 'home'],
},
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="items"
render={() => (
<FormItem>
<div className="mb-4">
<FormLabel className="text-base">Sidebar</FormLabel>
<FormDescription>
Select the items you want to display in the sidebar.
</FormDescription>
</div>
{items.map((item) => (
<FormField
key={item.id}
control={form.control}
name="items"
render={({ field }) => {
return (
<FormItem
key={item.id}
className="flex flex-row items-start space-x-3 space-y-0"
>
<FormControl>
<Checkbox
checked={field.value?.includes(item.id)}
onCheckedChange={(checked) => {
return checked
? field.onChange([...field.value, item.id])
: field.onChange(field.value?.filter((value) => value !== item.id))
}}
/>
</FormControl>
<FormLabel className="font-normal">{item.label}</FormLabel>
</FormItem>
)
}}
/>
))}
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
CheckboxWithText.storyName = 'Checkbox with text'
CheckboxDisabled.storyName = 'Checkbox disabled'
CheckboxReactHookFormSingle.storyName = 'Checkbox react hook form single'
CheckboxReactHookFormMultiple.storyName = ' Checkbox react hook form multiple'
export default meta
@@ -0,0 +1,39 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@ui/components/shadcn/ui/collapsible'
import { ChevronsUpDown } from 'lucide-react'
import * as React from 'react'
const meta: Meta = {
title: 'shadcn/Collapsible',
component: Collapsible,
}
export const Default = () => {
const [isOpen, setIsOpen] = React.useState(false)
return (
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="w-[350px] space-y-2">
<div className="flex items-center justify-between space-x-4 px-4">
<h4 className="text-sm font-semibold">@peduarte starred 3 repositories</h4>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="w-9 p-0">
<ChevronsUpDown className="h-4 w-4" />
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<div className="rounded-md border px-4 py-3 font-mono text-sm">@radix-ui/primitives</div>
<CollapsibleContent className="space-y-2">
<div className="rounded-md border px-4 py-3 font-mono text-sm">@radix-ui/colors</div>
<div className="rounded-md border px-4 py-3 font-mono text-sm">@stitches/react</div>
</CollapsibleContent>
</Collapsible>
)
}
export default meta
@@ -0,0 +1,130 @@
import { Meta } from '@storybook/react'
import {
Command,
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from '@ui/components/shadcn/ui/command'
import { Calculator, Calendar, CreditCard, Settings, Smile, User } from 'lucide-react'
import * as React from 'react'
const meta: Meta = {
title: 'shadcn/Command',
component: Command,
}
export function Default() {
return (
<Command className="rounded-lg border shadow-md">
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>
<Calendar className="mr-2 h-4 w-4" />
<span>Calendar</span>
</CommandItem>
<CommandItem>
<Smile className="mr-2 h-4 w-4" />
<span>Search Emoji</span>
</CommandItem>
<CommandItem>
<Calculator className="mr-2 h-4 w-4" />
<span>Calculator</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
<CommandShortcut>⌘P</CommandShortcut>
</CommandItem>
<CommandItem>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
<CommandShortcut>⌘B</CommandShortcut>
</CommandItem>
<CommandItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
<CommandShortcut>⌘S</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
)
}
export function CommandDialogDemo() {
const [open, setOpen] = React.useState(false)
React.useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && e.metaKey) {
setOpen((open) => !open)
}
}
document.addEventListener('keydown', down)
return () => document.removeEventListener('keydown', down)
}, [])
return (
<>
<p className="text-sm text-muted-foreground">
Press{' '}
<kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
<span className="text-xs">⌘</span>K
</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 className="mr-2 h-4 w-4" />
<span>Calendar</span>
</CommandItem>
<CommandItem>
<Smile className="mr-2 h-4 w-4" />
<span>Search Emoji</span>
</CommandItem>
<CommandItem>
<Calculator className="mr-2 h-4 w-4" />
<span>Calculator</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>
<User className="mr-2 h-4 w-4" />
<span>Profile</span>
<CommandShortcut>⌘P</CommandShortcut>
</CommandItem>
<CommandItem>
<CreditCard className="mr-2 h-4 w-4" />
<span>Billing</span>
<CommandShortcut>⌘B</CommandShortcut>
</CommandItem>
<CommandItem>
<Settings className="mr-2 h-4 w-4" />
<span>Settings</span>
<CommandShortcut>⌘S</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
</>
)
}
CommandDialogDemo.storyName = 'Command in dialog'
export default meta
@@ -0,0 +1,73 @@
import { Meta } from '@storybook/react'
import {
ContextMenu,
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from '@ui/components/shadcn/ui/context-menu'
const meta: Meta = {
title: 'shadcn/ContextMenu',
component: ContextMenu,
}
export const Default = () => {
return (
<ContextMenu>
<ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-md border border-dashed text-sm">
Right click here
</ContextMenuTrigger>
<ContextMenuContent className="w-64">
<ContextMenuItem inset>
Back
<ContextMenuShortcut>⌘[</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem inset disabled>
Forward
<ContextMenuShortcut>⌘]</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem inset>
Reload
<ContextMenuShortcut>⌘R</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger inset>More Tools</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-48">
<ContextMenuItem>
Save Page As...
<ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
</ContextMenuItem>
<ContextMenuItem>Create Shortcut...</ContextMenuItem>
<ContextMenuItem>Name Window...</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>Developer Tools</ContextMenuItem>
</ContextMenuSubContent>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuCheckboxItem checked>
Show Bookmarks Bar
<ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>
</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>
<ContextMenuSeparator />
<ContextMenuRadioGroup value="pedro">
<ContextMenuLabel inset>People</ContextMenuLabel>
<ContextMenuSeparator />
<ContextMenuRadioItem value="pedro">Pedro Duarte</ContextMenuRadioItem>
<ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenu>
)
}
export default meta
@@ -0,0 +1,311 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
;('use client')
import * as React from 'react'
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table'
import { ArrowUpDown, ChevronDown, MoreHorizontal } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
const data: Payment[] = [
{
id: 'm5gr84i9',
amount: 316,
status: 'success',
email: 'ken99@yahoo.com',
},
{
id: '3u1reuv4',
amount: 242,
status: 'success',
email: 'Abe45@gmail.com',
},
{
id: 'derv1ws0',
amount: 837,
status: 'processing',
email: 'Monserrat44@gmail.com',
},
{
id: '5kma53ae',
amount: 874,
status: 'success',
email: 'Silas22@gmail.com',
},
{
id: 'bhqecj4p',
amount: 721,
status: 'failed',
email: 'carmella@hotmail.com',
},
]
export type Payment = {
id: string
amount: number
status: 'pending' | 'processing' | 'success' | 'failed'
email: string
}
export const columns: ColumnDef<Payment>[] = [
{
id: 'select',
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
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 className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => <div className="lowercase">{row.getValue('email')}</div>,
},
{
accessorKey: 'amount',
header: () => <div className="text-right">Amount</div>,
cell: ({ row }) => {
const amount = parseFloat(row.getValue('amount'))
// Format the amount as a dollar amount
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(amount)
return <div className="text-right font-medium">{formatted}</div>
},
},
{
id: 'actions',
enableHiding: false,
cell: ({ row }) => {
const payment = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(payment.id)}>
Copy payment ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View customer</DropdownMenuItem>
<DropdownMenuItem>View payment details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
]
export function DataTableDemo() {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div className="w-full">
<div className="flex items-center py-4">
<Input
placeholder="Filter emails..."
value={(table.getColumn('email')?.getFilterValue() as string) ?? ''}
onChange={(event) => table.getColumn('email')?.setFilterValue(event.target.value)}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns <ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{' '}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
)
}
const meta: Meta = {
title: 'shadcn/Card',
component: Card,
}
export const Default = () => {
return (
<div>
<div></div>
</div>
)
}
export const CardWithForm = () => {
return (
<div>
<div></div>
</div>
)
}
CardWithForm.storyName = 'Card with form'
export default meta
@@ -0,0 +1,235 @@
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Calendar } from '@ui/components/shadcn/ui/calendar'
import { Popover, PopoverContent, PopoverTrigger } from '@ui/components/shadcn/ui/popover'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@ui/components/shadcn/ui/select'
import { cn } from '@ui/lib/utils'
import { addDays, format } from 'date-fns'
import { Calendar as CalendarIcon } from 'lucide-react'
import * as React from 'react'
import { DateRange } from 'react-day-picker'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/DatePicker',
// component: Card,
}
export function Default() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={'outline'}
className={cn(
'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, 'PPP') : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
</PopoverContent>
</Popover>
)
}
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 asChild>
<Button
id="date"
variant={'outline'}
className={cn(
'w-[300px] justify-start text-left font-normal',
!date && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date?.from ? (
date.to ? (
<>
{format(date.from, 'LLL dd, y')} - {format(date.to, 'LLL dd, y')}
</>
) : (
format(date.from, 'LLL dd, y')
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
defaultMonth={date?.from}
selected={date}
onSelect={setDate}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
)
}
// 'use client'
// import * as React from 'react'
// import { addDays, format } from 'date-fns'
// import { Calendar as CalendarIcon } from 'lucide-react'
// import { cn } from '@/lib/utils'
// import { Button } from '@/components/ui/button'
// import { Calendar } from '@/components/ui/calendar'
// import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
// import {
// Select,
// SelectContent,
// SelectItem,
// SelectTrigger,
// SelectValue,
// } from '@/components/ui/select'
export function DatePickerWithPresets() {
const [date, setDate] = React.useState<Date>()
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={'outline'}
className={cn(
'w-[280px] justify-start text-left font-normal',
!date && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, 'PPP') : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="flex w-auto flex-col space-y-2 p-2">
<Select onValueChange={(value) => setDate(addDays(new Date(), parseInt(value)))}>
<SelectTrigger>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="0">Today</SelectItem>
<SelectItem value="1">Tomorrow</SelectItem>
<SelectItem value="3">In 3 days</SelectItem>
<SelectItem value="7">In a week</SelectItem>
</SelectContent>
</Select>
<div className="rounded-md border">
<Calendar mode="single" selected={date} onSelect={setDate} />
</div>
</PopoverContent>
</Popover>
)
}
const FormSchema = z.object({
dob: z.date({
required_error: 'A date of birth is required.',
}),
})
export function DatePickerForm() {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="dob"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Date of birth</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={'outline'}
className={cn(
'w-[240px] pl-3 text-left font-normal',
!field.value && 'text-muted-foreground'
)}
>
{field.value ? format(field.value, 'PPP') : <span>Pick a date</span>}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={(e) => {
if (e) {
field.onChange(e)
}
}}
disabled={(date) => date > new Date() || date < new Date('1900-01-01')}
initialFocus
/>
</PopoverContent>
</Popover>
<FormDescription>Your date of birth is used to calculate your age.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
// CardWithForm.storyName = 'Card with form'
export default meta
@@ -0,0 +1,55 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@ui/components/shadcn/ui/dialog'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
const meta: Meta = {
title: 'shadcn/Dialog',
component: Dialog,
}
export function Default() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Edit Profile</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input id="name" value="Pedro Duarte" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input id="username" value="@peduarte" className="col-span-3" />
</div>
</div>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
export default meta
@@ -29,7 +29,7 @@ const meta: Meta<typeof Form> = {
* See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Shadcn/Form',
title: 'shadcn/Form',
component: Form,
}
@@ -173,7 +173,12 @@ export const CalendarExample = {
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
onSelect={(e) => {
if (e) {
// @mildtomato: guard in case returns undefined
field.onChange
}
}}
disabled={(date) => date > new Date() || date < new Date('1900-01-01')}
initialFocus
/>
@@ -0,0 +1,38 @@
import { Meta } from '@storybook/react'
import { Avatar, AvatarFallback, AvatarImage } from '@ui/components/shadcn/ui/avatar'
import { Button } from '@ui/components/shadcn/ui/button'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@ui/components/shadcn/ui/hover-card'
import { CalendarDays } from 'lucide-react'
const meta: Meta = {
title: 'shadcn/HoverCard',
component: HoverCard,
}
export function Default() {
return (
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="link">@nextjs</Button>
</HoverCardTrigger>
<HoverCardContent className="w-80">
<div className="flex justify-between space-x-4">
<Avatar>
<AvatarImage src="https://github.com/vercel.png" />
<AvatarFallback>VC</AvatarFallback>
</Avatar>
<div className="space-y-1">
<h4 className="text-sm font-semibold">@nextjs</h4>
<p className="text-sm">The React Framework – created and maintained by @vercel.</p>
<div className="flex items-center pt-2">
<CalendarDays className="mr-2 h-4 w-4 opacity-70" />{' '}
<span className="text-xs text-muted-foreground">Joined December 2021</span>
</div>
</div>
</div>
</HoverCardContent>
</HoverCard>
)
}
export default meta
@@ -0,0 +1,103 @@
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const FormSchema = z.object({
username: z.string().min(2, {
message: 'Username must be at least 2 characters.',
}),
})
const meta: Meta = {
title: 'shadcn/Input',
component: Input,
}
export function Default() {
return <Input type="email" placeholder="Email" />
}
export function InputFile() {
return (
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor="picture">Picture</Label>
<Input id="picture" type="file" />
</div>
)
}
export function InputDisabled() {
return <Input disabled type="email" placeholder="Email" />
}
export function InputWithLabel() {
return (
<div className="grid w-full max-w-sm items-center gap-1.5">
<Label htmlFor="email">Email</Label>
<Input type="email" id="email" placeholder="Email" />
</div>
)
}
export function InputWithButton() {
return (
<div className="flex w-full max-w-sm items-center space-x-2">
<Input type="email" placeholder="Email" />
<Button type="submit">Subscribe</Button>
</div>
)
}
export function InputForm() {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export default meta
@@ -0,0 +1,22 @@
import { Meta } from '@storybook/react'
import { Checkbox } from '@ui/components/shadcn/ui/checkbox'
import { Label } from '@ui/components/shadcn/ui/label'
const meta: Meta = {
title: 'shadcn/Label',
component: Label,
}
export function Default() {
return (
<div>
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
</div>
)
}
export default meta
@@ -0,0 +1,114 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from '@ui/components/shadcn/ui/menubar'
const meta: Meta = {
title: 'shadcn/Menubar',
component: Menubar,
}
export function Default() {
return (
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>
New Tab <MenubarShortcut>⌘T</MenubarShortcut>
</MenubarItem>
<MenubarItem>
New Window <MenubarShortcut>⌘N</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled>New Incognito Window</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Share</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Email link</MenubarItem>
<MenubarItem>Messages</MenubarItem>
<MenubarItem>Notes</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>
Print... <MenubarShortcut>⌘P</MenubarShortcut>
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Edit</MenubarTrigger>
<MenubarContent>
<MenubarItem>
Undo <MenubarShortcut>⌘Z</MenubarShortcut>
</MenubarItem>
<MenubarItem>
Redo <MenubarShortcut>⇧⌘Z</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarSub>
<MenubarSubTrigger>Find</MenubarSubTrigger>
<MenubarSubContent>
<MenubarItem>Search the web</MenubarItem>
<MenubarSeparator />
<MenubarItem>Find...</MenubarItem>
<MenubarItem>Find Next</MenubarItem>
<MenubarItem>Find Previous</MenubarItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<MenubarItem>Cut</MenubarItem>
<MenubarItem>Copy</MenubarItem>
<MenubarItem>Paste</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarCheckboxItem>Always Show Bookmarks Bar</MenubarCheckboxItem>
<MenubarCheckboxItem checked>Always Show Full URLs</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarItem inset>
Reload <MenubarShortcut>⌘R</MenubarShortcut>
</MenubarItem>
<MenubarItem disabled inset>
Force Reload <MenubarShortcut>⇧⌘R</MenubarShortcut>
</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Toggle Fullscreen</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Hide Sidebar</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Profiles</MenubarTrigger>
<MenubarContent>
<MenubarRadioGroup value="benoit">
<MenubarRadioItem value="andy">Andy</MenubarRadioItem>
<MenubarRadioItem value="benoit">Benoit</MenubarRadioItem>
<MenubarRadioItem value="Luis">Luis</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarItem inset>Edit...</MenubarItem>
<MenubarSeparator />
<MenubarItem inset>Add Profile...</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}
export default meta
@@ -0,0 +1,155 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
import * as React from 'react'
import Link from 'next/link'
import { cn } from '@ui/lib/utils'
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from '@ui/components/shadcn/ui/navigation-menu'
const meta: Meta = {
title: 'shadcn/NavigationMenu',
component: NavigationMenu,
}
export function Default() {
const components: { title: string; href: string; description: string }[] = [
{
title: 'Alert Dialog',
href: '/docs/primitives/alert-dialog',
description:
'A modal dialog that interrupts the user with important content and expects a response.',
},
{
title: 'Hover Card',
href: '/docs/primitives/hover-card',
description: 'For sighted users to preview content available behind a link.',
},
{
title: 'Progress',
href: '/docs/primitives/progress',
description:
'Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.',
},
{
title: 'Scroll-area',
href: '/docs/primitives/scroll-area',
description: 'Visually or semantically separates content.',
},
{
title: 'Tabs',
href: '/docs/primitives/tabs',
description:
'A set of layered sections of content—known as tab panels—that are displayed one at a time.',
},
{
title: 'Tooltip',
href: '/docs/primitives/tooltip',
description:
'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',
},
]
const ListItem = React.forwardRef<React.ElementRef<'a'>, React.ComponentPropsWithoutRef<'a'>>(
({ className, title, children, ...props }, ref) => {
return (
<li>
<NavigationMenuLink asChild>
<a
ref={ref}
className={cn(
'block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground',
className
)}
{...props}
>
<div className="text-sm font-medium leading-none">{title}</div>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">{children}</p>
</a>
</NavigationMenuLink>
</li>
/**
* if using Next.js
*
* import { navigationMenuTriggerStyle } from "@/components/ui/navigation-menu"
*
* <NavigationMenuItem>
* <Link href="/docs" legacyBehavior passHref>
* <NavigationMenuLink className={navigationMenuTriggerStyle()}>
* Documentation
* </NavigationMenuLink>
* </Link>
* </NavigationMenuItem>
*/
)
}
)
ListItem.displayName = 'ListItem'
return (
<div className="preview flex min-h-[350px] w-full justify-center p-10 items-center">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>Getting started</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
<li className="row-span-3">
<NavigationMenuLink asChild>
<a
className="flex h-full w-full select-none flex-col justify-end rounded-md bg-gradient-to-b from-muted/50 to-muted p-6 no-underline outline-none focus:shadow-md"
href="/"
>
<div className="w-7 h-7 rounded-lg bg-foreground"></div>
<div className="mb-2 mt-4 text-lg font-medium">shadcn/ui</div>
<p className="text-sm leading-tight text-muted-foreground">
Beautifully designed components built with Radix UI and Tailwind CSS.
</p>
</a>
</NavigationMenuLink>
</li>
<ListItem href="/docs" title="Introduction">
Re-usable components built using Radix UI and Tailwind CSS.
</ListItem>
<ListItem href="/docs/installation" title="Installation">
How to install dependencies and structure your app.
</ListItem>
<ListItem href="/docs/primitives/typography" title="Typography">
Styles for headings, paragraphs, lists...etc
</ListItem>
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
{components.map((component) => (
<ListItem key={component.title} title={component.title} href={component.href}>
{component.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/docs" legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
Documentation
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
)
}
export default meta
@@ -0,0 +1,49 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
import { Button } from '@ui/components/shadcn/ui/button'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@ui/components/shadcn/ui/popover'
const meta: Meta = {
title: 'shadcn/Popover',
component: Popover,
}
export function PopoverDemo() {
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open popover</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
<div className="grid gap-4">
<div className="space-y-2">
<h4 className="font-medium leading-none">Dimensions</h4>
<p className="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
</div>
<div className="grid gap-2">
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="width">Width</Label>
<Input id="width" defaultValue="100%" className="col-span-2 h-8" />
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxWidth">Max. width</Label>
<Input id="maxWidth" defaultValue="300px" className="col-span-2 h-8" />
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="height">Height</Label>
<Input id="height" defaultValue="25px" className="col-span-2 h-8" />
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="maxHeight">Max. height</Label>
<Input id="maxHeight" defaultValue="none" className="col-span-2 h-8" />
</div>
</div>
</div>
</PopoverContent>
</Popover>
)
}
export default meta
@@ -0,0 +1,21 @@
import { Progress } from '@ui/components/shadcn/ui/progress'
import { Meta } from '@storybook/react'
import * as React from 'react'
const meta: Meta = {
title: 'shadcn/Progress',
component: Progress,
}
export function Default() {
const [progress, setProgress] = React.useState(13)
React.useEffect(() => {
const timer = setTimeout(() => setProgress(66), 500)
return () => clearTimeout(timer)
}, [])
return <Progress value={progress} className="w-[60%]" />
}
export default meta
@@ -0,0 +1,109 @@
import { Button } from '@ui/components/shadcn/ui/button'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { Label } from '@ui/components/shadcn/ui/label'
import { RadioGroup, RadioGroupItem } from '@ui/components/shadcn/ui/radio-group'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/RadioGroup',
component: RadioGroup,
}
export function Default() {
return (
<RadioGroup defaultValue="comfortable">
<div className="flex items-center space-x-2">
<RadioGroupItem value="default" id="r1" />
<Label htmlFor="r1">Default</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="comfortable" id="r2" />
<Label htmlFor="r2">Comfortable</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="compact" id="r3" />
<Label htmlFor="r3">Compact</Label>
</div>
</RadioGroup>
)
}
export function RadioGroupForm() {
const FormSchema = z.object({
type: z.enum(['all', 'mentions', 'none'], {
required_error: 'You need to select a notification type.',
}),
})
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem className="space-y-3">
<FormLabel>Notify me about...</FormLabel>
<FormControl>
<RadioGroup
// @ts-expect-error
onValueChange={field.onChange}
defaultValue={field.value}
className="flex flex-col space-y-1"
>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="all" />
</FormControl>
<FormLabel className="font-normal">All new messages</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="mentions" />
</FormControl>
<FormLabel className="font-normal">Direct messages and mentions</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="none" />
</FormControl>
<FormLabel className="font-normal">Nothing</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export default meta
@@ -0,0 +1,30 @@
import { Meta } from '@storybook/react'
import { ScrollArea } from '@ui/components/shadcn/ui/scroll-area'
import { Separator } from '@ui/components/shadcn/ui/separator'
import * as React from 'react'
const meta: Meta = {
title: 'shadcn/ScrollArea',
component: ScrollArea,
}
export function Default() {
const tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`)
return (
<ScrollArea className="h-72 w-48 rounded-md border">
<div className="p-4">
<h4 className="mb-4 text-sm font-medium leading-none">Tags</h4>
{tags.map((tag) => (
<React.Fragment>
<div className="text-sm" key={tag}>
{tag}
</div>
<Separator className="my-2" />
</React.Fragment>
))}
</div>
</ScrollArea>
)
}
export default meta
@@ -0,0 +1,138 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Card } from '@ui/components/shadcn/ui/card'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from '@ui/components/shadcn/ui/select'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import Link from 'next/link'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
import { SelectSeparator } from '../select'
const meta: Meta = {
title: 'shadcn/Select',
component: Select,
}
export function Default() {
return (
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes">Grapes</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
)
}
export function withSeperators() {
return (
<Select>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Fruits</SelectLabel>
<SelectItem value="apple">Apple</SelectItem>
<SelectItem value="banana">Banana</SelectItem>
<SelectItem value="blueberry">Blueberry</SelectItem>
<SelectItem value="grapes">Grapes</SelectItem>
<SelectItem value="pineapple">Pineapple</SelectItem>
<SelectSeparator />
<SelectLabel>Vegetables</SelectLabel>
<SelectItem value="cabbage">Cabbage</SelectItem>
<SelectItem value="potato">Potato</SelectItem>
<SelectItem value="carrot">Carrot</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
)
}
export function SelectForm() {
const FormSchema = z.object({
email: z
.string({
required_error: 'Please select an email to display.',
})
.email(),
})
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="m@example.com">m@example.com</SelectItem>
<SelectItem value="m@google.com">m@google.com</SelectItem>
<SelectItem value="m@support.com">m@support.com</SelectItem>
</SelectContent>
</Select>
<FormDescription>
You can manage email addresses in your{' '}
<Link href="/examples/forms">email settings</Link>.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export default meta
@@ -0,0 +1,28 @@
import { Meta } from '@storybook/react'
import { Separator } from '@ui/components/shadcn/ui/separator'
const meta: Meta = {
title: 'shadcn/Separator',
component: Separator,
}
export function Default() {
return (
<div>
<div className="space-y-1">
<h4 className="text-sm font-medium leading-none">Radix Primitives</h4>
<p className="text-sm text-muted-foreground">An open-source UI component library.</p>
</div>
<Separator className="my-4" />
<div className="flex h-5 items-center space-x-4 text-sm">
<div>Blog</div>
<Separator orientation="vertical" />
<div>Docs</div>
<Separator orientation="vertical" />
<div>Source</div>
</div>
</div>
)
}
export default meta
@@ -0,0 +1,103 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@ui/components/shadcn/ui/sheet'
const meta: Meta = {
title: 'shadcn/Sheet',
component: Sheet,
}
export function Default() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you're done.
</SheetDescription>
</SheetHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input id="name" value="Pedro Duarte" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input id="username" value="@peduarte" className="col-span-3" />
</div>
</div>
<SheetFooter>
<SheetClose asChild>
<Button type="submit">Save changes</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
)
}
export function SheetSide() {
const SHEET_SIDES = ['top', 'right', 'bottom', 'left'] as const
type SheetSide = (typeof SHEET_SIDES)[number]
return (
<div className="grid grid-cols-2 gap-2">
{SHEET_SIDES.map((side) => (
<Sheet key={side}>
<SheetTrigger asChild>
<Button variant="outline">{side}</Button>
</SheetTrigger>
<SheetContent position={side}>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you're done.
</SheetDescription>
</SheetHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
</Label>
<Input id="name" value="Pedro Duarte" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="username" className="text-right">
Username
</Label>
<Input id="username" value="@peduarte" className="col-span-3" />
</div>
</div>
<SheetFooter>
<SheetClose asChild>
<Button type="submit">Save changes</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
))}
</div>
)
}
export default meta
@@ -0,0 +1,21 @@
import { Meta } from '@storybook/react'
import { Skeleton } from '@ui/components/shadcn/ui/skeleton'
const meta: Meta = {
title: 'shadcn/Skeleton',
component: Skeleton,
}
export function Default() {
return (
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
)
}
export default meta
@@ -0,0 +1,24 @@
import { Meta } from '@storybook/react'
import { Slider } from '@ui/components/shadcn/ui/slider'
import { cn } from '@ui/lib/utils'
const meta: Meta = {
title: 'shadcn/Slider',
component: Slider,
}
type SliderProps = React.ComponentProps<typeof Slider>
export function SliderDemo({ className, ...props }: SliderProps) {
return (
<Slider
defaultValue={[50]}
max={100}
step={1}
className={cn('w-[60%]', className)}
{...props}
/>
)
}
export default meta
@@ -0,0 +1,107 @@
import { Button } from '@ui/components/shadcn/ui/button'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
} from '@ui/components/shadcn/ui/form'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Label } from '@ui/components/shadcn/ui/label'
import { Switch } from '@ui/components/shadcn/ui/switch'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/Switch',
component: Switch,
}
export function Default() {
return (
<div className="flex items-center space-x-2">
<Switch id="airplane-mode" />
<Label htmlFor="airplane-mode">Airplane Mode</Label>
</div>
)
}
export function SwitchForm() {
const FormSchema = z.object({
marketing_emails: z.boolean().default(false).optional(),
security_emails: z.boolean(),
})
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
security_emails: true,
},
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full space-y-6">
<div>
<h3 className="mb-4 text-lg font-medium">Email Notifications</h3>
<div className="space-y-4">
<FormField
control={form.control}
name="marketing_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Marketing emails</FormLabel>
<FormDescription>
Receive emails about new products, features, and more.
</FormDescription>
</div>
<FormControl>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="security_emails"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Security emails</FormLabel>
<FormDescription>Receive emails about your account security.</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
disabled
aria-readonly
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export default meta
@@ -0,0 +1,88 @@
import { Meta } from '@storybook/react'
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@ui/components/shadcn/ui/table'
const invoices = [
{
invoice: 'INV001',
paymentStatus: 'Paid',
totalAmount: '$250.00',
paymentMethod: 'Credit Card',
},
{
invoice: 'INV002',
paymentStatus: 'Pending',
totalAmount: '$150.00',
paymentMethod: 'PayPal',
},
{
invoice: 'INV003',
paymentStatus: 'Unpaid',
totalAmount: '$350.00',
paymentMethod: 'Bank Transfer',
},
{
invoice: 'INV004',
paymentStatus: 'Paid',
totalAmount: '$450.00',
paymentMethod: 'Credit Card',
},
{
invoice: 'INV005',
paymentStatus: 'Paid',
totalAmount: '$550.00',
paymentMethod: 'PayPal',
},
{
invoice: 'INV006',
paymentStatus: 'Pending',
totalAmount: '$200.00',
paymentMethod: 'Bank Transfer',
},
{
invoice: 'INV007',
paymentStatus: 'Unpaid',
totalAmount: '$300.00',
paymentMethod: 'Credit Card',
},
]
const meta: Meta = {
title: 'shadcn/Table',
component: Table,
}
export function TableDemo() {
return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow key={invoice.invoice}>
<TableCell className="font-medium">{invoice.invoice}</TableCell>
<TableCell>{invoice.paymentStatus}</TableCell>
<TableCell>{invoice.paymentMethod}</TableCell>
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
export default meta
@@ -0,0 +1,77 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@ui/components/shadcn/ui/card'
import { Input } from '@ui/components/shadcn/ui/input'
import { Label } from '@ui/components/shadcn/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@ui/components/shadcn/ui/tabs'
const meta: Meta = {
title: 'shadcn/Tabs',
component: Tabs,
}
export function TabsDemo() {
return (
<Tabs defaultValue="account" className="w-[400px]">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
<TabsContent value="account">
<Card>
<CardHeader>
<CardTitle>Account</CardTitle>
<CardDescription>
Make changes to your account here. Click save when you're done.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="space-y-1">
<Label htmlFor="name">Name</Label>
<Input id="name" defaultValue="Pedro Duarte" />
</div>
<div className="space-y-1">
<Label htmlFor="username">Username</Label>
<Input id="username" defaultValue="@peduarte" />
</div>
</CardContent>
<CardFooter>
<Button>Save changes</Button>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="password">
<Card>
<CardHeader>
<CardTitle>Password</CardTitle>
<CardDescription>
Change your password here. After saving, you'll be logged out.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
<div className="space-y-1">
<Label htmlFor="current">Current password</Label>
<Input id="current" type="password" />
</div>
<div className="space-y-1">
<Label htmlFor="new">New password</Label>
<Input id="new" type="password" />
</div>
</CardContent>
<CardFooter>
<Button>Save password</Button>
</CardFooter>
</Card>
</TabsContent>
</Tabs>
)
}
export default meta
@@ -0,0 +1,117 @@
import { Button } from '@ui/components/shadcn/ui/button'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@ui/components/shadcn/ui/form'
import { toast } from '@ui/components/shadcn/ui/use-toast'
import { zodResolver } from '@hookform/resolvers/zod'
import { Meta } from '@storybook/react'
import { Textarea } from '@ui/components/shadcn/ui/textarea'
import { useForm } from 'react-hook-form'
import * as z from 'zod'
const meta: Meta = {
title: 'shadcn/Textarea',
component: Textarea,
}
export function Default() {
return <Textarea placeholder="Type your message here." />
}
export function TextareaDisabled() {
return <Textarea placeholder="Type your message here." disabled />
}
export function TextareaWithLabel() {
return (
<div className="grid w-full gap-1.5">
<Label htmlFor="message">Your message</Label>
<Textarea placeholder="Type your message here." id="message" />
</div>
)
}
export function TextareaWithText() {
return (
<div className="grid w-full gap-1.5">
<Label htmlFor="message-2">Your Message</Label>
<Textarea placeholder="Type your message here." id="message-2" />
<p className="text-sm text-muted-foreground">
Your message will be copied to the support team.
</p>
</div>
)
}
export function TextareaWithButton() {
return (
<div className="grid w-full gap-2">
<Textarea placeholder="Type your message here." />
<Button>Send message</Button>
</div>
)
}
export function TextareaForm() {
const FormSchema = z.object({
bio: z
.string()
.min(10, {
message: 'Bio must be at least 10 characters.',
})
.max(160, {
message: 'Bio must not be longer than 30 characters.',
}),
})
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us a little bit about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
You can <span>@mention</span> other users and organizations.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
export default meta
@@ -0,0 +1,104 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
import { Button } from '@ui/components/shadcn/ui/button'
import { ToastAction } from '@ui/components/shadcn/ui/toast'
import { useToast } from '@ui/components/shadcn/ui/use-toast'
const meta: Meta = {
title: 'shadcn/Toast',
}
export function Default() {
const { toast } = useToast()
return (
<Button
variant="outline"
onClick={() => {
toast({
description: 'Your message has been sent.',
})
}}
>
Show Toast
</Button>
)
}
export function MoreDescription() {
const { toast } = useToast()
return (
<Button
variant="outline"
onClick={() => {
toast({
title: 'Scheduled: Catch up ',
description: 'Friday, February 10, 2023 at 5:57 PM',
action: <ToastAction altText="Goto schedule to undo">Undo</ToastAction>,
})
}}
>
Add to calendar
</Button>
)
}
export function ToastWithTitle() {
const { toast } = useToast()
return (
<Button
variant="outline"
onClick={() => {
toast({
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
})
}}
>
Show Toast
</Button>
)
}
export function ToastWithAction() {
const { toast } = useToast()
return (
<Button
variant="outline"
onClick={() => {
toast({
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
action: <ToastAction altText="Try again">Try again</ToastAction>,
})
}}
>
Show Toast
</Button>
)
}
export function ToastDestructive() {
const { toast } = useToast()
return (
<Button
variant="outline"
onClick={() => {
toast({
variant: 'destructive',
title: 'Uh oh! Something went wrong.',
description: 'There was a problem with your request.',
action: <ToastAction altText="Try again">Try again</ToastAction>,
})
}}
>
Show Toast
</Button>
)
}
export default meta
@@ -0,0 +1,60 @@
import { Meta } from '@storybook/react'
import { Toggle } from '@ui/components/shadcn/ui/toggle'
import { Bold, Italic } from 'lucide-react'
import { Underline } from 'lucide-react'
const meta: Meta = {
title: 'shadcn/Toggle',
component: Toggle,
}
export function Default() {
return (
<Toggle aria-label="Toggle italic">
<Bold className="h-4 w-4" />
</Toggle>
)
}
export function ToggleOutline() {
return (
<Toggle variant="outline" aria-label="Toggle italic">
<Italic className="h-4 w-4" />
</Toggle>
)
}
export function ToggleWithText() {
return (
<Toggle aria-label="Toggle italic">
<Italic className="mr-2 h-4 w-4" />
Italic
</Toggle>
)
}
export function ToggleSm() {
return (
<Toggle size="sm" aria-label="Toggle italic">
<Italic className="h-4 w-4" />
</Toggle>
)
}
export function ToggleLg() {
return (
<Toggle size="lg" aria-label="Toggle italic">
<Italic className="h-4 w-4" />
</Toggle>
)
}
export function ToggleDisabled() {
return (
<Toggle aria-label="Toggle italic" disabled>
<Underline className="h-4 w-4" />
</Toggle>
)
}
export default meta
@@ -0,0 +1,30 @@
import { Meta } from '@storybook/react'
import { Button } from '@ui/components/shadcn/ui/button'
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@ui/components/shadcn/ui/tooltip'
const meta: Meta = {
title: 'shadcn/Tooltip',
component: Tooltip,
}
export function Default() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover</Button>
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}
export default meta
@@ -0,0 +1,27 @@
import { Meta } from '@storybook/react'
import { Card } from '@ui/components/shadcn/ui/card'
const meta: Meta = {
title: 'shadcn/Card',
component: Card,
}
export const Default = () => {
return (
<div>
<div></div>
</div>
)
}
export const CardWithForm = () => {
return (
<div>
<div></div>
</div>
)
}
CardWithForm.storyName = 'Card with form custom name'
export default meta