import type { ChakraProps, FlexProps, SystemStyleObject } from '@invoke-ai/ui-library'; import { Flex, Icon, Image } from '@invoke-ai/ui-library'; import { IAILoadingImageFallback, IAINoContentFallback } from 'common/components/IAIImageFallback'; import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay'; import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types'; import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu'; import type { MouseEvent, ReactElement, ReactNode, SyntheticEvent } from 'react'; import { memo, useCallback, useMemo, useState } from 'react'; import { PiImageBold, PiUploadSimpleBold } from 'react-icons/pi'; import type { ImageDTO, PostUploadAction } from 'services/api/types'; import IAIDraggable from './IAIDraggable'; import IAIDroppable from './IAIDroppable'; import SelectionOverlay from './SelectionOverlay'; const defaultUploadElement = ; const defaultNoContentFallback = ; type IAIDndImageProps = FlexProps & { imageDTO: ImageDTO | undefined; onError?: (event: SyntheticEvent) => void; onLoad?: (event: SyntheticEvent) => void; onClick?: (event: MouseEvent) => void; withMetadataOverlay?: boolean; isDragDisabled?: boolean; isDropDisabled?: boolean; isUploadDisabled?: boolean; minSize?: number; postUploadAction?: PostUploadAction; imageSx?: ChakraProps['sx']; fitContainer?: boolean; droppableData?: TypesafeDroppableData; draggableData?: TypesafeDraggableData; dropLabel?: ReactNode; isSelected?: boolean; thumbnail?: boolean; noContentFallback?: ReactElement; useThumbailFallback?: boolean; withHoverOverlay?: boolean; children?: JSX.Element; uploadElement?: ReactNode; dataTestId?: string; }; const IAIDndImage = (props: IAIDndImageProps) => { const { imageDTO, onError, onClick, withMetadataOverlay = false, isDropDisabled = false, isDragDisabled = false, isUploadDisabled = false, minSize = 24, postUploadAction, imageSx, fitContainer = false, droppableData, draggableData, dropLabel, isSelected = false, thumbnail = false, noContentFallback = defaultNoContentFallback, uploadElement = defaultUploadElement, useThumbailFallback, withHoverOverlay = false, children, onMouseOver, onMouseOut, dataTestId, } = props; const [isHovered, setIsHovered] = useState(false); const handleMouseOver = useCallback( (e: MouseEvent) => { if (onMouseOver) { onMouseOver(e); } setIsHovered(true); }, [onMouseOver] ); const handleMouseOut = useCallback( (e: MouseEvent) => { if (onMouseOut) { onMouseOut(e); } setIsHovered(false); }, [onMouseOut] ); const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ postUploadAction, isDisabled: isUploadDisabled, }); const uploadButtonStyles = useMemo(() => { const styles: SystemStyleObject = { minH: minSize, w: 'full', h: 'full', alignItems: 'center', justifyContent: 'center', borderRadius: 'base', transitionProperty: 'common', transitionDuration: '0.1s', color: 'base.500', }; if (!isUploadDisabled) { Object.assign(styles, { cursor: 'pointer', bg: 'base.700', _hover: { bg: 'base.650', color: 'base.300', }, }); } return styles; }, [isUploadDisabled, minSize]); return ( {(ref) => ( {imageDTO && ( } onError={onError} draggable={false} w={imageDTO.width} objectFit="contain" maxW="full" maxH="full" borderRadius="base" sx={imageSx} data-testid={dataTestId} /> {withMetadataOverlay && } )} {!imageDTO && !isUploadDisabled && ( <> {uploadElement} )} {!imageDTO && isUploadDisabled && noContentFallback} {imageDTO && !isDragDisabled && ( )} {children} {!isDropDisabled && } )} ); }; export default memo(IAIDndImage);