Commit 7aeea29f authored by tom's avatar tom

use interactive tooltips for address highlight feature

parent 7ec9a2d6
...@@ -11,9 +11,10 @@ export interface TruncatedTextTooltipProps { ...@@ -11,9 +11,10 @@ export interface TruncatedTextTooltipProps {
children: React.ReactNode; children: React.ReactNode;
label: React.ReactNode; label: React.ReactNode;
placement?: Placement; placement?: Placement;
interactive?: boolean;
} }
export const TruncatedTextTooltip = React.memo(({ children, label, placement }: TruncatedTextTooltipProps) => { export const TruncatedTextTooltip = React.memo(({ children, label, placement, interactive }: TruncatedTextTooltipProps) => {
const childRef = React.useRef<HTMLElement>(null); const childRef = React.useRef<HTMLElement>(null);
const [ isTruncated, setTruncated ] = React.useState(false); const [ isTruncated, setTruncated ] = React.useState(false);
const { open, onToggle, onOpen, onClose } = useDisclosure(); const { open, onToggle, onOpen, onClose } = useDisclosure();
...@@ -83,6 +84,7 @@ export const TruncatedTextTooltip = React.memo(({ children, label, placement }: ...@@ -83,6 +84,7 @@ export const TruncatedTextTooltip = React.memo(({ children, label, placement }:
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }} contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
positioning={{ placement }} positioning={{ placement }}
open={ open } open={ open }
interactive={ interactive }
> >
{ modifiedChildren } { modifiedChildren }
</Tooltip> </Tooltip>
......
...@@ -13,10 +13,11 @@ export interface Props extends Omit<IconButtonProps, 'type' | 'loading'> { ...@@ -13,10 +13,11 @@ export interface Props extends Omit<IconButtonProps, 'type' | 'loading'> {
// Chakra v3 doesn't support tooltip inside tooltip - https://github.com/chakra-ui/chakra-ui/issues/9939#issuecomment-2817168121 // Chakra v3 doesn't support tooltip inside tooltip - https://github.com/chakra-ui/chakra-ui/issues/9939#issuecomment-2817168121
// so we disable the copy tooltip manually when the button is inside a tooltip // so we disable the copy tooltip manually when the button is inside a tooltip
noTooltip?: boolean; noTooltip?: boolean;
tooltipInteractive?: boolean;
} }
const CopyToClipboard = (props: Props) => { const CopyToClipboard = (props: Props) => {
const { text, type = 'text', isLoading, onClick, boxSize = 5, noTooltip, ...rest } = props; const { text, type = 'text', isLoading, onClick, boxSize = 5, noTooltip, tooltipInteractive, ...rest } = props;
const { hasCopied, copy, disclosure } = useClipboard(text); const { hasCopied, copy, disclosure } = useClipboard(text);
...@@ -76,6 +77,7 @@ const CopyToClipboard = (props: Props) => { ...@@ -76,6 +77,7 @@ const CopyToClipboard = (props: Props) => {
open={ disclosure.open } open={ disclosure.open }
onOpenChange={ disclosure.onOpenChange } onOpenChange={ disclosure.onOpenChange }
closeOnPointerDown={ false } closeOnPointerDown={ false }
interactive={ tooltipInteractive }
> >
{ button } { button }
</Tooltip> </Tooltip>
......
...@@ -7,11 +7,12 @@ import { Tooltip } from 'toolkit/chakra/tooltip'; ...@@ -7,11 +7,12 @@ import { Tooltip } from 'toolkit/chakra/tooltip';
interface Props { interface Props {
hash: string; hash: string;
noTooltip?: boolean; noTooltip?: boolean;
tooltipInteractive?: boolean;
type?: 'long' | 'short'; type?: 'long' | 'short';
as?: React.ElementType; as?: React.ElementType;
} }
const HashStringShorten = ({ hash, noTooltip, as = 'span', type }: Props) => { const HashStringShorten = ({ hash, noTooltip, as = 'span', type, tooltipInteractive }: Props) => {
const charNumber = type === 'long' ? 16 : 8; const charNumber = type === 'long' ? 16 : 8;
if (hash.length <= charNumber) { if (hash.length <= charNumber) {
return <chakra.span as={ as }>{ hash }</chakra.span>; return <chakra.span as={ as }>{ hash }</chakra.span>;
...@@ -24,7 +25,7 @@ const HashStringShorten = ({ hash, noTooltip, as = 'span', type }: Props) => { ...@@ -24,7 +25,7 @@ const HashStringShorten = ({ hash, noTooltip, as = 'span', type }: Props) => {
} }
return ( return (
<Tooltip content={ hash } disabled={ noTooltip }> <Tooltip content={ hash } interactive={ tooltipInteractive }>
{ content } { content }
</Tooltip> </Tooltip>
); );
......
...@@ -24,11 +24,12 @@ interface Props { ...@@ -24,11 +24,12 @@ interface Props {
hash: string; hash: string;
fontWeight?: string | number; fontWeight?: string | number;
noTooltip?: boolean; noTooltip?: boolean;
tooltipInteractive?: boolean;
tailLength?: number; tailLength?: number;
as?: React.ElementType; as?: React.ElementType;
} }
const HashStringShortenDynamic = ({ hash, fontWeight = '400', noTooltip, tailLength = TAIL_LENGTH, as = 'span' }: Props) => { const HashStringShortenDynamic = ({ hash, fontWeight = '400', noTooltip, tailLength = TAIL_LENGTH, as = 'span', tooltipInteractive }: Props) => {
const elementRef = useRef<HTMLSpanElement>(null); const elementRef = useRef<HTMLSpanElement>(null);
const [ displayedString, setDisplayedString ] = React.useState(hash); const [ displayedString, setDisplayedString ] = React.useState(hash);
...@@ -98,6 +99,7 @@ const HashStringShortenDynamic = ({ hash, fontWeight = '400', noTooltip, tailLen ...@@ -98,6 +99,7 @@ const HashStringShortenDynamic = ({ hash, fontWeight = '400', noTooltip, tailLen
<Tooltip <Tooltip
content={ hash } content={ hash }
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }} contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
interactive={ tooltipInteractive }
> >
{ content } { content }
</Tooltip> </Tooltip>
......
...@@ -10,11 +10,12 @@ interface Props { ...@@ -10,11 +10,12 @@ interface Props {
isLoading?: boolean; isLoading?: boolean;
value: string; value: string;
tooltipPlacement?: Placement; tooltipPlacement?: Placement;
tooltipInteractive?: boolean;
} }
const TruncatedValue = ({ className, isLoading, value, tooltipPlacement }: Props) => { const TruncatedValue = ({ className, isLoading, value, tooltipPlacement, tooltipInteractive }: Props) => {
return ( return (
<TruncatedTextTooltip label={ value } placement={ tooltipPlacement }> <TruncatedTextTooltip label={ value } placement={ tooltipPlacement } interactive={ tooltipInteractive }>
<Skeleton <Skeleton
className={ className } className={ className }
loading={ isLoading } loading={ isLoading }
......
...@@ -36,7 +36,9 @@ const Link = chakra((props: LinkProps) => { ...@@ -36,7 +36,9 @@ const Link = chakra((props: LinkProps) => {
); );
}); });
type IconProps = Pick<EntityProps, 'address' | 'isSafeAddress'> & EntityBase.IconBaseProps; type IconProps = Pick<EntityProps, 'address' | 'isSafeAddress'> & EntityBase.IconBaseProps & {
tooltipInteractive?: boolean;
};
const Icon = (props: IconProps) => { const Icon = (props: IconProps) => {
if (props.noIcon) { if (props.noIcon) {
...@@ -70,7 +72,7 @@ const Icon = (props: IconProps) => { ...@@ -70,7 +72,7 @@ const Icon = (props: IconProps) => {
const label = (isVerified ? 'verified ' : '') + (isProxy ? 'proxy contract' : 'contract'); const label = (isVerified ? 'verified ' : '') + (isProxy ? 'proxy contract' : 'contract');
return ( return (
<Tooltip content={ label.slice(0, 1).toUpperCase() + label.slice(1) }> <Tooltip content={ label.slice(0, 1).toUpperCase() + label.slice(1) } interactive={ props.tooltipInteractive }>
<span> <span>
<EntityBase.Icon <EntityBase.Icon
{ ...props } { ...props }
...@@ -90,7 +92,7 @@ const Icon = (props: IconProps) => { ...@@ -90,7 +92,7 @@ const Icon = (props: IconProps) => {
})(); })();
return ( return (
<Tooltip content={ label } disabled={ !label }> <Tooltip content={ label } disabled={ !label } interactive={ props.tooltipInteractive }>
<Flex marginRight={ styles.marginRight } position="relative"> <Flex marginRight={ styles.marginRight } position="relative">
<AddressIdenticon <AddressIdenticon
size={ props.variant === 'heading' ? 30 : 20 } size={ props.variant === 'heading' ? 30 : 20 }
...@@ -128,7 +130,12 @@ const Content = chakra((props: ContentProps) => { ...@@ -128,7 +130,12 @@ const Content = chakra((props: ContentProps) => {
); );
return ( return (
<Tooltip content={ label } contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }} triggerProps={{ minW: 0 }}> <Tooltip
content={ label }
contentProps={{ maxW: { base: 'calc(100vw - 8px)', lg: '400px' } }}
triggerProps={{ minW: 0 }}
interactive={ props.tooltipInteractive }
>
<Skeleton loading={ props.isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" { ...styles }> <Skeleton loading={ props.isLoading } overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap" { ...styles }>
{ nameText } { nameText }
</Skeleton> </Skeleton>
...@@ -174,7 +181,10 @@ const AddressEntity = (props: EntityProps) => { ...@@ -174,7 +181,10 @@ const AddressEntity = (props: EntityProps) => {
const settingsContext = useSettingsContext(); const settingsContext = useSettingsContext();
const altHash = !props.noAltHash && settingsContext?.addressFormat === 'bech32' ? toBech32Address(props.address.hash) : undefined; const altHash = !props.noAltHash && settingsContext?.addressFormat === 'bech32' ? toBech32Address(props.address.hash) : undefined;
const content = <Content { ...partsProps.content } altHash={ altHash }/>; // inside highlight context all tooltips should be interactive
// because non-interactive ones will not pass 'onMouseLeave' event to the parent component
// see issue - https://github.com/chakra-ui/chakra-ui/issues/9939#issuecomment-2810567024
const content = <Content { ...partsProps.content } altHash={ altHash } tooltipInteractive={ Boolean(highlightContext) }/>;
return ( return (
<Container <Container
...@@ -187,9 +197,9 @@ const AddressEntity = (props: EntityProps) => { ...@@ -187,9 +197,9 @@ const AddressEntity = (props: EntityProps) => {
position="relative" position="relative"
zIndex={ 0 } zIndex={ 0 }
> >
<Icon { ...partsProps.icon }/> <Icon { ...partsProps.icon } tooltipInteractive={ Boolean(highlightContext) }/>
{ props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> } { props.noLink ? content : <Link { ...partsProps.link }>{ content }</Link> }
<Copy { ...partsProps.copy } altHash={ altHash }/> <Copy { ...partsProps.copy } altHash={ altHash } tooltipInteractive={ Boolean(highlightContext) }/>
</Container> </Container>
); );
}; };
......
...@@ -115,9 +115,20 @@ const Icon = ({ isLoading, noIcon, variant, name, color, borderRadius, marginRig ...@@ -115,9 +115,20 @@ const Icon = ({ isLoading, noIcon, variant, name, color, borderRadius, marginRig
export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength' | 'noTooltip' | 'variant'> { export interface ContentBaseProps extends Pick<EntityBaseProps, 'className' | 'isLoading' | 'truncation' | 'tailLength' | 'noTooltip' | 'variant'> {
asProp?: React.ElementType; asProp?: React.ElementType;
text: string; text: string;
tooltipInteractive?: boolean;
} }
const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dynamic', tailLength, noTooltip, variant }: ContentBaseProps) => { const Content = chakra(({
className,
isLoading,
asProp,
text,
truncation = 'dynamic',
tailLength,
variant,
noTooltip,
tooltipInteractive,
}: ContentBaseProps) => {
const styles = getContentProps(variant); const styles = getContentProps(variant);
if (truncation === 'tail') { if (truncation === 'tail') {
...@@ -126,6 +137,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna ...@@ -126,6 +137,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
className={ className } className={ className }
isLoading={ isLoading } isLoading={ isLoading }
value={ text } value={ text }
tooltipInteractive={ tooltipInteractive }
{ ...styles } { ...styles }
/> />
); );
...@@ -140,6 +152,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna ...@@ -140,6 +152,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
as={ asProp } as={ asProp }
type="long" type="long"
noTooltip={ noTooltip } noTooltip={ noTooltip }
tooltipInteractive={ tooltipInteractive }
/> />
); );
case 'constant': case 'constant':
...@@ -148,6 +161,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna ...@@ -148,6 +161,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
hash={ text } hash={ text }
as={ asProp } as={ asProp }
noTooltip={ noTooltip } noTooltip={ noTooltip }
tooltipInteractive={ tooltipInteractive }
/> />
); );
case 'dynamic': case 'dynamic':
...@@ -157,6 +171,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna ...@@ -157,6 +171,7 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
as={ asProp } as={ asProp }
tailLength={ tailLength } tailLength={ tailLength }
noTooltip={ noTooltip } noTooltip={ noTooltip }
tooltipInteractive={ tooltipInteractive }
/> />
); );
case 'none': case 'none':
...@@ -178,7 +193,10 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna ...@@ -178,7 +193,10 @@ const Content = chakra(({ className, isLoading, asProp, text, truncation = 'dyna
); );
}); });
export type CopyBaseProps = Pick<CopyToClipboardProps, 'isLoading' | 'text'> & Pick<EntityBaseProps, 'noCopy' | 'noTooltip'>; export type CopyBaseProps =
Pick<CopyToClipboardProps, 'isLoading' | 'text' | 'tooltipInteractive'> &
Pick<EntityBaseProps, 'noCopy' | 'noTooltip'>
;
const Copy = ({ noCopy, ...props }: CopyBaseProps) => { const Copy = ({ noCopy, ...props }: CopyBaseProps) => {
if (noCopy) { if (noCopy) {
......
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