portfolio-xs
Version:
This is a tool to generate portfolio based with your markdown file
112 lines (102 loc) • 3.7 kB
JSX
import React, { useEffect, useState, useMemo, Suspense } from "react";
import { Routes, Route, HashRouter as Router } from "react-router-dom";
import Layout from "./layout.jsx";
import { Flipper, Flipped } from "react-flip-toolkit";
import ProjectCard from "./projectCard.jsx";
import pageMap from "./pageMap.js";
export default function App() {
const [rawList, setRawList] = useState([]);
const [filterCategory, setFilterCategory] = useState("All");
const [sortOrder, setSortOrder] = useState("Created");
const [columns, setColumns] = useState(1);
useEffect(() => {
fetch("./rawList.json")
.then((res) => res.json())
.then(setRawList)
.catch((err) => console.error("Failed to load project list", err));
}, []);
// 1) filter + sort first
const visibleList = useMemo(() => {
const filtered =
filterCategory === "All"
? rawList
: rawList.filter((p) => p.category === filterCategory);
const sorted = [...filtered].sort((a, b) => {
const da = sortOrder === "Created" ? a.created : a.updated;
const db = sortOrder === "Created" ? b.created : b.updated;
return new Date(db) - new Date(da);
});
return sorted;
}, [rawList, filterCategory, sortOrder]);
// 2) chunk AFTER filter/sort
const chunkProjects = (projects, cols) => {
const rows = [];
for (let i = 0; i < projects.length; i += cols) {
rows.push(projects.slice(i, i + cols));
}
return rows;
};
const chunked = useMemo(() => chunkProjects(visibleList, columns), [visibleList, columns]);
// 3) layout logic should not depend on chunked (which depends on columns)
useEffect(() => {
const updateLayout = () => {
const width = window.innerWidth;
const count = visibleList.length;
if (width >= 1360) {
// e.g., only go 3 columns if there are enough items to make it worthwhile
setColumns(count >= 9 ? 3 : 2);
} else if (width >= 680) {
setColumns(2);
} else {
setColumns(1);
}
};
updateLayout();
window.addEventListener("resize", updateLayout);
return () => window.removeEventListener("resize", updateLayout);
}, [visibleList.length]);
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route
path="/"
element={
<Layout
rawList={rawList}
filterCategory={filterCategory}
sortOrder={sortOrder}
setFilterCategory={setFilterCategory}
setSortOrder={setSortOrder}
/>
}
>
<Route
index
element={
<Flipper flipKey={`${columns}-${filterCategory}-${sortOrder}-${visibleList.length}`}>
<div className="project-list">
{chunked.map((row, rowIndex) => (
<div className="project-row" key={`row-${columns}-${rowIndex}`}>
{row.map((project) => (
<Flipped key={project.path} flipId={project.path}>
<div className="card-wrap">
<ProjectCard project={project} />
</div>
</Flipped>
))}
</div>
))}
</div>
</Flipper>
}
/>
{Object.entries(pageMap).map(([path, Comp]) => (
<Route key={path} path={path} element={<Comp />} />
))}
</Route>
</Routes>
</Suspense>
</Router>
);
}