Files
zeekay f91e76841b fix: Resolve SSR errors and add 'use client' to interactive demos
Major fixes:
- Fixed wagmi/Web3 SSR errors by lazy-loading config in Web3Provider
- Added SidebarProvider to sidebar-demo components
- Added 'use client' to 14 demo files with hooks/state/event handlers
- Temporarily disabled force-static on blocks page

Build progress: 0 → 258/1035 pages (25%) before hitting /blocks error

Remaining issue:
- /blocks page has function-passing error during static generation

Package versions confirmed:
- React 19.2.0 ✅
- Next.js 16.0.0 ✅
- Tailwind CSS 4.1.14 ✅
2025-11-06 01:51:19 -08:00

14 lines
13 KiB
JSON

{
"name": "pulse-animation",
"description": "Pulsing animation effects",
"dependencies": [
"motion"
],
"files": [
{
"name": "pulse-animation.tsx",
"content": "\"use client\"\n\nimport React, { useEffect, useState } from \"react\"\nimport { motion, Variants } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/**\n * Custom hook to detect prefers-reduced-motion\n */\nfunction usePrefersReducedMotion(): boolean {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false)\n\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n const mediaQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n setPrefersReducedMotion(mediaQuery.matches)\n\n const handleChange = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches)\n }\n\n mediaQuery.addEventListener(\"change\", handleChange)\n return () => mediaQuery.removeEventListener(\"change\", handleChange)\n }, [])\n\n return prefersReducedMotion\n}\n\nexport interface PulseAnimationProps {\n /**\n * Children to animate\n */\n children?: React.ReactNode\n /**\n * Pulse intensity (0.1 - 2.0)\n */\n intensity?: number\n /**\n * Pulse duration in milliseconds\n */\n duration?: number\n /**\n * Number of pulse repetitions (0 for infinite)\n */\n repeat?: number\n /**\n * Delay between pulses in milliseconds\n */\n repeatDelay?: number\n /**\n * Pulse color variations\n */\n color?: string\n /**\n * Background color for pulse effect\n */\n backgroundColor?: string\n /**\n * Size scaling during pulse\n */\n scale?: number\n /**\n * Whether to show ring effect\n */\n showRing?: boolean\n /**\n * Number of ring layers\n */\n ringLayers?: number\n /**\n * Ring opacity\n */\n ringOpacity?: number\n /**\n * Whether animation is active\n */\n active?: boolean\n /**\n * Pulse shape\n */\n shape?: \"circle\" | \"square\" | \"rounded\"\n /**\n * Custom CSS class\n */\n className?: string\n /**\n * Custom styling\n */\n style?: React.CSSProperties\n /**\n * Whether to pulse on hover\n */\n pulseOnHover?: boolean\n /**\n * Whether to pulse on click\n */\n pulseOnClick?: boolean\n /**\n * Animation easing\n */\n easing?: \"linear\" | \"easeIn\" | \"easeOut\" | \"easeInOut\" | \"bounce\"\n /**\n * Callback when pulse completes\n */\n onPulseComplete?: () => void\n /**\n * Callback when clicked (if pulseOnClick is true)\n */\n onClick?: () => void\n /**\n * Size of the pulse container\n */\n size?: number | string\n /**\n * Whether to show glow effect\n */\n showGlow?: boolean\n /**\n * Glow intensity\n */\n glowIntensity?: number\n /**\n * Custom variants for complex animations\n */\n customVariants?: Variants\n /**\n * Pulse pattern type\n */\n pattern?: \"steady\" | \"heartbeat\" | \"notification\" | \"breathing\" | \"rapid\"\n}\n\nconst easingFunctions = {\n linear: \"linear\",\n easeIn: \"easeIn\",\n easeOut: \"easeOut\",\n easeInOut: \"easeInOut\",\n bounce: \"easeOut\",\n} as const\n\nconst pulsePatterns = {\n steady: {\n scale: [1, 1.1, 1],\n opacity: [0.7, 1, 0.7],\n duration: 1.5,\n repeatType: \"loop\" as const,\n },\n heartbeat: {\n scale: [1, 1.05, 1, 1.15, 1],\n opacity: [0.7, 0.9, 0.8, 1, 0.7],\n duration: 1.2,\n repeatType: \"loop\" as const,\n },\n notification: {\n scale: [1, 1.2, 1.1, 1.3, 1],\n opacity: [0.8, 1, 0.9, 1, 0.8],\n duration: 0.8,\n repeatType: \"loop\" as const,\n },\n breathing: {\n scale: [1, 1.05, 1],\n opacity: [0.5, 1, 0.5],\n duration: 3,\n repeatType: \"loop\" as const,\n },\n rapid: {\n scale: [1, 1.08, 1],\n opacity: [0.8, 1, 0.8],\n duration: 0.5,\n repeatType: \"loop\" as const,\n },\n}\n\n/**\n * Ring pulse effect component\n */\nfunction PulseRings({\n layers,\n color,\n opacity,\n size,\n shape,\n duration,\n active,\n easing,\n}: {\n layers: number\n color: string\n opacity: number\n size: number | string\n shape: \"circle\" | \"square\" | \"rounded\"\n duration: number\n active: boolean\n easing: keyof typeof easingFunctions\n}) {\n const getShapeClasses = () => {\n switch (shape) {\n case \"circle\":\n return \"rounded-full\"\n case \"square\":\n return \"rounded-none\"\n case \"rounded\":\n return \"rounded-lg\"\n default:\n return \"rounded-full\"\n }\n }\n\n return (\n <div className=\"absolute inset-0 pointer-events-none\">\n {Array.from({ length: layers }, (_, i) => (\n <motion.div\n key={i}\n className={cn(\"absolute border-2\", getShapeClasses())}\n style={{\n borderColor: color,\n width: size,\n height: size,\n left: \"50%\",\n top: \"50%\",\n transform: \"translate(-50%, -50%)\",\n }}\n animate={\n active\n ? {\n scale: [1, 1.5 + i * 0.2],\n opacity: [opacity, 0],\n }\n : {}\n }\n transition={{\n duration: duration / 1000,\n delay: i * 0.1,\n repeat: Infinity,\n ease: easingFunctions[easing],\n }}\n />\n ))}\n </div>\n )\n}\n\n/**\n * Glow effect component\n */\nfunction PulseGlow({\n color,\n intensity,\n size,\n shape,\n active,\n duration,\n easing,\n}: {\n color: string\n intensity: number\n size: number | string\n shape: \"circle\" | \"square\" | \"rounded\"\n active: boolean\n duration: number\n easing: keyof typeof easingFunctions\n}) {\n const getShapeClasses = () => {\n switch (shape) {\n case \"circle\":\n return \"rounded-full\"\n case \"square\":\n return \"rounded-none\"\n case \"rounded\":\n return \"rounded-lg\"\n default:\n return \"rounded-full\"\n }\n }\n\n return (\n <motion.div\n className={cn(\"absolute inset-0 pointer-events-none\", getShapeClasses())}\n style={{\n background: `radial-gradient(circle, ${color}40, transparent)`,\n filter: `blur(${intensity * 10}px)`,\n width: size,\n height: size,\n }}\n animate={\n active\n ? {\n scale: [1, 1.3, 1],\n opacity: [0.3, 0.7, 0.3],\n }\n : {}\n }\n transition={{\n duration: duration / 1000,\n repeat: Infinity,\n ease: easingFunctions[easing],\n }}\n />\n )\n}\n\n/**\n * Pulse animation component with customizable effects\n */\nexport function PulseAnimation({\n children,\n intensity = 1,\n duration = 1500,\n repeat = 0,\n repeatDelay = 0,\n color = \"#3b82f6\",\n backgroundColor,\n scale = 1.1,\n showRing = true,\n ringLayers = 3,\n ringOpacity = 0.6,\n active = true,\n shape = \"circle\",\n className,\n style,\n pulseOnHover = false,\n pulseOnClick = false,\n easing = \"easeOut\",\n onPulseComplete,\n onClick,\n size = \"100%\",\n showGlow = false,\n glowIntensity = 1,\n customVariants,\n pattern = \"steady\",\n}: PulseAnimationProps) {\n const [isHovered, setIsHovered] = useState(false)\n const [isClicked, setIsClicked] = useState(false)\n const [pulseCount, setPulseCount] = useState(0)\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const isActive =\n active &&\n (!pulseOnHover || isHovered) &&\n (!pulseOnClick || isClicked) &&\n !prefersReducedMotion\n\n const patternConfig = pulsePatterns[pattern]\n const variants: Variants = customVariants || {\n pulse: {\n scale: patternConfig.scale.map(\n (s) => s * (1 + (intensity - 1) * (scale - 1))\n ),\n opacity: patternConfig.opacity,\n transition: {\n duration: (duration / 1000) * (patternConfig.duration / 1.5),\n repeat: repeat === 0 ? Infinity : repeat - 1,\n repeatType: patternConfig.repeatType,\n repeatDelay: repeatDelay / 1000,\n ease: easingFunctions[easing],\n },\n },\n idle: {\n scale: 1,\n opacity: 1,\n },\n }\n\n useEffect(() => {\n if (repeat > 0 && isActive) {\n const timer = setTimeout(() => {\n setPulseCount((prev) => prev + 1)\n if (pulseCount + 1 >= repeat) {\n onPulseComplete?.()\n }\n }, duration + repeatDelay)\n\n return () => clearTimeout(timer)\n }\n }, [pulseCount, repeat, duration, repeatDelay, isActive, onPulseComplete])\n\n const handleClick = () => {\n if (pulseOnClick) {\n setIsClicked(true)\n setTimeout(() => setIsClicked(false), duration)\n }\n onClick?.()\n }\n\n const getShapeClasses = () => {\n switch (shape) {\n case \"circle\":\n return \"rounded-full\"\n case \"square\":\n return \"rounded-none\"\n case \"rounded\":\n return \"rounded-lg\"\n default:\n return \"rounded-full\"\n }\n }\n\n return (\n <motion.div\n className={cn(\n \"relative inline-flex items-center justify-center\",\n pulseOnHover && \"cursor-pointer\",\n pulseOnClick && \"cursor-pointer\",\n getShapeClasses(),\n className\n )}\n style={{\n width: size,\n height: size,\n backgroundColor,\n ...style,\n }}\n variants={variants}\n animate={isActive ? \"pulse\" : \"idle\"}\n onHoverStart={() => setIsHovered(true)}\n onHoverEnd={() => setIsHovered(false)}\n onClick={handleClick}\n onAnimationComplete={() => {\n if (repeat > 0 && pulseCount >= repeat - 1) {\n onPulseComplete?.()\n }\n }}\n >\n {/* Glow effect */}\n {showGlow && isActive && (\n <PulseGlow\n color={color}\n intensity={glowIntensity}\n size={size}\n shape={shape}\n active={isActive}\n duration={duration}\n easing={easing}\n />\n )}\n\n {/* Ring effects */}\n {showRing && isActive && (\n <PulseRings\n layers={ringLayers}\n color={color}\n opacity={ringOpacity}\n size={size}\n shape={shape}\n duration={duration}\n active={isActive}\n easing={easing}\n />\n )}\n\n {/* Content */}\n <div className=\"relative z-10\">{children}</div>\n </motion.div>\n )\n}\n\n/**\n * Attention-grabbing dot pulse\n */\nexport function PulseDot({\n color = \"#ef4444\",\n size = 12,\n ...props\n}: Omit<PulseAnimationProps, \"children\" | \"size\"> & {\n size?: number\n}) {\n return (\n <PulseAnimation\n size={size}\n color={color}\n shape=\"circle\"\n pattern=\"notification\"\n showRing\n ringLayers={2}\n {...props}\n >\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: color }}\n />\n </PulseAnimation>\n )\n}\n\n/**\n * Heartbeat pulse indicator\n */\nexport function HeartbeatPulse({\n color = \"#ef4444\",\n ...props\n}: Omit<PulseAnimationProps, \"pattern\">) {\n return (\n <PulseAnimation\n pattern=\"heartbeat\"\n color={color}\n showGlow\n glowIntensity={0.8}\n {...props}\n />\n )\n}\n\n/**\n * Breathing pulse effect\n */\nexport function BreathingPulse({\n color = \"#3b82f6\",\n ...props\n}: Omit<PulseAnimationProps, \"pattern\">) {\n return (\n <PulseAnimation\n pattern=\"breathing\"\n color={color}\n intensity={0.7}\n showGlow\n glowIntensity={1.2}\n {...props}\n />\n )\n}\n\n/**\n * Preset pulse animation variations\n */\nexport const PulsePresets = {\n /**\n * Notification indicator\n */\n Notification: (\n props: Omit<\n PulseAnimationProps,\n \"children\" | \"pattern\" | \"showRing\" | \"size\"\n > & {\n size?: number\n }\n ) => <PulseDot {...props} />,\n\n /**\n * Loading pulse\n */\n Loading: (props: Omit<PulseAnimationProps, \"pattern\" | \"shape\">) => (\n <PulseAnimation pattern=\"steady\" shape=\"circle\" showGlow {...props} />\n ),\n\n /**\n * Alert pulse\n */\n Alert: (\n props: Omit<PulseAnimationProps, \"color\" | \"pattern\" | \"intensity\">\n ) => (\n <PulseAnimation\n color=\"#ef4444\"\n pattern=\"rapid\"\n intensity={1.5}\n showRing\n showGlow\n {...props}\n />\n ),\n\n /**\n * Success pulse\n */\n Success: (props: Omit<PulseAnimationProps, \"color\" | \"pattern\">) => (\n <PulseAnimation\n color=\"#10b981\"\n pattern=\"breathing\"\n showGlow\n glowIntensity={0.8}\n repeat={3}\n {...props}\n />\n ),\n\n /**\n * Interactive button pulse\n */\n Button: (props: Omit<PulseAnimationProps, \"pulseOnHover\" | \"shape\">) => (\n <PulseAnimation\n pulseOnHover\n shape=\"rounded\"\n pattern=\"steady\"\n intensity={1.2}\n {...props}\n />\n ),\n\n /**\n * Avatar status pulse\n */\n Status: (\n props: Omit<PulseAnimationProps, \"shape\" | \"showRing\" | \"ringLayers\">\n ) => (\n <PulseAnimation\n shape=\"circle\"\n showRing\n ringLayers={2}\n pattern=\"breathing\"\n {...props}\n />\n ),\n}\n\nexport default PulseAnimation\n"
}
],
"type": "components:animation"
}