From 0fcbeb19db6b76038c0394ee3d335ffe6c689a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Casal?= Date: Wed, 8 Nov 2023 13:01:30 +0000 Subject: [PATCH] add drawer component --- app/routes/_marketing+/ui+/_layout.tsx | 2 +- .../ui+/components/modals/alert-dialog.tsx | 10 +- .../ui+/components/modals/dialog.tsx | 6 +- .../ui+/components/modals/drawer.tsx | 61 ++++++ .../_marketing+/ui+/modals+/alert-dialog.tsx | 27 ++- app/routes/_marketing+/ui+/modals+/dialog.tsx | 45 +++-- app/routes/_marketing+/ui+/modals+/drawer.tsx | 183 ++++++++++++++++++ app/styles/tailwind.css | 29 ++- 8 files changed, 316 insertions(+), 47 deletions(-) create mode 100644 app/routes/_marketing+/ui+/components/modals/drawer.tsx create mode 100644 app/routes/_marketing+/ui+/modals+/drawer.tsx diff --git a/app/routes/_marketing+/ui+/_layout.tsx b/app/routes/_marketing+/ui+/_layout.tsx index b3e747a..9dfe81b 100644 --- a/app/routes/_marketing+/ui+/_layout.tsx +++ b/app/routes/_marketing+/ui+/_layout.tsx @@ -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 }, diff --git a/app/routes/_marketing+/ui+/components/modals/alert-dialog.tsx b/app/routes/_marketing+/ui+/components/modals/alert-dialog.tsx index 909b319..2037951 100644 --- a/app/routes/_marketing+/ui+/components/modals/alert-dialog.tsx +++ b/app/routes/_marketing+/ui+/components/modals/alert-dialog.tsx @@ -9,7 +9,7 @@ const AlertDialog = AlertDialogPrimitive.Root const AlertDialogTrigger = AlertDialogPrimitive.Trigger const AlertDialogOverlay = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( - + )) AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName @@ -17,9 +17,9 @@ const AlertDialogContent = forwardRef ( - + {children} - + Close @@ -30,12 +30,12 @@ const AlertDialogContent = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( - + )) AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName const AlertDialogDescription = forwardRef, ComponentPropsWithoutRef>( - ({ className, ...props }, ref) => , + ({ className, ...props }, ref) => , ) AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName diff --git a/app/routes/_marketing+/ui+/components/modals/dialog.tsx b/app/routes/_marketing+/ui+/components/modals/dialog.tsx index b23a4fd..12e645b 100644 --- a/app/routes/_marketing+/ui+/components/modals/dialog.tsx +++ b/app/routes/_marketing+/ui+/components/modals/dialog.tsx @@ -9,7 +9,7 @@ const Dialog = DialogPrimitive.Root const DialogTrigger = DialogPrimitive.Trigger const DialogOverlay = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( - + )) DialogOverlay.displayName = DialogPrimitive.Overlay.displayName @@ -28,12 +28,12 @@ const DialogContent = forwardRef, Com DialogContent.displayName = DialogPrimitive.Content.displayName const DialogTitle = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( - + )) DialogTitle.displayName = DialogPrimitive.Title.displayName const DialogDescription = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( - + )) DialogDescription.displayName = DialogPrimitive.Description.displayName diff --git a/app/routes/_marketing+/ui+/components/modals/drawer.tsx b/app/routes/_marketing+/ui+/components/modals/drawer.tsx new file mode 100644 index 0000000..0ed9d13 --- /dev/null +++ b/app/routes/_marketing+/ui+/components/modals/drawer.tsx @@ -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, ComponentPropsWithoutRef>(({ className, ...props }, 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, VariantProps {} + +const DrawerContent = forwardRef, DrawerContentProps>(({ side = 'right', className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DrawerContent.displayName = DialogPrimitive.Content.displayName + +const DrawerTitle = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( + +)) +DrawerTitle.displayName = DialogPrimitive.Title.displayName + +const DrawerDescription = forwardRef, ComponentPropsWithoutRef>(({ className, ...props }, ref) => ( + +)) +DrawerDescription.displayName = DialogPrimitive.Description.displayName + +export { Drawer, DrawerTrigger, DrawerContent, DrawerTitle, DrawerDescription, DrawerClose } diff --git a/app/routes/_marketing+/ui+/modals+/alert-dialog.tsx b/app/routes/_marketing+/ui+/modals+/alert-dialog.tsx index b62afa2..0ddc750 100644 --- a/app/routes/_marketing+/ui+/modals+/alert-dialog.tsx +++ b/app/routes/_marketing+/ui+/modals+/alert-dialog.tsx @@ -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' @@ -84,8 +84,8 @@ const component: componentProps = { Are you absolutely sure? - This action cannot be undone. This will permanently delete your account and all your data. -
+ Deleting your account cannot be undone. This will permanently delete your account and all your data. + @@ -99,22 +99,17 @@ const component: componentProps = {
`, - 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; } }`, } @@ -157,8 +152,8 @@ const AlertDialogRoute = () => { Are you absolutely sure? - This action cannot be undone. This will permanently delete your account and all your data. -
+ Deleting your account cannot be undone. This will permanently delete your account and all your data. + diff --git a/app/routes/_marketing+/ui+/modals+/dialog.tsx b/app/routes/_marketing+/ui+/modals+/dialog.tsx index a96bfc1..15201ce 100644 --- a/app/routes/_marketing+/ui+/modals+/dialog.tsx +++ b/app/routes/_marketing+/ui+/modals+/dialog.tsx @@ -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', @@ -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: ` @@ -108,22 +109,17 @@ const component: componentProps = { `, - 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; } }`, } @@ -139,7 +135,20 @@ const DialogRoute = () => { - + {/* */} + + name === 'Dialog')!} /> + name === 'DialogTrigger')!} /> + name === 'DialogContent')!} /> + name === 'DialogTitle')!} /> + + If you want to hide the title, wrap it with {''}. + + name === 'DialogDescription')!} /> + + If you want to hide the description, wrap it with {''}. + + name === 'DialogClose')!} /> @@ -147,8 +156,8 @@ const DialogRoute = () => { Edit profile - Make changes to your profile here. Click save when you're done. -
+ Make changes to your profile here. Click save when you're done. +