Commit 29e8a6b8 authored by tom's avatar tom

SPA transition for search bar

parent df59efab
import { Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react'; import { Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure } from '@chakra-ui/react';
import _debounce from 'lodash/debounce'; import _debounce from 'lodash/debounce';
import { useRouter } from 'next/router';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import type { FormEvent, FocusEvent } from 'react'; import type { FormEvent, FocusEvent } from 'react';
import React from 'react'; import React from 'react';
...@@ -21,6 +22,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -21,6 +22,7 @@ const SearchBar = ({ isHomepage }: Props) => {
const menuRef = React.useRef<HTMLDivElement>(null); const menuRef = React.useRef<HTMLDivElement>(null);
const menuWidth = React.useRef<number>(0); const menuWidth = React.useRef<number>(0);
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const router = useRouter();
const { searchTerm, handleSearchTermChange, query, pathname } = useSearchQuery(); const { searchTerm, handleSearchTermChange, query, pathname } = useSearchQuery();
...@@ -33,9 +35,9 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -33,9 +35,9 @@ const SearchBar = ({ isHomepage }: Props) => {
'Source page type': mixpanel.getPageType(pathname), 'Source page type': mixpanel.getPageType(pathname),
'Result URL': url, 'Result URL': url,
}); });
window.location.assign(url); router.push({ pathname: '/search-results', query: { q: searchTerm } }, undefined, { shallow: true });
} }
}, [ searchTerm, pathname ]); }, [ searchTerm, pathname, router ]);
const handleFocus = React.useCallback(() => { const handleFocus = React.useCallback(() => {
onOpen(); onOpen();
...@@ -59,6 +61,15 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -59,6 +61,15 @@ const SearchBar = ({ isHomepage }: Props) => {
inputRef.current?.querySelector('input')?.focus(); inputRef.current?.querySelector('input')?.focus();
}, [ handleSearchTermChange ]); }, [ handleSearchTermChange ]);
const handleItemClick = React.useCallback((event: React.MouseEvent<HTMLAnchorElement>) => {
mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, {
'Search query': searchTerm,
'Source page type': mixpanel.getPageType(pathname),
'Result URL': event.currentTarget.href,
});
onClose();
}, [ pathname, searchTerm, onClose ]);
const menuPaddingX = isMobile && !isHomepage ? 32 : 0; const menuPaddingX = isMobile && !isHomepage ? 32 : 0;
const calculateMenuWidth = React.useCallback(() => { const calculateMenuWidth = React.useCallback(() => {
menuWidth.current = (inputRef.current?.getBoundingClientRect().width || 0) - menuPaddingX; menuWidth.current = (inputRef.current?.getBoundingClientRect().width || 0) - menuPaddingX;
...@@ -104,7 +115,7 @@ const SearchBar = ({ isHomepage }: Props) => { ...@@ -104,7 +115,7 @@ 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 }> <PopoverBody py={ 6 }>
<SearchBarSuggest query={ query } searchTerm={ searchTerm } pathname={ pathname }/> <SearchBarSuggest query={ query } searchTerm={ searchTerm } onItemClick={ handleItemClick }/>
</PopoverBody> </PopoverBody>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
......
import { Text } from '@chakra-ui/react'; import { Text } from '@chakra-ui/react';
import type { UseQueryResult } from '@tanstack/react-query'; import type { UseQueryResult } from '@tanstack/react-query';
import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResult } from 'types/api/search'; import type { SearchResult } from 'types/api/search';
...@@ -17,10 +16,10 @@ interface Props { ...@@ -17,10 +16,10 @@ interface Props {
pagination: PaginationProps; pagination: PaginationProps;
}; };
searchTerm: string; searchTerm: string;
pathname: Route['pathname']; onItemClick: (event: React.MouseEvent<HTMLAnchorElement>) => void;
} }
const SearchBarSuggest = ({ query, searchTerm, pathname }: Props) => { const SearchBarSuggest = ({ query, searchTerm, onItemClick }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const content = (() => { const content = (() => {
...@@ -39,7 +38,7 @@ const SearchBarSuggest = ({ query, searchTerm, pathname }: Props) => { ...@@ -39,7 +38,7 @@ const SearchBarSuggest = ({ query, searchTerm, pathname }: Props) => {
<> <>
<Text fontWeight={ 500 } fontSize="sm">Found <Text fontWeight={ 700 } as="span">{ num }</Text> matching { resultText }</Text> <Text fontWeight={ 500 } fontSize="sm">Found <Text fontWeight={ 700 } as="span">{ num }</Text> matching { resultText }</Text>
{ query.data.items.map((item, index) => { query.data.items.map((item, index) =>
<SearchBarSuggestItem key={ index } data={ item } isMobile={ isMobile } searchTerm={ searchTerm } pathname={ pathname }/>) } <SearchBarSuggestItem key={ index } data={ item } isMobile={ isMobile } searchTerm={ searchTerm } onClick={ onItemClick }/>) }
</> </>
); );
})(); })();
......
import { chakra, Text, Flex, useColorModeValue, Icon, Box } from '@chakra-ui/react'; import { chakra, Text, Flex, useColorModeValue, Icon, Box } from '@chakra-ui/react';
import type { LinkProps as NextLinkProps } from 'next/link';
import NextLink from 'next/link';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import type { Route } from 'nextjs-routes';
import React from 'react'; import React from 'react';
import type { SearchResultItem } from 'types/api/search'; import type { SearchResultItem } from 'types/api/search';
...@@ -8,7 +9,6 @@ import type { SearchResultItem } from 'types/api/search'; ...@@ -8,7 +9,6 @@ import type { SearchResultItem } from 'types/api/search';
import blockIcon from 'icons/block.svg'; import blockIcon from 'icons/block.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 AddressIcon from 'ui/shared/address/AddressIcon'; import AddressIcon from 'ui/shared/address/AddressIcon';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic'; import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import TokenLogo from 'ui/shared/TokenLogo'; import TokenLogo from 'ui/shared/TokenLogo';
...@@ -17,18 +17,10 @@ interface Props { ...@@ -17,18 +17,10 @@ interface Props {
data: SearchResultItem; data: SearchResultItem;
isMobile: boolean | undefined; isMobile: boolean | undefined;
searchTerm: string; searchTerm: string;
pathname: Route['pathname']; onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void;
} }
const SearchBarSuggestItem = ({ data, isMobile, searchTerm, pathname }: Props) => { const SearchBarSuggestItem = ({ data, isMobile, searchTerm, onClick }: Props) => {
const handleClick = React.useCallback((event: React.MouseEvent<HTMLAnchorElement>) => {
mixpanel.logEvent(mixpanel.EventTypes.SEARCH_QUERY, {
'Search query': searchTerm,
'Source page type': mixpanel.getPageType(pathname),
'Result URL': event.currentTarget.href,
});
}, [ searchTerm, pathname ]);
const url = (() => { const url = (() => {
switch (data.type) { switch (data.type) {
...@@ -153,6 +145,7 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm, pathname }: Props) = ...@@ -153,6 +145,7 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm, pathname }: Props) =
})(); })();
return ( return (
<NextLink href={ url as NextLinkProps['href'] } passHref legacyBehavior>
<chakra.a <chakra.a
py={ 3 } py={ 3 }
px={ 1 } px={ 1 }
...@@ -168,17 +161,17 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm, pathname }: Props) = ...@@ -168,17 +161,17 @@ const SearchBarSuggestItem = ({ data, isMobile, searchTerm, pathname }: Props) =
bgColor: useColorModeValue('blue.50', 'gray.800'), bgColor: useColorModeValue('blue.50', 'gray.800'),
}} }}
fontSize="sm" fontSize="sm"
href={ url }
_first={{ _first={{
mt: 2, mt: 2,
}} }}
onClick={ handleClick } onClick={ onClick }
> >
<Flex display="flex" alignItems="center"> <Flex display="flex" alignItems="center">
{ firstRow } { firstRow }
</Flex> </Flex>
{ secondRow } { secondRow }
</chakra.a> </chakra.a>
</NextLink>
); );
}; };
......
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