fix(ui): working panel size persistence

This commit is contained in:
psychedelicious
2024-09-11 09:25:30 +10:00
parent c8a74f969b
commit e88d4aa0e8
8 changed files with 42 additions and 64 deletions

View File

@@ -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}>

View File

@@ -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}>

View File

@@ -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 />

View File

@@ -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 && (
<>

View File

@@ -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: {

View File

@@ -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 };
};

View File

@@ -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;

View File

@@ -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.
*/