fix(story): harden notebook interaction and fallback
This commit is contained in:
1 parent
bb4bf8e7e2
commit
f3ed9da777
12 files changed
+344
-57
No files matched your search
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { PortfolioProject } from "@/lib/portfolio-data";
|
||||
import NotebookFallback from "./NotebookFallback";
|
||||
@@ -30,6 +31,14 @@ export function canUseNotebook3D(view: CapabilityWindow): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function getNotebookRunwayVh(tabCount: number): number {
|
||||
const count = Math.max(
|
||||
1,
|
||||
Number.isFinite(tabCount) ? Math.floor(tabCount) : 1,
|
||||
);
|
||||
return 360 + count * 40;
|
||||
}
|
||||
|
||||
export function getNotebookSeekProgress(
|
||||
index: number,
|
||||
tabCount: number,
|
||||
@@ -58,7 +67,28 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [activeIndex, setActiveIndex] = useState(activeIndexRef.current);
|
||||
useEffect(() => {
|
||||
setEngine(notebookProjects.length > 0 && canUseNotebook3D(window));
|
||||
const updateEngine = () => {
|
||||
setEngine(notebookProjects.length > 0 && canUseNotebook3D(window));
|
||||
};
|
||||
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);
|
||||
});
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", updateEngine);
|
||||
mediaQueries.forEach((mediaQuery) => {
|
||||
mediaQuery.removeEventListener("change", updateEngine);
|
||||
});
|
||||
};
|
||||
}, [notebookProjects.length]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -127,6 +157,10 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
};
|
||||
}, [engine, notebookProjects.length]);
|
||||
|
||||
const handleStageError = useCallback(() => {
|
||||
setEngine(false);
|
||||
}, []);
|
||||
|
||||
const seekToTab = useCallback(
|
||||
(index: number) => {
|
||||
const section = sectionRef.current;
|
||||
@@ -161,7 +195,7 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
if (!engine) {
|
||||
return (
|
||||
<section
|
||||
aria-label="Projetos em produção"
|
||||
aria-label="Projetos em produção no notebook"
|
||||
className="notebook-story notebook-story-static"
|
||||
data-testid="notebook-story"
|
||||
id="story"
|
||||
@@ -183,12 +217,18 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label="Apresentação em scroll contínuo"
|
||||
aria-label="Projetos em produção no notebook"
|
||||
className="notebook-story notebook-story-runway"
|
||||
data-notebook-engine="3d"
|
||||
data-notebook-runway-vh={getNotebookRunwayVh(notebookProjects.length)}
|
||||
data-testid="notebook-story"
|
||||
id="story"
|
||||
ref={sectionRef}
|
||||
style={
|
||||
{
|
||||
"--notebook-runway": `${getNotebookRunwayVh(notebookProjects.length)}vh`,
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div className="notebook-story-sticky">
|
||||
<div className="notebook-story-grid">
|
||||
@@ -208,6 +248,7 @@ export default function NotebookStory({ projects }: NotebookStoryProps) {
|
||||
activeIndex={activeIndex}
|
||||
isVisible={isVisible}
|
||||
onSelectTab={handleSelectTab}
|
||||
onStageError={handleStageError}
|
||||
progress={progressRef}
|
||||
projects={notebookProjects}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user