From c5d4356d6c6819862b76244a7ccda986b18b528a Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Sun, 28 Dec 2025 01:04:22 +0800 Subject: [PATCH] fix --- src/components/ui/Modal.tsx | 47 ++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index 227639d..e9c7b4e 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, type PropsWithChildren, type ReactNode } from 'react'; +import { useState, useEffect, useCallback, useRef, type PropsWithChildren, type ReactNode } from 'react'; import { IconX } from './icons'; interface ModalProps { @@ -14,22 +14,51 @@ const CLOSE_ANIMATION_DURATION = 350; export function Modal({ open, title, onClose, footer, width = 520, children }: PropsWithChildren) { const [isVisible, setIsVisible] = useState(false); const [isClosing, setIsClosing] = useState(false); + const closeTimerRef = useRef | null>(null); + + const startClose = useCallback( + (notifyParent: boolean) => { + if (closeTimerRef.current !== null) return; + setIsClosing(true); + closeTimerRef.current = window.setTimeout(() => { + setIsVisible(false); + setIsClosing(false); + closeTimerRef.current = null; + if (notifyParent) { + onClose(); + } + }, CLOSE_ANIMATION_DURATION); + }, + [onClose] + ); useEffect(() => { if (open) { + if (closeTimerRef.current !== null) { + window.clearTimeout(closeTimerRef.current); + closeTimerRef.current = null; + } setIsVisible(true); setIsClosing(false); + return; } - }, [open]); + + if (isVisible) { + startClose(false); + } + }, [open, isVisible, startClose]); const handleClose = useCallback(() => { - setIsClosing(true); - setTimeout(() => { - setIsVisible(false); - setIsClosing(false); - onClose(); - }, CLOSE_ANIMATION_DURATION); - }, [onClose]); + startClose(true); + }, [startClose]); + + useEffect(() => { + return () => { + if (closeTimerRef.current !== null) { + window.clearTimeout(closeTimerRef.current); + } + }; + }, []); if (!open && !isVisible) return null;