Case com mídia ao lado do título e play acessível; problema de cada cliente reescrito com base nos dados; serviços relacionados em 2 cards; FAQ antes do próximo passo e bloco 'Onde já apliquei isso'; Apolo citada em IoT e arquitetura. metaTitle até 43 caracteres e descrições até 155 com teste; JSON-LD CreativeWork; imagens de compartilhamento abaixo de 100 KB; 404 e robots corrigidos; server-only nos módulos com node:.
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { Play } from "lucide-react";
|
|
import Image from "next/image";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import type { Device, ShowcaseAssets } from "@/lib/project-showcase";
|
|
|
|
const deviceLabel: Record<Device, string> = {
|
|
desktop: "no computador",
|
|
mobile: "no celular",
|
|
};
|
|
|
|
/**
|
|
* Pôster com um botão de play. O vídeo só é montado, e só baixa, quando a pessoa aperta o botão:
|
|
* antes disso a página mostra a imagem do site, não um vídeo parado em 0:00 com controles do navegador.
|
|
* Vai dentro de um quadro com `position: relative` (moldura de navegador ou de celular) que dá o tamanho.
|
|
*/
|
|
export function CasePlayer({
|
|
assets,
|
|
title,
|
|
device,
|
|
sizes = "(min-width: 1280px) 900px, 100vw",
|
|
}: {
|
|
assets: ShowcaseAssets;
|
|
title: string;
|
|
device: Device;
|
|
sizes?: string;
|
|
}) {
|
|
const [playing, setPlaying] = useState(false);
|
|
const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
// o botão sai de cena ao tocar: o foco vai para o vídeo, para quem usa o teclado não o perder
|
|
useEffect(() => {
|
|
if (playing) videoRef.current?.focus();
|
|
}, [playing]);
|
|
|
|
if (playing) {
|
|
return (
|
|
<video
|
|
ref={videoRef}
|
|
className="absolute inset-0 h-full w-full object-cover object-top"
|
|
autoPlay
|
|
controls
|
|
muted
|
|
loop
|
|
playsInline
|
|
poster={assets.poster}
|
|
width={assets.width}
|
|
height={assets.height}
|
|
tabIndex={0}
|
|
aria-label={`Vídeo do site ${title} ${deviceLabel[device]}`}
|
|
disablePictureInPicture
|
|
>
|
|
{assets.sources.map((source) => (
|
|
<source key={source.src} src={source.src} type={source.type} />
|
|
))}
|
|
</video>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{/* o quadro do celular já mostra o pôster como fundo; no computador a imagem entra aqui */}
|
|
{device === "desktop" ? (
|
|
<Image
|
|
src={assets.poster}
|
|
alt=""
|
|
fill
|
|
sizes={sizes}
|
|
className="object-cover object-top"
|
|
/>
|
|
) : null}
|
|
<button
|
|
type="button"
|
|
onClick={() => setPlaying(true)}
|
|
aria-label={`Reproduzir o vídeo do site ${title} ${deviceLabel[device]}`}
|
|
className="group absolute inset-0 grid place-items-center focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-white"
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
className="grid size-14 place-items-center rounded-full border border-white/70 bg-black/65 text-white transition-colors group-hover:bg-black/85 motion-reduce:transition-none sm:size-16"
|
|
>
|
|
<Play className="ml-1 size-6" fill="currentColor" />
|
|
</span>
|
|
</button>
|
|
</>
|
|
);
|
|
}
|