diff --git a/app/components/InputSelect.tsx b/app/components/InputSelect.tsx index 7bbf30702..200c07700 100644 --- a/app/components/InputSelect.tsx +++ b/app/components/InputSelect.tsx @@ -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(value); - const contentRef = React.useRef(null); const selectedRef = React.useRef(null); const buttonRef = React.useRef(null); - const [offset, setOffset] = React.useState(0); + const contentRef = React.useRef(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 = {label}; 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 ( { {note} )} - {select.visible && } + {select.visible && isMobile && } ); }; diff --git a/app/hooks/useMenuHeight.ts b/app/hooks/useMenuHeight.ts index 77f6feb0f..2d544eb66 100644 --- a/app/hooks/useMenuHeight.ts +++ b/app/hooks/useMenuHeight.ts @@ -4,25 +4,24 @@ import useWindowSize from "~/hooks/useWindowSize"; const useMenuHeight = ( visible: void | boolean, - unstable_disclosureRef?: React.RefObject + unstable_disclosureRef?: React.RefObject, + margin = 8 ) => { const [maxHeight, setMaxHeight] = React.useState(); 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; };