Files
lti-web-client/src/components/Modal.tsx
T

105 lines
2.2 KiB
TypeScript

'use client';
import {
ReactNode,
RefObject,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import { cn } from '@/lib/helper';
export const useModal = (isNestingModal = false) => {
const ref = useRef<HTMLDialogElement>(null);
const [open, setOpen] = useState(false);
const openModal = useCallback(() => {
if (!ref.current) return;
if (isNestingModal) {
ref.current.showModal();
} else {
ref.current.show();
}
setOpen(true);
}, [isNestingModal]);
const closeModal = useCallback(() => {
if (!ref.current) return;
ref.current.close();
setOpen(false);
}, []);
const toggle = useCallback(() => {
if (open) {
closeModal();
} else {
openModal();
}
}, [open, closeModal, openModal]);
useEffect(() => {
const dialog = ref.current;
if (!dialog) return;
const handleClose = () => setOpen(false);
dialog.addEventListener('close', handleClose);
return () => {
dialog.removeEventListener('close', handleClose);
};
}, []);
return { ref, open, openModal, closeModal, toggle } as const;
};
interface ModalProps {
ref: RefObject<HTMLDialogElement | null>;
children?: ReactNode;
closeOnBackdrop?: boolean;
onBackdropClick?: () => void;
position?: 'top' | 'middle' | 'bottom' | 'start' | 'end';
className?: {
modal?: string;
modalBox?: string;
};
}
const Modal = ({
ref,
children,
closeOnBackdrop,
onBackdropClick,
position = 'middle',
className,
}: ModalProps) => {
const handleBackdropClick = (e: React.MouseEvent<HTMLDialogElement>) => {
if (closeOnBackdrop && e.target === ref.current) {
onBackdropClick?.();
ref.current?.close();
}
};
return (
<dialog
ref={ref}
className={cn(
'modal',
{
'modal-top': position === 'top',
'modal-middle': position === 'middle',
'modal-bottom': position === 'bottom',
'modal-start': position === 'start',
'modal-end': position === 'end',
},
className?.modal
)}
onClick={handleBackdropClick}
>
<div className={cn('modal-box', className?.modalBox)}>{children}</div>
</dialog>
);
};
export default Modal;