@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.
- package/dist/esm/AppDefault.js +14 -0
- package/dist/esm/AppDefault.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInput.js +2 -1
- package/dist/esm/components/AmountInput/AmountInput.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js +4 -2
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.js.map +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js +1 -1
- package/dist/esm/components/AmountInput/AmountInputEndAdornment.style.js.map +1 -1
- package/dist/esm/components/AmountInput/PriceFormHelperText.js +4 -2
- package/dist/esm/components/AmountInput/PriceFormHelperText.js.map +1 -1
- package/dist/esm/components/AmountInputCard/AmountInputCard.style.js +98 -0
- package/dist/esm/components/AmountInputCard/AmountInputCard.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js +25 -0
- package/dist/esm/components/AmountInputCard/AmountInputCardPair.js.map +1 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.js +35 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.js.map +1 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js +11 -0
- package/dist/esm/components/AmountInputCard/BalanceDisplay.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/FiatValueToggle.js +41 -0
- package/dist/esm/components/AmountInputCard/FiatValueToggle.js.map +1 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.js +80 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.js.map +1 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js +43 -0
- package/dist/esm/components/AmountInputCard/PercentageChips.style.js.map +1 -0
- package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js +137 -0
- package/dist/esm/components/AmountInputCard/ReceiveAmountCard.js.map +1 -0
- package/dist/esm/components/AmountInputCard/SendAmountCard.js +102 -0
- package/dist/esm/components/AmountInputCard/SendAmountCard.js.map +1 -0
- package/dist/esm/components/Avatar/Avatar.js +12 -3
- package/dist/esm/components/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js +2 -1
- package/dist/esm/components/Chains/VirtualizedChainList.js.map +1 -1
- package/dist/esm/components/Header/HeaderNavigationTabs.js +14 -1
- package/dist/esm/components/Header/HeaderNavigationTabs.js.map +1 -1
- package/dist/esm/components/IconCircle/IconCircle.style.js +1 -1
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js +99 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.js.map +1 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js +52 -0
- package/dist/esm/components/LimitOrderSettings/LimitOrderSettings.style.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.js +115 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js +89 -0
- package/dist/esm/components/LimitPriceCard/LimitPriceCard.style.js.map +1 -0
- package/dist/esm/components/LimitPriceCard/LimitPricePresets.js +91 -0
- package/dist/esm/components/LimitPriceCard/LimitPricePresets.js.map +1 -0
- package/dist/esm/components/RouteCard/RouteProviderCard.js +118 -0
- package/dist/esm/components/RouteCard/RouteProviderCard.js.map +1 -0
- package/dist/esm/components/Routes/LimitOrderRoutes.js +34 -0
- package/dist/esm/components/Routes/LimitOrderRoutes.js.map +1 -0
- package/dist/esm/components/Routes/Routes.js +2 -2
- package/dist/esm/components/Routes/Routes.js.map +1 -1
- package/dist/esm/components/SettingsPanel/SettingsPanel.js +51 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.js.map +1 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.style.js +33 -0
- package/dist/esm/components/SettingsPanel/SettingsPanel.style.js.map +1 -0
- package/dist/esm/components/SettingsPanel/utils.js +14 -0
- package/dist/esm/components/SettingsPanel/utils.js.map +1 -0
- package/dist/esm/components/SwapButton/SwapButton.js +50 -0
- package/dist/esm/components/SwapButton/SwapButton.js.map +1 -0
- package/dist/esm/components/SwapButton/SwapButton.style.js +24 -0
- package/dist/esm/components/SwapButton/SwapButton.style.js.map +1 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.js +52 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.js.map +1 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.style.js +39 -0
- package/dist/esm/components/TokenPillButton/TokenPillButton.style.js.map +1 -0
- 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/useChainSearch.js +2 -1
- package/dist/esm/hooks/useChainSearch.js.map +1 -1
- package/dist/esm/hooks/useLimitMarketRate.js +24 -0
- package/dist/esm/hooks/useLimitMarketRate.js.map +1 -0
- package/dist/esm/hooks/useLinkedLimitFields.js +49 -0
- package/dist/esm/hooks/useLinkedLimitFields.js.map +1 -0
- package/dist/esm/hooks/useRoutes.js +23 -6
- package/dist/esm/hooks/useRoutes.js.map +1 -1
- package/dist/esm/hooks/useTokens.js +16 -14
- package/dist/esm/hooks/useTokens.js.map +1 -1
- package/dist/esm/hooks/useWideVariant.js +5 -2
- package/dist/esm/hooks/useWideVariant.js.map +1 -1
- package/dist/esm/i18n/en.json +22 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/pages/MainPage/LimitPageContent.js +23 -0
- package/dist/esm/pages/MainPage/LimitPageContent.js.map +1 -0
- package/dist/esm/pages/MainPage/MainPage.js +19 -22
- package/dist/esm/pages/MainPage/MainPage.js.map +1 -1
- package/dist/esm/pages/MainPage/MainPageActions.js +28 -0
- package/dist/esm/pages/MainPage/MainPageActions.js.map +1 -0
- package/dist/esm/pages/MainPage/ReviewButton.js +9 -2
- package/dist/esm/pages/MainPage/ReviewButton.js.map +1 -1
- package/dist/esm/pages/RoutePriorityPage.js +48 -0
- package/dist/esm/pages/RoutePriorityPage.js.map +1 -0
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js +2 -2
- package/dist/esm/pages/SettingsPage/BridgeAndExchangeSettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js +12 -27
- package/dist/esm/pages/SettingsPage/RoutePrioritySettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js +133 -0
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippagePage.js.map +1 -0
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js +14 -72
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.js.map +1 -1
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js +26 -14
- package/dist/esm/pages/SettingsPage/SlippageSettings/SlippageSettings.style.js.map +1 -1
- package/dist/esm/pages/SettingsPage/ThemeSettings.js +4 -0
- package/dist/esm/pages/SettingsPage/ThemeSettings.js.map +1 -1
- package/dist/esm/pages/TransactionPage/TransactionReview.js +4 -1
- package/dist/esm/pages/TransactionPage/TransactionReview.js.map +1 -1
- package/dist/esm/providers/I18nProvider/I18nProvider.js +1 -1
- package/dist/esm/providers/I18nProvider/enResource.d.ts +1 -1
- package/dist/esm/providers/I18nProvider/enResource.js +1 -1
- package/dist/esm/providers/I18nProvider/types.d.ts +2 -2
- package/dist/esm/providers/SDKClientProvider.js +25 -24
- package/dist/esm/providers/SDKClientProvider.js.map +1 -1
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js +22 -21
- package/dist/esm/providers/WidgetProvider/WidgetProvider.js.map +1 -1
- package/dist/esm/stores/form/URLSearchParamsBuilder.js +14 -3
- package/dist/esm/stores/form/URLSearchParamsBuilder.js.map +1 -1
- package/dist/esm/stores/form/createFormStore.js +6 -2
- package/dist/esm/stores/form/createFormStore.js.map +1 -1
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js +3 -0
- package/dist/esm/stores/form/getDefaultValuesFromQueryString.js.map +1 -1
- package/dist/esm/stores/form/types.d.ts +9 -1
- package/dist/esm/stores/form/types.d.ts.map +1 -1
- package/dist/esm/stores/form/types.js.map +1 -1
- package/dist/esm/stores/form/useClearAmountFields.js +1 -0
- package/dist/esm/stores/form/useClearAmountFields.js.map +1 -1
- package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js +4 -2
- package/dist/esm/stores/navigationTabs/useNavigationTabsStore.js.map +1 -1
- package/dist/esm/stores/navigationTabs/utils.js +38 -8
- package/dist/esm/stores/navigationTabs/utils.js.map +1 -1
- package/dist/esm/types/events.d.ts +10 -1
- package/dist/esm/types/events.d.ts.map +1 -1
- package/dist/esm/types/events.js +2 -0
- package/dist/esm/types/events.js.map +1 -1
- package/dist/esm/types/widget.d.ts +9 -0
- package/dist/esm/types/widget.d.ts.map +1 -1
- package/dist/esm/types/widget.js.map +1 -1
- package/dist/esm/utils/format.d.ts +1 -1
- package/dist/esm/utils/format.d.ts.map +1 -1
- package/dist/esm/utils/format.js +2 -2
- package/dist/esm/utils/format.js.map +1 -1
- package/dist/esm/utils/limitOrder.js +61 -0
- package/dist/esm/utils/limitOrder.js.map +1 -0
- package/dist/esm/utils/navigationRoutes.d.ts +2 -0
- package/dist/esm/utils/navigationRoutes.d.ts.map +1 -1
- package/dist/esm/utils/navigationRoutes.js +6 -0
- package/dist/esm/utils/navigationRoutes.js.map +1 -1
- package/package.json +11 -11
- package/src/AppDefault.tsx +16 -0
- package/src/components/AmountInput/AmountInputEndAdornment.style.ts +1 -1
- package/src/components/AmountInput/AmountInputEndAdornment.tsx +5 -2
- package/src/components/AmountInputCard/AmountInputCard.style.tsx +157 -0
- package/src/components/AmountInputCard/AmountInputCardPair.tsx +25 -0
- package/src/components/AmountInputCard/BalanceDisplay.style.tsx +9 -0
- package/src/components/AmountInputCard/BalanceDisplay.tsx +46 -0
- package/src/components/AmountInputCard/FiatValueToggle.tsx +55 -0
- package/src/components/AmountInputCard/PercentageChips.style.tsx +60 -0
- package/src/components/AmountInputCard/PercentageChips.tsx +100 -0
- package/src/components/AmountInputCard/ReceiveAmountCard.tsx +219 -0
- package/src/components/AmountInputCard/SendAmountCard.tsx +179 -0
- package/src/components/Avatar/Avatar.tsx +8 -3
- package/src/components/Header/HeaderNavigationTabs.tsx +19 -2
- package/src/components/LimitOrderSettings/LimitOrderSettings.style.tsx +69 -0
- package/src/components/LimitOrderSettings/LimitOrderSettings.tsx +124 -0
- package/src/components/LimitPriceCard/LimitPriceCard.style.tsx +103 -0
- package/src/components/LimitPriceCard/LimitPriceCard.tsx +153 -0
- package/src/components/LimitPriceCard/LimitPricePresets.tsx +139 -0
- package/src/components/RouteCard/RouteProviderCard.tsx +113 -0
- package/src/components/Routes/LimitOrderRoutes.tsx +64 -0
- package/src/components/Routes/Routes.tsx +3 -2
- package/src/components/SettingsPanel/SettingsPanel.style.tsx +41 -0
- package/src/components/SettingsPanel/SettingsPanel.tsx +73 -0
- package/src/components/SettingsPanel/utils.ts +27 -0
- package/src/components/SwapButton/SwapButton.style.tsx +28 -0
- package/src/components/SwapButton/SwapButton.tsx +71 -0
- package/src/components/TokenPillButton/TokenPillButton.style.tsx +50 -0
- package/src/components/TokenPillButton/TokenPillButton.tsx +81 -0
- package/src/config/version.ts +1 -1
- package/src/hooks/useLimitMarketRate.ts +39 -0
- package/src/hooks/useLinkedLimitFields.ts +74 -0
- package/src/hooks/useRoutes.ts +45 -4
- package/src/hooks/useWideVariant.ts +8 -2
- package/src/pages/MainPage/LimitPageContent.tsx +18 -0
- package/src/pages/MainPage/MainPage.tsx +43 -30
- package/src/pages/MainPage/MainPageActions.tsx +35 -0
- package/src/pages/MainPage/ReviewButton.tsx +13 -1
- package/src/pages/RoutePriorityPage.tsx +55 -0
- package/src/pages/SettingsPage/RoutePrioritySettings.tsx +14 -38
- package/src/pages/SettingsPage/SlippageSettings/SlippagePage.tsx +176 -0
- package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.style.tsx +32 -25
- package/src/pages/SettingsPage/SlippageSettings/SlippageSettings.tsx +14 -128
- package/src/pages/SettingsPage/ThemeSettings.tsx +4 -0
- package/src/stores/form/URLSearchParamsBuilder.tsx +25 -3
- package/src/stores/form/createFormStore.ts +7 -3
- package/src/stores/form/getDefaultValuesFromQueryString.ts +11 -0
- package/src/stores/form/types.ts +14 -1
- package/src/stores/form/useClearAmountFields.ts +1 -0
- package/src/stores/navigationTabs/types.ts +6 -0
- package/src/stores/navigationTabs/useNavigationTabsStore.tsx +8 -3
- package/src/stores/navigationTabs/utils.ts +67 -5
- package/src/types/events.ts +12 -0
- package/src/types/widget.ts +9 -0
- package/src/utils/format.ts +6 -2
- package/src/utils/limitOrder.ts +88 -0
- package/src/utils/navigationRoutes.ts +6 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Box, Button, ButtonBase, styled, Typography } from '@mui/material'
|
|
2
|
+
import type React from 'react'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Quick-set preset pill. Mirrors the percentage chips' pill look but is always
|
|
6
|
+
* visible (no reveal-on-hover), since presets are the card's primary control.
|
|
7
|
+
*/
|
|
8
|
+
export const PriceChip: React.FC<React.ComponentProps<typeof Button>> = styled(
|
|
9
|
+
Button
|
|
10
|
+
)(({ theme }) => ({
|
|
11
|
+
minWidth: 0,
|
|
12
|
+
padding: theme.spacing(0.5, 1.5),
|
|
13
|
+
fontSize: 12,
|
|
14
|
+
fontWeight: 700,
|
|
15
|
+
lineHeight: 1.3334,
|
|
16
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 4%, transparent)`,
|
|
17
|
+
color: theme.vars.palette.text.primary,
|
|
18
|
+
transition: 'background-color 200ms ease',
|
|
19
|
+
'&:hover': {
|
|
20
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 8%, transparent)`,
|
|
21
|
+
},
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Editable variant of the preset pill: looks identical to {@link PriceChip} but
|
|
26
|
+
* wraps an inline input so the user can type an arbitrary percentage premium
|
|
27
|
+
* (`+__%`) instead of picking a fixed preset.
|
|
28
|
+
*/
|
|
29
|
+
export const EditablePriceChip: React.FC<React.ComponentProps<typeof Box>> =
|
|
30
|
+
styled(Box)(({ theme }) => ({
|
|
31
|
+
display: 'inline-flex',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
minWidth: 0,
|
|
34
|
+
padding: theme.spacing(0.5, 1.5),
|
|
35
|
+
borderRadius: theme.vars.shape.borderRadius,
|
|
36
|
+
fontSize: 12,
|
|
37
|
+
fontWeight: 700,
|
|
38
|
+
lineHeight: 1.3334,
|
|
39
|
+
color: theme.vars.palette.text.primary,
|
|
40
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 4%, transparent)`,
|
|
41
|
+
transition: 'background-color 200ms ease',
|
|
42
|
+
cursor: 'text',
|
|
43
|
+
'&:hover, &:focus-within': {
|
|
44
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 8%, transparent)`,
|
|
45
|
+
},
|
|
46
|
+
'&[aria-disabled="true"]': {
|
|
47
|
+
opacity: 0.5,
|
|
48
|
+
pointerEvents: 'none',
|
|
49
|
+
},
|
|
50
|
+
}))
|
|
51
|
+
|
|
52
|
+
/** Bare input inside {@link EditablePriceChip}; inherits the pill's typography. */
|
|
53
|
+
export const PriceChipInput: React.FC<React.ComponentProps<'input'>> = styled(
|
|
54
|
+
'input'
|
|
55
|
+
)(({ theme }) => ({
|
|
56
|
+
appearance: 'none',
|
|
57
|
+
border: 'none',
|
|
58
|
+
outline: 'none',
|
|
59
|
+
background: 'transparent',
|
|
60
|
+
padding: 0,
|
|
61
|
+
margin: 0,
|
|
62
|
+
font: 'inherit',
|
|
63
|
+
color: 'inherit',
|
|
64
|
+
textAlign: 'right',
|
|
65
|
+
minWidth: '1ch',
|
|
66
|
+
'&::placeholder': {
|
|
67
|
+
color: theme.vars.palette.text.secondary,
|
|
68
|
+
opacity: 1,
|
|
69
|
+
},
|
|
70
|
+
}))
|
|
71
|
+
|
|
72
|
+
export const UnitSymbol: React.FC<React.ComponentProps<typeof Typography>> =
|
|
73
|
+
styled(Typography)(({ theme }) => ({
|
|
74
|
+
fontSize: 18,
|
|
75
|
+
fontWeight: 600,
|
|
76
|
+
lineHeight: 1.4,
|
|
77
|
+
whiteSpace: 'nowrap',
|
|
78
|
+
color: theme.vars.palette.text.primary,
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Invert affordance anchored to the far right of the lead row. Styled as a
|
|
83
|
+
* pill to mirror the percentage chips, but always visible (no reveal-on-hover).
|
|
84
|
+
*/
|
|
85
|
+
export const InvertChip: React.FC<React.ComponentProps<typeof ButtonBase>> =
|
|
86
|
+
styled(ButtonBase)(({ theme }) => ({
|
|
87
|
+
display: 'flex',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
justifyContent: 'center',
|
|
90
|
+
gap: theme.spacing(0.5),
|
|
91
|
+
minWidth: 0,
|
|
92
|
+
padding: theme.spacing(0.5, 1.5),
|
|
93
|
+
borderRadius: '9999px',
|
|
94
|
+
fontSize: 12,
|
|
95
|
+
fontWeight: 700,
|
|
96
|
+
lineHeight: 1.3334,
|
|
97
|
+
color: theme.vars.palette.text.primary,
|
|
98
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 4%, transparent)`,
|
|
99
|
+
transition: 'background-color 200ms ease',
|
|
100
|
+
'&:hover': {
|
|
101
|
+
backgroundColor: `color-mix(in srgb, ${theme.vars.palette.common.onBackground} 8%, transparent)`,
|
|
102
|
+
},
|
|
103
|
+
}))
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import SwapHorizIcon from '@mui/icons-material/SwapHoriz'
|
|
2
|
+
import type { CardProps } from '@mui/material'
|
|
3
|
+
import { Skeleton } from '@mui/material'
|
|
4
|
+
import { type ChangeEvent, type JSX, useEffect, useRef, useState } from 'react'
|
|
5
|
+
import { useTranslation } from 'react-i18next'
|
|
6
|
+
import { useLimitMarketRate } from '../../hooks/useLimitMarketRate.js'
|
|
7
|
+
import { useLinkedLimitFields } from '../../hooks/useLinkedLimitFields.js'
|
|
8
|
+
import { useRoutes } from '../../hooks/useRoutes.js'
|
|
9
|
+
import { useToken } from '../../hooks/useToken.js'
|
|
10
|
+
import { FormKeyHelper } from '../../stores/form/types.js'
|
|
11
|
+
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
12
|
+
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
13
|
+
import { formatInputAmount } from '../../utils/format.js'
|
|
14
|
+
import { applyPriceOffset, invertPrice } from '../../utils/limitOrder.js'
|
|
15
|
+
import {
|
|
16
|
+
AmountCard,
|
|
17
|
+
amountHeight,
|
|
18
|
+
CardBodyRow,
|
|
19
|
+
CardHeaderRow,
|
|
20
|
+
LargeInput,
|
|
21
|
+
} from '../AmountInputCard/AmountInputCard.style.js'
|
|
22
|
+
import { CardTitle } from '../Card/CardTitle.js'
|
|
23
|
+
import { InvertChip } from './LimitPriceCard.style.js'
|
|
24
|
+
import { LimitPricePresets } from './LimitPricePresets.js'
|
|
25
|
+
|
|
26
|
+
const cleanDisplay = (value: string): string =>
|
|
27
|
+
value ? formatInputAmount(value, 8) : ''
|
|
28
|
+
|
|
29
|
+
export const LimitPriceCard: React.FC<CardProps> = (props): JSX.Element => {
|
|
30
|
+
const { t } = useTranslation()
|
|
31
|
+
const isEditingRef = useRef(false)
|
|
32
|
+
const [inputValue, setInputValue] = useState('')
|
|
33
|
+
|
|
34
|
+
const [
|
|
35
|
+
fromChainId,
|
|
36
|
+
fromTokenAddress,
|
|
37
|
+
toChainId,
|
|
38
|
+
toTokenAddress,
|
|
39
|
+
priceInverted,
|
|
40
|
+
] = useFieldValues(
|
|
41
|
+
FormKeyHelper.getChainKey('from'),
|
|
42
|
+
FormKeyHelper.getTokenKey('from'),
|
|
43
|
+
FormKeyHelper.getChainKey('to'),
|
|
44
|
+
FormKeyHelper.getTokenKey('to'),
|
|
45
|
+
'priceInverted'
|
|
46
|
+
)
|
|
47
|
+
const { token: fromToken } = useToken(fromChainId, fromTokenAddress)
|
|
48
|
+
const { token: toToken } = useToken(toChainId, toTokenAddress)
|
|
49
|
+
|
|
50
|
+
const { isFetching } = useRoutes()
|
|
51
|
+
|
|
52
|
+
// limitPrice is derived from the from/to amount fields, not stored.
|
|
53
|
+
const { limitPrice, setLimitPrice } = useLinkedLimitFields()
|
|
54
|
+
const { setFieldValue } = useFieldActions()
|
|
55
|
+
|
|
56
|
+
const hasTokens = Boolean(fromToken && toToken)
|
|
57
|
+
const showSkeleton = isFetching && !limitPrice
|
|
58
|
+
const leadToken = priceInverted ? toToken : fromToken
|
|
59
|
+
const unitToken = priceInverted ? fromToken : toToken
|
|
60
|
+
|
|
61
|
+
const marketCanonical = useLimitMarketRate()
|
|
62
|
+
|
|
63
|
+
// Canonical price is orientation-independent; flip only for display.
|
|
64
|
+
const displayPrice = priceInverted ? invertPrice(limitPrice) : limitPrice
|
|
65
|
+
|
|
66
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: pair fields are intentional re-run triggers, not read in the body
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setFieldValue('toAmount', '', { isDirty: false, isTouched: false })
|
|
69
|
+
setFieldValue('selectedRouteId', undefined)
|
|
70
|
+
}, [fromChainId, fromTokenAddress, toChainId, toTokenAddress])
|
|
71
|
+
|
|
72
|
+
// Keep the input in sync with derived/preset/invert updates, but never yank
|
|
73
|
+
// the value out from under an actively-typing user.
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!isEditingRef.current) {
|
|
76
|
+
setInputValue(cleanDisplay(displayPrice))
|
|
77
|
+
}
|
|
78
|
+
}, [displayPrice])
|
|
79
|
+
|
|
80
|
+
const handleChange = (
|
|
81
|
+
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
|
82
|
+
): void => {
|
|
83
|
+
isEditingRef.current = true
|
|
84
|
+
const formatted = formatInputAmount(event.target.value, 18, true)
|
|
85
|
+
setInputValue(formatted)
|
|
86
|
+
const numeric = Number(formatted)
|
|
87
|
+
if (!formatted || Number.isNaN(numeric) || numeric <= 0) {
|
|
88
|
+
setLimitPrice('')
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
// Canonical price is orientation-independent; un-flip the displayed value.
|
|
92
|
+
const canonical = priceInverted ? 1 / numeric : numeric
|
|
93
|
+
setLimitPrice(String(canonical))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const handleBlur = (): void => {
|
|
97
|
+
isEditingRef.current = false
|
|
98
|
+
setInputValue(cleanDisplay(displayPrice))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const handleInvert = (): void => {
|
|
102
|
+
isEditingRef.current = false
|
|
103
|
+
setFieldValue('priceInverted', !priceInverted)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Apply a percentage offset to the canonical market rate (0 = at market).
|
|
107
|
+
const handlePreset = (pct: number): void => {
|
|
108
|
+
if (marketCanonical === undefined) {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
isEditingRef.current = false
|
|
112
|
+
setLimitPrice(applyPriceOffset(String(marketCanonical), pct))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const label = hasTokens
|
|
116
|
+
? t('limitOrder.priceLabel', { symbol: leadToken?.symbol })
|
|
117
|
+
: t('limitOrder.price')
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<AmountCard {...props} mask={false}>
|
|
121
|
+
<CardHeaderRow>
|
|
122
|
+
<CardTitle sx={{ padding: 0 }}>{label}</CardTitle>
|
|
123
|
+
{hasTokens ? (
|
|
124
|
+
<InvertChip onClick={handleInvert} aria-label="invert price">
|
|
125
|
+
{unitToken?.symbol}
|
|
126
|
+
<SwapHorizIcon sx={{ fontSize: 16 }} />
|
|
127
|
+
</InvertChip>
|
|
128
|
+
) : null}
|
|
129
|
+
</CardHeaderRow>
|
|
130
|
+
<CardBodyRow>
|
|
131
|
+
{showSkeleton ? (
|
|
132
|
+
<Skeleton variant="rounded" height={amountHeight} sx={{ flex: 1 }} />
|
|
133
|
+
) : (
|
|
134
|
+
<LargeInput
|
|
135
|
+
size="small"
|
|
136
|
+
autoComplete="off"
|
|
137
|
+
placeholder="0"
|
|
138
|
+
inputProps={{ inputMode: 'decimal' }}
|
|
139
|
+
onChange={handleChange}
|
|
140
|
+
onBlur={handleBlur}
|
|
141
|
+
value={inputValue}
|
|
142
|
+
name="limitPrice"
|
|
143
|
+
disabled={!hasTokens}
|
|
144
|
+
/>
|
|
145
|
+
)}
|
|
146
|
+
</CardBodyRow>
|
|
147
|
+
<LimitPricePresets
|
|
148
|
+
onSelect={handlePreset}
|
|
149
|
+
disabled={!hasTokens || marketCanonical === undefined}
|
|
150
|
+
/>
|
|
151
|
+
</AmountCard>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type ChangeEvent, type JSX, useEffect, useRef, useState } from 'react'
|
|
2
|
+
import { useTranslation } from 'react-i18next'
|
|
3
|
+
import { ChipContainer } from '../AmountInputCard/PercentageChips.style.js'
|
|
4
|
+
import {
|
|
5
|
+
EditablePriceChip,
|
|
6
|
+
PriceChip,
|
|
7
|
+
PriceChipInput,
|
|
8
|
+
} from './LimitPriceCard.style.js'
|
|
9
|
+
|
|
10
|
+
/** Quick-set presets, as percentage offsets from market (0 = at market). */
|
|
11
|
+
const PRESETS = [0, 1, 5, 10] as const
|
|
12
|
+
|
|
13
|
+
interface LimitPricePresetsProps {
|
|
14
|
+
/** Apply a percentage offset from the market rate (0 = at market). */
|
|
15
|
+
onSelect: (pct: number) => void
|
|
16
|
+
/** Disables the free-form custom pill (no tokens / no market rate yet). */
|
|
17
|
+
disabled: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Row of quick-set price controls: fixed percentage presets plus a free-form
|
|
22
|
+
* "custom %" pill that collapses to a button until engaged. Owns its own
|
|
23
|
+
* custom-input state so the card only deals with applying an offset.
|
|
24
|
+
*/
|
|
25
|
+
export const LimitPricePresets = ({
|
|
26
|
+
onSelect,
|
|
27
|
+
disabled,
|
|
28
|
+
}: LimitPricePresetsProps): JSX.Element => {
|
|
29
|
+
const { t } = useTranslation()
|
|
30
|
+
const customInputRef = useRef<HTMLInputElement>(null)
|
|
31
|
+
const [customPct, setCustomPct] = useState('')
|
|
32
|
+
const [customFocused, setCustomFocused] = useState(false)
|
|
33
|
+
|
|
34
|
+
// The custom pill renders as a plain button until engaged; when it flips into
|
|
35
|
+
// edit mode (focused), move the caret into the freshly-mounted input.
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (customFocused) {
|
|
38
|
+
customInputRef.current?.focus()
|
|
39
|
+
}
|
|
40
|
+
}, [customFocused])
|
|
41
|
+
|
|
42
|
+
// Picking a fixed preset clears any custom value so the custom pill collapses
|
|
43
|
+
// back to its "Custom" button instead of showing a stale, no-longer-applied %.
|
|
44
|
+
const handlePresetClick = (pct: number): void => {
|
|
45
|
+
setCustomPct('')
|
|
46
|
+
setCustomFocused(false)
|
|
47
|
+
onSelect(pct)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Free-form offset pill: apply the typed percentage live so the price input
|
|
51
|
+
// tracks the offset as the user types. Accepts an optional leading +/- sign
|
|
52
|
+
// so the price can be set above (premium) or below (discount) market.
|
|
53
|
+
const handleCustomChange = (event: ChangeEvent<HTMLInputElement>): void => {
|
|
54
|
+
const raw = event.target.value
|
|
55
|
+
if (raw && !/^[+-]?\d*\.?\d*$/.test(raw)) {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
setCustomPct(raw)
|
|
59
|
+
const numeric = Number(raw)
|
|
60
|
+
// Bare '', '+', '-', '.' etc. aren't applicable numbers yet — Number() maps
|
|
61
|
+
// the signs/dot to NaN, and '' to 0, so guard both.
|
|
62
|
+
if (raw === '' || Number.isNaN(numeric)) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
onSelect(numeric)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const handleCustomKeyDown = (
|
|
69
|
+
event: React.KeyboardEvent<HTMLInputElement>
|
|
70
|
+
): void => {
|
|
71
|
+
if (event.key === 'Enter') {
|
|
72
|
+
event.currentTarget.blur()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// On blur, surface an explicit sign: a bare positive entry like "10" reads as
|
|
77
|
+
// "+10" so the direction (above/below market) is unambiguous once collapsed.
|
|
78
|
+
const handleCustomBlur = (): void => {
|
|
79
|
+
setCustomFocused(false)
|
|
80
|
+
setCustomPct((prev) => {
|
|
81
|
+
if (!prev || prev.startsWith('+') || prev.startsWith('-')) {
|
|
82
|
+
return prev
|
|
83
|
+
}
|
|
84
|
+
return Number.isNaN(Number(prev)) ? prev : `+${prev}`
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Collapse to a plain "Custom" label only while empty and unfocused; once the
|
|
89
|
+
// user engages (focus) or has typed a value, reveal the `+__%` affixes.
|
|
90
|
+
const showCustomLabel = customPct === '' && !customFocused
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<ChipContainer sx={{ marginTop: 0.5, flexWrap: 'wrap' }}>
|
|
94
|
+
{PRESETS.map((pct) => (
|
|
95
|
+
<PriceChip key={pct} onClick={() => handlePresetClick(pct)}>
|
|
96
|
+
{pct === 0 ? t('limitOrder.market') : `+${pct}%`}
|
|
97
|
+
</PriceChip>
|
|
98
|
+
))}
|
|
99
|
+
{showCustomLabel ? (
|
|
100
|
+
<PriceChip disabled={disabled} onClick={() => setCustomFocused(true)}>
|
|
101
|
+
{t('limitOrder.custom')}
|
|
102
|
+
</PriceChip>
|
|
103
|
+
) : (
|
|
104
|
+
<EditablePriceChip
|
|
105
|
+
aria-disabled={disabled}
|
|
106
|
+
onClick={(event) => {
|
|
107
|
+
// `aria-disabled` only announces the state; enforce it so a
|
|
108
|
+
// programmatic click can't apply an offset while disabled.
|
|
109
|
+
if (disabled) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
const input = event.currentTarget.querySelector('input')
|
|
113
|
+
input?.focus()
|
|
114
|
+
const numeric = Number(customPct)
|
|
115
|
+
if (customPct && !Number.isNaN(numeric)) {
|
|
116
|
+
onSelect(numeric)
|
|
117
|
+
}
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<PriceChipInput
|
|
121
|
+
ref={customInputRef}
|
|
122
|
+
inputMode="decimal"
|
|
123
|
+
autoComplete="off"
|
|
124
|
+
placeholder="0"
|
|
125
|
+
aria-label={t('limitOrder.custom')}
|
|
126
|
+
value={customPct}
|
|
127
|
+
onChange={handleCustomChange}
|
|
128
|
+
onKeyDown={handleCustomKeyDown}
|
|
129
|
+
onFocus={() => setCustomFocused(true)}
|
|
130
|
+
onBlur={handleCustomBlur}
|
|
131
|
+
disabled={disabled}
|
|
132
|
+
style={{ width: `${Math.max(customPct.length, 1)}ch` }}
|
|
133
|
+
/>
|
|
134
|
+
%
|
|
135
|
+
</EditablePriceChip>
|
|
136
|
+
)}
|
|
137
|
+
</ChipContainer>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Route } from '@lifi/sdk'
|
|
2
|
+
import LocalGasStationRounded from '@mui/icons-material/LocalGasStationRounded'
|
|
3
|
+
import { Box, Skeleton, Typography } from '@mui/material'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import { getAccumulatedFeeCostsBreakdown } from '../../utils/fees.js'
|
|
6
|
+
import { SmallAvatar } from '../Avatar/SmallAvatar.js'
|
|
7
|
+
import type { CardProps } from '../Card/Card.js'
|
|
8
|
+
import { Card } from '../Card/Card.js'
|
|
9
|
+
import { IconTypography } from '../IconTypography.js'
|
|
10
|
+
|
|
11
|
+
interface RouteProviderCardProps {
|
|
12
|
+
route: Route
|
|
13
|
+
active?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const RouteProviderCard: React.FC<
|
|
17
|
+
RouteProviderCardProps & Omit<CardProps, 'variant'>
|
|
18
|
+
> = ({ route, active, onClick, ...other }) => {
|
|
19
|
+
const { t } = useTranslation()
|
|
20
|
+
|
|
21
|
+
const provider = route.steps[0]?.toolDetails
|
|
22
|
+
const { gasCostUSD } = getAccumulatedFeeCostsBreakdown(route)
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Card
|
|
26
|
+
type={active ? 'selected' : 'default'}
|
|
27
|
+
selectionColor="secondary"
|
|
28
|
+
onClick={onClick}
|
|
29
|
+
sx={{ py: 1.5, px: 2, ...(onClick ? { cursor: 'pointer' } : {}) }}
|
|
30
|
+
{...other}
|
|
31
|
+
>
|
|
32
|
+
<Box
|
|
33
|
+
sx={{
|
|
34
|
+
display: 'flex',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
justifyContent: 'space-between',
|
|
37
|
+
gap: 1,
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<Box
|
|
41
|
+
sx={{
|
|
42
|
+
display: 'flex',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
gap: 1,
|
|
45
|
+
minWidth: 0,
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<SmallAvatar src={provider?.logoURI} alt={provider?.name} size={20}>
|
|
49
|
+
{provider?.name?.[0]}
|
|
50
|
+
</SmallAvatar>
|
|
51
|
+
<Typography
|
|
52
|
+
noWrap
|
|
53
|
+
sx={{
|
|
54
|
+
fontSize: 14,
|
|
55
|
+
fontWeight: 600,
|
|
56
|
+
color: 'text.primary',
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{provider?.name}
|
|
60
|
+
</Typography>
|
|
61
|
+
</Box>
|
|
62
|
+
<Box
|
|
63
|
+
sx={{
|
|
64
|
+
display: 'flex',
|
|
65
|
+
alignItems: 'center',
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<IconTypography component="span" sx={{ mr: 0.5, fontSize: 16 }}>
|
|
69
|
+
<LocalGasStationRounded fontSize="inherit" />
|
|
70
|
+
</IconTypography>
|
|
71
|
+
<Typography
|
|
72
|
+
data-value={gasCostUSD}
|
|
73
|
+
sx={{
|
|
74
|
+
fontSize: 14,
|
|
75
|
+
color: 'text.primary',
|
|
76
|
+
fontWeight: 600,
|
|
77
|
+
lineHeight: 1,
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
{!gasCostUSD
|
|
81
|
+
? t('main.fees.free')
|
|
82
|
+
: t('format.currency', {
|
|
83
|
+
value: gasCostUSD,
|
|
84
|
+
})}
|
|
85
|
+
</Typography>
|
|
86
|
+
</Box>
|
|
87
|
+
</Box>
|
|
88
|
+
</Card>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const RouteProviderCardSkeleton: React.FC<Omit<CardProps, 'variant'>> = (
|
|
93
|
+
props
|
|
94
|
+
) => {
|
|
95
|
+
return (
|
|
96
|
+
<Card sx={{ py: 1.5, px: 2 }} {...props}>
|
|
97
|
+
<Box
|
|
98
|
+
sx={{
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'space-between',
|
|
102
|
+
gap: 1,
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
106
|
+
<Skeleton variant="circular" width={20} height={20} />
|
|
107
|
+
<Skeleton variant="text" width={80} height={20} />
|
|
108
|
+
</Box>
|
|
109
|
+
<Skeleton variant="text" width={56} height={20} />
|
|
110
|
+
</Box>
|
|
111
|
+
</Card>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Stack } from '@mui/material'
|
|
2
|
+
import { useRoutes } from '../../hooks/useRoutes.js'
|
|
3
|
+
import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'
|
|
4
|
+
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
5
|
+
import { useFieldValues } from '../../stores/form/useFieldValues.js'
|
|
6
|
+
import type { CardProps } from '../Card/Card.js'
|
|
7
|
+
import { RouteNotFoundCard } from '../RouteCard/RouteNotFoundCard.js'
|
|
8
|
+
import {
|
|
9
|
+
RouteProviderCard,
|
|
10
|
+
RouteProviderCardSkeleton,
|
|
11
|
+
} from '../RouteCard/RouteProviderCard.js'
|
|
12
|
+
|
|
13
|
+
export const LimitOrderRoutes: React.FC<CardProps> = (props) => {
|
|
14
|
+
const { routes, isLoading, isFetching, isFetched } = useRoutes()
|
|
15
|
+
const [toAddress, selectedRouteId] = useFieldValues(
|
|
16
|
+
'toAddress',
|
|
17
|
+
'selectedRouteId'
|
|
18
|
+
)
|
|
19
|
+
const { setFieldValue } = useFieldActions()
|
|
20
|
+
const { requiredToAddress } = useToAddressRequirements()
|
|
21
|
+
|
|
22
|
+
const currentRoute = routes?.[0]
|
|
23
|
+
|
|
24
|
+
if (!currentRoute && !isLoading && !isFetching && !isFetched) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const toAddressUnsatisfied = currentRoute && requiredToAddress && !toAddress
|
|
29
|
+
const allowInteraction = !toAddressUnsatisfied
|
|
30
|
+
|
|
31
|
+
const activeRouteId =
|
|
32
|
+
routes?.find((route) => route.id === selectedRouteId)?.id ??
|
|
33
|
+
currentRoute?.id
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Stack
|
|
37
|
+
direction="column"
|
|
38
|
+
spacing={1}
|
|
39
|
+
sx={props.sx}
|
|
40
|
+
className={props.className}
|
|
41
|
+
>
|
|
42
|
+
{isLoading && !currentRoute ? (
|
|
43
|
+
Array.from({ length: 2 }).map((_, index) => (
|
|
44
|
+
<RouteProviderCardSkeleton key={index} />
|
|
45
|
+
))
|
|
46
|
+
) : !currentRoute ? (
|
|
47
|
+
<RouteNotFoundCard />
|
|
48
|
+
) : (
|
|
49
|
+
routes?.map((route) => (
|
|
50
|
+
<RouteProviderCard
|
|
51
|
+
key={route.id}
|
|
52
|
+
route={route}
|
|
53
|
+
active={route.id === activeRouteId}
|
|
54
|
+
onClick={
|
|
55
|
+
allowInteraction
|
|
56
|
+
? () => setFieldValue('selectedRouteId', route.id)
|
|
57
|
+
: undefined
|
|
58
|
+
}
|
|
59
|
+
/>
|
|
60
|
+
))
|
|
61
|
+
)}
|
|
62
|
+
</Stack>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -16,7 +16,7 @@ import { RouteNotFoundCard } from '../RouteCard/RouteNotFoundCard.js'
|
|
|
16
16
|
export const Routes: React.FC<CardProps> = (props) => {
|
|
17
17
|
const { t } = useTranslation()
|
|
18
18
|
const navigate = useNavigate()
|
|
19
|
-
const { mode, modeOptions, showSingleRoute } = useWidgetConfig()
|
|
19
|
+
const { mode, modeOptions, showSingleRoute, defaultUI } = useWidgetConfig()
|
|
20
20
|
const {
|
|
21
21
|
routes,
|
|
22
22
|
isLoading,
|
|
@@ -51,7 +51,8 @@ export const Routes: React.FC<CardProps> = (props) => {
|
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
53
|
<Card {...props}>
|
|
54
|
-
|
|
54
|
+
{/* The cards layout's Receive card already carries this header. */}
|
|
55
|
+
{defaultUI?.layout === 'cards' ? null : <CardTitle>{title}</CardTitle>}
|
|
55
56
|
<ProgressToNextUpdate
|
|
56
57
|
updatedAt={dataUpdatedAt || Date.now()}
|
|
57
58
|
timeToUpdate={refetchTime}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Box, ButtonBase, styled, Typography } from '@mui/material'
|
|
2
|
+
import type React from 'react'
|
|
3
|
+
|
|
4
|
+
export const SettingsPanelContainer: React.FC<
|
|
5
|
+
React.ComponentProps<typeof Box>
|
|
6
|
+
> = styled(Box)(({ theme }) => ({
|
|
7
|
+
display: 'flex',
|
|
8
|
+
flexDirection: 'column',
|
|
9
|
+
gap: theme.spacing(1),
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
export const SettingsPanelButton: React.FC<
|
|
13
|
+
React.ComponentProps<typeof ButtonBase>
|
|
14
|
+
> = styled(ButtonBase)(({ theme }) => ({
|
|
15
|
+
display: 'flex',
|
|
16
|
+
width: '100%',
|
|
17
|
+
justifyContent: 'space-between',
|
|
18
|
+
alignItems: 'center',
|
|
19
|
+
gap: theme.spacing(1),
|
|
20
|
+
padding: theme.spacing(2),
|
|
21
|
+
borderRadius: theme.vars.shape.borderRadius,
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
export const SettingsPanelTitle: React.FC<
|
|
25
|
+
React.ComponentProps<typeof Typography>
|
|
26
|
+
> = styled(Typography)(({ theme }) => ({
|
|
27
|
+
fontSize: 14,
|
|
28
|
+
lineHeight: 1,
|
|
29
|
+
fontWeight: 700,
|
|
30
|
+
color: theme.vars.palette.text.primary,
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
export const SettingsPanelValue: React.FC<
|
|
34
|
+
React.ComponentProps<typeof Typography>
|
|
35
|
+
> = styled(Typography)(({ theme }) => ({
|
|
36
|
+
fontSize: 14,
|
|
37
|
+
lineHeight: 1,
|
|
38
|
+
fontWeight: 500,
|
|
39
|
+
color: theme.vars.palette.text.secondary,
|
|
40
|
+
whiteSpace: 'nowrap',
|
|
41
|
+
}))
|