feat(sheet): add sheet component (#50)

This commit is contained in:
Damián Ricobelli
2023-03-02 07:57:44 -03:00
committed by GitHub
parent 1612538ea6
commit 773e4344b7
7 changed files with 521 additions and 0 deletions

View File

@@ -39,6 +39,9 @@ import { RadioGroupDemo } from "@/components/examples/radio-group/demo"
import { ScrollAreaDemo } from "@/components/examples/scroll-area/demo"
import { SelectDemo } from "@/components/examples/select/demo"
import { SeparatorDemo } from "@/components/examples/separator/demo"
import { SheetDemo } from "@/components/examples/sheet/demo"
import { SheetPosition } from "@/components/examples/sheet/position"
import { SheetSize } from "@/components/examples/sheet/size"
import { SliderDemo } from "@/components/examples/slider/demo"
import { SwitchDemo } from "@/components/examples/switch/demo"
import { TabsDemo } from "@/components/examples/tabs/demo"
@@ -116,6 +119,9 @@ export const examples = {
ScrollAreaDemo,
SelectDemo,
SeparatorDemo,
SheetDemo,
SheetSize,
SheetPosition,
SliderDemo,
SwitchDemo,
TabsDemo,

View File

@@ -0,0 +1,49 @@
"use client"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
export function SheetDemo() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open</Button>
</SheetTrigger>
<SheetContent position="right" size="sm">
<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>
<Button type="submit">Save changes</Button>
</SheetFooter>
</SheetContent>
</Sheet>
)
}

View File

@@ -0,0 +1,75 @@
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
const SHEET_POSITIONS = ["top", "right", "bottom", "left"] as const
type SheetPosition = typeof SHEET_POSITIONS[number]
export function SheetPosition() {
const [position, setPosition] = useState<SheetPosition>("right")
return (
<div className="flex flex-col space-y-8">
<RadioGroup
defaultValue={position}
onValueChange={(value) => setPosition(value as SheetPosition)}
>
<div className="grid grid-cols-2 gap-2">
{SHEET_POSITIONS.map((position, index) => (
<div
key={`${position}-${index}`}
className="flex items-center space-x-2"
>
<RadioGroupItem value={position} id={position} />
<Label htmlFor={position}>{position}</Label>
</div>
))}
</div>
</RadioGroup>
<Sheet>
<SheetTrigger asChild>
<Button>Open {position} sheet</Button>
</SheetTrigger>
<SheetContent position={position} size="content">
<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>
<Button type="submit">Save changes</Button>
</SheetFooter>
</SheetContent>
</Sheet>
</div>
)
}

View File

@@ -0,0 +1,75 @@
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
const SHEET_SIZES = ["sm", "default", "lg", "xl", "full", "content"] as const
type SheetSize = typeof SHEET_SIZES[number]
export function SheetSize() {
const [size, setSize] = useState<SheetSize>("default")
return (
<div className="flex flex-col space-y-8">
<RadioGroup
defaultValue={size}
onValueChange={(value) => setSize(value as SheetSize)}
>
<div className="grid grid-cols-2 gap-2">
{SHEET_SIZES.map((size, index) => (
<div
key={`${size}-${index}`}
className="flex items-center space-x-2"
>
<RadioGroupItem value={size} id={size} />
<Label htmlFor={size}>{size}</Label>
</div>
))}
</div>
</RadioGroup>
<Sheet>
<SheetTrigger asChild>
<Button>Open {size} sheet</Button>
</SheetTrigger>
<SheetContent position="right" size={size}>
<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>
<Button type="submit">Save changes</Button>
</SheetFooter>
</SheetContent>
</Sheet>
</div>
)
}

View File

