Skip to content

Commit

Permalink
add drawer component
Browse files Browse the repository at this point in the history
  • Loading branch information
andrecasal committed Nov 8, 2023
1 parent 7701e09 commit 0fcbeb1
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 47 deletions.
2 changes: 1 addition & 1 deletion app/routes/_marketing+/ui+/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const SideMenu = ({ setIsOpen }: { setIsOpen?: (a: boolean) => void }) => {
items: [
{ name: 'Dialog', href: '/ui/modals/dialog', done: true },
{ name: 'Alert Dialog', href: '/ui/modals/alert-dialog', done: true },
{ name: 'Drawer', href: '/ui/modals/drawer', done: false },
{ name: 'Drawer', href: '/ui/modals/drawer', done: true },
{ name: 'Dropdown Menu', href: '/ui/modals/dropdown-menu', done: false },
{ name: 'Upload Dialog', href: '/ui/modals/upload-dialog', done: false },
{ name: 'Search Dialog', href: '/ui/modals/search-dialog', done: false },
Expand Down
10 changes: 5 additions & 5 deletions app/routes/_marketing+/ui+/components/modals/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger

const AlertDialogOverlay = forwardRef<ElementRef<typeof AlertDialogPrimitive.Overlay>, ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Overlay ref={ref} className={cn('overlay fixed inset-0 z-50', className)} {...props} />
<AlertDialogPrimitive.Overlay ref={ref} className={cn('alert-dialog-overlay fixed inset-0 z-50', className)} {...props} />
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName

const AlertDialogContent = forwardRef<ElementRef<typeof AlertDialogPrimitive.Content>, ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>>(
({ className, children, ...props }, ref) => (
<AlertDialogPrimitive.Portal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content ref={ref} className={cn('dialog fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]', className)} {...props}>
<AlertDialogPrimitive.Content ref={ref} className={cn('alert-dialog fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%]', className)} {...props}>
{children}
<AlertDialogPrimitive.Cancel className="dialog-x-button">
<AlertDialogPrimitive.Cancel className="alert-dialog-x-button">
<Icon name="x-mark" />
<VisuallyHidden>Close</VisuallyHidden>
</AlertDialogPrimitive.Cancel>
Expand All @@ -30,12 +30,12 @@ const AlertDialogContent = forwardRef<ElementRef<typeof AlertDialogPrimitive.Con
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName

const AlertDialogTitle = forwardRef<ElementRef<typeof AlertDialogPrimitive.Title>, ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>>(({ className, ...props }, ref) => (
<AlertDialogPrimitive.Title ref={ref} className={cn('text-lg font-semibold leading-none tracking-tight', className)} {...props} />
<AlertDialogPrimitive.Title ref={ref} className={cn('text-size-lg font-semibold', className)} {...props} />
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName

const AlertDialogDescription = forwardRef<ElementRef<typeof AlertDialogPrimitive.Description>, ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>>(
({ className, ...props }, ref) => <AlertDialogPrimitive.Description ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} />,
({ className, ...props }, ref) => <AlertDialogPrimitive.Description ref={ref} className={cn('text-size-sm text-muted-500', className)} {...props} />,
)
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName

Expand Down
6 changes: 3 additions & 3 deletions app/routes/_marketing+/ui+/components/modals/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger

const DialogOverlay = forwardRef<ElementRef<typeof DialogPrimitive.Overlay>, ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay ref={ref} className={cn('overlay fixed inset-0 z-50', className)} {...props} />
<DialogPrimitive.Overlay ref={ref} className={cn('dialog-overlay fixed inset-0 z-50', className)} {...props} />
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName

Expand All @@ -28,12 +28,12 @@ const DialogContent = forwardRef<ElementRef<typeof DialogPrimitive.Content>, Com
DialogContent.displayName = DialogPrimitive.Content.displayName

const DialogTitle = forwardRef<ElementRef<typeof DialogPrimitive.Title>, ComponentPropsWithoutRef<typeof DialogPrimitive.Title>>(({ className, ...props }, ref) => (
<DialogPrimitive.Title ref={ref} className={cn('text-lg font-semibold leading-none tracking-tight', className)} {...props} />
<DialogPrimitive.Title ref={ref} className={cn('text-size-lg font-semibold', className)} {...props} />
))
DialogTitle.displayName = DialogPrimitive.Title.displayName

const DialogDescription = forwardRef<ElementRef<typeof DialogPrimitive.Description>, ComponentPropsWithoutRef<typeof DialogPrimitive.Description>>(({ className, ...props }, ref) => (
<DialogPrimitive.Description ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} />
<DialogPrimitive.Description ref={ref} className={cn('text-size-sm text-muted-500', className)} {...props} />
))
DialogDescription.displayName = DialogPrimitive.Description.displayName

Expand Down
61 changes: 61 additions & 0 deletions app/routes/_marketing+/ui+/components/modals/drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as DialogPrimitive from '@radix-ui/react-dialog'
import { cva, type VariantProps } from 'class-variance-authority'
import { type ComponentPropsWithoutRef, type ElementRef, forwardRef } from 'react'
import { Icon } from '~/components/ui/icon.tsx'
import { cn } from '~/utils/tailwind-merge.ts'
import { VisuallyHidden } from '../layout/visually-hidden.tsx'

const Drawer = DialogPrimitive.Root

const DrawerTrigger = DialogPrimitive.Trigger

const DrawerClose = DialogPrimitive.Close

const DrawerPortal = DialogPrimitive.Portal

const DrawerOverlay = forwardRef<ElementRef<typeof DialogPrimitive.Overlay>, ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay className={cn('drawer-overlay fixed inset-0 z-50', className)} {...props} ref={ref} />
))
DrawerOverlay.displayName = DialogPrimitive.Overlay.displayName

const drawerVariants = cva('fixed z-50 drawer', {
variants: {
side: {
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
bottom: 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right: 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
})

interface DrawerContentProps extends ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, VariantProps<typeof drawerVariants> {}

const DrawerContent = forwardRef<ElementRef<typeof DialogPrimitive.Content>, DrawerContentProps>(({ side = 'right', className, children, ...props }, ref) => (
<DrawerPortal>
<DrawerOverlay />
<DialogPrimitive.Content ref={ref} className={cn(drawerVariants({ side }), className)} {...props}>
{children}
<DialogPrimitive.Close className="drawer-x-button">
<Icon name="x-mark" />
<VisuallyHidden>Close</VisuallyHidden>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DrawerPortal>
))
DrawerContent.displayName = DialogPrimitive.Content.displayName

const DrawerTitle = forwardRef<ElementRef<typeof DialogPrimitive.Title>, ComponentPropsWithoutRef<typeof DialogPrimitive.Title>>(({ className, ...props }, ref) => (
<DialogPrimitive.Title ref={ref} className={cn('text-size-lg font-semibold', className)} {...props} />
))
DrawerTitle.displayName = DialogPrimitive.Title.displayName

const DrawerDescription = forwardRef<ElementRef<typeof DialogPrimitive.Description>, ComponentPropsWithoutRef<typeof DialogPrimitive.Description>>(({ className, ...props }, ref) => (
<DialogPrimitive.Description ref={ref} className={cn('text-size-sm text-muted-500', className)} {...props} />
))
DrawerDescription.displayName = DialogPrimitive.Description.displayName

export { Drawer, DrawerTrigger, DrawerContent, DrawerTitle, DrawerDescription, DrawerClose }
27 changes: 11 additions & 16 deletions app/routes/_marketing+/ui+/modals+/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { Text } from '~/routes/_marketing+/ui+/components/typography/text.tsx'
import { Code } from '../components/typography/code.tsx'
import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogTitle, AlertDialogCancel, AlertDialogAction } from '../components/modals/alert-dialog.tsx'
import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogTitle, AlertDialogCancel, AlertDialogAction, AlertDialogDescription } from '../components/modals/alert-dialog.tsx'
import { Button } from '~/components/ui/button.tsx'
import { Flex } from '../components/layout/flex.tsx'
import { Description, Features, Source, readSource, type componentProps, Usage, Keyboard, Styling, PartsTitle, Part } from '../sections/sections.tsx'
Expand Down Expand Up @@ -84,8 +84,8 @@ const component: componentProps = {
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<Text className="mt-4">This action cannot be undone. This will permanently delete your account and all your data.</Text>
<form>
<AlertDialogDescription>Deleting your account cannot be undone. This will permanently delete your account and all your data.</AlertDialogDescription>
<form className="mt-10">
<Flex justify="end" gap="6">
<AlertDialogCancel asChild>
<Button variant="secondary">Cancel</Button>
Expand All @@ -99,22 +99,17 @@ const component: componentProps = {
</form>
</AlertDialogContent>
</AlertDialog>`,
styling: `/* ### Layout ### */
styling: `/* ### Modals ### */
@layer components {
...
/* Modal Overlay */
.overlay {
/* Alert Dialog */
.alert-dialog-overlay {
@apply bg-background/80 backdrop-blur-sm;
}
/* Dialog */
.dialog {
.alert-dialog {
@apply grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full;
}
.dialog-x-button {
@apply absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-foreground data-[state=open]:text-muted-500;
.alert-dialog-x-button {
@apply absolute right-4 top-4 min-h-tap min-w-tap rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-foreground data-[state=open]:text-muted-500;
}
}`,
}
Expand Down Expand Up @@ -157,8 +152,8 @@ const AlertDialogRoute = () => {
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<Text className="mt-4">This action cannot be undone. This will permanently delete your account and all your data.</Text>
<form>
<AlertDialogDescription>Deleting your account cannot be undone. This will permanently delete your account and all your data.</AlertDialogDescription>
<form className="mt-10">
<Flex justify="end" gap="6">
<AlertDialogCancel asChild>
<Button variant="secondary">Cancel</Button>
Expand Down
45 changes: 27 additions & 18 deletions app/routes/_marketing+/ui+/modals+/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { json } from '@remix-run/node'
import { useLoaderData } from '@remix-run/react'
import { Text } from '~/routes/_marketing+/ui+/components/typography/text.tsx'
import { Code } from '../components/typography/code.tsx'
import { Dialog, DialogClose, DialogContent, DialogTitle, DialogTrigger } from '../components/modals/dialog.tsx'
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from '../components/modals/dialog.tsx'
import { Button } from '~/components/ui/button.tsx'
import { Input } from '~/components/ui/input.tsx'
import { Flex } from '../components/layout/flex.tsx'
import { Description, Features, Source, readSource, type componentProps, Parts, Usage, Keyboard, Styling } from '../sections/sections.tsx'
import { Description, Features, Source, readSource, type componentProps, Usage, Keyboard, Styling, PartsTitle, Part } from '../sections/sections.tsx'

const component: componentProps = {
name: 'Dialog',
Expand Down Expand Up @@ -68,13 +68,14 @@ const component: componentProps = {
props: [{ name: 'asChild', type: 'boolean', default: 'false' }],
},
{
name: 'DialogClose',
description: 'The button that closes the dialog.',
name: 'DialogDescription',
description: 'An optional accessible description to be announced when the dialog is opened.',
props: [{ name: 'asChild', type: 'boolean', default: 'false' }],
},
{
name: 'DialogAction',
name: 'DialogClose',
description: 'The button that closes the dialog.',
props: [{ name: 'asChild', type: 'boolean', default: 'false' }],
},
],
usage: `<Dialog>
Expand Down Expand Up @@ -108,22 +109,17 @@ const component: componentProps = {
</form>
</DialogContent>
</Dialog>`,
styling: `/* ### Layout ### */
styling: `/* ### Modals ### */
@layer components {
...
/* Modal Overlay */
.overlay {
/* Dialog */
.dialog-overlay {
@apply bg-background/80 backdrop-blur-sm;
}
/* Dialog */
.dialog {
@apply grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full;
}
.dialog-close-button {
@apply absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-foreground data-[state=open]:text-muted-500;
.dialog-x-button {
@apply absolute right-4 top-4 min-h-tap min-w-tap rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-foreground data-[state=open]:text-muted-500;
}
}`,
}
Expand All @@ -139,16 +135,29 @@ const DialogRoute = () => {
<Description name={name} description={description} />
<Features features={features} />
<Keyboard keyboard={keyboard} />
<Parts parts={parts} />
{/* <Parts parts={parts} /> */}
<PartsTitle />
<Part {...parts.find(({ name }) => name === 'Dialog')!} />
<Part {...parts.find(({ name }) => name === 'DialogTrigger')!} />
<Part {...parts.find(({ name }) => name === 'DialogContent')!} />
<Part {...parts.find(({ name }) => name === 'DialogTitle')!} />
<Text className="mt-4">
If you want to hide the title, wrap it with <Code>{'<VisuallyHidden asChild>'}</Code>.
</Text>
<Part {...parts.find(({ name }) => name === 'DialogDescription')!} />
<Text className="mt-4">
If you want to hide the description, wrap it with <Code>{'<VisuallyHidden asChild>'}</Code>.
</Text>
<Part {...parts.find(({ name }) => name === 'DialogClose')!} />
<Usage usage={usage} shortName={shortName} />
<Dialog>
<DialogTrigger asChild>
<Button variant="secondary">Open dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogTitle>Edit profile</DialogTitle>
<Text className="mt-4">Make changes to your profile here. Click save when you're done.</Text>
<form>
<DialogDescription className="mt-4">Make changes to your profile here. Click save when you're done.</DialogDescription>
<form className="mt-10">
<fieldset className="mb-5 flex items-center gap-5">
<label htmlFor="name" className="w-48 text-right">
Name
Expand Down
Loading

0 comments on commit 0fcbeb1

Please sign in to comment.