@lifi/widget 3.23.3 → 3.24.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.
Files changed (156) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +165 -0
  3. package/README.md +14 -14
  4. package/dist/esm/components/AppContainer.js +22 -8
  5. package/dist/esm/components/AppContainer.js.map +1 -1
  6. package/dist/esm/components/Avatar/Avatar.d.ts +6 -1
  7. package/dist/esm/components/Avatar/Avatar.js +4 -4
  8. package/dist/esm/components/Avatar/Avatar.js.map +1 -1
  9. package/dist/esm/components/Avatar/Avatar.style.d.ts +13 -4
  10. package/dist/esm/components/Avatar/Avatar.style.js +20 -10
  11. package/dist/esm/components/Avatar/Avatar.style.js.map +1 -1
  12. package/dist/esm/components/Avatar/SmallAvatar.d.ts +8 -2
  13. package/dist/esm/components/Avatar/SmallAvatar.js +7 -5
  14. package/dist/esm/components/Avatar/SmallAvatar.js.map +1 -1
  15. package/dist/esm/components/Avatar/TokenAvatar.d.ts +6 -0
  16. package/dist/esm/components/Avatar/TokenAvatar.js +7 -7
  17. package/dist/esm/components/Avatar/TokenAvatar.js.map +1 -1
  18. package/dist/esm/components/Avatar/utils.d.ts +1 -8
  19. package/dist/esm/components/Avatar/utils.js +5 -8
  20. package/dist/esm/components/Avatar/utils.js.map +1 -1
  21. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +6 -1
  22. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
  23. package/dist/esm/components/Header/Header.style.d.ts +4 -1
  24. package/dist/esm/components/Header/Header.style.js +8 -5
  25. package/dist/esm/components/Header/Header.style.js.map +1 -1
  26. package/dist/esm/components/RouteCard/RouteCardEssentials.js +2 -8
  27. package/dist/esm/components/RouteCard/RouteCardEssentials.js.map +1 -1
  28. package/dist/esm/components/Routes/RoutesExpanded.js.map +1 -1
  29. package/dist/esm/components/SelectTokenButton/SelectTokenButton.style.js +2 -2
  30. package/dist/esm/components/SendToWallet/SendToWalletButton.js +2 -1
  31. package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
  32. package/dist/esm/components/Step/CircularProgress.style.js +22 -17
  33. package/dist/esm/components/Step/CircularProgress.style.js.map +1 -1
  34. package/dist/esm/components/StepActions/StepActions.style.d.ts +4 -1
  35. package/dist/esm/components/TokenList/TokenDetailsSheet.d.ts +6 -0
  36. package/dist/esm/components/TokenList/TokenDetailsSheet.js +24 -0
  37. package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -0
  38. package/dist/esm/components/TokenList/TokenDetailsSheetContent.d.ts +8 -0
  39. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +67 -0
  40. package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -0
  41. package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.d.ts +5 -0
  42. package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.js +28 -0
  43. package/dist/esm/components/TokenList/TokenDetailsSheetContent.style.js.map +1 -0
  44. package/dist/esm/components/TokenList/TokenList.js +2 -2
  45. package/dist/esm/components/TokenList/TokenList.js.map +1 -1
  46. package/dist/esm/components/TokenList/TokenListItem.js +29 -11
  47. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  48. package/dist/esm/components/TokenList/VirtualizedTokenList.js +45 -40
  49. package/dist/esm/components/TokenList/VirtualizedTokenList.js.map +1 -1
  50. package/dist/esm/components/TokenList/types.d.ts +7 -0
  51. package/dist/esm/config/version.d.ts +1 -1
  52. package/dist/esm/config/version.js +1 -1
  53. package/dist/esm/hooks/useTokenBalances.d.ts +2 -1
  54. package/dist/esm/hooks/useTokenBalances.js +2 -2
  55. package/dist/esm/hooks/useTokenBalances.js.map +1 -1
  56. package/dist/esm/hooks/useTokenSearch.d.ts +2 -1
  57. package/dist/esm/hooks/useTokenSearch.js +7 -2
  58. package/dist/esm/hooks/useTokenSearch.js.map +1 -1
  59. package/dist/esm/hooks/useTokens.d.ts +2 -1
  60. package/dist/esm/hooks/useTokens.js +6 -13
  61. package/dist/esm/hooks/useTokens.js.map +1 -1
  62. package/dist/esm/i18n/bn.json +5 -0
  63. package/dist/esm/i18n/de.json +5 -0
  64. package/dist/esm/i18n/en.json +4 -0
  65. package/dist/esm/i18n/es.json +5 -0
  66. package/dist/esm/i18n/fr.json +5 -0
  67. package/dist/esm/i18n/hi.json +5 -0
  68. package/dist/esm/i18n/id.json +5 -0
  69. package/dist/esm/i18n/it.json +5 -0
  70. package/dist/esm/i18n/ja.json +5 -0
  71. package/dist/esm/i18n/ko.json +5 -0
  72. package/dist/esm/i18n/pt.json +5 -0
  73. package/dist/esm/i18n/th.json +5 -0
  74. package/dist/esm/i18n/tr.json +5 -0
  75. package/dist/esm/i18n/uk.json +5 -0
  76. package/dist/esm/i18n/vi.json +5 -0
  77. package/dist/esm/i18n/zh.json +5 -0
  78. package/dist/esm/icons/lifi.d.ts +1 -1
  79. package/dist/esm/icons/lifi.js +1 -1
  80. package/dist/esm/icons/lifi.js.map +1 -1
  81. package/dist/esm/index.d.ts +1 -0
  82. package/dist/esm/index.js +1 -0
  83. package/dist/esm/index.js.map +1 -1
  84. package/dist/esm/pages/ActiveTransactionsPage/ActiveTransactionsPage.js +1 -1
  85. package/dist/esm/pages/ActiveTransactionsPage/ActiveTransactionsPage.js.map +1 -1
  86. package/dist/esm/pages/RoutesPage/RoutesPage.js +1 -1
  87. package/dist/esm/pages/RoutesPage/RoutesPage.js.map +1 -1
  88. package/dist/esm/pages/SelectChainPage/SelectChainPage.js +1 -1
  89. package/dist/esm/pages/SelectChainPage/SelectChainPage.js.map +1 -1
  90. package/dist/esm/pages/SelectEnabledToolsPage.js +1 -1
  91. package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
  92. package/dist/esm/pages/TransactionHistoryPage/TransactionHistoryPage.js +1 -1
  93. package/dist/esm/pages/TransactionHistoryPage/TransactionHistoryPage.js.map +1 -1
  94. package/dist/esm/pages/TransactionPage/StatusBottomSheet.style.js +18 -7
  95. package/dist/esm/pages/TransactionPage/StatusBottomSheet.style.js.map +1 -1
  96. package/dist/esm/types/widget.d.ts +2 -0
  97. package/dist/esm/utils/format.d.ts +1 -0
  98. package/dist/esm/utils/format.js +16 -0
  99. package/dist/esm/utils/format.js.map +1 -1
  100. package/dist/esm/utils/item.d.ts +7 -2
  101. package/dist/esm/utils/item.js +10 -3
  102. package/dist/esm/utils/item.js.map +1 -1
  103. package/package.json +18 -18
  104. package/package.json.tmp +18 -18
  105. package/src/components/AppContainer.tsx +26 -8
  106. package/src/components/Avatar/Avatar.style.tsx +23 -11
  107. package/src/components/Avatar/Avatar.tsx +11 -6
  108. package/src/components/Avatar/SmallAvatar.tsx +13 -5
  109. package/src/components/Avatar/TokenAvatar.tsx +38 -8
  110. package/src/components/Avatar/utils.ts +5 -10
  111. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +5 -1
  112. package/src/components/Header/Header.style.ts +11 -6
  113. package/src/components/RouteCard/RouteCardEssentials.tsx +2 -9
  114. package/src/components/Routes/RoutesExpanded.tsx +7 -2
  115. package/src/components/SelectTokenButton/SelectTokenButton.style.tsx +2 -2
  116. package/src/components/SendToWallet/SendToWalletButton.tsx +3 -2
  117. package/src/components/Step/CircularProgress.style.tsx +25 -17
  118. package/src/components/TokenList/TokenDetailsSheet.tsx +48 -0
  119. package/src/components/TokenList/TokenDetailsSheetContent.style.tsx +34 -0
  120. package/src/components/TokenList/TokenDetailsSheetContent.tsx +200 -0
  121. package/src/components/TokenList/TokenList.tsx +7 -2
  122. package/src/components/TokenList/TokenListItem.tsx +117 -50
  123. package/src/components/TokenList/VirtualizedTokenList.tsx +95 -74
  124. package/src/components/TokenList/types.ts +8 -0
  125. package/src/config/version.ts +1 -1
  126. package/src/hooks/useTokenBalances.ts +9 -3
  127. package/src/hooks/useTokenSearch.ts +11 -2
  128. package/src/hooks/useTokens.ts +10 -21
  129. package/src/i18n/bn.json +5 -0
  130. package/src/i18n/de.json +5 -0
  131. package/src/i18n/en.json +4 -0
  132. package/src/i18n/es.json +5 -0
  133. package/src/i18n/fr.json +5 -0
  134. package/src/i18n/hi.json +5 -0
  135. package/src/i18n/id.json +5 -0
  136. package/src/i18n/it.json +5 -0
  137. package/src/i18n/ja.json +5 -0
  138. package/src/i18n/ko.json +5 -0
  139. package/src/i18n/pt.json +5 -0
  140. package/src/i18n/th.json +5 -0
  141. package/src/i18n/tr.json +5 -0
  142. package/src/i18n/uk.json +5 -0
  143. package/src/i18n/vi.json +5 -0
  144. package/src/i18n/zh.json +5 -0
  145. package/src/icons/lifi.ts +1 -1
  146. package/src/index.ts +1 -0
  147. package/src/pages/ActiveTransactionsPage/ActiveTransactionsPage.tsx +1 -0
  148. package/src/pages/RoutesPage/RoutesPage.tsx +1 -1
  149. package/src/pages/SelectChainPage/SelectChainPage.tsx +1 -1
  150. package/src/pages/SelectEnabledToolsPage.tsx +1 -1
  151. package/src/pages/TransactionHistoryPage/TransactionHistoryPage.tsx +1 -0
  152. package/src/pages/TransactionPage/StatusBottomSheet.style.tsx +19 -11
  153. package/src/types/widget.ts +2 -0
  154. package/src/utils/format.ts +19 -0
  155. package/src/utils/item.ts +29 -4
  156. package/LICENSE.md +0 -201
