mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-06 18:06:36 +08:00
fix(ui): restore missing UI transition animations (#408)
- Wrap `Routes` with `AnimatePresence` to enable framer-motion page transition exit animations and fix harsh route changes. - Replace `tw-animate-css` with `tailwindcss-animate` to properly bundle transition classes (like `animate-in`, `animate-accordion-down`) under Tailwind v4. - This restores animation to modals, accordions, and all page navigations. - Fix accessibility warning on table cell span element. Fixes #283
This commit is contained in:
@@ -52,7 +52,7 @@
|
|||||||
"tailwind-merge": "^2.5.5",
|
"tailwind-merge": "^2.5.5",
|
||||||
"tailwind-variants": "1.0.0",
|
"tailwind-variants": "1.0.0",
|
||||||
"tailwindcss": "4.1.11",
|
"tailwindcss": "4.1.11",
|
||||||
"tw-animate-css": "^1.3.0"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "1.2.8",
|
"@eslint/compat": "1.2.8",
|
||||||
|
|||||||
+106
-101
@@ -1,5 +1,6 @@
|
|||||||
import { Route, Routes, useNavigate } from "react-router-dom";
|
import { Route, Routes, useLocation, useNavigate } from "react-router-dom";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
import { AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
import IndexPage from "@/pages/index";
|
import IndexPage from "@/pages/index";
|
||||||
import ChangePasswordPage from "@/pages/change-password";
|
import ChangePasswordPage from "@/pages/change-password";
|
||||||
@@ -87,6 +88,8 @@ const LoginRoute = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
// 立即设置页面标题(使用已从缓存读取的配置)
|
// 立即设置页面标题(使用已从缓存读取的配置)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = siteConfig.name;
|
document.title = siteConfig.name;
|
||||||
@@ -105,106 +108,108 @@ function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<AnimatePresence mode="wait">
|
||||||
<Route element={<LoginRoute />} path="/" />
|
<Routes key={location.pathname} location={location}>
|
||||||
<Route
|
<Route element={<LoginRoute />} path="/" />
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute skipLayout={true}>
|
element={
|
||||||
<ChangePasswordPage />
|
<ProtectedRoute skipLayout={true}>
|
||||||
</ProtectedRoute>
|
<ChangePasswordPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/change-password"
|
}
|
||||||
/>
|
path="/change-password"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<DashboardPage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<DashboardPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/dashboard"
|
}
|
||||||
/>
|
path="/dashboard"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<MonitorPage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<MonitorPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/monitor"
|
}
|
||||||
/>
|
path="/monitor"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<ForwardPage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<ForwardPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/forward"
|
}
|
||||||
/>
|
path="/forward"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<TunnelPage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<TunnelPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/tunnel"
|
}
|
||||||
/>
|
path="/tunnel"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<NodePage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<NodePage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/node"
|
}
|
||||||
/>
|
path="/node"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute useSimpleLayout={true}>
|
element={
|
||||||
<UserPage />
|
<ProtectedRoute useSimpleLayout={true}>
|
||||||
</ProtectedRoute>
|
<UserPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/user"
|
}
|
||||||
/>
|
path="/user"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute useSimpleLayout={true}>
|
element={
|
||||||
<GroupPage />
|
<ProtectedRoute useSimpleLayout={true}>
|
||||||
</ProtectedRoute>
|
<GroupPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/group"
|
}
|
||||||
/>
|
path="/group"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<ProfilePage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<ProfilePage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/profile"
|
}
|
||||||
/>
|
path="/profile"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute useSimpleLayout={true}>
|
element={
|
||||||
<LimitPage />
|
<ProtectedRoute useSimpleLayout={true}>
|
||||||
</ProtectedRoute>
|
<LimitPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/limit"
|
}
|
||||||
/>
|
path="/limit"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute>
|
element={
|
||||||
<ConfigPage />
|
<ProtectedRoute>
|
||||||
</ProtectedRoute>
|
<ConfigPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/config"
|
}
|
||||||
/>
|
path="/config"
|
||||||
<Route
|
/>
|
||||||
element={
|
<Route
|
||||||
<ProtectedRoute useSimpleLayout={true}>
|
element={
|
||||||
<PanelSharingPage />
|
<ProtectedRoute useSimpleLayout={true}>
|
||||||
</ProtectedRoute>
|
<PanelSharingPage />
|
||||||
}
|
</ProtectedRoute>
|
||||||
path="/panel-sharing"
|
}
|
||||||
/>
|
path="/panel-sharing"
|
||||||
<Route element={<SettingsPage />} path="/settings" />
|
/>
|
||||||
</Routes>
|
<Route element={<SettingsPage />} path="/settings" />
|
||||||
|
</Routes>
|
||||||
|
</AnimatePresence>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -973,12 +973,13 @@ const SortableCompactTableRow = ({
|
|||||||
<TableCell
|
<TableCell
|
||||||
className={`whitespace-nowrap text-foreground ${selectedIds.has(forward.id) ? "bg-primary-50/70 dark:bg-primary-900/40" : ""}`}
|
className={`whitespace-nowrap text-foreground ${selectedIds.has(forward.id) ? "bg-primary-50/70 dark:bg-primary-900/40" : ""}`}
|
||||||
>
|
>
|
||||||
<span
|
<button
|
||||||
className="cursor-pointer hover:text-primary transition-colors"
|
className="cursor-pointer hover:text-primary transition-colors text-left bg-transparent border-none p-0 outline-none focus:ring-2 focus:ring-primary focus:ring-offset-1 rounded-sm"
|
||||||
|
type="button"
|
||||||
onClick={() => copyToClipboard(forward.name, "规则名")}
|
onClick={() => copyToClipboard(forward.name, "规则名")}
|
||||||
>
|
>
|
||||||
{forward.name}
|
{forward.name}
|
||||||
</span>
|
</button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
className={`whitespace-nowrap ${selectedIds.has(forward.id) ? "bg-primary-50/70 dark:bg-primary-900/40" : ""}`}
|
className={`whitespace-nowrap ${selectedIds.has(forward.id) ? "bg-primary-50/70 dark:bg-primary-900/40" : ""}`}
|
||||||
@@ -987,11 +988,12 @@ const SortableCompactTableRow = ({
|
|||||||
<span className="font-medium text-default-700 text-sm">
|
<span className="font-medium text-default-700 text-sm">
|
||||||
{forward.tunnelName}
|
{forward.tunnelName}
|
||||||
</span>
|
</span>
|
||||||
{forward.tunnelTrafficRatio !== undefined && normalizeTunnelTrafficRatio(forward.tunnelTrafficRatio) !== 1 && (
|
{forward.tunnelTrafficRatio !== undefined &&
|
||||||
<span className="text-success font-bold text-[12px] ml-1.5 border border-success/30 rounded px-1 bg-success/10">
|
normalizeTunnelTrafficRatio(forward.tunnelTrafficRatio) !== 1 && (
|
||||||
{formatTunnelTrafficRatio(forward.tunnelTrafficRatio)}
|
<span className="text-success font-bold text-[12px] ml-1.5 border border-success/30 rounded px-1 bg-success/10">
|
||||||
</span>
|
{formatTunnelTrafficRatio(forward.tunnelTrafficRatio)}
|
||||||
)}
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "tw-animate-css";
|
|
||||||
@import "./tailwind-theme.pcss";
|
@import "./tailwind-theme.pcss";
|
||||||
|
@plugin "tailwindcss-animate";
|
||||||
|
|
||||||
/* GPU acceleration helper - prevents font blurriness during transforms */
|
/* GPU acceleration helper - prevents font blurriness during transforms */
|
||||||
.gpu-accelerated {
|
.gpu-accelerated {
|
||||||
|
|||||||
Reference in New Issue
Block a user