mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): updated layout for small screens
- Move color picker to floating buttons - Always show floating buttons - Minor layout tweaks for floating buttons
This commit is contained in:
committed by
Kent Keirsey
parent
43b417be6b
commit
c4421241f6
@@ -11,7 +11,7 @@ import { ToolViewButton } from './ToolViewButton';
|
||||
export const ToolChooser: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonGroup isAttached>
|
||||
<ButtonGroup isAttached orientation='vertical'>
|
||||
<ToolBrushButton />
|
||||
<ToolEraserButton />
|
||||
<ToolRectButton />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable i18next/no-literal-string */
|
||||
import { Divider, Flex, Spacer } from '@invoke-ai/ui-library';
|
||||
import { CanvasSettingsPopover } from 'features/controlLayers/components/Settings/CanvasSettingsPopover';
|
||||
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
||||
import { ToolColorPicker } from 'features/controlLayers/components/Tool/ToolFillColorPicker';
|
||||
import { ToolSettings } from 'features/controlLayers/components/Tool/ToolSettings';
|
||||
import { CanvasToolbarFitBboxToLayersButton } from 'features/controlLayers/components/Toolbar/CanvasToolbarFitBboxToLayersButton';
|
||||
@@ -31,7 +30,6 @@ export const CanvasToolbar = memo(() => {
|
||||
|
||||
return (
|
||||
<Flex w="full" gap={2} alignItems="center">
|
||||
<ToolChooser />
|
||||
<ToolColorPicker />
|
||||
<ToolSettings />
|
||||
<Spacer />
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrentQueueItem';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
type Props = {
|
||||
sx?: ChakraProps['sx'];
|
||||
};
|
||||
|
||||
const CancelCurrentQueueItemIconButton = ({ sx }: Props) => {
|
||||
const CancelCurrentQueueItemIconButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { cancelQueueItem, isLoading, isDisabled } = useCancelCurrentQueueItem();
|
||||
|
||||
@@ -18,10 +14,9 @@ const CancelCurrentQueueItemIconButton = ({ sx }: Props) => {
|
||||
isLoading={isLoading}
|
||||
aria-label={t('queue.cancel')}
|
||||
tooltip={t('queue.cancelTooltip')}
|
||||
icon={<PiXBold size="16px" />}
|
||||
icon={<PiXBold />}
|
||||
onClick={cancelQueueItem}
|
||||
colorScheme="error"
|
||||
sx={sx}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||
import ParametersPanelUpscale from './ParametersPanels/ParametersPanelUpscale';
|
||||
import ResizeHandle from './tabs/ResizeHandle';
|
||||
|
||||
const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%' };
|
||||
const panelStyles: CSSProperties = { position: 'relative', height: '100%', width: '100%', minWidth: 0 };
|
||||
|
||||
const onLeftPanelCollapse = (isCollapsed: boolean) => $isLeftPanelOpen.set(!isCollapsed);
|
||||
const onRightPanelCollapse = (isCollapsed: boolean) => $isRightPanelOpen.set(!isCollapsed);
|
||||
@@ -117,42 +117,40 @@ export const AppContent = memo(() => {
|
||||
return (
|
||||
<Flex id="invoke-app-tabs" w="full" h="full" gap={4} p={4}>
|
||||
<VerticalNavBar />
|
||||
<Flex position="relative" w="full" h="full" gap={4} minW={0}>
|
||||
<PanelGroup
|
||||
ref={imperativePanelGroupRef}
|
||||
id="app-panel-group"
|
||||
autoSaveId="app-panel-group"
|
||||
direction="horizontal"
|
||||
style={panelStyles}
|
||||
>
|
||||
{withLeftPanel && (
|
||||
<>
|
||||
<Panel order={0} collapsible style={panelStyles} {...leftPanel.panelProps}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<LeftPanelContent />
|
||||
</Box>
|
||||
</Flex>
|
||||
</Panel>
|
||||
<ResizeHandle id="left-main-handle" {...leftPanel.resizeHandleProps} />
|
||||
</>
|
||||
)}
|
||||
<Panel id="main-panel" order={1} minSize={20} style={panelStyles}>
|
||||
<MainPanelContent />
|
||||
</Panel>
|
||||
{withRightPanel && (
|
||||
<>
|
||||
<ResizeHandle id="main-right-handle" {...rightPanel.resizeHandleProps} />
|
||||
<Panel order={2} style={panelStyles} collapsible {...rightPanel.panelProps}>
|
||||
<RightPanelContent />
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
</PanelGroup>
|
||||
{withLeftPanel && <FloatingParametersPanelButtons panelApi={leftPanel} />}
|
||||
{withRightPanel && <FloatingGalleryButton panelApi={rightPanel} />}
|
||||
</Flex>
|
||||
<PanelGroup
|
||||
ref={imperativePanelGroupRef}
|
||||
id="app-panel-group"
|
||||
autoSaveId="app-panel-group"
|
||||
direction="horizontal"
|
||||
style={panelStyles}
|
||||
>
|
||||
{withLeftPanel && (
|
||||
<>
|
||||
<Panel order={0} collapsible style={panelStyles} {...leftPanel.panelProps}>
|
||||
<Flex flexDir="column" w="full" h="full" gap={2}>
|
||||
<QueueControls />
|
||||
<Box position="relative" w="full" h="full">
|
||||
<LeftPanelContent />
|
||||
</Box>
|
||||
</Flex>
|
||||
</Panel>
|
||||
<ResizeHandle id="left-main-handle" {...leftPanel.resizeHandleProps} />
|
||||
</>
|
||||
)}
|
||||
<Panel id="main-panel" order={1} minSize={20} style={panelStyles}>
|
||||
<MainPanelContent />
|
||||
{withLeftPanel && <FloatingParametersPanelButtons panelApi={leftPanel} />}
|
||||
{withRightPanel && <FloatingGalleryButton panelApi={rightPanel} />}
|
||||
</Panel>
|
||||
{withRightPanel && (
|
||||
<>
|
||||
<ResizeHandle id="main-right-handle" {...rightPanel.resizeHandleProps} />
|
||||
<Panel order={2} style={panelStyles} collapsible {...rightPanel.panelProps}>
|
||||
<RightPanelContent />
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
</PanelGroup>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Flex, IconButton, Portal, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { Flex, IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||
import type { UsePanelReturn } from 'features/ui/hooks/usePanel';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -11,25 +11,17 @@ type Props = {
|
||||
const FloatingGalleryButton = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!props.panelApi.isCollapsed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Flex pos="absolute" transform="translate(0, -50%)" minW={8} top="50%" insetInlineEnd="21px" zIndex={11}>
|
||||
<Tooltip label={t('accessibility.showGalleryPanel')} placement="start">
|
||||
<IconButton
|
||||
aria-label={t('accessibility.showGalleryPanel')}
|
||||
onClick={props.panelApi.expand}
|
||||
icon={<PiImagesSquareBold size="20px" />}
|
||||
p={0}
|
||||
h={48}
|
||||
borderEndRadius={0}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
</Portal>
|
||||
<Flex pos="absolute" transform="translate(0, -50%)" minW={8} top="50%" insetInlineEnd={2} zIndex={11}>
|
||||
<Tooltip label={t('accessibility.showGalleryPanel')} placement="start">
|
||||
<IconButton
|
||||
aria-label={t('accessibility.showGalleryPanel')}
|
||||
onClick={props.panelApi.toggle}
|
||||
icon={<PiImagesSquareBold />}
|
||||
h={48}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { ButtonGroup, Flex, Icon, IconButton, Portal, spinAnimation, useShiftModifier } from '@invoke-ai/ui-library';
|
||||
import { ButtonGroup, Flex, Icon, IconButton, spinAnimation, useShiftModifier } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { ToolChooser } from 'features/controlLayers/components/Tool/ToolChooser';
|
||||
import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer';
|
||||
import CancelCurrentQueueItemIconButton from 'features/queue/components/CancelCurrentQueueItemIconButton';
|
||||
import { useClearQueue } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
|
||||
import { QueueButtonTooltip } from 'features/queue/components/QueueButtonTooltip';
|
||||
import { useInvoke } from 'features/queue/hooks/useInvoke';
|
||||
import type { UsePanelReturn } from 'features/ui/hooks/usePanel';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -16,11 +20,6 @@ import {
|
||||
} from 'react-icons/pi';
|
||||
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
|
||||
|
||||
const floatingButtonStyles: SystemStyleObject = {
|
||||
borderStartRadius: 0,
|
||||
flexGrow: 1,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
panelApi: UsePanelReturn;
|
||||
};
|
||||
@@ -29,6 +28,8 @@ const FloatingSidePanelButtons = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const queue = useInvoke();
|
||||
const shift = useShiftModifier();
|
||||
const tab = useAppSelector(selectActiveTab);
|
||||
const imageViewer = useImageViewer();
|
||||
const clearQueue = useClearQueue();
|
||||
const { data: queueStatus } = useGetQueueStatusQuery();
|
||||
|
||||
@@ -38,62 +39,56 @@ const FloatingSidePanelButtons = (props: Props) => {
|
||||
return <Icon boxSize={6} as={PiCircleNotchBold} animation={spinAnimation} />;
|
||||
}
|
||||
if (shift) {
|
||||
return <PiLightningFill size="16px" />;
|
||||
return <PiLightningFill />;
|
||||
}
|
||||
return <PiSparkleFill size="16px" />;
|
||||
return <PiSparkleFill />;
|
||||
}, [queue.isDisabled, queueStatus?.queue.in_progress, shift]);
|
||||
|
||||
if (!props.panelApi.isCollapsed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Flex
|
||||
pos="absolute"
|
||||
transform="translate(0, -50%)"
|
||||
minW={8}
|
||||
top="50%"
|
||||
insetInlineStart="63px"
|
||||
direction="column"
|
||||
gap={2}
|
||||
h={48}
|
||||
zIndex={11}
|
||||
>
|
||||
<ButtonGroup orientation="vertical" flexGrow={3}>
|
||||
<IconButton
|
||||
tooltip={t('accessibility.showOptionsPanel')}
|
||||
aria-label={t('accessibility.showOptionsPanel')}
|
||||
onClick={props.panelApi.expand}
|
||||
sx={floatingButtonStyles}
|
||||
icon={<PiSlidersHorizontalBold size="16px" />}
|
||||
/>
|
||||
<QueueButtonTooltip prepend={shift}>
|
||||
<IconButton
|
||||
aria-label={t('queue.queueBack')}
|
||||
onClick={shift ? queue.queueFront : queue.queueBack}
|
||||
isLoading={queue.isLoading}
|
||||
isDisabled={queue.isDisabled}
|
||||
icon={queueButtonIcon}
|
||||
colorScheme="invokeYellow"
|
||||
sx={floatingButtonStyles}
|
||||
/>
|
||||
</QueueButtonTooltip>
|
||||
<CancelCurrentQueueItemIconButton sx={floatingButtonStyles} />
|
||||
</ButtonGroup>
|
||||
<Flex
|
||||
pos="absolute"
|
||||
transform="translate(0, -50%)"
|
||||
top="50%"
|
||||
insetInlineStart={2}
|
||||
direction="column"
|
||||
gap={2}
|
||||
zIndex={11}
|
||||
>
|
||||
{tab === 'canvas' && !imageViewer.isOpen && (
|
||||
<CanvasManagerProviderGate>
|
||||
<ToolChooser />
|
||||
</CanvasManagerProviderGate>
|
||||
)}
|
||||
<ButtonGroup orientation="vertical">
|
||||
<IconButton
|
||||
isDisabled={clearQueue.isDisabled}
|
||||
isLoading={clearQueue.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
tooltip={t('queue.clearTooltip')}
|
||||
icon={<PiTrashSimpleBold />}
|
||||
colorScheme="error"
|
||||
onClick={clearQueue.openDialog}
|
||||
data-testid={t('queue.clear')}
|
||||
sx={floatingButtonStyles}
|
||||
tooltip={t('accessibility.showOptionsPanel')}
|
||||
aria-label={t('accessibility.showOptionsPanel')}
|
||||
onClick={props.panelApi.toggle}
|
||||
icon={<PiSlidersHorizontalBold />}
|
||||
/>
|
||||
</Flex>
|
||||
</Portal>
|
||||
<QueueButtonTooltip prepend={shift}>
|
||||
<IconButton
|
||||
aria-label={t('queue.queueBack')}
|
||||
onClick={shift ? queue.queueFront : queue.queueBack}
|
||||
isLoading={queue.isLoading}
|
||||
isDisabled={queue.isDisabled}
|
||||
icon={queueButtonIcon}
|
||||
colorScheme="invokeYellow"
|
||||
/>
|
||||
</QueueButtonTooltip>
|
||||
<CancelCurrentQueueItemIconButton />
|
||||
</ButtonGroup>
|
||||
<IconButton
|
||||
isDisabled={clearQueue.isDisabled}
|
||||
isLoading={clearQueue.isLoading}
|
||||
aria-label={t('queue.clear')}
|
||||
tooltip={t('queue.clearTooltip')}
|
||||
icon={<PiTrashSimpleBold />}
|
||||
colorScheme="error"
|
||||
onClick={clearQueue.openDialog}
|
||||
data-testid={t('queue.clear')}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user