@lifi/widget 4.1.0 → 4.2.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 (204) hide show
  1. package/dist/esm/AppDefault.js +14 -0
  2. package/dist/esm/AppDefault.js.map +1 -1
  3. package/dist/esm/components/AmountInput/AmountInput.js +2 -1
  4. package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
  5. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +4 -2
  6. package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
  7. package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js +1 -1
  8. package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js.map +1 -1
  9. package/dist/esm/components/AmountInput/PriceFormHelperText.js +4 -2
  10. package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
  11. package/dist/esm/components/AmountInputCard/AmountInputCard.style.js +98 -0
  12. package/dist/esm/components/AmountInputCard/AmountInputCard.style.js.map +1 -0
  13. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +25 -0
  14. package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -0
  15. package/dist/esm/components/AmountInputCard/BalanceDisplay.js +35 -0
  16. package/dist/esm/components/AmountInputCard/BalanceDisplay.js.map +1 -0
  17. package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js +11 -0
  18. package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js.map +1 -0
  19. package/dist/esm/components/AmountInputCard/FiatValueToggle.js +41 -0
  20. package/dist/esm/components/AmountInputCard/FiatValueToggle.js.map +1 -0
  21. package/dist/esm/components/AmountInputCard/PercentageChips.js +80 -0
  22. package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -0
  23. package/dist/esm/components/AmountInputCard/PercentageChips.style.js +43 -0
  24. package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -0
  25. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +137 -0
  26. package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -0
  27. package/dist/esm/components/AmountInputCard/SendAmountCard.js +102 -0
  28. package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -0
  29. package/dist/esm/components/Avatar/Avatar.js +12 -3
  30. package/dist/esm/components/Avatar/Avatar.js.map +1 -1
  31. package/dist/esm/components/Chains/VirtualizedChainList.js +2 -1
  32. package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
  33. package/dist/esm/components/Header/HeaderNavigationTabs.js +14 -1
  34. package/dist/esm/components/Header/HeaderNavigationTabs.js.map +1 -1
  35. package/dist/esm/components/IconCircle/IconCircle.style.js +1 -1
  36. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js +99 -0
  37. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js.map +1 -0
  38. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js +52 -0
  39. package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js.map +1 -0
  40. package/dist/esm/components/LimitPriceCard/LimitPriceCard.js +115 -0
  41. package/dist/esm/components/LimitPriceCard/LimitPriceCard.js.map +1 -0
  42. package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js +89 -0
  43. package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js.map +1 -0
  44. package/dist/esm/components/LimitPriceCard/LimitPricePresets.js +91 -0
  45. package/dist/esm/components/LimitPriceCard/LimitPricePresets.js.map +1 -0
  46. package/dist/esm/components/RouteCard/RouteProviderCard.js +118 -0
  47. package/dist/esm/components/RouteCard/RouteProviderCard.js.map +1 -0
  48. package/dist/esm/components/Routes/LimitOrderRoutes.js +34 -0
  49. package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -0
  50. package/dist/esm/components/Routes/Routes.js +2 -2
  51. package/dist/esm/components/Routes/Routes.js.map +1 -1
  52. package/dist/esm/components/SettingsPanel/SettingsPanel.js +51 -0
  53. package/dist/esm/components/SettingsPanel/SettingsPanel.js.map +1 -0
  54. package/dist/esm/components/SettingsPanel/SettingsPanel.style.js +33 -0
  55. package/dist/esm/components/SettingsPanel/SettingsPanel.style.js.map +1 -0
  56. package/dist/esm/components/SettingsPanel/utils.js +14 -0
  57. package/dist/esm/components/SettingsPanel/utils.js.map +1 -0
  58. package/dist/esm/components/SwapButton/SwapButton.js +50 -0
  59. package/dist/esm/components/SwapButton/SwapButton.js.map +1 -0
  60. package/dist/esm/components/SwapButton/SwapButton.style.js +24 -0
  61. package/dist/esm/components/SwapButton/SwapButton.style.js.map +1 -0
  62. package/dist/esm/components/TokenPillButton/TokenPillButton.js +52 -0
  63. package/dist/esm/components/TokenPillButton/TokenPillButton.js.map +1 -0
  64. package/dist/esm/components/TokenPillButton/TokenPillButton.style.js +39 -0
  65. package/dist/esm/components/TokenPillButton/TokenPillButton.style.js.map +1 -0
  66. package/dist/esm/config/version.d.ts +1 -1
  67. package/dist/esm/config/version.js +1 -1
  68. package/dist/esm/config/version.js.map +1 -1
  69. package/dist/esm/hooks/useChainSearch.js +2 -1
  70. package/dist/esm/hooks/useChainSearch.js.map +1 -1
  71. package/dist/esm/hooks/useLimitMarketRate.js +24 -0
  72. package/dist/esm/hooks/useLimitMarketRate.js.map +1 -0
  73. package/dist/esm/hooks/useLinkedLimitFields.js +49 -0
  74. package/dist/esm/hooks/useLinkedLimitFields.js.map +1 -0
  75. package/dist/esm/hooks/useRoutes.js +23 -6
  76. package/dist/esm/hooks/useRoutes.js.map +1 -1
  77. package/dist/esm/hooks/useTokens.js +16 -14
  78. package/dist/esm/hooks/useTokens.js.map +1 -1
  79. package/dist/esm/hooks/useWideVariant.js +5 -2
  80. package/dist/esm/hooks/useWideVariant.js.map +1 -1
  81. package/dist/esm/i18n/en.json +22 -0
  82. package/dist/esm/index.d.ts +2 -2
  83. package/dist/esm/pages/MainPage/LimitPageContent.js +23 -0
  84. package/dist/esm/pages/MainPage/LimitPageContent.js.map +1 -0
  85. package/dist/esm/pages/MainPage/MainPage.js +19 -22
  86. package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
  87. package/dist/esm/pages/MainPage/MainPageActions.js +28 -0
  88. package/dist/esm/pages/MainPage/MainPageActions.js.map +1 -0
  89. package/dist/esm/pages/MainPage/ReviewButton.js +9 -2
  90. package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
  91. package/dist/esm/pages/RoutePriorityPage.js +48 -0
  92. package/dist/esm/pages/RoutePriorityPage.js.map +1 -0
  93. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
  94. package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
  95. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +12 -27
  96. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
  97. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js +133 -0
  98. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js.map +1 -0
  99. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +14 -72
  100. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
  101. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js +26 -14
  102. package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js.map +1 -1
  103. package/dist/esm/pages/SettingsPage/ThemeSettings.js +4 -0
  104. package/dist/esm/pages/SettingsPage/ThemeSettings.js.map +1 -1
  105. package/dist/esm/pages/TransactionPage/TransactionReview.js +4 -1
  106. package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
  107. package/dist/esm/providers/I18nProvider/I18nProvider.js +1 -1
  108. package/dist/esm/providers/I18nProvider/enResource.d.ts +1 -1
  109. package/dist/esm/providers/I18nProvider/enResource.js +1 -1
  110. package/dist/esm/providers/I18nProvider/types.d.ts +2 -2
  111. package/dist/esm/providers/SDKClientProvider.js +25 -24
  112. package/dist/esm/providers/SDKClientProvider.js.map +1 -1
  113. package/dist/esm/providers/WidgetProvider/WidgetProvider.js +22 -21
  114. package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
  115. package/dist/esm/stores/form/URLSearchParamsBuilder.js +14 -3
  116. package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
  117. package/dist/esm/stores/form/createFormStore.js +6 -2
  118. package/dist/esm/stores/form/createFormStore.js.map +1 -1
  119. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +3 -0
  120. package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
  121. package/dist/esm/stores/form/types.d.ts +9 -1
  122. package/dist/esm/stores/form/types.d.ts.map +1 -1
  123. package/dist/esm/stores/form/types.js.map +1 -1
  124. package/dist/esm/stores/form/useClearAmountFields.js +1 -0
  125. package/dist/esm/stores/form/useClearAmountFields.js.map +1 -1
  126. package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js +4 -2
  127. package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js.map +1 -1
  128. package/dist/esm/stores/navigationTabs/utils.js +38 -8
  129. package/dist/esm/stores/navigationTabs/utils.js.map +1 -1
  130. package/dist/esm/types/events.d.ts +10 -1
  131. package/dist/esm/types/events.d.ts.map +1 -1
  132. package/dist/esm/types/events.js +2 -0
  133. package/dist/esm/types/events.js.map +1 -1
  134. package/dist/esm/types/widget.d.ts +9 -0
  135. package/dist/esm/types/widget.d.ts.map +1 -1
  136. package/dist/esm/types/widget.js.map +1 -1
  137. package/dist/esm/utils/format.d.ts +1 -1
  138. package/dist/esm/utils/format.d.ts.map +1 -1
  139. package/dist/esm/utils/format.js +2 -2
  140. package/dist/esm/utils/format.js.map +1 -1
  141. package/dist/esm/utils/limitOrder.js +61 -0
  142. package/dist/esm/utils/limitOrder.js.map +1 -0
  143. package/dist/esm/utils/navigationRoutes.d.ts +2 -0
  144. package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
  145. package/dist/esm/utils/navigationRoutes.js +6 -0
  146. package/dist/esm/utils/navigationRoutes.js.map +1 -1
  147. package/package.json +11 -11
  148. package/src/AppDefault.tsx +16 -0
  149. package/src/components/AmountInput/AmountInputEndAdornment.style.ts +1 -1
  150. package/src/components/AmountInput/AmountInputEndAdornment.tsx +5 -2
  151. package/src/components/AmountInputCard/AmountInputCard.style.tsx +157 -0
  152. package/src/components/AmountInputCard/AmountInputCardPair.tsx +25 -0
  153. package/src/components/AmountInputCard/BalanceDisplay.style.tsx +9 -0
  154. package/src/components/AmountInputCard/BalanceDisplay.tsx +46 -0
  155. package/src/components/AmountInputCard/FiatValueToggle.tsx +55 -0
  156. package/src/components/AmountInputCard/PercentageChips.style.tsx +60 -0
  157. package/src/components/AmountInputCard/PercentageChips.tsx +100 -0
  158. package/src/components/AmountInputCard/ReceiveAmountCard.tsx +219 -0
  159. package/src/components/AmountInputCard/SendAmountCard.tsx +179 -0
  160. package/src/components/Avatar/Avatar.tsx +8 -3
  161. package/src/components/Header/HeaderNavigationTabs.tsx +19 -2
  162. package/src/components/LimitOrderSettings/LimitOrderSettings.style.tsx +69 -0
  163. package/src/components/LimitOrderSettings/LimitOrderSettings.tsx +124 -0
  164. package/src/components/LimitPriceCard/LimitPriceCard.style.tsx +103 -0
  165. package/src/components/LimitPriceCard/LimitPriceCard.tsx +153 -0
  166. package/src/components/LimitPriceCard/LimitPricePresets.tsx +139 -0
  167. package/src/components/RouteCard/RouteProviderCard.tsx +113 -0
  168. package/src/components/Routes/LimitOrderRoutes.tsx +64 -0
  169. package/src/components/Routes/Routes.tsx +3 -2
  170. package/src/components/SettingsPanel/SettingsPanel.style.tsx +41 -0
  171. package/src/components/SettingsPanel/SettingsPanel.tsx +73 -0
  172. package/src/components/SettingsPanel/utils.ts +27 -0
  173. package/src/components/SwapButton/SwapButton.style.tsx +28 -0
  174. package/src/components/SwapButton/SwapButton.tsx +71 -0
  175. package/src/components/TokenPillButton/TokenPillButton.style.tsx +50 -0
  176. package/src/components/TokenPillButton/TokenPillButton.tsx +81 -0
  177. package/src/config/version.ts +1 -1
  178. package/src/hooks/useLimitMarketRate.ts +39 -0
  179. package/src/hooks/useLinkedLimitFields.ts +74 -0
  180. package/src/hooks/useRoutes.ts +45 -4
  181. package/src/hooks/useWideVariant.ts +8 -2
  182. package/src/pages/MainPage/LimitPageContent.tsx +18 -0
  183. package/src/pages/MainPage/MainPage.tsx +43 -30
  184. package/src/pages/MainPage/MainPageActions.tsx +35 -0
  185. package/src/pages/MainPage/ReviewButton.tsx +13 -1
  186. package/src/pages/RoutePriorityPage.tsx +55 -0
  187. package/src/pages/SettingsPage/RoutePrioritySettings.tsx +14 -38
  188. package/src/pages/SettingsPage/SlippageSettings/SlippagePage.tsx +176 -0
  189. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.style.tsx +32 -25
  190. package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +14 -128
  191. package/src/pages/SettingsPage/ThemeSettings.tsx +4 -0
  192. package/src/stores/form/URLSearchParamsBuilder.tsx +25 -3
  193. package/src/stores/form/createFormStore.ts +7 -3
  194. package/src/stores/form/getDefaultValuesFromQueryString.ts +11 -0
  195. package/src/stores/form/types.ts +14 -1
  196. package/src/stores/form/useClearAmountFields.ts +1 -0
  197. package/src/stores/navigationTabs/types.ts +6 -0
  198. package/src/stores/navigationTabs/useNavigationTabsStore.tsx +8 -3
  199. package/src/stores/navigationTabs/utils.ts +67 -5
  200. package/src/types/events.ts +12 -0
  201. package/src/types/widget.ts +9 -0
  202. package/src/utils/format.ts +6 -2
  203. package/src/utils/limitOrder.ts +88 -0
  204. package/src/utils/navigationRoutes.ts +6 -0
