mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge pull request #1443 from blockscout/search-focus-test
Search focus test
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Box, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter } from '@chakra-ui/react';
|
||||
import { Box, Portal, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, PopoverFooter, useOutsideClick } from '@chakra-ui/react';
|
||||
import _debounce from 'lodash/debounce';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { FormEvent, FocusEvent } from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import React from 'react';
|
||||
import { Element } from 'react-scroll';
|
||||
|
||||
@@ -59,13 +59,15 @@ const SearchBar = ({ isHomepage }: Props) => {
|
||||
inputRef.current?.querySelector('input')?.blur();
|
||||
}, [ onClose ]);
|
||||
|
||||
const handleBlur = React.useCallback((event: FocusEvent<HTMLFormElement>) => {
|
||||
const isFocusInMenu = menuRef.current?.contains(event.relatedTarget);
|
||||
const isFocusInInput = inputRef.current?.contains(event.relatedTarget);
|
||||
if (!isFocusInMenu && !isFocusInInput) {
|
||||
onClose();
|
||||
const handleOutsideClick = React.useCallback((event: Event) => {
|
||||
const isFocusInInput = inputRef.current?.contains(event.target as Node);
|
||||
|
||||
if (!isFocusInInput) {
|
||||
handelHide();
|
||||
}
|
||||
}, [ onClose ]);
|
||||
}, [ handelHide ]);
|
||||
|
||||
useOutsideClick({ ref: menuRef, handler: handleOutsideClick });
|
||||
|
||||
const handleClear = React.useCallback(() => {
|
||||
handleSearchTermChange('');
|
||||
@@ -118,53 +120,54 @@ const SearchBar = ({ isHomepage }: Props) => {
|
||||
onChange={ handleSearchTermChange }
|
||||
onSubmit={ handleSubmit }
|
||||
onFocus={ handleFocus }
|
||||
onBlur={ handleBlur }
|
||||
onHide={ handelHide }
|
||||
onClear={ handleClear }
|
||||
isHomepage={ isHomepage }
|
||||
value={ searchTerm }
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
w={ `${ menuWidth.current }px` }
|
||||
ref={ menuRef }
|
||||
>
|
||||
<PopoverBody
|
||||
p={ 0 }
|
||||
color="chakra-body-text"
|
||||
<Portal>
|
||||
<PopoverContent
|
||||
w={ `${ menuWidth.current }px` }
|
||||
ref={ menuRef }
|
||||
>
|
||||
<Box
|
||||
maxH="50vh"
|
||||
overflowY="auto"
|
||||
id={ SCROLL_CONTAINER_ID }
|
||||
ref={ scrollRef }
|
||||
as={ Element }
|
||||
px={ 4 }
|
||||
<PopoverBody
|
||||
p={ 0 }
|
||||
color="chakra-body-text"
|
||||
>
|
||||
{ searchTerm.trim().length === 0 && recentSearchKeywords.length > 0 && (
|
||||
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
|
||||
) }
|
||||
{ searchTerm.trim().length > 0 && (
|
||||
<SearchBarSuggest
|
||||
query={ query }
|
||||
searchTerm={ debouncedSearchTerm }
|
||||
onItemClick={ handleItemClick }
|
||||
containerId={ SCROLL_CONTAINER_ID }
|
||||
/>
|
||||
) }
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
{ searchTerm.trim().length > 0 && query.data && query.data.length >= 50 && (
|
||||
<PopoverFooter>
|
||||
<LinkInternal
|
||||
href={ route({ pathname: '/search-results', query: { q: searchTerm } }) }
|
||||
fontSize="sm"
|
||||
<Box
|
||||
maxH="50vh"
|
||||
overflowY="auto"
|
||||
id={ SCROLL_CONTAINER_ID }
|
||||
ref={ scrollRef }
|
||||
as={ Element }
|
||||
px={ 4 }
|
||||
>
|
||||
{ searchTerm.trim().length === 0 && recentSearchKeywords.length > 0 && (
|
||||
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
|
||||
) }
|
||||
{ searchTerm.trim().length > 0 && (
|
||||
<SearchBarSuggest
|
||||
query={ query }
|
||||
searchTerm={ debouncedSearchTerm }
|
||||
onItemClick={ handleItemClick }
|
||||
containerId={ SCROLL_CONTAINER_ID }
|
||||
/>
|
||||
) }
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
{ searchTerm.trim().length > 0 && query.data && query.data.length >= 50 && (
|
||||
<PopoverFooter>
|
||||
<LinkInternal
|
||||
href={ route({ pathname: '/search-results', query: { q: searchTerm } }) }
|
||||
fontSize="sm"
|
||||
>
|
||||
View all results
|
||||
</LinkInternal>
|
||||
</PopoverFooter>
|
||||
) }
|
||||
</PopoverContent>
|
||||
</LinkInternal>
|
||||
</PopoverFooter>
|
||||
) }
|
||||
</PopoverContent>
|
||||
</Portal>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,25 +20,33 @@ interface Props {
|
||||
}
|
||||
|
||||
const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHide, onClear, value }: Props, ref: React.ForwardedRef<HTMLFormElement>) => {
|
||||
const innerRef = React.useRef<HTMLFormElement>(null);
|
||||
React.useImperativeHandle(ref, () => innerRef.current as HTMLFormElement, []);
|
||||
const [ isSticky, setIsSticky ] = React.useState(false);
|
||||
const scrollDirection = useScrollDirection();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const handleScroll = React.useCallback(() => {
|
||||
const TOP_BAR_HEIGHT = 36;
|
||||
if (window.pageYOffset >= TOP_BAR_HEIGHT) {
|
||||
setIsSticky(true);
|
||||
} else {
|
||||
setIsSticky(false);
|
||||
if (!isHomepage) {
|
||||
if (window.scrollY >= TOP_BAR_HEIGHT) {
|
||||
setIsSticky(true);
|
||||
} else {
|
||||
setIsSticky(false);
|
||||
}
|
||||
}
|
||||
}, [ ]);
|
||||
const clientRect = isMobile && innerRef?.current?.getBoundingClientRect();
|
||||
if (clientRect && clientRect.y < TOP_BAR_HEIGHT) {
|
||||
onHide?.();
|
||||
}
|
||||
}, [ isMobile, onHide, isHomepage ]);
|
||||
|
||||
const handleChange = React.useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(event.target.value);
|
||||
}, [ onChange ]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isMobile || isHomepage) {
|
||||
if (!isMobile) {
|
||||
return;
|
||||
}
|
||||
const throttledHandleScroll = throttle(handleScroll, 300);
|
||||
@@ -48,22 +56,14 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid
|
||||
return () => {
|
||||
window.removeEventListener('scroll', throttledHandleScroll);
|
||||
};
|
||||
// replicate componentDidMount
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ isMobile ]);
|
||||
}, [ isMobile, handleScroll ]);
|
||||
|
||||
const bgColor = useColorModeValue('white', 'black');
|
||||
const transformMobile = scrollDirection !== 'down' ? 'translateY(0)' : 'translateY(-100%)';
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isMobile && scrollDirection === 'down') {
|
||||
onHide?.();
|
||||
}
|
||||
}, [ scrollDirection, onHide, isMobile ]);
|
||||
|
||||
return (
|
||||
<chakra.form
|
||||
ref={ ref }
|
||||
ref={ innerRef }
|
||||
noValidate
|
||||
onSubmit={ onSubmit }
|
||||
onBlur={ onBlur }
|
||||
|
||||
Reference in New Issue
Block a user