@@ -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
+ }
@@ -42,7 +42,7 @@ export const TokenList: FC<TokenListProps> = ({
42
42
  isBalanceLoading,
43
43
  featuredTokens,
44
44
  popularTokens,
45
- } = useTokenBalances(selectedChainId)
45
+ } = useTokenBalances(selectedChainId, formType)
46
46
 
47
47
  let filteredTokens = (tokensWithBalance ?? chainTokens ?? []) as TokenAmount[]
48
48
  const normalizedSearchFilter = tokenSearchFilter?.replaceAll('$', '')
@@ -70,7 +70,12 @@ export const TokenList: FC<TokenListProps> = ({
70
70
  !!selectedChainId
71
71
 
72
72
  const { token: searchedToken, isLoading: isSearchedTokenLoading } =
73
- useTokenSearch(selectedChainId, normalizedSearchFilter, tokenSearchEnabled)
73
+ useTokenSearch(
74
+ selectedChainId,
75
+ normalizedSearchFilter,
76
+ tokenSearchEnabled,
77
+ formType
78
+ )
74
79
 
75
80
  const isLoading =
76
81
  isTokensLoading ||
@@ -1,9 +1,8 @@
1
1
  import { ChainType } from '@lifi/sdk'
2
- import OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'
2
+ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'
3
3
  import {
4
4
  Avatar,
5
5
  Box,
6
- Link,
7
6
  ListItemAvatar,
8
7
  ListItemText,
9
8
  Skeleton,
@@ -13,7 +12,6 @@ import {
13
12
  import type { MouseEventHandler } from 'react'
14
13
  import { useRef, useState } from 'react'
15
14
  import { useTranslation } from 'react-i18next'
16
- import { useExplorer } from '../../hooks/useExplorer.js'
17
15
  import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
18
16
  import { shortenAddress } from '../../utils/wallet.js'
19
17
  import { ListItemButton } from '../ListItem/ListItemButton.js'
@@ -34,6 +32,7 @@ export const TokenListItem: React.FC<TokenListItemProps> = ({
34
32
  isBalanceLoading,
35
33
  startAdornment,
36
34
  endAdornment,
35
+ onShowTokenDetails,
37
36
  }) => {
38
37
  const handleClick: MouseEventHandler<HTMLDivElement> = (e) => {
39
38
  e.stopPropagation()
@@ -53,6 +52,7 @@ export const TokenListItem: React.FC<TokenListItemProps> = ({
53
52
  accountAddress={accountAddress}
54
53
  isBalanceLoading={isBalanceLoading}
55
54
  onClick={handleClick}
55
+ onShowTokenDetails={onShowTokenDetails}
56
56
  />
57
57
  {endAdornment}
58
58
  </ListItem>
@@ -77,26 +77,51 @@ export const TokenListItemAvatar: React.FC<TokenListItemAvatarProps> = ({
77
77
  )
78
78
  }
79
79
 
80
+ interface OpenTokenDetailsButtonProps {
81
+ tokenAddress: string | undefined
82
+ withoutContractAddress: boolean
83
+ onClick: (tokenAddress: string, withoutContractAddress: boolean) => void
84
+ }
85
+
86
+ const OpenTokenDetailsButton = ({
87
+ tokenAddress,
88
+ withoutContractAddress,
89
+ onClick,
90
+ }: OpenTokenDetailsButtonProps) => {
91
+ if (!tokenAddress) {
92
+ return null
93
+ }
94
+ return (
95
+ <IconButton
96
+ size="small"
97
+ onClick={(e) => {
98
+ e.stopPropagation()
99
+ onClick(tokenAddress, withoutContractAddress)
100
+ }}
101
+ >
102
+ <InfoOutlinedIcon />
103
+ </IconButton>
104
+ )
105
+ }
106
+
80
107
  export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
81
108
  onClick,
82
109
  token,
83
110
  chain,
84
111
  accountAddress,
85
112
  isBalanceLoading,
113
+ onShowTokenDetails,
86
114
  }) => {
87
115
  const { t } = useTranslation()
88
- const { getAddressLink } = useExplorer()
89
-
90
116
  const container = useRef(null)
91
117
  const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
92
118
  const [showAddress, setShowAddress] = useState(false)
93
119
 
94
- const tokenAddress =
95
- chain?.chainType === ChainType.UTXO ? accountAddress : token.address
120
+ const withoutContractAddress = chain?.chainType === ChainType.UTXO
96
121
 
97
122
  const onMouseEnter = () => {
98
123
  timeoutId.current = setTimeout(() => {
99
- if (tokenAddress) {
124
+ if (token.address) {
100
125
  setShowAddress(true)
101
126
  }
102
127
  }, 350)
@@ -108,6 +133,7 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
108
133
  setShowAddress(false)
109
134
  }
110
135
  }
136
+
111
137
  const tokenAmount = formatTokenAmount(token.amount, token.decimals)
112
138
  const tokenPrice = formatTokenPrice(
113
139
  token.amount,
@@ -133,21 +159,13 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
133
159
  },
134
160
  }}
135
161
  secondary={
136
- <Box
137
- ref={container}
138
- sx={{
139
- position: 'relative',
140
- height: 20,
141
- }}
142
- >
143
- <Slide
144
- direction="down"
145
- in={!showAddress}
146
- container={container.current}
147
- style={{
148
- position: 'absolute',
162
+ withoutContractAddress ? (
163
+ <Box
164
+ ref={container}
165
+ sx={{
166
+ height: 20,
167
+ display: 'flex',
149
168
  }}
150
- appear={false}
151
169
  >
152
170
  <Box
153
171
  sx={{
@@ -156,44 +174,93 @@ export const TokenListItemButton: React.FC<TokenListItemButtonProps> = ({
156
174
  >
157
175
  {token.name}
158
176
  </Box>
159
- </Slide>
160
- <Slide
161
- direction="up"
162
- in={showAddress}
163
- container={container.current}
164
- style={{
165
- position: 'absolute',
166
- }}
167
- appear={false}
168
- mountOnEnter
169
- >
170
177
  <Box
171
178
  sx={{
172
- display: 'flex',
179
+ position: 'relative',
173
180
  }}
181
+ >
182
+ <Slide
183
+ direction="up"
184
+ in={showAddress}
185
+ container={container.current}
186
+ style={{
187
+ position: 'absolute',
188
+ }}
189
+ appear={false}
190
+ mountOnEnter
191
+ >
192
+ <Box
193
+ sx={{
194
+ display: 'flex',
195
+ }}
196
+ >
197
+ <OpenTokenDetailsButton
198
+ tokenAddress={token.address}
199
+ withoutContractAddress={withoutContractAddress}
200
+ onClick={onShowTokenDetails}
201
+ />
202
+ </Box>
203
+ </Slide>
204
+ </Box>
205
+ </Box>
206
+ ) : (
207
+ <Box
208
+ ref={container}
209
+ sx={{
210
+ position: 'relative',
211
+ height: 20,
212
+ }}
213
+ >
214
+ <Slide
215
+ direction="down"
216
+ in={!showAddress}
217
+ container={container.current}
218
+ style={{
219
+ position: 'absolute',
220
+ }}
221
+ appear={false}
174
222
  >
175
223
  <Box
176
224
  sx={{
177
- display: 'flex',
178
- alignItems: 'center',
179
- pt: 0.125,
225
+ pt: 0.25,
180
226
  }}
181
227
  >
182
- {shortenAddress(tokenAddress)}
228
+ {token.name}
183
229
  </Box>
184
- <IconButton
185
- size="small"
186
- LinkComponent={Link}
187
- href={getAddressLink(tokenAddress!, chain)}
188
- target="_blank"
189
- rel="nofollow noreferrer"
190
- onClick={(e) => e.stopPropagation()}
230
+ </Slide>
231
+ <Slide
232
+ direction="up"
233
+ in={showAddress}
234
+ container={container.current}
235
+ style={{
236
+ position: 'absolute',
237
+ }}
238
+ appear={false}
239
+ mountOnEnter
240
+ >
241
+ <Box
242
+ sx={{
243
+ display: 'flex',
244
+ }}
191
245
  >
192
- <OpenInNewRounded />
193
- </IconButton>
194
- </Box>
195
- </Slide>
196
- </Box>
246
+ <Box
247
+ sx={{
248
+ display: 'flex',
249
+ alignItems: 'center',
250
+ pt: 0.125,
251
+ }}
252
+ >
253
+ {shortenAddress(token.address)}
254
+ </Box>
255
+ <OpenTokenDetailsButton
256
+ tokenAddress={token.address}
257
+ withoutContractAddress={withoutContractAddress}
258
+ onClick={onShowTokenDetails}
259
+ />
260
+ </Box>
261
+ </Slide>
262
+ </Box>
263
+ )
197
264
  }
198
265
  />
199
266
  {accountAddress ? (
@@ -1,11 +1,15 @@
1
1
  import { List, Typography } from '@mui/material'
2
2
  import { useVirtualizer } from '@tanstack/react-virtual'
3
3
  import type { FC } from 'react'
4
- import { useEffect } from 'react'
4
+ import { useCallback, useEffect, useRef } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import type { TokenAmount } from '../../types/token.js'
7
+ import { TokenDetailsSheet } from './TokenDetailsSheet.js'
7
8
  import { TokenListItem, TokenListItemSkeleton } from './TokenListItem.js'
8
- import type { VirtualizedTokenListProps } from './types.js'
9
+ import type {
10
+ TokenDetailsSheetBase,
11
+ VirtualizedTokenListProps,
12
+ } from './types.js'
9
13
 
10
14
  export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
11
15
  account,
@@ -20,6 +24,15 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
20
24
  }) => {
21
25
  const { t } = useTranslation()
22
26
 
27
+ const tokenDetailsSheetRef = useRef<TokenDetailsSheetBase>(null)
28
+
29
+ const onShowTokenDetails = useCallback(
30
+ (tokenAddress: string, noContractAddress: boolean) => {
31
+ tokenDetailsSheetRef.current?.open(tokenAddress, noContractAddress)
32
+ },
33
+ []
34
+ )
35
+
23
36
  const { getVirtualItems, getTotalSize, scrollToIndex } = useVirtualizer({
24
37
  count: tokens.length,
25
38
  overscan: 10,
@@ -75,78 +88,86 @@ export const VirtualizedTokenList: FC<VirtualizedTokenListProps> = ({
75
88
  }
76
89
 
77
90
  return (
78
- <List style={{ height: getTotalSize() }} disablePadding>
79
- {getVirtualItems().map((item) => {
80
- const currentToken = tokens[item.index]
81
- const previousToken: TokenAmount | undefined = tokens[item.index - 1]
82
-
83
- const isFirstFeaturedToken = currentToken.featured && item.index === 0
84
-
85
- const isTransitionFromFeaturedTokens =
86
- previousToken?.featured && !currentToken.featured
87
-
88
- const isTransitionFromMyTokens =
89
- previousToken?.amount && !currentToken.amount
90
-
91
- const isTransitionToMyTokens =
92
- isTransitionFromFeaturedTokens && currentToken.amount
93
-
94
- const isTransitionToPopularTokens =
95
- (isTransitionFromFeaturedTokens || isTransitionFromMyTokens) &&
96
- currentToken.popular
97
-
98
- const shouldShowAllTokensCategory =
99
- isTransitionFromMyTokens ||
100
- isTransitionFromFeaturedTokens ||
101
- (previousToken?.popular && !currentToken.popular)
102
-
103
- const startAdornmentLabel = showCategories
104
- ? (() => {
105
- if (isFirstFeaturedToken) {
106
- return t('main.featuredTokens')
107
- }
108
- if (isTransitionToMyTokens) {
109
- return t('main.myTokens')
91
+ <>
92
+ <List
93
+ className="long-list"
94
+ style={{ height: getTotalSize() }}
95
+ disablePadding
96
+ >
97
+ {getVirtualItems().map((item) => {
98
+ const currentToken = tokens[item.index]
99
+ const previousToken: TokenAmount | undefined = tokens[item.index - 1]
100
+
101
+ const isFirstFeaturedToken = currentToken.featured && item.index === 0
102
+
103
+ const isTransitionFromFeaturedTokens =
104
+ previousToken?.featured && !currentToken.featured
105
+
106
+ const isTransitionFromMyTokens =
107
+ previousToken?.amount && !currentToken.amount
108
+
109
+ const isTransitionToMyTokens =
110
+ isTransitionFromFeaturedTokens && currentToken.amount
111
+
112
+ const isTransitionToPopularTokens =
113
+ (isTransitionFromFeaturedTokens || isTransitionFromMyTokens) &&
114
+ currentToken.popular
115
+
116
+ const shouldShowAllTokensCategory =
117
+ isTransitionFromMyTokens ||
118
+ isTransitionFromFeaturedTokens ||
119
+ (previousToken?.popular && !currentToken.popular)
120
+
121
+ const startAdornmentLabel = showCategories
122
+ ? (() => {
123
+ if (isFirstFeaturedToken) {
124
+ return t('main.featuredTokens')
125
+ }
126
+ if (isTransitionToMyTokens) {
127
+ return t('main.myTokens')
128
+ }
129
+ if (isTransitionToPopularTokens) {
130
+ return t('main.popularTokens')
131
+ }
132
+ if (shouldShowAllTokensCategory) {
133
+ return t('main.allTokens')
134
+ }
135
+ return null
136
+ })()
137
+ : null
138
+
139
+ return (
140
+ <TokenListItem
141
+ key={item.key}
142
+ onClick={onClick}
143
+ size={item.size}
144
+ start={item.start}
145
+ token={currentToken}
146
+ chain={chain}
147
+ isBalanceLoading={isBalanceLoading}
148
+ accountAddress={account.address}
149
+ startAdornment={
150
+ startAdornmentLabel ? (
151
+ <Typography
152
+ sx={{
153
+ fontSize: 14,
154
+ fontWeight: 600,
155
+ lineHeight: '16px',
156
+ px: 1.5,
157
+ pt: isFirstFeaturedToken ? 0 : 1,
158
+ pb: 1,
159
+ }}
160
+ >
161
+ {startAdornmentLabel}
162
+ </Typography>
163
+ ) : null
110
164
  }
111
- if (isTransitionToPopularTokens) {
112
- return t('main.popularTokens')
113
- }
114
- if (shouldShowAllTokensCategory) {
115
- return t('main.allTokens')
116
- }
117
- return null
118
- })()
119
- : null
120
-
121
- return (
122
- <TokenListItem
123
- key={item.key}
124
- onClick={onClick}
125
- size={item.size}
126
- start={item.start}
127
- token={currentToken}
128
- chain={chain}
129
- isBalanceLoading={isBalanceLoading}
130
- accountAddress={account.address}
131
- startAdornment={
132
- startAdornmentLabel ? (
133
- <Typography
134
- sx={{
135
- fontSize: 14,
136
- fontWeight: 600,
137
- lineHeight: '16px',
138
- px: 1.5,
139
- pt: isFirstFeaturedToken ? 0 : 1,
140
- pb: 1,
141
- }}
142
- >
143
- {startAdornmentLabel}
144
- </Typography>
145
- ) : null
146
- }
147
- />
148
- )
149
- })}
150
- </List>
165
+ onShowTokenDetails={onShowTokenDetails}
166
+ />
167
+ )
168
+ })}
169
+ </List>
170
+ <TokenDetailsSheet ref={tokenDetailsSheetRef} chainId={chainId} />
171
+ </>
151
172
  )
152
173
  }
@@ -32,6 +32,7 @@ export interface TokenListItemBaseProps {
32
32
  export interface TokenListItemProps extends TokenListItemBaseProps {
33
33
  accountAddress?: string
34
34
  token: TokenAmount
35
+ onShowTokenDetails: (tokenAddress: string, noContractAddress: boolean) => void
35
36
  chain?: ExtendedChain
36
37
  isBalanceLoading?: boolean
37
38
  startAdornment?: React.ReactNode
@@ -39,6 +40,7 @@ export interface TokenListItemProps extends TokenListItemBaseProps {
39
40
  }
40
41
 
41
42
  export interface TokenListItemButtonProps {
43
+ onShowTokenDetails: (tokenAddress: string, noContractAddress: boolean) => void
42
44
  onClick?: MouseEventHandler<HTMLDivElement>
43
45
  accountAddress?: string
44
46
  token: TokenAmount
@@ -49,3 +51,9 @@ export interface TokenListItemButtonProps {
49
51
  export interface TokenListItemAvatarProps {
50
52
  token: TokenAmount
51
53
  }
54
+
55
+ export interface TokenDetailsSheetBase {
56
+ isOpen(): void
57
+ open(address: string, noContractAddress: boolean): void
58
+ close(): void
59
+ }
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.23.3'
2
+ export const version = '3.24.0'