Files

100 lines
3.3 KiB
TypeScript

"use client";
import { type RefObject, useEffect, useRef, useState } from "react";
import type { PortfolioProjectView } from "@/lib/portfolio-data";
import { secondaryButtonDark } from "./button-styles";
import HoverPrefetchLink from "./HoverPrefetchLink";
import { Section } from "./Section";
import { ProjectReel } from "./showcase/ProjectReel";
import { ShowcaseProvider } from "./showcase/ShowcaseProvider";
/**
* Quanto "Ver os cases" anda para a direita para ficar na coluna da legenda e dos botões da vitrine. Na vitrine fixa
* (a partir de 1024 px) o conjunto de aparelhos, com a legenda e os botões no canto esquerdo dele, fica centrado no
* palco, e esse canto muda com a altura da janela (o palco mede a si mesmo): só medindo dá para alinhar o link a ele.
* Sem vitrine fixa (celular, movimento reduzido, janela baixa) não há conjunto e o link fica no começo da coluna.
*/
function useReelColumnOffset(exitRef: RefObject<HTMLElement | null>) {
const [offset, setOffset] = useState(0);
useEffect(() => {
const exit = exitRef.current;
const section = exit?.closest("section");
if (!exit || !section) return;
let observedSet: Element | null = null;
const refresh = () => {
const set = section.querySelector(".ds-reel-set");
if (set !== observedSet) {
if (observedSet) resizes?.unobserve(observedSet);
if (set) resizes?.observe(set);
observedSet = set;
}
// o padding do próprio bloco não muda o `left` dele: a medida é só a distância até o canto do conjunto
setOffset(
set
? Math.max(
0,
Math.round(
set.getBoundingClientRect().left -
exit.getBoundingClientRect().left,
),
)
: 0,
);
};
const resizes =
typeof ResizeObserver === "undefined"
? undefined
: new ResizeObserver(refresh);
refresh();
resizes?.observe(section);
// a vitrine fixa chega depois (pacote sob demanda) e sai quando a janela deixa de comportá-la
const mutations = new MutationObserver(refresh);
mutations.observe(section, { childList: true, subtree: true });
return () => {
resizes?.disconnect();
mutations.disconnect();
};
}, [exitRef]);
return offset;
}
/** Vitrine única da home: recebe só projetos de cliente (o filtro vem de app/page.tsx). */
export default function Projects({
projects,
}: {
projects: PortfolioProjectView[];
}) {
const exitRef = useRef<HTMLDivElement>(null);
const offset = useReelColumnOffset(exitRef);
return (
<Section
id="projects"
eyebrow="Projetos"
title="Veja o que já saiu do papel."
description="Projetos feitos para empresas. Abra um case para entender o problema, a solução e as decisões por trás da interface."
dark
disableChildrenAnimation
>
<ShowcaseProvider projects={projects}>
<ProjectReel />
</ShowcaseProvider>
<div
ref={exitRef}
className="mt-sp-8"
data-reel-stagger="left"
style={offset ? { paddingLeft: offset } : undefined}
>
<HoverPrefetchLink href="/cases" className={secondaryButtonDark}>
Ver os cases
</HoverPrefetchLink>
</div>
</Section>
);
}