fix: Positioning of input select items when seleted item does not fit in available area

fix: Scroll selected item in input select
This commit is contained in:
Tom Moor
2021-10-06 23:31:35 -07:00
parent 4e05728218
commit ccd947c6e8

View File

@ -9,6 +9,7 @@ import {
import { CheckmarkIcon } from "outline-icons"; import { CheckmarkIcon } from "outline-icons";
import * as React from "react"; import * as React from "react";
import { VisuallyHidden } from "reakit/VisuallyHidden"; import { VisuallyHidden } from "reakit/VisuallyHidden";
import scrollIntoView from "smooth-scroll-into-view-if-needed";
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import Button, { Inner } from "components/Button"; import Button, { Inner } from "components/Button";
import { Position, Background, Backdrop } from "./ContextMenu"; import { Position, Background, Backdrop } from "./ContextMenu";
@ -53,6 +54,7 @@ const InputSelect = (props: Props) => {
gutter: 0, gutter: 0,
modal: true, modal: true,
selectedValue: value, selectedValue: value,
animated: 200,
}); });
const popOver = useSelectPopover({ const popOver = useSelectPopover({
@ -63,7 +65,10 @@ const InputSelect = (props: Props) => {
}); });
const previousValue = React.useRef(value); const previousValue = React.useRef(value);
const contentRef = React.useRef();
const selectedRef = React.useRef();
const buttonRef = React.useRef(); const buttonRef = React.useRef();
const [offset, setOffset] = React.useState(0);
const minWidth = buttonRef.current?.offsetWidth || 0; const minWidth = buttonRef.current?.offsetWidth || 0;
const maxHeight = useMenuHeight( const maxHeight = useMenuHeight(
@ -87,6 +92,27 @@ const InputSelect = (props: Props) => {
(option) => option.value === select.selectedValue (option) => option.value === 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: "instant",
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);
}
}, [select.visible]);
return ( return (
<> <>
<Wrapper short={short}> <Wrapper short={short}>
@ -122,13 +148,14 @@ const InputSelect = (props: Props) => {
// offset top of select to place selected item under the cursor // offset top of select to place selected item under the cursor
if (selectedValueIndex !== -1) { if (selectedValueIndex !== -1) {
props.style.top = `-${(selectedValueIndex + 1) * 32}px`; props.style.top = `-${offset + 32}px`;
} }
return ( return (
<Positioner {...props}> <Positioner {...props}>
<Background <Background
dir="auto" dir="auto"
ref={contentRef}
topAnchor={topAnchor} topAnchor={topAnchor}
rightAnchor={rightAnchor} rightAnchor={rightAnchor}
style={ style={
@ -144,6 +171,11 @@ const InputSelect = (props: Props) => {
value={option.value} value={option.value}
key={option.value} key={option.value}
animating={select.animating} animating={select.animating}
ref={
select.selectedValue === option.value
? selectedRef
: undefined
}
> >
{select.selectedValue !== undefined && ( {select.selectedValue !== undefined && (
<> <>