From 7d495aa772c7fefe1e7aa8bbd1b157cf8dbedabc Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 22 Dec 2025 22:27:32 +0800 Subject: [PATCH] feat(ui): add exit animation to FullScreenPanel dialogs - Wrap FullScreenPanel content with AnimatePresence for exit animation - Add exit={{ opacity: 0 }} to enable fade-out on close - Use useRef + useEffect to preserve provider data during exit animation - Follow React best practices by updating refs in useEffect instead of render Affected components: - EditProviderDialog - UsageScriptModal - AddProviderDialog - McpFormModal - ProxySettingsDialog --- src/App.tsx | 26 ++++++-- src/components/common/FullScreenPanel.tsx | 81 ++++++++++++----------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index fca3b8766..ca32be6e9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -65,6 +65,22 @@ function App() { const [envConflicts, setEnvConflicts] = useState([]); const [showEnvBanner, setShowEnvBanner] = useState(false); + // 保存最后一个有效的 provider,用于动画退出期间显示内容 + const lastUsageProviderRef = useRef(null); + const lastEditingProviderRef = useRef(null); + + useEffect(() => { + if (usageProvider) { + lastUsageProviderRef.current = usageProvider; + } + }, [usageProvider]); + + useEffect(() => { + if (editingProvider) { + lastEditingProviderRef.current = editingProvider; + } + }, [editingProvider]); + const promptPanelRef = useRef(null); const mcpPanelRef = useRef(null); const skillsPageRef = useRef(null); @@ -639,7 +655,7 @@ function App() { { if (!open) { setEditingProvider(null); @@ -650,14 +666,16 @@ function App() { isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive} /> - {usageProvider && ( + {lastUsageProviderRef.current && ( setUsageProvider(null)} onSave={(script) => { - void saveUsageScript(usageProvider, script); + if (usageProvider) { + void saveUsageScript(usageProvider, script); + } }} /> )} diff --git a/src/components/common/FullScreenPanel.tsx b/src/components/common/FullScreenPanel.tsx index edf3a1555..19fb65a71 100644 --- a/src/components/common/FullScreenPanel.tsx +++ b/src/components/common/FullScreenPanel.tsx @@ -1,6 +1,6 @@ import React from "react"; import { createPortal } from "react-dom"; -import { motion } from "framer-motion"; +import { motion, AnimatePresence } from "framer-motion"; import { ArrowLeft } from "lucide-react"; import { Button } from "@/components/ui/button"; @@ -33,49 +33,52 @@ export const FullScreenPanel: React.FC = ({ }; }, [isOpen]); - if (!isOpen) return null; - return createPortal( - - {/* Header */} -
-
-
- -

{title}

-
-
- - {/* Content */} -
-
- {children} -
-
- - {/* Footer */} - {footer && ( -
+ {isOpen && ( + -
- {footer} + {/* Header */} +
+
+
+ +

{title}

+
-
+ + {/* Content */} +
+
+ {children} +
+
+ + {/* Footer */} + {footer && ( +
+
+ {footer} +
+
+ )} + )} - , + , document.body, ); };