Commit 9e30f7f1 authored by isstuev's avatar isstuev

non-routed tabs with scroll

parent 6cb94da4
......@@ -9,11 +9,11 @@ import React from 'react';
import type { Chain } from 'wagmi';
import { configureChains, createClient, WagmiConfig } from 'wagmi';
import type { RoutedSubTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedSubTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config';
import { ContractContextProvider } from 'ui/address/contract/context';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
interface Props {
tabs: Array<RoutedSubTab>;
......
......@@ -9,7 +9,7 @@ import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { tokenTabsByType } from 'ui/pages/Address';
import Pagination from 'ui/shared/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import ERC1155Tokens from './tokens/ERC1155Tokens';
import ERC20Tokens from './tokens/ERC20Tokens';
......
......@@ -24,6 +24,10 @@ const LatestWatchlistTxs = () => {
return <Text mt={ 4 }>No data. Please reload page.</Text>;
}
if (data.length === 0) {
return <Text mt={ 4 }>There are no transactions.</Text>;
}
if (data) {
const txsUrl = route({ pathname: '/txs', query: { tab: 'watchlist' } });
return (
......@@ -32,7 +36,7 @@ const LatestWatchlistTxs = () => {
{ data.slice(0, txsCount).map((tx => <LatestTxsItem key={ tx.hash } tx={ tx }/>)) }
</Box>
<Flex justifyContent="center">
<LinkInternal fontSize="sm" href={ txsUrl }>View all watchlist transactions</LinkInternal>
<LinkInternal fontSize="sm" href={ txsUrl }>View all watch list transactions</LinkInternal>
</Flex>
</>
);
......
import { Heading, Tab, Tabs, TabList, TabPanel, TabPanels } from '@chakra-ui/react';
import { Heading } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
......@@ -6,35 +6,20 @@ import useHasAccount from 'lib/hooks/useHasAccount';
import LatestDeposits from 'ui/home/LatestDeposits';
import LatestTxs from 'ui/home/LatestTxs';
import LatestWatchlistTxs from 'ui/home/LatestWatchlistTxs';
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
const TransactionsHome = () => {
const hasAccount = useHasAccount();
if (appConfig.L2.isL2Network || hasAccount) {
const tabs = [
{ id: 'txn', title: 'Latest txn', component: <LatestTxs/> },
appConfig.L2.isL2Network && { id: 'deposits', title: 'Deposits (L1→L2 txn)', component: <LatestDeposits/> },
hasAccount && { id: 'watchlist', title: 'Watch list', component: <LatestWatchlistTxs/> },
].filter(Boolean);
return (
<>
<Heading as="h4" size="sm" mb={ 4 }>Transactions</Heading>
<Tabs isLazy lazyBehavior="keepMounted" defaultIndex={ 0 } variant="soft-rounded">
<TabList>
<Tab key="txn">Latest txn</Tab>
{ appConfig.L2.isL2Network && <Tab key="deposits">Deposits (L1→L2 txn)</Tab> }
{ hasAccount && <Tab key="deposits">Watch list</Tab> }
</TabList>
<TabPanels mt={ 4 }>
<TabPanel key="txn" p={ 0 }>
<LatestTxs/>
</TabPanel>
{ appConfig.L2.isL2Network && (
<TabPanel key="deposits" p={ 0 }>
<LatestDeposits/>
</TabPanel>
) }
{ hasAccount && (
<TabPanel key="deposits" p={ 0 }>
<LatestWatchlistTxs/>
</TabPanel>
) }
</TabPanels>
</Tabs>
<TabsWithScroll tabs={ tabs } lazyBehavior="keepMounted"/>
</>
);
}
......
......@@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import React from 'react';
import type { TokenType } from 'types/api/token';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config';
import iconSuccess from 'icons/status/success.svg';
......@@ -24,8 +24,8 @@ import AddressWithdrawals from 'ui/address/AddressWithdrawals';
import TextAd from 'ui/shared/ad/TextAd';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
export const tokenTabsByType: Record<TokenType, string> = {
'ERC-20': 'tokens_erc20',
......
......@@ -2,7 +2,7 @@ import { Skeleton } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config';
import useApiQuery from 'lib/api/useApiQuery';
......@@ -16,8 +16,8 @@ import TextAd from 'ui/shared/ad/TextAd';
import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import Pagination from 'ui/shared/Pagination';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TxsContent from 'ui/txs/TxsContent';
const TAB_LIST_PROPS = {
......
......@@ -2,7 +2,7 @@ import { useRouter } from 'next/router';
import React from 'react';
import type { BlockType } from 'types/api/block';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
......@@ -10,7 +10,7 @@ import BlocksContent from 'ui/blocks/BlocksContent';
import BlocksTabSlot from 'ui/blocks/BlocksTabSlot';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
const TAB_TO_TYPE: Record<string, BlockType> = {
blocks: 'block',
......
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
const TABS: Array<RoutedTab> = [
{ id: 'address', title: 'Address', component: <PrivateAddressTags/> },
......
......@@ -5,7 +5,7 @@ import React, { useEffect } from 'react';
import type { SocketMessage } from 'lib/socket/types';
import type { TokenInfo } from 'types/api/token';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import iconSuccess from 'icons/status/success.svg';
import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery';
......@@ -24,8 +24,8 @@ import Tag from 'ui/shared/chakra/Tag';
import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import Pagination from 'ui/shared/Pagination';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TokenLogo from 'ui/shared/TokenLogo';
import TokenContractInfo from 'ui/token/TokenContractInfo';
import TokenDetails from 'ui/token/TokenDetails';
......
......@@ -2,7 +2,7 @@ import { Flex, Tag } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
......@@ -12,7 +12,7 @@ import TextAd from 'ui/shared/ad/TextAd';
import LinkExternal from 'ui/shared/LinkExternal';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TxDetails from 'ui/tx/TxDetails';
import TxInternals from 'ui/tx/TxInternals';
import TxLogs from 'ui/tx/TxLogs';
......
......@@ -2,7 +2,7 @@ import { Box } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import appConfig from 'configs/app/config';
import useHasAccount from 'lib/hooks/useHasAccount';
......@@ -11,7 +11,7 @@ import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TxsContent from 'ui/txs/TxsContent';
import TxsTabSlot from 'ui/txs/TxsTabSlot';
import TxsWatchlist from 'ui/txs/TxsWatchlist';
......@@ -65,7 +65,7 @@ const Transactions = () => {
},
hasAccount ? {
id: 'watchlist',
title: 'Watchlist',
title: 'Watch list',
component: <TxsWatchlist query={ txsWatchlistQuery }/>,
} : undefined,
].filter(Boolean);
......
import type { ChakraProps, ThemingProps } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/react';
import _pickBy from 'lodash/pickBy';
import { useRouter } from 'next/router';
import React, { useEffect, useRef, useState } from 'react';
import type { RoutedTab } from './types';
import TabsWithScroll from './TabsWithScroll';
interface Props extends ThemingProps<'Tabs'> {
tabs: Array<RoutedTab>;
tabListProps?: ChakraProps | (({ isSticky, activeTabIndex }: { isSticky: boolean; activeTabIndex: number }) => ChakraProps);
rightSlot?: React.ReactNode;
stickyEnabled?: boolean;
className?: string;
}
const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, ...themeProps }: Props) => {
const router = useRouter();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
const tabsRef = useRef<HTMLDivElement>(null);
const handleTabChange = React.useCallback((index: number) => {
const nextTab = tabs[index];
const queryForPathname = _pickBy(router.query, (value, key) => router.pathname.includes(`[${ key }]`));
router.push(
{ pathname: router.pathname, query: { ...queryForPathname, tab: nextTab.id } },
undefined,
{ shallow: true },
);
}, [ tabs, router ]);
useEffect(() => {
if (router.query.scroll_to_tabs) {
tabsRef?.current?.scrollIntoView(true);
delete router.query.scroll_to_tabs;
router.push(
{
pathname: router.pathname,
query: router.query,
},
undefined,
{ shallow: true },
);
}
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (router.isReady) {
let tabIndex = 0;
const tabFromRoute = router.query.tab;
if (tabFromRoute) {
tabIndex = tabs.findIndex(({ id, subTabs }) => id === tabFromRoute || subTabs?.some((id) => id === tabFromRoute));
if (tabIndex < 0) {
tabIndex = 0;
}
}
setActiveTabIndex(tabIndex);
}
}, [ tabs, router, activeTabIndex ]);
return (
<TabsWithScroll
tabs={ tabs }
tabListProps={ tabListProps }
rightSlot={ rightSlot }
stickyEnabled={ stickyEnabled }
onTabChange={ handleTabChange }
activeTabIndex={ activeTabIndex }
{ ...themeProps }
/>
);
};
export default React.memo(chakra(RoutedTabs));
import type { LazyMode } from '@chakra-ui/lazy-utils';
import type { ChakraProps, ThemingProps } from '@chakra-ui/react';
import {
Tab,
......@@ -10,11 +11,9 @@ import {
chakra,
} from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system';
import _pickBy from 'lodash/pickBy';
import { useRouter } from 'next/router';
import React, { useEffect, useRef, useState } from 'react';
import type { RoutedTab } from './types';
import type { TabItem } from './types';
import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile';
......@@ -31,18 +30,29 @@ const hiddenItemStyles: StyleProps = {
};
interface Props extends ThemingProps<'Tabs'> {
tabs: Array<RoutedTab>;
tabs: Array<TabItem>;
lazyBehavior?: LazyMode;
tabListProps?: ChakraProps | (({ isSticky, activeTabIndex }: { isSticky: boolean; activeTabIndex: number }) => ChakraProps);
rightSlot?: React.ReactNode;
stickyEnabled?: boolean;
onTabChange?: (index: number) => void;
activeTabIndex?: number;
className?: string;
}
const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, ...themeProps }: Props) => {
const router = useRouter();
const TabsWithScroll = ({
tabs,
lazyBehavior,
tabListProps,
rightSlot,
stickyEnabled,
onTabChange,
activeTabIndex: activeTabIndexProps,
className,
...themeProps
}: Props) => {
const scrollDirection = useScrollDirection();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(activeTabIndexProps || 0);
const isMobile = useIsMobile();
const tabsRef = useRef<HTMLDivElement>(null);
const { tabsCut, tabsList, tabsRefs, listRef, rightSlotRef } = useAdaptiveTabs(tabs, isMobile);
......@@ -50,46 +60,14 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, .
const listBgColor = useColorModeValue('white', 'black');
const handleTabChange = React.useCallback((index: number) => {
const nextTab = tabs[index];
const queryForPathname = _pickBy(router.query, (value, key) => router.pathname.includes(`[${ key }]`));
router.push(
{ pathname: router.pathname, query: { ...queryForPathname, tab: nextTab.id } },
undefined,
{ shallow: true },
);
}, [ tabs, router ]);
onTabChange ? onTabChange(index) : setActiveTabIndex(index);
}, [ onTabChange ]);
useEffect(() => {
if (router.query.scroll_to_tabs) {
tabsRef?.current?.scrollIntoView(true);
delete router.query.scroll_to_tabs;
router.push(
{
pathname: router.pathname,
query: router.query,
},
undefined,
{ shallow: true },
);
if (activeTabIndexProps !== undefined) {
setActiveTabIndex(activeTabIndexProps);
}
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (router.isReady) {
let tabIndex = 0;
const tabFromRoute = router.query.tab;
if (tabFromRoute) {
tabIndex = tabs.findIndex(({ id, subTabs }) => id === tabFromRoute || subTabs?.some((id) => id === tabFromRoute));
if (tabIndex < 0) {
tabIndex = 0;
}
}
setActiveTabIndex(tabIndex);
}
}, [ tabs, router, activeTabIndex ]);
}, [ activeTabIndexProps ]);
useEffect(() => {
if (activeTabIndex < tabs.length && isMobile) {
......@@ -97,7 +75,6 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, .
const activeTabRef = tabsRefs[activeTabIndex];
if (activeTabRef.current && listRef.current) {
const activeTabRect = activeTabRef.current.getBoundingClientRect();
listRef.current.scrollTo({
left: activeTabRect.left + listRef.current.scrollLeft - 16,
behavior: 'smooth',
......@@ -125,6 +102,7 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, .
position="relative"
size={ themeProps.size || 'md' }
ref={ tabsRef }
lazyBehavior={ lazyBehavior }
>
<TabList
marginBottom={{ base: 6, lg: 8 }}
......@@ -201,4 +179,4 @@ const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled, className, .
);
};
export default React.memo(chakra(RoutedTabs));
export default React.memo(chakra(TabsWithScroll));
import type React from 'react';
export interface RoutedTab {
export interface TabItem {
id: string;
title: string | (() => React.ReactNode);
component: React.ReactNode;
subTabs?: Array<string>;
}
export type RoutedSubTab = Omit<RoutedTab, 'subTabs'>;
export type RoutedTab = TabItem & { subTabs?: Array<string> }
export type RoutedSubTab = Omit<TabItem, 'subTabs'>;
export interface MenuButton {
id: null;
......
......@@ -2,7 +2,7 @@ import { Box, Tag, Icon } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import type { RoutedTab } from 'ui/shared/Tabs/types';
import nftIcon from 'icons/nft_shield.svg';
import useApiQuery from 'lib/api/useApiQuery';
......@@ -15,8 +15,8 @@ import LinkExternal from 'ui/shared/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
import RoutedTabs from 'ui/shared/RoutedTabs/RoutedTabs';
import SkeletonTabs from 'ui/shared/skeletons/SkeletonTabs';
import RoutedTabs from 'ui/shared/Tabs/RoutedTabs';
import TokenHolders from 'ui/token/TokenHolders/TokenHolders';
import TokenTransfer from 'ui/token/TokenTransfer/TokenTransfer';
......
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