feat(ui): bump react-resizable-panels, improve panel resize logic

This commit is contained in:
psychedelicious 2023-12-31 13:05:41 +11:00 committed by Kent Keirsey
parent 2663a07e94
commit 2ffecef792
4 changed files with 78 additions and 45 deletions

View File

@ -95,7 +95,7 @@
"react-icons": "^4.12.0",
"react-konva": "^18.2.10",
"react-redux": "^9.0.4",
"react-resizable-panels": "^1.0.5",
"react-resizable-panels": "^1.0.6",
"react-select": "5.7.7",
"react-textarea-autosize": "^8.5.3",
"react-use": "^17.4.2",

View File

@ -135,8 +135,8 @@ dependencies:
specifier: ^9.0.4
version: 9.0.4(@types/react@18.2.46)(react@18.2.0)(redux@5.0.1)
react-resizable-panels:
specifier: ^1.0.5
version: 1.0.5(react-dom@18.2.0)(react@18.2.0)
specifier: ^1.0.6
version: 1.0.6(react-dom@18.2.0)(react@18.2.0)
react-select:
specifier: 5.7.7
version: 5.7.7(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)
@ -11221,8 +11221,8 @@ packages:
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
dev: false
/react-resizable-panels@1.0.5(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OP0whNQCko+f4BgoptGaeIc7StBRyeMeJ+8r/7rXACBDf9W5EcMWuM32hfqPDMenS2HFy/eZVi/r8XqK+ZIEag==}
/react-resizable-panels@1.0.6(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-yZQiOP/uW2nTSdESDUBlBkQ1NQjUABpRKfBqonUQnNYSur7qRDy3W2wEEmrEyUY+W3opshpMiHf45zngIduJ0g==}
peerDependencies:
react: ^16.14.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0

View File

