Files
paseo/packages/app/src/hooks/use-container-width.ts
T
Mohamed BoudraandGitHub 2298e9efdf Fix composer toolbar collapse flicker on tab switch (#2937)
Retained tabs stay mounted under display:none while inactive, which
reports a zero-width onLayout. ControlledAgentControls fed that
straight into its density resolver, so a hidden tab's toolbar
collapsed to its tightest density and painted one frame collapsed
before re-expanding when the tab became visible again.

Extract the zero-width guard that useContainerWidthBelow already had
into a shared readMeasuredWidth helper, and use it in the composer
toolbar's own layout handler so a display:none measurement can never
drive its density.

Also bump the chat outline rail's MIN_PANEL_WIDTH breakpoint (818 ->
918) so the rail disappears earlier under a narrower panel, and widen
the chat-outline e2e spec's viewport so its panel keeps healthy margin
over the new threshold.
2026-08-06 18:37:38 +08:00

58 lines
1.5 KiB
TypeScript

import { useCallback, useState } from "react";
import type { LayoutChangeEvent } from "react-native";
/**
* Reads a real width out of a layout event. Retained tabs stay mounted under
* `display: none`, so an inactive panel emits width 0 — that is "not rendered",
* not "very narrow", and must never drive a width-based collapse.
*/
export function readMeasuredWidth(event: LayoutChangeEvent): number | null {
const width = event.nativeEvent.layout.width;
return width > 0 ? width : null;
}
/**
* Tracks the width of a container via onLayout.
*/
export function useContainerWidth(): {
onLayout: (e: LayoutChangeEvent) => void;
width: number;
} {
const [width, setWidth] = useState(0);
return {
onLayout: useCallback((e: LayoutChangeEvent) => {
setWidth(e.nativeEvent.layout.width);
}, []),
width,
};
}
/**
* Tracks only whether a container is narrower than a threshold.
*/
export function useContainerWidthBelow(
threshold: number,
options?: { initialIsBelow?: boolean },
): {
onLayout: (e: LayoutChangeEvent) => void;
isBelow: boolean;
} {
const [isBelow, setIsBelow] = useState(options?.initialIsBelow ?? true);
return {
onLayout: useCallback(
(e: LayoutChangeEvent) => {
const width = readMeasuredWidth(e);
if (width === null) {
return;
}
const nextIsBelow = width < threshold;
setIsBelow((currentIsBelow) =>
currentIsBelow === nextIsBelow ? currentIsBelow : nextIsBelow,
);
},
[threshold],
),
isBelow,
};
}