"use client";
import type { HTMLAttributes, MouseEvent } from "react";
import {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef
} from "react";
import {
motion,
useAnimation,
useReducedMotion,
type Variants
} from "framer-motion";
import { cn } from "../../lib/utils";
export interface FilePenLineIconHandle {
startAnimation: () => void;
stopAnimation: () => void;
}
interface FilePenLineIconProps extends HTMLAttributes {
size?: number;
active?: boolean;
}
const PEN_VARIANTS: Variants = {
normal: {
rotate: 0,
x: 0,
y: 0
},
animate: {
rotate: [-0.3, 0.2, -0.4],
x: [0, -0.5, 1, 0],
y: [0, 1, -0.5, 0],
transition: {
duration: 0.5,
repeat: 1,
ease: "easeInOut"
}
},
active: {
rotate: [-0.3, 0.2, -0.4],
x: [0, -0.5, 1, 0],
y: [0, 1, -0.5, 0],
transition: {
duration: 0.5,
repeat: Number.POSITIVE_INFINITY,
repeatDelay: 0.75,
ease: "easeInOut"
}
}
};
const LINE_VARIANTS: Variants = {
normal: { d: "M8 18h1" },
animate: {
d: "M8 18h5",
transition: { duration: 0.5 }
},
active: {
d: ["M8 18h1", "M8 18h5", "M8 18h1"],
transition: {
duration: 0.8,
repeat: Number.POSITIVE_INFINITY,
repeatDelay: 0.45,
ease: "easeInOut"
}
}
};
const FilePenLineIcon = forwardRef(
(
{
active = false,
onMouseEnter,
onMouseLeave,
className,
size = 28,
...props
},
ref
) => {
const controls = useAnimation();
const reduceMotion = useReducedMotion();
const isControlledRef = useRef(false);
const startAnimation = useCallback(() => {
if (reduceMotion) {
return;
}
void controls.start(active ? "active" : "animate");
}, [active, controls, reduceMotion]);
const stopAnimation = useCallback(() => {
void controls.start("normal");
}, [controls]);
useImperativeHandle(ref, () => {
isControlledRef.current = true;
return {
startAnimation,
stopAnimation
};
});
useEffect(() => {
if (active) {
startAnimation();
return;
}
stopAnimation();
}, [active, startAnimation, stopAnimation]);
const handleMouseEnter = useCallback(
(event: MouseEvent) => {
if (isControlledRef.current) {
onMouseEnter?.(event);
} else {
startAnimation();
}
},
[onMouseEnter, startAnimation]
);
const handleMouseLeave = useCallback(
(event: MouseEvent) => {
if (isControlledRef.current) {
onMouseLeave?.(event);
} else {
stopAnimation();
}
},
[onMouseLeave, stopAnimation]
);
return (
);
}
);
FilePenLineIcon.displayName = "FilePenLineIcon";
export { FilePenLineIcon };