Commit dcc965c0 authored by isstuev's avatar isstuev

menu

parent 428ec75e
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 30 30">
<path fill="currentColor" fill-rule="evenodd" d="M14.688 4.75c-5.482 0-9.938 4.456-9.938 9.938 0 5.481 4.456 9.937 9.938 9.937 5.481 0 9.937-4.456 9.937-9.938 0-5.481-4.456-9.937-9.938-9.937Zm3.499 5.781a14.655 14.655 0 0 0-1.813-3.855 8.151 8.151 0 0 1 5.359 3.855h-3.546Zm-7.219 0H7.644a8.16 8.16 0 0 1 5.094-3.787 14.079 14.079 0 0 0-1.77 3.787Zm5.39 0h-3.56a12.705 12.705 0 0 1 1.78-3.382 12.64 12.64 0 0 1 1.78 3.382ZM6.5 14.687c0-.831.131-1.65.369-2.406h3.692a14.62 14.62 0 0 0-.013 4.737l-3.695.027a7.864 7.864 0 0 1-.353-2.358Zm5.83 2.327c-.3-1.57-.3-3.176 0-4.733h4.496c.3 1.555.3 3.147.028 4.705l-4.524.028Zm6.295-.047a14.364 14.364 0 0 0-.029-4.686h3.91c.238.756.369 1.575.369 2.406 0 .783-.116 1.542-.313 2.252l-3.937.028Zm-2.246 5.73a14.004 14.004 0 0 0 1.843-3.98l3.596-.026a8.21 8.21 0 0 1-5.439 4.005Zm-1.802-.472a12.283 12.283 0 0 1-1.8-3.462l3.613-.026a12.946 12.946 0 0 1-1.813 3.488Zm-1.84.406a8.208 8.208 0 0 1-5.128-3.837l3.328-.027c.394 1.347 1 2.657 1.8 3.864Z" clip-rule="evenodd"/>
</svg>
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#top-accounts_svg__a)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.585 2.973a3.412 3.412 0 0 0-3.412 3.412v17.23a3.412 3.412 0 0 0 3.412 3.412h17.23a.95.95 0 0 0 0-1.9H7.585a1.512 1.512 0 1 1 0-3.023h17.23a.95.95 0 0 0 .934-1.126.954.954 0 0 0 .016-.176V4.662c0-.933-.756-1.689-1.688-1.689H7.585ZM6.073 6.385c0-.835.677-1.512 1.512-1.512h16.28v15.33H7.585a3.4 3.4 0 0 0-1.512.353V6.385Zm8.897 1.4a3.013 3.013 0 1 0 0 6.026 3.013 3.013 0 0 0 0-6.026Zm-1.024 3.013a1.024 1.024 0 1 1 2.048 0 1.024 1.024 0 0 1-2.048 0Zm1.025 3.097c-2.316 0-4.3 1.303-5.319 3.243a.446.446 0 0 0 .395.654h1.281a.446.446 0 0 0 .366-.19 3.972 3.972 0 0 1 3.277-1.718c1.33 0 2.52.685 3.268 1.723a.446.446 0 0 0 .362.185h1.292a.446.446 0 0 0 .393-.659c-1.01-1.866-2.983-3.238-5.315-3.238Z" fill="#4A5568"/>
</g>
<defs>
<clipPath id="top-accounts_svg__a">
<path fill="#fff" transform="translate(3 3)" d="M0 0h24v24H0z"/>
</clipPath>
</defs>
</svg>
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#verified_svg__a)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.4 15a8.4 8.4 0 1 1-16.8 0 8.4 8.4 0 0 1 16.8 0Zm1.6 0c0 5.523-4.477 10-10 10S5 20.523 5 15 9.477 5 15 5s10 4.477 10 10Zm-5.895-3.706a.916.916 0 1 1 1.295 1.295l-6.022 6.022a1.05 1.05 0 0 1-1.485 0l-3.2-3.199a.915.915 0 0 1 1.296-1.295l2.257 2.258a.55.55 0 0 0 .778 0l5.081-5.081Z" fill="#4A5568"/>
</g>
<defs>
<clipPath id="verified_svg__a">
<path fill="#fff" transform="translate(5 5)" d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg>
...@@ -7,14 +7,16 @@ import abiIcon from 'icons/ABI.svg'; ...@@ -7,14 +7,16 @@ import abiIcon from 'icons/ABI.svg';
import apiKeysIcon from 'icons/API.svg'; import apiKeysIcon from 'icons/API.svg';
import appsIcon from 'icons/apps.svg'; import appsIcon from 'icons/apps.svg';
import blocksIcon from 'icons/block.svg'; import blocksIcon from 'icons/block.svg';
import globeIcon from 'icons/globe-b.svg';
// import gearIcon from 'icons/gear.svg'; // import gearIcon from 'icons/gear.svg';
import privateTagIcon from 'icons/privattags.svg'; import privateTagIcon from 'icons/privattags.svg';
import profileIcon from 'icons/profile.svg'; import profileIcon from 'icons/profile.svg';
import publicTagIcon from 'icons/publictags.svg'; import publicTagIcon from 'icons/publictags.svg';
import statsIcon from 'icons/stats.svg'; import statsIcon from 'icons/stats.svg';
import tokensIcon from 'icons/token.svg'; import tokensIcon from 'icons/token.svg';
import topAccountsIcon from 'icons/top-accounts.svg';
import transactionsIcon from 'icons/transactions.svg'; import transactionsIcon from 'icons/transactions.svg';
import walletIcon from 'icons/wallet.svg'; // import verifiedIcon from 'icons/verified.svg';
import watchlistIcon from 'icons/watchlist.svg'; import watchlistIcon from 'icons/watchlist.svg';
import notEmpty from 'lib/notEmpty'; import notEmpty from 'lib/notEmpty';
...@@ -26,8 +28,16 @@ export interface NavItem { ...@@ -26,8 +28,16 @@ export interface NavItem {
isNewUi?: boolean; isNewUi?: boolean;
} }
export interface NavGroupItem {
text: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
isActive?: boolean;
isNewUi?: boolean;
subItems: Array<NavItem>;
}
interface ReturnType { interface ReturnType {
mainNavItems: Array<NavItem>; mainNavItems: Array<NavItem | NavGroupItem>;
accountNavItems: Array<NavItem>; accountNavItems: Array<NavItem>;
profileItem: NavItem; profileItem: NavItem;
} }
...@@ -39,11 +49,23 @@ export default function useNavItems(): ReturnType { ...@@ -39,11 +49,23 @@ export default function useNavItems(): ReturnType {
const pathname = router.pathname; const pathname = router.pathname;
return React.useMemo(() => { return React.useMemo(() => {
const mainNavItems = [ const blockchainNavItems = [
{ text: 'Top accounts', nextRoute: { pathname: '/accounts' as const }, icon: topAccountsIcon, isActive: pathname === '/accounts', isNewUi: true },
{ text: 'Blocks', nextRoute: { pathname: '/blocks' as const }, icon: blocksIcon, isActive: pathname.startsWith('/block'), isNewUi: true }, { text: 'Blocks', nextRoute: { pathname: '/blocks' as const }, icon: blocksIcon, isActive: pathname.startsWith('/block'), isNewUi: true },
{ text: 'Transactions', nextRoute: { pathname: '/txs' as const }, icon: transactionsIcon, isActive: pathname.startsWith('/tx'), isNewUi: true }, { text: 'Transactions', nextRoute: { pathname: '/txs' as const }, icon: transactionsIcon, isActive: pathname.startsWith('/tx'), isNewUi: true },
// eslint-disable-next-line max-len
// { text: 'Verified contracts', nextRoute: { pathname: '/verified_contracts' as const }, icon: verifiedIcon, isActive: pathname === '/verified_contracts', isNewUi: false },
];
const mainNavItems = [
{
text: 'Blockchain',
icon: globeIcon,
isActive: pathname.startsWith('block' || 'tx') || pathname === '/accounts',
isNewUi: true,
subItems: blockchainNavItems,
},
{ text: 'Tokens', nextRoute: { pathname: '/tokens' as const }, icon: tokensIcon, isActive: pathname.startsWith('/token'), isNewUi: true }, { text: 'Tokens', nextRoute: { pathname: '/tokens' as const }, icon: tokensIcon, isActive: pathname.startsWith('/token'), isNewUi: true },
{ text: 'Accounts', nextRoute: { pathname: '/accounts' as const }, icon: walletIcon, isActive: pathname === '/accounts', isNewUi: true },
isMarketplaceFilled ? isMarketplaceFilled ?
{ text: 'Apps', nextRoute: { pathname: '/apps' as const }, icon: appsIcon, isActive: pathname.startsWith('/app'), isNewUi: true } : null, { text: 'Apps', nextRoute: { pathname: '/apps' as const }, icon: appsIcon, isActive: pathname.startsWith('/app'), isNewUi: true } : null,
{ text: 'Charts & stats', nextRoute: { pathname: '/stats' as const }, icon: statsIcon, isActive: pathname === '/stats', isNewUi: true }, { text: 'Charts & stats', nextRoute: { pathname: '/stats' as const }, icon: statsIcon, isActive: pathname === '/stats', isNewUi: true },
......
...@@ -21,7 +21,7 @@ const NavLink = ({ text, nextRoute, icon, isCollapsed, isActive, px, isNewUi }: ...@@ -21,7 +21,7 @@ const NavLink = ({ text, nextRoute, icon, isCollapsed, isActive, px, isNewUi }:
const content = ( const content = (
<Link <Link
{ ...(isNewUi ? {} : { href: route(nextRoute) }) } { ...(isNewUi ? {} : { href: route(nextRoute) }) }
w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }} w={{ base: '100%', lg: isExpanded ? '100%' : '60px', xl: isCollapsed ? '60px' : '100%' }}
px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } } px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
py={ 2.5 } py={ 2.5 }
display="flex" display="flex"
......
import {
Icon,
Text,
HStack,
Flex,
Box,
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
VStack,
} from '@chakra-ui/react';
import React from 'react';
import chevronIcon from 'icons/arrows/east-mini.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NavLink from './NavLink';
import useColors from './useColors';
type NavigationLink = {
text: string;
url: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
isNewUi: boolean;
isActive: boolean;
}
interface Props {
isCollapsed?: boolean;
isActive?: boolean;
subItems: Array<NavigationLink>;
text: string;
icon: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
}
const NavLinkGroup = ({ text, subItems, icon, isCollapsed, isActive }: Props) => {
const colors = useColors();
const isExpanded = isCollapsed === false;
const isMobile = useIsMobile();
return (
<Box as="li" listStyleType="none" w="100%">
<Popover
trigger="hover"
placement={ isMobile ? 'bottom-end' : 'right-start' }
isLazy
>
<PopoverTrigger>
<Box
w={{ base: '100%', lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }}
pl={{ base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }}
pr={{ base: 3, lg: isExpanded ? 0 : '15px', xl: isCollapsed ? '15px' : 0 }}
py={ 2.5 }
display="flex"
color={ isActive ? colors.text.active : colors.text.default }
bgColor={ isActive ? colors.bg.active : colors.bg.default }
_hover={{ color: isActive ? colors.text.active : colors.text.hover }}
borderRadius="base"
whiteSpace="nowrap"
aria-label={ `${ text } link group` }
{ ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) }
>
<Flex justifyContent="space-between" width="100%" alignItems="center" pr={ 1 }>
<HStack spacing={ 3 } overflow="hidden">
<Icon as={ icon } boxSize="30px"/>
<Text
variant="inherit"
fontSize="sm"
lineHeight="20px"
opacity={{ base: '1', lg: isExpanded ? '1' : '0', xl: isCollapsed ? '0' : '1' }}
transitionProperty="opacity"
transitionDuration="normal"
transitionTimingFunction="ease"
>
{ text }
</Text>
</HStack>
{ isExpanded && <Icon as={ chevronIcon } transform="rotate(180deg)" boxSize={ 6 }/> }
</Flex>
</Box>
</PopoverTrigger>
<PopoverContent width="auto">
<PopoverBody p={ 4 }>
<VStack spacing={ 1 } alignItems="start">
{ subItems.map(item => <NavLink key={ item.text } { ...item } isCollapsed={ false }/>) }
</VStack>
</PopoverBody>
</PopoverContent>
</Popover>
</Box>
);
};
export default NavLinkGroup;
...@@ -70,6 +70,21 @@ test('with tooltips +@desktop-xl -@default', async({ mount, page }) => { ...@@ -70,6 +70,21 @@ test('with tooltips +@desktop-xl -@default', async({ mount, page }) => {
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
test('with submenu +@desktop-xl', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('div[aria-label="Blockchain link group"]').hover();
await expect(component).toHaveScreenshot();
});
test.describe('cookie set to false', () => { test.describe('cookie set to false', () => {
const extendedTest = test.extend({ const extendedTest = test.extend({
context: ({ context }, use) => { context: ({ context }, use) => {
......
...@@ -12,6 +12,7 @@ import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu'; ...@@ -12,6 +12,7 @@ import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu';
import NavFooter from './NavFooter'; import NavFooter from './NavFooter';
import NavLink from './NavLink'; import NavLink from './NavLink';
import NavLinkGroup from './NavLinkGroup';
const NavigationDesktop = () => { const NavigationDesktop = () => {
const appProps = useAppContext(); const appProps = useAppContext();
...@@ -66,7 +67,8 @@ const NavigationDesktop = () => { ...@@ -66,7 +67,8 @@ const NavigationDesktop = () => {
alignItems="center" alignItems="center"
flexDirection="row" flexDirection="row"
w="100%" w="100%"
px={{ lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }} pl={{ lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }}
pr={{ lg: isExpanded ? 0 : '15px', xl: isCollapsed ? '15px' : 0 }}
h={ 10 } h={ 10 }
transitionProperty="padding" transitionProperty="padding"
transitionDuration="normal" transitionDuration="normal"
...@@ -77,7 +79,13 @@ const NavigationDesktop = () => { ...@@ -77,7 +79,13 @@ const NavigationDesktop = () => {
</Box> </Box>
<Box as="nav" mt={ 8 }> <Box as="nav" mt={ 8 }>
<VStack as="ul" spacing="1" alignItems="flex-start"> <VStack as="ul" spacing="1" alignItems="flex-start">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed }/>) } { mainNavItems.map((item) => {
if (item.subItems) {
return <NavLinkGroup key={ item.text } { ...item } isCollapsed={ isCollapsed }/>;
} else {
return <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed }/>;
}
}) }
</VStack> </VStack>
</Box> </Box>
{ hasAccount && ( { hasAccount && (
......
...@@ -7,6 +7,8 @@ import useNavItems from 'lib/hooks/useNavItems'; ...@@ -7,6 +7,8 @@ import useNavItems from 'lib/hooks/useNavItems';
import NavFooter from 'ui/snippets/navigation/NavFooter'; import NavFooter from 'ui/snippets/navigation/NavFooter';
import NavLink from 'ui/snippets/navigation/NavLink'; import NavLink from 'ui/snippets/navigation/NavLink';
import NavLinkGroup from './NavLinkGroup';
const NavigationMobile = () => { const NavigationMobile = () => {
const { mainNavItems, accountNavItems } = useNavItems(); const { mainNavItems, accountNavItems } = useNavItems();
...@@ -17,7 +19,13 @@ const NavigationMobile = () => { ...@@ -17,7 +19,13 @@ const NavigationMobile = () => {
<> <>
<Box as="nav" mt={ 6 }> <Box as="nav" mt={ 6 }>
<VStack as="ul" spacing="1" alignItems="flex-start"> <VStack as="ul" spacing="1" alignItems="flex-start">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item }/>) } { mainNavItems.map((item) => {
if (item.subItems) {
return <NavLinkGroup key={ item.text } { ...item }/>;
} else {
return <NavLink key={ item.text } { ...item }/>;
}
}) }
</VStack> </VStack>
</Box> </Box>
{ isAuth && ( { isAuth && (
......
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