feat(ui): add global progress bar for navigation feedback
This commit is contained in:
1 parent
d78744efc1
commit
966a925614
2 files changed
+44
-1
No files matched your search
+5
-1
@@ -5,8 +5,9 @@ import Script from "next/script";
|
|||||||
import { NextIntlClientProvider } from "next-intl";
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
import { getLocale, getMessages } from "next-intl/server";
|
import { getLocale, getMessages } from "next-intl/server";
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import { type ReactNode, Suspense } from "react";
|
||||||
import { Toaster } from "sonner";
|
import { Toaster } from "sonner";
|
||||||
|
import { GlobalProgressBar } from "@/components/global-progress-bar";
|
||||||
import { PwaRegister } from "@/components/pwa-register";
|
import { PwaRegister } from "@/components/pwa-register";
|
||||||
import { ScopedThemeVars } from "@/components/scoped-theme-vars";
|
import { ScopedThemeVars } from "@/components/scoped-theme-vars";
|
||||||
import { ThemeScopeDetector } from "@/components/theme-scope-detector";
|
import { ThemeScopeDetector } from "@/components/theme-scope-detector";
|
||||||
@@ -96,6 +97,9 @@ export default async function RootLayout({
|
|||||||
className="flex min-h-screen flex-col site-bg"
|
className="flex min-h-screen flex-col site-bg"
|
||||||
style={{ backgroundColor: "var(--color-background)" }}
|
style={{ backgroundColor: "var(--color-background)" }}
|
||||||
>
|
>
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<GlobalProgressBar />
|
||||||
|
</Suspense>
|
||||||
<NextIntlClientProvider locale={locale} messages={messages}>
|
<NextIntlClientProvider locale={locale} messages={messages}>
|
||||||
<ThemeVars nonce={nonce} />
|
<ThemeVars nonce={nonce} />
|
||||||
<ScopedThemeVars nonce={nonce} />
|
<ScopedThemeVars nonce={nonce} />
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion, useIsPresent } from "motion/react";
|
||||||
|
import { usePathname, useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global smooth progress indicator for page transitions.
|
||||||
|
* Uses motion/react for high-performance 60fps animations.
|
||||||
|
*/
|
||||||
|
export function GlobalProgressBar() {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const isPresent = useIsPresent();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
}, 400);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [pathname, searchParams]);
|
||||||
|
|
||||||
|
if (!isLoading && isPresent) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed top-0 left-0 right-0 z-[99999] h-1 bg-transparent pointer-events-none overflow-hidden">
|
||||||
|
<motion.div
|
||||||
|
className="h-full bg-[var(--color-primary)] shadow-[0_0_10px_var(--color-primary)]"
|
||||||
|
initial={{ width: "0%", opacity: 1 }}
|
||||||
|
animate={{ width: "85%", opacity: 1 }}
|
||||||
|
exit={{ width: "100%", opacity: 0 }}
|
||||||
|
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user