diff --git a/app/components/Projects.tsx b/app/components/Projects.tsx index e4e48a2..a66d646 100644 --- a/app/components/Projects.tsx +++ b/app/components/Projects.tsx @@ -8,6 +8,8 @@ import { DrawLine } from "./animations/DrawLine"; import MagneticButton from "./cinematic/MagneticButton"; import { ProjectCover } from "./ProjectCover"; import { Section } from "./Section"; +import { ProjectReel } from "./showcase/ProjectReel"; +import { ShowcaseProvider } from "./showcase/ShowcaseProvider"; export default function Projects({ projects, @@ -36,129 +38,139 @@ export default function Projects({ dark disableChildrenAnimation > -
- {projects.map((project) => { - const caseStudy = caseStudies.find( - (entry) => entry.id === project.id, - ); - return ( - - -
-
-
{project.category}
- -

{project.title}

- {project.subtitle && ( -

- {project.subtitle} -

- )} + +
+ {projects.map((project) => { + const caseStudy = caseStudies.find( + (entry) => entry.id === project.id, + ); + return ( + + +
+
+
{project.category}
+ +

{project.title}

+ {project.subtitle && ( +

+ {project.subtitle} +

+ )} +
+ + {project.status} +
- - {project.status} - -
-

- {caseStudy?.summary ?? project.description} -

- {caseStudy && ( -
-

O desafio

-

{caseStudy.impact}

-
- )} - {caseStudy?.role && ( -
-

Minha participação

-

{caseStudy.role}

-
- )} - -
    - {project.technologies.slice(0, 5).map((tech) => ( -
  • - {tech} -
  • - ))} -
- -
+

+ {caseStudy?.summary ?? project.description} +

{caseStudy && ( - - Entender a solução - +
+

O desafio

+

{caseStudy.impact}

+
)} - {project.githubUrl && ( - - Ver codigo - + {caseStudy?.role && ( +
+

Minha participação

+

{caseStudy.role}

+
)} - {project.demoUrl && - (project.demoAvailability === "available" || - project.demoAvailability === "unknown") && ( + +
    + {project.technologies.slice(0, 5).map((tech) => ( +
  • + {tech} +
  • + ))} +
+ +
+ {caseStudy && ( - Abrir projeto - {project.demoAvailability === "unknown" && ( - - {" "} - Disponibilidade não verificada - - )} + Entender a solução )} - {project.demoAvailability === "unavailable" && ( - - Demo indisponível na última verificação - - )} -
- - ); - })} -
+ {project.githubUrl && ( + + Ver codigo + + )} + {project.demoUrl && + (project.demoAvailability === "available" || + project.demoAvailability === "unknown") && ( + + Abrir projeto + {project.demoAvailability === "unknown" && ( + + {" "} + Disponibilidade não verificada + + )} + + )} + {project.demoAvailability === "unavailable" && ( + + Demo indisponível na última verificação + + )} +
+ + ); + })} +
+ + +
({ + useReducedMotion: () => reducedMotion, +})); + +const PIN = 800; +const CONTAINER = 7000; +const SCROLLABLE = CONTAINER - PIN; +const HEADER = 60; +let reelTop = HEADER; + +const projects = getFeaturedProjects(null); +const weights = [ + "sev-modaintima", + "lavacao-047", + "nutriscan", + "sc-plus", + "as-servicos", +].map((id) => getShowcaseConfig(id)?.weight ?? 1); + +const originalRect = Element.prototype.getBoundingClientRect; +const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + "offsetHeight", +); + +beforeAll(() => { + Object.defineProperty(HTMLElement.prototype, "offsetHeight", { + configurable: true, + get(this: HTMLElement) { + if (this.classList.contains("ds-reel")) return CONTAINER; + if (this.classList.contains("ds-reel-pin")) return PIN; + return 0; + }, + }); + Element.prototype.getBoundingClientRect = function rect(this: Element) { + if (this.classList.contains("ds-reel")) + return { + top: reelTop, + bottom: reelTop + CONTAINER, + height: CONTAINER, + left: 0, + right: 1000, + width: 1000, + x: 0, + y: reelTop, + } as DOMRect; + return originalRect.call(this); + }; + window.scrollTo = jest.fn(); +}); + +afterAll(() => { + Element.prototype.getBoundingClientRect = originalRect; + if (originalOffsetHeight) + Object.defineProperty( + HTMLElement.prototype, + "offsetHeight", + originalOffsetHeight, + ); +}); + +beforeEach(() => { + jest.useFakeTimers(); + reducedMotion = false; + reelTop = HEADER; + (window.scrollTo as jest.Mock).mockClear(); + jest.spyOn(HTMLMediaElement.prototype, "play").mockResolvedValue(); + jest.spyOn(HTMLMediaElement.prototype, "pause").mockImplementation(() => {}); +}); + +afterEach(() => { + jest.useRealTimers(); + jest.restoreAllMocks(); +}); + +function renderReel() { + const view = render( + + + , + ); + act(() => { + jest.advanceTimersByTime(50); + }); + return view; +} + +/** jsdom não carrega mídia: cada