Skip to content

Commit 7759b16

Browse files
fix(ui): dnd on images
Need to use callback refs else chakra's image fallback breaks the ref
1 parent 9fc51c7 commit 7759b16

File tree

2 files changed

+24
-18
lines changed

2 files changed

+24
-18
lines changed

invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@ const getImageDTOFromMap = (target: Node): ImageDTO | undefined => {
6060
* @param imageDTO The image DTO to register the context menu for.
6161
* @param targetRef The ref of the target element that should trigger the context menu.
6262
*/
63-
export const useImageContextMenu = (imageDTO: ImageDTO, ref: RefObject<HTMLElement>) => {
63+
export const useImageContextMenu = (imageDTO: ImageDTO, ref: RefObject<HTMLElement> | (HTMLElement | null)) => {
6464
useEffect(() => {
65-
const el = ref.current;
65+
if (ref === null) {
66+
return;
67+
}
68+
const el = ref instanceof HTMLElement ? ref : ref.current;
6669
if (!el) {
6770
return;
6871
}

invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,22 @@ import { firefoxDndFix } from 'features/dnd/util';
1616
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
1717
import { GalleryImageHoverIcons } from 'features/gallery/components/ImageGrid/GalleryImageHoverIcons';
1818
import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId';
19-
import { selectListImageNamesQueryArgs } from 'features/gallery/store/gallerySelectors';
19+
import {
20+
selectListImageNamesQueryArgs,
21+
selectSelectedBoardId,
22+
selectSelection,
23+
} from 'features/gallery/store/gallerySelectors';
2024
import { imageToCompareChanged, selectGallerySlice, selectionChanged } from 'features/gallery/store/gallerySlice';
2125
import { useAutoLayoutContext } from 'features/ui/layouts/auto-layout-context';
2226
import { VIEWER_PANEL_ID } from 'features/ui/layouts/shared';
2327
import type { MouseEvent, MouseEventHandler } from 'react';
24-
import { memo, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
28+
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
2529
import { PiImageBold } from 'react-icons/pi';
2630
import { imagesApi } from 'services/api/endpoints/images';
2731
import type { ImageDTO } from 'services/api/types';
2832

33+
const GALLERY_IMAGE_CLASS = 'gallery-image';
34+
2935
const galleryImageContainerSX = {
3036
containerType: 'inline-size',
3137
w: 'full',
@@ -38,7 +44,7 @@ const galleryImageContainerSX = {
3844
'&[data-is-dragging=true]': {
3945
opacity: 0.3,
4046
},
41-
'.gallery-image': {
47+
[`.${GALLERY_IMAGE_CLASS}`]: {
4248
touchAction: 'none',
4349
userSelect: 'none',
4450
webkitUserSelect: 'none',
@@ -139,8 +145,8 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
139145
const [dragPreviewState, setDragPreviewState] = useState<
140146
DndDragPreviewSingleImageState | DndDragPreviewMultipleImageState | null
141147
>(null);
142-
const ref = useRef<HTMLImageElement>(null);
143-
const dndId = useId();
148+
// Must use callback ref - else chakra's Image fallback prop will break the ref & dnd
149+
const [element, ref] = useState<HTMLImageElement | null>(null);
144150
const selectIsSelectedForCompare = useMemo(
145151
() => createSelector(selectGallerySlice, (gallery) => gallery.imageToCompare === imageDTO.image_name),
146152
[imageDTO.image_name]
@@ -153,7 +159,6 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
153159
const isSelected = useAppSelector(selectIsSelected);
154160

155161
useEffect(() => {
156-
const element = ref.current;
157162
if (!element) {
158163
return;
159164
}
@@ -162,16 +167,14 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
162167
draggable({
163168
element,
164169
getInitialData: () => {
165-
const { gallery } = store.getState();
170+
const selection = selectSelection(store.getState());
171+
const boardId = selectSelectedBoardId(store.getState());
166172
// When we have multiple images selected, and the dragged image is part of the selection, initiate a
167173
// multi-image drag.
168-
if (
169-
gallery.selection.length > 1 &&
170-
gallery.selection.find((image_name) => image_name === imageDTO.image_name) !== undefined
171-
) {
174+
if (selection.length > 1 && selection.includes(imageDTO.image_name)) {
172175
return multipleImageDndSource.getData({
173-
image_names: gallery.selection,
174-
board_id: gallery.selectedBoardId,
176+
image_names: selection,
177+
board_id: boardId,
175178
});
176179
}
177180

@@ -221,7 +224,7 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
221224
},
222225
})
223226
);
224-
}, [imageDTO, store, dndId]);
227+
}, [element, imageDTO, store]);
225228

226229
const [isHovered, setIsHovered] = useState(false);
227230

@@ -242,14 +245,14 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
242245

243246
const dataTestId = useMemo(() => getGalleryImageDataTestId(imageDTO.image_name), [imageDTO.image_name]);
244247

245-
useImageContextMenu(imageDTO, ref);
248+
useImageContextMenu(imageDTO, element);
246249

247250
return (
248251
<>
249252
<Box sx={galleryImageContainerSX} data-testid={dataTestId} data-is-dragging={isDragging}>
250253
<Flex
251254
role="button"
252-
className="gallery-image"
255+
className={GALLERY_IMAGE_CLASS}
253256
onMouseOver={onMouseOver}
254257
onMouseOut={onMouseOut}
255258
onClick={onClick}

0 commit comments

Comments
 (0)