fix: Various improvements to select input, closes #4528

This commit is contained in:
Tom Moor
2022-12-04 10:57:09 -05:00
parent 13db16283a
commit cd29cd3aec
2 changed files with 26 additions and 37 deletions

View File

@@ -8,11 +8,11 @@ import {
import { CheckmarkIcon } from "outline-icons";
import * as React from "react";
import { VisuallyHidden } from "reakit/VisuallyHidden";
import scrollIntoView from "smooth-scroll-into-view-if-needed";
import styled, { css } from "styled-components";
import Button, { Inner } from "~/components/Button";
import Text from "~/components/Text";
import useMenuHeight from "~/hooks/useMenuHeight";
import useMobile from "~/hooks/useMobile";
import { fadeAndScaleIn } from "~/styles/animations";
import {
Position,
@@ -78,16 +78,25 @@ const InputSelect = (props: Props) => {
disabled,
});
const isMobile = useMobile();
const previousValue = React.useRef<string | null>(value);
const contentRef = React.useRef<HTMLDivElement>(null);
const selectedRef = React.useRef<HTMLDivElement>(null);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const [offset, setOffset] = React.useState(0);
const contentRef = React.useRef<HTMLDivElement>(null);
const minWidth = buttonRef.current?.offsetWidth || 0;
const maxHeight = useMenuHeight(
const margin = 8;
const menuMaxHeight = useMenuHeight(
select.visible,
select.unstable_disclosureRef
select.unstable_disclosureRef,
margin
);
const maxHeight = Math.min(
menuMaxHeight ?? 0,
window.innerHeight -
(buttonRef.current?.getBoundingClientRect().bottom ?? 0) -
margin
);
const wrappedLabel = <LabelText>{label}</LabelText>;
const selectedValueIndex = options.findIndex(
(option) => option.value === select.selectedValue
@@ -106,26 +115,15 @@ const InputSelect = (props: Props) => {
load();
}, [onChange, select.selectedValue]);
// Ensure selected option is visible when opening the input
React.useEffect(() => {
if (!select.animating && selectedRef.current) {
scrollIntoView(selectedRef.current, {
scrollMode: "if-needed",
behavior: "auto",
block: "start",
});
}
}, [select.animating]);
React.useLayoutEffect(() => {
if (select.visible) {
const offset = Math.round(
(selectedRef.current?.getBoundingClientRect().top || 0) -
(contentRef.current?.getBoundingClientRect().top || 0)
);
setOffset(offset);
requestAnimationFrame(() => {
if (contentRef.current) {
contentRef.current.scrollTop = selectedValueIndex * 32;
}
});
}
}, [select.visible]);
}, [select.visible, selectedValueIndex]);
return (
<>
@@ -158,17 +156,9 @@ const InputSelect = (props: Props) => {
placement: Placement;
}
) => {
if (!props.style) {
props.style = {};
}
const topAnchor = props.style.top === "0";
const topAnchor = props.style?.top === "0";
const rightAnchor = props.placement === "bottom-end";
// offset top of select to place selected item under the cursor
if (selectedValueIndex !== -1) {
props.style.top = `-${offset + 32}px`;
}
return (
<Positioner {...props}>
<Background
@@ -218,7 +208,7 @@ const InputSelect = (props: Props) => {
{note}
</Text>
)}
{select.visible && <Backdrop />}
{select.visible && isMobile && <Backdrop />}
</>
);
};

View File

@@ -4,25 +4,24 @@ import useWindowSize from "~/hooks/useWindowSize";
const useMenuHeight = (
visible: void | boolean,
unstable_disclosureRef?: React.RefObject<HTMLElement | null>
unstable_disclosureRef?: React.RefObject<HTMLElement | null>,
margin = 8
) => {
const [maxHeight, setMaxHeight] = React.useState<number | undefined>();
const isMobile = useMobile();
const { height: windowHeight } = useWindowSize();
React.useEffect(() => {
const padding = 8;
if (visible && !isMobile) {
setMaxHeight(
unstable_disclosureRef?.current
? windowHeight -
unstable_disclosureRef.current.getBoundingClientRect().bottom -
padding
margin
: undefined
);
}
}, [visible, unstable_disclosureRef, windowHeight, isMobile]);
}, [visible, unstable_disclosureRef, windowHeight, margin, isMobile]);
return maxHeight;
};