style: format notebook contracts
This commit is contained in:
1 parent
8b858528f8
commit
c66ff7fde6
3 files changed
+78
-22
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
import { portfolio, type PortfolioProject } from "@/lib/portfolio-data";
|
import { type PortfolioProject, portfolio } from "@/lib/portfolio-data";
|
||||||
import { selectNotebookProjects } from "./notebookProjects";
|
import { selectNotebookProjects } from "./notebookProjects";
|
||||||
|
|
||||||
const projectsFixture = portfolio.projects as PortfolioProject[];
|
const projectsFixture = portfolio.projects as PortfolioProject[];
|
||||||
@@ -13,7 +13,11 @@ describe("selectNotebookProjects", () => {
|
|||||||
"sev-modaintima",
|
"sev-modaintima",
|
||||||
"as-servicos",
|
"as-servicos",
|
||||||
]);
|
]);
|
||||||
expect(result.every((project) => project.screenshotSrc.startsWith("/assets/prints/"))).toBe(true);
|
expect(
|
||||||
|
result.every((project) =>
|
||||||
|
project.screenshotSrc.startsWith("/assets/prints/"),
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("omits a project when its screenshot mapping is missing", () => {
|
it("omits a project when its screenshot mapping is missing", () => {
|
||||||
@@ -22,15 +26,16 @@ describe("selectNotebookProjects", () => {
|
|||||||
{ ...projectsFixture[0], id: "unknown-live-project" },
|
{ ...projectsFixture[0], id: "unknown-live-project" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(result.some((project) => project.id === "unknown-live-project")).toBe(false);
|
expect(
|
||||||
|
result.some((project) => project.id === "unknown-live-project"),
|
||||||
|
).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("omits a project without an approved demo URL", () => {
|
it("omits a project without an approved demo URL", () => {
|
||||||
const result = selectNotebookProjects(
|
const projectsWithoutDemoUrl = projectsFixture.map((project) =>
|
||||||
projectsFixture.map((project) =>
|
|
||||||
project.id === "nutriscan" ? { ...project, demoUrl: null } : project,
|
project.id === "nutriscan" ? { ...project, demoUrl: null } : project,
|
||||||
),
|
) as unknown as PortfolioProject[];
|
||||||
);
|
const result = selectNotebookProjects(projectsWithoutDemoUrl);
|
||||||
|
|
||||||
expect(result.some((project) => project.id === "nutriscan")).toBe(false);
|
expect(result.some((project) => project.id === "nutriscan")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,14 +16,19 @@ const NOTEBOOK_ORDER = [
|
|||||||
"as-servicos",
|
"as-servicos",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const SCREENSHOT_BY_PROJECT_ID: Record<(typeof NOTEBOOK_ORDER)[number], string> = {
|
const SCREENSHOT_BY_PROJECT_ID: Record<
|
||||||
|
(typeof NOTEBOOK_ORDER)[number],
|
||||||
|
string
|
||||||
|
> = {
|
||||||
nutriscan: "/assets/prints/print-nutriscan.png",
|
nutriscan: "/assets/prints/print-nutriscan.png",
|
||||||
"sc-plus": "/assets/prints/print-scplus.png",
|
"sc-plus": "/assets/prints/print-scplus.png",
|
||||||
"sev-modaintima": "/assets/prints/print-sev.png",
|
"sev-modaintima": "/assets/prints/print-sev.png",
|
||||||
"as-servicos": "/assets/prints/print-asservicos.png",
|
"as-servicos": "/assets/prints/print-asservicos.png",
|
||||||
};
|
};
|
||||||
|
|
||||||
function isApprovedDemoUrl(value: PortfolioProject["demoUrl"]): value is string {
|
function isApprovedDemoUrl(
|
||||||
|
value: PortfolioProject["demoUrl"],
|
||||||
|
): value is string {
|
||||||
return typeof value === "string" && /^https?:\/\//.test(value);
|
return typeof value === "string" && /^https?:\/\//.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,19 @@ function getPhase(progress: number): NotebookPhase {
|
|||||||
return "close";
|
return "close";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveTabIndex(progress: number, tabCount: number, phase: NotebookPhase): number {
|
function getActiveTabIndex(
|
||||||
if (tabCount < 1 || phase === "approach" || phase === "open" || phase === "boot" || phase === "cursor-in" || phase === "close") {
|
progress: number,
|
||||||
|
tabCount: number,
|
||||||
|
phase: NotebookPhase,
|
||||||
|
): number {
|
||||||
|
if (
|
||||||
|
tabCount < 1 ||
|
||||||
|
phase === "approach" ||
|
||||||
|
phase === "open" ||
|
||||||
|
phase === "boot" ||
|
||||||
|
phase === "cursor-in" ||
|
||||||
|
phase === "close"
|
||||||
|
) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,11 +72,16 @@ function getActiveTabIndex(progress: number, tabCount: number, phase: NotebookPh
|
|||||||
return tabCount - 1;
|
return tabCount - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const browseProgress = (progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
|
const browseProgress =
|
||||||
|
(progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
|
||||||
return clamp(Math.floor(browseProgress * tabCount), 0, tabCount - 1);
|
return clamp(Math.floor(browseProgress * tabCount), 0, tabCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCursorPoint(progress: number, phase: NotebookPhase, activeTabIndex: number): { x: number; y: number } {
|
function getCursorPoint(
|
||||||
|
progress: number,
|
||||||
|
phase: NotebookPhase,
|
||||||
|
activeTabIndex: number,
|
||||||
|
): { x: number; y: number } {
|
||||||
if (phase === "cursor-in") {
|
if (phase === "cursor-in") {
|
||||||
const cursorProgress = (progress - 0.34) / 0.1;
|
const cursorProgress = (progress - 0.34) / 0.1;
|
||||||
return {
|
return {
|
||||||
@@ -82,8 +98,18 @@ function getCursorPoint(progress: number, phase: NotebookPhase, activeTabIndex:
|
|||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCursorClicking(progress: number, phase: NotebookPhase, tabCount: number): boolean {
|
function isCursorClicking(
|
||||||
if (tabCount < 1 || phase === "approach" || phase === "open" || phase === "boot" || phase === "close") {
|
progress: number,
|
||||||
|
phase: NotebookPhase,
|
||||||
|
tabCount: number,
|
||||||
|
): boolean {
|
||||||
|
if (
|
||||||
|
tabCount < 1 ||
|
||||||
|
phase === "approach" ||
|
||||||
|
phase === "open" ||
|
||||||
|
phase === "boot" ||
|
||||||
|
phase === "close"
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +121,24 @@ function isCursorClicking(progress: number, phase: NotebookPhase, tabCount: numb
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const browseProgress = (progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
|
const browseProgress =
|
||||||
|
(progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
|
||||||
const slotProgress = (browseProgress * tabCount) % 1;
|
const slotProgress = (browseProgress * tabCount) % 1;
|
||||||
return slotProgress < 0.12;
|
return slotProgress < 0.12;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNotebookFrame(progress: number, tabCount: number): NotebookFrame {
|
export function getNotebookFrame(
|
||||||
|
progress: number,
|
||||||
|
tabCount: number,
|
||||||
|
): NotebookFrame {
|
||||||
const normalizedProgress = normalizeProgress(progress);
|
const normalizedProgress = normalizeProgress(progress);
|
||||||
const normalizedTabCount = Math.max(0, Math.floor(tabCount));
|
const normalizedTabCount = Math.max(0, Math.floor(tabCount));
|
||||||
const phase = getPhase(normalizedProgress);
|
const phase = getPhase(normalizedProgress);
|
||||||
const activeTabIndex = getActiveTabIndex(normalizedProgress, normalizedTabCount, phase);
|
const activeTabIndex = getActiveTabIndex(
|
||||||
|
normalizedProgress,
|
||||||
|
normalizedTabCount,
|
||||||
|
phase,
|
||||||
|
);
|
||||||
const isApproach = normalizedProgress < 0.1;
|
const isApproach = normalizedProgress < 0.1;
|
||||||
const isOpening = phase === "open";
|
const isOpening = phase === "open";
|
||||||
const isBooting = phase === "boot";
|
const isBooting = phase === "boot";
|
||||||
@@ -112,7 +146,11 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
|
|||||||
const lidAngle = isOpening
|
const lidAngle = isOpening
|
||||||
? interpolate(0, OPEN_ANGLE, (normalizedProgress - 0.1) / 0.15)
|
? interpolate(0, OPEN_ANGLE, (normalizedProgress - 0.1) / 0.15)
|
||||||
: isClosing
|
: isClosing
|
||||||
? interpolate(OPEN_ANGLE, 0, (normalizedProgress - HOLD_END) / (1 - HOLD_END))
|
? interpolate(
|
||||||
|
OPEN_ANGLE,
|
||||||
|
0,
|
||||||
|
(normalizedProgress - HOLD_END) / (1 - HOLD_END),
|
||||||
|
)
|
||||||
: normalizedProgress >= 0.25
|
: normalizedProgress >= 0.25
|
||||||
? OPEN_ANGLE
|
? OPEN_ANGLE
|
||||||
: 0;
|
: 0;
|
||||||
@@ -128,8 +166,14 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
|
|||||||
: isClosing
|
: isClosing
|
||||||
? interpolate(1, 0.92, (normalizedProgress - HOLD_END) / (1 - HOLD_END))
|
? interpolate(1, 0.92, (normalizedProgress - HOLD_END) / (1 - HOLD_END))
|
||||||
: 1;
|
: 1;
|
||||||
const cursorVisible = (phase === "cursor-in" || phase === "browse" || phase === "hold") && normalizedTabCount > 0;
|
const cursorVisible =
|
||||||
const { x: cursorX, y: cursorY } = getCursorPoint(normalizedProgress, phase, activeTabIndex);
|
(phase === "cursor-in" || phase === "browse" || phase === "hold") &&
|
||||||
|
normalizedTabCount > 0;
|
||||||
|
const { x: cursorX, y: cursorY } = getCursorPoint(
|
||||||
|
normalizedProgress,
|
||||||
|
phase,
|
||||||
|
activeTabIndex,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
phase,
|
phase,
|
||||||
@@ -140,6 +184,8 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
|
|||||||
cursorVisible,
|
cursorVisible,
|
||||||
cursorX,
|
cursorX,
|
||||||
cursorY,
|
cursorY,
|
||||||
cursorClicking: cursorVisible && isCursorClicking(normalizedProgress, phase, normalizedTabCount),
|
cursorClicking:
|
||||||
|
cursorVisible &&
|
||||||
|
isCursorClicking(normalizedProgress, phase, normalizedTabCount),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user