Fix: Add support of auto positioning for tag's drop-down (#1169)

* Feat: add auto position feature for tag dropdown

* increase buffer space by 50px
This commit is contained in:
Shailesh Parmar 2021-11-12 22:24:40 +05:30 committed by GitHub
parent 055f629cab
commit 04f1903481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 3 deletions

View File

@ -16,9 +16,11 @@
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { isNil, lowerCase } from 'lodash'; import { isNil, isUndefined, lowerCase } from 'lodash';
import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
import { useWindowDimensions } from '../../hooks/useWindowDimensions';
import { getCountBadge } from '../../utils/CommonUtils'; import { getCountBadge } from '../../utils/CommonUtils';
import { getTopPosition } from '../../utils/DropDownUtils';
import { DropDownListItem, DropDownListProp } from './types'; import { DropDownListItem, DropDownListProp } from './types';
const DropDownList: FunctionComponent<DropDownListProp> = ({ const DropDownList: FunctionComponent<DropDownListProp> = ({
@ -30,10 +32,15 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
value, value,
onSelect, onSelect,
groupType = 'label', groupType = 'label',
domPosition,
}: DropDownListProp) => { }: DropDownListProp) => {
const { height: windowHeight } = useWindowDimensions();
const isMounted = useRef<boolean>(false); const isMounted = useRef<boolean>(false);
const [searchedList, setSearchedList] = useState(dropDownList); const [searchedList, setSearchedList] = useState(dropDownList);
const [searchText, setSearchText] = useState(searchString); const [searchText, setSearchText] = useState(searchString);
const [dropDownPosition, setDropDownPosition] = useState<
{ bottom: string } | {}
>({});
const setCurrentTabOnMount = () => { const setCurrentTabOnMount = () => {
const selectedItem = dropDownList.find((l) => l.value === value); const selectedItem = dropDownList.find((l) => l.value === value);
@ -109,6 +116,14 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
} }
}, [searchText]); }, [searchText]);
useEffect(() => {
if (!isUndefined(domPosition)) {
setDropDownPosition(
getTopPosition(windowHeight, domPosition.bottom, domPosition.height)
);
}
}, [domPosition, searchText]);
useEffect(() => { useEffect(() => {
setActiveTab(setCurrentTabOnMount()); setActiveTab(setCurrentTabOnMount());
isMounted.current = true; isMounted.current = true;
@ -134,7 +149,8 @@ const DropDownList: FunctionComponent<DropDownListProp> = ({
horzPosRight ? 'dd-horz-right' : 'dd-horz-left' horzPosRight ? 'dd-horz-right' : 'dd-horz-left'
)} )}
data-testid="dropdown-list" data-testid="dropdown-list"
role="menu"> role="menu"
style={dropDownPosition}>
{showSearchBar && ( {showSearchBar && (
<div className="has-search tw-p-4 tw-pb-2"> <div className="has-search tw-p-4 tw-pb-2">
<input <input

View File

@ -53,6 +53,7 @@ export type DropDownListProp = {
) => void; ) => void;
setIsOpen?: (value: boolean) => void; setIsOpen?: (value: boolean) => void;
groupType?: GroupType; groupType?: GroupType;
domPosition?: DOMRect;
}; };
export type DropDownProp = { export type DropDownProp = {

View File

@ -16,7 +16,7 @@
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { capitalize, isEmpty } from 'lodash'; import { capitalize, isEmpty, isNull } from 'lodash';
import { EntityTags } from 'Models'; import { EntityTags } from 'Models';
import React, { FunctionComponent, useEffect, useRef, useState } from 'react'; import React, { FunctionComponent, useEffect, useRef, useState } from 'react';
import { Button } from '../buttons/Button/Button'; import { Button } from '../buttons/Button/Button';
@ -43,6 +43,7 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
const [hasFocus, setFocus] = useState<boolean>(false); const [hasFocus, setFocus] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const node = useRef<HTMLDivElement>(null); const node = useRef<HTMLDivElement>(null);
const [inputDomRect, setInputDomRect] = useState<DOMRect>();
// const [inputWidth, setInputWidth] = useState(INPUT_COLLAPED); // const [inputWidth, setInputWidth] = useState(INPUT_COLLAPED);
// const [inputMinWidth, setInputMinWidth] = useState(INPUT_AUTO); // const [inputMinWidth, setInputMinWidth] = useState(INPUT_AUTO);
@ -64,6 +65,12 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
} }
}; };
useEffect(() => {
if (!isNull(inputRef.current)) {
setInputDomRect(inputRef.current.getBoundingClientRect());
}
}, [newTag]);
const getTagList = () => { const getTagList = () => {
const newTags = tagList const newTags = tagList
.filter((tag) => { .filter((tag) => {
@ -227,6 +234,7 @@ const TagsContainer: FunctionComponent<TagsContainerProps> = ({
{newTag && ( {newTag && (
<DropDownList <DropDownList
horzPosRight horzPosRight
domPosition={inputDomRect}
dropDownList={getTagList()} dropDownList={getTagList()}
searchString={newTag} searchString={newTag}
onSelect={handleTagSelection} onSelect={handleTagSelection}

View File

@ -0,0 +1,28 @@
import { useEffect, useState } from 'react';
function getWindowDimensions() {
const { innerWidth: width, innerHeight: height } = window;
return {
width,
height,
};
}
export function useWindowDimensions() {
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
);
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowDimensions;
}

View File

@ -0,0 +1,12 @@
const BUFFER_VALUE = 200;
const GAP = 10;
export const getTopPosition = (
windowHeight: number,
bottomPosition: number,
elementHeight: number
) => {
const bottomSpace = windowHeight - (bottomPosition + BUFFER_VALUE);
return bottomSpace < 0 ? { bottom: `${elementHeight + GAP}px` } : {};
};