Initial Commit

This commit is contained in:
2026-10-03 20:07:49 +02:00
commit a1346c98b1
427 changed files with 59320 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
// 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
}