@lifi/widget 3.25.0-beta.5 → 3.26.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/CHANGELOG.md +43 -0
- package/dist/esm/components/AmountInput/AmountInput.js +75 -17
- package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInput.style.d.ts +4 -0
- package/dist/esm/components/AmountInput/AmountInput.style.js +11 -0
- package/dist/esm/components/AmountInput/AmountInput.style.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputAdornment.style.d.ts +1 -0
- package/dist/esm/components/AmountInput/AmountInputAdornment.style.js +64 -5
- package/dist/esm/components/AmountInput/AmountInputAdornment.style.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +20 -7
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.js +41 -15
- package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.style.d.ts +1 -0
- package/dist/esm/components/AmountInput/PriceFormHelperText.style.js +29 -0
- package/dist/esm/components/AmountInput/PriceFormHelperText.style.js.map +1 -0
- package/dist/esm/components/AppContainer.js +1 -0
- package/dist/esm/components/AppContainer.js.map +1 -1
- package/dist/esm/components/ChainSelect/useChainSelect.js +2 -4
- package/dist/esm/components/ChainSelect/useChainSelect.js.map +1 -1
- package/dist/esm/components/Chains/ChainList.d.ts +2 -3
- package/dist/esm/components/Chains/ChainList.js +4 -3
- package/dist/esm/components/Chains/ChainList.js.map +1 -1
- package/dist/esm/components/Chains/ChainList.style.js +10 -3
- package/dist/esm/components/Chains/ChainList.style.js.map +1 -1
- package/dist/esm/components/Chains/ChainListItem.d.ts +4 -1
- package/dist/esm/components/Chains/ChainListItem.js +14 -2
- package/dist/esm/components/Chains/ChainListItem.js.map +1 -1
- package/dist/esm/components/Chains/PinChainButton.d.ts +7 -0
- package/dist/esm/components/Chains/PinChainButton.js +34 -0
- package/dist/esm/components/Chains/PinChainButton.js.map +1 -0
- package/dist/esm/components/Chains/SelectChainContent.js +15 -16
- package/dist/esm/components/Chains/SelectChainContent.js.map +1 -1
- package/dist/esm/components/Chains/VirtualizedChainList.d.ts +2 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js +45 -9
- package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
- package/dist/esm/components/Header/NavigationHeader.js +5 -1
- package/dist/esm/components/Header/NavigationHeader.js.map +1 -1
- package/dist/esm/components/Messages/MinFromAmountUSDMessage.d.ts +6 -0
- package/dist/esm/components/Messages/MinFromAmountUSDMessage.js +15 -0
- package/dist/esm/components/Messages/MinFromAmountUSDMessage.js.map +1 -0
- package/dist/esm/components/Messages/WarningMessages.js +3 -0
- package/dist/esm/components/Messages/WarningMessages.js.map +1 -1
- package/dist/esm/components/Messages/useMessageQueue.js +15 -4
- package/dist/esm/components/Messages/useMessageQueue.js.map +1 -1
- package/dist/esm/components/Routes/RoutesContent.js +3 -2
- package/dist/esm/components/Routes/RoutesContent.js.map +1 -1
- package/dist/esm/components/Routes/RoutesExpanded.style.js +3 -2
- package/dist/esm/components/Routes/RoutesExpanded.style.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletButton.js.map +1 -1
- package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js +3 -3
- package/dist/esm/components/SendToWallet/SendToWalletExpandButton.js.map +1 -1
- package/dist/esm/components/Step/StepProcess.js +3 -8
- package/dist/esm/components/Step/StepProcess.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheet.js +0 -2
- package/dist/esm/components/TokenList/TokenDetailsSheet.js.map +1 -1
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js +27 -4
- package/dist/esm/components/TokenList/TokenDetailsSheetContent.js.map +1 -1
- package/dist/esm/components/TokenList/TokenList.js +10 -1
- package/dist/esm/components/TokenList/TokenList.js.map +1 -1
- package/dist/esm/components/TokenList/TokenListItem.js +3 -1
- package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
- package/dist/esm/components/TokenList/useTokenSelect.js +2 -4
- package/dist/esm/components/TokenList/useTokenSelect.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/hooks/useFromAmountThreshold.d.ts +4 -0
- package/dist/esm/hooks/useFromAmountThreshold.js +23 -0
- package/dist/esm/hooks/useFromAmountThreshold.js.map +1 -0
- package/dist/esm/hooks/useGasSufficiency.js +3 -3
- package/dist/esm/hooks/useGasSufficiency.js.map +1 -1
- package/dist/esm/hooks/useLongPress.d.ts +7 -0
- package/dist/esm/hooks/useLongPress.js +41 -0
- package/dist/esm/hooks/useLongPress.js.map +1 -0
- package/dist/esm/hooks/useNavigateBack.d.ts +1 -1
- package/dist/esm/hooks/useNavigateBack.js +8 -2
- package/dist/esm/hooks/useNavigateBack.js.map +1 -1
- package/dist/esm/hooks/useRouteExecution.js +1 -2
- package/dist/esm/hooks/useRouteExecution.js.map +1 -1
- package/dist/esm/hooks/useRoutes.js +19 -4
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useSettingMonitor.js +1 -2
- package/dist/esm/hooks/useSettingMonitor.js.map +1 -1
- package/dist/esm/hooks/useTokenAddressBalance.js +4 -1
- package/dist/esm/hooks/useTokenAddressBalance.js.map +1 -1
- package/dist/esm/hooks/useTokenBalances.d.ts +1 -2
- package/dist/esm/hooks/useTokenBalances.js +2 -3
- package/dist/esm/hooks/useTokenBalances.js.map +1 -1
- package/dist/esm/hooks/useTokenSearch.d.ts +2 -2
- package/dist/esm/hooks/useTokenSearch.js +5 -2
- package/dist/esm/hooks/useTokenSearch.js.map +1 -1
- package/dist/esm/hooks/useTokens.d.ts +1 -2
- package/dist/esm/hooks/useTokens.js +3 -12
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useTools.js +2 -2
- package/dist/esm/hooks/useTools.js.map +1 -1
- package/dist/esm/i18n/bn.json +5 -2
- package/dist/esm/i18n/de.json +5 -2
- package/dist/esm/i18n/en.json +5 -2
- package/dist/esm/i18n/es.json +5 -2
- package/dist/esm/i18n/fr.json +5 -2
- package/dist/esm/i18n/hi.json +5 -2
- package/dist/esm/i18n/id.json +5 -2
- package/dist/esm/i18n/it.json +5 -2
- package/dist/esm/i18n/ja.json +5 -2
- package/dist/esm/i18n/ko.json +5 -2
- package/dist/esm/i18n/pl.json +4 -1
- package/dist/esm/i18n/pt.json +5 -2
- package/dist/esm/i18n/th.json +5 -2
- package/dist/esm/i18n/tr.json +5 -2
- package/dist/esm/i18n/uk.json +5 -2
- package/dist/esm/i18n/vi.json +5 -2
- package/dist/esm/i18n/zh.json +5 -2
- package/dist/esm/pages/SelectEnabledToolsPage.js +4 -2
- package/dist/esm/pages/SelectEnabledToolsPage.js.map +1 -1
- package/dist/esm/pages/SelectTokenPage/SearchTokenInput.js +14 -7
- package/dist/esm/pages/SelectTokenPage/SearchTokenInput.js.map +1 -1
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +1 -2
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TransactionPage.js +5 -3
- package/dist/esm/pages/TransactionPage/TransactionPage.js.map +1 -1
- package/dist/esm/stores/bookmarks/BookmarkStore.d.ts +1 -2
- package/dist/esm/stores/bookmarks/BookmarkStore.js +3 -3
- package/dist/esm/stores/bookmarks/BookmarkStore.js.map +1 -1
- package/dist/esm/stores/bookmarks/useBookmarkActions.js +1 -2
- package/dist/esm/stores/bookmarks/useBookmarkActions.js.map +1 -1
- package/dist/esm/stores/bookmarks/useBookmarks.js +1 -2
- package/dist/esm/stores/bookmarks/useBookmarks.js.map +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.d.ts +1 -1
- package/dist/esm/stores/chains/ChainOrderStore.js +20 -7
- package/dist/esm/stores/chains/ChainOrderStore.js.map +1 -1
- package/dist/esm/stores/chains/createChainOrderStore.js +19 -1
- package/dist/esm/stores/chains/createChainOrderStore.js.map +1 -1
- package/dist/esm/stores/chains/types.d.ts +2 -0
- package/dist/esm/stores/chains/useChainOrder.js +1 -2
- package/dist/esm/stores/chains/useChainOrder.js.map +1 -1
- package/dist/esm/stores/form/useFieldActions.js +1 -2
- package/dist/esm/stores/form/useFieldActions.js.map +1 -1
- package/dist/esm/stores/form/useFieldValues.js +1 -2
- package/dist/esm/stores/form/useFieldValues.js.map +1 -1
- package/dist/esm/stores/form/useFormStore.d.ts +1 -2
- package/dist/esm/stores/form/useFormStore.js +3 -3
- package/dist/esm/stores/form/useFormStore.js.map +1 -1
- package/dist/esm/stores/form/useTouchedFields.js +1 -2
- package/dist/esm/stores/form/useTouchedFields.js.map +1 -1
- package/dist/esm/stores/form/useValidation.js +5 -2
- package/dist/esm/stores/form/useValidation.js.map +1 -1
- package/dist/esm/stores/form/useValidationActions.js +1 -2
- package/dist/esm/stores/form/useValidationActions.js.map +1 -1
- package/dist/esm/stores/header/useHeaderStore.d.ts +1 -1
- package/dist/esm/stores/header/useHeaderStore.js +5 -5
- package/dist/esm/stores/header/useHeaderStore.js.map +1 -1
- package/dist/esm/stores/inputMode/useInputModeStore.d.ts +9 -0
- package/dist/esm/stores/inputMode/useInputModeStore.js +19 -0
- package/dist/esm/stores/inputMode/useInputModeStore.js.map +1 -0
- package/dist/esm/stores/routes/RouteExecutionStore.d.ts +1 -1
- package/dist/esm/stores/routes/RouteExecutionStore.js +3 -2
- package/dist/esm/stores/routes/RouteExecutionStore.js.map +1 -1
- package/dist/esm/stores/routes/useExecutingRoutesIds.js +1 -2
- package/dist/esm/stores/routes/useExecutingRoutesIds.js.map +1 -1
- package/dist/esm/stores/settings/useSendToWalletStore.d.ts +2 -1
- package/dist/esm/stores/settings/useSendToWalletStore.js +6 -3
- package/dist/esm/stores/settings/useSendToWalletStore.js.map +1 -1
- package/dist/esm/stores/settings/useSettings.js +1 -2
- package/dist/esm/stores/settings/useSettings.js.map +1 -1
- package/dist/esm/stores/settings/useSettingsActions.js +1 -2
- package/dist/esm/stores/settings/useSettingsActions.js.map +1 -1
- package/dist/esm/stores/settings/useSettingsStore.d.ts +2 -1
- package/dist/esm/stores/settings/useSettingsStore.js +5 -1
- package/dist/esm/stores/settings/useSettingsStore.js.map +1 -1
- package/dist/esm/stores/settings/useSplitSubvariantStore.js +2 -1
- package/dist/esm/stores/settings/useSplitSubvariantStore.js.map +1 -1
- package/dist/esm/themes/createTheme.js +5 -1
- package/dist/esm/themes/createTheme.js.map +1 -1
- package/dist/esm/types/widget.d.ts +3 -1
- package/dist/esm/types/widget.js +1 -0
- package/dist/esm/types/widget.js.map +1 -1
- package/dist/esm/utils/format.d.ts +4 -0
- package/dist/esm/utils/format.js +26 -0
- package/dist/esm/utils/format.js.map +1 -1
- package/dist/esm/utils/getPriceImpact.d.ts +2 -2
- package/dist/esm/utils/getPriceImpact.js.map +1 -1
- package/package.json +12 -12
- package/package.json.tmp +14 -14
- package/src/components/AmountInput/AmountInput.style.tsx +13 -0
- package/src/components/AmountInput/AmountInput.tsx +91 -18
- package/src/components/AmountInput/AmountInputAdornment.style.tsx +66 -5
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +40 -10
- package/src/components/AmountInput/PriceFormHelperText.style.tsx +29 -0
- package/src/components/AmountInput/PriceFormHelperText.tsx +63 -19
- package/src/components/AppContainer.tsx +1 -0
- package/src/components/ChainSelect/useChainSelect.ts +2 -4
- package/src/components/Chains/ChainList.style.tsx +10 -2
- package/src/components/Chains/ChainList.tsx +6 -5
- package/src/components/Chains/ChainListItem.tsx +29 -0
- package/src/components/Chains/PinChainButton.tsx +51 -0
- package/src/components/Chains/SelectChainContent.tsx +18 -21
- package/src/components/Chains/VirtualizedChainList.tsx +67 -20
- package/src/components/Header/NavigationHeader.tsx +10 -1
- package/src/components/Messages/MinFromAmountUSDMessage.tsx +35 -0
- package/src/components/Messages/WarningMessages.tsx +8 -0
- package/src/components/Messages/useMessageQueue.ts +16 -4
- package/src/components/Routes/RoutesContent.tsx +6 -2
- package/src/components/Routes/RoutesExpanded.style.ts +3 -2
- package/src/components/SendToWallet/SendToWalletButton.tsx +1 -1
- package/src/components/SendToWallet/SendToWalletExpandButton.tsx +8 -3
- package/src/components/Step/StepProcess.tsx +3 -8
- package/src/components/TokenList/TokenDetailsSheet.tsx +0 -2
- package/src/components/TokenList/TokenDetailsSheetContent.tsx +50 -3
- package/src/components/TokenList/TokenList.tsx +24 -1
- package/src/components/TokenList/TokenListItem.tsx +6 -0
- package/src/components/TokenList/useTokenSelect.ts +2 -4
- package/src/config/version.ts +1 -1
- package/src/hooks/useFromAmountThreshold.ts +35 -0
- package/src/hooks/useGasSufficiency.ts +4 -4
- package/src/hooks/useLongPress.ts +51 -0
- package/src/hooks/useNavigateBack.ts +26 -17
- package/src/hooks/useRouteExecution.ts +1 -3
- package/src/hooks/useRoutes.ts +24 -4
- package/src/hooks/useSettingMonitor.ts +7 -11
- package/src/hooks/useTokenAddressBalance.ts +6 -1
- package/src/hooks/useTokenBalances.ts +3 -10
- package/src/hooks/useTokenSearch.ts +7 -4
- package/src/hooks/useTokens.ts +4 -31
- package/src/hooks/useTools.ts +2 -2
- package/src/i18n/bn.json +5 -2
- package/src/i18n/de.json +5 -2
- package/src/i18n/en.json +5 -2
- package/src/i18n/es.json +5 -2
- package/src/i18n/fr.json +5 -2
- package/src/i18n/hi.json +5 -2
- package/src/i18n/id.json +5 -2
- package/src/i18n/it.json +5 -2
- package/src/i18n/ja.json +5 -2
- package/src/i18n/ko.json +5 -2
- package/src/i18n/pl.json +4 -1
- package/src/i18n/pt.json +5 -2
- package/src/i18n/th.json +5 -2
- package/src/i18n/tr.json +5 -2
- package/src/i18n/uk.json +5 -2
- package/src/i18n/vi.json +5 -2
- package/src/i18n/zh.json +5 -2
- package/src/pages/SelectEnabledToolsPage.tsx +4 -5
- package/src/pages/SelectTokenPage/SearchTokenInput.tsx +19 -7
- package/src/pages/SettingsPage/BridgeAndExchangeSettings.tsx +1 -2
- package/src/pages/TransactionPage/TransactionPage.tsx +17 -9
- package/src/stores/bookmarks/BookmarkStore.tsx +3 -6
- package/src/stores/bookmarks/useBookmarkActions.ts +9 -13
- package/src/stores/bookmarks/useBookmarks.ts +1 -3
- package/src/stores/chains/ChainOrderStore.tsx +26 -8
- package/src/stores/chains/createChainOrderStore.ts +19 -1
- package/src/stores/chains/types.ts +2 -0
- package/src/stores/chains/useChainOrder.ts +1 -5
- package/src/stores/form/useFieldActions.ts +10 -14
- package/src/stores/form/useFieldValues.ts +1 -3
- package/src/stores/form/useFormStore.ts +3 -6
- package/src/stores/form/useTouchedFields.ts +1 -2
- package/src/stores/form/useValidation.ts +5 -5
- package/src/stores/form/useValidationActions.ts +5 -9
- package/src/stores/header/useHeaderStore.tsx +5 -14
- package/src/stores/inputMode/useInputModeStore.ts +29 -0
- package/src/stores/routes/RouteExecutionStore.tsx +3 -3
- package/src/stores/routes/useExecutingRoutesIds.ts +14 -17
- package/src/stores/settings/useSendToWalletStore.ts +11 -8
- package/src/stores/settings/useSettings.ts +8 -11
- package/src/stores/settings/useSettingsActions.ts +8 -12
- package/src/stores/settings/useSettingsStore.ts +8 -1
- package/src/stores/settings/useSplitSubvariantStore.tsx +2 -1
- package/src/themes/createTheme.ts +5 -1
- package/src/types/widget.ts +2 -0
- package/src/utils/format.ts +33 -0
- package/src/utils/getPriceImpact.ts +2 -2
|
@@ -8,7 +8,7 @@ import type { FormTypeProps } from '../../stores/form/types.js'
|
|
|
8
8
|
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
9
9
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
10
10
|
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
11
|
-
import {
|
|
11
|
+
import { ButtonContainer, MaxButton } from './AmountInputAdornment.style.js'
|
|
12
12
|
|
|
13
13
|
export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
14
14
|
const { t } = useTranslation()
|
|
@@ -24,11 +24,11 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
24
24
|
// the user will have enough funds remaining to cover gas costs
|
|
25
25
|
const { data } = useGasRecommendation(chainId)
|
|
26
26
|
|
|
27
|
-
const { token
|
|
27
|
+
const { token } = useTokenAddressBalance(chainId, tokenAddress)
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const getMaxAmount = () => {
|
|
30
30
|
if (!token?.amount) {
|
|
31
|
-
return
|
|
31
|
+
return 0n
|
|
32
32
|
}
|
|
33
33
|
const chain = getChainById(chainId)
|
|
34
34
|
let maxAmount = token.amount
|
|
@@ -38,7 +38,26 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
38
38
|
maxAmount = token.amount - recommendedAmount
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
return maxAmount
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const handlePercentage = (percentage: number) => {
|
|
45
|
+
const maxAmount = getMaxAmount()
|
|
46
|
+
if (maxAmount && token?.decimals) {
|
|
47
|
+
const percentageAmount = (maxAmount * BigInt(percentage)) / 100n
|
|
48
|
+
setFieldValue(
|
|
49
|
+
FormKeyHelper.getAmountKey(formType),
|
|
50
|
+
formatUnits(percentageAmount, token.decimals),
|
|
51
|
+
{
|
|
52
|
+
isTouched: true,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handleMax = () => {
|
|
59
|
+
const maxAmount = getMaxAmount()
|
|
60
|
+
if (maxAmount && token?.decimals) {
|
|
42
61
|
setFieldValue(
|
|
43
62
|
FormKeyHelper.getAmountKey(formType),
|
|
44
63
|
formatUnits(maxAmount, token.decimals),
|
|
@@ -50,11 +69,22 @@ export const AmountInputEndAdornment = ({ formType }: FormTypeProps) => {
|
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
return (
|
|
53
|
-
<InputAdornment position="end">
|
|
54
|
-
{
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
<InputAdornment position="end" sx={{ paddingTop: 2 }}>
|
|
73
|
+
{formType === 'from' && token?.amount ? (
|
|
74
|
+
<ButtonContainer>
|
|
75
|
+
<MaxButton onClick={() => handlePercentage(25)} data-delay="0">
|
|
76
|
+
25%
|
|
77
|
+
</MaxButton>
|
|
78
|
+
<MaxButton onClick={() => handlePercentage(50)} data-delay="1">
|
|
79
|
+
50%
|
|
80
|
+
</MaxButton>
|
|
81
|
+
<MaxButton onClick={() => handlePercentage(75)} data-delay="2">
|
|
82
|
+
75%
|
|
83
|
+
</MaxButton>
|
|
84
|
+
<MaxButton onClick={handleMax} data-delay="3">
|
|
85
|
+
{t('button.max')}
|
|
86
|
+
</MaxButton>
|
|
87
|
+
</ButtonContainer>
|
|
58
88
|
) : null}
|
|
59
89
|
</InputAdornment>
|
|
60
90
|
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Button, styled } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export const InputPriceButton = styled(Button)(({ theme, onClick }) => ({
|
|
4
|
+
color: theme.vars.palette.text.secondary,
|
|
5
|
+
padding: theme.spacing(0.25, 0.5, 0.25, 0.75),
|
|
6
|
+
maxHeight: 16,
|
|
7
|
+
fontSize: '0.75rem',
|
|
8
|
+
fontWeight: 500,
|
|
9
|
+
borderRadius: `calc(${theme.vars.shape.borderRadius} * 2)`,
|
|
10
|
+
backgroundColor: 'transparent',
|
|
11
|
+
minWidth: 32,
|
|
12
|
+
...(onClick
|
|
13
|
+
? {
|
|
14
|
+
'&:hover': {
|
|
15
|
+
backgroundColor: `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.04)`,
|
|
16
|
+
},
|
|
17
|
+
...theme.applyStyles('dark', {
|
|
18
|
+
backgroundColor: 'transparent',
|
|
19
|
+
'&:hover': {
|
|
20
|
+
backgroundColor: `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.04)`,
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
}
|
|
24
|
+
: {
|
|
25
|
+
cursor: 'text',
|
|
26
|
+
userSelect: 'text',
|
|
27
|
+
pointerEvents: 'none',
|
|
28
|
+
}),
|
|
29
|
+
}))
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { TokenAmount } from '@lifi/sdk'
|
|
2
|
+
import SwapVertIcon from '@mui/icons-material/SwapVert'
|
|
2
3
|
import { FormHelperText, Skeleton, Typography } from '@mui/material'
|
|
3
4
|
import { useTranslation } from 'react-i18next'
|
|
4
5
|
import { useTokenAddressBalance } from '../../hooks/useTokenAddressBalance.js'
|
|
5
6
|
import type { FormTypeProps } from '../../stores/form/types.js'
|
|
6
7
|
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
7
8
|
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
9
|
+
import { useInputModeStore } from '../../stores/inputMode/useInputModeStore.js'
|
|
8
10
|
import { formatTokenAmount, formatTokenPrice } from '../../utils/format.js'
|
|
11
|
+
import { InputPriceButton } from './PriceFormHelperText.style.js'
|
|
9
12
|
|
|
10
13
|
export const PriceFormHelperText: React.FC<FormTypeProps> = ({ formType }) => {
|
|
11
14
|
const [chainId, tokenAddress] = useFieldValues(
|
|
@@ -33,11 +36,31 @@ export const PriceFormHelperTextBase: React.FC<
|
|
|
33
36
|
> = ({ formType, isLoading, tokenAddress, token }) => {
|
|
34
37
|
const { t } = useTranslation()
|
|
35
38
|
const [amount] = useFieldValues(FormKeyHelper.getAmountKey(formType))
|
|
39
|
+
const { inputMode, toggleInputMode } = useInputModeStore()
|
|
40
|
+
|
|
41
|
+
const currentInputMode = inputMode[formType]
|
|
36
42
|
|
|
37
43
|
const tokenAmount = token
|
|
38
44
|
? formatTokenAmount(token.amount, token.decimals)
|
|
39
45
|
: '0'
|
|
40
|
-
|
|
46
|
+
|
|
47
|
+
const getPriceAmountDisplayValue = () => {
|
|
48
|
+
if (currentInputMode === 'amount') {
|
|
49
|
+
const tokenPrice = formatTokenPrice(
|
|
50
|
+
amount,
|
|
51
|
+
token?.priceUSD,
|
|
52
|
+
token?.decimals
|
|
53
|
+
)
|
|
54
|
+
return t('format.currency', { value: tokenPrice })
|
|
55
|
+
} else {
|
|
56
|
+
return t('format.tokenAmount', { value: amount || '0' })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const handleToggleMode = (e: React.MouseEvent) => {
|
|
61
|
+
e.stopPropagation()
|
|
62
|
+
toggleInputMode(formType)
|
|
63
|
+
}
|
|
41
64
|
|
|
42
65
|
return (
|
|
43
66
|
<FormHelperText
|
|
@@ -45,28 +68,49 @@ export const PriceFormHelperTextBase: React.FC<
|
|
|
45
68
|
sx={{
|
|
46
69
|
display: 'flex',
|
|
47
70
|
justifyContent: 'space-between',
|
|
71
|
+
alignItems: 'center',
|
|
48
72
|
margin: 0,
|
|
49
|
-
marginLeft:
|
|
50
|
-
marginTop: 0.
|
|
73
|
+
marginLeft: 1.25,
|
|
74
|
+
marginTop: 0.5,
|
|
51
75
|
}}
|
|
52
76
|
>
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
color: 'text.secondary',
|
|
56
|
-
fontWeight: 500,
|
|
57
|
-
fontSize: 12,
|
|
58
|
-
lineHeight: 1,
|
|
59
|
-
flex: 1,
|
|
60
|
-
wordBreak: 'break-word',
|
|
61
|
-
overflowWrap: 'break-word',
|
|
62
|
-
}}
|
|
77
|
+
<InputPriceButton
|
|
78
|
+
onClick={token?.priceUSD ? handleToggleMode : undefined}
|
|
63
79
|
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
<Typography
|
|
81
|
+
sx={{
|
|
82
|
+
color: 'text.secondary',
|
|
83
|
+
fontWeight: 500,
|
|
84
|
+
fontSize: 12,
|
|
85
|
+
lineHeight: 1,
|
|
86
|
+
marginRight: 0.25,
|
|
87
|
+
maxWidth: 136,
|
|
88
|
+
overflow: 'hidden',
|
|
89
|
+
textOverflow: 'ellipsis',
|
|
90
|
+
whiteSpace: 'nowrap',
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
{getPriceAmountDisplayValue()}
|
|
94
|
+
</Typography>
|
|
95
|
+
{currentInputMode === 'price' && token?.symbol ? (
|
|
96
|
+
<Typography
|
|
97
|
+
sx={{
|
|
98
|
+
color: 'text.secondary',
|
|
99
|
+
fontWeight: 500,
|
|
100
|
+
fontSize: 12,
|
|
101
|
+
lineHeight: 1,
|
|
102
|
+
wordBreak: 'break-word',
|
|
103
|
+
overflowWrap: 'break-word',
|
|
104
|
+
marginRight: 0.25,
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
{token.symbol}
|
|
108
|
+
</Typography>
|
|
109
|
+
) : null}
|
|
110
|
+
{token?.priceUSD && <SwapVertIcon sx={{ fontSize: 14 }} />}
|
|
111
|
+
</InputPriceButton>
|
|
68
112
|
{isLoading && tokenAddress ? (
|
|
69
|
-
<Skeleton variant="text" width={
|
|
113
|
+
<Skeleton variant="text" width={56} height={16} />
|
|
70
114
|
) : token?.amount ? (
|
|
71
115
|
<Typography
|
|
72
116
|
sx={{
|
|
@@ -74,7 +118,7 @@ export const PriceFormHelperTextBase: React.FC<
|
|
|
74
118
|
fontSize: 12,
|
|
75
119
|
color: 'text.secondary',
|
|
76
120
|
lineHeight: 1,
|
|
77
|
-
|
|
121
|
+
paddingLeft: 0.25,
|
|
78
122
|
}}
|
|
79
123
|
title={tokenAmount}
|
|
80
124
|
>
|
|
@@ -9,13 +9,11 @@ import { useChainOrder } from '../../stores/chains/useChainOrder.js'
|
|
|
9
9
|
import type { FormType } from '../../stores/form/types.js'
|
|
10
10
|
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
11
11
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
12
|
-
import { useFieldController } from '../../stores/form/useFieldController.js'
|
|
13
12
|
import type { DisabledUI } from '../../types/widget.js'
|
|
14
13
|
|
|
15
14
|
export const useChainSelect = (formType: FormType) => {
|
|
16
15
|
const { disabledUI } = useWidgetConfig()
|
|
17
16
|
const chainKey = FormKeyHelper.getChainKey(formType)
|
|
18
|
-
const { onChange } = useFieldController({ name: chainKey })
|
|
19
17
|
const { setFieldValue, getFieldValues } = useFieldActions()
|
|
20
18
|
const { useExternalWalletProvidersOnly, externalChainTypes } =
|
|
21
19
|
useExternalWalletProvider()
|
|
@@ -45,7 +43,7 @@ export const useChainSelect = (formType: FormType) => {
|
|
|
45
43
|
|
|
46
44
|
const setCurrentChain = useCallback(
|
|
47
45
|
(chainId: number) => {
|
|
48
|
-
|
|
46
|
+
setFieldValue(chainKey, chainId, { isDirty: true, isTouched: true })
|
|
49
47
|
if (swapOnly) {
|
|
50
48
|
setFieldValue(FormKeyHelper.getChainKey('to'), chainId, {
|
|
51
49
|
isTouched: true,
|
|
@@ -69,7 +67,7 @@ export const useChainSelect = (formType: FormType) => {
|
|
|
69
67
|
setChainOrder(chainId, formType)
|
|
70
68
|
},
|
|
71
69
|
[
|
|
72
|
-
|
|
70
|
+
chainKey,
|
|
73
71
|
swapOnly,
|
|
74
72
|
setFieldValue,
|
|
75
73
|
disabledUI,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ListItem as ListItemBase,
|
|
3
|
+
listItemButtonClasses,
|
|
3
4
|
listItemTextClasses,
|
|
4
5
|
Avatar as MuiAvatar,
|
|
5
6
|
List as MuiList,
|
|
@@ -7,7 +8,7 @@ import {
|
|
|
7
8
|
ListItemText as MuiListItemText,
|
|
8
9
|
styled,
|
|
9
10
|
} from '@mui/material'
|
|
10
|
-
import { ListItemButton as ListItemButtonBase } from '../
|
|
11
|
+
import { ListItemButton as ListItemButtonBase } from '../ListItemButton.js'
|
|
11
12
|
|
|
12
13
|
export const Avatar = styled(MuiAvatar)<{
|
|
13
14
|
size?: 'small' | 'medium'
|
|
@@ -28,6 +29,9 @@ export const ListItemText = styled(MuiListItemText)<{
|
|
|
28
29
|
[`.${listItemTextClasses.primary}`]: {
|
|
29
30
|
fontWeight: 500,
|
|
30
31
|
fontSize: size === 'small' ? '1rem' : '1.125rem',
|
|
32
|
+
textOverflow: 'ellipsis',
|
|
33
|
+
overflow: 'hidden',
|
|
34
|
+
whiteSpace: 'nowrap',
|
|
31
35
|
},
|
|
32
36
|
}))
|
|
33
37
|
|
|
@@ -38,6 +42,7 @@ export const ListItemButton = styled(ListItemButtonBase)<{
|
|
|
38
42
|
borderRadius: theme.vars.shape.borderRadius,
|
|
39
43
|
paddingLeft: size === 'small' ? theme.spacing(1) : theme.spacing(1.5),
|
|
40
44
|
height: size === 'small' ? 44 : 56,
|
|
45
|
+
width: '100%',
|
|
41
46
|
}
|
|
42
47
|
})
|
|
43
48
|
|
|
@@ -52,8 +57,11 @@ export const List = styled(MuiList)(({ theme }) => ({
|
|
|
52
57
|
cursor: 'pointer',
|
|
53
58
|
}))
|
|
54
59
|
|
|
55
|
-
export const ListItem = styled(ListItemBase)(() => ({
|
|
60
|
+
export const ListItem = styled(ListItemBase)(({ theme }) => ({
|
|
56
61
|
position: 'absolute',
|
|
57
62
|
top: 0,
|
|
58
63
|
left: 0,
|
|
64
|
+
[`& .${listItemButtonClasses.root}`]: {
|
|
65
|
+
paddingRight: theme.spacing(1),
|
|
66
|
+
},
|
|
59
67
|
}))
|
|
@@ -17,8 +17,7 @@ interface ChainListProps {
|
|
|
17
17
|
onSelect: (chain: ExtendedChain) => void
|
|
18
18
|
selectedChainId?: number
|
|
19
19
|
isLoading: boolean
|
|
20
|
-
|
|
21
|
-
adjustForStickySearchInput?: boolean
|
|
20
|
+
inExpansion: boolean
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
export const ChainList = ({
|
|
@@ -27,11 +26,12 @@ export const ChainList = ({
|
|
|
27
26
|
onSelect,
|
|
28
27
|
selectedChainId,
|
|
29
28
|
isLoading,
|
|
30
|
-
|
|
31
|
-
adjustForStickySearchInput,
|
|
29
|
+
inExpansion,
|
|
32
30
|
}: ChainListProps) => {
|
|
33
31
|
const { t } = useTranslation()
|
|
34
32
|
|
|
33
|
+
const itemsSize = inExpansion ? 'small' : 'medium'
|
|
34
|
+
|
|
35
35
|
if (isLoading) {
|
|
36
36
|
return (
|
|
37
37
|
<List disablePadding sx={{ cursor: 'default' }}>
|
|
@@ -69,7 +69,7 @@ export const ChainList = ({
|
|
|
69
69
|
return (
|
|
70
70
|
<SearchNotFound
|
|
71
71
|
message={t('info.message.emptyChainList')}
|
|
72
|
-
adjustForStickySearchInput={
|
|
72
|
+
adjustForStickySearchInput={!inExpansion}
|
|
73
73
|
/>
|
|
74
74
|
)
|
|
75
75
|
}
|
|
@@ -81,6 +81,7 @@ export const ChainList = ({
|
|
|
81
81
|
onSelect={onSelect}
|
|
82
82
|
selectedChainId={selectedChainId}
|
|
83
83
|
itemsSize={itemsSize}
|
|
84
|
+
withPinnedChains={inExpansion}
|
|
84
85
|
/>
|
|
85
86
|
)
|
|
86
87
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/sdk'
|
|
2
|
+
import { Box } from '@mui/material'
|
|
2
3
|
import { memo } from 'react'
|
|
3
4
|
import {
|
|
4
5
|
Avatar,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
ListItemButton,
|
|
8
9
|
ListItemText,
|
|
9
10
|
} from './ChainList.style'
|
|
11
|
+
import { PinChainButton, pinButtonClassName } from './PinChainButton'
|
|
10
12
|
|
|
11
13
|
interface ChainListItemProps {
|
|
12
14
|
chain: ExtendedChain
|
|
@@ -15,6 +17,9 @@ interface ChainListItemProps {
|
|
|
15
17
|
itemsSize: 'small' | 'medium'
|
|
16
18
|
size: number
|
|
17
19
|
start: number
|
|
20
|
+
isPinned: boolean
|
|
21
|
+
onPin: (chainId: number) => void
|
|
22
|
+
withPin: boolean
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export const ChainListItem = memo(
|
|
@@ -25,6 +30,9 @@ export const ChainListItem = memo(
|
|
|
25
30
|
itemsSize,
|
|
26
31
|
size,
|
|
27
32
|
start,
|
|
33
|
+
isPinned,
|
|
34
|
+
onPin,
|
|
35
|
+
withPin,
|
|
28
36
|
}: ChainListItemProps) => {
|
|
29
37
|
return (
|
|
30
38
|
<ListItem
|
|
@@ -32,6 +40,13 @@ export const ChainListItem = memo(
|
|
|
32
40
|
height: `${size}px`,
|
|
33
41
|
transform: `translateY(${start}px)`,
|
|
34
42
|
padding: 0,
|
|
43
|
+
overflow: 'hidden',
|
|
44
|
+
}}
|
|
45
|
+
sx={{
|
|
46
|
+
[`&:hover .${pinButtonClassName}`]: {
|
|
47
|
+
opacity: 1,
|
|
48
|
+
transform: 'translateY(0)',
|
|
49
|
+
},
|
|
35
50
|
}}
|
|
36
51
|
>
|
|
37
52
|
<ListItemButton
|
|
@@ -45,6 +60,20 @@ export const ChainListItem = memo(
|
|
|
45
60
|
</Avatar>
|
|
46
61
|
</ListItemAvatar>
|
|
47
62
|
<ListItemText primary={chain.name} size={itemsSize} />
|
|
63
|
+
{withPin && (
|
|
64
|
+
<Box
|
|
65
|
+
style={{
|
|
66
|
+
position: 'relative',
|
|
67
|
+
height: 28,
|
|
68
|
+
width: 28,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<PinChainButton
|
|
72
|
+
isPinned={isPinned}
|
|
73
|
+
onPin={() => onPin(chain.id)}
|
|
74
|
+
/>
|
|
75
|
+
</Box>
|
|
76
|
+
)}
|
|
48
77
|
</ListItemButton>
|
|
49
78
|
</ListItem>
|
|
50
79
|
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import PushPinIcon from '@mui/icons-material/PushPin'
|
|
2
|
+
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined'
|
|
3
|
+
import { IconButton } from '@mui/material'
|
|
4
|
+
|
|
5
|
+
interface PinChainButtonProps {
|
|
6
|
+
isPinned: boolean
|
|
7
|
+
onPin: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const pinButtonClassName = 'pin-button'
|
|
11
|
+
const animationDuration = 225
|
|
12
|
+
|
|
13
|
+
export const PinChainButton = ({ isPinned, onPin }: PinChainButtonProps) => {
|
|
14
|
+
const PinIcon = isPinned ? PushPinIcon : PushPinOutlinedIcon
|
|
15
|
+
return (
|
|
16
|
+
<IconButton
|
|
17
|
+
className={pinButtonClassName}
|
|
18
|
+
edge="end"
|
|
19
|
+
aria-label="pin"
|
|
20
|
+
onClick={(e) => {
|
|
21
|
+
e.stopPropagation()
|
|
22
|
+
onPin()
|
|
23
|
+
}}
|
|
24
|
+
sx={{
|
|
25
|
+
position: 'absolute',
|
|
26
|
+
top: 0,
|
|
27
|
+
left: 0,
|
|
28
|
+
height: 28,
|
|
29
|
+
width: 28,
|
|
30
|
+
transition: `opacity ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1), transform ${animationDuration}ms cubic-bezier(0.4, 0, 0.2, 1)`,
|
|
31
|
+
...(isPinned
|
|
32
|
+
? {
|
|
33
|
+
opacity: 1,
|
|
34
|
+
transform: 'translateY(0)',
|
|
35
|
+
}
|
|
36
|
+
: {
|
|
37
|
+
opacity: 0,
|
|
38
|
+
transform: 'translateY(-50%)',
|
|
39
|
+
}),
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<PinIcon
|
|
43
|
+
sx={{
|
|
44
|
+
height: 20,
|
|
45
|
+
width: 20,
|
|
46
|
+
color: 'text.secondary',
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
</IconButton>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -30,31 +30,28 @@ export const SelectChainContent: React.FC<SelectChainContentProps> = memo(
|
|
|
30
30
|
)
|
|
31
31
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
32
32
|
const listRef = useRef<HTMLDivElement>(null)
|
|
33
|
-
const [
|
|
34
|
-
|
|
35
|
-
)
|
|
33
|
+
const [debouncedSearchValue, setDebouncedSearchValue] = useState('')
|
|
34
|
+
|
|
35
|
+
const filteredChains = useMemo(() => {
|
|
36
|
+
const value = debouncedSearchValue.toLowerCase()
|
|
37
|
+
return value
|
|
38
|
+
? (chains?.filter((chain) =>
|
|
39
|
+
chain.name.toLowerCase().includes(value)
|
|
40
|
+
) ?? [])
|
|
41
|
+
: (chains ?? [])
|
|
42
|
+
}, [chains, debouncedSearchValue])
|
|
36
43
|
|
|
37
44
|
const scrollToTop = useCallback(() => {
|
|
38
45
|
// Scroll widget container to top
|
|
39
46
|
if (!inExpansion && scrollableContainer) {
|
|
40
47
|
scrollableContainer.scrollTop = 0
|
|
41
48
|
}
|
|
42
|
-
// Scroll chain list to top
|
|
43
|
-
if (inExpansion && listRef.current) {
|
|
44
|
-
listRef.current.scrollTop = 0
|
|
45
|
-
}
|
|
46
49
|
}, [inExpansion, scrollableContainer])
|
|
47
50
|
|
|
48
51
|
const debouncedFilterChains = useMemo(
|
|
49
52
|
() =>
|
|
50
|
-
debounce((
|
|
51
|
-
|
|
52
|
-
const filtered = value
|
|
53
|
-
? chains?.filter((chain) =>
|
|
54
|
-
chain.name.toLowerCase().includes(value)
|
|
55
|
-
)
|
|
56
|
-
: chains
|
|
57
|
-
setFilteredChains(filtered ?? [])
|
|
53
|
+
debounce((value: string) => {
|
|
54
|
+
setDebouncedSearchValue(value)
|
|
58
55
|
scrollToTop()
|
|
59
56
|
}, 250),
|
|
60
57
|
[scrollToTop]
|
|
@@ -68,13 +65,14 @@ export const SelectChainContent: React.FC<SelectChainContentProps> = memo(
|
|
|
68
65
|
)
|
|
69
66
|
|
|
70
67
|
const onChange = useCallback(() => {
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
const value = inputRef.current?.value || ''
|
|
69
|
+
debouncedFilterChains(value)
|
|
70
|
+
}, [debouncedFilterChains])
|
|
73
71
|
|
|
74
72
|
const onClear = useCallback(() => {
|
|
75
|
-
|
|
73
|
+
setDebouncedSearchValue('')
|
|
76
74
|
scrollToTop()
|
|
77
|
-
}, [
|
|
75
|
+
}, [scrollToTop])
|
|
78
76
|
|
|
79
77
|
const listContainerHeight = useMemo(() => {
|
|
80
78
|
const fullContainerHeight = getWidgetMaxHeight(theme)
|
|
@@ -106,8 +104,7 @@ export const SelectChainContent: React.FC<SelectChainContentProps> = memo(
|
|
|
106
104
|
isLoading={isLoading}
|
|
107
105
|
onSelect={onSelect ?? onSelectChainFallback}
|
|
108
106
|
selectedChainId={selectedChainId}
|
|
109
|
-
|
|
110
|
-
adjustForStickySearchInput={!inExpansion}
|
|
107
|
+
inExpansion={inExpansion}
|
|
111
108
|
/>
|
|
112
109
|
</Box>
|
|
113
110
|
</FullPageContainer>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/sdk'
|
|
2
2
|
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
3
3
|
import type { RefObject } from 'react'
|
|
4
|
-
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
4
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
|
|
5
|
+
import { useChainOrderStore } from '../../stores/chains/ChainOrderStore'
|
|
5
6
|
import { List } from './ChainList.style'
|
|
6
7
|
import { ChainListItem } from './ChainListItem'
|
|
7
8
|
|
|
@@ -11,6 +12,7 @@ interface VirtualizedChainListProps {
|
|
|
11
12
|
onSelect: (chain: ExtendedChain) => void
|
|
12
13
|
selectedChainId?: number
|
|
13
14
|
itemsSize: 'small' | 'medium'
|
|
15
|
+
withPinnedChains: boolean
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const VirtualizedChainList = ({
|
|
@@ -19,17 +21,33 @@ export const VirtualizedChainList = ({
|
|
|
19
21
|
selectedChainId,
|
|
20
22
|
itemsSize,
|
|
21
23
|
scrollElementRef,
|
|
24
|
+
withPinnedChains,
|
|
22
25
|
}: VirtualizedChainListProps) => {
|
|
23
|
-
const
|
|
26
|
+
const selectedChainIdRef = useRef(selectedChainId) // Store the initial selected chain ID to scroll to it once chains are loaded
|
|
27
|
+
const hasScrolledRef = useRef(false)
|
|
28
|
+
const [pinnedChains, setPinnedChain] = useChainOrderStore((state) => [
|
|
29
|
+
state.pinnedChains,
|
|
30
|
+
state.setPinnedChain,
|
|
31
|
+
])
|
|
32
|
+
const onPin = useCallback(
|
|
33
|
+
(chainId: number) => {
|
|
34
|
+
setPinnedChain(chainId)
|
|
35
|
+
},
|
|
36
|
+
[setPinnedChain]
|
|
37
|
+
)
|
|
38
|
+
|
|
24
39
|
const sortedChains = useMemo(() => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
if (!pinnedChains.length) {
|
|
41
|
+
return chains
|
|
42
|
+
}
|
|
43
|
+
// Pinning logic: move pinned chains to the top of the list
|
|
44
|
+
const pinned = pinnedChains
|
|
45
|
+
.map((id) => chains.find((c) => c.id === id))
|
|
46
|
+
.filter(Boolean) as ExtendedChain[]
|
|
47
|
+
const pinnedIds = new Set(pinned.map((c) => c.id))
|
|
48
|
+
const rest = chains.filter((c) => !pinnedIds.has(c.id))
|
|
49
|
+
return [...pinned, ...rest]
|
|
50
|
+
}, [chains, pinnedChains])
|
|
33
51
|
|
|
34
52
|
const getItemKey = useCallback(
|
|
35
53
|
(index: number) => {
|
|
@@ -38,16 +56,17 @@ export const VirtualizedChainList = ({
|
|
|
38
56
|
[sortedChains]
|
|
39
57
|
)
|
|
40
58
|
|
|
41
|
-
const { getVirtualItems, getTotalSize, measure } =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
const { getVirtualItems, getTotalSize, measure, scrollToIndex, range } =
|
|
60
|
+
useVirtualizer({
|
|
61
|
+
count: sortedChains.length,
|
|
62
|
+
overscan: 3,
|
|
63
|
+
paddingEnd: 0,
|
|
64
|
+
getScrollElement: () => scrollElementRef.current,
|
|
65
|
+
estimateSize: () => {
|
|
66
|
+
return itemsSize === 'small' ? 48 : 60
|
|
67
|
+
},
|
|
68
|
+
getItemKey,
|
|
69
|
+
})
|
|
51
70
|
|
|
52
71
|
// Using mountOnEnter of the ExpansionTransition component
|
|
53
72
|
// leads to a short delay for setting up scrollElementRef,
|
|
@@ -59,6 +78,31 @@ export const VirtualizedChainList = ({
|
|
|
59
78
|
}
|
|
60
79
|
}, [measure, scrollElementRef.current])
|
|
61
80
|
|
|
81
|
+
useLayoutEffect(() => {
|
|
82
|
+
// Only scroll if sortedChains is not empty and we haven't scrolled yet
|
|
83
|
+
if (!hasScrolledRef.current && sortedChains.length > 0 && range) {
|
|
84
|
+
const selectedChainIndex = sortedChains.findIndex(
|
|
85
|
+
(chain) => chain.id === selectedChainIdRef.current
|
|
86
|
+
)
|
|
87
|
+
if (selectedChainIndex !== -1) {
|
|
88
|
+
// Only scroll if the selected chain is not in the visible range
|
|
89
|
+
// +1 and -1 to account for partially visible items
|
|
90
|
+
if (
|
|
91
|
+
range.startIndex + 1 > selectedChainIndex ||
|
|
92
|
+
range.endIndex - 1 < selectedChainIndex
|
|
93
|
+
) {
|
|
94
|
+
requestAnimationFrame(() => {
|
|
95
|
+
scrollToIndex(selectedChainIndex, {
|
|
96
|
+
align: 'center',
|
|
97
|
+
behavior: 'smooth',
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
hasScrolledRef.current = true // Mark as scrolled (when needed)
|
|
103
|
+
}
|
|
104
|
+
}, [sortedChains, scrollToIndex, range])
|
|
105
|
+
|
|
62
106
|
return (
|
|
63
107
|
<List
|
|
64
108
|
className="long-list"
|
|
@@ -76,6 +120,9 @@ export const VirtualizedChainList = ({
|
|
|
76
120
|
itemsSize={itemsSize}
|
|
77
121
|
size={item.size}
|
|
78
122
|
start={item.start}
|
|
123
|
+
withPin={withPinnedChains}
|
|
124
|
+
isPinned={pinnedChains.includes(chain.id)}
|
|
125
|
+
onPin={onPin}
|
|
79
126
|
/>
|
|
80
127
|
)
|
|
81
128
|
})}
|