1
1
"use client" ;
2
- import React from "react" ;
2
+ import React , { useState } from "react" ;
3
3
import ProjectCard from "./ProjectCard" ;
4
4
import projects , { Project } from "../constants/projects" ;
5
- import { motion } from "framer-motion" ;
5
+ import { motion , AnimatePresence } from "framer-motion" ;
6
6
7
7
const Projects = ( ) => {
8
- // Group projects by year (assuming you'll add year to your project data)
8
+ const [ showAll , setShowAll ] = useState ( false ) ;
9
+
10
+ // Group projects by year
9
11
const projectsByYear = projects . reduce (
10
12
( acc : { [ key : string ] : Project [ ] } , project ) => {
11
- const year = project . year || 2023 ; // Default to 2023 if year is not specified
13
+ const year = project . year || 2023 ;
12
14
if ( ! acc [ year ] ) {
13
15
acc [ year ] = [ ] ;
14
16
}
@@ -23,6 +25,11 @@ const Projects = () => {
23
25
( a : string , b : string ) => parseInt ( b ) - parseInt ( a )
24
26
) ;
25
27
28
+ // Get visible years based on showAll state
29
+ const visibleYears = showAll ? sortedYears : sortedYears . slice ( 0 , 1 ) ; // Show first 2 years by default
30
+
31
+ const hasMoreYears = sortedYears . length > 2 ;
32
+
26
33
return (
27
34
< section className = "py-8 sm:py-16 px-3 sm:px-4 bg-gray-900" >
28
35
< div className = "max-w-4xl mx-auto" >
@@ -35,37 +42,94 @@ const Projects = () => {
35
42
Project Timeline
36
43
</ motion . h2 >
37
44
38
- < div className = "space-y-12 sm:space-y-16" >
39
- { sortedYears . map ( ( year , yearIndex ) => (
40
- < motion . div
41
- key = { year }
42
- initial = { { opacity : 0 , x : - 20 } }
43
- animate = { { opacity : 1 , x : 0 } }
44
- transition = { { duration : 0.5 , delay : yearIndex * 0.2 } }
45
- className = "relative"
46
- >
47
- { /* Year Label */ }
45
+ < AnimatePresence mode = "wait" >
46
+ < div className = "space-y-12 sm:space-y-16" >
47
+ { visibleYears . map ( ( year , yearIndex ) => (
48
48
< motion . div
49
- className = "flex items-center mb-6 sm:mb-8"
50
- initial = { { width : 0 } }
51
- animate = { { width : "100%" } }
52
- transition = { { duration : 0.8 , delay : yearIndex * 0.2 + 0.3 } }
49
+ key = { year }
50
+ initial = { { opacity : 0 , x : - 20 } }
51
+ animate = { { opacity : 1 , x : 0 } }
52
+ transition = { { duration : 0.5 , delay : yearIndex * 0.2 } }
53
+ className = "relative"
53
54
>
54
- < div className = "w-12 h-12 sm:w-16 sm:h-16 rounded-full bg-blue-600 flex items-center justify-center text-xl sm:text-2xl font-bold text-white" >
55
- { year }
55
+ { /* Year Label */ }
56
+ < motion . div
57
+ className = "flex items-center mb-6 sm:mb-8"
58
+ initial = { { width : 0 } }
59
+ animate = { { width : "100%" } }
60
+ transition = { { duration : 0.8 , delay : yearIndex * 0.2 + 0.3 } }
61
+ >
62
+ < div className = "w-12 h-12 sm:w-16 sm:h-16 rounded-full bg-blue-600 flex items-center justify-center text-xl sm:text-2xl font-bold text-white" >
63
+ { year }
64
+ </ div >
65
+ < div className = "h-0.5 flex-1 bg-blue-600 ml-3 sm:ml-4" > </ div >
66
+ </ motion . div >
67
+
68
+ { /* Projects Grid */ }
69
+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6" >
70
+ { projectsByYear [ year ] . map ( ( project , index ) => (
71
+ < ProjectCard
72
+ key = { project . name }
73
+ project = { project }
74
+ index = { index }
75
+ />
76
+ ) ) }
56
77
</ div >
57
- < div className = "h-0.5 flex-1 bg-blue-600 ml-3 sm:ml-4" > </ div >
58
78
</ motion . div >
79
+ ) ) }
80
+ </ div >
81
+ </ AnimatePresence >
59
82
60
- { /* Projects Grid */ }
61
- < div className = "grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6" >
62
- { projectsByYear [ year ] . map ( ( project , index ) => (
63
- < ProjectCard key = { index } project = { project } index = { index } />
64
- ) ) }
65
- </ div >
66
- </ motion . div >
67
- ) ) }
68
- </ div >
83
+ { /* Show More Button */ }
84
+ { hasMoreYears && (
85
+ < motion . div
86
+ initial = { { opacity : 0 } }
87
+ animate = { { opacity : 1 } }
88
+ transition = { { delay : 0.3 } }
89
+ className = "mt-12 flex justify-center"
90
+ >
91
+ < motion . button
92
+ onClick = { ( ) => setShowAll ( ! showAll ) }
93
+ className = "group relative px-6 py-2 text-sm font-medium text-gray-300 hover:text-white transition-colors"
94
+ whileHover = { { scale : 1.05 } }
95
+ whileTap = { { scale : 0.95 } }
96
+ >
97
+ < span className = "relative z-10" >
98
+ { showAll
99
+ ? "Show Less"
100
+ : `Show ${ sortedYears . length - 2 } More Years` }
101
+ </ span >
102
+ < motion . div
103
+ className = "absolute inset-0 bg-gradient-to-r from-blue-600/20 to-blue-500/20 rounded-full -z-0"
104
+ whileHover = { {
105
+ scale : 1.05 ,
106
+ background :
107
+ "linear-gradient(to right, rgba(37, 99, 235, 0.3), rgba(59, 130, 246, 0.3))" ,
108
+ } }
109
+ />
110
+ < motion . div
111
+ className = "absolute inset-0 border border-blue-500/50 rounded-full"
112
+ initial = { { scale : 1 } }
113
+ whileHover = { { scale : 1.05 } }
114
+ />
115
+
116
+ { /* Animated Arrow */ }
117
+ < motion . svg
118
+ className = { `w-4 h-4 ml-2 inline-block transition-transform duration-200 ${
119
+ showAll ? "rotate-180" : ""
120
+ } `}
121
+ viewBox = "0 0 24 24"
122
+ fill = "none"
123
+ stroke = "currentColor"
124
+ strokeWidth = "2"
125
+ strokeLinecap = "round"
126
+ strokeLinejoin = "round"
127
+ >
128
+ < polyline points = "6 9 12 15 18 9" />
129
+ </ motion . svg >
130
+ </ motion . button >
131
+ </ motion . div >
132
+ ) }
69
133
</ div >
70
134
</ section >
71
135
) ;
0 commit comments