Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/components/Fullscreen/Fullscreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {Button, Icon} from '@gravity-ui/uikit';
import {disableFullscreen} from '../../store/reducers/fullscreen';
import {cn} from '../../utils/cn';
import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
import {useResizeObserverTrigger} from '../../utils/hooks/useResizeObserverTrigger';
import {Portal} from '../Portal/Portal';

import {useFullscreenContext} from './FullscreenContext';
Expand Down Expand Up @@ -62,14 +63,12 @@ export function Fullscreen({children, className}: FullscreenProps) {
} else {
ref.current?.appendChild(container);
}
// Trigger recalculation for components relying on window resize
// Dispatch after DOM repaint to ensure correct measurements
requestAnimationFrame(() => {
window.dispatchEvent(new Event('resize'));
});
}
}, [container, fullscreenRootRef, isFullscreen]);

// Trigger resize event when fullscreen state changes to force virtualization recalculation
useResizeObserverTrigger([isFullscreen]);

if (!container) {
return null;
}
Expand Down
4 changes: 4 additions & 0 deletions src/containers/Cluster/ClusterOverview/ClusterOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {isClusterInfoV2, isClusterInfoV5} from '../../../types/api/cluster';
import type {TClusterInfo} from '../../../types/api/cluster';
import type {IResponseError} from '../../../types/api/error';
import {valueIsDefined} from '../../../utils';
import {useResizeObserverTrigger} from '../../../utils/hooks/useResizeObserverTrigger';
import {useSetting} from '../../../utils/hooks/useSetting';
import {ClusterInfo} from '../ClusterInfo/ClusterInfo';
import i18n from '../i18n';
Expand Down Expand Up @@ -43,6 +44,9 @@ export function ClusterOverview(props: ClusterOverviewProps) {
const [expandDashboard, setExpandDashboard] = useSetting<boolean>(
SETTING_KEYS.EXPAND_CLUSTER_DASHBOARD,
);

//needs timeout to ensure layout has been recalculated after Disclosure animations
useResizeObserverTrigger([expandDashboard], 110);
const bridgeModeEnabled = useBridgeModeEnabled();

const bridgePiles = React.useMemo(() => {
Expand Down
2 changes: 2 additions & 0 deletions src/containers/Tenant/utils/paneVisibilityToggleHelpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {ChevronsUp} from '@gravity-ui/icons';
import {ActionTooltip, Button, Icon} from '@gravity-ui/uikit';

import {cn} from '../../../utils/cn';
import {useResizeObserverTrigger} from '../../../utils/hooks/useResizeObserverTrigger';

import './ToggleButton.scss';

Expand Down Expand Up @@ -82,6 +83,7 @@ export function PaneVisibilityToggleButtons({
initialDirection = 'top',
className,
}: ToggleButtonProps) {
useResizeObserverTrigger([isCollapsed]);
return (
<React.Fragment>
<ActionTooltip title="Collapse">
Expand Down
43 changes: 43 additions & 0 deletions src/utils/hooks/useResizeObserverTrigger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

/**
* Triggers a window resize event when dependencies change.
* Useful for forcing virtualized components to recalculate visible items
* after layout changes (e.g., when panels expand/collapse).
*
* Uses double requestAnimationFrame to ensure the resize event is dispatched
* after the browser has completed layout recalculation. Properly cleans up
* all pending RAF callbacks and timeouts on unmount or dependency changes.
* @param dependencies - Array of values to watch for changes
* @param timeout - Optional delay in milliseconds before dispatching the resize event (default: 0)
* @example
* ```typescript
* const [isExpanded, setIsExpanded] = useState(false);
* useResizeObserverTrigger([isExpanded]);
* ```
*/
export function useResizeObserverTrigger(dependencies: React.DependencyList, timeout = 0): void {
React.useEffect(() => {
let rafId2: number | undefined;
let timeoutId: number | undefined;

// Use double RAF to ensure layout has been recalculated after animation
const rafId1 = requestAnimationFrame(() => {
rafId2 = requestAnimationFrame(() => {
// Dispatch resize event to trigger virtualization recalculation
timeoutId = window.setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, timeout);
});
});

return () => {
cancelAnimationFrame(rafId1);
if (rafId2 !== undefined) {
cancelAnimationFrame(rafId2);
}
clearTimeout(timeoutId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies);
}
Loading