feat(ui): improve controlnet image style

css is terrible
This commit is contained in:
psychedelicious 2023-06-07 15:42:17 +10:00
parent 0843028e6e
commit 6c2b39d1df
4 changed files with 78 additions and 37 deletions

View File

@ -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>
);

View File

@ -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>
</>
)}

View File

@ -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}

View File

@ -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>