@@ -1,26 +1,38 @@
1
- import { Box } from '@mui/material'
2
1
  import { useTranslation } from 'react-i18next'
3
2
  import { AmountInput } from '../../components/AmountInput/AmountInput.js'
3
+ import { AmountInputCardPair } from '../../components/AmountInputCard/AmountInputCardPair.js'
4
4
  import { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'
5
5
  import { GasRefuelMessage } from '../../components/Messages/GasRefuelMessage.js'
6
6
  import { PageContainer } from '../../components/PageContainer.js'
7
- import { PoweredBy } from '../../components/PoweredBy/PoweredBy.js'
8
7
  import { Routes } from '../../components/Routes/Routes.js'
9
8
  import { SelectChainAndToken } from '../../components/SelectChainAndToken.js'
10
9
  import { SendToWalletButton } from '../../components/SendToWallet/SendToWalletButton.js'
11
- import { SendToWalletExpandButton } from '../../components/SendToWallet/SendToWalletExpandButton.js'
10
+ import { SettingsPanel } from '../../components/SettingsPanel/SettingsPanel.js'
11
+ import { advancedNavigationTabKeys } from '../../components/SettingsPanel/utils.js'
12
12
  import { useHeader } from '../../hooks/useHeader.js'
13
13
  import { useWideVariant } from '../../hooks/useWideVariant.js'
14
14
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
15
- import { MainWarningMessages } from './MainWarningMessages.js'
16
- import { ReviewButton } from './ReviewButton.js'
15
+ import { useNavigationTabsStore } from '../../stores/navigationTabs/useNavigationTabsStore.js'
16
+ import { LimitPageContent } from './LimitPageContent.js'
17
+ import { MainPageActions } from './MainPageActions.js'
18
+
19
+ const marginSx = { marginBottom: 2 }
17
20
 
18
21
  export const MainPage: React.FC = () => {
19
22
  const { t } = useTranslation()
20
23
  const wideVariant = useWideVariant()
21
- const { mode, modeOptions, contractComponent, hiddenUI } = useWidgetConfig()
24
+ const { mode, modeOptions, contractComponent, hiddenUI, defaultUI } =
25
+ useWidgetConfig()
26
+ const activeTab = useNavigationTabsStore((state) => state.activeTab)
27
+
22
28
  const custom = mode === 'custom'
23
- const showPoweredBy = !hiddenUI?.poweredBy
29
+ // Surface the settings panel on the main page for advanced navigation tabs.
30
+ const advancedMode =
31
+ !!activeTab && advancedNavigationTabKeys.includes(activeTab)
32
+ const showSendReceiveCards = defaultUI?.layout === 'cards'
33
+ // Custom non-deposit contract flows have no amount entry — they keep the
34
+ // chain/token selectors only, in both layouts.
35
+ const showAmountEntry = !custom || modeOptions?.custom?.type === 'deposit'
24
36
  const showGasRefuelMessage = !hiddenUI?.gasRefuelMessage
25
37
 
26
38
  const splitTitle =
@@ -30,41 +42,42 @@ export const MainPage: React.FC = () => {
30
42
  ? t('header.swap')
31
43
  : undefined
32
44
  const title =
33
- mode === 'custom'
34
- ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}`)
35
- : mode === 'refuel'
36
- ? t('header.gas')
37
- : mode === 'split' && splitTitle
38
- ? splitTitle
39
- : t('header.exchange')
45
+ mode === 'limit'
46
+ ? t('header.limit')
47
+ : mode === 'custom'
48
+ ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}`)
49
+ : mode === 'refuel'
50
+ ? t('header.gas')
51
+ : mode === 'split' && splitTitle
52
+ ? splitTitle
53
+ : t('header.exchange')
40
54
 
41
55
  useHeader(title)
42
56
 
43
- const marginSx = { marginBottom: 2 }
57
+ if (mode === 'limit') {
58
+ return <LimitPageContent />
59
+ }
44
60
 
45
61
  return (
46
62
  <PageContainer topGutters>
47
63
  {custom ? (
48
64
  <ContractComponent sx={marginSx}>{contractComponent}</ContractComponent>
49
65
  ) : null}
50
- <SelectChainAndToken sx={marginSx} />
51
- {!custom || modeOptions?.custom?.type === 'deposit' ? (
52
- <AmountInput formType="from" sx={marginSx} />
53
- ) : null}
66
+ {showSendReceiveCards && showAmountEntry ? (
67
+ <AmountInputCardPair sx={marginSx} />
68
+ ) : (
69
+ <>
70
+ <SelectChainAndToken sx={marginSx} />
71
+ {!showSendReceiveCards && showAmountEntry ? (
72
+ <AmountInput formType="from" sx={marginSx} />
73
+ ) : null}
74
+ </>
75
+ )}
76
+ {advancedMode ? <SettingsPanel sx={marginSx} /> : null}
54
77
  {!wideVariant ? <Routes sx={marginSx} /> : null}
55
78
  <SendToWalletButton sx={marginSx} />
56
79
  {showGasRefuelMessage ? <GasRefuelMessage sx={marginSx} /> : null}
57
- <MainWarningMessages sx={marginSx} />
58
- <Box
59
- sx={{
60
- display: 'flex',
61
- mb: showPoweredBy ? 1 : 3,
62
- }}
63
- >
64
- <ReviewButton />
65
- <SendToWalletExpandButton />
66
- </Box>
67
- {showPoweredBy ? <PoweredBy /> : null}
80
+ <MainPageActions showSendToWalletExpand />
68
81
  </PageContainer>
69
82
  )
70
83
  }
