mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): improve controlnet image style
css is terrible
This commit is contained in:
parent
0843028e6e
commit
6c2b39d1df
@ -3,7 +3,6 @@ import {
|
|||||||
DragEndEvent,
|
DragEndEvent,
|
||||||
DragOverlay,
|
DragOverlay,
|
||||||
DragStartEvent,
|
DragStartEvent,
|
||||||
KeyboardSensor,
|
|
||||||
MouseSensor,
|
MouseSensor,
|
||||||
TouchSensor,
|
TouchSensor,
|
||||||
pointerWithin,
|
pointerWithin,
|
||||||
@ -15,6 +14,7 @@ import OverlayDragImage from './OverlayDragImage';
|
|||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import { isImageDTO } from 'services/types/guards';
|
import { isImageDTO } from 'services/types/guards';
|
||||||
import { snapCenterToCursor } from '@dnd-kit/modifiers';
|
import { snapCenterToCursor } from '@dnd-kit/modifiers';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
||||||
type ImageDndContextProps = PropsWithChildren;
|
type ImageDndContextProps = PropsWithChildren;
|
||||||
|
|
||||||
@ -62,7 +62,25 @@ const ImageDndContext = (props: ImageDndContextProps) => {
|
|||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
<DragOverlay dropAnimation={null} modifiers={[snapCenterToCursor]}>
|
<DragOverlay dropAnimation={null} modifiers={[snapCenterToCursor]}>
|
||||||
{draggedImage && <OverlayDragImage image={draggedImage} />}
|
<AnimatePresence>
|
||||||
|
{draggedImage && (
|
||||||
|
<motion.div
|
||||||
|
layout
|
||||||
|
key="overlay-drag-image"
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
scale: 0.7,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: { duration: 0.1 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OverlayDragImage image={draggedImage} />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { Box, Flex, Icon, IconButtonProps, Image } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
ChakraProps,
|
||||||
|
Flex,
|
||||||
|
Icon,
|
||||||
|
IconButtonProps,
|
||||||
|
Image,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
||||||
import { useCombinedRefs } from '@dnd-kit/utilities';
|
import { useCombinedRefs } from '@dnd-kit/utilities';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
@ -31,6 +38,7 @@ type IAIDndImageProps = {
|
|||||||
payloadImage?: ImageDTO | null | undefined;
|
payloadImage?: ImageDTO | null | undefined;
|
||||||
minSize?: number;
|
minSize?: number;
|
||||||
postUploadAction?: PostUploadAction;
|
postUploadAction?: PostUploadAction;
|
||||||
|
imageSx?: ChakraProps['sx'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const IAIDndImage = (props: IAIDndImageProps) => {
|
const IAIDndImage = (props: IAIDndImageProps) => {
|
||||||
@ -49,6 +57,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
payloadImage,
|
payloadImage,
|
||||||
minSize = 24,
|
minSize = 24,
|
||||||
postUploadAction,
|
postUploadAction,
|
||||||
|
imageSx,
|
||||||
} = props;
|
} = props;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const dndId = useRef(uuidv4());
|
const dndId = useRef(uuidv4());
|
||||||
@ -56,7 +65,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
const {
|
const {
|
||||||
isOver,
|
isOver,
|
||||||
setNodeRef: setDroppableRef,
|
setNodeRef: setDroppableRef,
|
||||||
active,
|
active: isDropActive,
|
||||||
} = useDroppable({
|
} = useDroppable({
|
||||||
id: dndId.current,
|
id: dndId.current,
|
||||||
disabled: isDropDisabled,
|
disabled: isDropDisabled,
|
||||||
@ -69,6 +78,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
setNodeRef: setDraggableRef,
|
setNodeRef: setDraggableRef,
|
||||||
|
isDragging,
|
||||||
} = useDraggable({
|
} = useDraggable({
|
||||||
id: dndId.current,
|
id: dndId.current,
|
||||||
data: {
|
data: {
|
||||||
@ -84,8 +94,6 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(postUploadAction);
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
imageUploaded({
|
imageUploaded({
|
||||||
formData: { file },
|
formData: { file },
|
||||||
@ -141,7 +149,6 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
position: 'relative',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
@ -157,6 +164,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
maxW: 'full',
|
maxW: 'full',
|
||||||
maxH: 'full',
|
maxH: 'full',
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
|
...imageSx,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{withMetadataOverlay && <ImageMetadataOverlay image={image} />}
|
{withMetadataOverlay && <ImageMetadataOverlay image={image} />}
|
||||||
@ -179,7 +187,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{active && <IAIDropOverlay isOver={isOver} />}
|
{isDropActive && <IAIDropOverlay isOver={isOver} />}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
@ -209,7 +217,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{active && <IAIDropOverlay isOver={isOver} />}
|
{isDropActive && <IAIDropOverlay isOver={isOver} />}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -20,6 +20,8 @@ import {
|
|||||||
Tab,
|
Tab,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Box,
|
Box,
|
||||||
|
VStack,
|
||||||
|
ChakraProps,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaCopy, FaPlus, FaTrash, FaWrench } from 'react-icons/fa';
|
import { FaCopy, FaPlus, FaTrash, FaWrench } from 'react-icons/fa';
|
||||||
|
|
||||||
@ -35,6 +37,8 @@ import IAIButton from 'common/components/IAIButton';
|
|||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
|
import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
|
const expandedControlImageSx: ChakraProps['sx'] = { maxH: 96 };
|
||||||
|
|
||||||
type ControlNetProps = {
|
type ControlNetProps = {
|
||||||
controlNet: ControlNetConfig;
|
controlNet: ControlNetConfig;
|
||||||
};
|
};
|
||||||
@ -53,7 +57,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
processorType,
|
processorType,
|
||||||
} = props.controlNet;
|
} = props.controlNet;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [shouldShowAdvanced, onToggleAdvanced] = useToggle(false);
|
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
const handleDelete = useCallback(() => {
|
||||||
dispatch(controlNetRemoved({ controlNetId }));
|
dispatch(controlNetRemoved({ controlNetId }));
|
||||||
@ -116,16 +120,14 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size="sm"
|
size="sm"
|
||||||
aria-label="Expand"
|
aria-label="Expand"
|
||||||
onClick={onToggleAdvanced}
|
onClick={toggleIsExpanded}
|
||||||
variant="link"
|
variant="link"
|
||||||
icon={
|
icon={
|
||||||
<ChevronUpIcon
|
<ChevronUpIcon
|
||||||
sx={{
|
sx={{
|
||||||
boxSize: 4,
|
boxSize: 4,
|
||||||
color: 'base.300',
|
color: 'base.300',
|
||||||
transform: shouldShowAdvanced
|
transform: isExpanded ? 'rotate(0deg)' : 'rotate(180deg)',
|
||||||
? 'rotate(0deg)'
|
|
||||||
: 'rotate(180deg)',
|
|
||||||
transitionProperty: 'common',
|
transitionProperty: 'common',
|
||||||
transitionDuration: 'normal',
|
transitionDuration: 'normal',
|
||||||
}}
|
}}
|
||||||
@ -135,7 +137,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
{isEnabled && (
|
{isEnabled && (
|
||||||
<>
|
<>
|
||||||
<Flex sx={{ gap: 4 }}>
|
<Flex sx={{ gap: 4, w: 'full' }}>
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
flexDir: 'column',
|
flexDir: 'column',
|
||||||
@ -143,7 +145,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
w: 'full',
|
w: 'full',
|
||||||
h: 24,
|
h: 24,
|
||||||
paddingInlineStart: 1,
|
paddingInlineStart: 1,
|
||||||
paddingInlineEnd: shouldShowAdvanced ? 1 : 0,
|
paddingInlineEnd: isExpanded ? 1 : 0,
|
||||||
pb: 2,
|
pb: 2,
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
@ -160,7 +162,7 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
mini
|
mini
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{!shouldShowAdvanced && (
|
{!isExpanded && (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -174,10 +176,13 @@ const ControlNet = (props: ControlNetProps) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
{shouldShowAdvanced && (
|
{isExpanded && (
|
||||||
<>
|
<>
|
||||||
<Box pt={2}>
|
<Box mt={2}>
|
||||||
<ControlNetImagePreview controlNet={props.controlNet} />
|
<ControlNetImagePreview
|
||||||
|
controlNet={props.controlNet}
|
||||||
|
imageSx={expandedControlImageSx}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<ParamControlNetProcessorSelect
|
<ParamControlNetProcessorSelect
|
||||||
controlNetId={controlNetId}
|
controlNetId={controlNetId}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { memo, useCallback, useRef } from 'react';
|
import { memo, useCallback, useState } from 'react';
|
||||||
import { ImageDTO } from 'services/api';
|
import { ImageDTO } from 'services/api';
|
||||||
import {
|
import {
|
||||||
ControlNetConfig,
|
ControlNetConfig,
|
||||||
@ -6,13 +6,14 @@ import {
|
|||||||
controlNetSelector,
|
controlNetSelector,
|
||||||
} from '../store/controlNetSlice';
|
} from '../store/controlNetSlice';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { Box } from '@chakra-ui/react';
|
import { Box, ChakraProps, Flex } from '@chakra-ui/react';
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||||
import { useHoverDirty } from 'react-use';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
import { FaUndo } from 'react-icons/fa';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
controlNetSelector,
|
controlNetSelector,
|
||||||
@ -25,22 +26,24 @@ const selector = createSelector(
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
controlNet: ControlNetConfig;
|
controlNet: ControlNetConfig;
|
||||||
|
imageSx?: ChakraProps['sx'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const ControlNetImagePreview = (props: Props) => {
|
const ControlNetImagePreview = (props: Props) => {
|
||||||
|
const { imageSx } = props;
|
||||||
const { controlNetId, controlImage, processedControlImage, processorType } =
|
const { controlNetId, controlImage, processedControlImage, processorType } =
|
||||||
props.controlNet;
|
props.controlNet;
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { pendingControlImages } = useAppSelector(selector);
|
const { pendingControlImages } = useAppSelector(selector);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
const isMouseOverImage = useHoverDirty(containerRef);
|
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const handleDrop = useCallback(
|
||||||
(droppedImage: ImageDTO) => {
|
(droppedImage: ImageDTO) => {
|
||||||
if (controlImage?.image_name === droppedImage.image_name) {
|
if (controlImage?.image_name === droppedImage.image_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setIsMouseOverImage(false);
|
||||||
dispatch(
|
dispatch(
|
||||||
controlNetImageChanged({ controlNetId, controlImage: droppedImage })
|
controlNetImageChanged({ controlNetId, controlImage: droppedImage })
|
||||||
);
|
);
|
||||||
@ -48,6 +51,14 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
[controlImage, controlNetId, dispatch]
|
[controlImage, controlNetId, dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleMouseEnter = useCallback(() => {
|
||||||
|
setIsMouseOverImage(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseLeave = useCallback(() => {
|
||||||
|
setIsMouseOverImage(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const shouldShowProcessedImageBackdrop =
|
const shouldShowProcessedImageBackdrop =
|
||||||
Number(controlImage?.width) > Number(processedControlImage?.width) ||
|
Number(controlImage?.width) > Number(processedControlImage?.width) ||
|
||||||
Number(controlImage?.height) > Number(processedControlImage?.height);
|
Number(controlImage?.height) > Number(processedControlImage?.height);
|
||||||
@ -61,8 +72,9 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
ref={containerRef}
|
onMouseEnter={handleMouseEnter}
|
||||||
sx={{ position: 'relative', w: 'full', h: 'full', aspectRatio: '1/1' }}
|
onMouseLeave={handleMouseLeave}
|
||||||
|
sx={{ position: 'relative', w: 'full', h: 'full' }}
|
||||||
>
|
>
|
||||||
<IAIDndImage
|
<IAIDndImage
|
||||||
image={controlImage}
|
image={controlImage}
|
||||||
@ -72,10 +84,12 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
)}
|
)}
|
||||||
isUploadDisabled={Boolean(controlImage)}
|
isUploadDisabled={Boolean(controlImage)}
|
||||||
postUploadAction={{ type: 'SET_CONTROLNET_IMAGE', controlNetId }}
|
postUploadAction={{ type: 'SET_CONTROLNET_IMAGE', controlNetId }}
|
||||||
|
imageSx={imageSx}
|
||||||
/>
|
/>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{shouldShowProcessedImage && (
|
{shouldShowProcessedImage && (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
style={{ width: '100%' }}
|
||||||
initial={{
|
initial={{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
}}
|
}}
|
||||||
@ -88,18 +102,13 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
transition: { duration: 0.1 },
|
transition: { duration: 0.1 },
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<>
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
w: 'full',
|
|
||||||
h: 'full',
|
|
||||||
top: 0,
|
|
||||||
insetInlineStart: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{shouldShowProcessedImageBackdrop && (
|
{shouldShowProcessedImageBackdrop && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
insetInlineStart: 0,
|
||||||
w: 'full',
|
w: 'full',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
bg: 'base.900',
|
bg: 'base.900',
|
||||||
@ -121,9 +130,10 @@ const ControlNetImagePreview = (props: Props) => {
|
|||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
payloadImage={controlImage}
|
payloadImage={controlImage}
|
||||||
isUploadDisabled={true}
|
isUploadDisabled={true}
|
||||||
|
imageSx={imageSx}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user