Skip to content

Commit 38a37b0

Browse files
committed
pass articleClass to Page layout for flex grow, gallery responsive pagination
1 parent 2148225 commit 38a37b0

File tree

9 files changed

+115
-16
lines changed

9 files changed

+115
-16
lines changed

src/components/react/Gallery.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,43 @@ import type { FC } from 'react';
1212

1313
import 'photoswipe/style.css';
1414

15+
import { sliceToModN } from '@/utils/array';
16+
import { getInitialPage, getPageSize } from '@/utils/gallery';
1517
import useScrollDown from './hooks/useScrollDown';
18+
import { useActiveBreakpoint } from './hooks/useWidth';
1619

1720
interface Props {
1821
images: GalleryImage[];
1922
}
2023

2124
type LoadedStates = Record<string, boolean>;
2225

23-
// INITIAL_PAGE controls first screen
24-
const { PAGE_SIZE, INITIAL_PAGE, OBSERVER_DEBOUNCE, GALLERY_ID } = GALLERY;
26+
const { OBSERVER_DEBOUNCE, GALLERY_ID } = GALLERY;
2527

26-
const fetchImagesUpToPage = (images: GalleryImage[], nextPage: number): GalleryImage[] => {
27-
const endIndex = nextPage * PAGE_SIZE;
28-
return images.slice(0, endIndex);
28+
const fetchImagesUpToPage = (
29+
images: GalleryImage[],
30+
pageSize: number,
31+
nextPage: number
32+
): GalleryImage[] => {
33+
const endIndex = nextPage * pageSize;
34+
const isLastPage = endIndex >= images.length;
35+
36+
// for fetchPageImages pagination startIndex must use loadedImages and not all images and page
37+
const selectedImages = images.slice(0, endIndex);
38+
39+
// load all images for last page
40+
return !isLastPage ? sliceToModN(selectedImages, pageSize) : selectedImages;
2941
};
3042

3143
const Gallery: FC<Props> = ({ images }) => {
44+
const breakpoint = useActiveBreakpoint();
45+
46+
const pageSize = getPageSize(breakpoint);
47+
// INITIAL_PAGE controls first screen
48+
const initialPage = getInitialPage(breakpoint);
49+
3250
const [loadedImages, setLoadedImages] = useState<GalleryImage[]>([]);
33-
const [page, setPage] = useState<number>(INITIAL_PAGE);
51+
const [page, setPage] = useState<number>(initialPage);
3452
const observerTarget = useRef(null);
3553

3654
const isScrollingDown = useScrollDown();
@@ -50,9 +68,9 @@ const Gallery: FC<Props> = ({ images }) => {
5068

5169
// converts page to loaded images
5270
useEffect(() => {
53-
const upToPageImages = fetchImagesUpToPage(images, page);
71+
const upToPageImages = fetchImagesUpToPage(images, pageSize, page);
5472
setLoadedImages(upToPageImages);
55-
}, [page, images]);
73+
}, [page, images, pageSize]);
5674

5775
// sets only page
5876
useEffect(() => {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useEffect, useState } from 'react';
2+
3+
import { TW_WIDTHS } from '@/constants/image';
4+
5+
import type { Breakpoint } from '@/types/constants';
6+
7+
export const useWidth = (): number => {
8+
const [width, setWidth] = useState<number>(window.innerWidth);
9+
10+
useEffect(() => {
11+
const handleResize = () => setWidth(window.innerWidth);
12+
13+
window.addEventListener('resize', handleResize);
14+
15+
return () => window.removeEventListener('resize', handleResize);
16+
}, []);
17+
18+
return width;
19+
};
20+
21+
export const getActiveBreakpoint = (width: number): Breakpoint =>
22+
(Object.entries(TW_WIDTHS)
23+
.reverse()
24+
.find(([_, value]) => width >= value)?.[0] as Breakpoint) ?? ('XXS' as const);
25+
26+
export const useActiveBreakpoint = (): Breakpoint => {
27+
const width = useWidth();
28+
return getActiveBreakpoint(width);
29+
};

src/constants/gallery.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ export const GALLERY = {
44
GALLERY_ID: 'my-gallery',
55
// Todo: make it responsive
66
/** step. */
7-
PAGE_SIZE: 3,
7+
PAGE_SIZE: {
8+
XS: 1,
9+
SM: 2,
10+
LG: 3,
11+
},
812
/** page dependency in useEffect is more important. To load first screen quickly, set to 3 pages */
9-
INITIAL_PAGE: 3,
13+
INITIAL_PAGE: {
14+
XS: 3,
15+
SM: 3,
16+
LG: 3,
17+
},
1018
/** fine tuned for scroll */
1119
OBSERVER_DEBOUNCE: 300,
1220
} as const;

src/constants/image.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ImageSizes } from '@/types/constants';
22

33
/** matches tailwindcss/defaultTheme.screens */
44

5-
export const TW_SCREENS = {
5+
export const TW_WIDTHS = {
66
/** added, for Image, bellow xs */
77
XXS: 320,
88
XS: 475,
@@ -11,6 +11,10 @@ export const TW_SCREENS = {
1111
LG: 1024,
1212
XL: 1280,
1313
_2XL: 1536,
14+
} as const;
15+
16+
export const TW_SCREENS = {
17+
...TW_WIDTHS,
1418
HEIGHTS: {
1519
XXS: 180,
1620
XS: 268,

src/layouts/Page.astro

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export interface Content {
1010
file: string;
1111
url: string;
1212
/** set max-w-xxx */
13-
class: string;
13+
class?: string;
14+
/** for flex flex-grow min-height to prevent layout shift for client components */
15+
articleClass?: string;
1416
}
1517
1618
export interface Props {
@@ -19,7 +21,7 @@ export interface Props {
1921
2022
// metadata from frontmatter
2123
const { content } = Astro.props;
22-
const { title, description, class: className } = content;
24+
const { title, description, class: className, articleClass } = content;
2325
2426
// available in Layouts
2527
const { pathname } = Astro.url;
@@ -33,7 +35,7 @@ const metadata = { title, description, image };
3335

3436
<Centered {metadata} class={cn(className)}>
3537
{/* must not have flex, it will disable margin collapsing */}
36-
<article class="my-prose">
38+
<article class={cn('my-prose', articleClass)}>
3739
<slot />
3840
</article>
3941
</Centered>

src/pages/gallery.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
layout: '../layouts/Page.astro'
33
title: 'Gallery'
44
description: 'Image gallery'
5+
# classNames for Page layout
56
class: 'max-w-5xl'
7+
articleClass: 'grow flex flex-col'
68
---
79

810
import Gallery from '../components/Gallery.astro';
911

1012
# Gallery
1113

12-
<Gallery class="not-prose" />
14+
<Gallery class="not-prose grow" />

src/types/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { CATEGORIES } from '@/constants/collections';
2+
import type { TW_WIDTHS } from '@/constants/image';
23
import type { PAGE_METADATA } from '@/constants/metadata';
34
import type { NAVIGATION_ITEMS } from '@/constants/navigation';
45
import type { MODES, THEMES } from '@/constants/theme';
@@ -21,3 +22,5 @@ export type ImageSizes = {
2122
};
2223

2324
export type ChangeThemeCustomEvent = CustomEvent<{ theme: Theme }>;
25+
26+
export type Breakpoint = keyof typeof TW_WIDTHS;

src/utils/array.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
export const getRandomElementFromArray = <T>(arr: T[]): T =>
22
arr[Math.floor(Math.random() * arr.length)];
33

4-
export const sliceToMod4 = <T>(arr: T[]): T[] => arr.slice(0, arr.length - (arr.length % 4));
4+
export const sliceToModN = <T>(arr: T[], modN: number): T[] =>
5+
arr.slice(0, arr.length - (arr.length % modN));
56

67
/** returns tuple, must map to object with named keys */
78
export const mergeArrays = <T extends unknown[]>(...arrays: T[]): T[number][][] =>

src/utils/gallery.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { GALLERY } from '@/constants/gallery';
2+
3+
import type { Breakpoint } from '@/types/constants';
4+
5+
const { PAGE_SIZE, INITIAL_PAGE } = GALLERY;
6+
7+
// related to gallery grid css
8+
const breakpointToPageKey = {
9+
XXS: 'XS',
10+
XS: 'XS',
11+
SM: 'SM',
12+
MD: 'SM',
13+
LG: 'LG',
14+
XL: 'LG',
15+
_2XL: 'LG',
16+
} as const;
17+
18+
const defaultPageKey = 'LG' as const;
19+
20+
export const getPageSize = (breakpoint: Breakpoint): number => {
21+
const key = breakpointToPageKey[breakpoint] ?? defaultPageKey;
22+
const pageSize = PAGE_SIZE[key];
23+
24+
return pageSize;
25+
};
26+
27+
export const getInitialPage = (breakpoint: Breakpoint): number => {
28+
const key = breakpointToPageKey[breakpoint] ?? defaultPageKey;
29+
const initialPage = INITIAL_PAGE[key];
30+
31+
return initialPage;
32+
};

0 commit comments

Comments
 (0)