59 lines
1.4 KiB
TypeScript
59 lines
1.4 KiB
TypeScript
import { useRef, useEffect, useCallback } from 'react';
|
|
|
|
interface UseDialogOptions {
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function useDialog(isOpen: boolean, options: UseDialogOptions) {
|
|
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
const triggerRef = useRef<Element | null>(null);
|
|
|
|
const close = useCallback(() => {
|
|
dialogRef.current?.close();
|
|
options.onClose();
|
|
}, [options.onClose]);
|
|
|
|
useEffect(() => {
|
|
const dialog = dialogRef.current;
|
|
if (!dialog) return;
|
|
|
|
if (isOpen) {
|
|
triggerRef.current = document.activeElement;
|
|
if (!dialog.open) {
|
|
dialog.showModal();
|
|
}
|
|
} else {
|
|
if (dialog.open) {
|
|
dialog.close();
|
|
}
|
|
// Restore focus to trigger
|
|
if (triggerRef.current instanceof HTMLElement) {
|
|
triggerRef.current.focus();
|
|
}
|
|
}
|
|
}, [isOpen]);
|
|
|
|
// Handle native cancel event (Escape key)
|
|
useEffect(() => {
|
|
const dialog = dialogRef.current;
|
|
if (!dialog) return;
|
|
|
|
const handleCancel = (e: Event) => {
|
|
e.preventDefault();
|
|
close();
|
|
};
|
|
|
|
dialog.addEventListener('cancel', handleCancel);
|
|
return () => dialog.removeEventListener('cancel', handleCancel);
|
|
}, [close]);
|
|
|
|
// Handle backdrop click
|
|
const handleBackdropClick = useCallback((e: React.MouseEvent<HTMLDialogElement>) => {
|
|
if (e.target === e.currentTarget) {
|
|
close();
|
|
}
|
|
}, [close]);
|
|
|
|
return { dialogRef, handleBackdropClick, close };
|
|
}
|