mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-17 02:31:28 -05:00
feat(ui): modularize imagesize components
Canvas and non-canvas have separate width and height and need their own separate aspect ratios. In order to not duplicate a lot of aspect ratio logic, the components relating to image size have been modularized.
This commit is contained in:
committed by
Kent Keirsey
parent
011757c497
commit
4f43eda09b
@@ -1,64 +0,0 @@
|
||||
import { Flex } from '@chakra-ui/layout';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { AspectRatioPreview } from './AspectRatioPreview';
|
||||
|
||||
const meta: Meta<typeof AspectRatioPreview> = {
|
||||
title: 'Components/AspectRatioPreview',
|
||||
tags: ['autodocs'],
|
||||
component: AspectRatioPreview,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InvControl>;
|
||||
|
||||
const MIN = 64;
|
||||
const MAX = 1024;
|
||||
const STEP = 64;
|
||||
const FINE_STEP = 8;
|
||||
const INITIAL = 512;
|
||||
const MARKS = Array.from(
|
||||
{ length: Math.floor(MAX / STEP) },
|
||||
(_, i) => MIN + i * STEP
|
||||
);
|
||||
|
||||
const Component = () => {
|
||||
const [width, setWidth] = useState(INITIAL);
|
||||
const [height, setHeight] = useState(INITIAL);
|
||||
return (
|
||||
<Flex w="full" flexDir="column">
|
||||
<InvControl label="Width">
|
||||
<InvSlider
|
||||
value={width}
|
||||
min={MIN}
|
||||
max={MAX}
|
||||
step={STEP}
|
||||
fineStep={FINE_STEP}
|
||||
onChange={setWidth}
|
||||
marks={MARKS}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Height">
|
||||
<InvSlider
|
||||
value={height}
|
||||
min={MIN}
|
||||
max={MAX}
|
||||
step={STEP}
|
||||
fineStep={FINE_STEP}
|
||||
onChange={setHeight}
|
||||
marks={MARKS}
|
||||
/>
|
||||
</InvControl>
|
||||
<Flex h={96} w={96} p={4}>
|
||||
<AspectRatioPreview width={width} height={height} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const AspectRatioWithSliderInvControls: Story = {
|
||||
render: Component,
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import { Flex, Icon } from '@chakra-ui/react';
|
||||
import { useSize } from '@chakra-ui/react-use-size';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
|
||||
import {
|
||||
BOX_SIZE_CSS_CALC,
|
||||
ICON_CONTAINER_STYLES,
|
||||
MOTION_ICON_ANIMATE,
|
||||
MOTION_ICON_EXIT,
|
||||
MOTION_ICON_INITIAL,
|
||||
} from './constants';
|
||||
import { useAspectRatioPreviewState } from './hooks';
|
||||
import type { AspectRatioPreviewProps } from './types';
|
||||
|
||||
export const AspectRatioPreview = (props: AspectRatioPreviewProps) => {
|
||||
const { width: _width, height: _height, icon = FaImage } = props;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const containerSize = useSize(containerRef);
|
||||
|
||||
const { width, height, shouldShowIcon } = useAspectRatioPreviewState({
|
||||
width: _width,
|
||||
height: _height,
|
||||
containerSize,
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
ref={containerRef}
|
||||
>
|
||||
<Flex
|
||||
bg="blackAlpha.400"
|
||||
borderRadius="base"
|
||||
width={`${width}px`}
|
||||
height={`${height}px`}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<AnimatePresence>
|
||||
{shouldShowIcon && (
|
||||
<Flex
|
||||
as={motion.div}
|
||||
initial={MOTION_ICON_INITIAL}
|
||||
animate={MOTION_ICON_ANIMATE}
|
||||
exit={MOTION_ICON_EXIT}
|
||||
style={ICON_CONTAINER_STYLES}
|
||||
>
|
||||
<Icon as={icon} color="base.700" boxSize={BOX_SIZE_CSS_CALC} />
|
||||
</Flex>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
// When the aspect ratio is between these two values, we show the icon (experimentally determined)
|
||||
export const ICON_LOW_CUTOFF = 0.23;
|
||||
export const ICON_HIGH_CUTOFF = 1 / ICON_LOW_CUTOFF;
|
||||
export const ICON_SIZE_PX = 48;
|
||||
export const ICON_PADDING_PX = 16;
|
||||
export const BOX_SIZE_CSS_CALC = `min(${ICON_SIZE_PX}px, calc(100% - ${ICON_PADDING_PX}px))`;
|
||||
export const MOTION_ICON_INITIAL = {
|
||||
opacity: 0,
|
||||
};
|
||||
export const MOTION_ICON_ANIMATE = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
export const MOTION_ICON_EXIT = {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
export const ICON_CONTAINER_STYLES = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { ICON_HIGH_CUTOFF, ICON_LOW_CUTOFF } from './constants';
|
||||
|
||||
type Dimensions = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type UseAspectRatioPreviewStateArg = {
|
||||
width: number;
|
||||
height: number;
|
||||
containerSize?: Dimensions;
|
||||
};
|
||||
type UseAspectRatioPreviewState = (
|
||||
arg: UseAspectRatioPreviewStateArg
|
||||
) => Dimensions & { shouldShowIcon: boolean };
|
||||
|
||||
export const useAspectRatioPreviewState: UseAspectRatioPreviewState = ({
|
||||
width: _width,
|
||||
height: _height,
|
||||
containerSize,
|
||||
}) => {
|
||||
const dimensions = useMemo(() => {
|
||||
if (!containerSize) {
|
||||
return { width: 0, height: 0, shouldShowIcon: false };
|
||||
}
|
||||
|
||||
const aspectRatio = _width / _height;
|
||||
let width = _width;
|
||||
let height = _height;
|
||||
|
||||
if (_width > _height) {
|
||||
width = containerSize.width;
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
height = containerSize.height;
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
const shouldShowIcon =
|
||||
aspectRatio < ICON_HIGH_CUTOFF && aspectRatio > ICON_LOW_CUTOFF;
|
||||
|
||||
return { width, height, shouldShowIcon };
|
||||
}, [_height, _width, containerSize]);
|
||||
|
||||
return dimensions;
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { IconType } from 'react-icons';
|
||||
|
||||
export type AspectRatioPreviewProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
icon?: IconType;
|
||||
};
|
||||
Reference in New Issue
Block a user