feat(ui): wip layout

This commit is contained in:
psychedelicious 2023-05-09 16:03:56 +10:00
parent 77f2690711
commit ec6c8e2a38
18 changed files with 209 additions and 656 deletions

View File

@ -23,16 +23,11 @@ const InitialImageButtons = () => {
<Spacer />
<ButtonGroup>
<IAIIconButton
size="sm"
icon={<FaUndo />}
aria-label={t('accessibility.reset')}
onClick={handleResetInitialImage}
/>
<IAIIconButton
size="sm"
icon={<FaUpload />}
aria-label={t('common.upload')}
/>
<IAIIconButton icon={<FaUpload />} aria-label={t('common.upload')} />
</ButtonGroup>
</Flex>
);

View File

@ -410,7 +410,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
}}
{...props}
>
<ButtonGroup size="sm" isAttached={true}>
<ButtonGroup isAttached={true}>
<IAIPopover
triggerComponent={
<IAIIconButton
@ -497,7 +497,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
)}
</ButtonGroup>
<ButtonGroup size="sm" isAttached={true}>
<ButtonGroup isAttached={true}>
<IAIIconButton
icon={<FaQuoteRight />}
tooltip={`${t('parameters.usePrompt')} (P)`}
@ -528,7 +528,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
</ButtonGroup>
{(isUpscalingEnabled || isFaceRestoreEnabled) && (
<ButtonGroup size="sm" isAttached={true}>
<ButtonGroup isAttached={true}>
{isFaceRestoreEnabled && (
<IAIPopover
triggerComponent={
@ -593,7 +593,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
</ButtonGroup>
)}
<ButtonGroup size="sm" isAttached={true}>
<ButtonGroup isAttached={true}>
<IAIIconButton
icon={<FaCode />}
tooltip={`${t('parameters.info')} (I)`}
@ -603,7 +603,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
/>
</ButtonGroup>
<ButtonGroup size="sm" isAttached={true}>
<ButtonGroup isAttached={true}>
<IAIIconButton
onClick={handleInitiateDelete}
icon={<FaTrash />}

View File

@ -14,6 +14,8 @@ const InitialImageDisplay = () => {
borderRadius: 'base',
alignItems: 'center',
justifyContent: 'center',
bg: 'base.850',
p: 4,
}}
>
<Flex

View File

@ -1,7 +1,4 @@
import {
Box,
ChakraProps,
Flex,
Icon,
Tab,
TabList,
@ -16,38 +13,19 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
import { InvokeTabName } from 'features/ui/store/tabMap';
import { setActiveTab, togglePanels } from 'features/ui/store/uiSlice';
import {
memo,
ReactNode,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import { memo, ReactNode, useMemo } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { MdDeviceHub, MdGridOn } from 'react-icons/md';
import { GoTextSize } from 'react-icons/go';
import { activeTabIndexSelector } from '../store/uiSelectors';
import UnifiedCanvasWorkarea from 'features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea';
import { useTranslation } from 'react-i18next';
import { ResourceKey } from 'i18next';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import NodeEditor from 'features/nodes/components/NodeEditor';
import { createSelector } from '@reduxjs/toolkit';
import { BsLightningChargeFill } from 'react-icons/bs';
import { configSelector } from 'features/system/store/configSelectors';
import { isEqual } from 'lodash-es';
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
import Scrollable from './common/Scrollable';
import TextTabParameters from './tabs/text/TextTabParameters';
import PinParametersPanelButton from './PinParametersPanelButton';
import ParametersSlide from './common/ParametersSlide';
import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { Panel, PanelGroup } from 'react-resizable-panels';
import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
import TextTabMain from './tabs/text/TextTabMain';
import ParametersPanel from './ParametersPanel';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import TextTab from './tabs/text/TextTab';
import UnifiedCanvasTab from './tabs/UnifiedCanvas/UnifiedCanvasTab';
import NodesTab from './tabs/Nodes/NodesTab';

View File

@ -1,84 +0,0 @@
import { Box, BoxProps, Grid, GridItem } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { initialImageChanged } from 'features/parameters/store/generationSlice';
import {
activeTabNameSelector,
uiSelector,
} from 'features/ui/store/uiSelectors';
import { DragEvent, ReactNode } from 'react';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import useGetImageByUuid from 'features/gallery/hooks/useGetImageByUuid';
import { isEqual } from 'lodash-es';
import { APP_CONTENT_HEIGHT } from 'theme/util/constants';
import ParametersPanel from './ParametersPanel';
const workareaSelector = createSelector(
[uiSelector, activeTabNameSelector],
(ui, activeTabName) => {
const { shouldPinParametersPanel } = ui;
return {
shouldPinParametersPanel,
activeTabName,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
type InvokeWorkareaProps = BoxProps & {
parametersPanelContent: ReactNode;
children: ReactNode;
};
const InvokeWorkarea = (props: InvokeWorkareaProps) => {
const { parametersPanelContent, children, ...rest } = props;
const dispatch = useAppDispatch();
const { activeTabName } = useAppSelector(workareaSelector);
const getImageByUuid = useGetImageByUuid();
const handleDrop = (e: DragEvent<HTMLDivElement>) => {
const uuid = e.dataTransfer.getData('invokeai/imageUuid');
const image = getImageByUuid(uuid);
if (!image) return;
if (activeTabName === 'unifiedCanvas') {
dispatch(setInitialCanvasImage(image));
}
};
return (
<Grid
{...rest}
gridTemplateAreas={{
base: `'workarea-display' 'workarea-panel'`,
xl: `'workarea-panel workarea-display'`,
}}
gridAutoRows={{ base: 'auto', xl: 'auto' }}
gridAutoColumns={{ md: 'max-content auto' }}
pos="relative"
w="full"
h={{ base: 'auto', xl: APP_CONTENT_HEIGHT }}
gap={4}
>
<ParametersPanel>{parametersPanelContent}</ParametersPanel>
<GridItem gridArea="workarea-display">
<Box
pos="relative"
w={{ base: '100vw', xl: 'full' }}
paddingRight={{ base: 8, xl: 0 }}
h={{ base: 600, xl: '100%' }}
onDrop={handleDrop}
>
{children}
</Box>
</GridItem>
</Grid>
);
};
export default InvokeWorkarea;

View File

@ -1,32 +1,93 @@
import { useAppSelector } from 'app/store/storeHooks';
import { memo } from 'react';
import { activeTabNameSelector } from '../store/uiSelectors';
import { isEqual } from 'lodash-es';
import { createSelector } from '@reduxjs/toolkit';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { PropsWithChildren, memo, useMemo } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import OverlayScrollable from './common/OverlayScrollable';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
import {
activeTabNameSelector,
uiSelector,
} from 'features/ui/store/uiSelectors';
import { setShouldShowParametersPanel } from 'features/ui/store/uiSlice';
import ResizableDrawer from './common/ResizableDrawer/ResizableDrawer';
import PinParametersPanelButton from './PinParametersPanelButton';
import TextTabParametersDrawer from './tabs/text/TextTabParametersDrawer';
import { RootState } from 'app/store/store';
import TextTabParameters from './tabs/text/TextTabParameters';
import ImageTabParameters from './tabs/image/ImageTabParameters';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
const selector = createSelector(
[uiSelector, activeTabNameSelector, lightboxSelector],
(ui, activeTabName, lightbox) => {
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = ui;
const { isLightboxOpen } = lightbox;
return {
activeTabName,
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
};
},
defaultSelectorOptions
);
const ParametersDrawer = () => {
const activeTabName = useAppSelector(activeTabNameSelector);
const shouldPinParametersPanel = useAppSelector(
(state: RootState) => state.ui.shouldPinParametersPanel
);
const dispatch = useAppDispatch();
const { shouldPinParametersPanel, shouldShowParametersPanel, activeTabName } =
useAppSelector(selector);
if (shouldPinParametersPanel) {
return null;
}
const handleClosePanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
const drawerContent = useMemo(() => {
if (activeTabName === 'text') {
return <TextTabParametersDrawer />;
return <TextTabParameters />;
}
if (activeTabName === 'image') {
return null;
return <ImageTabParameters />;
}
if (activeTabName === 'unifiedCanvas') {
return null;
}
}, [activeTabName]);
return null;
return (
<ResizableDrawer
direction="left"
isResizable={false}
isOpen={shouldShowParametersPanel && !shouldPinParametersPanel}
onClose={handleClosePanel}
>
<Flex flexDir="column" position="relative" h="full" w="full">
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<OverlayScrollable>
<Box sx={{ h: 'full', w: PARAMETERS_PANEL_WIDTH }}>
{drawerContent}
</Box>
</OverlayScrollable>
</Flex>
</ResizableDrawer>
);
};
export default memo(ParametersDrawer);

View File

@ -1,155 +0,0 @@
import { Flex } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { memo, ReactNode } from 'react';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
import ResizableDrawer from 'features/ui/components/common/ResizableDrawer/ResizableDrawer';
import {
setShouldShowParametersPanel,
toggleParametersPanel,
togglePinParametersPanel,
} from 'features/ui/store/uiSlice';
import { useHotkeys } from 'react-hotkeys-hook';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import Scrollable from './common/Scrollable';
import PinParametersPanelButton from './PinParametersPanelButton';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector, uiSelector } from '../store/uiSelectors';
import { isEqual } from 'lodash-es';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import useResolution from 'common/hooks/useResolution';
const parametersPanelSelector = createSelector(
[uiSelector, activeTabNameSelector, lightboxSelector],
(ui, activeTabName, lightbox) => {
const { shouldPinParametersPanel, shouldShowParametersPanel } = ui;
const { isLightboxOpen } = lightbox;
return {
shouldPinParametersPanel,
shouldShowParametersPanel,
isResizable: activeTabName !== 'unifiedCanvas',
isLightboxOpen,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
type ParametersPanelProps = {
children: ReactNode;
};
const ParametersPanel = ({ children }: ParametersPanelProps) => {
const dispatch = useAppDispatch();
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
isResizable,
isLightboxOpen,
} = useAppSelector(parametersPanelSelector);
const closeParametersPanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
const resolution = useResolution();
useHotkeys(
'o',
() => {
dispatch(toggleParametersPanel());
shouldPinParametersPanel && dispatch(requestCanvasRescale());
},
{ enabled: () => !isLightboxOpen },
[shouldPinParametersPanel, isLightboxOpen]
);
useHotkeys(
'esc',
() => {
dispatch(setShouldShowParametersPanel(false));
},
{
enabled: () => !shouldPinParametersPanel,
preventDefault: true,
},
[shouldPinParametersPanel]
);
useHotkeys(
'shift+o',
() => {
dispatch(togglePinParametersPanel());
dispatch(requestCanvasRescale());
},
[]
);
const parametersPanelContent = () => {
return (
<Flex
flexDir="column"
position="relative"
h={{ base: 600, xl: 'full' }}
w={{ sm: 'full', lg: '100vw', xl: 'full' }}
paddingRight={{ base: 8, xl: 0 }}
>
{!shouldPinParametersPanel && (
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
{resolution == 'desktop' && <PinParametersPanelButton />}
</Flex>
)}
<Scrollable>{children}</Scrollable>
{shouldPinParametersPanel && resolution == 'desktop' && (
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
)}
</Flex>
);
};
const resizableParametersPanelContent = () => {
return (
<ResizableDrawer
direction="left"
isResizable={isResizable || !shouldPinParametersPanel}
isOpen={shouldShowParametersPanel}
onClose={closeParametersPanel}
isPinned={shouldPinParametersPanel || isLightboxOpen}
sx={{
borderColor: 'base.700',
p: shouldPinParametersPanel ? 0 : 4,
bg: 'base.900',
}}
initialWidth={PARAMETERS_PANEL_WIDTH}
minWidth={PARAMETERS_PANEL_WIDTH}
>
{parametersPanelContent()}
</ResizableDrawer>
);
};
const renderParametersPanel = () => {
if (['mobile', 'tablet'].includes(resolution))
return parametersPanelContent();
return resizableParametersPanelContent();
};
return renderParametersPanel();
};
export default memo(ParametersPanel);

View File

@ -1,143 +0,0 @@
import { Box, Flex, useOutsideClick } from '@chakra-ui/react';
import { Slide } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { uiSelector } from 'features/ui/store/uiSelectors';
import { isEqual } from 'lodash-es';
import { memo, PropsWithChildren, useRef } from 'react';
import PinParametersPanelButton from 'features/ui/components/PinParametersPanelButton';
import {
setShouldShowParametersPanel,
toggleParametersPanel,
togglePinParametersPanel,
} from 'features/ui/store/uiSlice';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import Scrollable from 'features/ui/components/common/Scrollable';
import { useLangDirection } from 'features/ui/hooks/useDirection';
import { useHotkeys } from 'react-hotkeys-hook';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import { generationSelector } from 'features/parameters/store/generationSelectors';
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
const parametersSlideSelector = createSelector(
[uiSelector, generationSelector],
(ui, generation) => {
const { shouldPinParametersPanel, shouldShowParametersPanel } = ui;
const { isImageToImageEnabled } = generation;
return {
shouldPinParametersPanel,
shouldShowParametersPanel,
isImageToImageEnabled,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
type ParametersSlideProps = PropsWithChildren;
const ParametersSlide = (props: ParametersSlideProps) => {
const dispatch = useAppDispatch();
const {
shouldShowParametersPanel,
isImageToImageEnabled,
shouldPinParametersPanel,
} = useAppSelector(parametersSlideSelector);
const langDirection = useLangDirection();
const outsideClickRef = useRef<HTMLDivElement>(null);
const closeParametersPanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
useOutsideClick({
ref: outsideClickRef,
handler: () => {
closeParametersPanel();
},
enabled: shouldShowParametersPanel && !shouldPinParametersPanel,
});
useHotkeys(
'o',
() => {
dispatch(toggleParametersPanel());
shouldPinParametersPanel && dispatch(requestCanvasRescale());
},
[shouldPinParametersPanel]
);
useHotkeys(
'esc',
() => {
dispatch(setShouldShowParametersPanel(false));
},
{
enabled: () => !shouldPinParametersPanel,
preventDefault: true,
},
[shouldPinParametersPanel]
);
useHotkeys(
'shift+o',
() => {
dispatch(togglePinParametersPanel());
dispatch(requestCanvasRescale());
},
[]
);
return (
<Slide
direction={langDirection === 'rtl' ? 'right' : 'left'}
in={shouldShowParametersPanel}
motionProps={{ initial: false }}
style={{ zIndex: 99 }}
>
<Flex
sx={{
boxShadow: '0 0 4rem 0 rgba(0, 0, 0, 0.8)',
pl: 4,
py: 4,
h: 'full',
w: 'min',
bg: 'base.900',
borderInlineEndWidth: 4,
borderInlineEndColor: 'base.800',
}}
>
<Flex ref={outsideClickRef} position="relative" height="full" pr={4}>
<Flex
sx={{
flexDirection: 'column',
width: '28rem',
flexShrink: 0,
}}
>
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<Scrollable>{props.children}</Scrollable>
</Flex>
<AnimatedImageToImagePanel />
</Flex>
</Flex>
</Slide>
);
};
export default memo(ParametersSlide);

View File

@ -20,7 +20,6 @@ import {
getMinMaxDimensions,
getSlideDirection,
getStyles,
parseAndPadSize,
} from './util';
type ResizableDrawerProps = ResizableProps & {
@ -72,7 +71,7 @@ const ResizableDrawer = ({
() =>
initialWidth ??
minWidth ??
(['left', 'right'].includes(direction) ? 500 : '100%'),
(['left', 'right'].includes(direction) ? 'auto' : '100%'),
[initialWidth, minWidth, direction]
);
@ -80,7 +79,7 @@ const ResizableDrawer = ({
() =>
initialHeight ??
minHeight ??
(['top', 'bottom'].includes(direction) ? 500 : '100%'),
(['top', 'bottom'].includes(direction) ? 'auto' : '100%'),
[initialHeight, minHeight, direction]
);
@ -105,18 +104,6 @@ const ResizableDrawer = ({
() =>
getMinMaxDimensions({
direction,
// minWidth: isResizable
// ? parseAndPadSize(minWidth, 18)
// : parseAndPadSize(minWidth),
// maxWidth: isResizable
// ? parseAndPadSize(maxWidth, 18)
// : parseAndPadSize(maxWidth),
// minHeight: isResizable
// ? parseAndPadSize(minHeight, 18)
// : parseAndPadSize(minHeight),
// maxHeight: isResizable
// ? parseAndPadSize(maxHeight, 18)
// : parseAndPadSize(maxHeight),
minWidth,
maxWidth,
minHeight,
@ -154,24 +141,8 @@ const ResizableDrawer = ({
<Slide
direction={slideDirection}
in={isOpen}
// unmountOnExit={isPinned}
motionProps={{ initial: false }}
style={{ zIndex: 99, width: 'full' }}
// {...(isPinned
// ? {
// style: {
// position: undefined,
// left: undefined,
// right: undefined,
// top: undefined,
// bottom: undefined,
// width: undefined,
// },
// }
// : {
// // transition: { enter: { duration: 0.15 }, exit: { duration: 0.15 } },
// style: { zIndex: 99, width: 'full' },
// })}
>
<Box
ref={outsideClickRef}

View File

@ -164,9 +164,6 @@ const HANDLE_INTERACT_PADDING = '0.75rem';
// Visible padding around handle
const HANDLE_PADDING = '1rem';
const HANDLE_WIDTH_PINNED = '2px';
const HANDLE_WIDTH_UNPINNED = '5px';
const HANDLE_WIDTH = '5px';
// Get the styles for the container and handle. Do not need to handle langDirection here bc we use direction-agnostic CSS
@ -177,9 +174,9 @@ export const getStyles = ({
containerStyles: CSSProperties; // technically this could be ChakraProps['sx'], but we cannot use this for HandleStyles so leave it as CSSProperties to be consistent
handleStyles: HandleStyles;
} => {
if (!isResizable) {
return { containerStyles: {}, handleStyles: {} };
}
// if (!isResizable) {
// return { containerStyles: {}, handleStyles: {} };
// }
// Calculate the positioning offset of the handle hitbox so it is centered over the handle
const handleOffset = `calc((2 * ${HANDLE_INTERACT_PADDING} + ${HANDLE_WIDTH}) / -2)`;
@ -190,13 +187,15 @@ export const getStyles = ({
borderBottomWidth: HANDLE_WIDTH,
paddingBottom: HANDLE_PADDING,
},
handleStyles: {
handleStyles: isResizable
? {
top: {
paddingTop: HANDLE_INTERACT_PADDING,
paddingBottom: HANDLE_INTERACT_PADDING,
bottom: handleOffset,
},
},
}
: {},
};
}
@ -206,13 +205,15 @@ export const getStyles = ({
borderInlineEndWidth: HANDLE_WIDTH,
paddingInlineEnd: HANDLE_PADDING,
},
handleStyles: {
handleStyles: isResizable
? {
right: {
paddingInlineStart: HANDLE_INTERACT_PADDING,
paddingInlineEnd: HANDLE_INTERACT_PADDING,
insetInlineEnd: handleOffset,
},
},
}
: {},
};
}
@ -222,13 +223,15 @@ export const getStyles = ({
borderTopWidth: HANDLE_WIDTH,
paddingTop: HANDLE_PADDING,
},
handleStyles: {
handleStyles: isResizable
? {
bottom: {
paddingTop: HANDLE_INTERACT_PADDING,
paddingBottom: HANDLE_INTERACT_PADDING,
top: handleOffset,
},
},
}
: {},
};
}
@ -238,13 +241,15 @@ export const getStyles = ({
borderInlineStartWidth: HANDLE_WIDTH,
paddingInlineStart: HANDLE_PADDING,
},
handleStyles: {
handleStyles: isResizable
? {
left: {
paddingInlineStart: HANDLE_INTERACT_PADDING,
paddingInlineEnd: HANDLE_INTERACT_PADDING,
insetInlineStart: handleOffset,
},
},
}
: {},
};
}
@ -276,16 +281,3 @@ export const getSlideDirection = (
return 'left';
};
// Hack to correct the width of panels while pinned and unpinned, due to different padding in pinned vs unpinned
export const parseAndPadSize = (size?: number, padding?: number) => {
if (!size) {
return undefined;
}
if (!padding) {
return size;
}
return size + padding;
};

View File

@ -1,13 +1,13 @@
import { Box, Flex } from '@chakra-ui/react';
import { Box, Flex, FlexProps } from '@chakra-ui/react';
import { memo } from 'react';
import { PanelResizeHandle } from 'react-resizable-panels';
type ResizeHandleProps = {
type ResizeHandleProps = FlexProps & {
direction?: 'horizontal' | 'vertical';
};
const ResizeHandle = (props: ResizeHandleProps) => {
const { direction = 'horizontal' } = props;
const { direction = 'horizontal', ...rest } = props;
if (direction === 'horizontal') {
return (
@ -19,12 +19,14 @@ const ResizeHandle = (props: ResizeHandleProps) => {
justifyContent: 'center',
alignItems: 'center',
}}
{...rest}
>
<Box sx={{ w: 0.5, h: 'calc(100% - 4px)', bg: 'base.850' }} />
</Flex>
</PanelResizeHandle>
);
}
return (
<PanelResizeHandle>
<Flex
@ -34,6 +36,7 @@ const ResizeHandle = (props: ResizeHandleProps) => {
justifyContent: 'center',
alignItems: 'center',
}}
{...rest}
>
<Box sx={{ w: 'calc(100% - 4px)', h: 0.5, bg: 'base.850' }} />
</Flex>

View File

@ -4,7 +4,7 @@ import { memo } from 'react';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { RootState } from 'app/store/store';
import Scrollable from '../../common/Scrollable';
import ParametersSlide from '../../common/ParametersSlide';
import ParametersSlide from '../../ParametersDrawer';
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
import UnifiedCanvasContent from './UnifiedCanvasContent';

View File

@ -1,6 +1,10 @@
import { Portal, TabPanel } from '@chakra-ui/react';
import { memo } from 'react';
import { Panel, PanelGroup } from 'react-resizable-panels';
import { Box, Flex, Portal, TabPanel } from '@chakra-ui/react';
import { memo, useCallback, useRef } from 'react';
import {
ImperativePanelHandle,
Panel,
PanelGroup,
} from 'react-resizable-panels';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { createSelector } from '@reduxjs/toolkit';
import { uiSelector } from 'features/ui/store/uiSelectors';
@ -12,6 +16,8 @@ import ImageTabImageParameters from './ImageTabImageParameters';
import TextTabMain from '../text/TextTabMain';
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
import InitialImageDisplay from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImageDisplay';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
import { ImperativePanelGroupHandle } from 'react-resizable-panels';
const selector = createSelector(uiSelector, (ui) => {
const {
@ -33,6 +39,16 @@ const selector = createSelector(uiSelector, (ui) => {
const TextTab = () => {
const dispatch = useAppDispatch();
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
const handleDoubleClickHandle = useCallback(() => {
if (!panelGroupRef.current) {
return;
}
panelGroupRef.current.setLayout([50, 50]);
}, []);
const {
shouldPinGallery,
shouldShowGallery,
@ -42,36 +58,31 @@ const TextTab = () => {
} = useAppSelector(selector);
return (
<PanelGroup
autoSaveId="imageTab"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<Flex sx={{ gap: 4, w: 'full', h: 'full' }}>
{shouldPinParametersPanel && shouldShowParametersPanel && (
<>
<Panel
id="imageTab_parameters"
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
<Box
sx={{
position: 'relative',
h: 'full',
w: PARAMETERS_PANEL_WIDTH,
flexShrink: 0,
}}
>
<ImageTabParameters />
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
</Panel>
<ResizeHandle />
</>
</Box>
)}
<Panel id="imageTab_content" order={1}>
<Box sx={{ w: 'full', h: 'full' }}>
<PanelGroup
autoSaveId="imageTab_contentWrapper"
ref={panelGroupRef}
autoSaveId="imageTab.content"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<Panel
id="imageTab_initImage"
id="imageTab.content.initImage"
order={0}
defaultSize={50}
minSize={25}
@ -79,9 +90,9 @@ const TextTab = () => {
>
<InitialImageDisplay />
</Panel>
<ResizeHandle />
<ResizeHandle onDoubleClick={handleDoubleClickHandle} />
<Panel
id="imageTab_selectedImage"
id="imageTab.content.selectedImage"
order={1}
defaultSize={50}
minSize={25}
@ -92,8 +103,8 @@ const TextTab = () => {
<TextTabMain />
</Panel>
</PanelGroup>
</Panel>
</PanelGroup>
</Box>
</Flex>
);
};

View File

@ -1,4 +1,4 @@
import { Portal, TabPanel } from '@chakra-ui/react';
import { Box, Flex, Portal, TabPanel } from '@chakra-ui/react';
import { memo } from 'react';
import { Panel, PanelGroup } from 'react-resizable-panels';
import PinParametersPanelButton from '../../PinParametersPanelButton';
@ -9,6 +9,7 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas
import TextTabMain from './TextTabMain';
import ResizeHandle from '../ResizeHandle';
import TextTabParameters from './TextTabParameters';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
const selector = createSelector(uiSelector, (ui) => {
const {
@ -39,32 +40,24 @@ const TextTab = () => {
} = useAppSelector(selector);
return (
<PanelGroup
autoSaveId="textTab"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
>
<Flex sx={{ gap: 4, w: 'full', h: 'full' }}>
{shouldPinParametersPanel && shouldShowParametersPanel && (
<>
<Panel
id="textTab_parameters"
order={0}
defaultSize={25}
minSize={25}
style={{ position: 'relative' }}
<Box
sx={{
position: 'relative',
h: 'full',
// w: PARAMETERS_PANEL_WIDTH,
flexShrink: 0,
}}
>
<TextTabParameters />
<PinParametersPanelButton
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
/>
</Panel>
<ResizeHandle />
</>
</Box>
)}
<Panel id="textTab_content" order={2} minSize={30}>
<TextTabMain />
</Panel>
</PanelGroup>
</Flex>
);
};

View File

@ -9,12 +9,12 @@ const TextTabMain = () => {
width: '100%',
height: '100%',
borderRadius: 'base',
// bg: 'base.850',
bg: 'base.850',
p: 4,
}}
>
<Flex
sx={{
// p: 2,
width: '100%',
height: '100%',
}}

View File

@ -21,6 +21,7 @@ import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ParamHiresCollapse';
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
const selector = createSelector(
uiSelector,
@ -43,7 +44,6 @@ const TextTabParameters = () => {
flexDirection: 'column',
h: 'full',
w: 'full',
position: 'absolute',
}}
>
<ParamPositiveConditioning />

View File

@ -1,73 +0,0 @@
import { isEqual } from 'lodash-es';
import ResizableDrawer from '../../common/ResizableDrawer/ResizableDrawer';
import TextTabParameters from './TextTabParameters';
import { createSelector } from '@reduxjs/toolkit';
import { activeTabNameSelector, uiSelector } from '../../../store/uiSelectors';
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setShouldShowParametersPanel } from '../../../store/uiSlice';
import { memo } from 'react';
import { Flex } from '@chakra-ui/react';
import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent';
import PinParametersPanelButton from '../../PinParametersPanelButton';
import { Panel, PanelGroup } from 'react-resizable-panels';
const selector = createSelector(
[uiSelector, activeTabNameSelector, lightboxSelector],
(ui, activeTabName, lightbox) => {
const {
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
} = ui;
const { isLightboxOpen } = lightbox;
return {
activeTabName,
shouldPinParametersPanel,
shouldShowParametersPanel,
shouldShowImageParameters,
};
},
{
memoizeOptions: {
resultEqualityCheck: isEqual,
},
}
);
const TextTabParametersDrawer = () => {
const dispatch = useAppDispatch();
const { shouldPinParametersPanel, shouldShowParametersPanel } =
useAppSelector(selector);
const handleClosePanel = () => {
dispatch(setShouldShowParametersPanel(false));
};
return (
<ResizableDrawer
direction="left"
isResizable={true}
isOpen={shouldShowParametersPanel}
onClose={handleClosePanel}
minWidth={500}
>
<Flex flexDir="column" position="relative" h="full" w="full">
<Flex
paddingTop={1.5}
paddingBottom={4}
justifyContent="space-between"
alignItems="center"
>
<InvokeAILogoComponent />
<PinParametersPanelButton />
</Flex>
<TextTabParameters />
</Flex>
</ResizableDrawer>
);
};
export default memo(TextTabParametersDrawer);

View File

@ -11,7 +11,7 @@ export const APP_GALLERY_POPOVER_HEIGHT = `calc(100vh - (${APP_CONTENT_HEIGHT_CU
export const APP_METADATA_HEIGHT = `calc(100vh - (${APP_CONTENT_HEIGHT_CUTOFF} + 4.4rem))`;
// this is in pixels
export const PARAMETERS_PANEL_WIDTH = 384;
// export const PARAMETERS_PANEL_WIDTH = 384;
// do not touch ffs
export const APP_TEXT_TO_IMAGE_HEIGHT =
@ -19,3 +19,5 @@ export const APP_TEXT_TO_IMAGE_HEIGHT =
// option bar
export const OPTIONS_BAR_MAX_WIDTH = '22.5rem';
export const PARAMETERS_PANEL_WIDTH = '30rem';