mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Responsive Mobile Layout (#3207)
The first draft for a Responsive Mobile Layout for InvokeAI. Some basic documentation to help contributors. // Notes from: @blessedcoolant --- The whole rework needs to be done using the `mobile first` concept where the base design will be catered to mobile and we add responsive changes as we grow to larger screens. **Added** - Basic breakpoints have been added to the `theme.ts` file that indicate at which values Chakra makes the responsive changes. - A basic `useResolution` hook has been added that either returns `mobile`, `tablet` or `desktop` based on the breakpoint. We can customize this hook further to do more complex checks for us if need be. **Syntax** - Any Chakra component is directly capable of taking different values for the different breakpoints set in our `theme.ts` file. These can be passed in a few ways with the most descriptive being an object. For example: `flexDir={{ base: 'column', xl: 'row' }}` - This would set the `0em and above` to be column for the flex direction but change to row automatically when we hit `xl` and above resolutions which in our case is `80em or 1280px`. This same format is applicable for any element in Chakra. `flexDir={['column', null, null, 'row', null]}` - The above syntax can also be passed as an array to the property with each value in the array corresponding to each breakpoint we have. Setting `null` just bypasses it. This is a good short hand but I think we stick to the above syntax for readability. **Note**: I've modified a few elements here and there to give an idea on how the responsive syntax works for reference. --- **Problems to be solved** @SammCheese - Some issues you might run into are with the Resizable components. We've decided we will get not use resizable components for smaller resolutions. Doesn't make sense. So you'll need to make conditional renderings around these. - Some components that need custom layouts for different screens might be better if ported over to `Grid` and use `gridTemplateAreas` to swap out the design layout. I've demonstrated an example of this in a commit I've made. I'll let you be the judge of where we might need this. - The header will probably need to be converted to a burger menu of some sort with the model changing being handled correctly UX wise. We'll discuss this on discord. --- Anyone willing to contribute to this PR can feel free to join the discussion on discord. https://discord.com/channels/1020123559063990373/1020839344170348605/threads/1097323866780606615
This commit is contained in:
commit
2e70848aa0
@ -6,6 +6,7 @@
|
||||
"prepare": "cd ../../../ && husky install invokeai/frontend/web/.husky",
|
||||
"dev": "concurrently \"vite dev\" \"yarn run theme:watch\"",
|
||||
"dev:nodes": "concurrently \"vite dev --mode nodes\" \"yarn run theme:watch\"",
|
||||
"dev:host": "concurrently \"vite dev --host\" \"yarn run theme:watch\"",
|
||||
"build": "yarn run lint && vite build",
|
||||
"api:web": "openapi -i http://localhost:9090/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --exportSchemas true --indent 2 --request src/services/fixtures/request.ts",
|
||||
"api:file": "openapi -i src/services/fixtures/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --exportSchemas true --indent 2 --request src/services/fixtures/request.ts",
|
||||
|
@ -19,7 +19,8 @@
|
||||
"toggleAutoscroll": "Toggle autoscroll",
|
||||
"toggleLogViewer": "Toggle Log Viewer",
|
||||
"showGallery": "Show Gallery",
|
||||
"showOptionsPanel": "Show Options Panel"
|
||||
"showOptionsPanel": "Show Options Panel",
|
||||
"menu": "Menu"
|
||||
},
|
||||
"common": {
|
||||
"hotkeysLabel": "Hotkeys",
|
||||
|
@ -67,7 +67,12 @@ const App = (props: Props) => {
|
||||
h={APP_HEIGHT}
|
||||
>
|
||||
{props.children || <SiteHeader />}
|
||||
<Flex gap={4} w="full" h="full">
|
||||
<Flex
|
||||
gap={4}
|
||||
w={{ base: '100vw', xl: 'full' }}
|
||||
h="full"
|
||||
flexDir={{ base: 'column', xl: 'row' }}
|
||||
>
|
||||
<InvokeTabs />
|
||||
<ImageGalleryPanel />
|
||||
</Flex>
|
||||
|
@ -31,13 +31,13 @@ export const DIFFUSERS_SAMPLERS: Array<string> = [
|
||||
];
|
||||
|
||||
// Valid image widths
|
||||
export const WIDTHS: Array<number> = Array.from(Array(65)).map(
|
||||
(_x, i) => i * 64
|
||||
export const WIDTHS: Array<number> = Array.from(Array(64)).map(
|
||||
(_x, i) => (i + 1) * 64
|
||||
);
|
||||
|
||||
// Valid image heights
|
||||
export const HEIGHTS: Array<number> = Array.from(Array(65)).map(
|
||||
(_x, i) => i * 64
|
||||
export const HEIGHTS: Array<number> = Array.from(Array(64)).map(
|
||||
(_x, i) => (i + 1) * 64
|
||||
);
|
||||
|
||||
// Valid upscaling levels
|
||||
|
18
invokeai/frontend/web/src/common/hooks/useResolution.ts
Normal file
18
invokeai/frontend/web/src/common/hooks/useResolution.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { useBreakpoint } from '@chakra-ui/react';
|
||||
|
||||
export default function useResolution():
|
||||
| 'mobile'
|
||||
| 'tablet'
|
||||
| 'desktop'
|
||||
| 'unknown' {
|
||||
const breakpointValue = useBreakpoint();
|
||||
|
||||
const mobileResolutions = ['base', 'sm'];
|
||||
const tabletResolutions = ['md', 'lg'];
|
||||
const desktopResolutions = ['xl', '2xl'];
|
||||
|
||||
if (mobileResolutions.includes(breakpointValue)) return 'mobile';
|
||||
if (tabletResolutions.includes(breakpointValue)) return 'tablet';
|
||||
if (desktopResolutions.includes(breakpointValue)) return 'desktop';
|
||||
return 'unknown';
|
||||
}
|
@ -425,9 +425,10 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
columnGap: '0.5em',
|
||||
gap: 2,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
|
@ -13,7 +13,7 @@ const CurrentImageHidden = () => {
|
||||
color: 'base.400',
|
||||
}}
|
||||
>
|
||||
<FaEyeSlash size={'30vh'} />
|
||||
<FaEyeSlash fontSize="25vh" />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
@ -26,6 +26,8 @@ import {
|
||||
import { isStagingSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||
import useResolution from 'common/hooks/useResolution';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
|
||||
const GALLERY_TAB_WIDTHS: Record<
|
||||
InvokeTabName,
|
||||
@ -97,6 +99,8 @@ export default function ImageGalleryPanel() {
|
||||
shouldPinGallery && dispatch(requestCanvasRescale());
|
||||
};
|
||||
|
||||
const resolution = useResolution();
|
||||
|
||||
useHotkeys(
|
||||
'g',
|
||||
() => {
|
||||
@ -179,6 +183,25 @@ export default function ImageGalleryPanel() {
|
||||
[galleryImageMinimumWidth]
|
||||
);
|
||||
|
||||
const calcGalleryMinHeight = () => {
|
||||
if (resolution === 'desktop') return;
|
||||
return 300;
|
||||
};
|
||||
|
||||
const imageGalleryContent = () => {
|
||||
return (
|
||||
<Flex
|
||||
w="100vw"
|
||||
h={{ base: 300, xl: '100vh' }}
|
||||
paddingRight={{ base: 8, xl: 0 }}
|
||||
paddingBottom={{ base: 4, xl: 0 }}
|
||||
>
|
||||
<ImageGalleryContent />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const resizableImageGalleryContent = () => {
|
||||
return (
|
||||
<ResizableDrawer
|
||||
direction="right"
|
||||
@ -196,8 +219,17 @@ export default function ImageGalleryPanel() {
|
||||
? GALLERY_TAB_WIDTHS[activeTabName].galleryMaxWidth
|
||||
: undefined
|
||||
}
|
||||
minHeight={calcGalleryMinHeight()}
|
||||
>
|
||||
<ImageGalleryContent />
|
||||
</ResizableDrawer>
|
||||
);
|
||||
};
|
||||
|
||||
const renderImageGallery = () => {
|
||||
if (['mobile', 'tablet'].includes(resolution)) return imageGalleryContent();
|
||||
return resizableImageGalleryContent();
|
||||
};
|
||||
|
||||
return renderImageGallery();
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const NodeEditor = () => {
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
height: { base: '100vh', xl: 'full' },
|
||||
borderRadius: 'md',
|
||||
bg: 'base.850',
|
||||
}}
|
||||
|
@ -76,7 +76,7 @@ const PromptInput = () => {
|
||||
onKeyDown={handleKeyDown}
|
||||
resize="vertical"
|
||||
ref={promptRef}
|
||||
minH={40}
|
||||
minH={{ base: 20, lg: 40 }}
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
@ -10,7 +10,15 @@ const InvokeAILogoComponent = () => {
|
||||
|
||||
return (
|
||||
<Flex alignItems="center" gap={3} ps={1}>
|
||||
<Image src={InvokeAILogoImage} alt="invoke-ai-logo" w="32px" h="32px" />
|
||||
<Image
|
||||
src={InvokeAILogoImage}
|
||||
alt="invoke-ai-logo"
|
||||
w="32px"
|
||||
h="32px"
|
||||
minW="32px"
|
||||
minH="32px"
|
||||
/>
|
||||
<Flex gap={3} display={{ base: 'inherit', sm: 'none', md: 'inherit' }}>
|
||||
<Text fontSize="xl">
|
||||
invoke <strong>ai</strong>
|
||||
</Text>
|
||||
@ -24,6 +32,7 @@ const InvokeAILogoComponent = () => {
|
||||
{appVersion}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,126 +1,68 @@
|
||||
import { Flex, Grid, Link } from '@chakra-ui/react';
|
||||
|
||||
import { FaBug, FaCube, FaDiscord, FaGithub, FaKeyboard } from 'react-icons/fa';
|
||||
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
|
||||
import HotkeysModal from './HotkeysModal/HotkeysModal';
|
||||
|
||||
import ModelManagerModal from './ModelManager/ModelManagerModal';
|
||||
import { Flex, Grid } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
import ModelSelect from './ModelSelect';
|
||||
import SettingsModal from './SettingsModal/SettingsModal';
|
||||
import StatusIndicator from './StatusIndicator';
|
||||
import ThemeChanger from './ThemeChanger';
|
||||
|
||||
import LanguagePicker from './LanguagePicker';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MdSettings } from 'react-icons/md';
|
||||
import InvokeAILogoComponent from './InvokeAILogoComponent';
|
||||
import SiteHeaderMenu from './SiteHeaderMenu';
|
||||
import useResolution from 'common/hooks/useResolution';
|
||||
import { FaBars } from 'react-icons/fa';
|
||||
import { IAIIconButton } from 'exports';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* Header, includes color mode toggle, settings button, status message.
|
||||
*/
|
||||
const SiteHeader = () => {
|
||||
const [menuOpened, setMenuOpened] = useState(false);
|
||||
const resolution = useResolution();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Grid gridTemplateColumns="auto max-content">
|
||||
<Grid
|
||||
gridTemplateColumns={{ base: 'auto', sm: 'auto max-content' }}
|
||||
paddingRight={{ base: 10, xl: 0 }}
|
||||
gap={2}
|
||||
>
|
||||
<Flex justifyContent={{ base: 'center', sm: 'start' }}>
|
||||
<InvokeAILogoComponent />
|
||||
|
||||
<Flex alignItems="center" gap={2}>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
gap={2}
|
||||
justifyContent={{ base: 'center', sm: 'start' }}
|
||||
>
|
||||
<StatusIndicator />
|
||||
|
||||
<ModelSelect />
|
||||
|
||||
<ModelManagerModal>
|
||||
{resolution === 'desktop' ? (
|
||||
<SiteHeaderMenu />
|
||||
) : (
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.modelManager')}
|
||||
tooltip={t('modelManager.modelManager')}
|
||||
size="sm"
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
icon={<FaCube />}
|
||||
/>
|
||||
</ModelManagerModal>
|
||||
|
||||
<HotkeysModal>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.hotkeysLabel')}
|
||||
tooltip={t('common.hotkeysLabel')}
|
||||
size="sm"
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
icon={<FaKeyboard />}
|
||||
/>
|
||||
</HotkeysModal>
|
||||
|
||||
<ThemeChanger />
|
||||
|
||||
<LanguagePicker />
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="http://github.com/invoke-ai/InvokeAI/issues"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.reportBugLabel')}
|
||||
tooltip={t('common.reportBugLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaBug />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="http://github.com/invoke-ai/InvokeAI"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.githubLabel')}
|
||||
tooltip={t('common.githubLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaGithub />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="https://discord.gg/ZmtBAhwWhy"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.discordLabel')}
|
||||
tooltip={t('common.discordLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaDiscord />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<SettingsModal>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.settingsLabel')}
|
||||
tooltip={t('common.settingsLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={22}
|
||||
size="sm"
|
||||
icon={<MdSettings />}
|
||||
/>
|
||||
</SettingsModal>
|
||||
icon={<FaBars />}
|
||||
aria-label={t('accessibility.menu')}
|
||||
background={menuOpened ? 'base.800' : 'none'}
|
||||
_hover={{ background: menuOpened ? 'base.800' : 'none' }}
|
||||
onClick={() => setMenuOpened(!menuOpened)}
|
||||
p={0}
|
||||
></IAIIconButton>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{resolution !== 'desktop' && menuOpened && (
|
||||
<Flex
|
||||
position="absolute"
|
||||
right={6}
|
||||
top={{ base: 28, sm: 16 }}
|
||||
backgroundColor="base.800"
|
||||
padding={4}
|
||||
borderRadius={4}
|
||||
zIndex={{ base: 99, xl: 0 }}
|
||||
>
|
||||
<SiteHeaderMenu />
|
||||
</Flex>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,113 @@
|
||||
import { Flex, Link } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCube, FaKeyboard, FaBug, FaGithub, FaDiscord } from 'react-icons/fa';
|
||||
import { MdSettings } from 'react-icons/md';
|
||||
import HotkeysModal from './HotkeysModal/HotkeysModal';
|
||||
import LanguagePicker from './LanguagePicker';
|
||||
import ModelManagerModal from './ModelManager/ModelManagerModal';
|
||||
import SettingsModal from './SettingsModal/SettingsModal';
|
||||
import ThemeChanger from './ThemeChanger';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
|
||||
const SiteHeaderMenu = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
flexDirection={{ base: 'column', xl: 'row' }}
|
||||
gap={{ base: 4, xl: 1 }}
|
||||
>
|
||||
<ModelManagerModal>
|
||||
<IAIIconButton
|
||||
aria-label={t('modelManager.modelManager')}
|
||||
tooltip={t('modelManager.modelManager')}
|
||||
size="sm"
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
icon={<FaCube />}
|
||||
/>
|
||||
</ModelManagerModal>
|
||||
|
||||
<HotkeysModal>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.hotkeysLabel')}
|
||||
tooltip={t('common.hotkeysLabel')}
|
||||
size="sm"
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
icon={<FaKeyboard />}
|
||||
/>
|
||||
</HotkeysModal>
|
||||
|
||||
<ThemeChanger />
|
||||
|
||||
<LanguagePicker />
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="http://github.com/invoke-ai/InvokeAI/issues"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.reportBugLabel')}
|
||||
tooltip={t('common.reportBugLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaBug />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="http://github.com/invoke-ai/InvokeAI"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.githubLabel')}
|
||||
tooltip={t('common.githubLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaGithub />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
isExternal
|
||||
href="https://discord.gg/ZmtBAhwWhy"
|
||||
marginBottom="-0.25rem"
|
||||
>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.discordLabel')}
|
||||
tooltip={t('common.discordLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={20}
|
||||
size="sm"
|
||||
icon={<FaDiscord />}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<SettingsModal>
|
||||
<IAIIconButton
|
||||
aria-label={t('common.settingsLabel')}
|
||||
tooltip={t('common.settingsLabel')}
|
||||
variant="link"
|
||||
data-variant="link"
|
||||
fontSize={22}
|
||||
size="sm"
|
||||
icon={<MdSettings />}
|
||||
/>
|
||||
</SettingsModal>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
SiteHeaderMenu.displayName = 'SiteHeaderMenu';
|
||||
export default SiteHeaderMenu;
|
@ -138,9 +138,16 @@ export default function InvokeTabs() {
|
||||
dispatch(setActiveTab(index));
|
||||
}}
|
||||
flexGrow={1}
|
||||
flexDir={{ base: 'column', xl: 'row' }}
|
||||
gap={{ base: 4 }}
|
||||
isLazy
|
||||
>
|
||||
<TabList pt={2} gap={4}>
|
||||
<TabList
|
||||
pt={2}
|
||||
gap={4}
|
||||
flexDir={{ base: 'row', xl: 'column' }}
|
||||
justifyContent={{ base: 'center', xl: 'start' }}
|
||||
>
|
||||
{tabs}
|
||||
</TabList>
|
||||
<TabPanels>{tabPanels}</TabPanels>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, BoxProps, Flex } from '@chakra-ui/react';
|
||||
import { Box, BoxProps, Grid, GridItem } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
||||
@ -52,12 +52,32 @@ const InvokeWorkarea = (props: InvokeWorkareaProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex {...rest} pos="relative" w="full" h={APP_CONTENT_HEIGHT} gap={4}>
|
||||
<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>
|
||||
<Box pos="relative" w="100%" h="100%" onDrop={handleDrop}>
|
||||
<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>
|
||||
</Flex>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { activeTabNameSelector, uiSelector } from '../store/uiSelectors';
|
||||
import { isEqual } from 'lodash';
|
||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||
import useResolution from 'common/hooks/useResolution';
|
||||
|
||||
const parametersPanelSelector = createSelector(
|
||||
[uiSelector, activeTabNameSelector, lightboxSelector],
|
||||
@ -58,6 +59,8 @@ const ParametersPanel = ({ children }: ParametersPanelProps) => {
|
||||
dispatch(setShouldShowParametersPanel(false));
|
||||
};
|
||||
|
||||
const resolution = useResolution();
|
||||
|
||||
useHotkeys(
|
||||
'o',
|
||||
() => {
|
||||
@ -88,21 +91,16 @@ const ParametersPanel = ({ children }: ParametersPanelProps) => {
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const parametersPanelContent = () => {
|
||||
return (
|
||||
<ResizableDrawer
|
||||
direction="left"
|
||||
isResizable={isResizable || !shouldPinParametersPanel}
|
||||
isOpen={shouldShowParametersPanel}
|
||||
onClose={closeParametersPanel}
|
||||
isPinned={shouldPinParametersPanel || isLightboxOpen}
|
||||
sx={{
|
||||
p: shouldPinParametersPanel ? 0 : 4,
|
||||
bg: 'base.900',
|
||||
}}
|
||||
initialWidth={PARAMETERS_PANEL_WIDTH}
|
||||
minWidth={PARAMETERS_PANEL_WIDTH}
|
||||
<Flex
|
||||
flexDir="column"
|
||||
position="relative"
|
||||
h={{ base: 600, xl: 'full' }}
|
||||
w={{ sm: 'full', lg: '100vw', xl: 'full' }}
|
||||
paddingRight={{ base: 8, xl: 0 }}
|
||||
>
|
||||
<Flex flexDir="column" position="relative" h="full" w="full">
|
||||
{!shouldPinParametersPanel && (
|
||||
<Flex
|
||||
paddingTop={1.5}
|
||||
@ -111,18 +109,47 @@ const ParametersPanel = ({ children }: ParametersPanelProps) => {
|
||||
alignItems="center"
|
||||
>
|
||||
<InvokeAILogoComponent />
|
||||
<PinParametersPanelButton />
|
||||
{resolution == 'desktop' && <PinParametersPanelButton />}
|
||||
</Flex>
|
||||
)}
|
||||
<Scrollable>{children}</Scrollable>
|
||||
{shouldPinParametersPanel && (
|
||||
{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);
|
||||
|
@ -33,6 +33,7 @@ const PinParametersPanelButton = (props: PinParametersPanelButtonProps) => {
|
||||
icon={shouldPinParametersPanel ? <BsPinAngleFill /> : <BsPinAngle />}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
px={{ base: 10, xl: 0 }}
|
||||
sx={{
|
||||
color: 'base.700',
|
||||
_hover: {
|
||||
|
@ -29,7 +29,10 @@ export const theme: ThemeOverride = {
|
||||
body: {
|
||||
bg: 'base.900',
|
||||
color: 'base.50',
|
||||
overflow: 'hidden',
|
||||
overflow: {
|
||||
base: 'scroll',
|
||||
xl: 'hidden',
|
||||
},
|
||||
},
|
||||
'*': { ...no_scrollbar },
|
||||
}),
|
||||
@ -38,6 +41,14 @@ export const theme: ThemeOverride = {
|
||||
fonts: {
|
||||
body: `'Inter', sans-serif`,
|
||||
},
|
||||
breakpoints: {
|
||||
base: '0em', // 0px and onwards
|
||||
sm: '30em', // 480px and onwards
|
||||
md: '48em', // 768px and onwards
|
||||
lg: '62em', // 992px and onwards
|
||||
xl: '80em', // 1280px and onwards
|
||||
'2xl': '96em', // 1536px and onwards
|
||||
},
|
||||
shadows: {
|
||||
light: {
|
||||
accent: `0 0 10px 0 var(--invokeai-colors-accent-300)`,
|
||||
|
Loading…
Reference in New Issue
Block a user