(frontend) preserve left panel width on window resize

prevents automatic resizing to keep user-defined width stable

Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
Cyril
2025-11-13 09:49:00 +01:00
parent 26620f3471
commit 10a319881d
2 changed files with 27 additions and 48 deletions

View File

@@ -40,6 +40,7 @@ and this project adheres to
### Security ### Security
- mitigate role escalation in the ask_for_access viewset #1580 - mitigate role escalation in the ask_for_access viewset #1580
- 🐛(frontend) preserve left panel width on window resize #1588
### Removed ### Removed

View File

@@ -5,23 +5,12 @@ import {
PanelGroup, PanelGroup,
PanelResizeHandle, PanelResizeHandle,
} from 'react-resizable-panels'; } from 'react-resizable-panels';
import { createGlobalStyle } from 'styled-components';
import { useCunninghamTheme } from '@/cunningham'; import { useCunninghamTheme } from '@/cunningham';
interface PanelStyleProps {
$isResizing: boolean;
}
const PanelStyle = createGlobalStyle<PanelStyleProps>`
${({ $isResizing }) => $isResizing && `body * { transition: none !important; }`}
`;
// Convert a target pixel width to a percentage of the current viewport width. // Convert a target pixel width to a percentage of the current viewport width.
// react-resizable-panels expects sizes in %, not px. const pxToPercent = (px: number) => {
const calculateDefaultSize = (targetWidth: number) => { return (px / window.innerWidth) * 100;
const windowWidth = window.innerWidth;
return (targetWidth / windowWidth) * 100;
}; };
type ResizableLeftPanelProps = { type ResizableLeftPanelProps = {
@@ -37,60 +26,49 @@ export const ResizableLeftPanel = ({
minPanelSizePx = 300, minPanelSizePx = 300,
maxPanelSizePx = 450, maxPanelSizePx = 450,
}: ResizableLeftPanelProps) => { }: ResizableLeftPanelProps) => {
const [isResizing, setIsResizing] = useState(false);
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
const ref = useRef<ImperativePanelHandle>(null); const ref = useRef<ImperativePanelHandle>(null);
const resizeTimeoutRef = useRef<number | undefined>(undefined); const savedWidthPxRef = useRef<number>(minPanelSizePx);
const [minPanelSize, setMinPanelSize] = useState(0); const [panelSizePercent, setPanelSizePercent] = useState(() =>
const [maxPanelSize, setMaxPanelSize] = useState(0); pxToPercent(minPanelSizePx),
);
// Single resize listener that handles both panel size updates and transition disabling const minPanelSizePercent = pxToPercent(minPanelSizePx);
const maxPanelSizePercent = Math.min(pxToPercent(maxPanelSizePx), 40);
// Keep pixel width constant on window resize
useEffect(() => { useEffect(() => {
const handleResize = () => { const handleResize = () => {
// Update panel sizes (px -> %) const newPercent = pxToPercent(savedWidthPxRef.current);
const min = Math.round(calculateDefaultSize(minPanelSizePx)); setPanelSizePercent(newPercent);
const max = Math.round( if (ref.current) {
Math.min(calculateDefaultSize(maxPanelSizePx), 40), ref.current.resize?.(newPercent - (ref.current.getSize() || 0));
);
setMinPanelSize(min);
setMaxPanelSize(max);
// Temporarily disable transitions to avoid flicker
setIsResizing(true);
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
} }
resizeTimeoutRef.current = window.setTimeout(() => {
setIsResizing(false);
}, 150);
}; };
handleResize();
window.addEventListener('resize', handleResize); window.addEventListener('resize', handleResize);
return () => { return () => {
window.removeEventListener('resize', handleResize); window.removeEventListener('resize', handleResize);
if (resizeTimeoutRef.current) {
clearTimeout(resizeTimeoutRef.current);
}
}; };
}, [minPanelSizePx, maxPanelSizePx]); }, []);
const handleResize = (sizePercent: number) => {
const widthPx = (sizePercent / 100) * window.innerWidth;
savedWidthPxRef.current = widthPx;
setPanelSizePercent(sizePercent);
};
return ( return (
<> <>
<PanelStyle $isResizing={isResizing} /> <PanelGroup direction="horizontal">
<PanelGroup
autoSaveId="docs-left-panel-persistence"
direction="horizontal"
>
<Panel <Panel
ref={ref} ref={ref}
order={0} order={0}
defaultSize={minPanelSize} defaultSize={panelSizePercent}
minSize={minPanelSize} minSize={minPanelSizePercent}
maxSize={maxPanelSize} maxSize={maxPanelSizePercent}
onResize={handleResize}
> >
{leftPanel} {leftPanel}
</Panel> </Panel>