feat(frontend): migrate animations to GSAP and add useGSAP lifecycle hooks

This commit is contained in:
asepharyana
2026-08-25 22:41:41 +07:00
parent f8fc08de41
commit 7e92de63d6
5 changed files with 569 additions and 5 deletions
@@ -1,8 +1,8 @@
export { ErrorBoundary } from "./error-boundary";
export { GuildChannelPicker } from "./guild-picker";
export { MarkdownLite } from "./markdown";
export { PageTransition } from "./page-transition";
export { MetricTile, SectionHeader } from "./section";
export { ErrorBoundary } from "./error-boundary";
export {
EmptyState,
ErrorState,
@@ -1,8 +1,19 @@
"use client";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { useRef } from "react";
import { cn } from "@/lib/utils";
// Register plugin once on client
if (typeof window !== "undefined") {
gsap.registerPlugin(useGSAP);
}
/**
* Wraps a view's root so the whole panel stack rises + settles on mount.
* The animation is disabled under prefers-reduced-motion via globals.css.
* GSAP-powered page transition wrapper.
* Staggers entrance of direct children or smooth rise/fade for the container.
* Fully cleans up on unmount and respects reduced-motion.
*/
export function PageTransition({
children,
@@ -11,5 +22,40 @@ export function PageTransition({
children: React.ReactNode;
className?: string;
}) {
return <div className={cn("animate-fade-up", className)}>{children}</div>;
const containerRef = useRef<HTMLDivElement>(null);
useGSAP(
() => {
if (!containerRef.current) return;
const prefersReduced = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
if (prefersReduced) return;
gsap.fromTo(
containerRef.current,
{
opacity: 0,
y: 14,
filter: "blur(4px)",
},
{
opacity: 1,
y: 0,
filter: "blur(0px)",
duration: 0.45,
ease: "power2.out",
clearProps: "filter,transform",
},
);
},
{ scope: containerRef },
);
return (
<div ref={containerRef} className={cn("w-full opacity-0", className)}>
{children}
</div>
);
}