"use client"; import { PROJECTS } from "@/data/content"; import { useStaggerReveal } from "@/hooks/useAnimations"; import React, { useState, useCallback, useRef, useEffect } from "react"; import ProjectCard from "@/components/ProjectCard"; import { Layout, Responsive, useContainerWidth, DefaultBreakpoints, verticalCompactor, } from "react-grid-layout"; import "react-grid-layout/css/styles.css"; import "react-resizable/css/styles.css"; const ROW_HEIGHT = 100; const SMALL_BREAKPOINT = 768; function buildLayoutsFromHeights(activeId: number, heights: Record): Record { const selected = PROJECTS.find((p) => p.id === activeId); const rest = PROJECTS.filter((p) => p.id !== activeId); const h = (id: number, fallback: number) => heights[id.toString()] ?? fallback; function twoCols(): Layout { let items: Layout = []; const selH = selected ? h(selected.id, 2) : 0; if (selected) { items = items.concat({ i: selected.id.toString(), x: 0, y: 0, w: 2, h: selH, }); } const colY = [selH, selH]; for (const p of rest) { const pH = h(p.id, 3); const col = colY[0] <= colY[1] ? 0 : 1; items = items.concat({ i: p.id.toString(), x: col, y: colY[col], w: 1, h: pH }); colY[col] += pH; } return items; } return { lg: twoCols(), md: twoCols(), sm: twoCols(), // small screens get the horizontal scroll row xs: twoCols(), xxs: twoCols(), }; } /* ------------------------------------------------------------------ */ /* Component */ /* ------------------------------------------------------------------ */ export default function Projects() { const visible = useStaggerReveal(PROJECTS.length, 100); const { width, containerRef, mounted } = useContainerWidth(); const isSmall = mounted && width > 0 && width < SMALL_BREAKPOINT; const [selectedProject, setSelectedProject] = useState(1); const selectedRef = useRef(selectedProject); selectedRef.current = selectedProject; const [layouts, setLayouts] = useState>(() => buildLayoutsFromHeights(1, {})); const measuredHeights = useRef>({}); const updateHeight = useCallback((projectId: number, heightPx: number) => { const key = projectId.toString(); const rowH = Math.max(1, Math.ceil(heightPx / ROW_HEIGHT)); if (measuredHeights.current[key] === rowH) return; measuredHeights.current[key] = rowH; setLayouts(buildLayoutsFromHeights(selectedRef.current, measuredHeights.current)); }, []); function setSelectedHandler(projectId: number) { setSelectedProject(projectId); measuredHeights.current = {}; setLayouts(buildLayoutsFromHeights(projectId, {})); } const header = ( <>
{"# projects"}

{"Things I've built"}

); /* ---- small screen: horizontal scroll row ---- */ if (isSmall) { return (
{header}
{PROJECTS.map((project, i) => (
))}
); } /* ---- normal: grid layout ---- */ return (
{header}
{mounted && ( {PROJECTS.map((project, i) => (
))}
)}
); }