From b6f62e35fb5234e91f48af71a656cc88a76fb53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Tue, 23 May 2023 23:33:37 +0200 Subject: [PATCH] Remove unneeded padding on the FindChannels screen on Android (#7368) * Remove unneeded padding on the FindChannels screen on Android * Refactor keyboard overlap --- app/components/selected_users/index.tsx | 52 +++---------------- app/hooks/device.ts | 22 +++++++- .../channel_add_members.tsx | 9 ++-- .../create_direct_message.tsx | 9 ++-- .../channel_info_form.tsx | 29 +++-------- app/screens/edit_post/edit_post.tsx | 17 ++---- .../filtered_list/filtered_list.tsx | 6 +-- app/screens/find_channels/index.tsx | 25 ++++++--- .../unfiltered_list/unfiltered_list.tsx | 6 +-- app/screens/invite/invite.tsx | 9 ++-- app/screens/invite/selection.tsx | 28 ++-------- 11 files changed, 78 insertions(+), 134 deletions(-) diff --git a/app/components/selected_users/index.tsx b/app/components/selected_users/index.tsx index 98ff1ee620..983c63b694 100644 --- a/app/components/selected_users/index.tsx +++ b/app/components/selected_users/index.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback, useEffect, useMemo, useState} from 'react'; -import {type LayoutChangeEvent, Platform, ScrollView, useWindowDimensions, View} from 'react-native'; +import {type LayoutChangeEvent, Platform, ScrollView, View} from 'react-native'; import Animated, {useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; @@ -10,7 +10,7 @@ import Button from '@components/button'; import {USER_CHIP_BOTTOM_MARGIN, USER_CHIP_HEIGHT} from '@components/selected_chip'; import Toast from '@components/toast'; import {useTheme} from '@context/theme'; -import {useIsTablet, useKeyboardHeightWithDuration} from '@hooks/device'; +import {useKeyboardHeightWithDuration} from '@hooks/device'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import SelectedUser from './selected_user'; @@ -28,14 +28,9 @@ type Props = { buttonText: string; /** - * the height of the parent container + * the overlap of the keyboard with this list */ - containerHeight?: number; - - /** - * the Y position of the first view in the parent container - */ - modalPosition?: number; + keyboardOverlap?: number; /** * A handler function that will select or deselect a user when clicked on. @@ -145,8 +140,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { export default function SelectedUsers({ buttonIcon, buttonText, - containerHeight = 0, - modalPosition = 0, + keyboardOverlap = 0, onPress, onRemove, selectedIds, @@ -160,15 +154,12 @@ export default function SelectedUsers({ }: Props) { const theme = useTheme(); const style = getStyleFromTheme(theme); - const isTablet = useIsTablet(); const keyboard = useKeyboardHeightWithDuration(); const insets = useSafeAreaInsets(); - const dimensions = useWindowDimensions(); const usersChipsHeight = useSharedValue(0); const [isVisible, setIsVisible] = useState(false); const numberSelectedIds = Object.keys(selectedIds).length; - const bottomSpace = (dimensions.height - containerHeight - modalPosition); const users = useMemo(() => { const u = []; @@ -196,34 +187,6 @@ export default function SelectedUsers({ 0 ), [isVisible]); - const marginBottom = useMemo(() => { - let margin = keyboard.height && Platform.OS === 'ios' ? keyboard.height - insets.bottom : 0; - if (isTablet) { - margin = keyboard.height ? Math.max((keyboard.height - bottomSpace - insets.bottom), 0) : 0; - } - return margin; - }, [keyboard, isTablet, insets.bottom, bottomSpace]); - - const paddingBottom = useMemo(() => { - if (Platform.OS === 'android') { - return TABLET_MARGIN_BOTTOM + insets.bottom; - } - - if (!isVisible) { - return 0; - } - - if (isTablet) { - return TABLET_MARGIN_BOTTOM + insets.bottom; - } - - if (!keyboard.height) { - return insets.bottom; - } - - return TABLET_MARGIN_BOTTOM + insets.bottom; - }, [isTablet, isVisible, insets.bottom, keyboard.height]); - const handlePress = useCallback(() => { onPress(); }, [onPress]); @@ -242,11 +205,10 @@ export default function SelectedUsers({ }); const animatedContainerStyle = useAnimatedStyle(() => ({ - marginBottom: withTiming(marginBottom, {duration: keyboard.duration}), - paddingBottom: withTiming(paddingBottom, {duration: keyboard.duration}), + marginBottom: withTiming(keyboardOverlap + TABLET_MARGIN_BOTTOM, {duration: keyboard.duration}), backgroundColor: isVisible ? theme.centerChannelBg : 'transparent', ...androidMaxHeight, - }), [marginBottom, paddingBottom, keyboard.duration, isVisible, theme.centerChannelBg]); + }), [keyboardOverlap, keyboard.duration, isVisible, theme.centerChannelBg]); const animatedToastStyle = useAnimatedStyle(() => { return { diff --git a/app/hooks/device.ts b/app/hooks/device.ts index 768f09b5bb..1f3e0a5e07 100644 --- a/app/hooks/device.ts +++ b/app/hooks/device.ts @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React, {type RefObject, useEffect, useRef, useState} from 'react'; -import {AppState, Keyboard, NativeEventEmitter, NativeModules, Platform, View} from 'react-native'; +import {AppState, Keyboard, NativeEventEmitter, NativeModules, Platform, useWindowDimensions, View} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import {Device} from '@constants'; @@ -110,7 +110,7 @@ export function useKeyboardHeight(keyboardTracker?: React.RefObject, deps: React.DependencyList = []) { +export function useViewPosition(viewRef: RefObject, deps: React.DependencyList = []) { const [modalPosition, setModalPosition] = useState(0); const isTablet = useIsTablet(); const height = useKeyboardHeight(); @@ -127,3 +127,21 @@ export function useModalPosition(viewRef: RefObject, deps: React.Dependenc return modalPosition; } + +export function useKeyboardOverlap(viewRef: RefObject, containerHeight: number) { + const keyboardHeight = useKeyboardHeight(); + const isTablet = useIsTablet(); + const viewPosition = useViewPosition(viewRef, [containerHeight]); + const dimensions = useWindowDimensions(); + const insets = useSafeAreaInsets(); + + const bottomSpace = (dimensions.height - containerHeight - viewPosition); + const tabletOverlap = Math.max(0, keyboardHeight - bottomSpace); + const phoneOverlap = keyboardHeight || insets.bottom; + const overlap = Platform.select({ + ios: isTablet ? tabletOverlap : phoneOverlap, + default: 0, + }); + + return overlap; +} diff --git a/app/screens/channel_add_members/channel_add_members.tsx b/app/screens/channel_add_members/channel_add_members.tsx index ce8b5add7b..c60efcf15e 100644 --- a/app/screens/channel_add_members/channel_add_members.tsx +++ b/app/screens/channel_add_members/channel_add_members.tsx @@ -17,7 +17,7 @@ import {General} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; -import {useModalPosition} from '@hooks/device'; +import {useKeyboardOverlap} from '@hooks/device'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; import {t} from '@i18n'; import {dismissModal} from '@screens/navigation'; @@ -126,12 +126,12 @@ export default function ChannelAddMembers({ const {formatMessage} = intl; const mainView = useRef(null); - const modalPosition = useModalPosition(mainView); + const [containerHeight, setContainerHeight] = useState(0); + const keyboardOverlap = useKeyboardOverlap(mainView, containerHeight); const [term, setTerm] = useState(''); const [addingMembers, setAddingMembers] = useState(false); const [selectedIds, setSelectedIds] = useState<{[id: string]: UserProfile}>({}); - const [containerHeight, setContainerHeight] = useState(0); const clearSearch = useCallback(() => { setTerm(''); @@ -281,8 +281,7 @@ export default function ChannelAddMembers({ createFilter={createUserFilter} /> (null); - const modalPosition = useModalPosition(mainView); + const [containerHeight, setContainerHeight] = useState(0); + const keyboardOverlap = useKeyboardOverlap(mainView, containerHeight); const [term, setTerm] = useState(''); const [startingConversation, setStartingConversation] = useState(false); const [selectedIds, setSelectedIds] = useState<{[id: string]: UserProfile}>({}); const [showToast, setShowToast] = useState(false); - const [containerHeight, setContainerHeight] = useState(0); const selectedCount = Object.keys(selectedIds).length; const clearSearch = useCallback(() => { @@ -327,8 +327,7 @@ export default function CreateDirectMessage({ createFilter={createUserFilter} /> (); const mainView = useRef(null); - const modalPosition = useModalPosition(mainView); - - const dimensions = useWindowDimensions(); - const isTablet = useIsTablet(); + const [wrapperHeight, setWrapperHeight] = useState(0); + const keyboardOverlap = useKeyboardOverlap(mainView, wrapperHeight); const [propagateValue, shouldProcessEvent] = useInputPropagation(); @@ -133,7 +129,6 @@ export default function ChannelInfoForm({ const [keyboardVisible, setKeyBoardVisible] = useState(false); const [scrollPosition, setScrollPosition] = useState(0); - const [wrapperHeight, setWrapperHeight] = useState(0); const [errorHeight, setErrorHeight] = useState(0); const [displayNameFieldHeight, setDisplayNameFieldHeight] = useState(0); const [makePrivateHeight, setMakePrivateHeight] = useState(0); @@ -228,29 +223,17 @@ export default function ChannelInfoForm({ setWrapperHeight(e.nativeEvent.layout.height); }, []); - const bottomSpace = (dimensions.height - wrapperHeight - modalPosition); const otherElementsSize = LIST_PADDING + errorHeight + (showSelector ? makePrivateHeight + MAKE_PRIVATE_MARGIN_BOTTOM : 0) + (displayHeaderOnly ? 0 : purposeFieldHeight + FIELD_MARGIN_BOTTOM + displayNameFieldHeight + FIELD_MARGIN_BOTTOM); - const keyboardOverlap = Platform.select({ - ios: isTablet ? - Math.max(0, keyboardHeight - bottomSpace) : - keyboardHeight || insets.bottom, - default: 0}); const workingSpace = wrapperHeight - keyboardOverlap; const spaceOnTop = otherElementsSize - scrollPosition - AUTOCOMPLETE_ADJUST; const spaceOnBottom = (workingSpace + scrollPosition) - (otherElementsSize + headerFieldHeight + BOTTOM_AUTOCOMPLETE_SEPARATION); - const insetsAdjust = keyboardHeight || insets.bottom; - const keyboardAdjust = Platform.select({ - ios: isTablet ? - keyboardOverlap : - insetsAdjust, - default: 0, - }); + const autocompletePosition = spaceOnBottom > spaceOnTop ? (otherElementsSize + headerFieldHeight) - scrollPosition : - (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardAdjust) - otherElementsSize; + (workingSpace + scrollPosition + AUTOCOMPLETE_ADJUST + keyboardOverlap) - otherElementsSize; const autocompleteAvailableSpace = spaceOnBottom > spaceOnTop ? spaceOnBottom : spaceOnTop; const growDown = spaceOnBottom > spaceOnTop; diff --git a/app/screens/edit_post/edit_post.tsx b/app/screens/edit_post/edit_post.tsx index 18880fd44c..a0f07d9536 100644 --- a/app/screens/edit_post/edit_post.tsx +++ b/app/screens/edit_post/edit_post.tsx @@ -3,8 +3,7 @@ import React, {useCallback, useEffect, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; -import {Alert, Keyboard, type LayoutChangeEvent, Platform, SafeAreaView, useWindowDimensions, View} from 'react-native'; -import {useSafeAreaInsets} from 'react-native-safe-area-context'; +import {Alert, Keyboard, type LayoutChangeEvent, Platform, SafeAreaView, View} from 'react-native'; import {deletePost, editPost} from '@actions/remote/post'; import Autocomplete from '@components/autocomplete'; @@ -13,7 +12,7 @@ import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import {useAutocompleteDefaultAnimatedValues} from '@hooks/autocomplete'; -import {useIsTablet, useKeyboardHeight, useModalPosition} from '@hooks/device'; +import {useKeyboardOverlap} from '@hooks/device'; import useDidUpdate from '@hooks/did_update'; import {useInputPropagation} from '@hooks/input'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; @@ -66,17 +65,12 @@ const EditPost = ({componentId, maxPostSize, post, closeButtonId, hasFilesAttach const [propagateValue, shouldProcessEvent] = useInputPropagation(); const mainView = useRef(null); - const modalPosition = useModalPosition(mainView); const postInputRef = useRef(null); const theme = useTheme(); const intl = useIntl(); const serverUrl = useServerUrl(); const styles = getStyleSheet(theme); - const keyboardHeight = useKeyboardHeight(); - const insets = useSafeAreaInsets(); - const dimensions = useWindowDimensions(); - const isTablet = useIsTablet(); useEffect(() => { toggleSaveButton(false); @@ -204,11 +198,8 @@ const EditPost = ({componentId, maxPostSize, post, closeButtonId, hasFilesAttach useNavButtonPressed(closeButtonId, componentId, onClose, []); useAndroidHardwareBackHandler(componentId, onClose); - const bottomSpace = (dimensions.height - containerHeight - modalPosition); - const autocompletePosition = Platform.select({ - ios: isTablet ? Math.max(0, keyboardHeight - bottomSpace) : keyboardHeight || insets.bottom, - default: 0, - }) + AUTOCOMPLETE_SEPARATION; + const overlap = useKeyboardOverlap(mainView, containerHeight); + const autocompletePosition = overlap + AUTOCOMPLETE_SEPARATION; const autocompleteAvailableSpace = containerHeight - autocompletePosition; const [animatedAutocompletePosition, animatedAutocompleteAvailableSpace] = useAutocompleteDefaultAnimatedValues(autocompletePosition, autocompleteAvailableSpace); diff --git a/app/screens/find_channels/filtered_list/filtered_list.tsx b/app/screens/find_channels/filtered_list/filtered_list.tsx index c8c1f18273..d441a2a852 100644 --- a/app/screens/find_channels/filtered_list/filtered_list.tsx +++ b/app/screens/find_channels/filtered_list/filtered_list.tsx @@ -38,7 +38,7 @@ type Props = { channelsMatchStart: ChannelModel[]; currentTeamId: string; isCRTEnabled: boolean; - keyboardHeight: number; + keyboardOverlap: number; loading: boolean; onLoading: (loading: boolean) => void; restrictDirectMessage: boolean; @@ -75,7 +75,7 @@ const sortByUserOrChannel = (locale: string, teamm const FilteredList = ({ archivedChannels, close, channelsMatch, channelsMatchStart, currentTeamId, - isCRTEnabled, keyboardHeight, loading, onLoading, restrictDirectMessage, showTeamName, + isCRTEnabled, keyboardOverlap, loading, onLoading, restrictDirectMessage, showTeamName, teamIds, teammateDisplayNameSetting, term, usersMatch, usersMatchStart, testID, }: Props) => { const bounce = useRef void>>(); @@ -83,7 +83,7 @@ const FilteredList = ({ const serverUrl = useServerUrl(); const theme = useTheme(); const {locale, formatMessage} = useIntl(); - const flatListStyle = useMemo(() => ({flexGrow: 1, paddingBottom: keyboardHeight}), [keyboardHeight]); + const flatListStyle = useMemo(() => ({flexGrow: 1, paddingBottom: keyboardOverlap}), [keyboardOverlap]); const [remoteChannels, setRemoteChannels] = useState({archived: [], startWith: [], matches: []}); const totalLocalResults = channelsMatchStart.length + channelsMatch.length + usersMatchStart.length; diff --git a/app/screens/find_channels/index.tsx b/app/screens/find_channels/index.tsx index 37e5176c24..aae3f173a0 100644 --- a/app/screens/find_channels/index.tsx +++ b/app/screens/find_channels/index.tsx @@ -1,13 +1,13 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useMemo, useState} from 'react'; -import {Keyboard, View} from 'react-native'; +import React, {useCallback, useMemo, useRef, useState} from 'react'; +import {Keyboard, type LayoutChangeEvent, View} from 'react-native'; import SearchBar from '@components/search'; import {useTheme} from '@context/theme'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; -import {useKeyboardHeight} from '@hooks/device'; +import {useKeyboardOverlap} from '@hooks/device'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; import {dismissModal} from '@screens/navigation'; import {changeOpacity, getKeyboardAppearanceFromTheme, makeStyleSheetFromTheme} from '@utils/theme'; @@ -48,7 +48,10 @@ const FindChannels = ({closeButtonId, componentId}: Props) => { const [loading, setLoading] = useState(false); const styles = getStyleSheet(theme); const color = useMemo(() => changeOpacity(theme.centerChannelColor, 0.72), [theme]); - const keyboardHeight = useKeyboardHeight(); + const listView = useRef(null); + + const [containerHeight, setContainerHeight] = useState(0); + const overlap = useKeyboardOverlap(listView, containerHeight); const cancelButtonProps = useMemo(() => ({ color, @@ -57,6 +60,10 @@ const FindChannels = ({closeButtonId, componentId}: Props) => { }, }), [color]); + const onLayout = useCallback((e: LayoutChangeEvent) => { + setContainerHeight(e.nativeEvent.layout.height); + }, []); + const close = useCallback(() => { Keyboard.dismiss(); return dismissModal({componentId}); @@ -99,18 +106,22 @@ const FindChannels = ({closeButtonId, componentId}: Props) => { testID='find_channels.search_bar' /> {term === '' && } - + {term === '' && } {Boolean(term) && Promise; - keyboardHeight: number; + keyboardOverlap: number; recentChannels: ChannelModel[]; showTeamName: boolean; testID?: string; @@ -46,11 +46,11 @@ const buildSections = (recentChannels: ChannelModel[]) => { return sections; }; -const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, testID}: Props) => { +const UnfilteredList = ({close, keyboardOverlap, recentChannels, showTeamName, testID}: Props) => { const intl = useIntl(); const serverUrl = useServerUrl(); const [sections, setSections] = useState(buildSections(recentChannels)); - const sectionListStyle = useMemo(() => ({paddingBottom: keyboardHeight}), [keyboardHeight]); + const sectionListStyle = useMemo(() => ({paddingBottom: keyboardOverlap}), [keyboardOverlap]); const onPress = useCallback(async (c: Channel | ChannelModel) => { await close(); diff --git a/app/screens/invite/invite.tsx b/app/screens/invite/invite.tsx index 3c784e7419..fd81ccea49 100644 --- a/app/screens/invite/invite.tsx +++ b/app/screens/invite/invite.tsx @@ -13,7 +13,7 @@ import Loading from '@components/loading'; import {ServerErrors} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; -import {useModalPosition} from '@hooks/device'; +import {useKeyboardOverlap} from '@hooks/device'; import useNavButtonPressed from '@hooks/navigation_button_pressed'; import {dismissModal, setButtons} from '@screens/navigation'; import {isEmail} from '@utils/helpers'; @@ -112,8 +112,10 @@ export default function Invite({ const theme = useTheme(); const styles = getStyleSheet(theme); const serverUrl = useServerUrl(); + const mainView = useRef(null); - const modalPosition = useModalPosition(mainView); + const [wrapperHeight, setWrapperHeight] = useState(0); + const keyboardOverlap = useKeyboardOverlap(mainView, wrapperHeight); const searchTimeoutId = useRef(null); const retryTimeoutId = useRef(null); @@ -123,7 +125,6 @@ export default function Invite({ const [selectedIds, setSelectedIds] = useState<{[id: string]: SearchResult}>({}); const [loading, setLoading] = useState(false); const [result, setResult] = useState(DEFAULT_RESULT); - const [wrapperHeight, setWrapperHeight] = useState(0); const [stage, setStage] = useState(Stage.SELECTION); const [sendError, setSendError] = useState(''); @@ -394,7 +395,7 @@ export default function Invite({ term={term} searchResults={searchResults} selectedIds={selectedIds} - modalPosition={modalPosition} + keyboardOverlap={keyboardOverlap} wrapperHeight={wrapperHeight} loading={loading} onSearchChange={handleSearchChange} diff --git a/app/screens/invite/selection.tsx b/app/screens/invite/selection.tsx index d6065fbb92..b243fb6e8e 100644 --- a/app/screens/invite/selection.tsx +++ b/app/screens/invite/selection.tsx @@ -13,7 +13,6 @@ import { ScrollView, } from 'react-native'; import Animated, {useAnimatedStyle, useDerivedValue} from 'react-native-reanimated'; -import {useSafeAreaInsets} from 'react-native-safe-area-context'; import SelectedChip from '@components/selected_chip'; import SelectedUser from '@components/selected_users/selected_user'; @@ -22,7 +21,7 @@ import UserItem from '@components/user_item'; import {MAX_LIST_HEIGHT, MAX_LIST_TABLET_DIFF} from '@constants/autocomplete'; import {useTheme} from '@context/theme'; import {useAutocompleteDefaultAnimatedValues} from '@hooks/autocomplete'; -import {useIsTablet, useKeyboardHeight} from '@hooks/device'; +import {useIsTablet} from '@hooks/device'; import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme'; import SelectionSearchBar from './selection_search_bar'; @@ -32,7 +31,6 @@ import TextItem, {TextItemType} from './text_item'; import type {SearchResult} from './invite'; const AUTOCOMPLETE_ADJUST = 5; -const KEYBOARD_HEIGHT_ADJUST = 3; const INITIAL_BATCH_TO_RENDER = 15; const SCROLL_EVENT_THROTTLE = 60; @@ -112,7 +110,7 @@ type SelectionProps = { term: string; searchResults: SearchResult[]; selectedIds: {[id: string]: SearchResult}; - modalPosition: number; + keyboardOverlap: number; wrapperHeight: number; loading: boolean; testID: string; @@ -132,7 +130,7 @@ export default function Selection({ term, searchResults, selectedIds, - modalPosition, + keyboardOverlap, wrapperHeight, loading, testID, @@ -144,9 +142,7 @@ export default function Selection({ const theme = useTheme(); const styles = getStyleSheet(theme); const dimensions = useWindowDimensions(); - const insets = useSafeAreaInsets(); const isTablet = useIsTablet(); - const keyboardHeight = useKeyboardHeight(); const [teamBarHeight, setTeamBarHeight] = useState(0); const [searchBarHeight, setSearchBarHeight] = useState(0); @@ -163,26 +159,10 @@ export default function Selection({ onRemoveItem(id); }; - const bottomSpace = dimensions.height - wrapperHeight - modalPosition; const otherElementsSize = teamBarHeight + searchBarHeight; - const insetsAdjust = (keyboardHeight + KEYBOARD_HEIGHT_ADJUST) || insets.bottom; - - const keyboardOverlap = Platform.select({ - ios: isTablet ? ( - Math.max(0, keyboardHeight - bottomSpace) - ) : ( - insetsAdjust - ), - default: 0, - }); - const keyboardAdjust = Platform.select({ - ios: isTablet ? keyboardOverlap : insetsAdjust, - default: 0, - }); - const workingSpace = wrapperHeight - keyboardOverlap; const spaceOnTop = otherElementsSize - AUTOCOMPLETE_ADJUST; - const spaceOnBottom = workingSpace - (otherElementsSize + keyboardAdjust); + const spaceOnBottom = workingSpace - otherElementsSize; const autocompletePosition = spaceOnBottom > spaceOnTop ? ( otherElementsSize ) : (