@@ -0,0 +1,35 @@
1
+ import { Box } from '@mui/material'
2
+ import { PoweredBy } from '../../components/PoweredBy/PoweredBy.js'
3
+ import { SendToWalletExpandButton } from '../../components/SendToWallet/SendToWalletExpandButton.js'
4
+ import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
5
+ import { MainWarningMessages } from './MainWarningMessages.js'
6
+ import { ReviewButton } from './ReviewButton.js'
7
+
8
+ interface MainPageActionsProps {
9
+ showSendToWalletExpand?: boolean
10
+ }
11
+
12
+ const marginSx = { marginBottom: 2 }
13
+
14
+ export const MainPageActions: React.FC<MainPageActionsProps> = ({
15
+ showSendToWalletExpand,
16
+ }) => {
17
+ const { hiddenUI } = useWidgetConfig()
18
+ const showPoweredBy = !hiddenUI?.poweredBy
19
+
20
+ return (
21
+ <>
22
+ <MainWarningMessages sx={marginSx} />
23
+ <Box
24
+ sx={{
25
+ display: 'flex',
26
+ mb: showPoweredBy ? 1 : 3,
27
+ }}
28
+ >
29
+ <ReviewButton />
30
+ {showSendToWalletExpand ? <SendToWalletExpandButton /> : null}
31
+ </Box>
32
+ {showPoweredBy ? <PoweredBy /> : null}
33
+ </>
34
+ )
35
+ }
@@ -5,6 +5,7 @@ import { useRoutes } from '../../hooks/useRoutes.js'
5
5
  import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
