feat: rebuild landing experience, content and analytics

This commit is contained in:
Evandro Fonseca Junior committed 2026-09-13 04:30:58 -03:00
1 parent d24ee12299
commit 9aff4d1035
51 files changed
+3023 -2595

No files matched your search

+48 -20
View File
@@ -12,7 +12,7 @@ const NotebookStage3D = dynamic(() => import("./NotebookStage3D"), {
ssr: false,
});
type CapabilityWindow = Pick<Window, "innerWidth" | "matchMedia">;
type CapabilityWindow = Pick<Window, "matchMedia">;
export type NotebookStoryProps = {
projects: PortfolioProjectView[];
@@ -21,12 +21,7 @@ export type NotebookStoryProps = {
export function canUseNotebook3D(view: CapabilityWindow): boolean {
if (typeof view.matchMedia !== "function") return false;
return (
view.innerWidth >= 1025 &&
!view.matchMedia("(prefers-reduced-motion: reduce)").matches &&
!view.matchMedia("(pointer: coarse)").matches &&
view.matchMedia("(hover: hover)").matches
);
return !view.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
export function getNotebookRunwayVh(tabCount: number): number {
@@ -84,21 +79,11 @@ export default function NotebookStory({
updateEngine();
if (typeof window.matchMedia !== "function") return undefined;
const mediaQueries = [
window.matchMedia("(prefers-reduced-motion: reduce)"),
window.matchMedia("(pointer: coarse)"),
window.matchMedia("(hover: hover)"),
];
window.addEventListener("resize", updateEngine, { passive: true });
mediaQueries.forEach((mediaQuery) => {
mediaQuery.addEventListener("change", updateEngine);
});
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
reducedMotion.addEventListener("change", updateEngine);
return () => {
window.removeEventListener("resize", updateEngine);
mediaQueries.forEach((mediaQuery) => {
mediaQuery.removeEventListener("change", updateEngine);
});
reducedMotion.removeEventListener("change", updateEngine);
};
}, [notebookProjects.length]);
@@ -224,6 +209,8 @@ export default function NotebookStory({
[engine, notebookProjects.length],
);
const activeProject = notebookProjects[activeIndex];
if (!engine) {
return (
<section
@@ -235,6 +222,7 @@ export default function NotebookStory({
style={
{
"--notebook-runway": `${getNotebookRunwayVh(notebookProjects.length)}vh`,
"--notebook-mobile-runway": `${getNotebookRunwayVh(notebookProjects.length) * 0.8}svh`,
} as CSSProperties
}
>
@@ -269,6 +257,7 @@ export default function NotebookStory({
style={
{
"--notebook-runway": `${getNotebookRunwayVh(notebookProjects.length)}vh`,
"--notebook-mobile-runway": `${getNotebookRunwayVh(notebookProjects.length) * 0.8}svh`,
} as CSSProperties
}
>
@@ -295,6 +284,45 @@ export default function NotebookStory({
requestRender={requestRenderRef}
projects={notebookProjects}
/>
<nav
className="notebook-story-controls"
aria-label="Navegar projetos do notebook"
>
<div className="notebook-story-project-buttons">
{notebookProjects.map((project, index) => (
<button
key={project.id}
type="button"
aria-label={`Mostrar ${project.title}`}
aria-pressed={index === activeIndex}
onClick={() => handleSelectTab(index)}
>
{String(index + 1).padStart(2, "0")}
</button>
))}
</div>
<p aria-live="polite">{activeProject?.title}</p>
{activeProject?.demoAvailability === "unavailable" ? (
<span aria-disabled="true">
Demo indisponível na última verificação
</span>
) : activeProject?.demoAvailability !== "none" &&
activeProject?.demoUrl ? (
<a
href={activeProject.demoUrl}
rel="noopener noreferrer"
target="_blank"
>
Abrir projeto
{activeProject.demoAvailability === "unknown" && (
<span className="sr-only">
{" "}
— Disponibilidade não verificada
</span>
)}
</a>
) : null}
</nav>
</div>
</div>
</section>