mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
fix(ui): merge refs when forwardingin DndImage
This commit is contained in:
@@ -9,7 +9,7 @@ import { createSingleImageDragPreview, setSingleImageDragPreview } from 'feature
|
||||
import { firefoxDndFix } from 'features/dnd/util';
|
||||
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
|
||||
import { $imageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const sx = {
|
||||
@@ -23,69 +23,68 @@ const sx = {
|
||||
},
|
||||
} satisfies SystemStyleObject;
|
||||
|
||||
/* eslint-disable-next-line @typescript-eslint/no-namespace */
|
||||
export namespace DndImage {
|
||||
export interface Props extends ImageProps {
|
||||
imageDTO: ImageDTO;
|
||||
asThumbnail?: boolean;
|
||||
}
|
||||
}
|
||||
type Props = {
|
||||
imageDTO: ImageDTO;
|
||||
asThumbnail?: boolean;
|
||||
} & ImageProps;
|
||||
|
||||
export const DndImage = memo(({ imageDTO, asThumbnail, ...rest }: DndImage.Props) => {
|
||||
const store = useAppStore();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const ref = useRef<HTMLImageElement>(null);
|
||||
const [dragPreviewState, setDragPreviewState] = useState<DndDragPreviewSingleImageState | null>(null);
|
||||
export const DndImage = memo(
|
||||
forwardRef(({ imageDTO, asThumbnail, ...rest }: Props, forwardedRef) => {
|
||||
const store = useAppStore();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const ref = useRef<HTMLImageElement>(null);
|
||||
useImperativeHandle(forwardedRef, () => ref.current!, []);
|
||||
const [dragPreviewState, setDragPreviewState] = useState<DndDragPreviewSingleImageState | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
return combine(
|
||||
firefoxDndFix(element),
|
||||
draggable({
|
||||
element,
|
||||
getInitialData: () => singleImageDndSource.getData({ imageDTO }, imageDTO.image_name),
|
||||
onDragStart: () => {
|
||||
setIsDragging(true);
|
||||
if ($imageViewer.get()) {
|
||||
$imageViewer.set(false);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
setIsDragging(false);
|
||||
},
|
||||
onGenerateDragPreview: (args) => {
|
||||
if (singleImageDndSource.typeGuard(args.source.data)) {
|
||||
setSingleImageDragPreview({
|
||||
singleImageDndData: args.source.data,
|
||||
onGenerateDragPreviewArgs: args,
|
||||
setDragPreviewState,
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
return combine(
|
||||
firefoxDndFix(element),
|
||||
draggable({
|
||||
element,
|
||||
getInitialData: () => singleImageDndSource.getData({ imageDTO }, imageDTO.image_name),
|
||||
onDragStart: () => {
|
||||
setIsDragging(true);
|
||||
if ($imageViewer.get()) {
|
||||
$imageViewer.set(false);
|
||||
}
|
||||
},
|
||||
onDrop: () => {
|
||||
setIsDragging(false);
|
||||
},
|
||||
onGenerateDragPreview: (args) => {
|
||||
if (singleImageDndSource.typeGuard(args.source.data)) {
|
||||
setSingleImageDragPreview({
|
||||
singleImageDndData: args.source.data,
|
||||
onGenerateDragPreviewArgs: args,
|
||||
setDragPreviewState,
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [forwardedRef, imageDTO, store]);
|
||||
|
||||
useImageContextMenu(imageDTO, ref);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
role="button"
|
||||
ref={ref}
|
||||
src={asThumbnail ? imageDTO.thumbnail_url : imageDTO.image_url}
|
||||
fallbackSrc={asThumbnail ? undefined : imageDTO.thumbnail_url}
|
||||
w={imageDTO.width}
|
||||
sx={sx}
|
||||
data-is-dragging={isDragging}
|
||||
{...rest}
|
||||
/>
|
||||
{dragPreviewState?.type === 'single-image' ? createSingleImageDragPreview(dragPreviewState) : null}
|
||||
</>
|
||||
);
|
||||
}, [imageDTO, store]);
|
||||
|
||||
useImageContextMenu(imageDTO, ref);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
role="button"
|
||||
ref={ref}
|
||||
src={asThumbnail ? imageDTO.thumbnail_url : imageDTO.image_url}
|
||||
fallbackSrc={asThumbnail ? undefined : imageDTO.thumbnail_url}
|
||||
w={imageDTO.width}
|
||||
sx={sx}
|
||||
data-is-dragging={isDragging}
|
||||
{...rest}
|
||||
/>
|
||||
{dragPreviewState?.type === 'single-image' ? createSingleImageDragPreview(dragPreviewState) : null}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
})
|
||||
);
|
||||
DndImage.displayName = 'DndImage';
|
||||
|
||||
@@ -60,11 +60,8 @@ const getImageDTOFromMap = (target: Node): ImageDTO | undefined => {
|
||||
* @param imageDTO The image DTO to register the context menu for.
|
||||
* @param targetRef The ref of the target element that should trigger the context menu.
|
||||
*/
|
||||
export const useImageContextMenu = (imageDTO: ImageDTO | undefined, ref: RefObject<HTMLElement>) => {
|
||||
export const useImageContextMenu = (imageDTO: ImageDTO, ref: RefObject<HTMLElement>) => {
|
||||
useEffect(() => {
|
||||
if (!imageDTO) {
|
||||
return;
|
||||
}
|
||||
const el = ref.current;
|
||||
if (!el) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user