@ -104,19 +104,7 @@ const GALLERY_MIN_SIZE_PCT = 20;
const OPTIONS_PANEL_MIN_SIZE_PX = 430;
const OPTIONS_PANEL_MIN_SIZE_PCT = 20;
const optionsPanelUsePanelOptions: UsePanelOptions = {
unit: 'pixels',
minSize: OPTIONS_PANEL_MIN_SIZE_PX,
fallbackMinSizePct: OPTIONS_PANEL_MIN_SIZE_PCT,
panelGroupID: 'app',
};
const galleryPanelUsePanelOptions: UsePanelOptions = {
unit: 'pixels',
minSize: GALLERY_MIN_SIZE_PX,
fallbackMinSizePct: GALLERY_MIN_SIZE_PCT,
panelGroupID: 'app',
};
const appPanelGroupId = 'app-panel-group';
const InvokeTabs = () => {
const activeTabIndex = useAppSelector(activeTabIndexSelector);
@ -124,7 +112,7 @@ const InvokeTabs = () => {
const enabledTabs = useAppSelector(enabledTabsSelector);
const { t } = useTranslation();
const dispatch = useAppDispatch();
const panelGroupHandleRef = useRef<ImperativePanelGroupHandle>(null);
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
const handleClickTab = useCallback((e: MouseEvent<HTMLElement>) => {
if (e.target instanceof HTMLElement) {
e.target.blur();
@ -170,6 +158,28 @@ const InvokeTabs = () => {
[dispatch, enabledTabs]
);
const optionsPanelUsePanelOptions = useMemo<UsePanelOptions>(
() => ({
unit: 'pixels',
minSize: OPTIONS_PANEL_MIN_SIZE_PX,
fallbackMinSizePct: OPTIONS_PANEL_MIN_SIZE_PCT,
panelGroupRef,
panelGroupDirection: 'horizontal',
}),
[]
);
const galleryPanelUsePanelOptions = useMemo<UsePanelOptions>(
() => ({
unit: 'pixels',
minSize: GALLERY_MIN_SIZE_PX,
fallbackMinSizePct: GALLERY_MIN_SIZE_PCT,
panelGroupRef,
panelGroupDirection: 'horizontal',
}),
[]
);
const panelStorage = usePanelStorage();
const {
@ -227,8 +237,8 @@ const InvokeTabs = () => {
<Spacer />
</InvTabList>
<PanelGroup
ref={panelGroupHandleRef}
id="app"
ref={panelGroupRef}
id={appPanelGroupId}
autoSaveId="app"
direction="horizontal"
style={panelStyles}
@ -237,7 +247,7 @@ const InvokeTabs = () => {
{!NO_SIDE_PANEL_TABS.includes(activeTabName) && (
<>
<Panel
id="options"
id="options-panel"
ref={optionsPanelRef}
order={0}
defaultSize={optionsPanelMinSize}
@ -253,12 +263,13 @@ const InvokeTabs = () => {
)}
</Panel>
<ResizeHandle
id="options-main-handle"
onDoubleClick={resetOptionsPanel}
orientation="vertical"
/>
</>
)}
<Panel id="main" order={1} minSize={20}>
<Panel id="main-panel" order={1} minSize={20}>
<InvTabPanels w="full" h="full">
{tabPanels}
</InvTabPanels>
@ -266,11 +277,12 @@ const InvokeTabs = () => {
{!NO_GALLERY_TABS.includes(activeTabName) && (
<>
<ResizeHandle
id="main-gallery-handle"
onDoubleClick={resetGalleryPanel}
orientation="vertical"
/>
<Panel
id="gallery"
id="gallery-panel"
ref={galleryPanelRef}
order={2}
defaultSize={galleryPanelMinSize}

View File

@ -1,9 +1,15 @@
import type { RefObject } from 'react';
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import type {
ImperativePanelGroupHandle,
ImperativePanelHandle,
PanelOnCollapse,
PanelOnExpand,
} from 'react-resizable-panels';
import {
getPanelGroupElement,
getResizeHandleElementsForGroup,
} from 'react-resizable-panels';
export type UsePanelOptions =
| { minSize: number; unit: 'percentages' }
@ -11,46 +17,61 @@ export type UsePanelOptions =
minSize: number;
unit: 'pixels';
fallbackMinSizePct: number;
panelGroupID: string;
panelGroupRef: RefObject<ImperativePanelGroupHandle>;
panelGroupDirection: 'horizontal' | 'vertical';
};
export const usePanel = (arg: UsePanelOptions) => {
const panelHandleRef = useRef<ImperativePanelHandle>(null);
const newMinSizeRef = useRef<number>(0);
const currentSizeRef = useRef<number>(0);
const [_minSize, _setMinSize] = useState<number>(
arg.unit === 'percentages' ? arg.minSize : arg.fallbackMinSizePct
);
// If the units are pixels, we need to calculate the min size as a percentage of the available space
useLayoutEffect(() => {
if (arg.unit === 'percentages') {
if (arg.unit === 'percentages' || !arg.panelGroupRef.current) {
return;
}
const panelGroupElement = document.querySelector(
`[data-panel-group][data-panel-group-id="${arg.panelGroupID}"]`
const panelGroupElement = getPanelGroupElement(
arg.panelGroupRef.current.getId()
);
const panelGroupHandleElements = getResizeHandleElementsForGroup(
arg.panelGroupRef.current.getId()
);
if (!panelGroupElement) {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (!panelHandleRef?.current) {
return;
}
newMinSizeRef.current = (arg.minSize * 100) / entry.contentRect.width;
currentSizeRef.current =
panelHandleRef.current.getSize() ?? arg.fallbackMinSizePct;
if (currentSizeRef.current < newMinSizeRef.current) {
panelHandleRef.current.resize(newMinSizeRef.current);
}
_setMinSize(newMinSizeRef.current);
const resizeObserver = new ResizeObserver(() => {
if (!panelHandleRef?.current) {
return;
}
// Calculate the available space for the panel, minus the space taken by the handles
let dim =
arg.panelGroupDirection === 'horizontal'
? panelGroupElement.offsetWidth
: panelGroupElement.offsetHeight;
panelGroupHandleElements.forEach(
(el) =>
(dim -=
arg.panelGroupDirection === 'horizontal'
? el.offsetWidth
: el.offsetHeight)
);
// Calculate the min size as a percentage of the available space
const minSize = (arg.minSize * 100) / dim;
// Must store this to avoid race conditions
const currentSize = panelHandleRef.current.getSize();
// Resize if the current size is smaller than the new min size - happens when the window is resized smaller
if (currentSize < minSize) {
panelHandleRef.current.resize(minSize);
}
_setMinSize(minSize);
});
resizeObserver.observe(panelGroupElement);
// _setMinSize(
// (arg.minSize * 100) / panelGroupElement.getBoundingClientRect().width
// );
return () => {
resizeObserver.disconnect();