@lifi/widget 3.26.0 → 3.26.2-beta.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 (105) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js +2 -1
  3. package/dist/esm/components/BaseTransactionButton/BaseTransactionButton.js.map +1 -1
  4. package/dist/esm/components/BottomSheet/BottomSheet.js +13 -5
  5. package/dist/esm/components/BottomSheet/BottomSheet.js.map +1 -1
  6. package/dist/esm/components/Chains/ChainsExpanded.d.ts +1 -1
  7. package/dist/esm/components/Chains/ChainsExpanded.js +3 -2
  8. package/dist/esm/components/Chains/ChainsExpanded.js.map +1 -1
  9. package/dist/esm/components/Chains/ChainsExpanded.style.js +1 -2
  10. package/dist/esm/components/Chains/ChainsExpanded.style.js.map +1 -1
  11. package/dist/esm/components/Chains/SelectChainContent.d.ts +1 -1
  12. package/dist/esm/components/Chains/SelectChainContent.js +3 -2
  13. package/dist/esm/components/Chains/SelectChainContent.js.map +1 -1
  14. package/dist/esm/components/Expansion/Expansion.js +5 -16
  15. package/dist/esm/components/Expansion/Expansion.js.map +1 -1
  16. package/dist/esm/components/Expansion/Expansion.style.d.ts +32 -0
  17. package/dist/esm/components/Expansion/Expansion.style.js +40 -0
  18. package/dist/esm/components/Expansion/Expansion.style.js.map +1 -0
  19. package/dist/esm/components/Expansion/ExpansionTransition.d.ts +0 -1
  20. package/dist/esm/components/Expansion/ExpansionTransition.js +1 -30
  21. package/dist/esm/components/Expansion/ExpansionTransition.js.map +1 -1
  22. package/dist/esm/components/Header/CloseDrawerButton.js +2 -5
  23. package/dist/esm/components/Header/CloseDrawerButton.js.map +1 -1
  24. package/dist/esm/components/Header/Header.style.d.ts +1 -2
  25. package/dist/esm/components/Header/Header.style.js +3 -4
  26. package/dist/esm/components/Header/Header.style.js.map +1 -1
  27. package/dist/esm/components/Header/NavigationHeader.js +17 -21
  28. package/dist/esm/components/Header/NavigationHeader.js.map +1 -1
  29. package/dist/esm/components/Header/SplitNavigationTabs.d.ts +1 -0
  30. package/dist/esm/components/Header/{NavigationTabs.js → SplitNavigationTabs.js} +4 -5
  31. package/dist/esm/components/Header/SplitNavigationTabs.js.map +1 -0
  32. package/dist/esm/components/Header/WalletHeader.d.ts +0 -1
  33. package/dist/esm/components/Header/WalletHeader.js +10 -14
  34. package/dist/esm/components/Header/WalletHeader.js.map +1 -1
  35. package/dist/esm/components/Routes/RoutesContent.d.ts +2 -3
  36. package/dist/esm/components/Routes/RoutesContent.js +5 -21
  37. package/dist/esm/components/Routes/RoutesContent.js.map +1 -1
  38. package/dist/esm/components/Routes/RoutesExpanded.d.ts +2 -3
  39. package/dist/esm/components/Routes/RoutesExpanded.js +24 -8
  40. package/dist/esm/components/Routes/RoutesExpanded.js.map +1 -1
  41. package/dist/esm/components/Routes/RoutesExpanded.style.js +1 -2
  42. package/dist/esm/components/Routes/RoutesExpanded.style.js.map +1 -1
  43. package/dist/esm/components/Step/StepProcess.js +8 -3
  44. package/dist/esm/components/Step/StepProcess.js.map +1 -1
  45. package/dist/esm/components/Tabs/NavigationTabs.d.ts +4 -0
  46. package/dist/esm/components/Tabs/NavigationTabs.js +39 -0
  47. package/dist/esm/components/Tabs/NavigationTabs.js.map +1 -0
  48. package/dist/esm/components/TokenList/TokenListItem.js +1 -0
  49. package/dist/esm/components/TokenList/TokenListItem.js.map +1 -1
  50. package/dist/esm/components/TokenList/useTokenSelect.js +9 -2
  51. package/dist/esm/components/TokenList/useTokenSelect.js.map +1 -1
  52. package/dist/esm/config/version.d.ts +1 -1
  53. package/dist/esm/config/version.js +1 -1
  54. package/dist/esm/config/version.js.map +1 -1
  55. package/dist/esm/hooks/useRoutes.js +32 -4
  56. package/dist/esm/hooks/useRoutes.js.map +1 -1
  57. package/dist/esm/pages/MainPage/MainPage.js +8 -1
  58. package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
  59. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +1 -1
  60. package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
  61. package/dist/esm/themes/azureLight.js +2 -3
  62. package/dist/esm/themes/azureLight.js.map +1 -1
  63. package/dist/esm/themes/createTheme.js +6 -0
  64. package/dist/esm/themes/createTheme.js.map +1 -1
  65. package/dist/esm/themes/types.d.ts +15 -1
  66. package/dist/esm/themes/watermelonLight.js +12 -0
  67. package/dist/esm/themes/watermelonLight.js.map +1 -1
  68. package/dist/esm/themes/windows95.js +11 -0
  69. package/dist/esm/themes/windows95.js.map +1 -1
  70. package/dist/esm/types/widget.d.ts +1 -1
  71. package/dist/esm/types/widget.js.map +1 -1
  72. package/package.json +7 -7
  73. package/package.json.tmp +6 -6
  74. package/src/components/BaseTransactionButton/BaseTransactionButton.tsx +2 -1
  75. package/src/components/BottomSheet/BottomSheet.tsx +14 -3
  76. package/src/components/Chains/ChainsExpanded.style.tsx +1 -2
  77. package/src/components/Chains/ChainsExpanded.tsx +6 -2
  78. package/src/components/Chains/SelectChainContent.tsx +81 -82
  79. package/src/components/Expansion/Expansion.style.tsx +43 -0
  80. package/src/components/Expansion/Expansion.tsx +8 -23
  81. package/src/components/Expansion/ExpansionTransition.tsx +5 -33
  82. package/src/components/Header/CloseDrawerButton.tsx +2 -5
  83. package/src/components/Header/Header.style.ts +3 -5
  84. package/src/components/Header/NavigationHeader.tsx +66 -73
  85. package/src/components/Header/{NavigationTabs.tsx → SplitNavigationTabs.tsx} +10 -14
  86. package/src/components/Header/WalletHeader.tsx +13 -19
  87. package/src/components/Routes/RoutesContent.tsx +8 -29
  88. package/src/components/Routes/RoutesExpanded.style.ts +1 -2
  89. package/src/components/Routes/RoutesExpanded.tsx +33 -13
  90. package/src/components/Step/StepProcess.tsx +8 -3
  91. package/src/components/Tabs/NavigationTabs.tsx +40 -0
  92. package/src/components/TokenList/TokenListItem.tsx +1 -0
  93. package/src/components/TokenList/useTokenSelect.ts +14 -2
  94. package/src/config/version.ts +1 -1
  95. package/src/hooks/useRoutes.ts +68 -33
  96. package/src/pages/MainPage/MainPage.tsx +9 -1
  97. package/src/pages/SettingsPage/RoutePrioritySettings.tsx +0 -1
  98. package/src/themes/azureLight.ts +2 -3
  99. package/src/themes/createTheme.ts +6 -0
  100. package/src/themes/types.ts +20 -0
  101. package/src/themes/watermelonLight.ts +12 -0
  102. package/src/themes/windows95.ts +12 -0
  103. package/src/types/widget.ts +2 -0
  104. package/dist/esm/components/Header/NavigationTabs.d.ts +0 -1
  105. package/dist/esm/components/Header/NavigationTabs.js.map +0 -1
