feat(ui): add fade transition for view and panel switching

Add smooth fade animations when navigating between views (Settings,
MCP, Skills, Prompts) and opening full-screen panels (Add/Edit Provider).
This commit is contained in:
Jason
2025-12-21 16:05:22 +08:00
parent d303706d51
commit 26c3f05daf
2 changed files with 91 additions and 73 deletions
+85 -71
View File
@@ -299,78 +299,92 @@ function App() {
};
const renderContent = () => {
switch (currentView) {
case "settings":
return (
<SettingsPage
open={true}
onOpenChange={() => setCurrentView("providers")}
onImportSuccess={handleImportSuccess}
/>
);
case "prompts":
return (
<PromptPanel
ref={promptPanelRef}
open={true}
onOpenChange={() => setCurrentView("providers")}
appId={activeApp}
/>
);
case "skills":
return (
<SkillsPage
ref={skillsPageRef}
onClose={() => setCurrentView("providers")}
initialApp={activeApp}
/>
);
case "mcp":
return (
<UnifiedMcpPanel
ref={mcpPanelRef}
onOpenChange={() => setCurrentView("providers")}
/>
);
case "agents":
return <AgentsPanel onOpenChange={() => setCurrentView("providers")} />;
default:
return (
<div className="mx-auto max-w-[56rem] px-5 flex flex-col h-[calc(100vh-8rem)] overflow-hidden">
{/* 独立滚动容器 - 解决 Linux/Ubuntu 下 DndContext 与滚轮事件冲突 */}
<div className="flex-1 px-1 pb-12 overflow-x-hidden overflow-y-auto">
<AnimatePresence mode="wait">
<motion.div
key={activeApp}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
className="space-y-4"
>
<ProviderList
providers={providers}
currentProviderId={currentProviderId}
appId={activeApp}
isLoading={isLoading}
isProxyRunning={isProxyRunning}
isProxyTakeover={
isProxyRunning && isCurrentAppTakeoverActive
}
onSwitch={switchProvider}
onEdit={setEditingProvider}
onDelete={setConfirmDelete}
onDuplicate={handleDuplicateProvider}
onConfigureUsage={setUsageProvider}
onOpenWebsite={handleOpenWebsite}
onCreate={() => setIsAddOpen(true)}
/>
</motion.div>
</AnimatePresence>
const content = (() => {
switch (currentView) {
case "settings":
return (
<SettingsPage
open={true}
onOpenChange={() => setCurrentView("providers")}
onImportSuccess={handleImportSuccess}
/>
);
case "prompts":
return (
<PromptPanel
ref={promptPanelRef}
open={true}
onOpenChange={() => setCurrentView("providers")}
appId={activeApp}
/>
);
case "skills":
return (
<SkillsPage
ref={skillsPageRef}
onClose={() => setCurrentView("providers")}
initialApp={activeApp}
/>
);
case "mcp":
return (
<UnifiedMcpPanel
ref={mcpPanelRef}
onOpenChange={() => setCurrentView("providers")}
/>
);
case "agents":
return <AgentsPanel onOpenChange={() => setCurrentView("providers")} />;
default:
return (
<div className="mx-auto max-w-[56rem] px-5 flex flex-col h-[calc(100vh-8rem)] overflow-hidden">
{/* 独立滚动容器 - 解决 Linux/Ubuntu 下 DndContext 与滚轮事件冲突 */}
<div className="flex-1 overflow-y-auto overflow-x-hidden pb-12 px-1">
<AnimatePresence mode="wait">
<motion.div
key={activeApp}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
className="space-y-4"
>
<ProviderList
providers={providers}
currentProviderId={currentProviderId}
appId={activeApp}
isLoading={isLoading}
isProxyRunning={isProxyRunning}
isProxyTakeover={isProxyRunning && isCurrentAppTakeoverActive}
onSwitch={switchProvider}
onEdit={setEditingProvider}
onDelete={setConfirmDelete}
onDuplicate={handleDuplicateProvider}
onConfigureUsage={setUsageProvider}
onOpenWebsite={handleOpenWebsite}
onCreate={() => setIsAddOpen(true)}
/>
</motion.div>
</AnimatePresence>
</div>
</div>
</div>
);
}
);
}
})();
return (
<AnimatePresence mode="wait">
<motion.div
key={currentView}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
>
{content}
</motion.div>
</AnimatePresence>
);
};
return (
+6 -2
View File
@@ -1,5 +1,6 @@
import React from "react";
import { createPortal } from "react-dom";
import { motion } from "framer-motion";
import { ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button";
@@ -35,7 +36,10 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
if (!isOpen) return null;
return createPortal(
<div
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 z-[60] flex flex-col"
style={{ backgroundColor: "hsl(var(--background))" }}
>
@@ -71,7 +75,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
</div>
</div>
)}
</div>,
</motion.div>,
document.body,
);
};