diff --git a/src/components/gallery/JustifiedGallery.tsx b/src/components/gallery/JustifiedGallery.tsx index e9981ed..5e2f3f0 100644 --- a/src/components/gallery/JustifiedGallery.tsx +++ b/src/components/gallery/JustifiedGallery.tsx @@ -44,6 +44,9 @@ type Props = { maxRowItems?: number; // desktop maxRowItemsMobile?: number; // <640px gap?: number; // px + gapNarrow?: number; // px for narrower containers + gapNarrowMaxWidth?: number; // px breakpoint for gapNarrow + gapBreakpoints?: Array<{ maxWidth: number; gap: number }>; debug?: boolean; className?: string; }; @@ -85,12 +88,33 @@ export default function JustifiedGallery({ maxRowItems = 5, maxRowItemsMobile = 3, gap = 12, + gapNarrow, + gapNarrowMaxWidth = 720, + gapBreakpoints, debug = false, className, }: Props) { const containerRef = useRef(null); const sentinelRef = useRef(null); const [containerWidth, setContainerWidth] = useState(0); + const effectiveGap = (() => { + if (gapBreakpoints && containerWidth > 0) { + const sorted = [...gapBreakpoints].sort((a, b) => a.maxWidth - b.maxWidth); + for (const bp of sorted) { + if (containerWidth <= bp.maxWidth) return bp.gap; + } + } + + if ( + gapNarrow != null && + containerWidth > 0 && + containerWidth <= gapNarrowMaxWidth + ) { + return gapNarrow; + } + + return gap; + })(); // Measure container width (responsive) useEffect(() => { @@ -143,7 +167,7 @@ export default function JustifiedGallery({ const flush = () => { if (current.length === 0) return; - const gaps = gap * (current.length - 1); + const gaps = effectiveGap * (current.length - 1); const widthWithoutGaps = Math.max(0, available - gaps); // Compute row height so it exactly fills the row width. @@ -172,14 +196,15 @@ export default function JustifiedGallery({ aspectSum += a; // Estimate the row width if we were to keep targetH - const estimatedWidth = aspectSum * targetH + gap * (current.length - 1); + const estimatedWidth = + aspectSum * targetH + effectiveGap * (current.length - 1); // If we've filled the row (or reached max items) and have at least 2 tiles, flush. if ( (estimatedWidth >= available || current.length >= maxItems) && current.length > 1 ) { - const gaps = gap * (current.length - 1); + const gaps = effectiveGap * (current.length - 1); const widthWithoutGaps = Math.max(0, available - gaps); const computedH = widthWithoutGaps / aspectSum; @@ -227,7 +252,7 @@ export default function JustifiedGallery({ }, [ items, containerWidth, - gap, + effectiveGap, targetRowHeight, targetRowHeightMobile, maxRowHeight, @@ -260,7 +285,7 @@ export default function JustifiedGallery({ ? "justify-start" : "justify-between", )} - style={{ columnGap: gap }} + style={{ columnGap: effectiveGap }} > {row.map((t) => ( {`row ${idx + 1} | h=${Math.round(row[0]?.h ?? 0)} | w=${Math.round( - row.reduce((sum, t) => sum + t.w, 0) + gap * (row.length - 1), + row.reduce((sum, t) => sum + t.w, 0) + + effectiveGap * (row.length - 1), )} | items=${row.length} | `} {row .map( diff --git a/src/components/portfolio/PortfolioGallery.tsx b/src/components/portfolio/PortfolioGallery.tsx index f5fcb3c..f2570ad 100644 --- a/src/components/portfolio/PortfolioGallery.tsx +++ b/src/components/portfolio/PortfolioGallery.tsx @@ -151,6 +151,11 @@ export default function PortfolioGallery({ maxRowItems={5} maxRowItemsMobile={1} gap={12} + gapBreakpoints={[ + { maxWidth: 685, gap: 6 }, + { maxWidth: 910, gap: 8 }, + { maxWidth: 1130, gap: 10 }, + ]} onLoadMore={done ? undefined : () => void loadMore()} hasMore={!done} isLoadingMore={loading}