mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): hover comparison mode
This commit is contained in:
parent
8bb9571485
commit
34d68a3663
@ -385,6 +385,7 @@
|
|||||||
"selectAnImageToCompare": "Select an Image to Compare",
|
"selectAnImageToCompare": "Select an Image to Compare",
|
||||||
"slider": "Slider",
|
"slider": "Slider",
|
||||||
"sideBySide": "Side-by-Side",
|
"sideBySide": "Side-by-Side",
|
||||||
|
"hover": "Hover",
|
||||||
"swapImages": "Swap Images",
|
"swapImages": "Swap Images",
|
||||||
"compareOptions": "Comparison Options",
|
"compareOptions": "Comparison Options",
|
||||||
"stretchToFit": "Stretch to Fit",
|
"stretchToFit": "Stretch to Fit",
|
||||||
|
@ -2,9 +2,9 @@ import { Button, ButtonGroup, Flex, IconButton } from '@invoke-ai/ui-library';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
comparedImagesSwapped,
|
comparedImagesSwapped,
|
||||||
|
comparisonFitChanged,
|
||||||
comparisonModeChanged,
|
comparisonModeChanged,
|
||||||
imageToCompareChanged,
|
imageToCompareChanged,
|
||||||
sliderFitChanged,
|
|
||||||
} from 'features/gallery/store/gallerySlice';
|
} from 'features/gallery/store/gallerySlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -14,19 +14,22 @@ export const CompareToolbar = memo(() => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const comparisonMode = useAppSelector((s) => s.gallery.comparisonMode);
|
const comparisonMode = useAppSelector((s) => s.gallery.comparisonMode);
|
||||||
const sliderFit = useAppSelector((s) => s.gallery.sliderFit);
|
const comparisonFit = useAppSelector((s) => s.gallery.comparisonFit);
|
||||||
const setComparisonModeSlider = useCallback(() => {
|
const setComparisonModeSlider = useCallback(() => {
|
||||||
dispatch(comparisonModeChanged('slider'));
|
dispatch(comparisonModeChanged('slider'));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const setComparisonModeSideBySide = useCallback(() => {
|
const setComparisonModeSideBySide = useCallback(() => {
|
||||||
dispatch(comparisonModeChanged('side-by-side'));
|
dispatch(comparisonModeChanged('side-by-side'));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
const setComparisonModeHover = useCallback(() => {
|
||||||
|
dispatch(comparisonModeChanged('hover'));
|
||||||
|
}, [dispatch]);
|
||||||
const swapImages = useCallback(() => {
|
const swapImages = useCallback(() => {
|
||||||
dispatch(comparedImagesSwapped());
|
dispatch(comparedImagesSwapped());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const toggleSliderFit = useCallback(() => {
|
const togglecomparisonFit = useCallback(() => {
|
||||||
dispatch(sliderFitChanged(sliderFit === 'contain' ? 'fill' : 'contain'));
|
dispatch(comparisonFitChanged(comparisonFit === 'contain' ? 'fill' : 'contain'));
|
||||||
}, [dispatch, sliderFit]);
|
}, [dispatch, comparisonFit]);
|
||||||
const exitCompare = useCallback(() => {
|
const exitCompare = useCallback(() => {
|
||||||
dispatch(imageToCompareChanged(null));
|
dispatch(imageToCompareChanged(null));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
@ -41,13 +44,12 @@ export const CompareToolbar = memo(() => {
|
|||||||
tooltip={t('gallery.swapImages')}
|
tooltip={t('gallery.swapImages')}
|
||||||
onClick={swapImages}
|
onClick={swapImages}
|
||||||
/>
|
/>
|
||||||
{comparisonMode === 'slider' && (
|
{comparisonMode !== 'side-by-side' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={t('gallery.stretchToFit')}
|
aria-label={t('gallery.stretchToFit')}
|
||||||
tooltip={t('gallery.stretchToFit')}
|
tooltip={t('gallery.stretchToFit')}
|
||||||
isDisabled={comparisonMode !== 'slider'}
|
onClick={togglecomparisonFit}
|
||||||
onClick={toggleSliderFit}
|
colorScheme={comparisonFit === 'fill' ? 'invokeBlue' : 'base'}
|
||||||
colorScheme={sliderFit === 'fill' ? 'invokeBlue' : 'base'}
|
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon={<PiArrowsOutBold />}
|
icon={<PiArrowsOutBold />}
|
||||||
/>
|
/>
|
||||||
@ -70,6 +72,13 @@ export const CompareToolbar = memo(() => {
|
|||||||
>
|
>
|
||||||
{t('gallery.sideBySide')}
|
{t('gallery.sideBySide')}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
flexShrink={0}
|
||||||
|
onClick={setComparisonModeHover}
|
||||||
|
colorScheme={comparisonMode === 'hover' ? 'invokeBlue' : 'base'}
|
||||||
|
>
|
||||||
|
{t('gallery.hover')}
|
||||||
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex flex={1} justifyContent="center">
|
<Flex flex={1} justifyContent="center">
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
|
import { ImageComparisonHover } from 'features/gallery/components/ImageViewer/ImageComparisonHover';
|
||||||
import { ImageComparisonSideBySide } from 'features/gallery/components/ImageViewer/ImageComparisonSideBySide';
|
import { ImageComparisonSideBySide } from 'features/gallery/components/ImageViewer/ImageComparisonSideBySide';
|
||||||
import { ImageComparisonSlider } from 'features/gallery/components/ImageViewer/ImageComparisonSlider';
|
import { ImageComparisonSlider } from 'features/gallery/components/ImageViewer/ImageComparisonSlider';
|
||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
@ -31,6 +32,10 @@ export const ImageComparison = memo(() => {
|
|||||||
if (comparisonMode === 'side-by-side') {
|
if (comparisonMode === 'side-by-side') {
|
||||||
return <ImageComparisonSideBySide firstImage={firstImage} secondImage={secondImage} />;
|
return <ImageComparisonSideBySide firstImage={firstImage} secondImage={secondImage} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comparisonMode === 'hover') {
|
||||||
|
return <ImageComparisonHover firstImage={firstImage} secondImage={secondImage} />;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ImageComparison.displayName = 'ImageComparison';
|
ImageComparison.displayName = 'ImageComparison';
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
import { Flex, Image, Text } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { preventDefault } from 'common/util/stopPropagation';
|
||||||
|
import { DROP_SHADOW } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||||
|
import { memo, useCallback, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
/**
|
||||||
|
* The first image to compare
|
||||||
|
*/
|
||||||
|
firstImage: ImageDTO;
|
||||||
|
/**
|
||||||
|
* The second image to compare
|
||||||
|
*/
|
||||||
|
secondImage: ImageDTO;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ImageComparisonHover = memo(({ firstImage, secondImage }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const comparisonFit = useAppSelector((s) => s.gallery.comparisonFit);
|
||||||
|
const [isMouseOver, setIsMouseOver] = useState(false);
|
||||||
|
const onMouseOver = useCallback(() => {
|
||||||
|
setIsMouseOver(true);
|
||||||
|
}, []);
|
||||||
|
const onMouseOut = useCallback(() => {
|
||||||
|
setIsMouseOver(false);
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<Flex w="full" h="full" maxW="full" maxH="full" position="relative" alignItems="center" justifyContent="center">
|
||||||
|
<Flex position="absolute" maxW="full" maxH="full" aspectRatio={firstImage.width / firstImage.height}>
|
||||||
|
<Image
|
||||||
|
id="image-comparison-first-image"
|
||||||
|
w={firstImage.width}
|
||||||
|
h={firstImage.height}
|
||||||
|
maxW="full"
|
||||||
|
maxH="full"
|
||||||
|
src={firstImage.image_url}
|
||||||
|
fallbackSrc={firstImage.thumbnail_url}
|
||||||
|
objectFit="contain"
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
position="absolute"
|
||||||
|
bottom={4}
|
||||||
|
insetInlineStart={4}
|
||||||
|
textOverflow="clip"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
filter={DROP_SHADOW}
|
||||||
|
color="base.50"
|
||||||
|
>
|
||||||
|
{t('gallery.viewerImage')}
|
||||||
|
</Text>
|
||||||
|
<Flex
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
left={0}
|
||||||
|
opacity={isMouseOver ? 1 : 0}
|
||||||
|
transitionDuration="0.2s"
|
||||||
|
transitionProperty="common"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
id="image-comparison-second-image"
|
||||||
|
w={comparisonFit === 'fill' ? 'full' : secondImage.width}
|
||||||
|
h={comparisonFit === 'fill' ? 'full' : secondImage.height}
|
||||||
|
maxW={comparisonFit === 'contain' ? 'full' : undefined}
|
||||||
|
maxH={comparisonFit === 'contain' ? 'full' : undefined}
|
||||||
|
src={secondImage.image_url}
|
||||||
|
fallbackSrc={secondImage.thumbnail_url}
|
||||||
|
objectFit={comparisonFit}
|
||||||
|
objectPosition="top left"
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
position="absolute"
|
||||||
|
bottom={4}
|
||||||
|
insetInlineStart={4}
|
||||||
|
textOverflow="clip"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
filter={DROP_SHADOW}
|
||||||
|
color="base.50"
|
||||||
|
>
|
||||||
|
{t('gallery.compareImage')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
<Flex
|
||||||
|
id="image-comparison-interaction-overlay"
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
left={0}
|
||||||
|
onMouseOver={onMouseOver}
|
||||||
|
onMouseOut={onMouseOut}
|
||||||
|
onContextMenu={preventDefault}
|
||||||
|
userSelect="none"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ImageComparisonHover.displayName = 'ImageComparisonHover';
|
@ -1,8 +1,8 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
import { Flex, Image, Text } from '@invoke-ai/ui-library';
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
import { DROP_SHADOW } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||||
import type { ImageDraggableData } from 'features/dnd/types';
|
|
||||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||||
import { memo, useCallback, useMemo, useRef } from 'react';
|
import { memo, useCallback, useRef } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
@ -19,6 +19,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Props) => {
|
export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||||
const onDoubleClickHandle = useCallback(() => {
|
const onDoubleClickHandle = useCallback(() => {
|
||||||
if (!panelGroupRef.current) {
|
if (!panelGroupRef.current) {
|
||||||
@ -27,36 +28,36 @@ export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Prop
|
|||||||
panelGroupRef.current.setLayout([50, 50]);
|
panelGroupRef.current.setLayout([50, 50]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const firstImageDraggableData = useMemo<ImageDraggableData>(
|
|
||||||
() => ({
|
|
||||||
id: 'image-compare-first-image',
|
|
||||||
payloadType: 'IMAGE_DTO',
|
|
||||||
payload: { imageDTO: firstImage },
|
|
||||||
}),
|
|
||||||
[firstImage]
|
|
||||||
);
|
|
||||||
|
|
||||||
const secondImageDraggableData = useMemo<ImageDraggableData>(
|
|
||||||
() => ({
|
|
||||||
id: 'image-compare-second-image',
|
|
||||||
payloadType: 'IMAGE_DTO',
|
|
||||||
payload: { imageDTO: secondImage },
|
|
||||||
}),
|
|
||||||
[secondImage]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex w="full" h="full" maxW="full" maxH="full" position="relative" alignItems="center" justifyContent="center">
|
<Flex w="full" h="full" maxW="full" maxH="full" position="relative" alignItems="center" justifyContent="center">
|
||||||
<Flex w="full" h="full" maxW="full" maxH="full" position="absolute" alignItems="center" justifyContent="center">
|
<Flex w="full" h="full" maxW="full" maxH="full" position="absolute" alignItems="center" justifyContent="center">
|
||||||
<PanelGroup ref={panelGroupRef} direction="horizontal" id="image-comparison-side-by-side">
|
<PanelGroup ref={panelGroupRef} direction="horizontal" id="image-comparison-side-by-side">
|
||||||
<Panel minSize={20}>
|
<Panel minSize={20}>
|
||||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
<Flex position="relative" w="full" h="full" alignItems="center" justifyContent="center">
|
||||||
<IAIDndImage
|
<Flex position="absolute" maxW="full" maxH="full" aspectRatio={firstImage.width / firstImage.height}>
|
||||||
imageDTO={firstImage}
|
<Image
|
||||||
isDropDisabled={true}
|
id="image-comparison-side-by-side-first-image"
|
||||||
draggableData={firstImageDraggableData}
|
w={firstImage.width}
|
||||||
useThumbailFallback
|
h={firstImage.height}
|
||||||
/>
|
maxW="full"
|
||||||
|
maxH="full"
|
||||||
|
src={firstImage.image_url}
|
||||||
|
fallbackSrc={firstImage.thumbnail_url}
|
||||||
|
objectFit="contain"
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
position="absolute"
|
||||||
|
bottom={4}
|
||||||
|
insetInlineStart={4}
|
||||||
|
textOverflow="clip"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
filter={DROP_SHADOW}
|
||||||
|
color="base.50"
|
||||||
|
userSelect="none"
|
||||||
|
>
|
||||||
|
{t('gallery.viewerImage')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Panel>
|
</Panel>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
@ -66,13 +67,31 @@ export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Prop
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Panel minSize={20}>
|
<Panel minSize={20}>
|
||||||
<Flex w="full" h="full" alignItems="center" justifyContent="center">
|
<Flex position="relative" w="full" h="full" alignItems="center" justifyContent="center">
|
||||||
<IAIDndImage
|
<Flex position="absolute" maxW="full" maxH="full" aspectRatio={secondImage.width / secondImage.height}>
|
||||||
imageDTO={secondImage}
|
<Image
|
||||||
isDropDisabled={true}
|
id="image-comparison-side-by-side-first-image"
|
||||||
draggableData={secondImageDraggableData}
|
w={secondImage.width}
|
||||||
useThumbailFallback
|
h={secondImage.height}
|
||||||
/>
|
maxW="full"
|
||||||
|
maxH="full"
|
||||||
|
src={secondImage.image_url}
|
||||||
|
fallbackSrc={secondImage.thumbnail_url}
|
||||||
|
objectFit="contain"
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
position="absolute"
|
||||||
|
bottom={4}
|
||||||
|
insetInlineStart={4}
|
||||||
|
textOverflow="clip"
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
filter={DROP_SHADOW}
|
||||||
|
color="base.50"
|
||||||
|
userSelect="none"
|
||||||
|
>
|
||||||
|
{t('gallery.compareImage')}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Panel>
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
|
@ -9,7 +9,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
|
import { PiCaretLeftBold, PiCaretRightBold } from 'react-icons/pi';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
import type { ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
const DROP_SHADOW = 'drop-shadow(0px 0px 4px rgb(0, 0, 0))';
|
import { DROP_SHADOW } from './useImageViewer';
|
||||||
|
|
||||||
const INITIAL_POS = '50%';
|
const INITIAL_POS = '50%';
|
||||||
const HANDLE_WIDTH = 2;
|
const HANDLE_WIDTH = 2;
|
||||||
const HANDLE_WIDTH_PX = `${HANDLE_WIDTH}px`;
|
const HANDLE_WIDTH_PX = `${HANDLE_WIDTH}px`;
|
||||||
@ -31,7 +32,7 @@ type Props = {
|
|||||||
|
|
||||||
export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) => {
|
export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const sliderFit = useAppSelector((s) => s.gallery.sliderFit);
|
const comparisonFit = useAppSelector((s) => s.gallery.comparisonFit);
|
||||||
// How far the handle is from the left - this will be a CSS calculation that takes into account the handle width
|
// How far the handle is from the left - this will be a CSS calculation that takes into account the handle width
|
||||||
const [left, setLeft] = useState(HANDLE_LEFT_INITIAL_PX);
|
const [left, setLeft] = useState(HANDLE_LEFT_INITIAL_PX);
|
||||||
// How wide the first image is
|
// How wide the first image is
|
||||||
@ -169,11 +170,11 @@ export const ImageComparisonSlider = memo(({ firstImage, secondImage }: Props) =
|
|||||||
id="image-comparison-second-image"
|
id="image-comparison-second-image"
|
||||||
src={secondImage.image_url}
|
src={secondImage.image_url}
|
||||||
fallbackSrc={secondImage.thumbnail_url}
|
fallbackSrc={secondImage.thumbnail_url}
|
||||||
w={sliderFit === 'fill' ? fittedSize.width : (fittedSize.width * secondImage.width) / firstImage.width}
|
w={comparisonFit === 'fill' ? fittedSize.width : (fittedSize.width * secondImage.width) / firstImage.width}
|
||||||
h={sliderFit === 'fill' ? fittedSize.height : (fittedSize.height * secondImage.height) / firstImage.height}
|
h={comparisonFit === 'fill' ? fittedSize.height : (fittedSize.height * secondImage.height) / firstImage.height}
|
||||||
maxW={fittedSize.width}
|
maxW={fittedSize.width}
|
||||||
maxH={fittedSize.height}
|
maxH={fittedSize.height}
|
||||||
objectFit={sliderFit}
|
objectFit={comparisonFit}
|
||||||
objectPosition="top left"
|
objectPosition="top left"
|
||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
|
@ -29,3 +29,5 @@ export const useImageViewer = () => {
|
|||||||
|
|
||||||
return { isOpen, onOpen, onClose, onToggle };
|
return { isOpen, onOpen, onClose, onToggle };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DROP_SHADOW = 'drop-shadow(0px 0px 4px rgb(0, 0, 0)) drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.3))';
|
||||||
|
@ -24,7 +24,7 @@ const initialGalleryState: GalleryState = {
|
|||||||
isImageViewerOpen: true,
|
isImageViewerOpen: true,
|
||||||
imageToCompare: null,
|
imageToCompare: null,
|
||||||
comparisonMode: 'slider',
|
comparisonMode: 'slider',
|
||||||
sliderFit: 'fill',
|
comparisonFit: 'fill',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gallerySlice = createSlice({
|
export const gallerySlice = createSlice({
|
||||||
@ -98,8 +98,8 @@ export const gallerySlice = createSlice({
|
|||||||
state.imageToCompare = oldSelection[0] ?? null;
|
state.imageToCompare = oldSelection[0] ?? null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sliderFitChanged: (state, action: PayloadAction<'contain' | 'fill'>) => {
|
comparisonFitChanged: (state, action: PayloadAction<'contain' | 'fill'>) => {
|
||||||
state.sliderFit = action.payload;
|
state.comparisonFit = action.payload;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
@ -142,7 +142,7 @@ export const {
|
|||||||
imageToCompareChanged,
|
imageToCompareChanged,
|
||||||
comparisonModeChanged,
|
comparisonModeChanged,
|
||||||
comparedImagesSwapped,
|
comparedImagesSwapped,
|
||||||
sliderFitChanged,
|
comparisonFitChanged,
|
||||||
} = gallerySlice.actions;
|
} = gallerySlice.actions;
|
||||||
|
|
||||||
const isAnyBoardDeleted = isAnyOf(
|
const isAnyBoardDeleted = isAnyOf(
|
||||||
|
@ -7,7 +7,7 @@ export const IMAGE_LIMIT = 20;
|
|||||||
|
|
||||||
export type GalleryView = 'images' | 'assets';
|
export type GalleryView = 'images' | 'assets';
|
||||||
export type BoardId = 'none' | (string & Record<never, never>);
|
export type BoardId = 'none' | (string & Record<never, never>);
|
||||||
export type ComparisonMode = 'slider' | 'side-by-side';
|
export type ComparisonMode = 'slider' | 'side-by-side' | 'hover';
|
||||||
|
|
||||||
export type GalleryState = {
|
export type GalleryState = {
|
||||||
selection: ImageDTO[];
|
selection: ImageDTO[];
|
||||||
@ -23,6 +23,6 @@ export type GalleryState = {
|
|||||||
alwaysShowImageSizeBadge: boolean;
|
alwaysShowImageSizeBadge: boolean;
|
||||||
imageToCompare: ImageDTO | null;
|
imageToCompare: ImageDTO | null;
|
||||||
comparisonMode: ComparisonMode;
|
comparisonMode: ComparisonMode;
|
||||||
sliderFit: 'contain' | 'fill';
|
comparisonFit: 'contain' | 'fill';
|
||||||
isImageViewerOpen: boolean;
|
isImageViewerOpen: boolean;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user