terry-staging #14

Merged
terry merged 3 commits from terry-staging into main 2026-05-31 10:36:12 +00:00
Showing only changes of commit c7e0562d9a - Show all commits

View File

@@ -6,8 +6,10 @@ import {
type MouseEvent as ReactMouseEvent,
type PointerEvent as ReactPointerEvent,
} from "react";
import { m } from "framer-motion";
import { useNavigate } from "react-router-dom";
import { assetUrl, getJSON, itemsOrEmpty, readJSONCache } from "../api";
import { EASE_OUT } from "../motion";
import { langQuery, useI18n, type Lang } from "../i18n";
const FIGMA_ASSET_BASE = "/assets/ark-library/figma";
@@ -123,7 +125,11 @@ export function FigmaBanner() {
if (!scroller) return;
const target = scroller.children[index] as HTMLElement | undefined;
if (!target) return;
scroller.scrollTo({ left: target.offsetLeft, behavior });
// Center the slide in the viewport. On mobile (full-width slides) the
// offset is 0 so this matches the previous snap-start behavior.
const centerOffset = (scroller.clientWidth - target.offsetWidth) / 2;
const left = Math.max(0, target.offsetLeft - centerOffset);
scroller.scrollTo({ left, behavior });
}, []);
const pauseAutoplay = useCallback(() => {
@@ -168,9 +174,10 @@ export function FigmaBanner() {
if (!scroller) return;
const handleScroll = () => {
const width = scroller.clientWidth;
if (width === 0) return;
const next = Math.round(scroller.scrollLeft / width);
const firstChild = scroller.children[0] as HTMLElement | undefined;
const slideWidth = firstChild?.offsetWidth ?? scroller.clientWidth;
if (slideWidth === 0) return;
const next = Math.round(scroller.scrollLeft / slideWidth);
setActiveIndex((prev) => (prev === next ? prev : next));
};
@@ -238,8 +245,9 @@ export function FigmaBanner() {
window.setTimeout(() => {
suppressClickRef.current = false;
}, 0);
const width = scroller.clientWidth || 1;
const nearest = Math.round(scroller.scrollLeft / width);
const firstChild = scroller.children[0] as HTMLElement | undefined;
const slideWidth = firstChild?.offsetWidth || scroller.clientWidth || 1;
const nearest = Math.round(scroller.scrollLeft / slideWidth);
const clamped = Math.max(0, Math.min(slides.length - 1, nearest));
scroller.style.scrollSnapType = "";
goTo(clamped, "smooth");
@@ -315,6 +323,7 @@ export function FigmaBanner() {
aria-label="ARK Library banner"
>
{slides.map((slide, index) => {
const isActive = index === activeIndex;
const image = (
<picture className="block aspect-video w-full overflow-hidden bg-black md:rounded-xl">
<source media="(max-width: 767px)" srcSet={slide.mobile} />
@@ -330,11 +339,27 @@ export function FigmaBanner() {
/>
</picture>
);
const animatedImage = (
<m.div
className="h-full w-full origin-center will-change-transform"
animate={{
filter: isActive ? "blur(0px)" : "blur(6px)",
opacity: isActive ? 1 : 0.55,
scale: isActive ? 1 : 0.93,
}}
transition={{
duration: 0.45,
ease: EASE_OUT as unknown as number[],
}}
>
{image}
</m.div>
);
return (
<div
key={slide.id}
className="relative w-full shrink-0 snap-start"
className="relative w-full shrink-0 snap-center md:w-[78%] md:first:ml-[11%] md:last:mr-[11%] lg:w-[72%] lg:first:ml-[14%] lg:last:mr-[14%] xl:w-[60%] xl:first:ml-[20%] xl:last:mr-[20%]"
role="group"
aria-roledescription="slide"
aria-label={`${index + 1} / ${slides.length}`}
@@ -346,10 +371,10 @@ export function FigmaBanner() {
rel="noreferrer"
onClick={(event) => handleSlideClick(event, slide.linkUrl!)}
>
{image}
{animatedImage}
</a>
) : (
image
animatedImage
)}
</div>
);