Commit 70c9cce4 authored by tom's avatar tom

sticky pagination for blocks

parent 0f710c19
import throttle from 'lodash/throttle';
import React from 'react';
export default function useIsSticky(ref: React.RefObject<HTMLDivElement>, offset = 0, isEnabled = true) {
const [ isSticky, setIsSticky ] = React.useState(false);
const handleScroll = React.useCallback(() => {
if (
Number(ref.current?.getBoundingClientRect().y) < offset
) {
setIsSticky(true);
} else {
setIsSticky(false);
}
}, [ ref, offset ]);
React.useEffect(() => {
if (!isEnabled) {
return;
}
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isEnabled ]);
return isSticky;
}
...@@ -93,7 +93,7 @@ const BlocksContent = ({ type, query }: Props) => { ...@@ -93,7 +93,7 @@ const BlocksContent = ({ type, query }: Props) => {
<> <>
{ socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> } { socketAlert && <Alert status="warning" mb={ 6 } as="a" href={ window.document.location.href }>{ socketAlert }</Alert> }
<Show below="lg" key="content-mobile"><BlocksList data={ query.data.items }/></Show> <Show below="lg" key="content-mobile"><BlocksList data={ query.data.items }/></Show>
<Hide below="lg" key="content-desktop"><BlocksTable data={ query.data.items } top={ 0 } page={ 1 }/></Hide> <Hide below="lg" key="content-desktop"><BlocksTable data={ query.data.items } top={ 80 } page={ 1 }/></Hide>
</> </>
); );
......
...@@ -24,7 +24,7 @@ const BlockPageContent = () => { ...@@ -24,7 +24,7 @@ const BlockPageContent = () => {
return ( return (
<Page> <Page>
<PageTitle text={ `Block #${ router.query.id }` }/> <PageTitle text={ `Block #${ router.query.id }` }/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/> <RoutedTabs tabs={ TABS }/>
</Page> </Page>
); );
}; };
......
...@@ -5,6 +5,7 @@ import type { BlockType } from 'types/api/block'; ...@@ -5,6 +5,7 @@ import type { BlockType } from 'types/api/block';
import { QueryKeys } from 'types/client/queries'; import { QueryKeys } from 'types/client/queries';
import type { RoutedTab } from 'ui/shared/RoutedTabs/types'; import type { RoutedTab } from 'ui/shared/RoutedTabs/types';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages'; import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import BlocksContent from 'ui/blocks/BlocksContent'; import BlocksContent from 'ui/blocks/BlocksContent';
import BlocksTabSlot from 'ui/blocks/BlocksTabSlot'; import BlocksTabSlot from 'ui/blocks/BlocksTabSlot';
...@@ -18,8 +19,15 @@ const TAB_TO_TYPE: Record<string, BlockType> = { ...@@ -18,8 +19,15 @@ const TAB_TO_TYPE: Record<string, BlockType> = {
uncles: 'uncle', uncles: 'uncle',
}; };
const TAB_LIST_PROPS = {
marginBottom: 0,
py: 5,
marginTop: -5,
};
const BlocksPageContent = () => { const BlocksPageContent = () => {
const router = useRouter(); const router = useRouter();
const isMobile = useIsMobile();
const type = router.query.tab && !Array.isArray(router.query.tab) ? TAB_TO_TYPE[router.query.tab] : undefined; const type = router.query.tab && !Array.isArray(router.query.tab) ? TAB_TO_TYPE[router.query.tab] : undefined;
const blocksQuery = useQueryWithPages({ const blocksQuery = useQueryWithPages({
apiPath: '/node-api/blocks', apiPath: '/node-api/blocks',
...@@ -38,8 +46,9 @@ const BlocksPageContent = () => { ...@@ -38,8 +46,9 @@ const BlocksPageContent = () => {
<PageTitle text="Blocks"/> <PageTitle text="Blocks"/>
<RoutedTabs <RoutedTabs
tabs={ tabs } tabs={ tabs }
tabListMarginBottom={{ base: 6, lg: 8 }} tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
rightSlot={ <BlocksTabSlot pagination={ blocksQuery.pagination }/> } rightSlot={ <BlocksTabSlot pagination={ blocksQuery.pagination }/> }
stickyEnabled={ !isMobile }
/> />
</Page> </Page>
); );
......
...@@ -20,7 +20,7 @@ const PrivateTags = () => { ...@@ -20,7 +20,7 @@ const PrivateTags = () => {
return ( return (
<Page> <Page>
<PageTitle text="Private tags"/> <PageTitle text="Private tags"/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/> <RoutedTabs tabs={ TABS }/>
</Page> </Page>
); );
}; };
......
...@@ -82,7 +82,7 @@ const TransactionPageContent = () => { ...@@ -82,7 +82,7 @@ const TransactionPageContent = () => {
</Flex> </Flex>
) } ) }
</Flex> </Flex>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/> <RoutedTabs tabs={ TABS }/>
</Page> </Page>
); );
}; };
......
...@@ -22,7 +22,7 @@ const Transactions = () => { ...@@ -22,7 +22,7 @@ const Transactions = () => {
<Page hideMobileHeaderOnScrollDown> <Page hideMobileHeaderOnScrollDown>
<Box h="100%"> <Box h="100%">
<PageTitle text="Transactions"/> <PageTitle text="Transactions"/>
<RoutedTabs tabs={ TABS } tabListMarginBottom={{ base: 6, lg: 8 }}/> <RoutedTabs tabs={ TABS }/>
</Box> </Box>
</Page> </Page>
); );
......
import { Flex, useColorModeValue, chakra } from '@chakra-ui/react'; import { Flex, useColorModeValue, chakra } from '@chakra-ui/react';
import throttle from 'lodash/throttle';
import React from 'react'; import React from 'react';
import { useScrollDirection } from 'lib/contexts/scrollDirection'; import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsSticky from 'lib/hooks/useIsSticky';
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
...@@ -13,32 +13,9 @@ const TOP_UP = 106; ...@@ -13,32 +13,9 @@ const TOP_UP = 106;
const TOP_DOWN = 0; const TOP_DOWN = 0;
const ActionBar = ({ children, className }: Props) => { const ActionBar = ({ children, className }: Props) => {
const [ isSticky, setIsSticky ] = React.useState(false);
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
const scrollDirection = useScrollDirection(); const scrollDirection = useScrollDirection();
const isSticky = useIsSticky(ref, TOP_UP + 5);
const handleScroll = React.useCallback(() => {
if (
Number(ref.current?.getBoundingClientRect().y) < TOP_UP + 5
) {
setIsSticky(true);
} else {
setIsSticky(false);
}
}, [ ]);
React.useEffect(() => {
const throttledHandleScroll = throttle(handleScroll, 300);
window.addEventListener('scroll', throttledHandleScroll);
return () => {
window.removeEventListener('scroll', throttledHandleScroll);
};
// replicate componentDidMount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ ]);
const bgColor = useColorModeValue('white', 'black'); const bgColor = useColorModeValue('white', 'black');
return ( return (
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
TabPanel, TabPanel,
TabPanels, TabPanels,
Box, Box,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { StyleProps } from '@chakra-ui/styled-system'; import type { StyleProps } from '@chakra-ui/styled-system';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -13,7 +14,9 @@ import React, { useEffect, useState } from 'react'; ...@@ -13,7 +14,9 @@ import React, { useEffect, useState } from 'react';
import type { RoutedTab } from './types'; import type { RoutedTab } from './types';
import { useScrollDirection } from 'lib/contexts/scrollDirection';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import useIsSticky from 'lib/hooks/useIsSticky';
import RoutedTabsMenu from './RoutedTabsMenu'; import RoutedTabsMenu from './RoutedTabsMenu';
import useAdaptiveTabs from './useAdaptiveTabs'; import useAdaptiveTabs from './useAdaptiveTabs';
...@@ -27,12 +30,14 @@ const hiddenItemStyles: StyleProps = { ...@@ -27,12 +30,14 @@ const hiddenItemStyles: StyleProps = {
interface Props { interface Props {
tabs: Array<RoutedTab>; tabs: Array<RoutedTab>;
tabListMarginBottom?: ChakraProps['marginBottom']; tabListProps?: ChakraProps;
rightSlot?: React.ReactNode; rightSlot?: React.ReactNode;
stickyEnabled?: boolean;
} }
const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => { const RoutedTabs = ({ tabs, tabListProps, rightSlot, stickyEnabled }: Props) => {
const router = useRouter(); const router = useRouter();
const scrollDirection = useScrollDirection();
const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1); const [ activeTabIndex, setActiveTabIndex ] = useState<number>(tabs.length + 1);
useEffect(() => { useEffect(() => {
...@@ -50,6 +55,8 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => { ...@@ -50,6 +55,8 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { tabsCut, tabsList, tabsRefs, listRef, rightSlotRef } = useAdaptiveTabs(tabs, isMobile); const { tabsCut, tabsList, tabsRefs, listRef, rightSlotRef } = useAdaptiveTabs(tabs, isMobile);
const isSticky = useIsSticky(listRef, 5, stickyEnabled);
const listBgColor = useColorModeValue('white', 'black');
const handleTabChange = React.useCallback((index: number) => { const handleTabChange = React.useCallback((index: number) => {
const nextTab = tabs[index]; const nextTab = tabs[index];
...@@ -62,9 +69,16 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => { ...@@ -62,9 +69,16 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => {
}, [ tabs, router ]); }, [ tabs, router ]);
return ( return (
<Tabs variant="soft-rounded" colorScheme="blue" isLazy onChange={ handleTabChange } index={ activeTabIndex } position="relative"> <Tabs
variant="soft-rounded"
colorScheme="blue"
isLazy
onChange={ handleTabChange }
index={ activeTabIndex }
position="relative"
>
<TabList <TabList
marginBottom={ tabListMarginBottom } marginBottom={{ base: 6, lg: 8 }}
flexWrap="nowrap" flexWrap="nowrap"
whiteSpace="nowrap" whiteSpace="nowrap"
ref={ listRef } ref={ listRef }
...@@ -80,6 +94,18 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => { ...@@ -80,6 +94,18 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => {
'-ms-overflow-style': 'none', /* IE and Edge */ '-ms-overflow-style': 'none', /* IE and Edge */
'scrollbar-width': 'none', /* Firefox */ 'scrollbar-width': 'none', /* Firefox */
}} }}
bgColor={ listBgColor }
transitionProperty="top,box-shadow,background-color,color"
transitionDuration="slow"
{
...(stickyEnabled ? {
position: 'sticky',
boxShadow: { base: isSticky ? 'md' : 'none', lg: 'none' },
top: { base: scrollDirection === 'down' ? `0px` : `106px`, lg: 0 },
zIndex: { base: 'sticky2', lg: 'docked' },
} : { })
}
{ ...tabListProps }
> >
{ tabsList.map((tab, index) => { { tabsList.map((tab, index) => {
if (!tab.id) { if (!tab.id) {
...@@ -114,8 +140,8 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => { ...@@ -114,8 +140,8 @@ const RoutedTabs = ({ tabs, tabListMarginBottom, rightSlot }: Props) => {
</Tab> </Tab>
); );
}) } }) }
{ rightSlot ? <Box ref={ rightSlotRef } ml="auto" > { rightSlot } </Box> : null }
</TabList> </TabList>
{ rightSlot ? <Box position="absolute" top={ 0 } right={ 0 } ref={ rightSlotRef }>{ rightSlot }</Box> : null }
<TabPanels> <TabPanels>
{ tabsList.map((tab) => <TabPanel padding={ 0 } key={ tab.id }>{ tab.component }</TabPanel>) } { tabsList.map((tab) => <TabPanel padding={ 0 } key={ tab.id }>{ tab.component }</TabPanel>) }
</TabPanels> </TabPanels>
......
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