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