"use client" import React, { useCallback, useEffect, useRef, useState } from "react" import Image from "next/image" import NoResultIcon from "@/public/icons/no-result.svg" import { useProjectFiltersState } from "@/state/useProjectFiltersState" import { cva } from "class-variance-authority" import { LangProps } from "@/types/common" import { ProjectCategories, ProjectCategory, ProjectCategoryLabelMapping, ProjectSection, ProjectSectionDescriptionMapping, ProjectSectionLabelMapping, ProjectSections, } from "@/lib/types" import { cn } from "@/lib/utils" import { useTranslation } from "@/app/i18n/client" import ProjectCard from "./project-card" const sectionTitleClass = cva( "relative font-sans text-base font-bold uppercase tracking-[3.36px] text-anakiwa-950 after:ml-8 after:absolute after:top-1/2 after:h-[1px] after:w-full after:translate-y-1/2 after:bg-anakiwa-300 after:content-['']" ) const NoResults = ({ lang }: LangProps["params"]) => { const { t } = useTranslation(lang, "common") return (
no result icon
{t("noResults")} {t("noResultsDescription")}
) } export const ProjectList = ({ lang }: LangProps["params"]) => { const { t } = useTranslation(lang, "resources-page") const SCROLL_OFFSET = -400 const [activeId, setActiveId] = useState("") const [isManualScroll, setIsManualScroll] = useState(false) const [isMounted, setIsMounted] = useState(false) const { projects, currentCategory } = useProjectFiltersState((state) => state) const noItems = projects?.length === 0 const sectionsRef = useRef | null>(null) // sections are constant so useRef might be better here useEffect(() => { setIsMounted(true) if (typeof window !== "undefined") { sectionsRef.current = document.querySelectorAll(`div[data-section]`) const handleScroll = () => { if (isManualScroll) return sectionsRef.current?.forEach((section: any) => { const sectionTop = section.offsetTop - SCROLL_OFFSET if (window.scrollY >= sectionTop && window.scrollY > 0) { setActiveId(section.getAttribute("id")) } }) } window.addEventListener("scroll", handleScroll) return () => window.removeEventListener("scroll", handleScroll) } }, [SCROLL_OFFSET, isManualScroll]) const scrollToId = useCallback((id: string) => { if (typeof window !== "undefined") { const element = document.getElementById(id) const top = element?.offsetTop ?? 0 if (element) { setActiveId(id) // active clicked id setIsManualScroll(true) // tell the window event listener to ignore this scrolling window?.scrollTo({ behavior: "smooth", top: (top ?? 0) - SCROLL_OFFSET, }) } setTimeout(() => setIsManualScroll(false), 800) } }, []) // loading state skeleton if (!isMounted) { return (
) } if (noItems) return console.log("ProjectCategories", ProjectCategories) return (
{ProjectCategories.map((category: any, index: number) => { const sectionProjects = projects.filter( (project) => project.category === category && project.section !== "archived" ) ?? [] const hasProjectsForSection = sectionProjects.length > 0 const sectionTitle = ProjectCategoryLabelMapping[category as ProjectCategory] const sectionDescription = // @ts-ignore ProjectSectionDescriptionMapping[category as any] // todo: filter by project section if (!hasProjectsForSection) return null const showTitle = ["archived"].includes(category) return (
0 && currentCategory == null ? "pt-10" : "" )} > {showTitle && (

{sectionTitle}

{sectionDescription}
)}
{sectionProjects.map((project) => ( ))}
) })} {currentCategory == undefined && (

Archived

{ProjectSectionDescriptionMapping.archived}
{projects .filter((project) => project.section === "archived") .map((project) => ( ))}
)}
) }