Files
portifolio-evandrodevbr/app/components/showcase/ProjectReel.tsx
T
Evandro Fonseca Junior 204ebd5fa5 fix(navegacao): foco em âncora de outra página, vitrine acessível e 3D mais leve
Foco cai no destino ao chegar de outra página; âncoras ignoram ?tipo=; movimento reduzido salta em todos os cliques; cronômetro da janela ampliada; degradê no topo de Projetos e legendas do mergulho com intervalos testados; vitrine acima da barra de cookies e com 2 paradas por projeto; reel-list.css no global; 3D montado em tarefas curtas, limitado a ~30 qps e estático com WebGL por software; pôsteres da vitrine de 330 para 158 KB; SC Plus regravado; scripts sem funções acima de complexidade 10.
2026-10-14 19:14:48 -03:00

63 lines
2.3 KiB
TypeScript

"use client";
import { type CSSProperties, Suspense } from "react";
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
import { PinnedReelLazy } from "./PinnedReelLazy";
import { ProjectList } from "./ProjectList";
import { type ShowcaseItem, useShowcaseItems } from "./ShowcaseProvider";
import { usePinnedViewport, useViewportDevice } from "./useShowcaseMedia";
/**
* O bloco que segura o lugar da vitrine fixa, com a mesma altura dela: a página não muda de tamanho quando ela
* aparece (o salto para #contact, por exemplo, cai no lugar certo). O CSS só o mostra onde a vitrine fixa vai existir.
*/
function ReelPlaceholder({ items }: { items: ShowcaseItem[] }) {
return (
<div
className="ds-reel ds-reel-placeholder"
aria-hidden="true"
style={
{
"--reel-sum": items.reduce(
(sum, item) => sum + item.config.weight,
0,
),
} as CSSProperties
}
/>
);
}
/**
* Projetos da home. Em tablet e desktop (768 px de largura e 561 px de altura, no mínimo), a vitrine animada:
* o site de cada projeto rola dentro de um notebook (e de um celular ao lado) conforme a página rola, com a
* cena presa (position: sticky) durante a rolagem de todos os projetos. No celular, com prefers-reduced-motion
* e em janelas baixas, nada fica preso: é a lista de projetos (carrossel por toque no celular), só com pôsteres.
* O servidor entrega a lista (texto e links); o navegador troca pela vitrine fixa quando ela cabe, e até lá
* (e enquanto o pacote dela chega) um bloco da mesma altura segura o lugar. Funciona sozinha dentro de
* `ShowcaseProvider`: não depende dos cards da grade.
*/
export function ProjectReel() {
const items = useShowcaseItems();
const reducedMotion = useReducedMotion();
const wide = usePinnedViewport();
const device = useViewportDevice();
if (items.length === 0) return null;
const mounted = wide !== null && device !== null;
if (mounted && wide && !reducedMotion)
return (
<Suspense fallback={<ReelPlaceholder items={items} />}>
<PinnedReelLazy items={items} />
</Suspense>
);
return (
<>
<ProjectList items={items} device={device} pending={!mounted} />
{!mounted && <ReelPlaceholder items={items} />}
</>
);
}