@lifi/widget 3.23.3 → 3.24.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +165 -0
- package/README.md +4 -8
- package/dist/esm/components/AppContainer.js +22 -8
- package/dist/esm/components/AppContainer.js.map +1 -1
- package/dist/esm/components/Avatar/Avatar.d.ts +6 -1
- package/dist/esm/components/Avatar/Avatar.js +4 -4
- package/dist/esm/components/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/Avatar/Avatar.style.d.ts +13 -4
- package/dist/esm/components/Avatar/Avatar.style.js +20 -10
- package/dist/esm/components/Avatar/Avatar.style.js.map +1 -1
- package/dist/esm/components/Avatar/SmallAvatar.d.ts +8 -2
- package/dist/esm/components/Avatar/SmallAvatar.js +7 -5
- package/dist/esm/components/Avatar/SmallAvatar.js.map +1 -1
- package/dist/esm/components/Avatar/TokenAvatar.d.ts +6 -0
- package/dist/esm/components/Avatar/TokenAvatar.js +7 -7
- package/dist/esm/components/Avatar/TokenAvatar.js.map +1 -1
- package/dist/esm/components/Avatar/utils.d.ts +1 -8
- package/dist/esm/components/Avatar/utils.js +5 -8
- package/dist/esm/components/Avatar/utils.js.map +1 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +6 -1
- package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
- package/dist/esm/components/Header/Header.style.d.ts +4 -1
- package/dist/esm/components/Header/Header.style.js +7 -5
- package/dist/esm/components/Header/Header.style.js.map +1 -1
- package/dist/esm/components/RouteCard/RouteCard.js +3 -3
- package/dist/esm/components/RouteCard/RouteCard.js.map +1 -1
- package/dist/esm/components/RouteCard/RouteCardEssentials.js +7 -13
- package/dist/esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
- package/dist/esm/components/Routes/RoutesExpanded.js.map +1 -1
- package/dist/esm/components/SelectTokenButton/SelectTokenButton.style.js +2 -2
- package/dist/esm/components/SendToWallet/SendToWalletButton.js +2 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
- package/dist/esm/components/StepActions/StepActions.js +3 -3
- package/dist/esm/components/StepActions/StepActions.style.d.ts +4 -1
- package/dist/esm/components/TokenList/TokenDetailsSheet.d.ts +6 -0
- package/dist/esm/components/TokenList/TokenDetailsSheet.js +24 -0
- package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.d.ts +8 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +67 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.d.ts +5 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.js +28 -0
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.js.map +1 -0
- package/dist/esm/components/TokenList/TokenListItem.js +29 -11
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/VirtualizedTokenList.js +45 -40
- package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
- package/dist/esm/components/TokenList/types.d.ts +7 -0
- package/dist/esm/components/TransactionDetails.js +7 -7
- package/dist/esm/components/TransactionDetails.js.map +1 -1
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/i18n/bn.json +5 -0
- package/dist/esm/i18n/de.json +5 -0
- package/dist/esm/i18n/en.json +4 -0
- package/dist/esm/i18n/es.json +5 -0
- package/dist/esm/i18n/fr.json +5 -0
- package/dist/esm/i18n/hi.json +5 -0
- package/dist/esm/i18n/id.json +5 -0
- package/dist/esm/i18n/it.json +5 -0
- package/dist/esm/i18n/ja.json +5 -0
- package/dist/esm/i18n/ko.json +5 -0
- package/dist/esm/i18n/pt.json +5 -0
- package/dist/esm/i18n/th.json +5 -0
- package/dist/esm/i18n/tr.json +5 -0
- package/dist/esm/i18n/uk.json +5 -0
- package/dist/esm/i18n/vi.json +5 -0
- package/dist/esm/i18n/zh.json +5 -0
- package/dist/esm/icons/lifi.d.ts +1 -1
- package/dist/esm/icons/lifi.js +1 -1
- package/dist/esm/icons/lifi.js.map +1 -1
- package/dist/esm/pages/ActiveTransactionsPage/ActiveTransactionsPage.js +1 -1
- package/dist/esm/pages/ActiveTransactionsPage/ActiveTransactionsPage.js.map +1 -1
- package/dist/esm/pages/RoutesPage/RoutesPage.js +1 -1
- package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
- package/dist/esm/pages/SelectChainPage/SelectChainPage.js +1 -1
- package/dist/esm/pages/SelectChainPage/SelectChainPage.js.map +1 -1
- package/dist/esm/pages/SelectEnabledToolsPage.js +1 -1
- package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
- package/dist/esm/pages/TransactionHistoryPage/TransactionHistoryPage.js +1 -1
- package/dist/esm/pages/TransactionHistoryPage/TransactionHistoryPage.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js +4 -4
- package/dist/esm/pages/TransactionPage/TokenValueBottomSheet.js.map +1 -1
- package/dist/esm/utils/format.d.ts +1 -0
- package/dist/esm/utils/format.js +16 -0
- package/dist/esm/utils/format.js.map +1 -1
- package/package.json +14 -14
- package/package.json.tmp +14 -14
- package/src/components/AppContainer.tsx +26 -8
- package/src/components/Avatar/Avatar.style.tsx +23 -11
- package/src/components/Avatar/Avatar.tsx +11 -6
- package/src/components/Avatar/SmallAvatar.tsx +13 -5
- package/src/components/Avatar/TokenAvatar.tsx +38 -8
- package/src/components/Avatar/utils.ts +5 -10
- package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +5 -1
- package/src/components/Header/Header.style.ts +10 -6
- package/src/components/RouteCard/RouteCard.tsx +3 -3
- package/src/components/RouteCard/RouteCardEssentials.tsx +7 -14
- package/src/components/Routes/RoutesExpanded.tsx +7 -2
- package/src/components/SelectTokenButton/SelectTokenButton.style.tsx +2 -2
- package/src/components/SendToWallet/SendToWalletButton.tsx +3 -2
- package/src/components/StepActions/StepActions.tsx +3 -3
- package/src/components/TokenList/TokenDetailsSheet.tsx +48 -0
- package/src/components/TokenList/TokenDetailsSheetContent.style.tsx +34 -0
- package/src/components/TokenList/TokenDetailsSheetContent.tsx +200 -0
- package/src/components/TokenList/TokenListItem.tsx +117 -50
- package/src/components/TokenList/VirtualizedTokenList.tsx +95 -74
- package/src/components/TokenList/types.ts +8 -0
- package/src/components/TransactionDetails.tsx +8 -8
- package/src/config/version.ts +1 -1
- package/src/i18n/bn.json +5 -0
- package/src/i18n/de.json +5 -0
- package/src/i18n/en.json +4 -0
- package/src/i18n/es.json +5 -0
- package/src/i18n/fr.json +5 -0
- package/src/i18n/hi.json +5 -0
- package/src/i18n/id.json +5 -0
- package/src/i18n/it.json +5 -0
- package/src/i18n/ja.json +5 -0
- package/src/i18n/ko.json +5 -0
- package/src/i18n/pt.json +5 -0
- package/src/i18n/th.json +5 -0
- package/src/i18n/tr.json +5 -0
- package/src/i18n/uk.json +5 -0
- package/src/i18n/vi.json +5 -0
- package/src/i18n/zh.json +5 -0
- package/src/icons/lifi.ts +1 -1
- package/src/pages/ActiveTransactionsPage/ActiveTransactionsPage.tsx +1 -0
- package/src/pages/RoutesPage/RoutesPage.tsx +1 -1
- package/src/pages/SelectChainPage/SelectChainPage.tsx +1 -1
- package/src/pages/SelectEnabledToolsPage.tsx +1 -1
- package/src/pages/TransactionHistoryPage/TransactionHistoryPage.tsx +1 -0
- package/src/pages/TransactionPage/TokenValueBottomSheet.tsx +4 -4
- package/src/utils/format.ts +19 -0
- package/LICENSE.md +0 -201
|
@@ -30,7 +30,11 @@ export const BaseTransactionButton: React.FC<BaseTransactionButtonProps> = ({
|
|
|
30
30
|
} else if (walletConfig?.onConnect) {
|
|
31
31
|
walletConfig.onConnect()
|
|
32
32
|
} else {
|
|
33
|
-
|
|
33
|
+
if (missingChain) {
|
|
34
|
+
openWalletMenu({ chain: missingChain })
|
|
35
|
+
} else {
|
|
36
|
+
openWalletMenu()
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
styled,
|
|
8
8
|
} from '@mui/material'
|
|
9
9
|
import type { WidgetSubvariant } from '../../types/widget.js'
|
|
10
|
-
import {
|
|
10
|
+
import { getAvatarMask } from '../Avatar/utils.js'
|
|
11
11
|
|
|
12
12
|
export const HeaderAppBar = styled(AppBar)(({ theme }) => ({
|
|
13
13
|
backgroundColor: 'transparent',
|
|
@@ -101,8 +101,12 @@ export const HeaderControlsContainer = styled(Box)(({ theme }) => ({
|
|
|
101
101
|
}),
|
|
102
102
|
}))
|
|
103
103
|
|
|
104
|
-
export const WalletAvatar = styled(Avatar
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
export const WalletAvatar = styled(Avatar, {
|
|
105
|
+
shouldForwardProp: (prop) => prop !== 'avatarSize' && prop !== 'badgeSize',
|
|
106
|
+
})<{ avatarSize?: number; badgeSize?: number }>(
|
|
107
|
+
({ avatarSize = 24, badgeSize = 12 }) => ({
|
|
108
|
+
mask: getAvatarMask(badgeSize),
|
|
109
|
+
width: avatarSize,
|
|
110
|
+
height: avatarSize,
|
|
111
|
+
})
|
|
112
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TokenAmount } from '@lifi/sdk'
|
|
2
|
-
import {
|
|
2
|
+
import { isRelayerStep } from '@lifi/sdk'
|
|
3
3
|
import ExpandLess from '@mui/icons-material/ExpandLess'
|
|
4
4
|
import ExpandMore from '@mui/icons-material/ExpandMore'
|
|
5
5
|
import { Box, Collapse } from '@mui/material'
|
|
@@ -53,8 +53,8 @@ export const RouteCard: React.FC<
|
|
|
53
53
|
(tag) => tag === 'CHEAPEST' || tag === 'FASTEST'
|
|
54
54
|
)
|
|
55
55
|
const tags: string[] = mainTag ? [mainTag] : []
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
56
|
+
const hasRelayerSupport = route.steps.every(isRelayerStep)
|
|
57
|
+
if (hasRelayerSupport) {
|
|
58
58
|
tags.push('GASLESS')
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRelayerStep } from '@lifi/sdk'
|
|
2
2
|
import AccessTimeFilled from '@mui/icons-material/AccessTimeFilled'
|
|
3
3
|
import LocalGasStationRounded from '@mui/icons-material/LocalGasStationRounded'
|
|
4
4
|
import { Box, Tooltip, Typography } from '@mui/material'
|
|
5
5
|
import { useTranslation } from 'react-i18next'
|
|
6
6
|
import { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js'
|
|
7
|
+
import { formatDuration } from '../../utils/format.js'
|
|
7
8
|
import { FeeBreakdownTooltip } from '../FeeBreakdownTooltip.js'
|
|
8
9
|
import { IconTypography } from '../IconTypography.js'
|
|
9
10
|
import { TokenRate } from '../TokenRate/TokenRate.js'
|
|
@@ -19,10 +20,9 @@ export const RouteCardEssentials: React.FC<RouteCardEssentialsProps> = ({
|
|
|
19
20
|
0
|
|
20
21
|
)
|
|
21
22
|
)
|
|
22
|
-
const executionTimeMinutes = Math.floor(executionTimeSeconds / 60)
|
|
23
23
|
const { gasCosts, feeCosts, combinedFeesUSD } =
|
|
24
24
|
getAccumulatedFeeCostsBreakdown(route)
|
|
25
|
-
const
|
|
25
|
+
const hasRelayerSupport = route.steps.every(isRelayerStep)
|
|
26
26
|
return (
|
|
27
27
|
<Box
|
|
28
28
|
sx={{
|
|
@@ -43,7 +43,7 @@ export const RouteCardEssentials: React.FC<RouteCardEssentialsProps> = ({
|
|
|
43
43
|
<FeeBreakdownTooltip
|
|
44
44
|
gasCosts={gasCosts}
|
|
45
45
|
feeCosts={feeCosts}
|
|
46
|
-
relayerSupport={
|
|
46
|
+
relayerSupport={hasRelayerSupport}
|
|
47
47
|
>
|
|
48
48
|
<Box
|
|
49
49
|
sx={{
|
|
@@ -56,7 +56,7 @@ export const RouteCardEssentials: React.FC<RouteCardEssentialsProps> = ({
|
|
|
56
56
|
<LocalGasStationRounded fontSize="inherit" />
|
|
57
57
|
</IconTypography>
|
|
58
58
|
<Typography
|
|
59
|
-
data-value={
|
|
59
|
+
data-value={hasRelayerSupport ? 0 : combinedFeesUSD}
|
|
60
60
|
sx={{
|
|
61
61
|
fontSize: 14,
|
|
62
62
|
color: 'text.primary',
|
|
@@ -64,7 +64,7 @@ export const RouteCardEssentials: React.FC<RouteCardEssentialsProps> = ({
|
|
|
64
64
|
lineHeight: 1,
|
|
65
65
|
}}
|
|
66
66
|
>
|
|
67
|
-
{
|
|
67
|
+
{hasRelayerSupport
|
|
68
68
|
? t('main.fees.free')
|
|
69
69
|
: t('format.currency', {
|
|
70
70
|
value: combinedFeesUSD,
|
|
@@ -90,14 +90,7 @@ export const RouteCardEssentials: React.FC<RouteCardEssentialsProps> = ({
|
|
|
90
90
|
lineHeight: 1,
|
|
91
91
|
}}
|
|
92
92
|
>
|
|
93
|
-
{(executionTimeSeconds
|
|
94
|
-
? executionTimeSeconds
|
|
95
|
-
: executionTimeMinutes
|
|
96
|
-
).toLocaleString(i18n.language, {
|
|
97
|
-
style: 'unit',
|
|
98
|
-
unit: executionTimeSeconds < 60 ? 'second' : 'minute',
|
|
99
|
-
unitDisplay: 'narrow',
|
|
100
|
-
})}
|
|
93
|
+
{formatDuration(executionTimeSeconds, i18n.language)}
|
|
101
94
|
</Typography>
|
|
102
95
|
</Box>
|
|
103
96
|
</Tooltip>
|
|
@@ -127,12 +127,17 @@ export const RoutesExpandedElement = () => {
|
|
|
127
127
|
|
|
128
128
|
return (
|
|
129
129
|
<RoutesExpandedCollapse
|
|
130
|
-
timeout={timeout.enter}
|
|
130
|
+
timeout={timeout.enter as number}
|
|
131
131
|
in={expanded}
|
|
132
132
|
orientation="horizontal"
|
|
133
133
|
onExited={onExit}
|
|
134
134
|
>
|
|
135
|
-
<Grow
|
|
135
|
+
<Grow
|
|
136
|
+
timeout={timeout.enter as number}
|
|
137
|
+
in={expanded}
|
|
138
|
+
mountOnEnter
|
|
139
|
+
unmountOnExit
|
|
140
|
+
>
|
|
136
141
|
<Container enableColorScheme minimumHeight={isLoading}>
|
|
137
142
|
<ScrollableContainer>
|
|
138
143
|
<Header>
|
|
@@ -106,9 +106,9 @@ export const CardContent = styled(MuiCardContent, {
|
|
|
106
106
|
...(cardVariant === 'filled' && {
|
|
107
107
|
'&:hover': {
|
|
108
108
|
cursor: 'pointer',
|
|
109
|
-
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.background.paper} 98%,
|
|
109
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.background.paper} 98%, black)`,
|
|
110
110
|
...theme.applyStyles('dark', {
|
|
111
|
-
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.background.paper} 98%,
|
|
111
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.background.paper} 98%, white)`,
|
|
112
112
|
}),
|
|
113
113
|
},
|
|
114
114
|
}),
|
|
@@ -113,9 +113,10 @@ export const SendToWalletButton: React.FC<CardProps> = (props) => {
|
|
|
113
113
|
// Timeout is needed here to push the collapseTransitionTime update to the back of the event loop so that it doesn't fired too quickly
|
|
114
114
|
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
115
115
|
useEffect(() => {
|
|
116
|
-
setTimeout(() => {
|
|
116
|
+
const timeout = setTimeout(() => {
|
|
117
117
|
collapseTransitionTime.current = 225
|
|
118
118
|
}, 0)
|
|
119
|
+
return () => clearTimeout(timeout)
|
|
119
120
|
}, [collapseTransitionTime])
|
|
120
121
|
|
|
121
122
|
const isOpenCollapse =
|
|
@@ -128,7 +129,7 @@ export const SendToWalletButton: React.FC<CardProps> = (props) => {
|
|
|
128
129
|
|
|
129
130
|
return (
|
|
130
131
|
<Collapse
|
|
131
|
-
timeout={collapseTransitionTime.current}
|
|
132
|
+
timeout={collapseTransitionTime.current as number}
|
|
132
133
|
in={isOpenCollapse}
|
|
133
134
|
mountOnEnter
|
|
134
135
|
unmountOnExit
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LiFiStep, StepExtended } from '@lifi/sdk'
|
|
2
|
-
import {
|
|
2
|
+
import { isRelayerStep } from '@lifi/sdk'
|
|
3
3
|
import ArrowForward from '@mui/icons-material/ArrowForward'
|
|
4
4
|
import ExpandLess from '@mui/icons-material/ExpandLess'
|
|
5
5
|
import ExpandMore from '@mui/icons-material/ExpandMore'
|
|
@@ -162,7 +162,7 @@ export const IncludedSteps: React.FC<IncludedStepsProps> = ({ step }) => {
|
|
|
162
162
|
) : null
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
const
|
|
165
|
+
const hasRelayerSupport = isRelayerStep(step)
|
|
166
166
|
|
|
167
167
|
return (
|
|
168
168
|
<Box
|
|
@@ -194,7 +194,7 @@ export const IncludedSteps: React.FC<IncludedStepsProps> = ({ step }) => {
|
|
|
194
194
|
<ProtocolStepDetailsLabel
|
|
195
195
|
step={step}
|
|
196
196
|
feeConfig={feeConfig}
|
|
197
|
-
relayerSupport={
|
|
197
|
+
relayerSupport={hasRelayerSupport}
|
|
198
198
|
/>
|
|
199
199
|
) : (
|
|
200
200
|
<SwapStepDetailsLabel step={step} />
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { forwardRef, useImperativeHandle, useRef, useState } from 'react'
|
|
2
|
+
import { BottomSheet } from '../BottomSheet/BottomSheet.js'
|
|
3
|
+
import type { BottomSheetBase } from '../BottomSheet/types.js'
|
|
4
|
+
import { TokenDetailsSheetContent } from './TokenDetailsSheetContent.js'
|
|
5
|
+
import type { TokenDetailsSheetBase } from './types.js'
|
|
6
|
+
interface TokenDetailsSheetProps {
|
|
7
|
+
chainId: number | undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const TokenDetailsSheet = forwardRef<
|
|
11
|
+
TokenDetailsSheetBase,
|
|
12
|
+
TokenDetailsSheetProps
|
|
13
|
+
>(({ chainId }, ref) => {
|
|
14
|
+
const bottomSheetRef = useRef<BottomSheetBase>(null)
|
|
15
|
+
const [tokenAddress, setTokenAddress] = useState<string | undefined>(
|
|
16
|
+
undefined
|
|
17
|
+
)
|
|
18
|
+
const [withoutContractAddress, setWithoutContractAddress] = useState(false)
|
|
19
|
+
|
|
20
|
+
useImperativeHandle(
|
|
21
|
+
ref,
|
|
22
|
+
() => ({
|
|
23
|
+
isOpen: () => bottomSheetRef.current?.isOpen(),
|
|
24
|
+
open: (address: string, noContractAddress: boolean) => {
|
|
25
|
+
setTokenAddress(address)
|
|
26
|
+
setWithoutContractAddress(noContractAddress)
|
|
27
|
+
bottomSheetRef.current?.open()
|
|
28
|
+
},
|
|
29
|
+
close: () => {
|
|
30
|
+
bottomSheetRef.current?.close()
|
|
31
|
+
setTokenAddress(undefined)
|
|
32
|
+
setWithoutContractAddress(false)
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
[]
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<BottomSheet ref={bottomSheetRef}>
|
|
40
|
+
<TokenDetailsSheetContent
|
|
41
|
+
ref={ref}
|
|
42
|
+
tokenAddress={tokenAddress}
|
|
43
|
+
withoutContractAddress={withoutContractAddress}
|
|
44
|
+
chainId={chainId}
|
|
45
|
+
/>
|
|
46
|
+
</BottomSheet>
|
|
47
|
+
)
|
|
48
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Box, Typography, styled } from '@mui/material'
|
|
2
|
+
import type { PageContainerProps } from '../PageContainer.js'
|
|
3
|
+
import { PageContainer } from '../PageContainer.js'
|
|
4
|
+
|
|
5
|
+
export const TokenDetailsSheetContainer = styled(
|
|
6
|
+
PageContainer
|
|
7
|
+
)<PageContainerProps>(({ theme }) => ({
|
|
8
|
+
display: 'flex',
|
|
9
|
+
flexDirection: 'column',
|
|
10
|
+
gap: theme.spacing(2),
|
|
11
|
+
paddingTop: theme.spacing(3),
|
|
12
|
+
paddingBottom: theme.spacing(3),
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
export const TokenDetailsSheetHeader = styled(Box)(({ theme }) => ({
|
|
16
|
+
display: 'flex',
|
|
17
|
+
flexDirection: 'row',
|
|
18
|
+
alignItems: 'flex-start',
|
|
19
|
+
justifyContent: 'space-between',
|
|
20
|
+
gap: theme.spacing(2),
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
export const Label = styled(Typography)(({ theme }) => ({
|
|
24
|
+
fontWeight: 500,
|
|
25
|
+
fontSize: '12px',
|
|
26
|
+
lineHeight: '16px',
|
|
27
|
+
color: theme.vars.palette.text.secondary,
|
|
28
|
+
}))
|
|
29
|
+
|
|
30
|
+
export const MetricContainer = styled(Box)(({ theme }) => ({
|
|
31
|
+
display: 'flex',
|
|
32
|
+
flexDirection: 'column',
|
|
33
|
+
gap: theme.spacing(1),
|
|
34
|
+
}))
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import Close from '@mui/icons-material/Close'
|
|
2
|
+
import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'
|
|
3
|
+
import OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'
|
|
4
|
+
import { Box, IconButton, Link, Skeleton, Typography } from '@mui/material'
|
|
5
|
+
import { type PropsWithChildren, forwardRef, useMemo } from 'react'
|
|
6
|
+
import { useTranslation } from 'react-i18next'
|
|
7
|
+
import { useAvailableChains } from '../../hooks/useAvailableChains.js'
|
|
8
|
+
import { useExplorer } from '../../hooks/useExplorer.js'
|
|
9
|
+
import { useTokenSearch } from '../../hooks/useTokenSearch.js'
|
|
10
|
+
import { formatTokenPrice } from '../../utils/format.js'
|
|
11
|
+
import { shortenAddress } from '../../utils/wallet.js'
|
|
12
|
+
import { TokenAvatar } from '../Avatar/TokenAvatar.js'
|
|
13
|
+
import { CardIconButton } from '../Card/CardIconButton.js'
|
|
14
|
+
import {
|
|
15
|
+
Label,
|
|
16
|
+
MetricContainer,
|
|
17
|
+
TokenDetailsSheetContainer,
|
|
18
|
+
TokenDetailsSheetHeader,
|
|
19
|
+
} from './TokenDetailsSheetContent.style.js'
|
|
20
|
+
import type { TokenDetailsSheetBase } from './types.js'
|
|
21
|
+
|
|
22
|
+
interface TokenDetailsSheetContentProps {
|
|
23
|
+
tokenAddress: string | undefined
|
|
24
|
+
chainId: number | undefined
|
|
25
|
+
withoutContractAddress: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const noDataLabel = '-'
|
|
29
|
+
|
|
30
|
+
export const TokenDetailsSheetContent = forwardRef<
|
|
31
|
+
TokenDetailsSheetBase,
|
|
32
|
+
TokenDetailsSheetContentProps
|
|
33
|
+
>(({ tokenAddress, chainId, withoutContractAddress }, ref) => {
|
|
34
|
+
const { t } = useTranslation()
|
|
35
|
+
const { getAddressLink } = useExplorer()
|
|
36
|
+
const { getChainById } = useAvailableChains()
|
|
37
|
+
|
|
38
|
+
const { token, isLoading } = useTokenSearch(
|
|
39
|
+
chainId,
|
|
40
|
+
tokenAddress,
|
|
41
|
+
!!tokenAddress
|
|
42
|
+
)
|
|
43
|
+
const chain = useMemo(() => getChainById(chainId), [chainId, getChainById])
|
|
44
|
+
|
|
45
|
+
const copyContractAddress = async (e: React.MouseEvent) => {
|
|
46
|
+
e.stopPropagation()
|
|
47
|
+
try {
|
|
48
|
+
// Clipboard API may throw if access is denied (e.g., in insecure contexts or older browsers)
|
|
49
|
+
await navigator.clipboard.writeText(tokenAddress || '')
|
|
50
|
+
} catch {
|
|
51
|
+
// Silently fail to avoid crashing the UI if clipboard write fails
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<TokenDetailsSheetContainer>
|
|
57
|
+
<TokenDetailsSheetHeader>
|
|
58
|
+
<Box
|
|
59
|
+
sx={{
|
|
60
|
+
display: 'flex',
|
|
61
|
+
flexDirection: 'row',
|
|
62
|
+
alignItems: 'center',
|
|
63
|
+
gap: 3,
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<TokenAvatar
|
|
67
|
+
token={token}
|
|
68
|
+
chain={chain}
|
|
69
|
+
tokenAvatarSize={72}
|
|
70
|
+
chainAvatarSize={28}
|
|
71
|
+
isLoading={isLoading}
|
|
72
|
+
/>
|
|
73
|
+
<MetricContainer>
|
|
74
|
+
{isLoading ? (
|
|
75
|
+
<>
|
|
76
|
+
<Skeleton variant="rounded" width={80} height={24} />
|
|
77
|
+
<Skeleton variant="rounded" width={80} height={16} />
|
|
78
|
+
</>
|
|
79
|
+
) : (
|
|
80
|
+
<>
|
|
81
|
+
<Typography
|
|
82
|
+
fontWeight={700}
|
|
83
|
+
fontSize="24px"
|
|
84
|
+
lineHeight="24px"
|
|
85
|
+
color="text.primary"
|
|
86
|
+
>
|
|
87
|
+
{token?.symbol || noDataLabel}
|
|
88
|
+
</Typography>
|
|
89
|
+
<Label>{token?.name || noDataLabel}</Label>
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
92
|
+
</MetricContainer>
|
|
93
|
+
</Box>
|
|
94
|
+
<IconButton
|
|
95
|
+
onClick={(e) => {
|
|
96
|
+
e.stopPropagation()
|
|
97
|
+
if (ref && typeof ref !== 'function') {
|
|
98
|
+
ref.current?.close()
|
|
99
|
+
}
|
|
100
|
+
}}
|
|
101
|
+
sx={{ mt: '-8px', mr: '-8px' }}
|
|
102
|
+
>
|
|
103
|
+
<Close />
|
|
104
|
+
</IconButton>
|
|
105
|
+
</TokenDetailsSheetHeader>
|
|
106
|
+
<MetricWithSkeleton
|
|
107
|
+
isLoading={isLoading}
|
|
108
|
+
label={t('tokenMetric.currentPrice')}
|
|
109
|
+
width={200}
|
|
110
|
+
height={40}
|
|
111
|
+
>
|
|
112
|
+
<Typography
|
|
113
|
+
sx={{
|
|
114
|
+
fontWeight: 700,
|
|
115
|
+
fontSize: '32px',
|
|
116
|
+
lineHeight: '40px',
|
|
117
|
+
color: 'text.primary',
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{token
|
|
121
|
+
? t('format.currency', {
|
|
122
|
+
value: formatTokenPrice('1', token.priceUSD, token.decimals),
|
|
123
|
+
})
|
|
124
|
+
: noDataLabel}
|
|
125
|
+
</Typography>
|
|
126
|
+
</MetricWithSkeleton>
|
|
127
|
+
{!withoutContractAddress && (
|
|
128
|
+
<MetricWithSkeleton
|
|
129
|
+
isLoading={isLoading}
|
|
130
|
+
label={t('tokenMetric.contractAddress')}
|
|
131
|
+
width={200}
|
|
132
|
+
height={24}
|
|
133
|
+
>
|
|
134
|
+
<Box
|
|
135
|
+
sx={{
|
|
136
|
+
display: 'flex',
|
|
137
|
+
flexDirection: 'row',
|
|
138
|
+
alignItems: 'center',
|
|
139
|
+
gap: 1,
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<Typography
|
|
143
|
+
sx={{
|
|
144
|
+
fontWeight: 700,
|
|
145
|
+
fontSize: '18px',
|
|
146
|
+
lineHeight: '24px',
|
|
147
|
+
color: 'text.primary',
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{shortenAddress(tokenAddress)}
|
|
151
|
+
</Typography>
|
|
152
|
+
{tokenAddress && (
|
|
153
|
+
<CardIconButton size="small" onClick={copyContractAddress}>
|
|
154
|
+
<ContentCopyRounded fontSize="inherit" />
|
|
155
|
+
</CardIconButton>
|
|
156
|
+
)}
|
|
157
|
+
{tokenAddress && (
|
|
158
|
+
<CardIconButton
|
|
159
|
+
size="small"
|
|
160
|
+
LinkComponent={Link}
|
|
161
|
+
href={getAddressLink(tokenAddress, chainId)}
|
|
162
|
+
target="_blank"
|
|
163
|
+
rel="nofollow noreferrer"
|
|
164
|
+
onClick={(e) => e.stopPropagation()}
|
|
165
|
+
>
|
|
166
|
+
<OpenInNewRounded fontSize="inherit" />
|
|
167
|
+
</CardIconButton>
|
|
168
|
+
)}
|
|
169
|
+
</Box>
|
|
170
|
+
</MetricWithSkeleton>
|
|
171
|
+
)}
|
|
172
|
+
</TokenDetailsSheetContainer>
|
|
173
|
+
)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
interface MetricWithSkeletonProps {
|
|
177
|
+
label: string
|
|
178
|
+
isLoading: boolean
|
|
179
|
+
width: number
|
|
180
|
+
height: number
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const MetricWithSkeleton = ({
|
|
184
|
+
label,
|
|
185
|
+
width,
|
|
186
|
+
height,
|
|
187
|
+
isLoading,
|
|
188
|
+
children,
|
|
189
|
+
}: PropsWithChildren<MetricWithSkeletonProps>) => {
|
|
190
|
+
return (
|
|
191
|
+
<MetricContainer>
|
|
192
|
+
<Label>{label}</Label>
|
|
193
|
+
{isLoading ? (
|
|
194
|
+
<Skeleton variant="rounded" width={width} height={height} />
|
|
195
|
+
) : (
|
|
196
|
+
children
|
|
197
|
+
)}
|
|
198
|
+
</MetricContainer>
|
|
199
|
+
)
|
|
200
|
+
}
|