Change gap calculation on gallery for better breakpoint on mobile and tablet
This commit is contained in:
@ -44,6 +44,9 @@ type Props = {
|
|||||||
maxRowItems?: number; // desktop
|
maxRowItems?: number; // desktop
|
||||||
maxRowItemsMobile?: number; // <640px
|
maxRowItemsMobile?: number; // <640px
|
||||||
gap?: number; // px
|
gap?: number; // px
|
||||||
|
gapNarrow?: number; // px for narrower containers
|
||||||
|
gapNarrowMaxWidth?: number; // px breakpoint for gapNarrow
|
||||||
|
gapBreakpoints?: Array<{ maxWidth: number; gap: number }>;
|
||||||
debug?: boolean;
|
debug?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
@ -85,12 +88,33 @@ export default function JustifiedGallery({
|
|||||||
maxRowItems = 5,
|
maxRowItems = 5,
|
||||||
maxRowItemsMobile = 3,
|
maxRowItemsMobile = 3,
|
||||||
gap = 12,
|
gap = 12,
|
||||||
|
gapNarrow,
|
||||||
|
gapNarrowMaxWidth = 720,
|
||||||
|
gapBreakpoints,
|
||||||
debug = false,
|
debug = false,
|
||||||
className,
|
className,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
const sentinelRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [containerWidth, setContainerWidth] = useState(0);
|
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)
|
// Measure container width (responsive)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -143,7 +167,7 @@ export default function JustifiedGallery({
|
|||||||
const flush = () => {
|
const flush = () => {
|
||||||
if (current.length === 0) return;
|
if (current.length === 0) return;
|
||||||
|
|
||||||
const gaps = gap * (current.length - 1);
|
const gaps = effectiveGap * (current.length - 1);
|
||||||
const widthWithoutGaps = Math.max(0, available - gaps);
|
const widthWithoutGaps = Math.max(0, available - gaps);
|
||||||
|
|
||||||
// Compute row height so it exactly fills the row width.
|
// Compute row height so it exactly fills the row width.
|
||||||
@ -172,14 +196,15 @@ export default function JustifiedGallery({
|
|||||||
aspectSum += a;
|
aspectSum += a;
|
||||||
|
|
||||||
// Estimate the row width if we were to keep targetH
|
// 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 we've filled the row (or reached max items) and have at least 2 tiles, flush.
|
||||||
if (
|
if (
|
||||||
(estimatedWidth >= available || current.length >= maxItems) &&
|
(estimatedWidth >= available || current.length >= maxItems) &&
|
||||||
current.length > 1
|
current.length > 1
|
||||||
) {
|
) {
|
||||||
const gaps = gap * (current.length - 1);
|
const gaps = effectiveGap * (current.length - 1);
|
||||||
const widthWithoutGaps = Math.max(0, available - gaps);
|
const widthWithoutGaps = Math.max(0, available - gaps);
|
||||||
const computedH = widthWithoutGaps / aspectSum;
|
const computedH = widthWithoutGaps / aspectSum;
|
||||||
|
|
||||||
@ -227,7 +252,7 @@ export default function JustifiedGallery({
|
|||||||
}, [
|
}, [
|
||||||
items,
|
items,
|
||||||
containerWidth,
|
containerWidth,
|
||||||
gap,
|
effectiveGap,
|
||||||
targetRowHeight,
|
targetRowHeight,
|
||||||
targetRowHeightMobile,
|
targetRowHeightMobile,
|
||||||
maxRowHeight,
|
maxRowHeight,
|
||||||
@ -260,7 +285,7 @@ export default function JustifiedGallery({
|
|||||||
? "justify-start"
|
? "justify-start"
|
||||||
: "justify-between",
|
: "justify-between",
|
||||||
)}
|
)}
|
||||||
style={{ columnGap: gap }}
|
style={{ columnGap: effectiveGap }}
|
||||||
>
|
>
|
||||||
{row.map((t) => (
|
{row.map((t) => (
|
||||||
<GalleryTile
|
<GalleryTile
|
||||||
@ -275,7 +300,8 @@ export default function JustifiedGallery({
|
|||||||
{debug ? (
|
{debug ? (
|
||||||
<div className="text-xs text-muted-foreground font-mono">
|
<div className="text-xs text-muted-foreground font-mono">
|
||||||
{`row ${idx + 1} | h=${Math.round(row[0]?.h ?? 0)} | w=${Math.round(
|
{`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} | `}
|
)} | items=${row.length} | `}
|
||||||
{row
|
{row
|
||||||
.map(
|
.map(
|
||||||
|
|||||||
@ -151,6 +151,11 @@ export default function PortfolioGallery({
|
|||||||
maxRowItems={5}
|
maxRowItems={5}
|
||||||
maxRowItemsMobile={1}
|
maxRowItemsMobile={1}
|
||||||
gap={12}
|
gap={12}
|
||||||
|
gapBreakpoints={[
|
||||||
|
{ maxWidth: 685, gap: 6 },
|
||||||
|
{ maxWidth: 910, gap: 8 },
|
||||||
|
{ maxWidth: 1130, gap: 10 },
|
||||||
|
]}
|
||||||
onLoadMore={done ? undefined : () => void loadMore()}
|
onLoadMore={done ? undefined : () => void loadMore()}
|
||||||
hasMore={!done}
|
hasMore={!done}
|
||||||
isLoadingMore={loading}
|
isLoadingMore={loading}
|
||||||
|
|||||||
Reference in New Issue
Block a user