@@ -0,0 +1,234 @@
"use client"
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { VariantProps, cva } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Sheet = SheetPrimitive.Root
const SheetTrigger = SheetPrimitive.Trigger
const portalVariants = cva("fixed inset-0 z-50 flex", {
variants: {
position: {
top: "items-start",
bottom: "items-end",
left: "justify-start",
right: "justify-end",
},
},
defaultVariants: { position: "right" },
})
interface SheetPortalProps
extends SheetPrimitive.DialogPortalProps,
VariantProps<typeof portalVariants> {}
const SheetPortal = ({
position,
className,
children,
...props
}: SheetPortalProps) => (
<SheetPrimitive.Portal className={cn(className)} {...props}>
<div className={portalVariants({ position })}>{children}</div>
</SheetPrimitive.Portal>
)
SheetPortal.displayName = SheetPrimitive.Portal.displayName
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, children, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=open]:fade-in data-[state=closed]:fade-out",
className
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
const sheetVariants = cva(
"fixed z-50 scale-100 gap-4 bg-white p-6 opacity-100 dark:bg-slate-900",
{
variants: {
position: {
top: "animate-in slide-in-from-top w-full duration-300",
bottom: "animate-in slide-in-from-bottom w-full duration-300",
left: "animate-in slide-in-from-left h-full duration-300",
right: "animate-in slide-in-from-right h-full duration-300",
},
size: {
content: "",
default: "",
sm: "",
lg: "",
xl: "",
full: "",
},
},
compoundVariants: [
{
position: ["top", "bottom"],
size: "content",
class: "max-h-screen",
},
{
position: ["top", "bottom"],
size: "default",
class: "h-1/3",
},
{
position: ["top", "bottom"],
size: "sm",
class: "h-1/4",
},
{
position: ["top", "bottom"],
size: "lg",
class: "h-1/2",
},
{
position: ["top", "bottom"],
size: "xl",
class: "h-5/6",
},
{
position: ["top", "bottom"],
size: "full",
class: "h-screen",
},
{
position: ["right", "left"],
size: "content",
class: "max-w-screen",
},
{
position: ["right", "left"],
size: "default",
class: "w-1/3",
},
{
position: ["right", "left"],
size: "sm",
class: "w-1/4",
},
{
position: ["right", "left"],
size: "lg",
class: "w-1/2",
},
{
position: ["right", "left"],
size: "xl",
class: "w-5/6",
},
{
position: ["right", "left"],
size: "full",
class: "w-screen",
},
],
defaultVariants: {
position: "right",
size: "default",
},
}
)
export interface DialogContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
DialogContentProps
>(({ position, size, className, children, ...props }, ref) => (
<SheetPortal position={position}>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ position, size }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=open]:bg-slate-800">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
SheetHeader.displayName = "SheetHeader"
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
SheetFooter.displayName = "SheetFooter"
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold text-slate-900",
"dark:text-slate-50",
className
)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-slate-500", "dark:text-slate-400", className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName
export {
Sheet,
SheetTrigger,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}

View File

@@ -177,6 +177,11 @@ export const docsConfig: DocsConfig = {
href: "/docs/primitives/separator",
items: [],
},
{
title: "Sheet",
href: "/docs/primitives/sheet",
items: [],
},
{
title: "Slider",
href: "/docs/primitives/slider",

View File

@@ -0,0 +1,77 @@
---
title: Sheet
description: Extension of Dialog component that displays content that complements the main content of the screen, or a list of actions that affect the main content of the screen.
radix:
link: https://www.radix-ui.com/docs/primitives/components/dialog
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
---
<ComponentExample src="/components/examples/sheet/demo.tsx">
<SheetDemo />
</ComponentExample>
## Installation
1. Install the `@radix-ui/react-dialog` component from radix-ui:
```bash
npm install @radix-ui/react-dialog
```
2. Copy and paste the following code into your project.
<ComponentSource src="/components/ui/sheet.tsx" />
<Callout>
This is the `<Sheet />` primitive. You can place it in a file at `components/ui/sheet.tsx`.
</Callout>
## Usage
```tsx
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/dialog"
```
```tsx
<Sheet>
<SheetTrigger>Open</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Are you sure absolutely sure?</SheetTitle>
<SheetDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
```
## Examples
### Position
Pass the `position` property to `<SheetContent />` to indicate the edge of the screen where the component will appear. The values can be `top`, `right`, `bottom` or `left`.
<ComponentExample src="/components/examples/sheet/position.tsx">
<SheetPosition />
</ComponentExample>
---
### Size
Pass the `size` property to `<SheetContent />` if you need to adjust the size of the sheet. The values can be `sm`, `default`, `lg`, `xl`, `full` or `content`.
<ComponentExample src="/components/examples/sheet/size.tsx">
<SheetSize />
</ComponentExample>