'use client'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { VisuallyHidden } from '@radix-ui/react-visually-hidden'; import { cva } from 'class-variance-authority'; import * as React from 'react'; import { CloseButton } from '../button'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const ModalAnatomy = defineStyleAnatomy({ overlay: cva([ 'UI-Modal__overlay', 'fixed inset-0 z-50 bg-black/80', 'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', // "overflow-y-auto p-0 md:p-4 grid place-items-center", ]), content: cva([ 'UI-Modal__content', 'z-50 grid relative w-full w-full shadow-xl border border-[rgb(255_255_255_/_5%)] max-w-lg gap-4 bg-[--background] p-6 shadow-xl 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=open]:slide-in-from-top-[40%] data-[state=closed]:slide-out-to-bottom-[40%]", // "data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]", 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95', // process.env.NEXT_PUBLIC_PLATFORM === "desktop" && "mt-10", // process.env.NEXT_PUBLIC_PLATFORM === "desktop" && "select-none", 'sm:rounded-xl', ]), close: cva(['UI-Modal__close', 'absolute right-4 top-4 !mt-0']), header: cva([ 'UI-Modal__header', 'flex flex-col space-y-1.5 text-center sm:text-left', ]), footer: cva([ 'UI-Modal__footer', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', ]), title: cva([ 'UI-Modal__title', 'text-xl font-semibold leading-none tracking-tight', ]), description: cva(['UI-Modal__description', 'text-sm text-[--muted]']), }); /* ------------------------------------------------------------------------------------------------- * Modal * -----------------------------------------------------------------------------------------------*/ export type ModalProps = Omit< React.ComponentPropsWithoutRef, 'modal' > & Pick< React.ComponentPropsWithoutRef, | 'onOpenAutoFocus' | 'onCloseAutoFocus' | 'onEscapeKeyDown' | 'onPointerDownCapture' | 'onInteractOutside' > & ComponentAnatomy & { /** * Interaction with outside elements will be enabled and other elements will be visible to screen readers. */ allowOutsideInteraction?: boolean; /** * The button that opens the modal */ trigger?: React.ReactElement; /** * Title of the modal */ title?: React.ReactNode; /** * An optional accessible description to be announced when the dialog is opened. */ description?: React.ReactNode; /** * Footer of the modal */ footer?: React.ReactNode; /** * Optional replacement for the default close button */ closeButton?: React.ReactElement; /** * Whether to hide the close button */ hideCloseButton?: boolean; }; export function Modal(props: ModalProps) { const { allowOutsideInteraction = false, trigger, title, footer, description, children, closeButton, overlayClass, contentClass, closeClass, headerClass, footerClass, titleClass, descriptionClass, hideCloseButton, // Content onOpenAutoFocus, onCloseAutoFocus, onEscapeKeyDown, onPointerDownCapture, onInteractOutside, ...rest } = props; return ( {trigger && ( {trigger} )}
{!title && !description ? ( Dialog ) : (
{title} {description && ( {description} )}
)} {children} {footer && (
{footer}
)} {!hideCloseButton && ( {closeButton ? closeButton : } )}
); } Modal.displayName = 'Modal';