mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
fix(ui): working panel size persistence
This commit is contained in:
@@ -88,7 +88,7 @@ const GalleryPanelContent = () => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<PanelGroup ref={panelGroupRef} direction="vertical">
|
||||
<PanelGroup ref={panelGroupRef} direction="vertical" autoSaveId="boards-list-panel">
|
||||
<Panel collapsible {...boardsListPanel.panelProps}>
|
||||
<Flex flexDir="column" w="full" h="full">
|
||||
<Collapse in={boardSearchDisclosure.isOpen} style={COLLAPSE_STYLES}>
|
||||
|
||||
@@ -18,7 +18,12 @@ export const ImageComparisonSideBySide = memo(({ firstImage, secondImage }: Comp
|
||||
return (
|
||||
<Flex w="full" h="full" maxW="full" maxH="full" position="relative" alignItems="center" justifyContent="center">
|
||||
<Flex w="full" h="full" maxW="full" maxH="full" position="absolute" alignItems="center" justifyContent="center">
|
||||
<PanelGroup ref={panelGroupRef} direction="horizontal" id="image-comparison-side-by-side">
|
||||
<PanelGroup
|
||||
ref={panelGroupRef}
|
||||
direction="horizontal"
|
||||
id="image-comparison-side-by-side"
|
||||
autoSaveId="image-comparison-side-by-side"
|
||||
>
|
||||
<Panel minSize={20}>
|
||||
<Flex position="relative" w="full" h="full" alignItems="center" justifyContent="center">
|
||||
<Flex position="absolute" maxW="full" maxH="full" aspectRatio={firstImage.width / firstImage.height}>
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Flex } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { selectWorkflowMode } from 'features/nodes/store/workflowSlice';
|
||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
||||
import WorkflowLibraryButton from 'features/workflowLibrary/components/WorkflowLibraryButton';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
@@ -22,7 +21,6 @@ const panelGroupStyles: CSSProperties = { height: '100%', width: '100%' };
|
||||
const NodeEditorPanelGroup = () => {
|
||||
const mode = useAppSelector(selectWorkflowMode);
|
||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
const panelStorage = usePanelStorage();
|
||||
|
||||
const handleDoubleClickHandle = useCallback(() => {
|
||||
if (!panelGroupRef.current) {
|
||||
@@ -49,7 +47,6 @@ const NodeEditorPanelGroup = () => {
|
||||
autoSaveId="workflow-panel-group"
|
||||
direction="vertical"
|
||||
style={panelGroupStyles}
|
||||
storage={panelStorage}
|
||||
>
|
||||
<Panel id="workflow" collapsible minSize={25}>
|
||||
<WorkflowPanel />
|
||||
|
||||
@@ -16,7 +16,6 @@ import { WorkflowsTabContent } from 'features/ui/components/tabs/WorkflowsTabCon
|
||||
import { VerticalNavBar } from 'features/ui/components/VerticalNavBar';
|
||||
import type { UsePanelOptions } from 'features/ui/hooks/usePanel';
|
||||
import { usePanel } from 'features/ui/hooks/usePanel';
|
||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import {
|
||||
$isLeftPanelOpen,
|
||||
@@ -47,7 +46,6 @@ export const AppContent = memo(() => {
|
||||
useScopeOnFocus('gallery', ref);
|
||||
|
||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
||||
const panelStorage = usePanelStorage();
|
||||
|
||||
const withLeftPanel = useAppSelector(selectWithLeftPanel);
|
||||
const leftPanelUsePanelOptions = useMemo<UsePanelOptions>(
|
||||
@@ -117,10 +115,9 @@ export const AppContent = memo(() => {
|
||||
<PanelGroup
|
||||
ref={panelGroupRef}
|
||||
id="app-panel-group"
|
||||
autoSaveId="app"
|
||||
autoSaveId="app-panel-group"
|
||||
direction="horizontal"
|
||||
style={panelStyles}
|
||||
storage={panelStorage}
|
||||
>
|
||||
{withLeftPanel && (
|
||||
<>
|
||||
|
||||
@@ -85,7 +85,7 @@ export type UsePanelReturn = {
|
||||
};
|
||||
|
||||
export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
const panelHandleRef = useRef<ImperativePanelHandle>(null);
|
||||
const imperativePanelRef = useRef<ImperativePanelHandle>(null);
|
||||
const [_minSize, _setMinSize] = useState<number>(arg.unit === 'percentages' ? arg.minSize : 0);
|
||||
const [_defaultSize, _setDefaultSize] = useState<number>(arg.defaultSize ?? arg.minSize);
|
||||
|
||||
@@ -102,7 +102,7 @@ export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
return;
|
||||
}
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (!panelHandleRef?.current) {
|
||||
if (!imperativePanelRef?.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,30 +116,42 @@ export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
_setMinSize(minSizePct);
|
||||
|
||||
if (arg.defaultSize && arg.defaultSize > minSizePct) {
|
||||
_setDefaultSize(defaultSizePct);
|
||||
_setDefaultSize(arg.defaultSize);
|
||||
} else {
|
||||
_setDefaultSize(minSizePct);
|
||||
}
|
||||
|
||||
if (!panelHandleRef.current.isCollapsed() && panelHandleRef.current.getSize() < minSizePct && minSizePct > 0) {
|
||||
panelHandleRef.current.resize(minSizePct);
|
||||
const currentSize = imperativePanelRef.current.getSize();
|
||||
const isCollapsed = imperativePanelRef.current.isCollapsed();
|
||||
|
||||
if (isCollapsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCollapsed && currentSize < minSizePct && minSizePct > 0) {
|
||||
imperativePanelRef.current.resize(minSizePct);
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(panelGroupElement);
|
||||
panelGroupHandleElements.forEach((el) => resizeObserver.observe(el));
|
||||
|
||||
// Resize the panel to the min size once on startup
|
||||
const defaultSizePct =
|
||||
arg.defaultSize ?? getSizeAsPercentage(arg.minSize, arg.panelGroupRef, arg.panelGroupDirection);
|
||||
panelHandleRef.current?.resize(defaultSizePct);
|
||||
if (imperativePanelRef.current) {
|
||||
const currentSize = imperativePanelRef.current.getSize();
|
||||
const isCollapsed = imperativePanelRef.current.isCollapsed();
|
||||
|
||||
// Resize the panel to the min size once on startup if it is too small
|
||||
if (!isCollapsed && currentSize < _minSize) {
|
||||
imperativePanelRef.current.resize(_minSize);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [arg]);
|
||||
}, [_minSize, arg]);
|
||||
|
||||
const [isCollapsed, setIsCollapsed] = useState(() => Boolean(panelHandleRef.current?.isCollapsed()));
|
||||
const [isCollapsed, setIsCollapsed] = useState(() => Boolean(imperativePanelRef.current?.isCollapsed()));
|
||||
|
||||
const onCollapse = useCallback<PanelOnCollapse>(() => {
|
||||
setIsCollapsed(true);
|
||||
@@ -152,49 +164,49 @@ export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
}, [arg]);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
if (panelHandleRef.current?.isCollapsed()) {
|
||||
panelHandleRef.current?.expand();
|
||||
if (imperativePanelRef.current?.isCollapsed()) {
|
||||
imperativePanelRef.current?.expand();
|
||||
} else {
|
||||
panelHandleRef.current?.collapse();
|
||||
imperativePanelRef.current?.collapse();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const expand = useCallback(() => {
|
||||
panelHandleRef.current?.expand();
|
||||
imperativePanelRef.current?.expand();
|
||||
}, []);
|
||||
|
||||
const collapse = useCallback(() => {
|
||||
panelHandleRef.current?.collapse();
|
||||
imperativePanelRef.current?.collapse();
|
||||
}, []);
|
||||
|
||||
const resize = useCallback(
|
||||
(size: number) => {
|
||||
// If we are using percentages, we can just resize to the given size
|
||||
if (arg.unit === 'percentages') {
|
||||
panelHandleRef.current?.resize(size);
|
||||
imperativePanelRef.current?.resize(size);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are using pixels, we need to calculate the size as a percentage of the available space
|
||||
const sizeAsPct = getSizeAsPercentage(size, arg.panelGroupRef, arg.panelGroupDirection);
|
||||
panelHandleRef.current?.resize(sizeAsPct);
|
||||
imperativePanelRef.current?.resize(sizeAsPct);
|
||||
},
|
||||
[arg]
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
panelHandleRef.current?.resize(_minSize);
|
||||
imperativePanelRef.current?.resize(_minSize);
|
||||
}, [_minSize]);
|
||||
|
||||
const cycleState = useCallback(() => {
|
||||
// If the panel is really super close to the min size, collapse it
|
||||
if (Math.abs((panelHandleRef.current?.getSize() ?? 0) - _defaultSize) < 0.01) {
|
||||
if (Math.abs((imperativePanelRef.current?.getSize() ?? 0) - _defaultSize) < 0.01) {
|
||||
collapse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, resize to the min size
|
||||
panelHandleRef.current?.resize(_defaultSize);
|
||||
imperativePanelRef.current?.resize(_defaultSize);
|
||||
}, [_defaultSize, collapse]);
|
||||
|
||||
return {
|
||||
@@ -209,7 +221,7 @@ export const usePanel = (arg: UsePanelOptions): UsePanelReturn => {
|
||||
defaultSize: _defaultSize,
|
||||
onCollapse,
|
||||
onExpand,
|
||||
ref: panelHandleRef,
|
||||
ref: imperativePanelRef,
|
||||
minSize: _minSize,
|
||||
},
|
||||
resizeHandleProps: {
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { useAppStore } from 'app/store/nanostores/store';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { panelsChanged } from 'features/ui/store/uiSlice';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const usePanelStorage = () => {
|
||||
const store = useAppStore();
|
||||
const dispatch = useAppDispatch();
|
||||
const getItem = useCallback(
|
||||
(name: string) => {
|
||||
const panels = store.getState().ui.panels;
|
||||
return panels[name] ?? '';
|
||||
},
|
||||
[store]
|
||||
);
|
||||
const setItem = useCallback(
|
||||
(name: string, value: string) => {
|
||||
dispatch(panelsChanged({ name, value }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return { getItem, setItem };
|
||||
};
|
||||
@@ -11,7 +11,6 @@ const initialUIState: UIState = {
|
||||
activeTab: 'generation',
|
||||
shouldShowImageDetails: false,
|
||||
shouldShowProgressInViewer: true,
|
||||
panels: {},
|
||||
accordions: {},
|
||||
expanders: {},
|
||||
};
|
||||
@@ -29,9 +28,6 @@ export const uiSlice = createSlice({
|
||||
setShouldShowProgressInViewer: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldShowProgressInViewer = action.payload;
|
||||
},
|
||||
panelsChanged: (state, action: PayloadAction<{ name: string; value: string }>) => {
|
||||
state.panels[action.payload.name] = action.payload.value;
|
||||
},
|
||||
accordionStateChanged: (state, action: PayloadAction<{ id: string; isOpen: boolean }>) => {
|
||||
const { id, isOpen } = action.payload;
|
||||
state.accordions[id] = isOpen;
|
||||
@@ -52,7 +48,6 @@ export const {
|
||||
setActiveTab,
|
||||
setShouldShowImageDetails,
|
||||
setShouldShowProgressInViewer,
|
||||
panelsChanged,
|
||||
accordionStateChanged,
|
||||
expanderStateChanged,
|
||||
} = uiSlice.actions;
|
||||
|
||||
@@ -17,10 +17,6 @@ export interface UIState {
|
||||
* Whether or not to show progress in the viewer.
|
||||
*/
|
||||
shouldShowProgressInViewer: boolean;
|
||||
/**
|
||||
* The react-resizable-panels state. The shape is managed by react-resizable-panels.
|
||||
*/
|
||||
panels: Record<string, string>;
|
||||
/**
|
||||
* The state of accordions. The key is the id of the accordion, and the value is a boolean representing the open state.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user