Files
Evandro Fonseca Junior 6ac87824f3 perf(movimento): sem tempo morto, vídeos regravados e 3D sem React Three Fiber
Legenda do mergulho até Projetos cobrir a cena; transição de página sem blur e com fundo escuro; foco no conteúdo ao trocar de página; listas com marcador e dois pesos de título; vídeos da SEV e da SC Plus regravados sem quadros brancos; scrub a 12-15 fps (-30%); vitrine com uma parada de Tab e setas, contador no celular e CSS sob demanda; âncoras num módulo só (fragment-nav); FramePulse com setInterval; CSS morto removido.
2026-10-14 17:01:37 -03:00

87 lines
2.3 KiB
TypeScript

"use client";
import {
createContext,
type ReactNode,
Suspense,
useCallback,
useContext,
useMemo,
useState,
} from "react";
import type { PortfolioProjectView } from "@/lib/portfolio-data";
import { SHOWCASE, type ShowcaseConfig } from "@/lib/project-showcase";
import { ProjectLightboxLazy } from "./ProjectLightboxLazy";
export interface ShowcaseItem {
config: ShowcaseConfig;
project: PortfolioProjectView;
}
interface ShowcaseApi {
open(id: string): void;
items: ShowcaseItem[];
}
const ApiContext = createContext<ShowcaseApi | null>(null);
/** Só aparece como card com vídeo o projeto que existe na lista e tem vídeo gravado. */
export function buildShowcaseItems(
projects: PortfolioProjectView[],
): ShowcaseItem[] {
const items: ShowcaseItem[] = [];
for (const config of SHOWCASE) {
const project = projects.find((candidate) => candidate.id === config.id);
if (project) items.push({ config, project });
}
return items;
}
/** Itens da vitrine para quem está dentro do provedor. */
export function useShowcaseItems() {
return useContext(ApiContext)?.items ?? [];
}
/** Abre a janela ampliada de um projeto (nada acontece fora do provedor). */
export function useShowcaseOpen() {
const api = useContext(ApiContext);
return useCallback((id: string) => api?.open(id), [api]);
}
/**
* Guarda os projetos da vitrine e qual deles está ampliado, e mostra a janela ampliada. A vitrine animada
* (ProjectReel) e a lista do celular (ProjectList) leem a mesma lista de itens.
*/
export function ShowcaseProvider({
projects,
children,
}: {
projects: PortfolioProjectView[];
children: ReactNode;
}) {
const items = useMemo(() => buildShowcaseItems(projects), [projects]);
const [openId, setOpenId] = useState<string | null>(null);
const api = useMemo<ShowcaseApi>(() => ({ items, open: setOpenId }), [items]);
const openIndex = openId
? items.findIndex((item) => item.config.id === openId)
: -1;
return (
<ApiContext.Provider value={api}>
{children}
{openIndex >= 0 && (
<Suspense fallback={null}>
<ProjectLightboxLazy
items={items}
index={openIndex}
onClose={() => setOpenId(null)}
onNavigate={(index) => setOpenId(items[index]?.config.id ?? null)}
/>
</Suspense>
)}
</ApiContext.Provider>
);
}