11import React , { useState , useEffect , useRef , useCallback } from 'react' ;
2- import { Link , useLocation } from 'react-router-dom' ; // Import useLocation
3- import { FaExternalLinkAlt } from 'react-icons/fa' ;
2+ import { Link , useLocation } from 'react-router-dom' ;
3+ import { FaExternalLinkAlt , FaChevronRight } from 'react-icons/fa' ;
44import Dot from './Dot' ;
5- import { useAnimation } from '../context/AnimationContext' ; // Import useAnimation
5+ import { useAnimation } from '../context/AnimationContext' ;
66
77const ProjectCard = ( { project, size = 1 } ) => {
88 const colSpanClass =
@@ -15,8 +15,8 @@ const ProjectCard = ({ project, size = 1 }) => {
1515 isAnimationEnabled,
1616 showAnimationsHomepage,
1717 showAnimationsInnerPages,
18- } = useAnimation ( ) ; // Use the animation context
19- const location = useLocation ( ) ; // Get current location
18+ } = useAnimation ( ) ;
19+ const location = useLocation ( ) ;
2020
2121 const handleAnimationEnd = useCallback ( ( id ) => {
2222 setDots ( ( prevDots ) => prevDots . filter ( ( dot ) => dot . id !== id ) ) ;
@@ -29,23 +29,23 @@ const ProjectCard = ({ project, size = 1 }) => {
2929 ( ( location . pathname === '/' && showAnimationsHomepage ) ||
3030 ( location . pathname !== '/' && showAnimationsInnerPages ) )
3131 ) {
32- // Only spawn dots if animations are enabled and on homepage or everywhere
3332 const spawnDot = ( ) => {
34- if ( cardRef . current && dots . length < 10 ) {
33+ if ( cardRef . current && dots . length < 8 ) {
3534 const cardRect = cardRef . current . getBoundingClientRect ( ) ;
3635 const newDot = {
3736 id : dotIdRef . current ++ ,
38- size : Math . floor ( Math . random ( ) * 4 ) + 5 , // Size between 5 and 8
39- color : `hsl(0, ${ Math . floor ( Math . random ( ) * 30 ) + 70 } %, ${ Math . floor ( Math . random ( ) * 20 ) + 60 } %)` , // Tones of red
37+ size : Math . floor ( Math . random ( ) * 3 ) + 2 ,
38+ // Cyan/Teal/Greenish Matrix tones
39+ color : `hsl(${ Math . floor ( Math . random ( ) * 60 ) + 160 } , ${ Math . floor ( Math . random ( ) * 50 ) + 50 } %, ${ Math . floor ( Math . random ( ) * 30 ) + 60 } %)` ,
4040 initialX : Math . random ( ) * cardRect . width ,
41- initialY : - 5 , // Start slightly off-screen top
42- animationDuration : Math . random ( ) * 3 + 2 , // Duration between 2 and 5 seconds
41+ initialY : - 10 , // Start slightly off-screen top
42+ animationDuration : Math . random ( ) * 4 + 3 ,
4343 } ;
4444 setDots ( ( prevDots ) => [ ...prevDots , newDot ] ) ;
4545 }
4646 } ;
4747
48- interval = setInterval ( spawnDot , 500 ) ; // Spawn a new dot every 0.5 seconds
48+ interval = setInterval ( spawnDot , 600 ) ;
4949 }
5050
5151 return ( ) => clearInterval ( interval ) ;
@@ -60,40 +60,90 @@ const ProjectCard = ({ project, size = 1 }) => {
6060 return (
6161 < div
6262 ref = { cardRef }
63- className = { `block bg-gray-500/10 p-6 rounded-lg shadow-lg hover: bg-gray-500/20 transition-colors border border-gray-700 /50 cursor-pointer flex flex-col relative overflow-hidden ${ colSpanClass } ` }
63+ className = { `group relative flex flex-col overflow-hidden rounded-xl bg-gray-900/90 border border-gray-800 hover:border-cyan-500 /50 transition-all duration-500 ease-out hover:shadow-[0_0_20px_-5px_rgba(6,182,212,0.4)] hover:-translate-y-1 ${ colSpanClass } ` }
6464 >
65- { dots . map ( ( dot ) => (
66- < Dot
67- key = { dot . id }
68- id = { dot . id }
69- size = { dot . size }
70- color = { dot . color }
71- initialX = { dot . initialX }
72- initialY = { dot . initialY }
73- animationDuration = { dot . animationDuration }
74- onAnimationEnd = { handleAnimationEnd }
75- />
76- ) ) }
65+ { /* Background Grid Effect */ }
66+ < div className = "absolute inset-0 z-0 opacity-20 pointer-events-none bg-[linear-gradient(to_right,#4f4f4f2e_1px,transparent_1px),linear-gradient(to_bottom,#4f4f4f2e_1px,transparent_1px)] bg-[size:14px_24px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)]" />
67+
68+ { /* Tech Accents - Top Right */ }
69+ < div className = "absolute top-0 right-0 p-2 z-20" >
70+ < div className = "flex gap-1" >
71+ < div className = "w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-75" > </ div >
72+ < div className = "w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-100" > </ div >
73+ < div className = "w-1 h-1 rounded-full bg-gray-700 group-hover:bg-cyan-500/50 transition-colors duration-300 delay-150" > </ div >
74+ </ div >
75+ </ div >
76+
77+ { /* Dots Layer */ }
78+ < div className = "absolute inset-0 pointer-events-none z-0 overflow-hidden rounded-xl" >
79+ { dots . map ( ( dot ) => (
80+ < Dot
81+ key = { dot . id }
82+ id = { dot . id }
83+ size = { dot . size }
84+ color = { dot . color }
85+ initialX = { dot . initialX }
86+ initialY = { dot . initialY }
87+ animationDuration = { dot . animationDuration }
88+ onAnimationEnd = { handleAnimationEnd }
89+ />
90+ ) ) }
91+ </ div >
92+
93+ { /* Watermark */ }
94+ < div className = "absolute bottom-0 right-0 p-4 pointer-events-none z-0 opacity-5 group-hover:opacity-10 transition-opacity duration-500" >
95+ < span className = "text-6xl font-black font-mono text-cyan-500 tracking-tighter select-none" >
96+ FCX
97+ </ span >
98+ </ div >
99+
100+ { /* Content */ }
77101 < Link
78102 to = { `/projects/${ project . slug } ` }
79- className = "flex flex-col flex-grow relative z-10"
103+ className = "flex flex-col flex-grow relative z-10 p-6 "
80104 >
81- < h3 className = "font-arvo text-xl text-orange-400" > { project . title } </ h3 >
82- < hr className = "border-gray-700 -mx-6 mb-4 mt-4" />
83- < p className = "text-gray-400 flex-grow" > { project . description } </ p >
105+ < div className = "flex justify-between items-start mb-3" >
106+ < h3 className = "font-mono text-xl font-bold text-gray-100 group-hover:text-cyan-400 transition-colors tracking-tight" >
107+ { project . title }
108+ </ h3 >
109+ </ div >
110+
111+ < div className = "h-px w-12 bg-gradient-to-r from-cyan-500 to-transparent mb-4 group-hover:w-full transition-all duration-500 ease-out" />
112+
113+ < p className = "text-gray-400 text-sm leading-relaxed flex-grow font-sans" >
114+ { project . description }
115+ </ p >
116+
117+ < div className = "mt-6 flex items-center justify-between" >
118+ < div className = "flex items-center gap-2" >
119+ < span className = "text-[10px] font-mono text-cyan-500/70 border border-cyan-900/30 bg-cyan-950/30 px-2 py-1 rounded uppercase tracking-wider" >
120+ ID: { project . slug . split ( '-' ) [ 0 ] . toUpperCase ( ) }
121+ </ span >
122+ </ div >
123+ < FaChevronRight
124+ className = "text-gray-600 group-hover:text-cyan-400 transform group-hover:translate-x-1 transition-all duration-300"
125+ size = { 14 }
126+ />
127+ </ div >
84128 </ Link >
129+
130+ { /* External Link footer if exists */ }
85131 { project . link && (
86- < a
87- href = { project . link }
88- target = "_blank"
89- rel = "noopener noreferrer"
90- className = "text-red-500 hover:text-red-300 transition-colors mt-auto flex items-center relative z-10"
91- >
92- View Project < FaExternalLinkAlt className = "ml-1" size = { 12 } />
93- </ a >
132+ < div className = "relative z-10 px-6 pb-4" >
133+ < a
134+ href = { project . link }
135+ target = "_blank"
136+ rel = "noopener noreferrer"
137+ className = "inline-flex items-center gap-2 text-[10px] font-bold text-gray-500 hover:text-cyan-400 uppercase tracking-widest transition-colors border-t border-gray-800 pt-3 w-full"
138+ >
139+ < span className = "w-1.5 h-1.5 bg-current rounded-full animate-pulse" />
140+ Live System
141+ < FaExternalLinkAlt size = { 9 } className = "ml-auto" />
142+ </ a >
143+ </ div >
94144 ) }
95145 </ div >
96146 ) ;
97147} ;
98148
99- export default ProjectCard ;
149+ export default ProjectCard ;
0 commit comments