v0.7.0 Add filtering feature in projects to filter project cards based on categories

This commit is contained in:
Murtadha 2024-07-15 15:09:00 -04:00
parent ff04802a61
commit 5a4c05fcba
7 changed files with 130 additions and 7 deletions

View file

@ -1,10 +1,12 @@
import React, { useState, useCallback } from "react";
import React, { useState, useCallback, useMemo } from "react";
import ProjectCard from "./projectCard/ProjectCard";
import ProjectModal from "./projectModal/ProjectModal";
import styles from "./Projects.module.css";
function Projects({ title, data }) {
const [selectedProject, setSelectedProject] = useState(null);
const [activeFilter, setActiveFilter] = useState("All");
const [animatingOut, setAnimatingOut] = useState(false);
const openModal = (project) => {
setSelectedProject(project);
@ -14,12 +16,39 @@ function Projects({ title, data }) {
setSelectedProject(null);
}, []);
const categories = useMemo(() => {
const cats = new Set(data.map((project) => project.category));
return ["All", ...Array.from(cats)];
}, [data]);
const filteredProjects = useMemo(() => {
if (activeFilter === "All") return data;
return data.filter((project) => project.category === activeFilter);
}, [data, activeFilter]);
const handleFilterClick = (category) => {
if (category !== activeFilter) {
setAnimatingOut(true);
setTimeout(() => {
setActiveFilter(category);
setAnimatingOut(false);
}, 300); // This should match the CSS animation duration
}
};
return (
<section className={styles.projects}>
<h2 className={styles.sectionTitle}>{title}</h2>
<div className={styles.filterContainer}>
{categories.map((category) => (
<button key={category} className={`${styles.filterButton} ${activeFilter === category ? styles.active : ""}`} onClick={() => handleFilterClick(category)}>
{category}
</button>
))}
</div>
<div className={styles.projectGrid}>
{data.map((project) => (
<ProjectCard key={project.id} project={project} onClick={openModal} />
{filteredProjects.map((project) => (
<ProjectCard key={project.id} project={project} onClick={openModal} className={animatingOut ? styles.fadeOut : styles.fadeIn} />
))}
</div>
{selectedProject && <ProjectModal project={selectedProject} onClose={closeModal} />}

View file

@ -11,12 +11,64 @@
color: #333;
}
.filterContainer {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 30px;
}
.filterButton {
background: none;
border: 1px solid #333;
padding: 8px 16px;
margin: 0 8px 8px 0;
cursor: pointer;
transition: all 0.3s ease;
}
.filterButton:hover,
.filterButton.active {
background-color: #333;
color: #fff;
}
.projectGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 2rem;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(20px);
}
}
.fadeIn {
animation: fadeIn 0.3s ease forwards;
}
.fadeOut {
animation: fadeOut 0.3s ease forwards;
}
@media (max-width: 768px) {
.projects {
padding: 30px 5%;

View file

@ -1,13 +1,14 @@
import React from "react";
import styles from "./ProjectCard.module.css";
function ProjectCard({ project, onClick }) {
function ProjectCard({ project, onClick, className }) {
return (
<div className={styles.card} onClick={() => onClick(project)}>
<div className={`${styles.card} ${className}`} onClick={() => onClick(project)}>
<div className={styles.imageSlider}>
<img src={project.images[0]} alt={`${project.title} - main image`} className={styles.projectImage} />
</div>
<h3 className={styles.title}>{project.title}</h3>
<p className={styles.category}>{project.category}</p>
<p className={styles.description}>{project.description}</p>
</div>
);

View file

@ -36,3 +36,9 @@
margin: 0 1rem 1rem;
color: #666;
}
.category {
font-size: 0.8rem;
color: #666;
margin: 0 1rem;
}

View file

@ -83,7 +83,7 @@
margin-right: 1rem;
margin-top: 1rem;
padding: 0.5rem 1rem;
background-color: #007bff;
background-color: var(--accent-color);
color: white;
text-decoration: none;
border-radius: 4px;