6b9953625e
- Add AnimatedPanel component with CSS transition-based enter/exit for Profile popover and Notification panel (140ms scale+fade) - Add nav-activate-pulse animation for floating-nav active indicator (320ms glow) - Add tool-panel-fade-in crossfade when switching ecommerce tools - Add carousel-card-label slide-up-in 260ms on active carousel card - Add feature-visual img hover scale(1.03)+brightness, experience-route hover translateY(-2px) - Add community-case-card--mosaic hover scale(1.02)+shadow lift - Add directional PageTransition: forward→slideX(20px), backward→slideX(-20px) - Move vite proxy target from hardcoded IP to VITE_DEV_PROXY env variable - Add .env.example for developer onboarding Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
54 lines
1.3 KiB
TypeScript
54 lines
1.3 KiB
TypeScript
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
|
|
interface AnimatedPanelProps {
|
|
open: boolean;
|
|
children: ReactNode;
|
|
className?: string;
|
|
/** Duration in ms for the exit animation before unmounting. */
|
|
exitDuration?: number;
|
|
}
|
|
|
|
export function AnimatedPanel({ open, children, className, exitDuration = 140 }: AnimatedPanelProps) {
|
|
const [mounted, setMounted] = useState(open);
|
|
const [visible, setVisible] = useState(open);
|
|
const timerRef = useRef<number | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
if (timerRef.current) {
|
|
window.clearTimeout(timerRef.current);
|
|
timerRef.current = null;
|
|
}
|
|
setMounted(true);
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
setVisible(true);
|
|
});
|
|
});
|
|
} else {
|
|
setVisible(false);
|
|
timerRef.current = window.setTimeout(() => {
|
|
setMounted(false);
|
|
timerRef.current = null;
|
|
}, exitDuration);
|
|
}
|
|
}, [open, exitDuration]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (timerRef.current) {
|
|
window.clearTimeout(timerRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
if (!mounted) return null;
|
|
|
|
return (
|
|
<div
|
|
className={`${className ?? ""} animated-panel${visible ? " is-visible" : ""}`}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
} |