From 75c1c4ce5a3750030665edf2e268f9620041c8e6 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sun, 7 Jan 2024 11:58:52 +1100 Subject: [PATCH] fix(ui): fix gallery nav math - Use the virtuoso grid item container and list containers to calculate imagesPerRow, skipping manual compensation for padding of images - Round the imagesPerRow instead of flooring - we often will end up with values like 4.99999 due to floating point precision - Update `getDownImage` comments & logic to be clearer - Use variables for the ids in query selectors, preventing future typos - Only scroll if the new selected image is different from the prev one --- .../ImageGrid/ImageGridItemContainer.tsx | 8 ++-- .../ImageGrid/ImageGridListContainer.tsx | 4 +- .../gallery/hooks/useGalleryNavigation.ts | 39 ++++++++----------- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridItemContainer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridItemContainer.tsx index 9d09ef6409..6d793a767a 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridItemContainer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridItemContainer.tsx @@ -3,17 +3,15 @@ import { Box, forwardRef } from '@chakra-ui/react'; import type { PropsWithChildren } from 'react'; import { memo } from 'react'; -// This is exported so that we can use it to calculate the number of images per row -// for the directional gallery navigation. -export const GALLERY_IMAGE_PADDING_PX = 6; +export const imageItemContainerTestId = 'image-item-container'; type ItemContainerProps = PropsWithChildren & FlexProps; const ItemContainer = forwardRef((props: ItemContainerProps, ref) => ( {props.children} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridListContainer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridListContainer.tsx index e28dcdd348..678748e8df 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridListContainer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/ImageGridListContainer.tsx @@ -4,6 +4,8 @@ import { useAppSelector } from 'app/store/storeHooks'; import type { PropsWithChildren } from 'react'; import { memo } from 'react'; +export const imageListContainerTestId = 'image-list-container'; + type ListContainerProps = PropsWithChildren & FlexProps; const ListContainer = forwardRef((props: ListContainerProps, ref) => { const galleryImageMinimumWidth = useAppSelector( @@ -16,7 +18,7 @@ const ListContainer = forwardRef((props: ListContainerProps, ref) => { className="list-container" ref={ref} gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`} - data-testid="image-list-container" + data-testid={imageListContainerTestId} > {props.children} diff --git a/invokeai/frontend/web/src/features/gallery/hooks/useGalleryNavigation.ts b/invokeai/frontend/web/src/features/gallery/hooks/useGalleryNavigation.ts index 4744de8069..a0d6d208a4 100644 --- a/invokeai/frontend/web/src/features/gallery/hooks/useGalleryNavigation.ts +++ b/invokeai/frontend/web/src/features/gallery/hooks/useGalleryNavigation.ts @@ -1,6 +1,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId'; -import { GALLERY_IMAGE_PADDING_PX } from 'features/gallery/components/ImageGrid/ImageGridItemContainer'; +import { imageItemContainerTestId } from 'features/gallery/components/ImageGrid/ImageGridItemContainer'; +import { imageListContainerTestId } from 'features/gallery/components/ImageGrid/ImageGridListContainer'; import { virtuosoGridRefs } from 'features/gallery/components/ImageGrid/types'; import { useGalleryImages } from 'features/gallery/hooks/useGalleryImages'; import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors'; @@ -27,23 +28,17 @@ import { imagesSelectors } from 'services/api/util'; * Gets the number of images per row in the gallery by grabbing their DOM elements. */ const getImagesPerRow = (): number => { - const imageRect = Object.values( - document.getElementsByClassName('gallerygrid-image') - )[0]?.getBoundingClientRect(); + const widthOfGalleryImage = + document + .querySelector(`[data-testid="${imageItemContainerTestId}"]`) + ?.getBoundingClientRect().width ?? 1; - // We have to manually take into account the padding of the image container, else - // imagesPerRow will be wrong when the gallery is large or images are very small. - const widthOfGalleryImage = imageRect - ? imageRect.width + GALLERY_IMAGE_PADDING_PX * 2 - : 0; + const widthOfGalleryGrid = + document + .querySelector(`[data-testid="${imageListContainerTestId}"]`) + ?.getBoundingClientRect().width ?? 0; - const galleryGridRect = document - .getElementById('gallery-grid') - ?.getBoundingClientRect(); - - const widthOfGalleryGrid = galleryGridRect?.width ?? 0; - - const imagesPerRow = Math.floor(widthOfGalleryGrid / widthOfGalleryImage); + const imagesPerRow = Math.round(widthOfGalleryGrid / widthOfGalleryImage); return imagesPerRow; }; @@ -104,11 +99,11 @@ const getUpImage = (images: ImageDTO[], currentIndex: number) => { const getDownImage = (images: ImageDTO[], currentIndex: number) => { const imagesPerRow = getImagesPerRow(); - // If we are on the first row, we want to stay on the first row, not go to last image - const isOnLastRow = currentIndex >= images.length - imagesPerRow; - const index = isOnLastRow - ? currentIndex - : clamp(currentIndex + imagesPerRow, 0, images.length - 1); + // If there are no images below the current image, we want to stay where we are + const areImagesBelow = currentIndex < images.length - imagesPerRow; + const index = areImagesBelow + ? clamp(currentIndex + imagesPerRow, 0, images.length - 1) + : currentIndex; const image = images[index]; return { index, image }; }; @@ -164,7 +159,7 @@ export const useGalleryNavigation = (): UseGalleryNavigationReturn => { imagesSelectors.selectAll(data), lastSelectedImageIndex ); - if (!image) { + if (!image || index === lastSelectedImageIndex) { return; } dispatch(imageSelected(image));