fix: Opening contextual menus sometimes change scroll position

This commit is contained in:
Tom Moor
2023-07-18 21:31:43 -04:00
parent eae6204d55
commit 3c6e2aaac6
3 changed files with 42 additions and 21 deletions

View File

@@ -56,7 +56,10 @@ const ContextMenu: React.FC<Props> = ({
...rest
}) => {
const previousVisible = usePrevious(rest.visible);
const maxHeight = useMenuHeight(rest.visible, rest.unstable_disclosureRef);
const maxHeight = useMenuHeight({
visible: rest.visible,
elementRef: rest.unstable_disclosureRef,
});
const backgroundRef = React.useRef<HTMLDivElement>(null);
const { ui } = useStores();
const { t } = useTranslation();
@@ -147,9 +150,9 @@ const ContextMenu: React.FC<Props> = ({
ref={backgroundRef}
hiddenScrollbars
style={
maxHeight && topAnchor
topAnchor
? {
maxHeight: `min(${maxHeight}px, 75vh)`,
maxHeight,
}
: undefined
}

View File

@@ -85,11 +85,11 @@ const InputSelect = (props: Props) => {
const contentRef = React.useRef<HTMLDivElement>(null);
const minWidth = buttonRef.current?.offsetWidth || 0;
const margin = 8;
const menuMaxHeight = useMenuHeight(
select.visible,
select.unstable_disclosureRef,
margin
);
const menuMaxHeight = useMenuHeight({
visible: select.visible,
elementRef: select.unstable_disclosureRef,
margin,
});
const maxHeight = Math.min(
menuMaxHeight ?? 0,
window.innerHeight -

View File

@@ -2,26 +2,44 @@ import * as React from "react";
import useMobile from "~/hooks/useMobile";
import useWindowSize from "~/hooks/useWindowSize";
const useMenuHeight = (
visible: void | boolean,
unstable_disclosureRef?: React.RefObject<HTMLElement | null>,
margin = 8
) => {
const [maxHeight, setMaxHeight] = React.useState<number | undefined>();
const useMenuHeight = ({
visible,
elementRef,
maxViewportHeight = 70,
margin = 8,
}: {
/** Whether the menu is visible. */
visible: void | boolean;
/** The maximum height of the menu as a percentage of the viewport. */
maxViewportHeight?: number;
/** A ref pointing to the element for the menu disclosure. */
elementRef?: React.RefObject<HTMLElement | null>;
/** The margin to apply to the positioning. */
margin?: number;
}) => {
const [maxHeight, setMaxHeight] = React.useState<number | undefined>(10);
const isMobile = useMobile();
const { height: windowHeight } = useWindowSize();
React.useEffect(() => {
React.useLayoutEffect(() => {
if (visible && !isMobile) {
const maxHeight = (windowHeight / 100) * maxViewportHeight;
setMaxHeight(
unstable_disclosureRef?.current
Math.min(
maxHeight,
elementRef?.current
? windowHeight -
unstable_disclosureRef.current.getBoundingClientRect().bottom -
elementRef.current.getBoundingClientRect().bottom -
margin
: undefined
: 0
)
);
} else {
setMaxHeight(0);
}
}, [visible, unstable_disclosureRef, windowHeight, margin, isMobile]);
}, [visible, elementRef, windowHeight, margin, isMobile, maxViewportHeight]);
return maxHeight;
};