Files
portifolio-evandrodevbr/app/components/showcase/ShowcaseProvider.tsx
T
Evandro Fonseca Junior 318a764190 perf(movimento): sem tela vazia, passagens em CSS e vitrine leve
Projetos sobem sobre o palco do hero e o celular tem hero de uma tela sem mergulho; passagens com animation-timeline em CSS, sem GSAP nem motion; troca da vitrine sem texto duplicado; moldura de celular só com pôster e scrub reencodado em 960 px (9,4 para 5,2 MB); 3D monta na primeira interação e para quando parado; transição entre páginas do mesmo segmento; foco em anel duplo; texto de 15 px no celular; código morto removido.
2026-10-14 14:15:50 -03:00

84 lines
2.2 KiB
TypeScript

"use client";
import {
createContext,
type ReactNode,
useCallback,
useContext,
useMemo,
useState,
} from "react";
import type { PortfolioProjectView } from "@/lib/portfolio-data";
import { SHOWCASE, type ShowcaseConfig } from "@/lib/project-showcase";
import { ProjectLightbox } from "./ProjectLightbox";
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 && (
<ProjectLightbox
items={items}
index={openIndex}
onClose={() => setOpenId(null)}
onNavigate={(index) => setOpenId(items[index]?.config.id ?? null)}
/>
)}
</ApiContext.Provider>
);
}