6
6
  import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
7
7
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
8
+ import { useFieldValues } from '../../stores/form/useFieldValues.js'
8
9
  import { useSplitMode } from '../../stores/navigationTabs/useNavigationTabsStore.js'
9
10
  import { WidgetEvent } from '../../types/events.js'
10
11
  import { navigationRoutes } from '../../utils/navigationRoutes.js'
@@ -17,8 +18,15 @@ export const ReviewButton: React.FC = () => {
17
18
  const splitState = useSplitMode()
18
19
  const { toAddress, requiredToAddress } = useToAddressRequirements()
19
20
  const { routes, setReviewableRoute } = useRoutes()
21
+ const [selectedRouteId] = useFieldValues('selectedRouteId')
20
22
 
21
- const currentRoute = routes?.[0]
23
+ // The provider pick only applies in limit mode (it's the only flow that sets
24
+ // it). Honoring it in other modes would be wrong, since provider/tool keys
25
+ // can collide across modes. Everywhere else, fall back to the best route.
26
+ const currentRoute =
27
+ (mode === 'limit'
28
+ ? routes?.find((route) => route.id === selectedRouteId)
29
+ : undefined) ?? routes?.[0]
22
30
 
23
31
  const handleClick = async () => {
24
32
  if (!currentRoute) {
@@ -43,6 +51,8 @@ export const ReviewButton: React.FC = () => {
43
51
  return t(`button.${modeOptions?.custom?.type ?? 'checkout'}Review`)
44
52
  case 'refuel':
45
53
  return t('button.getGas')
54
+ case 'limit':
55
+ return t('button.review')
46
56
  default: {
47
57
  const transactionType =
48
58
  currentRoute.fromChainId === currentRoute.toChainId
@@ -59,6 +69,8 @@ export const ReviewButton: React.FC = () => {
59
69
  : t('button.buy')
60
70
  case 'refuel':
61
71
  return t('button.getGas')
72
+ case 'limit':
73
+ return t('button.review')
62
74
  case 'split':
63
75
  if (splitState) {
64
76
  return t(`button.${splitState}`)
@@ -0,0 +1,55 @@
1
+ import type { Order } from '@lifi/sdk'
2
+ import Check from '@mui/icons-material/Check'
3
+ import { List } from '@mui/material'
4
+ import { useRef } from 'react'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { ListItemText } from '../components/ListItemText.js'
7
+ import { PageContainer } from '../components/PageContainer.js'
8
+ import { SettingsListItemButton } from '../components/SettingsListItemButton.js'
9
+ import { useHeader } from '../hooks/useHeader.js'
10
+ import { useListHeight } from '../hooks/useListHeight.js'
11
+ import { useSettings } from '../stores/settings/useSettings.js'
12
+ import { useSettingsActions } from '../stores/settings/useSettingsActions.js'
13
+
14
+ const routePriorities: Order[] = ['CHEAPEST', 'FASTEST']
15
+
16
+ export const RoutePriorityPage: React.FC = () => {
17
+ const { t } = useTranslation()
18
+ const { setValue } = useSettingsActions()
19
+ const { routePriority } = useSettings(['routePriority'])
20
+ const listParentRef = useRef<HTMLUListElement | null>(null)
21
+ const { listHeight } = useListHeight({ listParentRef })
22
+
23
+ useHeader(t('settings.routePriority'))
24
+
25
+ return (
26
+ <PageContainer disableGutters>
27
+ <List
28
+ className="long-list"
29
+ ref={listParentRef}
30
+ style={{ height: listHeight, overflow: 'auto' }}
31
+ sx={{
32
+ padding: 1.5,
33
+ }}
34
+ >
35
+ {routePriorities.map((priority) => {
36
+ const key = priority.toLowerCase()
37
+ return (
38
+ <SettingsListItemButton
39
+ key={priority}
40
+ onClick={() => setValue('routePriority', priority)}
41
+ >
42
+ <ListItemText
43
+ primary={t(`settings.routePriorityOptions.${key}.title` as any)}
44
+ secondary={t(
45
+ `settings.routePriorityOptions.${key}.description` as any
46
+ )}
47
+ />
48
+ {routePriority === priority && <Check color="primary" />}
49
+ </SettingsListItemButton>
50
+ )
51
+ })}
52
+ </List>
53
+ </PageContainer>
54
+ )
55
+ }
@@ -1,57 +1,33 @@
1
- import type { Order } from '@lifi/sdk'
2
1
  import Route from '@mui/icons-material/Route'
2
+ import { useNavigate } from '@tanstack/react-router'
3
3
  import { useTranslation } from 'react-i18next'
4
- import { CardTabs, Tab } from '../../components/Tabs/Tabs.style.js'
4
+ import { CardButton } from '../../components/Card/CardButton.js'
5
5
  import { useSettingMonitor } from '../../hooks/useSettingMonitor.js'
6
6
  import { useSettings } from '../../stores/settings/useSettings.js'
7
- import { useSettingsActions } from '../../stores/settings/useSettingsActions.js'
7
+ import { navigationRoutes } from '../../utils/navigationRoutes.js'
8
8
  import { BadgedValue } from './SettingsCard/BadgedValue.js'
9
- import { SettingCardExpandable } from './SettingsCard/SettingCardExpandable.js'
10
-
11
- const Priorities: Order[] = ['CHEAPEST', 'FASTEST']
12
9
 
13
10
  export const RoutePrioritySettings: React.FC = () => {
14
11
  const { t } = useTranslation()
15
- const { setValue } = useSettingsActions()
12
+ const navigate = useNavigate()
16
13
  const { isRoutePriorityChanged } = useSettingMonitor()
17
14
  const { routePriority } = useSettings(['routePriority'])
18
- const currentRoutePriority = routePriority ?? ''
19
15
 
20
- const handleRoutePriorityChange = (
21
- _: React.SyntheticEvent,
22
- routePriority: Order
23
- ) => {
24
- setValue('routePriority', routePriority)
16
+ const handleClick = () => {
17
+ navigate({ to: navigationRoutes.routePriority })
25
18
  }
26
19
 
27
20
  return (
28
- <SettingCardExpandable
29
- value={
30
- <BadgedValue badgeColor="info" showBadge={isRoutePriorityChanged}>
31
- {t(`main.tags.${currentRoutePriority.toLowerCase()}` as any)}
32
- </BadgedValue>
33
- }
21
+ <CardButton
22
+ onClick={handleClick}
34
23
  icon={<Route />}
35
24
  title={t('settings.routePriority')}
36
25
  >
37
- <CardTabs
38
- value={currentRoutePriority}
39
- aria-label="tabs"
40
- indicatorColor="primary"
41
- onChange={handleRoutePriorityChange}
42
- sx={{ mt: 1.5 }}
43
- >
44
- {Priorities.map((priority) => {
45
- return (
46
- <Tab
47
- key={priority}
48
- label={t(`main.tags.${priority.toLowerCase()}` as any)}
49
- value={priority}
50
- disableRipple
51
- />
52
- )
53
- })}
54
- </CardTabs>
55
- </SettingCardExpandable>
26
+ <BadgedValue badgeColor="info" showBadge={isRoutePriorityChanged}>
27
+ {routePriority === 'FASTEST'
28
+ ? t('settings.routePriorityOptions.fastest.title')
29
+ : t('settings.routePriorityOptions.cheapest.title')}
30
+ </BadgedValue>
31
+ </CardButton>
56
32
  )
57
33
  }
@@ -0,0 +1,176 @@
1
+ import Check from '@mui/icons-material/Check'
2
+ import CloseRounded from '@mui/icons-material/CloseRounded'
3
+ import WarningRounded from '@mui/icons-material/WarningRounded'
4
+ import { Box, Collapse, List, Typography } from '@mui/material'
5
+ import type { ChangeEventHandler, FocusEventHandler } from 'react'
6
+ import { useRef, useState } from 'react'
7
+ import { useTranslation } from 'react-i18next'
8
+ import { ListItemText } from '../../../components/ListItemText.js'
9
+ import { PageContainer } from '../../../components/PageContainer.js'
10
+ import { SettingsListItemButton } from '../../../components/SettingsListItemButton.js'
11
+ import { useHeader } from '../../../hooks/useHeader.js'
12
+ import { useListHeight } from '../../../hooks/useListHeight.js'
13
+ import { useSettingMonitor } from '../../../hooks/useSettingMonitor.js'
14
+ import { defaultSlippage } from '../../../stores/settings/createSettingsStore.js'
15
+ import { useSettings } from '../../../stores/settings/useSettings.js'
16
+ import { useSettingsActions } from '../../../stores/settings/useSettingsActions.js'
17
+ import { formatInputAmount, formatSlippage } from '../../../utils/format.js'
18
+ import {
19
+ SlippageClearButton,
20
+ SlippageInput,
21
+ SlippageLimitsWarningContainer,
22
+ } from './SlippageSettings.style.js'
23
+
24
+ const slippagePresets = ['0.5', '1']
25
+ const maxFractionDigits = 5
26
+
27
+ export const SlippagePage: React.FC = () => {
28
+ const { t } = useTranslation()
29
+ const {
30
+ isSlippageNotRecommended,
31
+ isSlippageUnderRecommendedLimits,
32
+ isSlippageOutsideRecommendedLimits,
33
+ } = useSettingMonitor()
34
+ const { slippage } = useSettings(['slippage'])
35
+ const { setValue } = useSettingsActions()
36
+ const defaultValue = useRef(slippage)
37
+ const listParentRef = useRef<HTMLUListElement | null>(null)
38
+ const { listHeight } = useListHeight({ listParentRef })
39
+
40
+ useHeader(t('settings.slippage'))
41
+
42
+ const isAuto = !slippage || slippage === defaultSlippage
43
+ const isPreset = !!slippage && slippagePresets.includes(slippage)
44
+ const isCustom = !isAuto && !isPreset
45
+
46
+ const [customMode, setCustomMode] = useState(isCustom)
47
+ const [inputValue, setInputValue] = useState(isCustom ? (slippage ?? '') : '')
48
+
49
+ const handleAuto = () => {
50
+ setCustomMode(false)
51
+ setInputValue('')
52
+ setValue('slippage', defaultSlippage)
53
+ }
54
+
55
+ const handlePreset = (preset: string) => {
56
+ setCustomMode(false)
57
+ setValue('slippage', preset)
58
+ }
59
+
60
+ const handleCustom = () => {
61
+ setCustomMode(true)
62
+ const initialValue = slippage || ''
63
+ if (initialValue) {
64
+ setInputValue(initialValue)
65
+ setValue('slippage', initialValue)
66
+ }
67
+ }
68
+
69
+ const formatAndSetSlippage = (value: string, returnInitial = false) => {
70
+ const formattedSlippage = formatSlippage(
71
+ value,
72
+ defaultValue.current,
73
+ returnInitial
74
+ )
75
+ const formattedValue =
76
+ Number(formattedSlippage) === 0 && !returnInitial
77
+ ? '0'
78
+ : formatInputAmount(formattedSlippage, maxFractionDigits, returnInitial)
79
+ const maxLength =
80
+ Number(formattedValue) < 10
81
+ ? maxFractionDigits + 2
82
+ : maxFractionDigits + 3
83
+ const slicedValue = formattedValue.slice(0, maxLength)
84
+ setInputValue(slicedValue)
85
+ setValue('slippage', slicedValue.length ? slicedValue : defaultSlippage)
86
+ }
87
+
88
+ const handleInputUpdate: ChangeEventHandler<HTMLInputElement> = (event) => {
89
+ formatAndSetSlippage(event.target.value, true)
90
+ }
91
+
92
+ const handleInputFocus: FocusEventHandler<HTMLInputElement> = (event) => {
93
+ formatAndSetSlippage(event.target.value)
94
+ }
95
+
96
+ const handleInputBlur: FocusEventHandler<HTMLInputElement> = (event) => {
97
+ formatAndSetSlippage(event.target.value)
98
+ }
99
+
100
+ const slippageWarningText = isSlippageOutsideRecommendedLimits
101
+ ? t('warning.message.slippageOutsideRecommendedLimits')
102
+ : isSlippageUnderRecommendedLimits
103
+ ? t('warning.message.slippageUnderRecommendedLimits')
104
+ : ''
105
+
106
+ return (
107
+ <PageContainer disableGutters>
108
+ <List
109
+ className="long-list"
110
+ ref={listParentRef}
111
+ style={{ height: listHeight, overflow: 'auto' }}
112
+ sx={{
113
+ padding: 1.5,
114
+ }}
115
+ >
116
+ <SettingsListItemButton onClick={handleAuto}>
117
+ <ListItemText
118
+ primary={t('button.auto')}
119
+ secondary={t('settings.slippageAutoDescription')}
120
+ />
121
+ {!customMode && isAuto && <Check color="primary" />}
122
+ </SettingsListItemButton>
123
+ {slippagePresets.map((preset) => (
124
+ <SettingsListItemButton
125
+ key={preset}
126
+ onClick={() => handlePreset(preset)}
127
+ >
128
+ <ListItemText primary={`${preset}%`} />
129
+ {!customMode && slippage === preset && <Check color="primary" />}
130
+ </SettingsListItemButton>
131
+ ))}
132
+ <SettingsListItemButton onClick={handleCustom}>
133
+ <ListItemText primary={t('settings.custom')} />
134
+ {customMode && <Check color="primary" />}
135
+ </SettingsListItemButton>
136
+ <Collapse in={customMode}>
137
+ <Box sx={{ px: 1.5, pt: 1 }}>
138
+ <SlippageInput
139
+ placeholder="0.5"
140
+ inputProps={{ inputMode: 'decimal' }}
141
+ value={inputValue}
142
+ onChange={handleInputUpdate}
143
+ onFocus={handleInputFocus}
144
+ onBlur={handleInputBlur}
145
+ autoComplete="off"
146
+ endAdornment={
147
+ !isAuto && (
148
+ <SlippageClearButton
149
+ size="small"
150
+ onClick={handleAuto}
151
+ aria-label="Clear"
152
+ >
153
+ <CloseRounded fontSize="small" />
154
+ </SlippageClearButton>
155
+ )
156
+ }
157
+ />
158
+ {isSlippageNotRecommended && (
159
+ <SlippageLimitsWarningContainer>
160
+ <WarningRounded color="warning" />
161
+ <Typography
162
+ sx={{
163
+ fontSize: 13,
164
+ fontWeight: 400,
165
+ }}
166
+ >
167
+ {slippageWarningText}
168
+ </Typography>
169
+ </SlippageLimitsWarningContainer>
170
+ )}
171
+ </Box>
172
+ </Collapse>
173
+ </List>
174
+ </PageContainer>
175
+ )
176
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Theme } from '@mui/material'
2
2
  import {
3
3
  Box,
4
- ButtonBase,
4
+ IconButton,
5
5
  InputBase,
6
6
  inputBaseClasses,
7
7
  styled,
@@ -36,30 +36,6 @@ interface SettingsControlProps {
36
36
  selected?: boolean
37
37
  }
38
38
 
39
- export const SettingsDefaultButton: React.FC<
40
- React.ComponentProps<typeof ButtonBase> & SettingsControlProps
41
- > = styled(ButtonBase)<SettingsControlProps>(({ theme, selected }) => {
42
- const settingsControlSelectedStyles = settingsControlSelected(theme)
43
- const selectedStyle = selected
44
- ? {
45
- '&:not(:focus)': {
46
- ...settingsControlSelectedStyles,
47
- },
48
- }
49
- : {}
50
-
51
- return {
52
- height: '100%',
53
- width: '100%',
54
- fontSize: '1rem',
55
- fontWeight: 700,
56
- '&:focus': {
57
- ...settingsControlSelectedStyles,
58
- },
59
- ...selectedStyle,
60
- }
61
- })
62
-
63
39
  export const SettingsCustomInput: React.FC<
64
40
  React.ComponentProps<typeof InputBase> & SettingsControlProps
65
41
  > = styled(InputBase)<SettingsControlProps>(({ theme, selected }) => {
@@ -100,3 +76,34 @@ export const SlippageLimitsWarningContainer: React.FC<
100
76
  gap: theme.spacing(1.25),
101
77
  marginTop: theme.spacing(1.5),
102
78
  }))
79
+
80
+ export const SlippageInput: React.FC<React.ComponentProps<typeof InputBase>> =
81
+ styled(InputBase)(({ theme }) => ({
82
+ minHeight: 44,
83
+ width: '100%',
84
+ backgroundColor: theme.vars.palette.background.default,
85
+ border: '1px solid',
86
+ borderColor: theme.vars.palette.divider,
87
+ borderRadius: theme.vars.shape.borderRadius,
88
+ paddingRight: theme.spacing(1),
89
+ [`.${inputBaseClasses.input}`]: {
90
+ padding: theme.spacing(1.5),
91
+ fontSize: 16,
92
+ fontWeight: 500,
93
+ lineHeight: '20px',
94
+ '&::placeholder': {
95
+ opacity: 1,
96
+ color: theme.vars.palette.text.secondary,
97
+ },
98
+ '&::-webkit-outer-spin-button, &::-webkit-inner-spin-button': {
99
+ display: 'none',
100
+ },
101
+ },
102
+ }))
103
+
104
+ export const SlippageClearButton: React.FC<
105
+ React.ComponentProps<typeof IconButton>
106
+ > = styled(IconButton)(({ theme }) => ({
107
+ padding: theme.spacing(0.5),
108
+ flexShrink: 0,
109
+ }))