feat(ui): minimum gallery size

Add `useMinimumPanelSize()` hook to provide minimum resizable panel sizes (in pixels).

The library we are using for the gallery panel uses percentages only. To provide a minimum size in pixels, we need to do some math to calculate the percentage of window size that corresponds to the desired min width in pixels.
This commit is contained in:
psychedelicious 2023-07-01 17:44:55 +10:00
parent d9ac36df1d
commit 3aa6a7e7df
2 changed files with 81 additions and 2 deletions

View File

@ -36,6 +36,7 @@ import { FaFont, FaImage } from 'react-icons/fa';
import ResizeHandle from './tabs/ResizeHandle';
import ImageTab from './tabs/ImageToImage/ImageToImageTab';
import AuxiliaryProgressIndicator from 'app/components/AuxiliaryProgressIndicator';
import { useMinimumPanelSize } from '../hooks/useMinimumPanelSize';
export interface InvokeTabInfo {
id: InvokeTabName;
@ -78,6 +79,9 @@ const enabledTabsSelector = createSelector(
}
);
const MIN_GALLERY_WIDTH = 300;
const DEFAULT_GALLERY_PCT = 20;
const InvokeTabs = () => {
const activeTab = useAppSelector(activeTabIndexSelector);
const activeTabName = useAppSelector(activeTabNameSelector);
@ -150,6 +154,9 @@ const InvokeTabs = () => {
[enabledTabs]
);
const { ref: galleryPanelRef, minSizePct: galleryMinSizePct } =
useMinimumPanelSize(MIN_GALLERY_WIDTH, DEFAULT_GALLERY_PCT, 'app');
return (
<Tabs
defaultIndex={activeTab}
@ -175,6 +182,7 @@ const InvokeTabs = () => {
<AuxiliaryProgressIndicator />
</TabList>
<PanelGroup
id="app"
autoSaveId="app"
direction="horizontal"
style={{ height: '100%', width: '100%' }}
@ -188,11 +196,12 @@ const InvokeTabs = () => {
<>
<ResizeHandle />
<Panel
ref={galleryPanelRef}
onResize={handleResizeGallery}
id="gallery"
order={3}
defaultSize={10}
minSize={10}
defaultSize={galleryMinSizePct}
minSize={galleryMinSizePct}
maxSize={50}
>
<ImageGalleryContent />

View File

@ -0,0 +1,70 @@
// adapted from https://github.com/bvaughn/react-resizable-panels/issues/141#issuecomment-1540048714
import {
RefObject,
useCallback,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { ImperativePanelHandle } from 'react-resizable-panels';
export const useMinimumPanelSize = (
minSizePx: number,
defaultSizePct: number,
groupId: string,
orientation: 'horizontal' | 'vertical' = 'horizontal'
): { ref: RefObject<ImperativePanelHandle>; minSizePct: number } => {
const ref = useRef<ImperativePanelHandle>(null);
const [minSizePct, setMinSizePct] = useState(defaultSizePct);
const handleWindowResize = useCallback(() => {
const size = ref.current?.getSize();
if (size !== undefined && size < minSizePct) {
ref.current?.resize(minSizePct);
}
}, [minSizePct]);
useLayoutEffect(() => {
const panelGroup = document.querySelector(
`[data-panel-group-id="${groupId}"]`
);
const resizeHandles = document.querySelectorAll(
'[data-panel-resize-handle-id]'
);
if (!panelGroup) {
return;
}
const observer = new ResizeObserver(() => {
let dim =
orientation === 'horizontal'
? panelGroup.getBoundingClientRect().width
: panelGroup.getBoundingClientRect().height;
resizeHandles.forEach((resizeHandle) => {
dim -=
orientation === 'horizontal'
? resizeHandle.getBoundingClientRect().width
: resizeHandle.getBoundingClientRect().height;
});
// Minimum size in pixels is a percentage of the PanelGroup's width/height
setMinSizePct((minSizePx / dim) * 100);
});
observer.observe(panelGroup);
resizeHandles.forEach((resizeHandle) => {
observer.observe(resizeHandle);
});
window.addEventListener('resize', handleWindowResize);
return () => {
observer.disconnect();
window.removeEventListener('resize', handleWindowResize);
};
}, [groupId, handleWindowResize, minSizePct, minSizePx, orientation]);
return { ref, minSizePct };
};