import { memo, useCallback, useEffect, useRef } from 'react' import type { FormEvent, KeyboardEvent as ReactKeyboardEvent } from 'react' import { Trash } from '@phosphor-icons/react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' interface ConfirmDeleteDialogProps { open: boolean title: string message: string confirmLabel?: string onConfirm: () => void onCancel: () => void } function focusConfirmButton(confirmButton: HTMLButtonElement | null) { confirmButton?.focus() } function focusPrimaryDeleteButton(root: ParentNode | null) { focusConfirmButton(root?.querySelector('[data-confirm-delete-primary="true"]') ?? null) } function scheduleConfirmButtonFocus(root: ParentNode | null) { const focusDelays = [0, 50, 150] return focusDelays.map((delay) => ( window.setTimeout(() => { focusPrimaryDeleteButton(root) }, delay) )) } type ConfirmShortcutEvent = Pick< KeyboardEvent, 'key' | 'defaultPrevented' | 'repeat' | 'metaKey' | 'ctrlKey' | 'altKey' | 'shiftKey' > & { isComposing?: boolean nativeEvent?: Pick } function isComposingEvent(event: ConfirmShortcutEvent) { return event.isComposing || event.nativeEvent?.isComposing === true } function isConfirmShortcut(event: ConfirmShortcutEvent) { return ( event.key === 'Enter' && !event.defaultPrevented && !event.repeat && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey && !isComposingEvent(event) ) } export const ConfirmDeleteDialog = memo(function ConfirmDeleteDialog({ open, title, message, confirmLabel = 'Delete permanently', onConfirm, onCancel, }: ConfirmDeleteDialogProps) { const confirmingRef = useRef(false) const cancelFocusIsIntentionalRef = useRef(false) useEffect(() => { confirmingRef.current = false cancelFocusIsIntentionalRef.current = false if (!open) { return } focusPrimaryDeleteButton(document) const timeoutIds = scheduleConfirmButtonFocus(document) return () => { timeoutIds.forEach((timeoutId) => { window.clearTimeout(timeoutId) }) } }, [open]) const handleConfirm = useCallback(() => { if (confirmingRef.current) { return } confirmingRef.current = true onConfirm() }, [onConfirm]) const handleSubmit = useCallback((event: FormEvent) => { event.preventDefault() handleConfirm() }, [handleConfirm]) useEffect(() => { if (!open) { return } const handleWindowKeyDown = (event: KeyboardEvent) => { if (!isConfirmShortcut(event)) { return } event.preventDefault() handleConfirm() } window.addEventListener('keydown', handleWindowKeyDown, true) return () => { window.removeEventListener('keydown', handleWindowKeyDown, true) } }, [handleConfirm, open]) const handleKeyDown = useCallback((event: ReactKeyboardEvent) => { if (!isConfirmShortcut(event)) { return } event.preventDefault() handleConfirm() }, [handleConfirm]) return ( { if (!isOpen) onCancel() }}> { if (event.key === 'Tab') { cancelFocusIsIntentionalRef.current = true } }} onOpenAutoFocus={(event) => { event.preventDefault() const dialogRoot = event.currentTarget as ParentNode | null focusPrimaryDeleteButton(dialogRoot) scheduleConfirmButtonFocus(dialogRoot) }} > {title} {message}
) })