forked from BRETT/BRETT
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
// Gemeinsame Viewport-Pagination für Upload-Grids (Mainseite, Profilseite).
|
|
// Der Client schickt cells (Zeilen x Spalten, die ohne Scrollen passen), cols
|
|
// und den Modus — der Server simuliert grid-auto-flow: dense exakt nach.
|
|
|
|
export function getGridPageParams(q: Record<string, unknown>) {
|
|
const page = Math.max(1, Math.floor(Number(q.page)) || 1)
|
|
const cells = Math.min(400, Math.max(1, Math.floor(Number(q.cells)) || 48))
|
|
const cols = Math.min(12, Math.max(1, Math.floor(Number(q.cols)) || 12))
|
|
const uniform = q.mode === 'uniform'
|
|
return { page, cells, cols, uniform }
|
|
}
|
|
|
|
// Gleiche Maße wie UploadTile: uniform = 1x1, >=7 Kommentare = 3x3
|
|
// (ab sm/cols>=6) bzw. 2x2, >=1 Kommentar = 2x2, sonst 1x1
|
|
export function gridTileDims(commentCount: number, uniform: boolean, cols: number): [h: number, w: number] {
|
|
return uniform ? [1, 1] : commentCount >= 7 ? (cols >= 6 ? [3, 3] : [2, 2]) : commentCount >= 1 ? [2, 2] : [1, 1]
|
|
}
|
|
|
|
// Simuliert grid-auto-flow: dense — jedes Tile geht in die erste freie
|
|
// Rechteck-Position (zeilenweise von oben links). Passt es nirgends in die
|
|
// maxRows-Zeilen, wird es auf die nächste Seite verschoben (Reihenfolge der
|
|
// übrigen Items bleibt erhalten). Kleinere Tiles danach füllen noch Lücken.
|
|
export function paginateGrid<T>(items: T[], dims: (item: T) => [h: number, w: number], cells: number, cols: number): T[][] {
|
|
const maxRows = Math.max(1, Math.floor(cells / cols))
|
|
|
|
const pack = (input: T[]) => {
|
|
const occupied: boolean[][] = []
|
|
const placed: T[] = []
|
|
const deferred: T[] = []
|
|
const fits = (r: number, c: number, h: number, w: number) => {
|
|
if (r + h > maxRows || c + w > cols) return false
|
|
for (let dr = 0; dr < h; dr++)
|
|
for (let dc = 0; dc < w; dc++)
|
|
if (occupied[r + dr]?.[c + dc]) return false
|
|
return true
|
|
}
|
|
for (const item of input) {
|
|
const [h, w] = dims(item)
|
|
let done = false
|
|
for (let r = 0; r + h <= maxRows && !done; r++) {
|
|
for (let c = 0; c + w <= cols; c++) {
|
|
if (fits(r, c, h, w)) {
|
|
for (let dr = 0; dr < h; dr++) {
|
|
const line = (occupied[r + dr] ??= new Array(cols).fill(false))
|
|
for (let dc = 0; dc < w; dc++) line[c + dc] = true
|
|
}
|
|
placed.push(item)
|
|
done = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if (!done) deferred.push(item)
|
|
}
|
|
return { placed, deferred }
|
|
}
|
|
|
|
const pages: T[][] = []
|
|
let rest = items
|
|
while (rest.length) {
|
|
const { placed, deferred } = pack(rest)
|
|
if (!placed.length) {
|
|
// Sicherheitsnetz: kann bei cols>=1 nie leer sein (1x1 passt immer)
|
|
pages.push(rest)
|
|
break
|
|
}
|
|
pages.push(placed)
|
|
rest = deferred
|
|
}
|
|
return pages
|
|
}
|