Commit 20d8c0cd authored by tom goriunov's avatar tom goriunov Committed by GitHub

improve perf of highlighting same address in lists (#1662)

Fixes #1661
parent 82b7f3dd
...@@ -5,7 +5,6 @@ interface AddressHighlightProviderProps { ...@@ -5,7 +5,6 @@ interface AddressHighlightProviderProps {
} }
interface TAddressHighlightContext { interface TAddressHighlightContext {
highlightedAddress: string | null;
onMouseEnter: (event: React.MouseEvent) => void; onMouseEnter: (event: React.MouseEvent) => void;
onMouseLeave: (event: React.MouseEvent) => void; onMouseLeave: (event: React.MouseEvent) => void;
} }
...@@ -13,30 +12,40 @@ interface TAddressHighlightContext { ...@@ -13,30 +12,40 @@ interface TAddressHighlightContext {
export const AddressHighlightContext = React.createContext<TAddressHighlightContext | null>(null); export const AddressHighlightContext = React.createContext<TAddressHighlightContext | null>(null);
export function AddressHighlightProvider({ children }: AddressHighlightProviderProps) { export function AddressHighlightProvider({ children }: AddressHighlightProviderProps) {
const [ highlightedAddress, setHighlightedAddress ] = React.useState<string | null>(null);
const timeoutId = React.useRef<number | null>(null); const timeoutId = React.useRef<number | null>(null);
const hashRef = React.useRef<string | null>(null);
const onMouseEnter = React.useCallback((event: React.MouseEvent) => { const onMouseEnter = React.useCallback((event: React.MouseEvent) => {
const hash = event.currentTarget.getAttribute('data-hash'); const hash = event.currentTarget.getAttribute('data-hash');
if (hash) { if (hash) {
hashRef.current = hash;
timeoutId.current = window.setTimeout(() => { timeoutId.current = window.setTimeout(() => {
setHighlightedAddress(hash); // for better performance we update DOM-nodes directly bypassing React reconciliation
const nodes = window.document.querySelectorAll(`[data-hash="${ hashRef.current }"]`);
for (const node of nodes) {
node.classList.add('address-entity_highlighted');
}
}, 100); }, 100);
} }
}, []); }, []);
const onMouseLeave = React.useCallback(() => { const onMouseLeave = React.useCallback(() => {
setHighlightedAddress(null); if (hashRef.current) {
const nodes = window.document.querySelectorAll(`[data-hash="${ hashRef.current }"]`);
for (const node of nodes) {
node.classList.remove('address-entity_highlighted');
}
hashRef.current = null;
}
typeof timeoutId.current === 'number' && window.clearTimeout(timeoutId.current); typeof timeoutId.current === 'number' && window.clearTimeout(timeoutId.current);
}, []); }, []);
const value = React.useMemo(() => { const value = React.useMemo(() => {
return { return {
highlightedAddress,
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
}; };
}, [ highlightedAddress, onMouseEnter, onMouseLeave ]); }, [ onMouseEnter, onMouseLeave ]);
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
......
...@@ -2,6 +2,7 @@ import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; ...@@ -2,6 +2,7 @@ import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import scrollbar from './foundations/scrollbar'; import scrollbar from './foundations/scrollbar';
import addressEntity from './globals/address-entity';
import getDefaultTransitionProps from './utils/getDefaultTransitionProps'; import getDefaultTransitionProps from './utils/getDefaultTransitionProps';
const global = (props: StyleFunctionProps) => ({ const global = (props: StyleFunctionProps) => ({
...@@ -23,6 +24,7 @@ const global = (props: StyleFunctionProps) => ({ ...@@ -23,6 +24,7 @@ const global = (props: StyleFunctionProps) => ({
w: '100%', w: '100%',
}, },
...scrollbar(props), ...scrollbar(props),
...addressEntity(props),
}); });
export default global; export default global;
import { mode } from '@chakra-ui/theme-tools';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
const styles = (props: StyleFunctionProps) => {
return {
'.address-entity': {
'&.address-entity_highlighted': {
_before: {
content: `" "`,
position: 'absolute',
py: 1,
pl: 1,
pr: 0,
top: '-5px',
left: '-5px',
width: `100%`,
height: '100%',
borderRadius: 'base',
borderColor: mode('blue.200', 'blue.600')(props),
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: mode('blue.50', 'blue.900')(props),
zIndex: -1,
},
},
},
'.address-entity_no-copy': {
'&.address-entity_highlighted': {
_before: {
pr: 2,
},
},
},
};
};
export default styles;
import type { As } from '@chakra-ui/react'; import type { As } from '@chakra-ui/react';
import { Box, Flex, Skeleton, Tooltip, chakra, VStack, useColorModeValue } from '@chakra-ui/react'; import { Box, Flex, Skeleton, Tooltip, chakra, VStack } from '@chakra-ui/react';
import _omit from 'lodash/omit'; import _omit from 'lodash/omit';
import React from 'react'; import React from 'react';
...@@ -148,33 +148,16 @@ const AddressEntry = (props: EntityProps) => { ...@@ -148,33 +148,16 @@ const AddressEntry = (props: EntityProps) => {
const partsProps = _omit(props, [ 'className', 'onClick' ]); const partsProps = _omit(props, [ 'className', 'onClick' ]);
const context = useAddressHighlightContext(); const context = useAddressHighlightContext();
const highlightedBgColor = useColorModeValue('blue.50', 'blue.900');
const highlightedBorderColor = useColorModeValue('blue.200', 'blue.600');
return ( return (
<Container <Container
className={ props.className } // we have to use the global classnames here, see theme/global.ts
data-hash={ props.address.hash } // otherwise, if we use sx prop, Chakra will generate the same styles for each instance of the component on the page
className={ `${ props.className } address-entity ${ props.noCopy ? 'address-entity_no-copy' : '' }` }
data-hash={ context && !props.isLoading ? props.address.hash : undefined }
onMouseEnter={ context?.onMouseEnter } onMouseEnter={ context?.onMouseEnter }
onMouseLeave={ context?.onMouseLeave } onMouseLeave={ context?.onMouseLeave }
position="relative" position="relative"
_before={ !props.isLoading && context?.highlightedAddress === props.address.hash ? {
content: `" "`,
position: 'absolute',
py: 1,
pl: 1,
pr: props.noCopy ? 2 : 0,
top: '-5px',
left: '-5px',
width: `100%`,
height: '100%',
borderRadius: 'base',
borderColor: highlightedBorderColor,
borderWidth: '1px',
borderStyle: 'dashed',
bgColor: highlightedBgColor,
zIndex: -1,
} : undefined }
> >
<Icon { ...partsProps }/> <Icon { ...partsProps }/>
<Link { ...linkProps }> <Link { ...linkProps }>
......
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