mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): update panel lib, move gallery to percentages
This commit is contained in:
parent
47b1fd4bce
commit
10fd4f6a61
@ -94,7 +94,7 @@
|
|||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-redux": "^9.0.4",
|
"react-redux": "^9.0.4",
|
||||||
"react-resizable-panels": "^0.0.55",
|
"react-resizable-panels": "^1.0.5",
|
||||||
"react-select": "5.7.7",
|
"react-select": "5.7.7",
|
||||||
"react-textarea-autosize": "^8.5.3",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
"react-use": "^17.4.2",
|
"react-use": "^17.4.2",
|
||||||
|
@ -132,8 +132,8 @@ dependencies:
|
|||||||
specifier: ^9.0.4
|
specifier: ^9.0.4
|
||||||
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
|
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
|
||||||
react-resizable-panels:
|
react-resizable-panels:
|
||||||
specifier: ^0.0.55
|
specifier: ^1.0.5
|
||||||
version: 0.0.55(react-dom@18.2.0)(react@18.2.0)
|
version: 1.0.5(react-dom@18.2.0)(react@18.2.0)
|
||||||
react-select:
|
react-select:
|
||||||
specifier: 5.7.7
|
specifier: 5.7.7
|
||||||
version: 5.7.7(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)
|
version: 5.7.7(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -11199,8 +11199,8 @@ packages:
|
|||||||
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
|
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-resizable-panels@0.0.55(react-dom@18.2.0)(react@18.2.0):
|
/react-resizable-panels@1.0.5(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-J/LTFzUEjJiqwSjVh8gjUXkQDA8MRPjARASfn++d2+KOgA+9UcRYUfE3QBJixer2vkk+ffQ4cq3QzWzzHgqYpQ==}
|
resolution: {integrity: sha512-OP0whNQCko+f4BgoptGaeIc7StBRyeMeJ+8r/7rXACBDf9W5EcMWuM32hfqPDMenS2HFy/eZVi/r8XqK+ZIEag==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.14.0 || ^17.0.0 || ^18.0.0
|
react: ^16.14.0 || ^17.0.0 || ^18.0.0
|
||||||
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
|
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
|
||||||
|
@ -5,7 +5,7 @@ import QueueControls from 'features/queue/components/QueueControls';
|
|||||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { memo, useCallback, useRef, useState } from 'react';
|
import { memo, useCallback, useRef } from 'react';
|
||||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
|
|
||||||
@ -15,8 +15,6 @@ import WorkflowPanel from './workflow/WorkflowPanel';
|
|||||||
const panelGroupStyles: CSSProperties = { height: '100%', width: '100%' };
|
const panelGroupStyles: CSSProperties = { height: '100%', width: '100%' };
|
||||||
|
|
||||||
const NodeEditorPanelGroup = () => {
|
const NodeEditorPanelGroup = () => {
|
||||||
const [isTopPanelCollapsed, setIsTopPanelCollapsed] = useState(false);
|
|
||||||
const [isBottomPanelCollapsed, setIsBottomPanelCollapsed] = useState(false);
|
|
||||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||||
const panelStorage = usePanelStorage();
|
const panelStorage = usePanelStorage();
|
||||||
const handleDoubleClickHandle = useCallback(() => {
|
const handleDoubleClickHandle = useCallback(() => {
|
||||||
@ -37,31 +35,14 @@ const NodeEditorPanelGroup = () => {
|
|||||||
style={panelGroupStyles}
|
style={panelGroupStyles}
|
||||||
storage={panelStorage}
|
storage={panelStorage}
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel id="workflow" collapsible minSize={25}>
|
||||||
id="workflow"
|
|
||||||
collapsible
|
|
||||||
onCollapse={setIsTopPanelCollapsed}
|
|
||||||
minSize={25}
|
|
||||||
>
|
|
||||||
<WorkflowPanel />
|
<WorkflowPanel />
|
||||||
</Panel>
|
</Panel>
|
||||||
<ResizeHandle
|
<ResizeHandle
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
onDoubleClick={handleDoubleClickHandle}
|
onDoubleClick={handleDoubleClickHandle}
|
||||||
collapsedDirection={
|
|
||||||
isTopPanelCollapsed
|
|
||||||
? 'top'
|
|
||||||
: isBottomPanelCollapsed
|
|
||||||
? 'bottom'
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<Panel
|
<Panel id="inspector" collapsible minSize={25}>
|
||||||
id="inspector"
|
|
||||||
collapsible
|
|
||||||
onCollapse={setIsBottomPanelCollapsed}
|
|
||||||
minSize={25}
|
|
||||||
>
|
|
||||||
<InspectorPanel />
|
<InspectorPanel />
|
||||||
</Panel>
|
</Panel>
|
||||||
</PanelGroup>
|
</PanelGroup>
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
import { Flex } from '@chakra-ui/layout';
|
import { Flex } from '@chakra-ui/layout';
|
||||||
import { Portal } from '@chakra-ui/portal';
|
import { Portal } from '@chakra-ui/portal';
|
||||||
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
import { InvIconButton } from 'common/components/InvIconButton/InvIconButton';
|
||||||
import type { RefObject } from 'react';
|
import { memo } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { MdPhotoLibrary } from 'react-icons/md';
|
import { MdPhotoLibrary } from 'react-icons/md';
|
||||||
import type { ImperativePanelHandle } from 'react-resizable-panels';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isGalleryCollapsed: boolean;
|
isGalleryCollapsed: boolean;
|
||||||
galleryPanelRef: RefObject<ImperativePanelHandle>;
|
expandGallery: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FloatingGalleryButton = ({
|
const FloatingGalleryButton = ({
|
||||||
isGalleryCollapsed,
|
isGalleryCollapsed,
|
||||||
galleryPanelRef,
|
expandGallery,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleShowGallery = useCallback(() => {
|
|
||||||
galleryPanelRef.current?.expand();
|
|
||||||
}, [galleryPanelRef]);
|
|
||||||
|
|
||||||
if (!isGalleryCollapsed) {
|
if (!isGalleryCollapsed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -38,7 +32,7 @@ const FloatingGalleryButton = ({
|
|||||||
<InvIconButton
|
<InvIconButton
|
||||||
tooltip="Show Gallery (G)"
|
tooltip="Show Gallery (G)"
|
||||||
aria-label={t('accessibility.showGalleryPanel')}
|
aria-label={t('accessibility.showGalleryPanel')}
|
||||||
onClick={handleShowGallery}
|
onClick={expandGallery}
|
||||||
icon={<MdPhotoLibrary />}
|
icon={<MdPhotoLibrary />}
|
||||||
p={0}
|
p={0}
|
||||||
px={3}
|
px={3}
|
||||||
|
@ -95,13 +95,10 @@ const enabledTabsSelector = createMemoizedSelector(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const SIDE_PANEL_MIN_SIZE_PX = 448;
|
|
||||||
const MAIN_PANEL_MIN_SIZE_PX = 448;
|
|
||||||
const GALLERY_PANEL_MIN_SIZE_PX = 360;
|
|
||||||
|
|
||||||
export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
export const NO_GALLERY_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
||||||
export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
export const NO_SIDE_PANEL_TABS: InvokeTabName[] = ['modelManager', 'queue'];
|
||||||
const panelStyles: CSSProperties = { height: '100%', width: '100%' };
|
const panelStyles: CSSProperties = { height: '100%', width: '100%' };
|
||||||
|
const GALLERY_MIN_SIZE_PCT = 20
|
||||||
|
|
||||||
const InvokeTabs = () => {
|
const InvokeTabs = () => {
|
||||||
const activeTabIndex = useAppSelector(activeTabIndexSelector);
|
const activeTabIndex = useAppSelector(activeTabIndexSelector);
|
||||||
@ -155,53 +152,18 @@ const InvokeTabs = () => {
|
|||||||
[dispatch, enabledTabs]
|
[dispatch, enabledTabs]
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
|
||||||
isCollapsed: isSidePanelCollapsed,
|
|
||||||
expand: expandSidePanel,
|
|
||||||
collapse: collapseSidePanel,
|
|
||||||
toggle: toggleSidePanel,
|
|
||||||
} = usePanel(SIDE_PANEL_MIN_SIZE_PX, 'pixels');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ref: galleryPanelRef,
|
ref: galleryPanelRef,
|
||||||
minSize: galleryPanelMinSize,
|
minSize: galleryPanelMinSize,
|
||||||
isCollapsed: isGalleryPanelCollapsed,
|
isCollapsed: isGalleryPanelCollapsed,
|
||||||
setIsCollapsed: setIsGalleryPanelCollapsed,
|
onCollapse: onCollapseGalleryPanel,
|
||||||
|
onExpand: onExpandGalleryPanel,
|
||||||
reset: resetGalleryPanel,
|
reset: resetGalleryPanel,
|
||||||
expand: expandGalleryPanel,
|
expand: expandGalleryPanel,
|
||||||
collapse: collapseGalleryPanel,
|
|
||||||
toggle: toggleGalleryPanel,
|
toggle: toggleGalleryPanel,
|
||||||
} = usePanel(GALLERY_PANEL_MIN_SIZE_PX, 'pixels');
|
} = usePanel(GALLERY_MIN_SIZE_PCT);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys('g', toggleGalleryPanel, []);
|
||||||
'f',
|
|
||||||
() => {
|
|
||||||
if (isGalleryPanelCollapsed || isSidePanelCollapsed) {
|
|
||||||
expandGalleryPanel();
|
|
||||||
expandSidePanel();
|
|
||||||
} else {
|
|
||||||
collapseSidePanel();
|
|
||||||
collapseGalleryPanel();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[dispatch, isGalleryPanelCollapsed, isSidePanelCollapsed]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
['t', 'o'],
|
|
||||||
() => {
|
|
||||||
toggleSidePanel();
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'g',
|
|
||||||
() => {
|
|
||||||
toggleGalleryPanel();
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const panelStorage = usePanelStorage();
|
const panelStorage = usePanelStorage();
|
||||||
|
|
||||||
@ -219,6 +181,15 @@ const InvokeTabs = () => {
|
|||||||
{tabs}
|
{tabs}
|
||||||
<Spacer />
|
<Spacer />
|
||||||
</InvTabList>
|
</InvTabList>
|
||||||
|
<PanelGroup
|
||||||
|
id="app"
|
||||||
|
autoSaveId="app"
|
||||||
|
direction="horizontal"
|
||||||
|
style={panelStyles}
|
||||||
|
storage={panelStorage}
|
||||||
|
>
|
||||||
|
<Panel id="main" order={0} minSize={50}>
|
||||||
|
<Flex w="full" h="full" gap={4}>
|
||||||
{!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
|
{!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
|
||||||
<Flex h="full" w={434} flexShrink={0}>
|
<Flex h="full" w={434} flexShrink={0}>
|
||||||
{activeTabName === 'nodes' ? (
|
{activeTabName === 'nodes' ? (
|
||||||
@ -228,64 +199,29 @@ const InvokeTabs = () => {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
<PanelGroup
|
<InvTabPanels w="full" h="full">
|
||||||
id="app"
|
{tabPanels}
|
||||||
autoSaveId="app"
|
</InvTabPanels>
|
||||||
direction="horizontal"
|
</Flex>
|
||||||
style={panelStyles}
|
|
||||||
storage={panelStorage}
|
|
||||||
units="pixels"
|
|
||||||
>
|
|
||||||
{/* {!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
|
|
||||||
<>
|
|
||||||
<Panel
|
|
||||||
order={0}
|
|
||||||
id="side"
|
|
||||||
ref={sidePanelRef}
|
|
||||||
defaultSize={sidePanelMinSize}
|
|
||||||
minSize={sidePanelMinSize}
|
|
||||||
onCollapse={setIsSidePanelCollapsed}
|
|
||||||
collapsible
|
|
||||||
>
|
|
||||||
{activeTabName === 'nodes' ? (
|
|
||||||
<NodeEditorPanelGroup />
|
|
||||||
) : (
|
|
||||||
<ParametersPanel />
|
|
||||||
)}
|
|
||||||
</Panel>
|
|
||||||
<ResizeHandle
|
|
||||||
onDoubleClick={resetSidePanel}
|
|
||||||
collapsedDirection={isSidePanelCollapsed ? 'left' : undefined}
|
|
||||||
/>
|
|
||||||
<FloatingSidePanelButtons
|
|
||||||
isSidePanelCollapsed={isSidePanelCollapsed}
|
|
||||||
sidePanelRef={sidePanelRef}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)} */}
|
|
||||||
<Panel id="main" order={1} minSize={MAIN_PANEL_MIN_SIZE_PX}>
|
|
||||||
<InvTabPanels style={panelStyles}>{tabPanels}</InvTabPanels>
|
|
||||||
</Panel>
|
</Panel>
|
||||||
{!NO_GALLERY_TABS.includes(activeTabName) && (
|
{!NO_GALLERY_TABS.includes(activeTabName) && (
|
||||||
<>
|
<>
|
||||||
<ResizeHandle
|
<ResizeHandle onDoubleClick={resetGalleryPanel} />
|
||||||
onDoubleClick={resetGalleryPanel}
|
|
||||||
collapsedDirection={isGalleryPanelCollapsed ? 'right' : undefined}
|
|
||||||
/>
|
|
||||||
<Panel
|
<Panel
|
||||||
id="gallery"
|
id="gallery"
|
||||||
ref={galleryPanelRef}
|
ref={galleryPanelRef}
|
||||||
order={2}
|
order={1}
|
||||||
defaultSize={galleryPanelMinSize}
|
defaultSize={galleryPanelMinSize}
|
||||||
minSize={galleryPanelMinSize}
|
minSize={galleryPanelMinSize}
|
||||||
onCollapse={setIsGalleryPanelCollapsed}
|
onCollapse={onCollapseGalleryPanel}
|
||||||
|
onExpand={onExpandGalleryPanel}
|
||||||
collapsible
|
collapsible
|
||||||
>
|
>
|
||||||
<ImageGalleryContent />
|
<ImageGalleryContent />
|
||||||
</Panel>
|
</Panel>
|
||||||
<FloatingGalleryButton
|
<FloatingGalleryButton
|
||||||
isGalleryCollapsed={isGalleryPanelCollapsed}
|
isGalleryCollapsed={isGalleryPanelCollapsed}
|
||||||
galleryPanelRef={galleryPanelRef}
|
expandGallery={expandGalleryPanel}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -36,7 +36,6 @@ const ImageToImageTab = () => {
|
|||||||
direction="horizontal"
|
direction="horizontal"
|
||||||
style={panelGroupStyles}
|
style={panelGroupStyles}
|
||||||
storage={panelStorage}
|
storage={panelStorage}
|
||||||
units="percentages"
|
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel
|
||||||
id="imageTab.content.initImage"
|
id="imageTab.content.initImage"
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { flushSync } from 'react-dom';
|
import { flushSync } from 'react-dom';
|
||||||
import type { ImperativePanelHandle, Units } from 'react-resizable-panels';
|
import type {
|
||||||
|
ImperativePanelHandle,
|
||||||
|
PanelOnCollapse,
|
||||||
|
PanelOnExpand,
|
||||||
|
} from 'react-resizable-panels';
|
||||||
|
|
||||||
export const usePanel = (minSize: number, units: Units) => {
|
export const usePanel = (minSize: number) => {
|
||||||
const ref = useRef<ImperativePanelHandle>(null);
|
const ref = useRef<ImperativePanelHandle>(null);
|
||||||
|
|
||||||
const [isCollapsed, setIsCollapsed] = useState(() =>
|
const [isCollapsed, setIsCollapsed] = useState(() =>
|
||||||
Boolean(ref.current?.getCollapsed())
|
Boolean(ref.current?.isCollapsed())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onCollapse = useCallback<PanelOnCollapse>(() => {
|
||||||
|
setIsCollapsed(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onExpand = useCallback<PanelOnExpand>(() => {
|
||||||
|
setIsCollapsed(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const toggle = useCallback(() => {
|
const toggle = useCallback(() => {
|
||||||
if (ref.current?.getCollapsed()) {
|
if (ref.current?.isCollapsed()) {
|
||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
ref.current?.expand();
|
ref.current?.expand();
|
||||||
});
|
});
|
||||||
@ -35,15 +47,16 @@ export const usePanel = (minSize: number, units: Units) => {
|
|||||||
|
|
||||||
const reset = useCallback(() => {
|
const reset = useCallback(() => {
|
||||||
flushSync(() => {
|
flushSync(() => {
|
||||||
ref.current?.resize(minSize, units);
|
ref.current?.resize(minSize);
|
||||||
});
|
});
|
||||||
}, [minSize, units]);
|
}, [minSize]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ref,
|
ref,
|
||||||
minSize,
|
minSize,
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
setIsCollapsed,
|
onCollapse,
|
||||||
|
onExpand,
|
||||||
reset,
|
reset,
|
||||||
toggle,
|
toggle,
|
||||||
expand,
|
expand,
|
||||||
|
Loading…
Reference in New Issue
Block a user