import GameDto from "Frontend/generated/org/gameyfin/app/games/dto/GameDto"; import {GameCover} from "Frontend/components/general/covers/GameCover"; import type {CellComponentProps} from "react-window"; import {Grid} from "react-window"; import {useEffect, useRef, useState} from "react"; interface CoverGridProps { games: GameDto[]; } // Constants for grid layout const MIN_COLUMN_WIDTH = 180; // Minimum width per item (minmax value from original) const MAX_COLUMN_WIDTH = 212; // Maximum width per item (minmax value from original) const GAP = 16; // gap-4 = 1rem = 16px const ASPECT_RATIO = 12 / 17; // Game cover aspect ratio (width/height) export default function CoverGrid({games}: CoverGridProps) { const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(0); // Update container width on resize useEffect(() => { const updateDimensions = () => { if (containerRef.current) { setContainerWidth(containerRef.current.offsetWidth); } }; const resizeObserver = new ResizeObserver(updateDimensions); if (containerRef.current) { resizeObserver.observe(containerRef.current); } updateDimensions(); return () => resizeObserver.disconnect(); }, []); // Calculate how many columns can fit const columnCount = Math.max(1, Math.floor((containerWidth + GAP) / (MIN_COLUMN_WIDTH + GAP))); // Calculate actual column width to distribute space evenly (up to MAX_COLUMN_WIDTH) const actualColumnWidth = Math.min( MAX_COLUMN_WIDTH, Math.floor((containerWidth - (columnCount - 1) * GAP) / columnCount) ); // Calculate cover height based on width and aspect ratio // GameCover's size prop is the height, so we need to calculate height from width const coverHeight = Math.floor(actualColumnWidth / ASPECT_RATIO); // Calculate row count const rowCount = Math.ceil(games.length / columnCount); // Cell renderer for react-window Grid const Cell = ({ columnIndex, rowIndex, style }: CellComponentProps<{}>) => { const gameIndex = rowIndex * columnCount + columnIndex; // Return empty cell if we're past the end of the games array if (gameIndex >= games.length) { return
; } const game = games[gameIndex]; return (
); }; // Column width function to handle the last column differently const getColumnWidth = (index: number) => { // Last column doesn't need gap after it if (index === columnCount - 1) { return actualColumnWidth; } return actualColumnWidth + GAP; }; return (
{containerWidth > 0 && ( columnCount={columnCount} columnWidth={getColumnWidth} rowCount={rowCount} rowHeight={coverHeight + GAP} defaultWidth={containerWidth} cellComponent={Cell} cellProps={{}} style={{overflowX: 'hidden'}} /> )}
); }