1+ "use client" ;
2+ import React , { useEffect , useId , useState } from "react" ;
3+ import { AnimatePresence , motion } from "motion/react" ;
4+ import { useRef } from "react" ;
5+ import { SparklesCore } from "./sparkles" ;
6+ import { cn } from "@/app/lib/utils" ;
7+
8+ export const Cover = ( {
9+ children,
10+ className,
11+ } : {
12+ children ?: React . ReactNode ;
13+ className ?: string ;
14+ } ) => {
15+ const [ hovered , setHovered ] = useState ( false ) ;
16+
17+ const ref = useRef < HTMLDivElement > ( null ) ;
18+
19+ const [ containerWidth , setContainerWidth ] = useState ( 0 ) ;
20+ const [ beamPositions , setBeamPositions ] = useState < number [ ] > ( [ ] ) ;
21+
22+ useEffect ( ( ) => {
23+ if ( ref . current ) {
24+ setContainerWidth ( ref . current ?. clientWidth ?? 0 ) ;
25+
26+ const height = ref . current ?. clientHeight ?? 0 ;
27+ const numberOfBeams = Math . floor ( height / 10 ) ; // Adjust the divisor to control the spacing
28+ const positions = Array . from (
29+ { length : numberOfBeams } ,
30+ ( _ , i ) => ( i + 1 ) * ( height / ( numberOfBeams + 1 ) ) ,
31+ ) ;
32+ setBeamPositions ( positions ) ;
33+ }
34+ } , [ ref . current ] ) ;
35+
36+ return (
37+ < div
38+ onMouseEnter = { ( ) => setHovered ( true ) }
39+ onMouseLeave = { ( ) => setHovered ( false ) }
40+ ref = { ref }
41+ className = "relative hover:bg-[#0ac529] cursor-pointer group/cover inline-block dark:bg-green-950 bg-black px-2 py-2 transition duration-200 rounded-sm"
42+ >
43+ < AnimatePresence >
44+ { hovered && (
45+ < motion . div
46+ initial = { { opacity : 0 } }
47+ animate = { { opacity : 1 } }
48+ exit = { { opacity : 0 } }
49+ transition = { {
50+ opacity : {
51+ duration : 0.2 ,
52+ } ,
53+ } }
54+ className = "h-full w-full overflow-hidden absolute inset-0"
55+ >
56+ < motion . div
57+ animate = { {
58+ translateX : [ "-50%" , "0%" ] ,
59+ } }
60+ transition = { {
61+ translateX : {
62+ duration : 10 ,
63+ ease : "linear" ,
64+ repeat : Infinity ,
65+ } ,
66+ } }
67+ className = "w-[200%] h-full flex"
68+ >
69+ < SparklesCore
70+ background = "transparent"
71+ minSize = { 0.4 }
72+ maxSize = { 1 }
73+ particleDensity = { 500 }
74+ className = "w-full h-full"
75+ particleColor = "#000"
76+ />
77+ < SparklesCore
78+ background = "transparent"
79+ minSize = { 0.4 }
80+ maxSize = { 1 }
81+ particleDensity = { 500 }
82+ className = "w-full h-full"
83+ particleColor = "#000"
84+ />
85+ </ motion . div >
86+ </ motion . div >
87+ ) }
88+ </ AnimatePresence >
89+ { beamPositions . map ( ( position , index ) => (
90+ < Beam
91+ key = { index }
92+ hovered = { hovered }
93+ duration = { Math . random ( ) * 2 + 1 }
94+ delay = { Math . random ( ) * 2 + 1 }
95+ width = { containerWidth }
96+ style = { {
97+ top : `${ position } px` ,
98+ } }
99+ />
100+ ) ) }
101+ < motion . span
102+ key = { String ( hovered ) }
103+ animate = { {
104+ scale : hovered ? 0.8 : 1 ,
105+ x : hovered ? [ 0 , - 30 , 30 , - 30 , 30 , 0 ] : 0 ,
106+ y : hovered ? [ 0 , 30 , - 30 , 30 , - 30 , 0 ] : 0 ,
107+ } }
108+ exit = { {
109+ filter : "none" ,
110+ scale : 1 ,
111+ x : 0 ,
112+ y : 0 ,
113+ } }
114+ transition = { {
115+ duration : 0.2 ,
116+ x : {
117+ duration : 0.2 ,
118+ repeat : Infinity ,
119+ repeatType : "loop" ,
120+ } ,
121+ y : {
122+ duration : 0.2 ,
123+ repeat : Infinity ,
124+ repeatType : "loop" ,
125+ } ,
126+ scale : {
127+ duration : 0.2 ,
128+ } ,
129+ filter : {
130+ duration : 0.2 ,
131+ } ,
132+ } }
133+ className = { cn (
134+ "dark:text-white inline-block text-neutral-900 relative z-20 group-hover/cover:text-black transition duration-200" ,
135+ className ,
136+ ) }
137+ >
138+ { children }
139+ </ motion . span >
140+ < CircleIcon className = "absolute -right-0.5 -top-0.5" />
141+ < CircleIcon className = "absolute -bottom-0.5 -right-0.5" delay = { 0.4 } />
142+ < CircleIcon className = "absolute -left-0.5 -top-0.5" delay = { 0.8 } />
143+ < CircleIcon className = "absolute -bottom-0.5 -left-0.5" delay = { 1.6 } />
144+ </ div >
145+ ) ;
146+ } ;
147+
148+ export const Beam = ( {
149+ className,
150+ delay,
151+ duration,
152+ hovered,
153+ width = 600 ,
154+ ...svgProps
155+ } : {
156+ className ?: string ;
157+ delay ?: number ;
158+ duration ?: number ;
159+ hovered ?: boolean ;
160+ width ?: number ;
161+ } & React . ComponentProps < typeof motion . svg > ) => {
162+ const id = useId ( ) ;
163+
164+ return (
165+ < motion . svg
166+ width = { width ?? "600" }
167+ height = "1"
168+ viewBox = { `0 0 ${ width ?? "600" } 1` }
169+ fill = "none"
170+ xmlns = "http://www.w3.org/2000/svg"
171+ className = { cn ( "absolute inset-x-0 w-full" , className ) }
172+ { ...svgProps }
173+ >
174+ < motion . path
175+ d = { `M0 0.5H${ width ?? "600" } ` }
176+ stroke = { `url(#svgGradient-${ id } )` }
177+ />
178+
179+ < defs >
180+ < motion . linearGradient
181+ id = { `svgGradient-${ id } ` }
182+ key = { String ( hovered ) }
183+ gradientUnits = "userSpaceOnUse"
184+ initial = { {
185+ x1 : "0%" ,
186+ x2 : hovered ? "-10%" : "-5%" ,
187+ y1 : 0 ,
188+ y2 : 0 ,
189+ } }
190+ animate = { {
191+ x1 : "110%" ,
192+ x2 : hovered ? "100%" : "105%" ,
193+ y1 : 0 ,
194+ y2 : 0 ,
195+ } }
196+ transition = { {
197+ duration : hovered ? 0.5 : ( duration ?? 2 ) ,
198+ ease : "linear" ,
199+ repeat : Infinity ,
200+ delay : hovered ? Math . random ( ) * ( 1 - 0.2 ) + 0.2 : 0 ,
201+ repeatDelay : hovered ? Math . random ( ) * ( 2 - 1 ) + 1 : ( delay ?? 1 ) ,
202+ } }
203+ >
204+ < stop stopColor = "#2EB9DF" stopOpacity = "0" />
205+ < stop stopColor = "#3b82f6" />
206+ < stop offset = "1" stopColor = "#3b82f6" stopOpacity = "0" />
207+ </ motion . linearGradient >
208+ </ defs >
209+ </ motion . svg >
210+ ) ;
211+ } ;
212+
213+ export const CircleIcon = ( {
214+ className,
215+ delay,
216+ } : {
217+ className ?: string ;
218+ delay ?: number ;
219+ } ) => {
220+ return (
221+ < div
222+ className = { cn (
223+ `pointer-events-none animate-pulse group-hover/cover:hidden group-hover/cover:opacity-100 group h-2 w-2 rounded-full bg-neutral-600 dark:bg-black opacity-20 group-hover/cover:bg-black` ,
224+ className ,
225+ ) }
226+ > </ div >
227+ ) ;
228+ } ;
0 commit comments