mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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
This commit is contained in:
parent
ffa05a0bb3
commit
75c1c4ce5a
@ -3,17 +3,15 @@ import { Box, forwardRef } from '@chakra-ui/react';
|
|||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
// This is exported so that we can use it to calculate the number of images per row
|
export const imageItemContainerTestId = 'image-item-container';
|
||||||
// for the directional gallery navigation.
|
|
||||||
export const GALLERY_IMAGE_PADDING_PX = 6;
|
|
||||||
|
|
||||||
type ItemContainerProps = PropsWithChildren & FlexProps;
|
type ItemContainerProps = PropsWithChildren & FlexProps;
|
||||||
const ItemContainer = forwardRef((props: ItemContainerProps, ref) => (
|
const ItemContainer = forwardRef((props: ItemContainerProps, ref) => (
|
||||||
<Box
|
<Box
|
||||||
className="item-container"
|
className="item-container"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
p={`${GALLERY_IMAGE_PADDING_PX}px`}
|
p={1.5}
|
||||||
data-testid="image-item-container"
|
data-testid={imageItemContainerTestId}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -4,6 +4,8 @@ import { useAppSelector } from 'app/store/storeHooks';
|
|||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
export const imageListContainerTestId = 'image-list-container';
|
||||||
|
|
||||||
type ListContainerProps = PropsWithChildren & FlexProps;
|
type ListContainerProps = PropsWithChildren & FlexProps;
|
||||||
const ListContainer = forwardRef((props: ListContainerProps, ref) => {
|
const ListContainer = forwardRef((props: ListContainerProps, ref) => {
|
||||||
const galleryImageMinimumWidth = useAppSelector(
|
const galleryImageMinimumWidth = useAppSelector(
|
||||||
@ -16,7 +18,7 @@ const ListContainer = forwardRef((props: ListContainerProps, ref) => {
|
|||||||
className="list-container"
|
className="list-container"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
|
gridTemplateColumns={`repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, 1fr))`}
|
||||||
data-testid="image-list-container"
|
data-testid={imageListContainerTestId}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId';
|
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 { virtuosoGridRefs } from 'features/gallery/components/ImageGrid/types';
|
||||||
import { useGalleryImages } from 'features/gallery/hooks/useGalleryImages';
|
import { useGalleryImages } from 'features/gallery/hooks/useGalleryImages';
|
||||||
import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors';
|
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.
|
* Gets the number of images per row in the gallery by grabbing their DOM elements.
|
||||||
*/
|
*/
|
||||||
const getImagesPerRow = (): number => {
|
const getImagesPerRow = (): number => {
|
||||||
const imageRect = Object.values(
|
const widthOfGalleryImage =
|
||||||
document.getElementsByClassName('gallerygrid-image')
|
document
|
||||||
)[0]?.getBoundingClientRect();
|
.querySelector(`[data-testid="${imageItemContainerTestId}"]`)
|
||||||
|
?.getBoundingClientRect().width ?? 1;
|
||||||
|
|
||||||
// We have to manually take into account the padding of the image container, else
|
const widthOfGalleryGrid =
|
||||||
// imagesPerRow will be wrong when the gallery is large or images are very small.
|
document
|
||||||
const widthOfGalleryImage = imageRect
|
.querySelector(`[data-testid="${imageListContainerTestId}"]`)
|
||||||
? imageRect.width + GALLERY_IMAGE_PADDING_PX * 2
|
?.getBoundingClientRect().width ?? 0;
|
||||||
: 0;
|
|
||||||
|
|
||||||
const galleryGridRect = document
|
const imagesPerRow = Math.round(widthOfGalleryGrid / widthOfGalleryImage);
|
||||||
.getElementById('gallery-grid')
|
|
||||||
?.getBoundingClientRect();
|
|
||||||
|
|
||||||
const widthOfGalleryGrid = galleryGridRect?.width ?? 0;
|
|
||||||
|
|
||||||
const imagesPerRow = Math.floor(widthOfGalleryGrid / widthOfGalleryImage);
|
|
||||||
|
|
||||||
return imagesPerRow;
|
return imagesPerRow;
|
||||||
};
|
};
|
||||||
@ -104,11 +99,11 @@ const getUpImage = (images: ImageDTO[], currentIndex: number) => {
|
|||||||
|
|
||||||
const getDownImage = (images: ImageDTO[], currentIndex: number) => {
|
const getDownImage = (images: ImageDTO[], currentIndex: number) => {
|
||||||
const imagesPerRow = getImagesPerRow();
|
const imagesPerRow = getImagesPerRow();
|
||||||
// If we are on the first row, we want to stay on the first row, not go to last image
|
// If there are no images below the current image, we want to stay where we are
|
||||||
const isOnLastRow = currentIndex >= images.length - imagesPerRow;
|
const areImagesBelow = currentIndex < images.length - imagesPerRow;
|
||||||
const index = isOnLastRow
|
const index = areImagesBelow
|
||||||
? currentIndex
|
? clamp(currentIndex + imagesPerRow, 0, images.length - 1)
|
||||||
: clamp(currentIndex + imagesPerRow, 0, images.length - 1);
|
: currentIndex;
|
||||||
const image = images[index];
|
const image = images[index];
|
||||||
return { index, image };
|
return { index, image };
|
||||||
};
|
};
|
||||||
@ -164,7 +159,7 @@ export const useGalleryNavigation = (): UseGalleryNavigationReturn => {
|
|||||||
imagesSelectors.selectAll(data),
|
imagesSelectors.selectAll(data),
|
||||||
lastSelectedImageIndex
|
lastSelectedImageIndex
|
||||||
);
|
);
|
||||||
if (!image) {
|
if (!image || index === lastSelectedImageIndex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(imageSelected(image));
|
dispatch(imageSelected(image));
|
||||||
|
Loading…
Reference in New Issue
Block a user