"use client"; import type { LucideIcon } from "lucide-react"; import { motion, useReducedMotion } from "motion/react"; import { cn } from "@/lib/utils"; export type AnimatedIconVariant = | "draw" | "wiggle" | "spin" | "bounce" | "pop" | "pulse" | "nudge"; export interface AnimatedIconProps { /** Which hover animation to play. */ variant: AnimatedIconVariant; /** Icon size in px. */ size?: number; className?: string; /** Lucide icon component to animate. Ignored by the `draw` variant. */ icon?: LucideIcon; /** SVG path `d` strings the `draw` variant renders. Defaults to a checkmark. */ paths?: string[]; } const DEFAULT_PATHS = ["M5 13l4 4L19 7"]; /** * Seven hover-played icon micro-interactions behind a single `variant` prop: * `draw` self-draws its stroke (pathLength 0 → 1) — ignores `icon`, uses `paths`; * `wiggle` rocks side to side; `spin` rotates a full turn; `bounce` springs * vertically; `pop` springs its scale up and back; `pulse` loops a gentle * scale while hovered; `nudge` springs a small step sideways. Reduced-motion * renders a static icon (or a static checkmark for `draw`) with no animation. */ export function AnimatedIcon({ variant, size = 28, className, icon: Icon, paths = DEFAULT_PATHS, }: AnimatedIconProps) { const reduce = useReducedMotion(); if (variant === "draw") { if (reduce) { return ( ); } return ( ); } if (!Icon) return null; if (reduce) { return ; } switch (variant) { case "wiggle": return ( ); case "spin": return ( ); case "bounce": return ( ); case "pop": return ( ); case "pulse": return ( ); case "nudge": return ( ); default: return null; } }