cog.tsx103 lines · main
1'use client';
2
3import { motion, useAnimation } from 'motion/react';
4import type { HTMLAttributes } from 'react';
5import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
6
7import { cn } from '@/lib/utils';
8
9export interface CogIconHandle {
10 startAnimation: () => void;
11 stopAnimation: () => void;
12}
13
14interface CogIconProps extends HTMLAttributes<HTMLDivElement> {
15 size?: number;
16}
17
18const CogIcon = forwardRef<CogIconHandle, CogIconProps>(
19 ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
20 const controls = useAnimation();
21 const isControlledRef = useRef(false);
22
23 useImperativeHandle(ref, () => {
24 isControlledRef.current = true;
25
26 return {
27 startAnimation: () => controls.start('animate'),
28 stopAnimation: () => controls.start('normal'),
29 };
30 });
31
32 const handleMouseEnter = useCallback(
33 (e: React.MouseEvent<HTMLDivElement>) => {
34 if (isControlledRef.current) {
35 onMouseEnter?.(e);
36 } else {
37 controls.start('animate');
38 }
39 },
40 [controls, onMouseEnter],
41 );
42
43 const handleMouseLeave = useCallback(
44 (e: React.MouseEvent<HTMLDivElement>) => {
45 if (isControlledRef.current) {
46 onMouseLeave?.(e);
47 } else {
48 controls.start('normal');
49 }
50 },
51 [controls, onMouseLeave],
52 );
53 return (
54 <div
55 className={cn(className)}
56 onMouseEnter={handleMouseEnter}
57 onMouseLeave={handleMouseLeave}
58 {...props}
59 >
60 <motion.svg
61 animate={controls}
62 fill="none"
63 height={size}
64 stroke="currentColor"
65 strokeLinecap="round"
66 strokeLinejoin="round"
67 strokeWidth="2"
68 transition={{ type: 'spring', stiffness: 50, damping: 10 }}
69 variants={{
70 normal: {
71 rotate: 0,
72 },
73 animate: {
74 rotate: 180,
75 },
76 }}
77 viewBox="0 0 24 24"
78 width={size}
79 xmlns="http://www.w3.org/2000/svg"
80 >
81 <path d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" />
82 <path d="M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" />
83 <path d="M12 2v2" />
84 <path d="M12 22v-2" />
85 <path d="m17 20.66-1-1.73" />
86 <path d="M11 10.27 7 3.34" />
87 <path d="m20.66 17-1.73-1" />
88 <path d="m3.34 7 1.73 1" />
89 <path d="M14 12h8" />
90 <path d="M2 12h2" />
91 <path d="m20.66 7-1.73 1" />
92 <path d="m3.34 17 1.73-1" />
93 <path d="m17 3.34-1 1.73" />
94 <path d="m11 13.73-4 6.93" />
95 </motion.svg>
96 </div>
97 );
98 },
99);
100
101CogIcon.displayName = 'CogIcon';
102
103export { CogIcon };