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.
63 lines
2.3 KiB
TypeScript
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} />}
|
|
</>
|
|
);
|
|
}
|