diff --git a/packages/widget/src/components/SwapInput/FitInputText.ts b/packages/widget/src/components/SwapInput/FitInputText.ts deleted file mode 100644 index a9654df81..000000000 --- a/packages/widget/src/components/SwapInput/FitInputText.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { MutableRefObject } from 'react'; -import { forwardRef, useLayoutEffect } from 'react'; -import { useWatch } from 'react-hook-form'; -import type { SwapFormTypeProps } from '../../providers'; -import { SwapFormKeyHelper } from '../../providers'; -import { fitInputText } from '../../utils'; -import { maxInputFontSize, minInputFontSize } from './SwapInput.style'; - -export const FitInputText = forwardRef( - ({ formType }, ref) => { - const amountKey = SwapFormKeyHelper.getAmountKey(formType); - const [amount] = useWatch({ - name: [amountKey], - }); - - useLayoutEffect(() => { - fitInputText( - maxInputFontSize, - minInputFontSize, - (ref as MutableRefObject) - .current as HTMLElement, - ); - }, [amount, ref]); - - return null; - }, -); diff --git a/packages/widget/src/components/SwapInput/FormPriceHelperText.tsx b/packages/widget/src/components/SwapInput/FormPriceHelperText.tsx index a3d045433..af84e5d66 100644 --- a/packages/widget/src/components/SwapInput/FormPriceHelperText.tsx +++ b/packages/widget/src/components/SwapInput/FormPriceHelperText.tsx @@ -1,3 +1,4 @@ +import type { TokenAmount } from '@lifi/sdk'; import { FormHelperText, Skeleton, Typography } from '@mui/material'; import { useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -9,10 +10,8 @@ import { formatTokenAmount, formatTokenPrice } from '../../utils'; export const FormPriceHelperText: React.FC = ({ formType, }) => { - const { t } = useTranslation(); - const [amount, chainId, tokenAddress] = useWatch({ + const [chainId, tokenAddress] = useWatch({ name: [ - SwapFormKeyHelper.getAmountKey(formType), SwapFormKeyHelper.getChainKey(formType), SwapFormKeyHelper.getTokenKey(formType), ], @@ -20,6 +19,28 @@ export const FormPriceHelperText: React.FC = ({ const { token, isLoading } = useTokenAddressBalance(chainId, tokenAddress); + return ( + + ); +}; + +export const FormPriceHelperTextBase: React.FC< + SwapFormTypeProps & { + isLoading?: boolean; + tokenAddress?: string; + token?: TokenAmount; + } +> = ({ formType, isLoading, tokenAddress, token }) => { + const { t } = useTranslation(); + const amount = useWatch({ + name: SwapFormKeyHelper.getAmountKey(formType), + }); + const fromAmountTokenPrice = formatTokenPrice(amount, token?.priceUSD); return ( diff --git a/packages/widget/src/components/SwapInput/SwapInput.tsx b/packages/widget/src/components/SwapInput/SwapInput.tsx index 7e3c57f29..7d33608b6 100644 --- a/packages/widget/src/components/SwapInput/SwapInput.tsx +++ b/packages/widget/src/components/SwapInput/SwapInput.tsx @@ -1,22 +1,70 @@ +import type { Token } from '@lifi/sdk'; import type { BoxProps } from '@mui/material'; -import type { ChangeEvent } from 'react'; -import { useRef } from 'react'; -import { useController } from 'react-hook-form'; +import type { ChangeEvent, ReactNode } from 'react'; +import { useLayoutEffect, useRef } from 'react'; +import { useController, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import { useToken } from '../../hooks'; import type { SwapFormTypeProps } from '../../providers'; import { SwapFormKeyHelper, useWidgetConfig } from '../../providers'; import { DisabledUI } from '../../types'; -import { formatInputAmount } from '../../utils'; +import { fitInputText, formatInputAmount } from '../../utils'; import { Card, CardTitle } from '../Card'; -import { FitInputText } from './FitInputText'; import { FormPriceHelperText } from './FormPriceHelperText'; -import { FormControl, Input } from './SwapInput.style'; +import { + FormControl, + Input, + maxInputFontSize, + minInputFontSize, +} from './SwapInput.style'; import { SwapInputEndAdornment } from './SwapInputEndAdornment'; import { SwapInputStartAdornment } from './SwapInputStartAdornment'; export const SwapInput: React.FC = ({ formType, ...props +}) => { + const { disabledUI } = useWidgetConfig(); + const [chainId, tokenAddress] = useWatch({ + name: [ + SwapFormKeyHelper.getChainKey(formType), + SwapFormKeyHelper.getTokenKey(formType), + ], + }); + const { token } = useToken(chainId, tokenAddress); + const disabled = disabledUI?.includes(DisabledUI.FromAmount); + return ( + } + endAdornment={ + !disabled ? : undefined + } + bottomAdornment={} + disabled={disabled} + {...props} + /> + ); +}; + +export const SwapInputBase: React.FC< + SwapFormTypeProps & + BoxProps & { + token?: Token; + startAdornment?: ReactNode; + endAdornment?: ReactNode; + bottomAdornment?: ReactNode; + disabled?: boolean; + } +> = ({ + formType, + token, + startAdornment, + endAdornment, + bottomAdornment, + disabled, + ...props }) => { const { t } = useTranslation(); const amountKey = SwapFormKeyHelper.getAmountKey(formType); @@ -25,14 +73,13 @@ export const SwapInput: React.FC = ({ } = useController({ name: amountKey, }); - const { disabledUI } = useWidgetConfig(); const ref = useRef(null); const handleChange = ( event: ChangeEvent, ) => { const { value } = event.target; - const formattedAmount = formatInputAmount(value, true); + const formattedAmount = formatInputAmount(value, token?.decimals, true); onChange(formattedAmount); }; @@ -40,12 +87,16 @@ export const SwapInput: React.FC = ({ event: ChangeEvent, ) => { const { value } = event.target; - const formattedAmount = formatInputAmount(value); + const formattedAmount = formatInputAmount(value, token?.decimals); onChange(formattedAmount); onBlur(); }; - const disabled = disabledUI?.includes(DisabledUI.FromAmount); + useLayoutEffect(() => { + if (ref.current) { + fitInputText(maxInputFontSize, minInputFontSize, ref.current); + } + }, [value, ref]); return ( @@ -56,12 +107,8 @@ export const SwapInput: React.FC = ({ size="small" autoComplete="off" placeholder="0" - startAdornment={} - endAdornment={ - !disabled ? ( - - ) : undefined - } + startAdornment={startAdornment} + endAdornment={endAdornment} inputProps={{ inputMode: 'decimal', }} @@ -72,9 +119,8 @@ export const SwapInput: React.FC = ({ disabled={disabled} required /> - + {bottomAdornment} - ); }; diff --git a/packages/widget/src/hooks/useTokens.ts b/packages/widget/src/hooks/useTokens.ts index 07a54a343..f545ed617 100644 --- a/packages/widget/src/hooks/useTokens.ts +++ b/packages/widget/src/hooks/useTokens.ts @@ -7,7 +7,9 @@ import { useFeaturedTokens } from './useFeaturedTokens'; export const useTokens = (selectedChainId?: number) => { const lifi = useLiFi(); - const { data, isLoading } = useQuery(['tokens'], () => lifi.getTokens()); + const { data, isLoading } = useQuery(['tokens'], () => lifi.getTokens(), { + refetchInterval: 3_600_000, + }); const { getChainById, isLoading: isSupportedChainsLoading } = useChains(); const featuredTokens = useFeaturedTokens(selectedChainId); const { tokens: configTokens, chains: configChains } = useWidgetConfig(); @@ -62,12 +64,14 @@ export const useTokens = (selectedChainId?: number) => { ] as TokenAmount[]; return tokens; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ configChains, configTokens?.allow, configTokens?.deny, configTokens?.include, data?.tokens, + data, featuredTokens, getChainById, isSupportedChainsLoading, diff --git a/packages/widget/src/utils/format.ts b/packages/widget/src/utils/format.ts index 60d01b9d1..fa30c4879 100644 --- a/packages/widget/src/utils/format.ts +++ b/packages/widget/src/utils/format.ts @@ -29,7 +29,7 @@ export const formatTokenAmount = ( decimalPlaces++; } - return Big(parsedAmount).toFixed(decimalPlaces + 1, 0); + return parsedAmount.toFixed(decimalPlaces + 1); }; export const formatSlippage = ( @@ -61,6 +61,7 @@ export const formatSlippage = ( export const formatInputAmount = ( amount: string, + decimals: number = 0, returnInitial: boolean = false, ) => { if (!amount) { @@ -74,15 +75,19 @@ export const formatInputAmount = ( if (isNaN(Number(formattedAmount)) && !isNaN(parsedAmount)) { return parsedAmount.toString(); } - try { - const absFormattedAmount = Big(formattedAmount).abs(); - if (returnInitial) { - return formattedAmount; - } - return absFormattedAmount.toString(); - } catch { + if (isNaN(Math.abs(Number(formattedAmount)))) { return ''; } + if (returnInitial) { + return formattedAmount; + } + let [integer, fraction = ''] = formattedAmount.split('.'); + if (fraction.length > decimals) { + fraction = fraction.slice(0, decimals); + } + integer = integer.replace(/^0+|-/, ''); + fraction = fraction.replace(/(0+)$/, ''); + return `${integer || (fraction ? '0' : '')}${fraction ? `.${fraction}` : ''}`; }; export const formatTokenPrice = (amount?: string, price?: string) => {