✨(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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user