diff --git a/src/ui/Picturarium.tsx b/src/ui/Picturarium.tsx index 4c19d19..1ff0b83 100644 --- a/src/ui/Picturarium.tsx +++ b/src/ui/Picturarium.tsx @@ -10,6 +10,57 @@ import { Remote } from "../remote" // I also like to keep views/states/messages/initialization/effects/update of one component in one single (even if giant) file (or a structure like `Component/...` + `Component.tsx`) // - Others school of thought are to grind all of these into fine dust and scatter the files all over the codebase (all state definitions in one giant folder, all messages/updates in another). I prefer not doing that. It violates locality too much for me, and I constantly have to jump between files "far-away". +// === Config === +// prefered number of cols/rows. The page size (how many images are on a page) is calculated as `columns * rows` +type Config = { + pageSize: number + + mobileColumns: number + mobileRows: number + + desktopColumns: number + desktopRows: number +} + +const CONFIG = { + pageSize: 10, + + mobileColumns: 2, + mobileRows: 5, + + desktopColumns: 5, + desktopRows: 2, +} as const satisfies Config +// const CONFIG = { +// pageSize: 12, + +// mobileColumns: 2, +// mobileRows: 6, + +// desktopColumns: 4, +// desktopRows: 3, +// } as const satisfies Config + +// Could do comptime assert: `size = mobile product`, and `size = desktop product`, but this is pretty heavy in TS. +assertValidConfig(CONFIG) + +function assertValidConfig(config: Config): void { + const mobilePageSize = config.mobileColumns * config.mobileRows + const desktopPageSize = config.desktopColumns * config.desktopRows + + if (mobilePageSize !== config.pageSize) { + throw new Error( + `Invalid config: mobile grid defines ${String(mobilePageSize)} images, but pageSize is ${String(config.pageSize)}`, + ) + } + + if (desktopPageSize !== config.pageSize) { + throw new Error( + `Invalid config: desktop grid defines ${String(desktopPageSize)} images, but pageSize is ${String(config.pageSize)}`, + ) + } +} + // === Image === type ImageId = string type ImageRef = string @@ -28,45 +79,44 @@ const Dimension = { } // === Page === -type PageNumber = number +type PageIndex = number type Page = { - page: PageNumber - limit: number // page size + index: PageIndex + size: number } type PageKey = string -const LIMIT = 10 -const FIRST_PAGE: PageNumber = 1 +const FIRST_PAGE: PageIndex = 1 const Page = { init(): Page { - return { page: FIRST_PAGE, limit: LIMIT } + return { index: FIRST_PAGE, size: CONFIG.pageSize } }, next(page: Page): Page { - return { ...page, page: page.page + 1 } + return { ...page, index: page.index + 1 } }, previous(page: Page): Page { // Could do // if (page.page == FIRST_PAGE) { return page } // this preserves identity of object, so is nicer for `useEffect`, but it's waaaay to subtle. So I'm not relying on that. - return { ...page, page: Math.max(FIRST_PAGE, page.page - 1) } + return { ...page, index: Math.max(FIRST_PAGE, page.index - 1) } }, eq(page0: Page, page1: Page): boolean { - return page0.page === page1.page && page0.limit === page1.limit + return page0.index === page1.index && page0.size === page1.size }, // for hashing in maps to avoid identity problems key(page: Page): PageKey { - return `${String(page.page)}:${String(page.limit)}` + return `${String(page.index)}:${String(page.size)}` }, isFirst(page: Page): boolean { - return page.page === FIRST_PAGE + return page.index === FIRST_PAGE }, } // === api === async function getImageIds(page: Page): Promise> { - const result = await getPicsumImages(page) + const result = await getPicsumImages({ page: page.index, limit: page.size }) return Result.map(result, (data) => data.map(({ id }) => id)) } @@ -233,8 +283,8 @@ function imageGridStyle(dimension: Dimension) { return { display: "grid", gridTemplateColumns: { - xs: `repeat(2, ${String(dimension.width)}px)`, - md: `repeat(5, ${String(dimension.width)}px)`, + xs: `repeat(${String(CONFIG.mobileColumns)}, ${String(dimension.width)}px)`, + md: `repeat(${String(CONFIG.desktopColumns)}, ${String(dimension.width)}px)`, }, gridAutoRows: `${String(dimension.height)}px`, gap: 2, @@ -243,7 +293,7 @@ function imageGridStyle(dimension: Dimension) { // 2x5 on mobile, 5x2 on desktop (assuming 10 images per page) function ImagesSkeleton({ visible = true }: { visible?: boolean }) { - const images = Array.from({ length: 10 }) + const images = Array.from({ length: CONFIG.pageSize }) return ( {images.map((_, index) => @@ -396,7 +446,7 @@ export default function Picturarium() { color: "oklch(65% 0.02 260)", }} > - {state.page.page} + {state.page.index}