Introduce Picturarium config.
This commit is contained in:
parent
c3c629943c
commit
65fe6a9a82
1 changed files with 66 additions and 16 deletions
|
|
@ -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<Result<ImageId[], RequestError>> {
|
||||
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 (
|
||||
<Box sx={imageGridStyle(Dimension.medium)}>
|
||||
{images.map((_, index) =>
|
||||
|
|
@ -396,7 +446,7 @@ export default function Picturarium() {
|
|||
color: "oklch(65% 0.02 260)",
|
||||
}}
|
||||
>
|
||||
{state.page.page}
|
||||
{state.page.index}
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue