import React, { useState, useCallback, useRef } from 'react'; import './DialogProvider.css'; import { FaTimes } from 'react-icons/fa'; import { registerDialogFns } from '../../util/dialogUtil'; import type { ConfirmOptions, PromptOptions, TimeSigResult } from '../../util/dialogUtil'; interface DialogInfo { type: 'alert' | 'confirm' | 'prompt' | 'timesig'; message: string; options?: ConfirmOptions | PromptOptions; defaultValue?: string; defaultTimeSig?: TimeSigResult; } const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [dialog, setDialog] = useState(null); const [isClosing, setIsClosing] = useState(false); const [inputValue, setInputValue] = useState(''); const [timeSigNumerator, setTimeSigNumerator] = useState(''); const [timeSigDenominator, setTimeSigDenominator] = useState(''); // eslint-disable-next-line @typescript-eslint/no-explicit-any const resolveRef = useRef<((value: any) => void) | null>(null); const pendingValueRef = useRef(undefined); const openAlert = useCallback((message: string): Promise => { return new Promise((resolve) => { resolveRef.current = resolve; setDialog({ type: 'alert', message }); }); }, []); const openConfirm = useCallback((message: string, options?: ConfirmOptions): Promise => { return new Promise((resolve) => { resolveRef.current = resolve; setDialog({ type: 'confirm', message, options }); }); }, []); const openPrompt = useCallback((message: string, defaultValue?: string, options?: PromptOptions): Promise => { return new Promise((resolve) => { resolveRef.current = resolve; setInputValue(defaultValue ?? ''); setDialog({ type: 'prompt', message, options, defaultValue }); }); }, []); const openTimeSig = useCallback((message: string, defaultValue?: TimeSigResult): Promise => { return new Promise((resolve) => { resolveRef.current = resolve; setTimeSigNumerator(String(defaultValue?.numerator ?? 4)); setTimeSigDenominator(String(defaultValue?.denominator ?? 4)); setDialog({ type: 'timesig', message, defaultTimeSig: defaultValue }); }); }, []); const close = useCallback((value: unknown) => { pendingValueRef.current = value; setIsClosing(true); }, []); const handleAnimationEnd = useCallback((e: React.AnimationEvent) => { if (e.target !== e.currentTarget) return; if (!isClosing) return; setIsClosing(false); setDialog(null); setInputValue(''); setTimeSigNumerator(''); setTimeSigDenominator(''); if (resolveRef.current) { resolveRef.current(pendingValueRef.current); resolveRef.current = null; } }, [isClosing]); const mouseDownOnOverlay = useRef(false); const registered = useRef(false); if (!registered.current) { registered.current = true; registerDialogFns(openAlert, openConfirm, openPrompt, openTimeSig); } if (!dialog) { return <>{children}; } const isAlert = dialog.type === 'alert'; const isPrompt = dialog.type === 'prompt'; const isTimeSig = dialog.type === 'timesig'; const promptOptions = isPrompt ? (dialog.options as PromptOptions | undefined) : undefined; const title = isAlert ? 'Notice' : isTimeSig ? 'Time Signature' : isPrompt ? 'Input' : 'Confirm'; const handleOverlayMouseDown = (e: React.MouseEvent) => { mouseDownOnOverlay.current = e.target === e.currentTarget; }; const handleOverlayClick = (e: React.MouseEvent) => { if (e.target === e.currentTarget && mouseDownOnOverlay.current) { close(isAlert ? undefined : (isPrompt || isTimeSig) ? null : false); } }; const handleCancel = () => close(isAlert ? undefined : (isPrompt || isTimeSig) ? null : false); const handleConfirm = () => { if (isAlert) { close(undefined); return; } if (isPrompt) { close(inputValue); return; } if (isTimeSig) { close({ numerator: Number(timeSigNumerator), denominator: Number(timeSigDenominator) }); return; } close(true); }; return ( <> {children}

{title}

{dialog.message}

{isPrompt && ( setInputValue(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleConfirm(); if (e.key === 'Escape') handleCancel(); }} autoFocus /> )} {isTimeSig && (
setTimeSigNumerator(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleConfirm(); if (e.key === 'Escape') handleCancel(); }} autoFocus /> / setTimeSigDenominator(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleConfirm(); if (e.key === 'Escape') handleCancel(); }} />
)}
{!isAlert && ( )}
); }; export default DialogProvider;