init project files
This commit is contained in:
117
components/sections/Projects.tsx
Normal file
117
components/sections/Projects.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
'use client';
|
||||
import { motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { projects } from '@/content/projects';
|
||||
import styles from './Projects.module.scss';
|
||||
import { SiGithub } from 'react-icons/si';
|
||||
import { ArrowUpRight } from 'lucide-react';
|
||||
|
||||
export default function Projects() {
|
||||
const featured = projects.find(p => p.featured);
|
||||
const rest = projects.filter(p => !p.featured);
|
||||
const hasProjects = projects.length > 0;
|
||||
|
||||
return (
|
||||
<section className={styles.section} id="projects">
|
||||
<div className={styles.container}>
|
||||
<motion.header
|
||||
className={styles.header}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<h2 className={styles.heading}>Selected Work<span className={styles.dot}>.</span></h2>
|
||||
<p className={styles.subtitle}>
|
||||
{hasProjects ? 'A selection of recent projects and side work.' : 'Projects will be added here soon.'}
|
||||
</p>
|
||||
</motion.header>
|
||||
|
||||
{!hasProjects && (
|
||||
<motion.div
|
||||
className={styles.empty}
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<p className={styles.emptyText}>Nothing here yet — check back later or get in touch.</p>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Featured Project */}
|
||||
{hasProjects && featured && (
|
||||
<motion.div
|
||||
className={styles.featured}
|
||||
initial={{ opacity: 0, y: 28 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, ease: [0.22, 0.61, 0.36, 1] }}
|
||||
>
|
||||
{featured.image && (
|
||||
<div className={styles.featuredImage}>
|
||||
<Image src={featured.image} alt={featured.title} fill style={{ objectFit: 'cover' }} />
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.featuredContent}>
|
||||
<h3>{featured.title}</h3>
|
||||
<p>{featured.description}</p>
|
||||
<div className={styles.links}>
|
||||
{featured.liveUrl && (
|
||||
<a href={featured.liveUrl} target="_blank" rel="noopener noreferrer" className={styles.link}>
|
||||
Live <ArrowUpRight size={14} />
|
||||
</a>
|
||||
)}
|
||||
{featured.githubUrl && (
|
||||
<a href={featured.githubUrl} target="_blank" rel="noopener noreferrer" className={styles.linkGhost}>
|
||||
<SiGithub size={15} /> GitHub
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Project Grid */}
|
||||
{hasProjects && (
|
||||
<motion.div
|
||||
className={styles.grid}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: '-20px' }}
|
||||
variants={{ visible: { transition: { staggerChildren: 0.08, delayChildren: 0.05 } } }}
|
||||
>
|
||||
{rest.map((project, i) => (
|
||||
<motion.article
|
||||
key={project.id}
|
||||
className={styles.card}
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: { opacity: 1, y: 0 },
|
||||
}}
|
||||
transition={{ duration: 0.45, ease: [0.22, 0.61, 0.36, 1] }}
|
||||
whileHover={{ y: -5, transition: { duration: 0.2 } }}
|
||||
>
|
||||
<h3 className={styles.cardTitle}>{project.title}</h3>
|
||||
<p className={styles.cardDesc}>{project.description}</p>
|
||||
<div className={styles.cardLinks}>
|
||||
{project.liveUrl && (
|
||||
<a href={project.liveUrl} target="_blank" rel="noopener noreferrer" className={styles.link}>
|
||||
View <ArrowUpRight size={13} />
|
||||
</a>
|
||||
)}
|
||||
{project.githubUrl && (
|
||||
<a href={project.githubUrl} target="_blank" rel="noopener noreferrer" className={styles.linkGhost}>
|
||||
<SiGithub size={14} />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</motion.article>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user