'use client' import { ComponentProps, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { twMerge } from 'tailwind-merge' import { classNames } from './classNames' export interface ModalWindowProps extends ComponentProps<'div'> { disableClosing?: boolean isOpen: boolean onClose: () => void } export function ModalWindow({ children, className, disableClosing, isOpen, onClose, ...otherProps }: ModalWindowProps) { const [isClient, setIsClient] = useState(false) const [modalState, setModalState] = useState< 'opening' | 'open' | 'closing' | 'closed' >('closed') const windowContentsContainerRef = useRef(null) function handleTransitionEnd() { if (modalState === 'closing') { setModalState('closed') onClose() } if (modalState === 'opening') { setModalState('open') } } function handleClose() { setModalState('closing') } function focusFirstElement() { const windowContentsContainer = windowContentsContainerRef.current if (!windowContentsContainer) { return } const firstFocusableElement = windowContentsContainer.querySelector( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', ) as HTMLElement if (firstFocusableElement) { firstFocusableElement.focus() } } useEffect(() => { setIsClient(true) }, []) useEffect(() => { if (isOpen) { setModalState('opening') focusFirstElement() } else { setModalState('closing') } }, [isOpen]) useEffect(() => { if (!disableClosing && isOpen) { const handleEscape = (event: KeyboardEvent) => { if (event.key === 'Escape') { handleClose() } } window.addEventListener('keydown', handleEscape) return () => window.removeEventListener('keydown', handleEscape) } }, [disableClosing, isOpen, onClose]) if (!isClient) { return null } return createPortal( <>
{children}
, document.body, ) } ModalWindow.Title = function ModalWindowTitle({ children, className, }: ComponentProps<'header'>) { return
{children}
} ModalWindow.Body = function ModalWindowBody({ children, className, }: ComponentProps<'main'>) { return
{children}
} ModalWindow.Buttons = function ModalWindowBody({ children, className, }: ComponentProps<'div'>) { return (
{children}
) }