import React, { useState, useRef, useCallback } from 'react'; export type ModalId = string | number; export interface UseModalReturn { open: (id: ModalId) => void; close: () => void; currentOpen: ModalId | null; registerModal: ( id: ModalId, options?: { clickOutside?: boolean; closeOnEscape?: boolean; } ) => { open: boolean; role: string; id: string; 'aria-modal': boolean; 'aria-labelledby': string; ref?: React.RefObject; }; registerAction: ( id: ModalId, type: 'open' | 'close', options?: { variant?: string; color?: string; } ) => { onClick: () => void; ref?: React.RefObject; }; } export const useModal = (): UseModalReturn => { const [currentOpen, setCurrentOpen] = useState(null); const modalsRef = useRef< Map> >(new Map()); const buttonsRef = useRef< Map> >(new Map()); const open = useCallback((id: ModalId) => { setCurrentOpen(id); }, []); const close = useCallback(() => { setCurrentOpen(null); }, []); const registerModal = useCallback( (id, options) => { const modalId = `modal-${id}`; if (!modalsRef.current.get(id)?.current) { modalsRef.current.set(id, React.createRef()); if (options?.clickOutside && currentOpen === id) { const handleOutsideClick = (event: MouseEvent) => { const modalContainer = document.getElementById(modalId); if (modalContainer === event.target) { close(); } }; window.addEventListener('mousedown', handleOutsideClick); } } return { open: currentOpen === id, role: 'dialog', id: modalId, onEscape: options?.closeOnEscape ? close : null, 'aria-modal': true, 'aria-labelledby': `modal-heading-${id}`, ref: modalsRef.current.get(id as string), }; }, [currentOpen] ); const registerAction = useCallback( (id: ModalId, type: 'open' | 'close', options) => { if (!buttonsRef.current.has(id)) { buttonsRef.current.set(id, React.createRef()); } return { onClick: () => (type === 'open' ? open(id) : close()), ref: buttonsRef.current.get(id), ...options, }; }, [currentOpen] ); return { open, close, currentOpen, registerAction, registerModal, }; };