@@ -27,10 +27,9 @@ import { WalletMenu } from './WalletMenu.js'
27
27
  import { WalletMenuContainer } from './WalletMenu.style.js'
28
28
 
29
29
  const useInternalWalletManagement = () => {
30
- const { subvariant, hiddenUI } = useWidgetConfig()
30
+ const { hiddenUI } = useWidgetConfig()
31
31
  const { useExternalWalletProvidersOnly } = useExternalWalletProvider()
32
32
 
33
- const isSplitVariant = subvariant === 'split'
34
33
  const isWalletMenuHidden = hiddenUI?.includes(HiddenUI.WalletMenu)
35
34
 
36
35
  const shouldShowWalletMenu =
@@ -38,25 +37,19 @@ const useInternalWalletManagement = () => {
38
37
 
39
38
  return {
40
39
  shouldShowWalletMenu,
41
- isSplitVariant,
42
40
  }
43
41
  }
44
42
 
45
43
  export const WalletHeader: React.FC = () => {
46
- const { shouldShowWalletMenu, isSplitVariant } = useInternalWalletManagement()
44
+ const { shouldShowWalletMenu } = useInternalWalletManagement()
47
45
 
48
- return shouldShowWalletMenu && !isSplitVariant ? (
46
+ return shouldShowWalletMenu ? (
49
47
  <HeaderAppBar elevation={0} sx={{ justifyContent: 'flex-end' }}>
50
48
  <WalletMenuButton />
51
49
  </HeaderAppBar>
52
50
  ) : null
53
51
  }
54
52
 
55
- export const SplitWalletMenuButton: React.FC = () => {
56
- const { shouldShowWalletMenu, isSplitVariant } = useInternalWalletManagement()
57
- return shouldShowWalletMenu && isSplitVariant ? <WalletMenuButton /> : null
58
- }
59
-
60
53
  export const WalletMenuButton: React.FC = () => {
61
54
  const { variant, hiddenUI } = useWidgetConfig()
62
55
  const { account, accounts } = useAccount()
@@ -92,24 +85,25 @@ export const WalletMenuButton: React.FC = () => {
92
85
 
93
86
  const ConnectButton = () => {
94
87
  const { t } = useTranslation()
95
- const { walletConfig, subvariant, variant } = useWidgetConfig()
88
+ const { walletConfig, variant } = useWidgetConfig()
96
89
  const { openWalletMenu } = useWalletMenu()
97
- const connect = async () => {
90
+ const connect = async (event: React.MouseEvent<HTMLElement>) => {
98
91
  if (!walletConfig?.usePartialWalletManagement && walletConfig?.onConnect) {
99
92
  walletConfig.onConnect()
100
93
  return
101
94
  }
102
95
  openWalletMenu()
96
+ event.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer
103
97
  }
104
98
 
99
+ const walletPosition = variant === 'drawer' ? 'start' : 'end'
100
+
105
101
  return (
106
102
  <WalletButton
107
- subvariant={subvariant}
108
- endIcon={
109
- variant !== 'drawer' && subvariant !== 'split' ? <Wallet /> : undefined
110
- }
103
+ withOffset={walletPosition === 'end'}
104
+ endIcon={walletPosition === 'end' ? <Wallet /> : undefined}
111
105
  startIcon={
112
- variant === 'drawer' || subvariant === 'split' ? (
106
+ walletPosition === 'start' ? (
113
107
  <Wallet sx={{ marginLeft: -0.25 }} />
114
108
  ) : undefined
115
109
  }
@@ -121,13 +115,13 @@ const ConnectButton = () => {
121
115
  }
122
116
 
123
117
  const ConnectedButton = ({ account }: { account: Account }) => {
124
- const { subvariant } = useWidgetConfig()
125
118
  const { chain } = useChain(account.chainId)
126
119
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
127
120
  const walletAddress = shortenAddress(account.address)
128
121
 
129
122
  const handleClick = (event: React.MouseEvent<HTMLElement>) => {
130
123
  setAnchorEl(event.currentTarget)
124
+ event.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer
131
125
  }
132
126
 
133
127
  const handleClose = () => {
@@ -137,7 +131,7 @@ const ConnectedButton = ({ account }: { account: Account }) => {
137
131
  return (
138
132
  <>
139
133
  <WalletButton
140
- subvariant={subvariant}
134
+ withOffset
141
135
  endIcon={<ExpandMore />}
142
136
  startIcon={
143
137
  chain?.logoURI ? (
@@ -1,15 +1,11 @@
1
1
  import type { ExtendedChain, Route } from '@lifi/sdk'
2
2
  import { useAccount } from '@lifi/wallet-management'
3
3
  import { Stack, Typography } from '@mui/material'
4
- import { useEffect } from 'react'
4
+ import { memo } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
- import { useNavigate } from 'react-router-dom'
7
6
  import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
8
- import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
9
7
  import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
10
8
  import { useFieldValues } from '../../stores/form/useFieldValues.js'
11
- import { WidgetEvent } from '../../types/events.js'
12
- import { navigationRoutes } from '../../utils/navigationRoutes.js'
13
9
  import { PageContainer } from '../PageContainer.js'
14
10
  import { ProgressToNextUpdate } from '../ProgressToNextUpdate.js'
15
11
  import { RouteCard } from '../RouteCard/RouteCard.js'
@@ -21,55 +17,38 @@ interface RoutesContentProps {
21
17
  routes: Route[]
22
18
  isFetching: boolean
23
19
  isLoading: boolean
24
- expanded: boolean
25
- setReviewableRoute: (route: Route) => void
26
20
  dataUpdatedAt: number
27
21
  refetchTime: number
28
22
  fromChain: ExtendedChain | undefined
29
23
  refetch: () => void
24
+ onRouteClick: (route: Route) => void
30
25
  }
31
26
 
32
27
  const headerHeight = '64px'
33
28
 
34
- export const RoutesContent = ({
29
+ export const RoutesContent = memo(function RoutesContent({
35
30
  routes,
36
31
  isFetching,
37
32
  isLoading,
38
- expanded,
39
- setReviewableRoute,
40
33
  dataUpdatedAt,
41
34
  refetchTime,
42
35
  fromChain,
43
36
  refetch,
44
- }: RoutesContentProps) => {
37
+ onRouteClick,
38
+ }: RoutesContentProps) {
45
39
  const { t } = useTranslation()
46
-
47
- const navigate = useNavigate()
48
40
  const { subvariant, subvariantOptions } = useWidgetConfig()
49
41
 
50
42
  const { account } = useAccount({ chainType: fromChain?.chainType })
51
43
  const [toAddress] = useFieldValues('toAddress')
52
44
  const { requiredToAddress } = useToAddressRequirements()
53
45
 
54
- const emitter = useWidgetEvents()
55
-
56
- const handleRouteClick = (route: Route) => {
57
- setReviewableRoute(route)
58
- navigate(navigationRoutes.transactionExecution, {
59
- state: { routeId: route.id },
60
- })
61
- emitter.emit(WidgetEvent.RouteSelected, { route, routes: routes! })
62
- }
63
46
  const currentRoute = routes?.[0]
64
47
 
65
- const routeNotFound = !currentRoute && !isLoading && !isFetching && expanded
48
+ const routeNotFound = !currentRoute && !isLoading && !isFetching
66
49
  const toAddressUnsatisfied = currentRoute && requiredToAddress && !toAddress
67
50
  const allowInteraction = account.isConnected && !toAddressUnsatisfied
68
51
 
69
- useEffect(() => {
70
- emitter.emit(WidgetEvent.WidgetExpanded, expanded)
71
- }, [emitter, expanded])
72
-
73
52
  const title =
74
53
  subvariant === 'custom'
75
54
  ? subvariantOptions?.custom === 'deposit'
@@ -121,7 +100,7 @@ export const RoutesContent = ({
121
100
  key={index}
122
101
  route={route}
123
102
  onClick={
124
- allowInteraction ? () => handleRouteClick(route) : undefined
103
+ allowInteraction ? () => onRouteClick(route) : undefined
125
104
  }
126
105
  active={index === 0}
127
106
  expanded={routes?.length === 1}
@@ -132,4 +111,4 @@ export const RoutesContent = ({
132
111
  </PageContainer>
133
112
  </Container>
134
113
  )
135
- }
114
+ })
@@ -11,14 +11,13 @@ interface ContainerProps extends ScopedCssBaselineProps {
11
11
  export const Container = styled(ScopedCssBaseline, {
12
12
  shouldForwardProp: (prop) => !['minimumHeight'].includes(prop as string),
13
13
  })<ContainerProps>(({ theme, minimumHeight }) => ({
14
+ ...theme.container,
14
15
  backgroundColor: theme.vars.palette.background.default,
15
16
  overflow: 'hidden',
16
17
  width: routesExpansionWidth,
17
18
  display: 'flex',
18
19
  flexDirection: 'column',
19
20
  whiteSpace: 'normal',
20
- borderRadius: theme.container?.borderRadius ?? 0,
21
- boxShadow: theme.container?.boxShadow ?? 'none',
22
21
  ...(theme.container?.display !== 'flex'
23
22
  ? {
24
23
  maxHeight:
@@ -1,23 +1,28 @@
1
1
  import type { Route } from '@lifi/sdk'
2
- import { useEffect, useRef } from 'react'
2
+ import { memo, useCallback, useEffect, useLayoutEffect, useRef } from 'react'
3
+ import { useNavigate } from 'react-router-dom'
3
4
  import { useRoutes } from '../../hooks/useRoutes.js'
4
- import { ExpansionType } from '../../types/widget.js'
5
+ import { useWidgetEvents } from '../../hooks/useWidgetEvents.js'
6
+ import { WidgetEvent } from '../../types/events.js'
7
+ import { navigationRoutes } from '../../utils/navigationRoutes.js'
5
8
  import { ExpansionTransition } from '../Expansion/ExpansionTransition.js'
6
9
  import { RoutesContent } from './RoutesContent.js'
7
10
  import { routesExpansionWidth } from './RoutesExpanded.style.js'
8
11
 
9
12
  interface RoutesExpandedProps {
10
- expansionType: ExpansionType
13
+ canOpen: boolean
11
14
  setOpenExpansion: (open: boolean) => void
12
15
  }
13
16
 
14
- export const RoutesExpanded = ({
15
- expansionType,
17
+ export const RoutesExpanded = memo(function RoutesExpanded({
18
+ canOpen,
16
19
  setOpenExpansion,
17
- }: RoutesExpandedProps) => {
20
+ }: RoutesExpandedProps) {
21
+ const emitter = useWidgetEvents()
22
+ const navigate = useNavigate()
18
23
  const routesRef = useRef<Route[]>(undefined)
19
-
20
24
  const routesActiveRef = useRef(false)
25
+
21
26
  const {
22
27
  routes,
23
28
  isLoading,
@@ -45,13 +50,29 @@ export const RoutesExpanded = ({
45
50
 
46
51
  const expanded =
47
52
  Boolean(routesActiveRef.current || isLoading || isFetching || isFetched) &&
48
- expansionType === ExpansionType.Routes
53
+ canOpen
49
54
 
50
- useEffect(() => {
51
- // To update parent's width when expansion changes
55
+ // biome-ignore lint/correctness/useExhaustiveDependencies: stable navigation callback that won't cause rerenders in RoutesContent
56
+ const onRouteClick = useCallback(
57
+ (route: Route) => {
58
+ setReviewableRoute(route)
59
+ navigate(navigationRoutes.transactionExecution, {
60
+ state: { routeId: route.id },
61
+ })
62
+ emitter.emit(WidgetEvent.RouteSelected, { route, routes: routes! })
63
+ },
64
+ [emitter, routes, setReviewableRoute]
65
+ )
66
+
67
+ // Use layout effect to update parent's width when expansion changes
68
+ useLayoutEffect(() => {
52
69
  setOpenExpansion(expanded)
53
70
  }, [expanded, setOpenExpansion])
54
71
 
72
+ useEffect(() => {
73
+ emitter.emit(WidgetEvent.WidgetExpanded, expanded)
74
+ }, [emitter, expanded])
75
+
55
76
  return (
56
77
  <ExpansionTransition
57
78
  in={expanded}
@@ -62,13 +83,12 @@ export const RoutesExpanded = ({
62
83
  routes={routesRef.current || []}
63
84
  isFetching={isFetching}
64
85
  isLoading={isLoading}
65
- expanded={expanded}
66
- setReviewableRoute={setReviewableRoute}
67
86
  dataUpdatedAt={dataUpdatedAt}
68
87
  refetchTime={refetchTime}
69
88
  fromChain={fromChain}
70
89
  refetch={refetch}
90
+ onRouteClick={onRouteClick}
71
91
  />
72
92
  </ExpansionTransition>
73
93
  )
74
- }
94
+ })
@@ -13,12 +13,17 @@ export const StepProcess: React.FC<{
13
13
  const { title, message } = useProcessMessage(step, process)
14
14
  const { getTransactionLink } = useExplorer()
15
15
 
16
- const transactionLink = process.txLink
16
+ const transactionLink = process.txHash
17
17
  ? getTransactionLink({
18
- txLink: process.txLink,
18
+ txHash: process.txHash,
19
19
  chain: process.chainId,
20
20
  })
21
- : undefined
21
+ : process.txLink
22
+ ? getTransactionLink({
23
+ txLink: process.txLink,
24
+ chain: process.chainId,
25
+ })
26
+ : undefined
22
27
 
23
28
  return (
24
29
  <Box
@@ -0,0 +1,40 @@
1
+ import { styled, Tabs, tabsClasses } from '@mui/material'
2
+ import { Tab } from './Tabs.style.js'
3
+
4
+ export const NavigationTabs = styled(Tabs, {
5
+ name: 'MuiNavigationTabs',
6
+ slot: 'root',
7
+ })(({ theme }) => ({
8
+ overflow: 'visible', // Prevent shadows from being cut off
9
+ width: 'fit-content',
10
+ minHeight: theme.spacing(5),
11
+ maxHeight: theme.spacing(5),
12
+ background: 'transparent',
13
+ ...theme.applyStyles('dark', {
14
+ backgroundColor: 'transparent',
15
+ }),
16
+ [`& .${tabsClasses.scroller}`]: {
17
+ padding: 0,
18
+ overflow: 'visible !important', // Enforce since overflow is set dynamically
19
+ },
20
+ [`& .${tabsClasses.indicator}`]: {
21
+ boxShadow: 'none',
22
+ top: 0,
23
+ left: 0,
24
+ height: '100%',
25
+ width: '100%',
26
+ borderRadius: theme.vars.shape.borderRadius,
27
+ backgroundColor: `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.04)`,
28
+ ...theme.applyStyles('dark', {
29
+ backgroundColor: `rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.08)`,
30
+ }),
31
+ },
32
+ }))
33
+
34
+ export const NavigationTab = styled(Tab, {
35
+ name: 'MuiNavigationTab',
36
+ slot: 'root',
37
+ })(({ theme }) => ({
38
+ minHeight: theme.spacing(5),
39
+ maxHeight: theme.spacing(5),
40
+ }))
@@ -102,6 +102,7 @@ const OpenTokenDetailsButton = ({
102
102
  size="small"
103
103
  onClick={(e) => {
104
104
  e.stopPropagation()
105
+ e.currentTarget.blur() // Remove focus to prevent accessibility issues when opening drawer
105
106
  onClick(tokenAddress, withoutContractAddress)
106
107
  }}
107
108
  >
@@ -6,6 +6,7 @@ import { useChainOrderStoreContext } from '../../stores/chains/ChainOrderStore.j
6
6
  import type { FormType } from '../../stores/form/types.js'
7
7
  import { FormKeyHelper } from '../../stores/form/types.js'
8
8
  import { useFieldActions } from '../../stores/form/useFieldActions.js'
9
+ import { useSplitSubvariantStore } from '../../stores/settings/useSplitSubvariantStore.js'
9
10
  import { WidgetEvent } from '../../types/events.js'
10
11
  import type { DisabledUI } from '../../types/widget.js'
11
12
 
@@ -16,6 +17,7 @@ export type UseTokenSelectArgs = {
16
17
 
17
18
  export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
18
19
  const { subvariant, disabledUI } = useWidgetConfig()
20
+ const splitSubvariant = useSplitSubvariantStore((store) => store.state)
19
21
  const emitter = useWidgetEvents()
20
22
  const { setFieldValue, getFieldValues } = useFieldActions()
21
23
  const autoPopulateToAddress = useToAddressAutoPopulate()
@@ -47,10 +49,19 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
47
49
  FormKeyHelper.getChainKey(oppositeFormType),
48
50
  'toAddress'
49
51
  )
52
+
50
53
  // TODO: remove when we enable same chain/token transfers
51
- if (
54
+ const isSameTokenTransfer =
52
55
  selectedOppositeTokenAddress === tokenAddress &&
53
- selectedOppositeChainId === selectedChainId &&
56
+ selectedOppositeChainId === selectedChainId
57
+
58
+ const isBridgeToSameChain =
59
+ subvariant === 'split' &&
60
+ splitSubvariant === 'bridge' &&
61
+ selectedOppositeChainId === selectedChainId
62
+
63
+ if (
64
+ (isSameTokenTransfer || isBridgeToSameChain) &&
54
65
  subvariant !== 'custom'
55
66
  ) {
56
67
  setFieldValue(FormKeyHelper.getTokenKey(oppositeFormType), '', {
@@ -106,6 +117,7 @@ export const useTokenSelect = (formType: FormType, onClick?: () => void) => {
106
117
  onClick,
107
118
  setFieldValue,
108
119
  subvariant,
120
+ splitSubvariant,
109
121
  tokenKey,
110
122
  ]
111
123
  )
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/widget'
2
- export const version = '3.26.0'
2
+ export const version = '3.26.2-beta.0'
@@ -10,6 +10,7 @@ import {
10
10
  } from '@lifi/sdk'
11
11
  import { useAccount } from '@lifi/wallet-management'
12
12
  import { useQuery, useQueryClient } from '@tanstack/react-query'
13
+ import { useCallback, useMemo } from 'react'
13
14
  import { parseUnits } from 'viem'
14
15
  import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
15
16
  import { useFieldValues } from '../stores/form/useFieldValues.js'
@@ -137,32 +138,63 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
137
138
  !isBatchingSupportedLoading
138
139
 
139
140
  // Some values should be strictly typed and isEnabled ensures that
140
- const queryKey = [
141
- getQueryKey('routes', keyPrefix),
142
- account.address,
143
- fromChain?.id as number,
144
- fromToken?.address as string,
145
- fromTokenAmount,
146
- toWalletAddress,
147
- toChain?.id as number,
148
- toToken?.address as string,
149
- toTokenAmount,
150
- contractCalls,
151
- slippage,
152
- swapOnly,
153
- disabledBridges,
154
- disabledExchanges,
155
- allowedBridges,
156
- allowedExchanges,
157
- routePriority,
158
- subvariant,
159
- allowSwitchChain,
160
- enabledRefuel && enabledAutoRefuel,
161
- gasRecommendationFromAmount,
162
- feeConfig?.fee || fee,
163
- !!isBatchingSupported,
164
- observableRoute?.id,
165
- ] as const
141
+ const queryKey = useMemo(
142
+ () =>
143
+ [
144
+ getQueryKey('routes', keyPrefix),
145
+ account.address,
146
+ fromChain?.id as number,
147
+ fromToken?.address as string,
148
+ fromTokenAmount,
149
+ toWalletAddress,
150
+ toChain?.id as number,
151
+ toToken?.address as string,
152
+ toTokenAmount,
153
+ contractCalls,
154
+ slippage,
155
+ swapOnly,
156
+ disabledBridges,
157
+ disabledExchanges,
158
+ allowedBridges,
159
+ allowedExchanges,
160
+ routePriority,
161
+ subvariant,
162
+ allowSwitchChain,
163
+ enabledRefuel && enabledAutoRefuel,
164
+ gasRecommendationFromAmount,
165
+ feeConfig?.fee || fee,
166
+ !!isBatchingSupported,
167
+ observableRoute?.id,
168
+ ] as const,
169
+ [
170
+ keyPrefix,
171
+ account.address,
172
+ fromChain?.id,
173
+ fromToken?.address,
174
+ fromTokenAmount,
175
+ toWalletAddress,
176
+ toChain?.id,
177
+ toToken?.address,
178
+ toTokenAmount,
179
+ contractCalls,
180
+ slippage,
181
+ swapOnly,
182
+ disabledBridges,
183
+ disabledExchanges,
184
+ allowedBridges,
185
+ allowedExchanges,
186
+ routePriority,
187
+ subvariant,
188
+ allowSwitchChain,
189
+ enabledRefuel,
190
+ enabledAutoRefuel,
191
+ gasRecommendationFromAmount,
192
+ feeConfig?.fee,
193
+ fee,
194
+ isBatchingSupported,
195
+ observableRoute?.id,
196
+ ]
197
+ )
166
198
 
167
199
  const { getIntermediateRoutes, setIntermediateRoutes } =
168
200
  useIntermediateRoutesStore()
@@ -496,13 +528,16 @@ export const useRoutes = ({ observableRoute }: RoutesProps = {}) => {
496
528
  },
497
529
  })
498
530
 
499
- const setReviewableRoute = (route: Route) => {
500
- const queryDataKey = queryKey.toSpliced(queryKey.length - 1, 1, route.id)
501
- queryClient.setQueryData(queryDataKey, [route], {
502
- updatedAt: dataUpdatedAt || Date.now(),
503
- })
504
- setExecutableRoute(route)
505
- }
531
+ const setReviewableRoute = useCallback(
532
+ (route: Route) => {
533
+ const queryDataKey = queryKey.toSpliced(queryKey.length - 1, 1, route.id)
534
+ queryClient.setQueryData(queryDataKey, [route], {
535
+ updatedAt: dataUpdatedAt || Date.now(),
536
+ })
537
+ setExecutableRoute(route)
538
+ },
539
+ [queryClient, dataUpdatedAt, setExecutableRoute, queryKey]
540
+ )
506
541
 
507
542
  return {
508
543
  routes: data || getIntermediateRoutes(queryKey),
@@ -25,12 +25,20 @@ export const MainPage: React.FC = () => {
25
25
  const custom = subvariant === 'custom'
26
26
  const showPoweredBy = !hiddenUI?.includes(HiddenUI.PoweredBy)
27
27
 
28
+ const splitTitle =
29
+ subvariantOptions?.split === 'bridge'
30
+ ? t('header.bridge')
31
+ : subvariantOptions?.split === 'swap'
32
+ ? t('header.swap')
33
+ : undefined
28
34
  const title =
29
35
  subvariant === 'custom'
30
36
  ? t(`header.${subvariantOptions?.custom ?? 'checkout'}`)
31
37
  : subvariant === 'refuel'
32
38
  ? t('header.gas')
33
- : t('header.exchange')
39
+ : subvariant === 'split' && splitTitle
40
+ ? splitTitle
41
+ : t('header.exchange')
34
42
 
35
43
  useHeader(title)
36
44
 
@@ -39,7 +39,6 @@ export const RoutePrioritySettings: React.FC = () => {
39
39
  aria-label="tabs"
40
40
  indicatorColor="primary"
41
41
  onChange={handleRoutePriorityChange}
42
- orientation="vertical"
43
42
  sx={{ mt: 1.5 }}
44
43
  >
45
44
  {Priorities.map((priority) => {
@@ -48,12 +48,11 @@ export const azureLightTheme: WidgetTheme = {
48
48
  defaultProps: { variant: 'filled' },
49
49
  },
50
50
  // Used only for 'split' subvariant and can be safely removed if not used
51
- MuiTabs: {
51
+ MuiNavigationTabs: {
52
52
  styleOverrides: {
53
53
  root: {
54
- backgroundColor: '#f8f8fa',
55
54
  [`.${tabsClasses.indicator}`]: {
56
- backgroundColor: '#ffffff',
55
+ backgroundColor: '#f8f8fa',
57
56
  },
58
57
  },
59
58
  },
@@ -469,6 +469,12 @@ export const createTheme = (widgetTheme: WidgetTheme = {}) => {
469
469
  },
470
470
  },
471
471
  },
472
+ MuiNavigationTabs: {
473
+ ...widgetTheme.components?.MuiNavigationTabs,
474
+ },
475
+ MuiNavigationTab: {
476
+ ...widgetTheme.components?.MuiNavigationTab,
477
+ },
472
478
  },
473
479
  })
474
480
 
@@ -3,6 +3,8 @@ import type {
3
3
  ComponentsOverrides,
4
4
  ComponentsVariants,
5
5
  SimplePaletteColorOptions,
6
+ TabProps,
7
+ TabsProps,
6
8
  } from '@mui/material'
7
9
  import type {} from '@mui/material/themeCssVarsAugmentation'
8
10
  import type { CSSProperties } from 'react'
@@ -42,9 +44,13 @@ declare module '@mui/material/styles' {
42
44
  }
43
45
  interface ComponentNameToClassKey {
44
46
  MuiInputCard: 'root'
47
+ MuiNavigationTabs: 'root'
48
+ MuiNavigationTab: 'root'
45
49
  }
46
50
  interface ComponentsPropsList {
47
51
  MuiInputCard: Partial<CardProps>
52
+ MuiNavigationTabs: Partial<TabsProps>
53
+ MuiNavigationTab: Partial<TabProps>
48
54
  }
49
55
  interface Components {
50
56
  MuiInputCard?: {
@@ -54,6 +60,20 @@ declare module '@mui/material/styles' {
54
60
  >['MuiInputCard']
55
61
  variants?: ComponentsVariants['MuiInputCard']
56
62
  }
63
+ MuiNavigationTabs?: {
64
+ defaultProps?: ComponentsPropsList['MuiNavigationTabs']
65
+ styleOverrides?: ComponentsOverrides<
66
+ Omit<Theme, 'components'>
67
+ >['MuiNavigationTabs']
68
+ variants?: ComponentsVariants['MuiNavigationTabs']
69
+ }
70
+ MuiNavigationTab?: {
71
+ defaultProps?: ComponentsPropsList['MuiNavigationTab']
72
+ styleOverrides?: ComponentsOverrides<
73
+ Omit<Theme, 'components'>
74
+ >['MuiNavigationTab']
75
+ variants?: ComponentsVariants['MuiNavigationTab']
76
+ }
57
77
  }
58
78
  interface Palette {
59
79
  playground: Palette['primary']
@@ -1,3 +1,4 @@
1
+ import { tabsClasses } from '@mui/material'
1
2
  import type { WidgetTheme } from '../types/widget.js'
2
3
 
3
4
  export const watermelonLightTheme: WidgetTheme = {
@@ -46,6 +47,17 @@ export const watermelonLightTheme: WidgetTheme = {
46
47
  MuiCard: {
47
48
  defaultProps: { variant: 'elevation' },
48
49
  },
50
+ MuiNavigationTabs: {
51
+ styleOverrides: {
52
+ root: ({ theme }) => ({
53
+ [`.${tabsClasses.indicator}`]: {
54
+ backgroundColor: '#ffffff',
55
+ filter: `drop-shadow(0 1px 4px rgba(${theme.vars.palette.common.onBackgroundChannel} / 0.08))`,
56
+ borderRadius: '16px',
57
+ },
58
+ }),
59
+ },
60
+ },
49
61
  // MuiIconButton: {
50
62
  // styleOverrides: {
51
63
  // root: ({ theme }) => ({