Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
7aeea29f
Commit
7aeea29f
authored
Apr 22, 2025
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
use interactive tooltips for address highlight feature
parent
7ec9a2d6
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
52 additions
and
16 deletions
+52
-16
TruncatedTextTooltip.tsx
toolkit/components/truncation/TruncatedTextTooltip.tsx
+3
-1
CopyToClipboard.tsx
ui/shared/CopyToClipboard.tsx
+3
-1
HashStringShorten.tsx
ui/shared/HashStringShorten.tsx
+3
-2
HashStringShortenDynamic.tsx
ui/shared/HashStringShortenDynamic.tsx
+3
-1
TruncatedValue.tsx
ui/shared/TruncatedValue.tsx
+3
-2
AddressEntity.tsx
ui/shared/entities/address/AddressEntity.tsx
+17
-7
components.tsx
ui/shared/entities/base/components.tsx
+20
-2
No files found.
toolkit/components/truncation/TruncatedTextTooltip.tsx
View file @
7aeea29f
...
@@ -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
>
...
...
ui/shared/CopyToClipboard.tsx
View file @
7aeea29f
...
@@ -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
>
...
...
ui/shared/HashStringShorten.tsx
View file @
7aeea29f
...
@@ -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
>
);
);
...
...
ui/shared/HashStringShortenDynamic.tsx
View file @
7aeea29f
...
@@ -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
>
...
...
ui/shared/TruncatedValue.tsx
View file @
7aeea29f
...
@@ -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
}
...
...
ui/shared/entities/address/AddressEntity.tsx
View file @
7aeea29f
...
@@ -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
>
);
);
};
};
...
...
ui/shared/entities/base/components.tsx
View file @
7aeea29f
...
@@ -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
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment