|
import { Box, Flex, IconButton } from '@invoke-ai/ui-library'; |
|
import { useAppSelector } from 'app/store/storeHooks'; |
|
import { useFocusRegion } from 'common/hooks/focus'; |
|
import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; |
|
import { CompareToolbar } from 'features/gallery/components/ImageViewer/CompareToolbar'; |
|
import CurrentImagePreview from 'features/gallery/components/ImageViewer/CurrentImagePreview'; |
|
import { ImageComparison } from 'features/gallery/components/ImageViewer/ImageComparison'; |
|
import { ImageComparisonDroppable } from 'features/gallery/components/ImageViewer/ImageComparisonDroppable'; |
|
import { ViewerToolbar } from 'features/gallery/components/ImageViewer/ViewerToolbar'; |
|
import { selectHasImageToCompare } from 'features/gallery/store/gallerySelectors'; |
|
import type { ReactNode } from 'react'; |
|
import { memo, useRef } from 'react'; |
|
import { useHotkeys } from 'react-hotkeys-hook'; |
|
import { useTranslation } from 'react-i18next'; |
|
import { PiXBold } from 'react-icons/pi'; |
|
import { useMeasure } from 'react-use'; |
|
|
|
import { useImageViewer } from './useImageViewer'; |
|
|
|
type Props = { |
|
closeButton?: ReactNode; |
|
}; |
|
|
|
const useFocusRegionOptions = { |
|
focusOnMount: true, |
|
}; |
|
|
|
export const ImageViewer = memo(({ closeButton }: Props) => { |
|
useAssertSingleton('ImageViewer'); |
|
const hasImageToCompare = useAppSelector(selectHasImageToCompare); |
|
const [containerRef, containerDims] = useMeasure<HTMLDivElement>(); |
|
const ref = useRef<HTMLDivElement>(null); |
|
useFocusRegion('viewer', ref, useFocusRegionOptions); |
|
|
|
return ( |
|
<Flex |
|
ref={ref} |
|
tabIndex={-1} |
|
layerStyle="first" |
|
borderRadius="base" |
|
position="absolute" |
|
flexDirection="column" |
|
top={0} |
|
right={0} |
|
bottom={0} |
|
left={0} |
|
alignItems="center" |
|
justifyContent="center" |
|
> |
|
{hasImageToCompare && <CompareToolbar />} |
|
{!hasImageToCompare && <ViewerToolbar closeButton={closeButton} />} |
|
<Box ref={containerRef} w="full" h="full" p={2}> |
|
{!hasImageToCompare && <CurrentImagePreview />} |
|
{hasImageToCompare && <ImageComparison containerDims={containerDims} />} |
|
</Box> |
|
<ImageComparisonDroppable /> |
|
</Flex> |
|
); |
|
}); |
|
|
|
ImageViewer.displayName = 'ImageViewer'; |
|
|
|
export const GatedImageViewer = memo(() => { |
|
const imageViewer = useImageViewer(); |
|
|
|
if (!imageViewer.isOpen) { |
|
return null; |
|
} |
|
|
|
return <ImageViewer closeButton={<ImageViewerCloseButton />} />; |
|
}); |
|
|
|
GatedImageViewer.displayName = 'GatedImageViewer'; |
|
|
|
const ImageViewerCloseButton = memo(() => { |
|
const { t } = useTranslation(); |
|
const imageViewer = useImageViewer(); |
|
useAssertSingleton('ImageViewerCloseButton'); |
|
useHotkeys('esc', imageViewer.close); |
|
return ( |
|
<IconButton |
|
tooltip={t('gallery.closeViewer')} |
|
aria-label={t('gallery.closeViewer')} |
|
icon={<PiXBold />} |
|
variant="link" |
|
alignSelf="stretch" |
|
onClick={imageViewer.close} |
|
/> |
|
); |
|
}); |
|
|
|
ImageViewerCloseButton.displayName = 'ImageViewerCloseButton'; |
|
|
|
const GatedImageViewerCloseButton = memo(() => { |
|
const imageViewer = useImageViewer(); |
|
|
|
if (!imageViewer.isOpen) { |
|
return null; |
|
} |
|
|
|
return <ImageViewerCloseButton />; |
|
}); |
|
|
|
GatedImageViewerCloseButton.displayName = 'GatedImageViewerCloseButton'; |
|
|