diff --git a/web/packages/new/photos/components/utils/thumbnail-grid-layout.ts b/web/packages/new/photos/components/utils/thumbnail-grid-layout.ts new file mode 100644 index 0000000000..cfe1b9b34d --- /dev/null +++ b/web/packages/new/photos/components/utils/thumbnail-grid-layout.ts @@ -0,0 +1,80 @@ +export interface ThumbnailGridLayoutParams { + /** The inline padding (px) of the thumbnail grid. */ + paddingInline: number; + /** The number of columns in the thumbnail grid. */ + columns: number; + /** The width (px) of each item. */ + itemWidth: number; + /** The height (px) of each item. */ + itemHeight: number; + /** The gap (px) between each grid item. */ + gap: number; +} + +/** + * Determine the layout parameters for a grid of thumbnails that would best fit + * the given width under various constraints. + * + * @param containerWidth The width available to the container. In our case, + * since the thumbnail grids span the entire available width, this is the width + * of the page itself. + */ +export const computeThumbnailGridLayoutParams = ( + containerWidth: number, +): ThumbnailGridLayoutParams => { + const paddingInline = getGapFromScreenEdge(containerWidth); + const fittableColumns = getFractionFittableColumns(containerWidth); + + let columns = Math.floor(fittableColumns); + if (columns < MIN_COLUMNS) { + columns = MIN_COLUMNS; + } + + const shrinkRatio = getShrinkRatio(containerWidth, columns); + const itemHeight = IMAGE_CONTAINER_MAX_HEIGHT * shrinkRatio; + const itemWidth = IMAGE_CONTAINER_MAX_WIDTH * shrinkRatio; + const gap = GAP_BTW_TILES; + + return { + paddingInline, + columns, + itemWidth, + itemHeight, + gap, + }; +}; + +/* TODO: Some of this code is also duplicated elsewhere. See if those places can + reuse the same function as above, with some extra params if needed. + + So that the duplication is easier to identify, keeping the duplication + verbatim for now */ + +const GAP_BTW_TILES = 4; +const IMAGE_CONTAINER_MAX_HEIGHT = 180; +const IMAGE_CONTAINER_MAX_WIDTH = 180; +const MIN_COLUMNS = 4; + +function getFractionFittableColumns(width: number): number { + return ( + (width - 2 * getGapFromScreenEdge(width) + GAP_BTW_TILES) / + (IMAGE_CONTAINER_MAX_WIDTH + GAP_BTW_TILES) + ); +} + +function getGapFromScreenEdge(width: number) { + if (width > MIN_COLUMNS * IMAGE_CONTAINER_MAX_WIDTH) { + return 24; + } else { + return 4; + } +} + +function getShrinkRatio(width: number, columns: number) { + return ( + (width - + 2 * getGapFromScreenEdge(width) - + (columns - 1) * GAP_BTW_TILES) / + (columns * IMAGE_CONTAINER_MAX_WIDTH) + ); +} diff --git a/web/packages/new/photos/pages/duplicates.tsx b/web/packages/new/photos/pages/duplicates.tsx index e94b22247c..3f1c0140ae 100644 --- a/web/packages/new/photos/pages/duplicates.tsx +++ b/web/packages/new/photos/pages/duplicates.tsx @@ -440,6 +440,10 @@ const ListItem: React.FC> = memo( {pt(`${count} items, ${itemSize} each`)} + {/* + The size of this Checkbox, 42px, drives the height of + the header. + */}