feat: debounce input sliders (#784)

* feat: debounce input sliders

* fix: fixed the debounce function
This commit is contained in:
Linkie Link 2024-02-09 08:29:26 +01:00 committed by GitHub
parent 6d5e7c7325
commit 6ac7708ca5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 172 additions and 98 deletions

View File

@ -24,6 +24,7 @@ interface Props {
onClose: () => void onClose: () => void
onChange: (value: BigNumber) => void onChange: (value: BigNumber) => void
onAction: (value: BigNumber, isMax: boolean) => void onAction: (value: BigNumber, isMax: boolean) => void
onDebounce: () => void
} }
export default function AssetAmountSelectActionModal(props: Props) { export default function AssetAmountSelectActionModal(props: Props) {
@ -36,6 +37,7 @@ export default function AssetAmountSelectActionModal(props: Props) {
onClose, onClose,
onChange, onChange,
onAction, onAction,
onDebounce,
} = props } = props
const [amount, setAmount] = useState(BN_ZERO) const [amount, setAmount] = useState(BN_ZERO)
const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0) const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0)
@ -74,6 +76,7 @@ export default function AssetAmountSelectActionModal(props: Props) {
<TokenInputWithSlider <TokenInputWithSlider
asset={asset} asset={asset}
onChange={handleAmountChange} onChange={handleAmountChange}
onDebounce={onDebounce}
amount={amount} amount={amount}
max={maxAmount} max={maxAmount}
hasSelect hasSelect

View File

@ -1,8 +1,8 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
import AccountSummary from 'components/account/AccountSummary' import Modal from 'components/Modals/Modal'
import AssetImage from 'components/common/assets/AssetImage' import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal'
import Button from 'components/common/Button' import Button from 'components/common/Button'
import Card from 'components/common/Card' import Card from 'components/common/Card'
import DisplayCurrency from 'components/common/DisplayCurrency' import DisplayCurrency from 'components/common/DisplayCurrency'
@ -13,7 +13,7 @@ import Switch from 'components/common/Switch'
import Text from 'components/common/Text' import Text from 'components/common/Text'
import TitleAndSubCell from 'components/common/TitleAndSubCell' import TitleAndSubCell from 'components/common/TitleAndSubCell'
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider' import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
import Modal from 'components/Modals/Modal' import AssetImage from 'components/common/assets/AssetImage'
import { BN_ZERO } from 'constants/math' import { BN_ZERO } from 'constants/math'
import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useMarkets from 'hooks/markets/useMarkets' import useMarkets from 'hooks/markets/useMarkets'
@ -29,7 +29,6 @@ import { byDenom } from 'utils/array'
import { formatPercent } from 'utils/formatters' import { formatPercent } from 'utils/formatters'
import { BN } from 'utils/helpers' import { BN } from 'utils/helpers'
import { getDebtAmountWithInterest } from 'utils/tokens' import { getDebtAmountWithInterest } from 'utils/tokens'
import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal'
interface Props { interface Props {
account: Account account: Account
@ -92,6 +91,7 @@ function BorrowModal(props: Props) {
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id)
const { computeMaxBorrowAmount } = useHealthComputer(account) const { computeMaxBorrowAmount } = useHealthComputer(account)
const totalDebt = BN(getDebtAmount(modal)) const totalDebt = BN(getDebtAmount(modal))
const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO
const markets = useMarkets() const markets = useMarkets()
const [depositBalance, lendBalance] = useMemo( const [depositBalance, lendBalance] = useMemo(
@ -102,34 +102,34 @@ function BorrowModal(props: Props) {
[account, asset.denom], [account, asset.denom],
) )
const totalDebtRepayAmount = useMemo( const accountDebtWithInterest = useMemo(
() => getDebtAmountWithInterest(totalDebt, apy), () => getDebtAmountWithInterest(accountDebt, apy),
[totalDebt, apy], [accountDebt, apy],
) )
const overpayExeedsCap = useMemo(() => { const overpayExeedsCap = useMemo(() => {
const marketAsset = markets.find((market) => market.asset.denom === asset.denom) const marketAsset = markets.find((market) => market.asset.denom === asset.denom)
if (!marketAsset) return if (!marketAsset) return
const overpayAmount = totalDebtRepayAmount.minus(totalDebt) const overpayAmount = accountDebtWithInterest.minus(accountDebt)
const marketCapAfterOverpay = marketAsset.cap.used.plus(overpayAmount) const marketCapAfterOverpay = marketAsset.cap.used.plus(overpayAmount)
return marketAsset.cap.max.isLessThanOrEqualTo(marketCapAfterOverpay) return marketAsset.cap.max.isLessThanOrEqualTo(marketCapAfterOverpay)
}, [markets, asset.denom, totalDebt, totalDebtRepayAmount]) }, [markets, asset.denom, accountDebt, accountDebtWithInterest])
const maxRepayAmount = useMemo(() => { const maxRepayAmount = useMemo(() => {
const maxBalance = repayFromWallet const maxBalance = repayFromWallet
? BN(walletBalances.find(byDenom(asset.denom))?.amount ?? 0) ? BN(walletBalances.find(byDenom(asset.denom))?.amount ?? 0)
: depositBalance.plus(lendBalance) : depositBalance.plus(lendBalance)
return isRepay return isRepay
? BigNumber.min(maxBalance, overpayExeedsCap ? totalDebt : totalDebtRepayAmount) ? BigNumber.min(maxBalance, overpayExeedsCap ? accountDebt : accountDebtWithInterest)
: BN_ZERO : BN_ZERO
}, [ }, [
depositBalance, depositBalance,
lendBalance, lendBalance,
isRepay, isRepay,
totalDebtRepayAmount, accountDebtWithInterest,
overpayExeedsCap, overpayExeedsCap,
totalDebt, accountDebt,
walletBalances, walletBalances,
asset.denom, asset.denom,
repayFromWallet, repayFromWallet,
@ -149,7 +149,7 @@ function BorrowModal(props: Props) {
repay({ repay({
accountId: account.id, accountId: account.id,
coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount), coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount),
accountBalance: amount.isEqualTo(totalDebtRepayAmount), accountBalance: amount.isEqualTo(accountDebtWithInterest),
lend: repayFromWallet ? BNCoin.fromDenomAndBigNumber(asset.denom, BN_ZERO) : lend, lend: repayFromWallet ? BNCoin.fromDenomAndBigNumber(asset.denom, BN_ZERO) : lend,
fromWallet: repayFromWallet, fromWallet: repayFromWallet,
}) })
@ -172,17 +172,39 @@ function BorrowModal(props: Props) {
const handleChange = useCallback( const handleChange = useCallback(
(newAmount: BigNumber) => { (newAmount: BigNumber) => {
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, newAmount)
if (!amount.isEqualTo(newAmount)) setAmount(newAmount) if (!amount.isEqualTo(newAmount)) setAmount(newAmount)
if (!isRepay) return
const repayCoin = coin.amount.isGreaterThan(totalDebt)
? BNCoin.fromDenomAndBigNumber(asset.denom, totalDebt)
: coin
simulateRepay(repayCoin, repayFromWallet)
}, },
[amount, asset.denom, isRepay, simulateRepay, totalDebt, repayFromWallet], [amount, setAmount],
) )
const onDebounce = useCallback(() => {
if (isRepay) {
const repayCoin = BNCoin.fromDenomAndBigNumber(
asset.denom,
amount.isGreaterThan(accountDebt) ? accountDebt : amount,
)
simulateRepay(repayCoin, repayFromWallet)
} else {
const borrowCoin = BNCoin.fromDenomAndBigNumber(
asset.denom,
amount.isGreaterThan(max) ? max : amount,
)
const target = borrowToWallet ? 'wallet' : isAutoLendEnabled ? 'lend' : 'deposit'
simulateBorrow(target, borrowCoin)
}
}, [
amount,
isRepay,
repayFromWallet,
maxRepayAmount,
max,
asset,
borrowToWallet,
isAutoLendEnabled,
simulateBorrow,
simulateRepay,
])
const maxBorrow = useMemo(() => { const maxBorrow = useMemo(() => {
const maxBorrowAmount = isRepay const maxBorrowAmount = isRepay
? BN_ZERO ? BN_ZERO
@ -209,13 +231,6 @@ function BorrowModal(props: Props) {
setAmount(max) setAmount(max)
}, [amount, max, handleChange]) }, [amount, max, handleChange])
useEffect(() => {
if (isRepay) return
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, amount.isGreaterThan(max) ? max : amount)
const target = borrowToWallet ? 'wallet' : isAutoLendEnabled ? 'lend' : 'deposit'
simulateBorrow(target, coin)
}, [isRepay, borrowToWallet, isAutoLendEnabled, simulateBorrow, asset, amount, max])
if (!modal || !asset) return null if (!modal || !asset) return null
return ( return (
<Modal <Modal
@ -257,7 +272,7 @@ function BorrowModal(props: Props) {
/> />
</div> </div>
<Text size='xs' className='text-white/50' tag='span'> <Text size='xs' className='text-white/50' tag='span'>
Borrowed Total Borrowed
</Text> </Text>
</div> </div>
</> </>
@ -294,6 +309,7 @@ function BorrowModal(props: Props) {
<TokenInputWithSlider <TokenInputWithSlider
asset={asset} asset={asset}
onChange={handleChange} onChange={handleChange}
onDebounce={onDebounce}
amount={amount} amount={amount}
max={max} max={max}
disabled={max.isZero()} disabled={max.isZero()}

View File

@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import { useEffect, useState } from 'react' import { useCallback, useState } from 'react'
import Button from 'components/common/Button' import Button from 'components/common/Button'
import Divider from 'components/common/Divider' import Divider from 'components/common/Divider'
@ -86,17 +86,10 @@ export default function WithdrawFromAccount(props: Props) {
useStore.setState({ fundAndWithdrawModal: null }) useStore.setState({ fundAndWithdrawModal: null })
} }
useEffect(() => { const onDebounce = useCallback(() => {
const coin = BNCoin.fromDenomAndBigNumber(currentAsset.denom, withdrawAmount.plus(debtAmount)) const coin = BNCoin.fromDenomAndBigNumber(currentAsset.denom, withdrawAmount.plus(debtAmount))
simulateWithdraw(withdrawWithBorrowing, coin) simulateWithdraw(withdrawWithBorrowing, coin)
}, [ }, [withdrawWithBorrowing, currentAsset.denom, debtAmount, simulateWithdraw, withdrawAmount])
amount,
withdrawWithBorrowing,
currentAsset.denom,
debtAmount,
simulateWithdraw,
withdrawAmount,
])
return ( return (
<> <>
@ -104,6 +97,7 @@ export default function WithdrawFromAccount(props: Props) {
<TokenInputWithSlider <TokenInputWithSlider
asset={currentAsset} asset={currentAsset}
onChange={onChangeAmount} onChange={onChangeAmount}
onDebounce={onDebounce}
onChangeAsset={(asset) => { onChangeAsset={(asset) => {
setAmount(BN_ZERO) setAmount(BN_ZERO)
setWithdrawWithBorrowing(false) setWithdrawWithBorrowing(false)

View File

@ -1,4 +1,4 @@
import { useCallback } from 'react' import { useCallback, useState } from 'react'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal' import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader' import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
@ -27,6 +27,7 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
const reclaim = useStore((s) => s.reclaim) const reclaim = useStore((s) => s.reclaim)
const { close } = useLendAndReclaimModal() const { close } = useLendAndReclaimModal()
const { simulateLending } = useUpdatedAccount(currentAccount) const { simulateLending } = useUpdatedAccount(currentAccount)
const [coin, setCoin] = useState<BNCoin>()
const { data, action } = config const { data, action } = config
const { asset } = data const { asset } = data
@ -37,12 +38,16 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
const handleAmountChange = useCallback( const handleAmountChange = useCallback(
(value: BigNumber) => { (value: BigNumber) => {
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value) setCoin(BNCoin.fromDenomAndBigNumber(asset.denom, value))
simulateLending(isLendAction, coin)
}, },
[asset.denom, isLendAction, simulateLending], [asset.denom],
) )
const onDebounce = useCallback(() => {
if (!coin) return
simulateLending(isLendAction, coin)
}, [coin, isLendAction, simulateLending])
const handleAction = useCallback( const handleAction = useCallback(
(value: BigNumber, isMax: boolean) => { (value: BigNumber, isMax: boolean) => {
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value) const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
@ -70,6 +75,7 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
onClose={close} onClose={close}
onAction={handleAction} onAction={handleAction}
onChange={handleAmountChange} onChange={handleAmountChange}
onDebounce={onDebounce}
/> />
) )
} }

View File

@ -95,10 +95,6 @@ export default function AccountFundContent(props: Props) {
} }
}, [baseBalance]) }, [baseBalance])
useEffect(() => {
simulateDeposits(isLending ? 'lend' : 'deposit', fundingAssets)
}, [isLending, fundingAssets, simulateDeposits])
useEffect(() => { useEffect(() => {
const currentSelectedDenom = fundingAssets.map((asset) => asset.denom) const currentSelectedDenom = fundingAssets.map((asset) => asset.denom)
@ -125,6 +121,10 @@ export default function AccountFundContent(props: Props) {
}) })
}, []) }, [])
const onDebounce = useCallback(() => {
simulateDeposits(isLending ? 'lend' : 'deposit', fundingAssets)
}, [isLending, fundingAssets, simulateDeposits])
const depositCapReachedCoins = useMemo(() => { const depositCapReachedCoins = useMemo(() => {
const depositCapReachedCoins: BNCoin[] = [] const depositCapReachedCoins: BNCoin[] = []
fundingAssets.forEach((asset) => { fundingAssets.forEach((asset) => {
@ -159,6 +159,7 @@ export default function AccountFundContent(props: Props) {
amount={coin.amount ?? BN_ZERO} amount={coin.amount ?? BN_ZERO}
isConfirming={isConfirming} isConfirming={isConfirming}
updateFundingAssets={updateFundingAssets} updateFundingAssets={updateFundingAssets}
onDebounce={onDebounce}
/> />
</div> </div>
) )

View File

@ -10,6 +10,7 @@ interface Props {
denom: string denom: string
isConfirming: boolean isConfirming: boolean
updateFundingAssets: (amount: BigNumber, denom: string) => void updateFundingAssets: (amount: BigNumber, denom: string) => void
onDebounce: () => void
} }
export default function AccountFundRow(props: Props) { export default function AccountFundRow(props: Props) {
@ -23,6 +24,7 @@ export default function AccountFundRow(props: Props) {
<TokenInputWithSlider <TokenInputWithSlider
asset={asset} asset={asset}
onChange={(amount) => props.updateFundingAssets(amount, asset.denom)} onChange={(amount) => props.updateFundingAssets(amount, asset.denom)}
onDebounce={props.onDebounce}
amount={props.amount} amount={props.amount}
max={balance} max={balance}
balances={props.balances} balances={props.balances}

View File

@ -11,7 +11,7 @@ interface Props {
} }
export default function BorrowActionButtons(props: Props) { export default function BorrowActionButtons(props: Props) {
const { asset, debt } = props.data const { asset, accountDebt } = props.data
const marketAssets = useMarketEnabledAssets() const marketAssets = useMarketEnabledAssets()
const currentAsset = marketAssets.find((a) => a.denom === asset.denom) const currentAsset = marketAssets.find((a) => a.denom === asset.denom)
@ -33,10 +33,10 @@ export default function BorrowActionButtons(props: Props) {
leftIcon={<Plus className='w-3' />} leftIcon={<Plus className='w-3' />}
onClick={borrowHandler} onClick={borrowHandler}
color='secondary' color='secondary'
text={debt ? 'Borrow more' : 'Borrow'} text={accountDebt ? 'Borrow more' : 'Borrow'}
className='min-w-40 text-center' className='text-center min-w-40'
/> />
{debt && ( {accountDebt && (
<Button color='tertiary' leftIcon={<HandCoins />} text='Repay' onClick={repayHandler} /> <Button color='tertiary' leftIcon={<HandCoins />} text='Repay' onClick={repayHandler} />
)} )}
</div> </div>

View File

@ -1,5 +1,6 @@
import classNames from 'classnames' import classNames from 'classnames'
import { ChangeEvent, useCallback } from 'react' import debounce from 'lodash.debounce'
import { ChangeEvent, useCallback, useMemo } from 'react'
import InputOverlay from 'components/common/LeverageSlider/InputOverlay' import InputOverlay from 'components/common/LeverageSlider/InputOverlay'
@ -13,20 +14,41 @@ type Props = {
marginThreshold?: number marginThreshold?: number
wrapperClassName?: string wrapperClassName?: string
onChange: (value: number) => void onChange: (value: number) => void
onDebounce?: () => void
onBlur?: () => void onBlur?: () => void
type: LeverageSliderType type: LeverageSliderType
} }
export type LeverageSliderType = 'margin' | 'long' | 'short' export type LeverageSliderType = 'margin' | 'long' | 'short'
function LeverageSlider(props: Props) { function LeverageSlider(props: Props) {
const { value, max, onChange, wrapperClassName, disabled, marginThreshold, onBlur, type } = props const {
value,
max,
onChange,
wrapperClassName,
disabled,
marginThreshold,
onBlur,
type,
onDebounce,
} = props
const min = props.min ?? 0 const min = props.min ?? 0
const debounceFunction = useMemo(
() =>
debounce(() => {
if (!onDebounce) return
onDebounce()
}, 250),
[onDebounce],
)
const handleOnChange = useCallback( const handleOnChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => { (event: ChangeEvent<HTMLInputElement>) => {
onChange(parseFloat(event.target.value)) onChange(parseFloat(event.target.value))
debounceFunction()
}, },
[onChange], [onChange, debounceFunction],
) )
const markPosPercent = 100 / (max / (marginThreshold ?? 1)) const markPosPercent = 100 / (max / (marginThreshold ?? 1))

View File

@ -1,4 +1,5 @@
import classNames from 'classnames' import classNames from 'classnames'
import debounce from 'lodash.debounce'
import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import Draggable from 'react-draggable' import Draggable from 'react-draggable'
@ -19,6 +20,7 @@ const colors = {
type Props = { type Props = {
value: number value: number
onChange: (value: number) => void onChange: (value: number) => void
onDebounce?: () => void
leverage?: { leverage?: {
current: number current: number
max: number max: number
@ -28,6 +30,7 @@ type Props = {
} }
export default function Slider(props: Props) { export default function Slider(props: Props) {
const { value, onChange, onDebounce, leverage, className, disabled } = props
const [showTooltip, setShowTooltip] = useToggle() const [showTooltip, setShowTooltip] = useToggle()
const [sliderRect, setSliderRect] = useState({ width: 0, left: 0, right: 0 }) const [sliderRect, setSliderRect] = useState({ width: 0, left: 0, right: 0 })
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
@ -52,6 +55,20 @@ export default function Slider(props: Props) {
} }
}, [sliderRect.left, sliderRect.right, sliderRect.width]) }, [sliderRect.left, sliderRect.right, sliderRect.width])
const debounceFunction = useMemo(
() =>
debounce(() => {
if (!onDebounce) return
onDebounce()
}, 250),
[onDebounce],
)
function handleOnChange(value: number) {
onChange(value)
debounceFunction()
}
function handleDrag(e: any) { function handleDrag(e: any) {
if (!isDragging) { if (!isDragging) {
setIsDragging(true) setIsDragging(true)
@ -60,24 +77,24 @@ export default function Slider(props: Props) {
const current: number = e.clientX const current: number = e.clientX
if (current < sliderRect.left) { if (current < sliderRect.left) {
props.onChange(0) handleOnChange(0)
return return
} }
if (current > sliderRect.right) { if (current > sliderRect.right) {
props.onChange(100) handleOnChange(100)
return return
} }
const value = Math.round(((current - sliderRect.left) / sliderRect.width) * 100) const currentValue = Math.round(((current - sliderRect.left) / sliderRect.width) * 100)
if (value !== props.value) { if (currentValue !== value) {
props.onChange(value) handleOnChange(currentValue)
} }
} }
function handleSliderClick(e: ChangeEvent<HTMLInputElement>) { function handleSliderClick(e: ChangeEvent<HTMLInputElement>) {
props.onChange(Number(e.target.value)) handleOnChange(Number(e.target.value))
} }
function handleShowTooltip() { function handleShowTooltip() {
@ -89,21 +106,22 @@ export default function Slider(props: Props) {
} }
function getActiveIndex() { function getActiveIndex() {
if (props.value >= 100) return '5' if (value >= 100) return '5'
if (props.value >= 75) return '4' if (value >= 75) return '4'
if (props.value >= 50) return '3' if (value >= 50) return '3'
if (props.value >= 25) return '2' if (value >= 25) return '2'
return '1' return '1'
} }
const DraggableElement: any = Draggable const DraggableElement: any = Draggable
const [positionOffset, position] = useMemo(() => { const [positionOffset, position] = useMemo(() => {
debounceFunction()
return [ return [
{ x: (props.value / 100) * -12, y: 0 }, { x: (value / 100) * -12, y: 0 },
{ x: (sliderRect.width / 100) * props.value, y: -2 }, { x: (sliderRect.width / 100) * value, y: -2 },
] ]
}, [props.value, sliderRect.width]) }, [value, sliderRect.width])
useEffect(() => { useEffect(() => {
handleSliderRect() handleSliderRect()
@ -115,60 +133,60 @@ export default function Slider(props: Props) {
ref={ref} ref={ref}
className={classNames( className={classNames(
'relative min-h-3 w-full transition-opacity', 'relative min-h-3 w-full transition-opacity',
props.className, className,
props.disabled && 'pointer-events-none', disabled && 'pointer-events-none',
)} )}
onMouseEnter={handleSliderRect} onMouseEnter={handleSliderRect}
> >
<input <input
type='range' type='range'
value={props.value} value={value}
onChange={handleSliderClick} onChange={handleSliderClick}
onMouseDown={handleShowTooltip} onMouseDown={handleShowTooltip}
className='absolute z-2 w-full hover:cursor-pointer appearance-none bg-transparent [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:appearance-none' className='absolute z-2 w-full hover:cursor-pointer appearance-none bg-transparent [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:appearance-none'
/> />
<div className='absolute flex items-center w-full gap-1.5'> <div className='absolute flex items-center w-full gap-1.5'>
<Mark <Mark
onClick={props.onChange} onClick={() => handleOnChange(0)}
value={0} value={0}
sliderValue={props.value} sliderValue={value}
disabled={props.disabled} disabled={disabled}
style={{ backgroundColor: colors['1'] }} style={{ backgroundColor: colors['1'] }}
/> />
<Track maxValue={23} sliderValue={props.value} bg='before:gradient-slider-1' /> <Track maxValue={23} sliderValue={value} bg='before:gradient-slider-1' />
<Mark <Mark
onClick={props.onChange} onClick={() => handleOnChange(25)}
value={25} value={25}
sliderValue={props.value} sliderValue={value}
disabled={props.disabled} disabled={disabled}
style={{ backgroundColor: colors['2'] }} style={{ backgroundColor: colors['2'] }}
/> />
<Track maxValue={48} sliderValue={props.value} bg='before:gradient-slider-2' /> <Track maxValue={48} sliderValue={value} bg='before:gradient-slider-2' />
<Mark <Mark
onClick={props.onChange} onClick={() => handleOnChange(50)}
value={50} value={50}
sliderValue={props.value} sliderValue={value}
disabled={props.disabled} disabled={disabled}
style={{ backgroundColor: colors['3'] }} style={{ backgroundColor: colors['3'] }}
/> />
<Track maxValue={73} sliderValue={props.value} bg='before:gradient-slider-3' /> <Track maxValue={73} sliderValue={value} bg='before:gradient-slider-3' />
<Mark <Mark
onClick={props.onChange} onClick={() => handleOnChange(75)}
value={75} value={75}
sliderValue={props.value} sliderValue={value}
disabled={props.disabled} disabled={disabled}
style={{ backgroundColor: colors['4'] }} style={{ backgroundColor: colors['4'] }}
/> />
<Track maxValue={98} sliderValue={props.value} bg='before:gradient-slider-4' /> <Track maxValue={98} sliderValue={value} bg='before:gradient-slider-4' />
<Mark <Mark
onClick={props.onChange} onClick={() => handleOnChange(100)}
value={100} value={100}
sliderValue={props.value} sliderValue={value}
disabled={props.disabled} disabled={disabled}
style={{ backgroundColor: colors['5'] }} style={{ backgroundColor: colors['5'] }}
/> />
</div> </div>
{!props.disabled && ( {!disabled && (
<div onMouseEnter={handleShowTooltip} onMouseLeave={handleHideTooltip}> <div onMouseEnter={handleShowTooltip} onMouseLeave={handleHideTooltip}>
<DraggableElement <DraggableElement
nodeRef={nodeRef} nodeRef={nodeRef}
@ -187,12 +205,12 @@ export default function Slider(props: Props) {
)} )}
style={{ background: colors[getActiveIndex()] }} style={{ background: colors[getActiveIndex()] }}
/> />
{props.leverage ? ( {leverage ? (
<div className='pt-2.5'> <div className='pt-2.5'>
<LeverageLabel <LeverageLabel
leverage={props.leverage.current} leverage={leverage.current}
decimals={1} decimals={1}
className={props.leverage.current >= 10 ? '-translate-x-2' : '-translate-x-1'} className={leverage.current >= 10 ? '-translate-x-2' : '-translate-x-1'}
/> />
</div> </div>
) : ( ) : (
@ -203,7 +221,7 @@ export default function Slider(props: Props) {
'absolute h-2 -translate-x-1/2 -bottom-2 left-1/2 -z-1 text-fuchsia', 'absolute h-2 -translate-x-1/2 -bottom-2 left-1/2 -z-1 text-fuchsia',
)} )}
/> />
{props.value.toFixed(0)}% {value.toFixed(0)}%
</div> </div>
) )
)} )}
@ -212,19 +230,19 @@ export default function Slider(props: Props) {
</div> </div>
)} )}
</div> </div>
{props.leverage && ( {leverage && (
<div className='flex justify-between pt-2'> <div className='flex justify-between pt-2'>
<LeverageLabel <LeverageLabel
leverage={1} leverage={1}
decimals={0} decimals={0}
className='-translate-x-0.5' className='-translate-x-0.5'
style={{ opacity: props.value < 5 ? 0 : 1 }} style={{ opacity: value < 5 ? 0 : 1 }}
/> />
<LeverageLabel <LeverageLabel
leverage={props.leverage.max || 1} leverage={leverage.max || 1}
decimals={0} decimals={0}
className='translate-x-1.5' className='translate-x-1.5'
style={{ opacity: props.value > 95 ? 0 : 1 }} style={{ opacity: value > 95 ? 0 : 1 }}
/> />
</div> </div>
)} )}

View File

@ -12,6 +12,7 @@ interface Props {
asset: Asset asset: Asset
max: BigNumber max: BigNumber
onChange: (amount: BigNumber) => void onChange: (amount: BigNumber) => void
onDebounce?: () => void
accountId?: string accountId?: string
balances?: BNCoin[] balances?: BNCoin[]
className?: string className?: string
@ -37,6 +38,11 @@ export default function TokenInputWithSlider(props: Props) {
props.onChange(newAmount) props.onChange(newAmount)
} }
function onDebounce() {
if (!props.onDebounce) return
props.onDebounce()
}
function onChangeAmount(newAmount: BigNumber) { function onChangeAmount(newAmount: BigNumber) {
setAmount(newAmount) setAmount(newAmount)
setPercentage(BN(newAmount).dividedBy(props.max).multipliedBy(100).toNumber()) setPercentage(BN(newAmount).dividedBy(props.max).multipliedBy(100).toNumber())
@ -76,6 +82,7 @@ export default function TokenInputWithSlider(props: Props) {
<Slider <Slider
value={percentage || 0} value={percentage || 0}
onChange={(value) => onChangeSlider(value)} onChange={(value) => onChangeSlider(value)}
onDebounce={onDebounce}
disabled={props.disabled} disabled={props.disabled}
leverage={props.leverage} leverage={props.leverage}
/> />

View File

@ -61,6 +61,10 @@ export function PerpsModule() {
previousTradeDirection, previousTradeDirection,
]) ])
const onDebounce = useCallback(() => {
// TODO: Implement debounced simulation
}, [])
const setLeverage = useCallback((leverage: number) => { const setLeverage = useCallback((leverage: number) => {
// TODO: Implement leverage setting // TODO: Implement leverage setting
}, []) }, [])
@ -116,6 +120,7 @@ export function PerpsModule() {
max={10} max={10}
value={leverage} value={leverage}
onChange={setLeverage} onChange={setLeverage}
onDebounce={onDebounce}
type={tradeDirection} type={tradeDirection}
/> />
<LeverageButtons /> <LeverageButtons />

View File

@ -201,7 +201,7 @@ export default function SwapForm(props: Props) {
isAutoLendEnabled && !isAutoRepayChecked ? 'lend' : 'deposit', isAutoLendEnabled && !isAutoRepayChecked ? 'lend' : 'deposit',
isAutoRepayChecked, isAutoRepayChecked,
) )
}, 100), }, 250),
[simulateTrade, isAutoLendEnabled, isAutoRepayChecked], [simulateTrade, isAutoLendEnabled, isAutoRepayChecked],
) )