Skip to content

Commit 14ec98a

Browse files
authored
fix: virtualization problems (#3100)
1 parent deed421 commit 14ec98a

File tree

4 files changed

+53
-5
lines changed

4 files changed

+53
-5
lines changed

src/components/Fullscreen/Fullscreen.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {Button, Icon} from '@gravity-ui/uikit';
55
import {disableFullscreen} from '../../store/reducers/fullscreen';
66
import {cn} from '../../utils/cn';
77
import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
8+
import {useResizeObserverTrigger} from '../../utils/hooks/useResizeObserverTrigger';
89
import {Portal} from '../Portal/Portal';
910

1011
import {useFullscreenContext} from './FullscreenContext';
@@ -62,14 +63,12 @@ export function Fullscreen({children, className}: FullscreenProps) {
6263
} else {
6364
ref.current?.appendChild(container);
6465
}
65-
// Trigger recalculation for components relying on window resize
66-
// Dispatch after DOM repaint to ensure correct measurements
67-
requestAnimationFrame(() => {
68-
window.dispatchEvent(new Event('resize'));
69-
});
7066
}
7167
}, [container, fullscreenRootRef, isFullscreen]);
7268

69+
// Trigger resize event when fullscreen state changes to force virtualization recalculation
70+
useResizeObserverTrigger([isFullscreen]);
71+
7372
if (!container) {
7473
return null;
7574
}

src/containers/Cluster/ClusterOverview/ClusterOverview.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {isClusterInfoV2, isClusterInfoV5} from '../../../types/api/cluster';
1414
import type {TClusterInfo} from '../../../types/api/cluster';
1515
import type {IResponseError} from '../../../types/api/error';
1616
import {valueIsDefined} from '../../../utils';
17+
import {useResizeObserverTrigger} from '../../../utils/hooks/useResizeObserverTrigger';
1718
import {useSetting} from '../../../utils/hooks/useSetting';
1819
import {ClusterInfo} from '../ClusterInfo/ClusterInfo';
1920
import i18n from '../i18n';
@@ -43,6 +44,9 @@ export function ClusterOverview(props: ClusterOverviewProps) {
4344
const [expandDashboard, setExpandDashboard] = useSetting<boolean>(
4445
SETTING_KEYS.EXPAND_CLUSTER_DASHBOARD,
4546
);
47+
48+
//needs timeout to ensure layout has been recalculated after Disclosure animations
49+
useResizeObserverTrigger([expandDashboard], 110);
4650
const bridgeModeEnabled = useBridgeModeEnabled();
4751

4852
const bridgePiles = React.useMemo(() => {

src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {ChevronsUp} from '@gravity-ui/icons';
44
import {ActionTooltip, Button, Icon} from '@gravity-ui/uikit';
55

66
import {cn} from '../../../utils/cn';
7+
import {useResizeObserverTrigger} from '../../../utils/hooks/useResizeObserverTrigger';
78

89
import './ToggleButton.scss';
910

@@ -82,6 +83,7 @@ export function PaneVisibilityToggleButtons({
8283
initialDirection = 'top',
8384
className,
8485
}: ToggleButtonProps) {
86+
useResizeObserverTrigger([isCollapsed]);
8587
return (
8688
<React.Fragment>
8789
<ActionTooltip title="Collapse">
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
3+
/**
4+
* Triggers a window resize event when dependencies change.
5+
* Useful for forcing virtualized components to recalculate visible items
6+
* after layout changes (e.g., when panels expand/collapse).
7+
*
8+
* Uses double requestAnimationFrame to ensure the resize event is dispatched
9+
* after the browser has completed layout recalculation. Properly cleans up
10+
* all pending RAF callbacks and timeouts on unmount or dependency changes.
11+
* @param dependencies - Array of values to watch for changes
12+
* @param timeout - Optional delay in milliseconds before dispatching the resize event (default: 0)
13+
* @example
14+
* ```typescript
15+
* const [isExpanded, setIsExpanded] = useState(false);
16+
* useResizeObserverTrigger([isExpanded]);
17+
* ```
18+
*/
19+
export function useResizeObserverTrigger(dependencies: React.DependencyList, timeout = 0): void {
20+
React.useEffect(() => {
21+
let rafId2: number | undefined;
22+
let timeoutId: number | undefined;
23+
24+
// Use double RAF to ensure layout has been recalculated after animation
25+
const rafId1 = requestAnimationFrame(() => {
26+
rafId2 = requestAnimationFrame(() => {
27+
// Dispatch resize event to trigger virtualization recalculation
28+
timeoutId = window.setTimeout(() => {
29+
window.dispatchEvent(new Event('resize'));
30+
}, timeout);
31+
});
32+
});
33+
34+
return () => {
35+
cancelAnimationFrame(rafId1);
36+
if (rafId2 !== undefined) {
37+
cancelAnimationFrame(rafId2);
38+
}
39+
clearTimeout(timeoutId);
40+
};
41+
// eslint-disable-next-line react-hooks/exhaustive-deps
42+
}, dependencies);
43+
}

0 commit comments

Comments
 (0)