chevron-right.tsx94 lines · main
1'use client';
2
3import type { Transition } from 'motion/react';
4import { motion, useAnimation } from 'motion/react';
5import type { HTMLAttributes } from 'react';
6import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
7
8import { cn } from '@/lib/utils';
9
10export interface ChevronRightIconHandle {
11 startAnimation: () => void;
12 stopAnimation: () => void;
13}
14
15interface ChevronRightIconProps extends HTMLAttributes<HTMLDivElement> {
16 size?: number;
17}
18
19const DEFAULT_TRANSITION: Transition = {
20 times: [0, 0.4, 1],
21 duration: 0.5,
22};
23
24const ChevronRightIcon = forwardRef<ChevronRightIconHandle, ChevronRightIconProps>(
25 ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
26 const controls = useAnimation();
27 const isControlledRef = useRef(false);
28
29 useImperativeHandle(ref, () => {
30 isControlledRef.current = true;
31 return {
32 startAnimation: () => controls.start('animate'),
33 stopAnimation: () => controls.start('normal'),
34 };
35 });
36
37 const handleMouseEnter = useCallback(
38 (e: React.MouseEvent<HTMLDivElement>) => {
39 if (isControlledRef.current) {
40 onMouseEnter?.(e);
41 } else {
42 controls.start('animate');
43 }
44 },
45 [controls, onMouseEnter],
46 );
47
48 const handleMouseLeave = useCallback(
49 (e: React.MouseEvent<HTMLDivElement>) => {
50 if (isControlledRef.current) {
51 onMouseLeave?.(e);
52 } else {
53 controls.start('normal');
54 }
55 },
56 [controls, onMouseLeave],
57 );
58
59 return (
60 <div
61 className={cn(className)}
62 onMouseEnter={handleMouseEnter}
63 onMouseLeave={handleMouseLeave}
64 {...props}
65 >
66 <svg
67 fill="none"
68 height={size}
69 stroke="currentColor"
70 strokeLinecap="round"
71 strokeLinejoin="round"
72 strokeWidth="2"
73 viewBox="0 0 24 24"
74 width={size}
75 xmlns="http://www.w3.org/2000/svg"
76 >
77 <motion.path
78 animate={controls}
79 d="m9 18 6-6-6-6"
80 transition={DEFAULT_TRANSITION}
81 variants={{
82 normal: { x: 0 },
83 animate: { x: [0, 2, 0] },
84 }}
85 />
86 </svg>
87 </div>
88 );
89 },
90);
91
92ChevronRightIcon.displayName = 'ChevronRightIcon';
93
94export { ChevronRightIcon };