Commit 1c78a12c authored by tom's avatar tom

highlight similar addresses in the table views

parent e1cf276c
import React from 'react';
interface AddressHighlightProviderProps {
children: React.ReactNode;
}
interface TAddressHighlightContext {
highlightedAddress: string | null;
onMouseEnter: (event: React.MouseEvent) => void;
onMouseLeave: (event: React.MouseEvent) => void;
}
export const AddressHighlightContext = React.createContext<TAddressHighlightContext | null>(null);
export function AddressHighlightProvider({ children }: AddressHighlightProviderProps) {
const [ highlightedAddress, setHighlightedAddress ] = React.useState<string | null>(null);
const onMouseEnter = React.useCallback((event: React.MouseEvent) => {
// TODO @tom2drum add throttling
const hash = event.currentTarget.getAttribute('data-hash');
hash && setHighlightedAddress(hash);
}, []);
const onMouseLeave = React.useCallback(() => {
setHighlightedAddress(null);
}, []);
const value = React.useMemo(() => {
return {
highlightedAddress,
onMouseEnter,
onMouseLeave,
};
}, [ highlightedAddress, onMouseEnter, onMouseLeave ]);
return (
<AddressHighlightContext.Provider value={ value }>
{ children }
</AddressHighlightContext.Provider>
);
}
export function useAddressHighlightContext() {
const context = React.useContext(AddressHighlightContext);
if (context === undefined) {
return null;
}
return context;
}
......@@ -4,6 +4,7 @@ import React from 'react';
import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky';
import AddressIntTxsTableItem from './AddressIntTxsTableItem';
......@@ -16,6 +17,7 @@ interface Props {
const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
return (
<AddressHighlightProvider>
<Table variant="simple" size="sm">
<Thead top={ 80 }>
<Tr>
......@@ -41,6 +43,8 @@ const AddressIntTxsTable = ({ data, currentAddress, isLoading }: Props) => {
)) }
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -83,6 +83,7 @@ const AddressIntTxsTableItem = ({
isLoading={ isLoading }
noLink={ isOut }
noCopy={ isOut }
w="min-content"
/>
</Td>
<Td px={ 0 } verticalAlign="middle">
......@@ -98,6 +99,7 @@ const AddressIntTxsTableItem = ({
isLoading={ isLoading }
noLink={ isIn }
noCopy={ isIn }
w="min-content"
/>
) }
</Td>
......
......@@ -6,6 +6,7 @@ import React from 'react';
import type { Block } from 'types/api/block';
import config from 'configs/app';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import BlocksTableItem from 'ui/blocks/BlocksTableItem';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
......@@ -37,6 +38,7 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
(!isRollup && !config.UI.views.block.hiddenFields?.burnt_fees ? FEES_COL_WEIGHT : 0);
return (
<AddressHighlightProvider>
<Table variant="simple" minWidth="1040px" size="md" fontWeight={ 500 }>
<Thead top={ top }>
<Tr>
......@@ -74,6 +76,7 @@ const BlocksTable = ({ data, isLoading, top, page, showSocketInfo, socketInfoNum
</AnimatePresence>
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -4,6 +4,7 @@ import React from 'react';
import { route } from 'nextjs-routes';
import useApiQuery from 'lib/api/useApiQuery';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useIsMobile from 'lib/hooks/useIsMobile';
import useNewTxsSocket from 'lib/hooks/useNewTxsSocket';
import { TX } from 'stubs/tx';
......@@ -42,6 +43,7 @@ const LatestTransactions = () => {
/>
))) }
</Box>
<AddressHighlightProvider>
<Box mb={ 4 } display={{ base: 'none', lg: 'block' }}>
{ data.slice(0, txsCount).map(((tx, index) => (
<LatestTxsItem
......@@ -51,6 +53,7 @@ const LatestTransactions = () => {
/>
))) }
</Box>
</AddressHighlightProvider>
<Flex justifyContent="center">
<LinkInternal fontSize="sm" href={ txsUrl }>View all transactions</LinkInternal>
</Flex>
......
......@@ -35,7 +35,10 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
return (
<Grid
gridTemplateColumns={ columnNum === 2 ? '3fr 2fr' : '3fr 2fr 150px' }
gridTemplateColumns={{
lg: columnNum === 2 ? '3fr minmax(auto, 160px)' : '3fr minmax(auto, 160px) 150px',
xl: columnNum === 2 ? '3fr minmax(auto, 250px)' : '3fr minmax(auto, 250px) 150px',
}}
gridGap={ 8 }
width="100%"
minW="700px"
......@@ -77,15 +80,16 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
</Flex>
</Box>
</Flex>
<Grid alignItems="center" alignSelf="flex-start" templateColumns="24px auto">
<Flex alignItems="center" alignSelf="flex-start">
<Icon
as={ rightArrowIcon }
boxSize={ 6 }
color="gray.500"
transform="rotate(90deg)"
isLoading={ isLoading }
flexShrink={ 0 }
/>
<Box overflow="hidden" ml={ 1 }>
<Box ml={ 1 } maxW="calc(100% - 24px)">
<AddressEntity
isLoading={ isLoading }
address={ tx.from }
......@@ -104,7 +108,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
/>
) }
</Box>
</Grid>
</Flex>
<Box>
{ !config.UI.views.tx.hiddenFields?.value && (
<Skeleton isLoaded={ !isLoading } mb={ 2 }>
......
......@@ -3,6 +3,7 @@ import React from 'react';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky';
import TokenTransferTableItem from 'ui/shared/TokenTransfer/TokenTransferTableItem';
......@@ -32,6 +33,7 @@ const TokenTransferTable = ({
}: Props) => {
return (
<AddressHighlightProvider>
<Table variant="simple" size="sm" minW="950px">
<Thead top={ top }>
<Tr>
......@@ -67,6 +69,7 @@ const TokenTransferTable = ({
)) }
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
import type { As } from '@chakra-ui/react';
import { Box, Flex, Skeleton, Tooltip, chakra, VStack } from '@chakra-ui/react';
import { Box, Flex, Skeleton, Tooltip, chakra, VStack, useColorModeValue } from '@chakra-ui/react';
import _omit from 'lodash/omit';
import React from 'react';
......@@ -10,6 +10,7 @@ import { route } from 'nextjs-routes';
import iconSafe from 'icons/brands/safe.svg';
import iconContractVerified from 'icons/contract_verified.svg';
import iconContract from 'icons/contract.svg';
import { useAddressHighlightContext } from 'lib/contexts/addressHighlight';
import * as EntityBase from 'ui/shared/entities/base/components';
import { getIconProps } from '../base/utils';
......@@ -148,8 +149,32 @@ const AddressEntry = (props: EntityProps) => {
const linkProps = _omit(props, [ 'className' ]);
const partsProps = _omit(props, [ 'className', 'onClick' ]);
const context = useAddressHighlightContext();
const highlightedBgColor = useColorModeValue('blue.50', 'blue.900');
const highlightedBorderColor = useColorModeValue('blue.200', 'blue.600');
return (
<Container className={ props.className }>
<Container
className={ props.className }
data-hash={ props.address.hash }
onMouseEnter={ context?.onMouseEnter }
onMouseLeave={ context?.onMouseLeave }
position="relative"
_before={ !props.isLoading && context?.highlightedAddress === props.address.hash ? {
content: `" "`,
position: 'absolute',
top: '-7px',
left: '-5px',
width: `calc(100% + ${ props.noCopy ? 10 : 5 }px)`,
height: 'calc(100% + 12px)',
borderRadius: 'base',
borderColor: highlightedBorderColor,
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: highlightedBgColor,
zIndex: -1,
} : undefined }
>
<Icon { ...partsProps }/>
<Link { ...linkProps }>
<Content { ...partsProps }/>
......
......@@ -32,14 +32,17 @@ export interface EntityBaseProps {
export interface ContainerBaseProps extends Pick<EntityBaseProps, 'className'> {
children: React.ReactNode;
onMouseEnter?: (event: React.MouseEvent) => void;
onMouseLeave?: (event: React.MouseEvent) => void;
}
const Container = chakra(({ className, children }: ContainerBaseProps) => {
const Container = chakra(({ className, children, ...props }: ContainerBaseProps) => {
return (
<Flex
className={ className }
alignItems="center"
minWidth={ 0 } // for content truncation - https://css-tricks.com/flexbox-truncated-text/
{ ...props }
>
{ children }
</Flex>
......
......@@ -5,6 +5,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token';
import type { ResourceError } from 'lib/api/resources';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useIsMobile from 'lib/hooks/useIsMobile';
import ActionBar from 'ui/shared/ActionBar';
import DataListDisplay from 'ui/shared/DataListDisplay';
......@@ -58,6 +59,7 @@ const TokenInventory = ({ inventoryQuery, tokenQuery, ownerFilter }: Props) => {
const token = tokenQuery.data;
const content = items && token ? (
<AddressHighlightProvider>
<Grid
w="100%"
columnGap={{ base: 3, lg: 6 }}
......@@ -73,6 +75,7 @@ const TokenInventory = ({ inventoryQuery, tokenQuery, ownerFilter }: Props) => {
/>
)) }
</Grid>
</AddressHighlightProvider>
) : null;
return (
......
......@@ -4,6 +4,7 @@ import React from 'react';
import type { TokenInfo } from 'types/api/token';
import type { TokenTransfer } from 'types/api/tokenTransfer';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import { default as Thead } from 'ui/shared/TheadSticky';
import TruncatedValue from 'ui/shared/TruncatedValue';
......@@ -24,6 +25,7 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
const tokenType = data[0].token.type;
return (
<AddressHighlightProvider>
<Table variant="simple" size="sm" minW="950px">
<Thead top={ top }>
<Tr>
......@@ -60,6 +62,7 @@ const TokenTransferTable = ({ data, top, showSocketInfo, socketInfoAlert, socket
)) }
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -67,6 +67,7 @@ const TokenTransferTableItem = ({
truncation="constant"
tokenHash={ token.address }
my="5px"
w="min-content"
/>
</Td>
<Td px={ 0 }>
......@@ -81,6 +82,7 @@ const TokenTransferTableItem = ({
truncation="constant"
tokenHash={ token.address }
my="5px"
w="min-content"
/>
</Td>
{ (token.type === 'ERC-721' || token.type === 'ERC-1155') && (
......
......@@ -5,6 +5,7 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import config from 'configs/app';
import arrowIcon from 'icons/arrows/east.svg';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
import type { Sort, SortField } from 'ui/tx/internals/utils';
......@@ -21,6 +22,7 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<AddressHighlightProvider>
<Table variant="simple" size="sm">
<Thead top={ top }>
<Tr>
......@@ -48,6 +50,7 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
)) }
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -8,6 +8,7 @@ import React from 'react';
import type { TxStateChange } from 'types/api/txStateChanges';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { default as Thead } from 'ui/shared/TheadSticky';
import TxStateTableItem from 'ui/tx/state/TxStateTableItem';
......@@ -19,6 +20,7 @@ interface Props {
const TxStateTable = ({ data, isLoading, top }: Props) => {
return (
<AddressHighlightProvider>
<Table variant="simple" minWidth="1000px" size="sm" w="100%">
<Thead top={ top }>
<Tr>
......@@ -34,6 +36,7 @@ const TxStateTable = ({ data, isLoading, top }: Props) => {
{ data.map((item, index) => <TxStateTableItem data={ item } key={ index } isLoading={ isLoading }/>) }
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -27,7 +27,8 @@ const TxStateTableItem = ({ data, isLoading }: Props) => {
address={ data.address }
isLoading={ isLoading }
truncation="constant"
py="7px"
my="7px"
w="min-content"
/>
</Td>
<Td isNumeric><Box py="7px">{ before }</Box></Td>
......
......@@ -6,6 +6,7 @@ import type { Transaction, TransactionsSortingField, TransactionsSortingValue }
import config from 'configs/app';
import rightArrowIcon from 'icons/arrows/east.svg';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TheadSticky from 'ui/shared/TheadSticky';
......@@ -39,6 +40,7 @@ const TxsTable = ({
isLoading,
}: Props) => {
return (
<AddressHighlightProvider>
<Table variant="simple" minWidth="950px" size="xs">
<TheadSticky top={ top }>
<Tr>
......@@ -98,6 +100,7 @@ const TxsTable = ({
</AnimatePresence>
</Tbody>
</Table>
</AddressHighlightProvider>
);
};
......
......@@ -52,7 +52,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
noCopy={ isOut }
noLink={ isOut }
truncation="constant"
w="100%"
w="min-content"
maxW="100%"
py="2px"
/>
);
......@@ -64,7 +65,8 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
truncation="constant"
noCopy={ isIn }
noLink={ isIn }
w="100%"
w="min-content"
maxW="100%"
py="2px"
/>
) : '-';
......@@ -150,7 +152,7 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
isLoading={ isLoading }
/>
) }
<VStack alignItems="start" overflow="hidden" ml={ 1 }>
<VStack alignItems="start" ml={ 1 } w="calc(100% - 48px)">
{ addressFrom }
{ addressTo }
</VStack>
......
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