| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- "use client";
- import { motion, useAnimation } from "motion/react";
- import type { HTMLAttributes } from "react";
- import { forwardRef, useCallback, useImperativeHandle, useRef } from "react";
- import { cn } from "@/lib/utils";
- export interface SearchIconHandle {
- startAnimation: () => void;
- stopAnimation: () => void;
- }
- interface SearchIconProps extends HTMLAttributes<HTMLDivElement> {
- size?: number;
- }
- const SearchIcon = forwardRef<SearchIconHandle, SearchIconProps>(
- ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
- const controls = useAnimation();
- const isControlledRef = useRef(false);
- useImperativeHandle(ref, () => {
- isControlledRef.current = true;
- return {
- startAnimation: () => controls.start("animate"),
- stopAnimation: () => controls.start("normal"),
- };
- });
- const handleMouseEnter = useCallback(
- (e: React.MouseEvent<HTMLDivElement>) => {
- if (isControlledRef.current) {
- onMouseEnter?.(e);
- } else {
- controls.start("animate");
- }
- },
- [controls, onMouseEnter]
- );
- const handleMouseLeave = useCallback(
- (e: React.MouseEvent<HTMLDivElement>) => {
- if (isControlledRef.current) {
- onMouseLeave?.(e);
- } else {
- controls.start("normal");
- }
- },
- [controls, onMouseLeave]
- );
- return (
- <div
- className={cn(className)}
- onMouseEnter={handleMouseEnter}
- onMouseLeave={handleMouseLeave}
- {...props}
- >
- <motion.svg
- animate={controls}
- fill="none"
- height={size}
- stroke="currentColor"
- strokeLinecap="round"
- strokeLinejoin="round"
- strokeWidth="2"
- transition={{
- duration: 1,
- bounce: 0.3,
- }}
- variants={{
- normal: { x: 0, y: 0 },
- animate: {
- x: [0, 0, -3, 0],
- y: [0, -4, 0, 0],
- },
- }}
- viewBox="0 0 24 24"
- width={size}
- xmlns="http://www.w3.org/2000/svg"
- >
- <circle cx="11" cy="11" r="8" />
- <path d="m21 21-4.3-4.3" />
- </motion.svg>
- </div>
- );
- }
- );
- SearchIcon.displayName = "SearchIcon";
- export { SearchIcon };
|