mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): wip layout
This commit is contained in:
parent
77f2690711
commit
ec6c8e2a38
@ -23,16 +23,11 @@ const InitialImageButtons = () => {
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
size="sm"
|
|
||||||
icon={<FaUndo />}
|
icon={<FaUndo />}
|
||||||
aria-label={t('accessibility.reset')}
|
aria-label={t('accessibility.reset')}
|
||||||
onClick={handleResetInitialImage}
|
onClick={handleResetInitialImage}
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton icon={<FaUpload />} aria-label={t('common.upload')} />
|
||||||
size="sm"
|
|
||||||
icon={<FaUpload />}
|
|
||||||
aria-label={t('common.upload')}
|
|
||||||
/>
|
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -410,7 +410,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ButtonGroup size="sm" isAttached={true}>
|
<ButtonGroup isAttached={true}>
|
||||||
<IAIPopover
|
<IAIPopover
|
||||||
triggerComponent={
|
triggerComponent={
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
@ -497,7 +497,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
)}
|
)}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
<ButtonGroup size="sm" isAttached={true}>
|
<ButtonGroup isAttached={true}>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
icon={<FaQuoteRight />}
|
icon={<FaQuoteRight />}
|
||||||
tooltip={`${t('parameters.usePrompt')} (P)`}
|
tooltip={`${t('parameters.usePrompt')} (P)`}
|
||||||
@ -528,7 +528,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
{(isUpscalingEnabled || isFaceRestoreEnabled) && (
|
{(isUpscalingEnabled || isFaceRestoreEnabled) && (
|
||||||
<ButtonGroup size="sm" isAttached={true}>
|
<ButtonGroup isAttached={true}>
|
||||||
{isFaceRestoreEnabled && (
|
{isFaceRestoreEnabled && (
|
||||||
<IAIPopover
|
<IAIPopover
|
||||||
triggerComponent={
|
triggerComponent={
|
||||||
@ -593,7 +593,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ButtonGroup size="sm" isAttached={true}>
|
<ButtonGroup isAttached={true}>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
icon={<FaCode />}
|
icon={<FaCode />}
|
||||||
tooltip={`${t('parameters.info')} (I)`}
|
tooltip={`${t('parameters.info')} (I)`}
|
||||||
@ -603,7 +603,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
<ButtonGroup size="sm" isAttached={true}>
|
<ButtonGroup isAttached={true}>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
onClick={handleInitiateDelete}
|
onClick={handleInitiateDelete}
|
||||||
icon={<FaTrash />}
|
icon={<FaTrash />}
|
||||||
|
@ -14,6 +14,8 @@ const InitialImageDisplay = () => {
|
|||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
bg: 'base.850',
|
||||||
|
p: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
ChakraProps,
|
|
||||||
Flex,
|
|
||||||
Icon,
|
Icon,
|
||||||
Tab,
|
Tab,
|
||||||
TabList,
|
TabList,
|
||||||
@ -16,38 +13,19 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
import { setActiveTab, togglePanels } from 'features/ui/store/uiSlice';
|
import { setActiveTab, togglePanels } from 'features/ui/store/uiSlice';
|
||||||
import {
|
import { memo, ReactNode, useMemo } from 'react';
|
||||||
memo,
|
|
||||||
ReactNode,
|
|
||||||
useLayoutEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { MdDeviceHub, MdGridOn } from 'react-icons/md';
|
import { MdDeviceHub, MdGridOn } from 'react-icons/md';
|
||||||
import { GoTextSize } from 'react-icons/go';
|
import { GoTextSize } from 'react-icons/go';
|
||||||
import { activeTabIndexSelector } from '../store/uiSelectors';
|
import { activeTabIndexSelector } from '../store/uiSelectors';
|
||||||
import UnifiedCanvasWorkarea from 'features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasWorkarea';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ResourceKey } from 'i18next';
|
import { ResourceKey } from 'i18next';
|
||||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||||
import NodeEditor from 'features/nodes/components/NodeEditor';
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { BsLightningChargeFill } from 'react-icons/bs';
|
|
||||||
import { configSelector } from 'features/system/store/configSelectors';
|
import { configSelector } from 'features/system/store/configSelectors';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import AnimatedImageToImagePanel from 'features/parameters/components/AnimatedImageToImagePanel';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
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 ImageGalleryContent from 'features/gallery/components/ImageGalleryContent';
|
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 TextTab from './tabs/text/TextTab';
|
||||||
import UnifiedCanvasTab from './tabs/UnifiedCanvas/UnifiedCanvasTab';
|
import UnifiedCanvasTab from './tabs/UnifiedCanvas/UnifiedCanvasTab';
|
||||||
import NodesTab from './tabs/Nodes/NodesTab';
|
import NodesTab from './tabs/Nodes/NodesTab';
|
||||||
|
@ -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;
|
|
@ -1,32 +1,93 @@
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { isEqual } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { activeTabNameSelector } from '../store/uiSelectors';
|
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 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 ParametersDrawer = () => {
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const dispatch = useAppDispatch();
|
||||||
const shouldPinParametersPanel = useAppSelector(
|
const { shouldPinParametersPanel, shouldShowParametersPanel, activeTabName } =
|
||||||
(state: RootState) => state.ui.shouldPinParametersPanel
|
useAppSelector(selector);
|
||||||
|
|
||||||
|
const handleClosePanel = () => {
|
||||||
|
dispatch(setShouldShowParametersPanel(false));
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawerContent = useMemo(() => {
|
||||||
|
if (activeTabName === 'text') {
|
||||||
|
return <TextTabParameters />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTabName === 'image') {
|
||||||
|
return <ImageTabParameters />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTabName === 'unifiedCanvas') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, [activeTabName]);
|
||||||
|
|
||||||
|
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>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (shouldPinParametersPanel) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'text') {
|
|
||||||
return <TextTabParametersDrawer />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'image') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'unifiedCanvas') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ParametersDrawer);
|
export default memo(ParametersDrawer);
|
||||||
|
@ -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);
|
|
@ -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);
|
|
@ -20,7 +20,6 @@ import {
|
|||||||
getMinMaxDimensions,
|
getMinMaxDimensions,
|
||||||
getSlideDirection,
|
getSlideDirection,
|
||||||
getStyles,
|
getStyles,
|
||||||
parseAndPadSize,
|
|
||||||
} from './util';
|
} from './util';
|
||||||
|
|
||||||
type ResizableDrawerProps = ResizableProps & {
|
type ResizableDrawerProps = ResizableProps & {
|
||||||
@ -72,7 +71,7 @@ const ResizableDrawer = ({
|
|||||||
() =>
|
() =>
|
||||||
initialWidth ??
|
initialWidth ??
|
||||||
minWidth ??
|
minWidth ??
|
||||||
(['left', 'right'].includes(direction) ? 500 : '100%'),
|
(['left', 'right'].includes(direction) ? 'auto' : '100%'),
|
||||||
[initialWidth, minWidth, direction]
|
[initialWidth, minWidth, direction]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -80,7 +79,7 @@ const ResizableDrawer = ({
|
|||||||
() =>
|
() =>
|
||||||
initialHeight ??
|
initialHeight ??
|
||||||
minHeight ??
|
minHeight ??
|
||||||
(['top', 'bottom'].includes(direction) ? 500 : '100%'),
|
(['top', 'bottom'].includes(direction) ? 'auto' : '100%'),
|
||||||
[initialHeight, minHeight, direction]
|
[initialHeight, minHeight, direction]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -105,18 +104,6 @@ const ResizableDrawer = ({
|
|||||||
() =>
|
() =>
|
||||||
getMinMaxDimensions({
|
getMinMaxDimensions({
|
||||||
direction,
|
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,
|
minWidth,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
minHeight,
|
minHeight,
|
||||||
@ -154,24 +141,8 @@ const ResizableDrawer = ({
|
|||||||
<Slide
|
<Slide
|
||||||
direction={slideDirection}
|
direction={slideDirection}
|
||||||
in={isOpen}
|
in={isOpen}
|
||||||
// unmountOnExit={isPinned}
|
|
||||||
motionProps={{ initial: false }}
|
motionProps={{ initial: false }}
|
||||||
style={{ zIndex: 99, width: 'full' }}
|
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
|
<Box
|
||||||
ref={outsideClickRef}
|
ref={outsideClickRef}
|
||||||
|
@ -164,9 +164,6 @@ const HANDLE_INTERACT_PADDING = '0.75rem';
|
|||||||
|
|
||||||
// Visible padding around handle
|
// Visible padding around handle
|
||||||
const HANDLE_PADDING = '1rem';
|
const HANDLE_PADDING = '1rem';
|
||||||
|
|
||||||
const HANDLE_WIDTH_PINNED = '2px';
|
|
||||||
const HANDLE_WIDTH_UNPINNED = '5px';
|
|
||||||
const HANDLE_WIDTH = '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
|
// 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
|
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;
|
handleStyles: HandleStyles;
|
||||||
} => {
|
} => {
|
||||||
if (!isResizable) {
|
// if (!isResizable) {
|
||||||
return { containerStyles: {}, handleStyles: {} };
|
// return { containerStyles: {}, handleStyles: {} };
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Calculate the positioning offset of the handle hitbox so it is centered over the handle
|
// 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)`;
|
const handleOffset = `calc((2 * ${HANDLE_INTERACT_PADDING} + ${HANDLE_WIDTH}) / -2)`;
|
||||||
@ -190,13 +187,15 @@ export const getStyles = ({
|
|||||||
borderBottomWidth: HANDLE_WIDTH,
|
borderBottomWidth: HANDLE_WIDTH,
|
||||||
paddingBottom: HANDLE_PADDING,
|
paddingBottom: HANDLE_PADDING,
|
||||||
},
|
},
|
||||||
handleStyles: {
|
handleStyles: isResizable
|
||||||
top: {
|
? {
|
||||||
paddingTop: HANDLE_INTERACT_PADDING,
|
top: {
|
||||||
paddingBottom: HANDLE_INTERACT_PADDING,
|
paddingTop: HANDLE_INTERACT_PADDING,
|
||||||
bottom: handleOffset,
|
paddingBottom: HANDLE_INTERACT_PADDING,
|
||||||
},
|
bottom: handleOffset,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,13 +205,15 @@ export const getStyles = ({
|
|||||||
borderInlineEndWidth: HANDLE_WIDTH,
|
borderInlineEndWidth: HANDLE_WIDTH,
|
||||||
paddingInlineEnd: HANDLE_PADDING,
|
paddingInlineEnd: HANDLE_PADDING,
|
||||||
},
|
},
|
||||||
handleStyles: {
|
handleStyles: isResizable
|
||||||
right: {
|
? {
|
||||||
paddingInlineStart: HANDLE_INTERACT_PADDING,
|
right: {
|
||||||
paddingInlineEnd: HANDLE_INTERACT_PADDING,
|
paddingInlineStart: HANDLE_INTERACT_PADDING,
|
||||||
insetInlineEnd: handleOffset,
|
paddingInlineEnd: HANDLE_INTERACT_PADDING,
|
||||||
},
|
insetInlineEnd: handleOffset,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,13 +223,15 @@ export const getStyles = ({
|
|||||||
borderTopWidth: HANDLE_WIDTH,
|
borderTopWidth: HANDLE_WIDTH,
|
||||||
paddingTop: HANDLE_PADDING,
|
paddingTop: HANDLE_PADDING,
|
||||||
},
|
},
|
||||||
handleStyles: {
|
handleStyles: isResizable
|
||||||
bottom: {
|
? {
|
||||||
paddingTop: HANDLE_INTERACT_PADDING,
|
bottom: {
|
||||||
paddingBottom: HANDLE_INTERACT_PADDING,
|
paddingTop: HANDLE_INTERACT_PADDING,
|
||||||
top: handleOffset,
|
paddingBottom: HANDLE_INTERACT_PADDING,
|
||||||
},
|
top: handleOffset,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,13 +241,15 @@ export const getStyles = ({
|
|||||||
borderInlineStartWidth: HANDLE_WIDTH,
|
borderInlineStartWidth: HANDLE_WIDTH,
|
||||||
paddingInlineStart: HANDLE_PADDING,
|
paddingInlineStart: HANDLE_PADDING,
|
||||||
},
|
},
|
||||||
handleStyles: {
|
handleStyles: isResizable
|
||||||
left: {
|
? {
|
||||||
paddingInlineStart: HANDLE_INTERACT_PADDING,
|
left: {
|
||||||
paddingInlineEnd: HANDLE_INTERACT_PADDING,
|
paddingInlineStart: HANDLE_INTERACT_PADDING,
|
||||||
insetInlineStart: handleOffset,
|
paddingInlineEnd: HANDLE_INTERACT_PADDING,
|
||||||
},
|
insetInlineStart: handleOffset,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,16 +281,3 @@ export const getSlideDirection = (
|
|||||||
|
|
||||||
return 'left';
|
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;
|
|
||||||
};
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex, FlexProps } from '@chakra-ui/react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { PanelResizeHandle } from 'react-resizable-panels';
|
import { PanelResizeHandle } from 'react-resizable-panels';
|
||||||
|
|
||||||
type ResizeHandleProps = {
|
type ResizeHandleProps = FlexProps & {
|
||||||
direction?: 'horizontal' | 'vertical';
|
direction?: 'horizontal' | 'vertical';
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResizeHandle = (props: ResizeHandleProps) => {
|
const ResizeHandle = (props: ResizeHandleProps) => {
|
||||||
const { direction = 'horizontal' } = props;
|
const { direction = 'horizontal', ...rest } = props;
|
||||||
|
|
||||||
if (direction === 'horizontal') {
|
if (direction === 'horizontal') {
|
||||||
return (
|
return (
|
||||||
@ -19,12 +19,14 @@ const ResizeHandle = (props: ResizeHandleProps) => {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<Box sx={{ w: 0.5, h: 'calc(100% - 4px)', bg: 'base.850' }} />
|
<Box sx={{ w: 0.5, h: 'calc(100% - 4px)', bg: 'base.850' }} />
|
||||||
</Flex>
|
</Flex>
|
||||||
</PanelResizeHandle>
|
</PanelResizeHandle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelResizeHandle>
|
<PanelResizeHandle>
|
||||||
<Flex
|
<Flex
|
||||||
@ -34,6 +36,7 @@ const ResizeHandle = (props: ResizeHandleProps) => {
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<Box sx={{ w: 'calc(100% - 4px)', h: 0.5, bg: 'base.850' }} />
|
<Box sx={{ w: 'calc(100% - 4px)', h: 0.5, bg: 'base.850' }} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -4,7 +4,7 @@ import { memo } from 'react';
|
|||||||
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import Scrollable from '../../common/Scrollable';
|
import Scrollable from '../../common/Scrollable';
|
||||||
import ParametersSlide from '../../common/ParametersSlide';
|
import ParametersSlide from '../../ParametersDrawer';
|
||||||
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
|
import UnifiedCanvasParameters from './UnifiedCanvasParameters';
|
||||||
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
|
import UnifiedCanvasContentBeta from './UnifiedCanvasBeta/UnifiedCanvasContentBeta';
|
||||||
import UnifiedCanvasContent from './UnifiedCanvasContent';
|
import UnifiedCanvasContent from './UnifiedCanvasContent';
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { Portal, TabPanel } from '@chakra-ui/react';
|
import { Box, Flex, Portal, TabPanel } from '@chakra-ui/react';
|
||||||
import { memo } from 'react';
|
import { memo, useCallback, useRef } from 'react';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import {
|
||||||
|
ImperativePanelHandle,
|
||||||
|
Panel,
|
||||||
|
PanelGroup,
|
||||||
|
} from 'react-resizable-panels';
|
||||||
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
@ -12,6 +16,8 @@ import ImageTabImageParameters from './ImageTabImageParameters';
|
|||||||
import TextTabMain from '../text/TextTabMain';
|
import TextTabMain from '../text/TextTabMain';
|
||||||
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
|
import InitialImagePreview from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImagePreview';
|
||||||
import InitialImageDisplay from 'features/parameters/components/AdvancedParameters/ImageToImage/InitialImageDisplay';
|
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 selector = createSelector(uiSelector, (ui) => {
|
||||||
const {
|
const {
|
||||||
@ -33,6 +39,16 @@ const selector = createSelector(uiSelector, (ui) => {
|
|||||||
|
|
||||||
const TextTab = () => {
|
const TextTab = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||||
|
|
||||||
|
const handleDoubleClickHandle = useCallback(() => {
|
||||||
|
if (!panelGroupRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelGroupRef.current.setLayout([50, 50]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
shouldPinGallery,
|
shouldPinGallery,
|
||||||
shouldShowGallery,
|
shouldShowGallery,
|
||||||
@ -42,36 +58,31 @@ const TextTab = () => {
|
|||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelGroup
|
<Flex sx={{ gap: 4, w: 'full', h: 'full' }}>
|
||||||
autoSaveId="imageTab"
|
|
||||||
direction="horizontal"
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
>
|
|
||||||
{shouldPinParametersPanel && shouldShowParametersPanel && (
|
{shouldPinParametersPanel && shouldShowParametersPanel && (
|
||||||
<>
|
<Box
|
||||||
<Panel
|
sx={{
|
||||||
id="imageTab_parameters"
|
position: 'relative',
|
||||||
order={0}
|
h: 'full',
|
||||||
defaultSize={25}
|
w: PARAMETERS_PANEL_WIDTH,
|
||||||
minSize={25}
|
flexShrink: 0,
|
||||||
style={{ position: 'relative' }}
|
}}
|
||||||
>
|
>
|
||||||
<ImageTabParameters />
|
<ImageTabParameters />
|
||||||
<PinParametersPanelButton
|
<PinParametersPanelButton
|
||||||
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Box>
|
||||||
<ResizeHandle />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<Panel id="imageTab_content" order={1}>
|
<Box sx={{ w: 'full', h: 'full' }}>
|
||||||
<PanelGroup
|
<PanelGroup
|
||||||
autoSaveId="imageTab_contentWrapper"
|
ref={panelGroupRef}
|
||||||
|
autoSaveId="imageTab.content"
|
||||||
direction="horizontal"
|
direction="horizontal"
|
||||||
style={{ height: '100%', width: '100%' }}
|
style={{ height: '100%', width: '100%' }}
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel
|
||||||
id="imageTab_initImage"
|
id="imageTab.content.initImage"
|
||||||
order={0}
|
order={0}
|
||||||
defaultSize={50}
|
defaultSize={50}
|
||||||
minSize={25}
|
minSize={25}
|
||||||
@ -79,9 +90,9 @@ const TextTab = () => {
|
|||||||
>
|
>
|
||||||
<InitialImageDisplay />
|
<InitialImageDisplay />
|
||||||
</Panel>
|
</Panel>
|
||||||
<ResizeHandle />
|
<ResizeHandle onDoubleClick={handleDoubleClickHandle} />
|
||||||
<Panel
|
<Panel
|
||||||
id="imageTab_selectedImage"
|
id="imageTab.content.selectedImage"
|
||||||
order={1}
|
order={1}
|
||||||
defaultSize={50}
|
defaultSize={50}
|
||||||
minSize={25}
|
minSize={25}
|
||||||
@ -92,8 +103,8 @@ const TextTab = () => {
|
|||||||
<TextTabMain />
|
<TextTabMain />
|
||||||
</Panel>
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
</Panel>
|
</Box>
|
||||||
</PanelGroup>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 { memo } from 'react';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
import PinParametersPanelButton from '../../PinParametersPanelButton';
|
||||||
@ -9,6 +9,7 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas
|
|||||||
import TextTabMain from './TextTabMain';
|
import TextTabMain from './TextTabMain';
|
||||||
import ResizeHandle from '../ResizeHandle';
|
import ResizeHandle from '../ResizeHandle';
|
||||||
import TextTabParameters from './TextTabParameters';
|
import TextTabParameters from './TextTabParameters';
|
||||||
|
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
|
||||||
|
|
||||||
const selector = createSelector(uiSelector, (ui) => {
|
const selector = createSelector(uiSelector, (ui) => {
|
||||||
const {
|
const {
|
||||||
@ -39,32 +40,24 @@ const TextTab = () => {
|
|||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelGroup
|
<Flex sx={{ gap: 4, w: 'full', h: 'full' }}>
|
||||||
autoSaveId="textTab"
|
|
||||||
direction="horizontal"
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
>
|
|
||||||
{shouldPinParametersPanel && shouldShowParametersPanel && (
|
{shouldPinParametersPanel && shouldShowParametersPanel && (
|
||||||
<>
|
<Box
|
||||||
<Panel
|
sx={{
|
||||||
id="textTab_parameters"
|
position: 'relative',
|
||||||
order={0}
|
h: 'full',
|
||||||
defaultSize={25}
|
// w: PARAMETERS_PANEL_WIDTH,
|
||||||
minSize={25}
|
flexShrink: 0,
|
||||||
style={{ position: 'relative' }}
|
}}
|
||||||
>
|
>
|
||||||
<TextTabParameters />
|
<TextTabParameters />
|
||||||
<PinParametersPanelButton
|
<PinParametersPanelButton
|
||||||
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
sx={{ position: 'absolute', top: 0, insetInlineEnd: 0 }}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Box>
|
||||||
<ResizeHandle />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<Panel id="textTab_content" order={2} minSize={30}>
|
<TextTabMain />
|
||||||
<TextTabMain />
|
</Flex>
|
||||||
</Panel>
|
|
||||||
</PanelGroup>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ const TextTabMain = () => {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
// bg: 'base.850',
|
bg: 'base.850',
|
||||||
|
p: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
// p: 2,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
|
@ -21,6 +21,7 @@ import ParamNoiseCollapse from 'features/parameters/components/Parameters/Noise/
|
|||||||
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
import ParamSymmetryCollapse from 'features/parameters/components/Parameters/Symmetry/ParamSymmetryCollapse';
|
||||||
import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ParamHiresCollapse';
|
import ParamHiresCollapse from 'features/parameters/components/Parameters/Hires/ParamHiresCollapse';
|
||||||
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
import ParamSeamlessCollapse from 'features/parameters/components/Parameters/Seamless/ParamSeamlessCollapse';
|
||||||
|
import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
uiSelector,
|
uiSelector,
|
||||||
@ -43,7 +44,6 @@ const TextTabParameters = () => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
h: 'full',
|
h: 'full',
|
||||||
w: 'full',
|
w: 'full',
|
||||||
position: 'absolute',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ParamPositiveConditioning />
|
<ParamPositiveConditioning />
|
||||||
|
@ -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);
|
|
@ -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))`;
|
export const APP_METADATA_HEIGHT = `calc(100vh - (${APP_CONTENT_HEIGHT_CUTOFF} + 4.4rem))`;
|
||||||
|
|
||||||
// this is in pixels
|
// this is in pixels
|
||||||
export const PARAMETERS_PANEL_WIDTH = 384;
|
// export const PARAMETERS_PANEL_WIDTH = 384;
|
||||||
|
|
||||||
// do not touch ffs
|
// do not touch ffs
|
||||||
export const APP_TEXT_TO_IMAGE_HEIGHT =
|
export const APP_TEXT_TO_IMAGE_HEIGHT =
|
||||||
@ -19,3 +19,5 @@ export const APP_TEXT_TO_IMAGE_HEIGHT =
|
|||||||
|
|
||||||
// option bar
|
// option bar
|
||||||
export const OPTIONS_BAR_MAX_WIDTH = '22.5rem';
|
export const OPTIONS_BAR_MAX_WIDTH = '22.5rem';
|
||||||
|
|
||||||
|
export const PARAMETERS_PANEL_WIDTH = '30rem';
|
||||||
|
Loading…
Reference in New Issue
Block a user