Commit 2fd367c4 authored by isstuev's avatar isstuev

recent keywords

parent 7098b5cf
import { uniq } from 'lodash';
import isBrowser from './isBrowser';
const RECENT_KEYWORDS_LS_KEY = 'recent_search_keywords';
const MAX_KEYWORDS_NUMBER = 10;
const parseKeywordsArray = (keywordsStr: string) => {
if (!keywordsStr) {
return [];
}
try {
const parsedResult = JSON.parse(keywordsStr);
if (Array.isArray(parsedResult)) {
return parsedResult;
}
return [];
} catch (error) {
return [];
}
};
export function saveToRecentKeywords(value: string) {
if (!value) {
return;
}
const keywordsArr = getRecentSearchKeywords();
const result = uniq([ value, ...keywordsArr ]).slice(0, MAX_KEYWORDS_NUMBER - 1);
window.localStorage.setItem(RECENT_KEYWORDS_LS_KEY, JSON.stringify(result));
}
export function getRecentSearchKeywords(input?: string) {
if (!isBrowser()) {
return [];
}
const keywordsStr = window.localStorage.getItem(RECENT_KEYWORDS_LS_KEY) || '';
const keywordsArr = parseKeywordsArray(keywordsStr) as Array<string>;
if (!input) {
return keywordsArr;
}
return keywordsArr.filter(kw => kw.includes(input));
}
export function removeRecentSearchKeyword(value: string) {
const keywordsArr = getRecentSearchKeywords();
const result = keywordsArr.filter(kw => kw !== value);
window.localStorage.setItem(RECENT_KEYWORDS_LS_KEY, JSON.stringify(result));
}
export function clearRecentSearchKeywords() {
window.localStorage.setItem(RECENT_KEYWORDS_LS_KEY, '');
}
...@@ -11,7 +11,7 @@ import { Controller } from 'react-hook-form'; ...@@ -11,7 +11,7 @@ import { Controller } from 'react-hook-form';
import type { MethodFormFields } from './types'; import type { MethodFormFields } from './types';
import type { SmartContractMethodArgType } from 'types/api/contract'; import type { SmartContractMethodArgType } from 'types/api/contract';
import InputClearButton from 'ui/shared/InputClearButton'; import ClearButton from 'ui/shared/ClearButton';
import ContractMethodFieldZeroes from './ContractMethodFieldZeroes'; import ContractMethodFieldZeroes from './ContractMethodFieldZeroes';
import { addZeroesAllowed } from './utils'; import { addZeroesAllowed } from './utils';
...@@ -63,7 +63,7 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue, ...@@ -63,7 +63,7 @@ const ContractMethodField = ({ control, name, valueType, placeholder, setValue,
paddingRight={ hasZerosControl ? '120px' : '40px' } paddingRight={ hasZerosControl ? '120px' : '40px' }
/> />
<InputRightElement w="auto" right={ 1 }> <InputRightElement w="auto" right={ 1 }>
{ field.value && <InputClearButton onClick={ handleClear } isDisabled={ isDisabled }/> } { field.value && <ClearButton onClick={ handleClear } isDisabled={ isDisabled }/> }
{ hasZerosControl && <ContractMethodFieldZeroes onClick={ handleAddZeroesClick } isDisabled={ isDisabled }/> } { hasZerosControl && <ContractMethodFieldZeroes onClick={ handleAddZeroesClick } isDisabled={ isDisabled }/> }
</InputRightElement> </InputRightElement>
</InputGroup> </InputGroup>
......
...@@ -4,6 +4,7 @@ import type { FormEvent } from 'react'; ...@@ -4,6 +4,7 @@ import type { FormEvent } from 'react';
import React from 'react'; import React from 'react';
import SearchResultListItem from 'ui/searchResults/SearchResultListItem'; import SearchResultListItem from 'ui/searchResults/SearchResultListItem';
import SearchResultsInput from 'ui/searchResults/SearchResultsInput';
import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem'; import SearchResultTableItem from 'ui/searchResults/SearchResultTableItem';
import ActionBar from 'ui/shared/ActionBar'; import ActionBar from 'ui/shared/ActionBar';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
...@@ -13,7 +14,6 @@ import PageTitle from 'ui/shared/Page/PageTitle'; ...@@ -13,7 +14,6 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import Thead from 'ui/shared/TheadSticky'; import Thead from 'ui/shared/TheadSticky';
import Header from 'ui/snippets/header/Header'; import Header from 'ui/snippets/header/Header';
import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery'; import useSearchQuery from 'ui/snippets/searchBar/useSearchQuery';
const SearchResultsPageContent = () => { const SearchResultsPageContent = () => {
...@@ -128,28 +128,15 @@ const SearchResultsPageContent = () => { ...@@ -128,28 +128,15 @@ const SearchResultsPageContent = () => {
); );
})(); })();
const inputRef = React.useRef<HTMLFormElement>(null);
const handelHide = React.useCallback(() => {
inputRef.current?.querySelector('input')?.blur();
}, [ ]);
const handleClear = React.useCallback(() => {
handleSearchTermChange('');
inputRef.current?.querySelector('input')?.focus();
}, [ handleSearchTermChange ]);
const renderSearchBar = React.useCallback(() => { const renderSearchBar = React.useCallback(() => {
return ( return (
<SearchBarInput <SearchResultsInput
ref={ inputRef } searchTerm={ searchTerm }
onChange={ handleSearchTermChange } handleSubmit={ handleSubmit }
onSubmit={ handleSubmit } handleSearchTermChange={ handleSearchTermChange }
value={ searchTerm }
onHide={ handelHide }
onClear={ handleClear }
/> />
); );
}, [ handleSearchTermChange, handleSubmit, searchTerm, handelHide, handleClear ]); }, [ handleSearchTermChange, handleSubmit, searchTerm ]);
const renderHeader = React.useCallback(() => { const renderHeader = React.useCallback(() => {
return <Header renderSearchBar={ renderSearchBar }/>; return <Header renderSearchBar={ renderSearchBar }/>;
......
...@@ -9,6 +9,7 @@ import labelIcon from 'icons/publictags.svg'; ...@@ -9,6 +9,7 @@ import labelIcon from 'icons/publictags.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import trimTokenSymbol from 'lib/token/trimTokenSymbol'; import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
...@@ -27,6 +28,7 @@ interface Props { ...@@ -27,6 +28,7 @@ interface Props {
const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
const handleLinkClick = React.useCallback((e: React.MouseEvent<HTMLAnchorElement>) => { const handleLinkClick = React.useCallback((e: React.MouseEvent<HTMLAnchorElement>) => {
saveToRecentKeywords(searchTerm);
mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, { mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, {
'Search query': searchTerm, 'Search query': searchTerm,
'Source page type': 'Search results', 'Source page type': 'Search results',
...@@ -152,7 +154,11 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => { ...@@ -152,7 +154,11 @@ const SearchResultListItem = ({ data, searchTerm, isLoading }: Props) => {
<span>{ data.type }</span> <span>{ data.type }</span>
</Skeleton> </Skeleton>
</Flex> </Flex>
{ secondRow } { Boolean(secondRow) && (
<Box w="100%" overflow="hidden" whiteSpace="nowrap">
{ secondRow }
</Box>
) }
</ListItemMobile> </ListItemMobile>
); );
}; };
......
...@@ -9,6 +9,7 @@ import labelIcon from 'icons/publictags.svg'; ...@@ -9,6 +9,7 @@ import labelIcon from 'icons/publictags.svg';
import txIcon from 'icons/transactions.svg'; import txIcon from 'icons/transactions.svg';
import highlightText from 'lib/highlightText'; import highlightText from 'lib/highlightText';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { saveToRecentKeywords } from 'lib/recentSearchKeywords';
import trimTokenSymbol from 'lib/token/trimTokenSymbol'; import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import Address from 'ui/shared/address/Address'; import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
...@@ -26,6 +27,7 @@ interface Props { ...@@ -26,6 +27,7 @@ interface Props {
const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => { const SearchResultTableItem = ({ data, searchTerm, isLoading }: Props) => {
const handleLinkClick = React.useCallback((e: React.MouseEvent<HTMLAnchorElement>) => { const handleLinkClick = React.useCallback((e: React.MouseEvent<HTMLAnchorElement>) => {
saveToRecentKeywords(searchTerm);
mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, { mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, {
'Search query': searchTerm, 'Search query': searchTerm,
'Source page type': 'Search results', 'Source page type': 'Search results',
......
import { Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce';
import type { FormEvent, FocusEvent } from 'react';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import { getRecentSearchKeywords } from 'lib/recentSearchKeywords';
import SearchBarInput from 'ui/snippets/searchBar/SearchBarInput';
import SearchBarRecentKeywords from 'ui/snippets/searchBar/SearchBarRecentKeywords';
type Props = {
searchTerm: string;
handleSubmit: (event: FormEvent<HTMLFormElement>) => void;
handleSearchTermChange: (value: string) => void;
}
const SearchResultsInput = ({ searchTerm, handleSubmit, handleSearchTermChange }: Props) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const inputRef = React.useRef<HTMLFormElement>(null);
const menuRef = React.useRef<HTMLDivElement>(null);
const menuWidth = React.useRef<number>(0);
const isMobile = useIsMobile();
const recentSearchKeywords = getRecentSearchKeywords();
const handleFocus = React.useCallback(() => {
onOpen();
}, [ onOpen ]);
const handelHide = React.useCallback(() => {
onClose();
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();
}
}, [ onClose ]);
const handleClear = React.useCallback(() => {
handleSearchTermChange('');
inputRef.current?.querySelector('input')?.focus();
}, [ handleSearchTermChange ]);
const menuPaddingX = isMobile ? 32 : 0;
const calculateMenuWidth = React.useCallback(() => {
menuWidth.current = (inputRef.current?.getBoundingClientRect().width || 0) - menuPaddingX;
}, [ menuPaddingX ]);
React.useEffect(() => {
const inputEl = inputRef.current;
if (!inputEl) {
return;
}
calculateMenuWidth();
const resizeHandler = _debounce(calculateMenuWidth, 200);
const resizeObserver = new ResizeObserver(resizeHandler);
resizeObserver.observe(inputRef.current);
return function cleanup() {
resizeObserver.unobserve(inputEl);
};
}, [ calculateMenuWidth ]);
return (
<Popover
isOpen={ isOpen && recentSearchKeywords.length > 0 && searchTerm.trim().length === 0 }
autoFocus={ false }
onClose={ onClose }
placement="bottom-start"
offset={ isMobile ? [ 16, -12 ] : undefined }
isLazy
>
<PopoverTrigger>
<SearchBarInput
ref={ inputRef }
onChange={ handleSearchTermChange }
onSubmit={ handleSubmit }
onFocus={ handleFocus }
onBlur={ handleBlur }
onHide={ handelHide }
onClear={ handleClear }
value={ searchTerm }
/>
</PopoverTrigger>
<PopoverContent w={ `${ menuWidth.current }px` } maxH={{ base: '300px', lg: '500px' }} overflowY="scroll" ref={ menuRef }>
<PopoverBody py={ 6 }>
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default SearchResultsInput;
...@@ -4,12 +4,12 @@ import React from 'react'; ...@@ -4,12 +4,12 @@ import React from 'react';
import errorIcon from 'icons/status/error.svg'; import errorIcon from 'icons/status/error.svg';
interface Props { interface Props {
onClick: () => void; onClick: (e: React.SyntheticEvent) => void;
isDisabled?: boolean; isDisabled?: boolean;
className?: string; className?: string;
} }
const InputClearButton = ({ onClick, isDisabled, className }: Props) => { const ClearButton = ({ onClick, isDisabled, className }: Props) => {
const iconColor = useColorModeValue('gray.300', 'gray.600'); const iconColor = useColorModeValue('gray.300', 'gray.600');
const iconColorHover = useColorModeValue('gray.200', 'gray.500'); const iconColorHover = useColorModeValue('gray.200', 'gray.500');
...@@ -28,4 +28,4 @@ const InputClearButton = ({ onClick, isDisabled, className }: Props) => { ...@@ -28,4 +28,4 @@ const InputClearButton = ({ onClick, isDisabled, className }: Props) => {
); );
}; };
export default chakra(InputClearButton); export default chakra(ClearButton);
...@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react'; ...@@ -3,7 +3,7 @@ import type { ChangeEvent } from 'react';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import searchIcon from 'icons/search.svg'; import searchIcon from 'icons/search.svg';
import InputClearButton from 'ui/shared/InputClearButton'; import ClearButton from 'ui/shared/ClearButton';
type Props = { type Props = {
onChange: (searchTerm: string) => void; onChange: (searchTerm: string) => void;
...@@ -60,7 +60,7 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder, initialVal ...@@ -60,7 +60,7 @@ const FilterInput = ({ onChange, className, size = 'sm', placeholder, initialVal
{ filterQuery ? ( { filterQuery ? (
<InputRightElement> <InputRightElement>
<InputClearButton onClick={ handleFilterQueryClear }/> <ClearButton onClick={ handleFilterQueryClear }/>
</InputRightElement> </InputRightElement>
) : null } ) : null }
</InputGroup> </InputGroup>
......
...@@ -196,3 +196,15 @@ test('search with simple match', async({ mount, page }) => { ...@@ -196,3 +196,15 @@ test('search with simple match', async({ mount, page }) => {
const links = page.getByText(searchMock.tx1.tx_hash); const links = page.getByText(searchMock.tx1.tx_hash);
await expect(links).toHaveCount(1); await expect(links).toHaveCount(1);
}); });
test('recent keywords suggest +@mobile', async({ mount, page }) => {
await mount(
<TestApp>
<SearchBar/>
</TestApp>,
);
// eslint-disable-next-line max-len
await page.evaluate(() => window.localStorage.setItem('recent_search_keywords', '["10x1d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","0x1d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","usd","bob"]'));
await page.getByPlaceholder(/search/i).click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 500 } });
});
...@@ -7,8 +7,10 @@ import React from 'react'; ...@@ -7,8 +7,10 @@ import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
import { getRecentSearchKeywords, saveToRecentKeywords } from 'lib/recentSearchKeywords';
import SearchBarInput from './SearchBarInput'; import SearchBarInput from './SearchBarInput';
import SearchBarRecentKeywords from './SearchBarRecentKeywords';
import SearchBarSuggest from './SearchBarSuggest'; import SearchBarSuggest from './SearchBarSuggest';
import useSearchQuery from './useSearchQuery'; import useSearchQuery from './useSearchQuery';
...@@ -24,6 +26,8 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -24,6 +26,8 @@ const SearchBar = ({ isHomepage }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const router = useRouter(); const router = useRouter();
const recentSearchKeywords = getRecentSearchKeywords();
const { searchTerm, handleSearchTermChange, query, pathname, redirectCheckQuery } = useSearchQuery(); const { searchTerm, handleSearchTermChange, query, pathname, redirectCheckQuery } = useSearchQuery();
const handleSubmit = React.useCallback((event: FormEvent<HTMLFormElement>) => { const handleSubmit = React.useCallback((event: FormEvent<HTMLFormElement>) => {
...@@ -35,6 +39,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -35,6 +39,7 @@ const SearchBar = ({ isHomepage }: Props) => {
'Source page type': mixpanel.getPageType(pathname), 'Source page type': mixpanel.getPageType(pathname),
'Result URL': url, 'Result URL': url,
}); });
saveToRecentKeywords(searchTerm);
router.push({ pathname: '/search-results', query: { q: searchTerm } }, undefined, { shallow: true }); router.push({ pathname: '/search-results', query: { q: searchTerm } }, undefined, { shallow: true });
} }
}, [ searchTerm, pathname, router ]); }, [ searchTerm, pathname, router ]);
...@@ -67,6 +72,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -67,6 +72,7 @@ const SearchBar = ({ isHomepage }: Props) => {
'Source page type': mixpanel.getPageType(pathname), 'Source page type': mixpanel.getPageType(pathname),
'Result URL': event.currentTarget.href, 'Result URL': event.currentTarget.href,
}); });
saveToRecentKeywords(searchTerm);
onClose(); onClose();
}, [ pathname, searchTerm, onClose ]); }, [ pathname, searchTerm, onClose ]);
...@@ -93,7 +99,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -93,7 +99,7 @@ const SearchBar = ({ isHomepage }: Props) => {
return ( return (
<Popover <Popover
isOpen={ isOpen && searchTerm.trim().length > 0 } isOpen={ isOpen && (searchTerm.trim().length > 0 || recentSearchKeywords.length > 0) }
autoFocus={ false } autoFocus={ false }
onClose={ onClose } onClose={ onClose }
placement="bottom-start" placement="bottom-start"
...@@ -115,7 +121,12 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -115,7 +121,12 @@ const SearchBar = ({ isHomepage }: Props) => {
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w={ `${ menuWidth.current }px` } maxH={{ base: '300px', lg: '500px' }} overflowY="scroll" ref={ menuRef }> <PopoverContent w={ `${ menuWidth.current }px` } maxH={{ base: '300px', lg: '500px' }} overflowY="scroll" ref={ menuRef }>
<PopoverBody py={ 6 } sx={ isHomepage ? { mark: { bgColor: 'green.100' } } : {} }> <PopoverBody py={ 6 } sx={ isHomepage ? { mark: { bgColor: 'green.100' } } : {} }>
<SearchBarSuggest query={ query } redirectCheckQuery={ redirectCheckQuery } searchTerm={ searchTerm } onItemClick={ handleItemClick }/> { searchTerm.trim().length === 0 && recentSearchKeywords.length > 0 && (
<SearchBarRecentKeywords onClick={ handleSearchTermChange } onClear={ onClose }/>
) }
{ searchTerm.trim().length > 0 && (
<SearchBarSuggest query={ query } redirectCheckQuery={ redirectCheckQuery } searchTerm={ searchTerm } onItemClick={ handleItemClick }/>
) }
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
......
...@@ -6,7 +6,7 @@ import type { ChangeEvent, FormEvent, FocusEvent } from 'react'; ...@@ -6,7 +6,7 @@ import type { ChangeEvent, FormEvent, FocusEvent } from 'react';
import searchIcon from 'icons/search.svg'; import searchIcon from 'icons/search.svg';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import InputClearButton from 'ui/shared/InputClearButton'; import ClearButton from 'ui/shared/ClearButton';
interface Props { interface Props {
onChange: (value: string) => void; onChange: (value: string) => void;
...@@ -108,7 +108,7 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid ...@@ -108,7 +108,7 @@ const SearchBarInput = ({ onChange, onSubmit, isHomepage, onFocus, onBlur, onHid
/> />
{ value && ( { value && (
<InputRightElement top={{ base: 2, lg: '18px' }} right={ 2 }> <InputRightElement top={{ base: 2, lg: '18px' }} right={ 2 }>
<InputClearButton onClick={ onClear }/> <ClearButton onClick={ onClear }/>
</InputRightElement> </InputRightElement>
) } ) }
</InputGroup> </InputGroup>
......
import { Box, Flex, Link, Text, useColorModeValue } from '@chakra-ui/react';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import { clearRecentSearchKeywords, getRecentSearchKeywords, removeRecentSearchKeyword } from 'lib/recentSearchKeywords';
import TextAd from 'ui/shared/ad/TextAd';
import ClearButton from 'ui/shared/ClearButton';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
type Props = {
onClick: (kw: string) => void;
onClear: () => void;
}
const SearchBarSuggest = ({ onClick, onClear }: Props) => {
const isMobile = useIsMobile();
const bgHoverColor = useColorModeValue('blue.50', 'gray.800');
const [ keywords, setKeywords ] = React.useState<Array<string>>(getRecentSearchKeywords());
const handleClick = React.useCallback((kw: string) => () => {
onClick(kw);
}, [ onClick ]);
const clearKeywords = React.useCallback(() => {
clearRecentSearchKeywords();
onClear();
}, [ onClear ]);
const removeKeyword = React.useCallback((kw: string) => (e: React.SyntheticEvent) => {
e.stopPropagation();
const result = keywords.filter(item => item !== kw);
setKeywords(result);
if (result.length === 0) {
onClear();
}
removeRecentSearchKeyword(kw);
}, [ keywords, onClear ]);
if (keywords.length === 0) {
return null;
}
return (
<>
{ !isMobile && (
<Box pb={ 4 } mb={ 5 } borderColor="divider" borderBottomWidth="1px" _empty={{ display: 'none' }}>
<TextAd/>
</Box>
) }
<Flex mb={ 3 } justifyContent="space-between" fontSize="sm">
<Text fontWeight={ 600 } variant="secondary">Recent</Text>
<Link onClick={ clearKeywords }>Clear all</Link>
</Flex>
{ keywords.map(kw => (
<Box
key={ kw }
py={ 3 }
px={ 1 }
display="flex"
flexDir="column"
rowGap={ 2 }
borderColor="divider"
borderBottomWidth="1px"
_last={{
borderBottomWidth: '0',
}}
_hover={{
bgColor: bgHoverColor,
}}
fontSize="sm"
_first={{
mt: 2,
}}
onClick={ handleClick(kw) }
>
<Flex display="flex" alignItems="center" justifyContent="space-between" cursor="pointer">
<Text fontWeight={ 700 } mr={ 2 } w="calc(100% - 36px)" overflow="hidden" whiteSpace="nowrap" textOverflow="ellipsis">
{ kw.startsWith('0x') ? <HashStringShortenDynamic hash={ kw } isTooltipDisabled/> : kw }
</Text>
<ClearButton onClick={ removeKeyword(kw) }/>
</Flex>
</Box>
)) }
</>
);
};
export default SearchBarSuggest;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment