feat(dependencies): add Lottie animation libraries and refactor NotFoundPage
- Added `@lottiefiles/dotlottie-react` and `lottie-react` dependencies for enhanced animation capabilities. - Refactored the NotFoundPage component to utilize a dedicated NotFoundContent component, improving code organization and readability. - Updated package.json and pnpm-lock.yaml to reflect new dependencies and their versions.
This commit is contained in:
+58
-85
@@ -1,106 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import rawLoadingMain from "@/assets/lottie/loading-main.json";
|
||||
import { applyLoadingLottieTheme } from "@/lib/lottie-theme";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Lottie from "lottie-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
interface IProps {
|
||||
className?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full-screen route-style loader (orbs, rings, shimmer). Pass `className` to adapt
|
||||
* the shell—for example `min-h-48 flex-none` inside compact panels.
|
||||
* Extract of `animations/main.json` from `public/lottie/loading.lottie` (regenerate when
|
||||
* the .lottie changes: `unzip -p public/lottie/loading.lottie animations/main.json > src/assets/lottie/loading-main.json`).
|
||||
*/
|
||||
const LOTTIE_RAW = rawLoadingMain as object;
|
||||
|
||||
/**
|
||||
* Route-level loader: SVG Lottie via `lottie-react` (reliable on refresh). Source animation
|
||||
* stays in sync with `public/lottie/loading.lottie` via the bundled extract above.
|
||||
*/
|
||||
const Loading = ({ className }: IProps) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const mode = mounted && resolvedTheme === "dark" ? "dark" : "light";
|
||||
|
||||
const animationData = useMemo(
|
||||
() => applyLoadingLottieTheme(LOTTIE_RAW, mode),
|
||||
[mode],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>{`
|
||||
@keyframes app-route-loading-shimmer {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(400%); }
|
||||
}
|
||||
`}</style>
|
||||
<div
|
||||
role="status"
|
||||
aria-busy="true"
|
||||
aria-live="polite"
|
||||
className={cn(
|
||||
"relative flex min-h-svh w-full min-w-0 flex-1 flex-col items-center justify-center overflow-hidden",
|
||||
"bg-gray-1 dark:bg-dark",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex min-h-svh w-full min-w-0 flex-1 items-center justify-center overflow-hidden bg-gray dark:bg-dark",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Soft gradient wash */}
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_80%_50%_at_50%_-20%,rgba(87,80,241,0.22),transparent)] dark:bg-[radial-gradient(ellipse_80%_50%_at_50%_-20%,rgba(87,80,241,0.35),transparent)]"
|
||||
aria-hidden
|
||||
/>
|
||||
className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_75%_55%_at_50%_45%,transparent,rgba(17,25,40,0.06))] dark:bg-[radial-gradient(ellipse_75%_55%_at_50%_45%,transparent,rgba(0,0,0,0.45))]"
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
{/* Floating orbs */}
|
||||
<div
|
||||
className="pointer-events-none absolute -left-32 top-1/4 h-72 w-72 animate-pulse rounded-full bg-primary/20 blur-3xl dark:bg-primary/25"
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="pointer-events-none absolute -right-24 bottom-1/4 h-64 w-64 animate-pulse rounded-full bg-blue/25 blur-3xl dark:bg-blue/20"
|
||||
style={{ animationDelay: "1s" }}
|
||||
aria-hidden
|
||||
/>
|
||||
<div
|
||||
className="pointer-events-none absolute left-1/2 top-1/2 h-96 w-96 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary/10 blur-[100px] dark:bg-primary/15"
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="pointer-events-none absolute left-6 top-6 h-14 w-14 border-l-2 border-t-2 border-primary/45 dark:border-primary/40 sm:left-10 sm:top-10" />
|
||||
<div className="pointer-events-none absolute bottom-6 right-6 h-14 w-14 border-b-2 border-r-2 border-primary/45 dark:border-primary/40 sm:bottom-10 sm:right-10" />
|
||||
|
||||
{/* Subtle grid */}
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 bg-[linear-gradient(to_right,rgba(148,163,184,0.06)_1px,transparent_1px),linear-gradient(to_bottom,rgba(148,163,184,0.06)_1px,transparent_1px)] bg-[size:48px_48px] dark:bg-[linear-gradient(to_right,rgba(55,65,81,0.15)_1px,transparent_1px),linear-gradient(to_bottom,rgba(55,65,81,0.15)_1px,transparent_1px)]"
|
||||
aria-hidden
|
||||
/>
|
||||
|
||||
<div className="relative z-10 flex flex-col items-center gap-8 px-6">
|
||||
<div className="relative flex h-28 w-28 items-center justify-center">
|
||||
{/* Outer glow */}
|
||||
<div className="absolute inset-0 rounded-full bg-primary/20 blur-xl dark:bg-primary/30" />
|
||||
|
||||
{/* Concentric rings */}
|
||||
<div className="absolute inset-0 animate-spin rounded-full border-2 border-transparent border-r-primary/40 border-t-primary/90 shadow-[0_0_20px_rgba(87,80,241,0.35)]" />
|
||||
<div
|
||||
className="absolute inset-2 animate-[spin_2.5s_linear_infinite_reverse] rounded-full border-2 border-transparent border-b-blue/70 border-l-blue/30"
|
||||
<div className="relative z-10 flex flex-col items-center gap-6 px-8">
|
||||
<div className="relative aspect-square w-[min(200px,55vw)] shrink-0">
|
||||
<div className="absolute inset-0 flex items-center justify-center [&_svg]:h-full [&_svg]:w-full [&_svg]:max-h-full [&_svg]:max-w-full">
|
||||
<Lottie
|
||||
key={mode}
|
||||
animationData={animationData}
|
||||
loop
|
||||
className="h-full w-full"
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="absolute inset-5 rounded-full border border-stroke/80 dark:border-stroke-dark/60" />
|
||||
|
||||
{/* Core */}
|
||||
<div className="relative flex h-14 w-14 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-blue shadow-lg ring-4 ring-white/60 dark:ring-dark-2/80">
|
||||
<span className="text-lg font-bold tracking-tight text-white drop-shadow-sm">
|
||||
OL
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-3 text-center">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="h-2 w-2 animate-pulse rounded-full bg-primary" />
|
||||
<span
|
||||
className="h-2 w-2 animate-pulse rounded-full bg-primary/70"
|
||||
style={{ animationDelay: "0.2s" }}
|
||||
/>
|
||||
<span
|
||||
className="h-2 w-2 animate-pulse rounded-full bg-primary/40"
|
||||
style={{ animationDelay: "0.4s" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="bg-gradient-to-r from-dark via-primary to-blue bg-clip-text text-body-sm font-medium text-transparent dark:from-gray-7 dark:via-primary dark:to-blue-light">
|
||||
Preparing your workspace…
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Shimmer bar */}
|
||||
<div className="h-1 w-48 overflow-hidden rounded-full bg-stroke dark:bg-stroke-dark">
|
||||
<div
|
||||
className="h-full w-1/3 rounded-full bg-gradient-to-r from-transparent via-primary to-transparent"
|
||||
style={{
|
||||
animation:
|
||||
"app-route-loading-shimmer 1.5s ease-in-out infinite",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="font-mono text-[11px] font-semibold uppercase tracking-[0.45em] text-gray-6 dark:text-dark-6">
|
||||
Loading
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<span className="sr-only">Loading, please wait.</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user