Mp 2544 Vault deposit (#240)
* Show guages on deposit * Finish VaultDeposit * resolve PR comments
This commit is contained in:
parent
ac7e82b0a4
commit
9d09ebdf77
@ -12,29 +12,39 @@ export interface Item {
|
||||
title: string
|
||||
renderContent: () => React.ReactNode
|
||||
isOpen?: boolean
|
||||
subTitle?: string | React.ReactNode
|
||||
toggleOpen: (index: number) => void
|
||||
}
|
||||
|
||||
export default function AccordionContent(props: Props) {
|
||||
const { title, renderContent, isOpen, subTitle, toggleOpen } = props.item
|
||||
|
||||
const shouldShowSubTitle = subTitle && !isOpen
|
||||
|
||||
return (
|
||||
<div key={props.item.title} className='group border-b-white/10 [&:not(:last-child)]:border-b'>
|
||||
<div key={title} className='group border-b-white/10 [&:not(:last-child)]:border-b'>
|
||||
<div
|
||||
onClick={() => props.item.toggleOpen(props.index)}
|
||||
onClick={() => toggleOpen(props.index)}
|
||||
className={classNames(
|
||||
'mb-0 flex cursor-pointer items-center justify-between border-t border-white/10 bg-white/10 p-4 text-white',
|
||||
'group-[&:first-child]:border-t-0 group-[[open]]:border-b',
|
||||
'[&::marker]:hidden [&::marker]:content-[""]',
|
||||
props.item.isOpen && 'border-b [&:first-child]:border-t-0',
|
||||
isOpen && 'border-b [&:first-child]:border-t-0',
|
||||
)}
|
||||
>
|
||||
<Text>{props.item.title}</Text>
|
||||
<div>
|
||||
<Text>{title}</Text>
|
||||
{shouldShowSubTitle && (
|
||||
<Text size='xs' className='mt-1 text-white/60'>
|
||||
{subTitle}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
<div className='block pr-1 group-[[open]]:hidden'>
|
||||
{props.item.isOpen ? <ChevronRight /> : <ChevronDown />}
|
||||
{isOpen ? <ChevronDown /> : <ChevronRight />}
|
||||
</div>
|
||||
</div>
|
||||
{props.item.isOpen && (
|
||||
<div className='bg-white/5 transition-[padding]'>{props.item.renderContent()}</div>
|
||||
)}
|
||||
{isOpen && <div className='bg-white/5 transition-[padding]'>{renderContent()}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export default function AccountDetails() {
|
||||
className='fixed right-4 top-[89px] w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky'
|
||||
>
|
||||
<div className='flex w-full flex-wrap justify-center py-4'>
|
||||
<Gauge tooltip='Health Factor' value={0.2} icon={<Heart />} />
|
||||
<Gauge tooltip='Health Factor' percentage={20} icon={<Heart />} />
|
||||
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
|
||||
Health
|
||||
</Text>
|
||||
|
@ -3,33 +3,42 @@ import { ReactElement, ReactNode } from 'react'
|
||||
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import useStore from 'store'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
|
||||
interface Props {
|
||||
tooltip: string | ReactNode
|
||||
strokeColor?: string
|
||||
strokeWidth?: number
|
||||
background?: string
|
||||
diameter?: number
|
||||
value: number
|
||||
label?: string
|
||||
percentage: number
|
||||
labelClassName?: string
|
||||
icon?: ReactElement
|
||||
}
|
||||
|
||||
export const Gauge = ({
|
||||
background = '#FFFFFF22',
|
||||
strokeColor,
|
||||
strokeWidth = 4,
|
||||
diameter = 40,
|
||||
value = 0,
|
||||
percentage = 0,
|
||||
tooltip,
|
||||
icon,
|
||||
labelClassName,
|
||||
}: Props) => {
|
||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||
const radius = 16
|
||||
const percentage = value * 100
|
||||
const percentageValue = percentage > 100 ? 100 : percentage < 0 ? 0 : percentage
|
||||
const circlePercent = 100 - percentageValue
|
||||
|
||||
return (
|
||||
<Tooltip type='info' content={tooltip}>
|
||||
<div className={classNames('relative', `w-${diameter / 4} h-${diameter / 4}`)}>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative grid place-items-center',
|
||||
`w-${diameter / 4} h-${diameter / 4}`,
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
viewBox='2 -2 28 36'
|
||||
width={diameter}
|
||||
@ -37,15 +46,17 @@ export const Gauge = ({
|
||||
style={{ transform: 'rotate(-90deg)' }}
|
||||
className='absolute left-0 top-0'
|
||||
>
|
||||
<linearGradient id='gradient'>
|
||||
<stop stopColor='rgba(255, 160, 187)' offset='0%'></stop>
|
||||
<stop stopColor='rgba(186, 8, 189)' offset='50%'></stop>
|
||||
<stop stopColor='rgba(255, 160, 187)' offset='100%'></stop>
|
||||
</linearGradient>
|
||||
{!strokeColor && (
|
||||
<linearGradient id='gradient'>
|
||||
<stop stopColor='rgba(255, 160, 187)' offset='0%'></stop>
|
||||
<stop stopColor='rgba(186, 8, 189)' offset='50%'></stop>
|
||||
<stop stopColor='rgba(255, 160, 187)' offset='100%'></stop>
|
||||
</linearGradient>
|
||||
)}
|
||||
<circle
|
||||
fill='none'
|
||||
stroke={background}
|
||||
strokeWidth={4}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeDashoffset='0'
|
||||
r={radius}
|
||||
cx={radius}
|
||||
@ -57,8 +68,8 @@ export const Gauge = ({
|
||||
cx={radius}
|
||||
cy={radius}
|
||||
fill='transparent'
|
||||
stroke='url(#gradient)'
|
||||
strokeWidth={5}
|
||||
stroke={strokeColor ? strokeColor : `url(#gradient)`}
|
||||
strokeWidth={strokeWidth}
|
||||
strokeDashoffset={circlePercent}
|
||||
strokeDasharray='100'
|
||||
pathLength='100'
|
||||
@ -74,6 +85,12 @@ export const Gauge = ({
|
||||
{icon}
|
||||
</div>
|
||||
)}
|
||||
<FormattedNumber
|
||||
className={classNames(labelClassName, 'text-2xs')}
|
||||
amount={Math.round(percentage)}
|
||||
options={{ maxDecimals: 0, minDecimals: 0 }}
|
||||
animate
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Divider from 'components/Divider'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
|
||||
import Slider from 'components/Slider'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
@ -12,89 +12,117 @@ import TokenInput from 'components/TokenInput'
|
||||
import usePrice from 'hooks/usePrice'
|
||||
import { getAmount } from 'utils/accounts'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { Gauge } from 'components/Gauge'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
primaryAmount: BigNumber
|
||||
secondaryAmount: BigNumber
|
||||
primaryAsset: Asset
|
||||
secondaryAsset: Asset
|
||||
account: Account
|
||||
onChangeDeposits: (deposits: Map<string, BigNumber>) => void
|
||||
isCustomRatio: boolean
|
||||
onChangeIsCustomRatio: (isCustomRatio: boolean) => void
|
||||
onChangePrimaryAmount: (amount: BigNumber) => void
|
||||
onChangeSecondaryAmount: (amount: BigNumber) => void
|
||||
toggleOpen: (index: number) => void
|
||||
}
|
||||
|
||||
export default function VaultDeposit(props: Props) {
|
||||
const [isCustomAmount, setIsCustomAmount] = useState(false)
|
||||
const [percentage, setPercentage] = useState(0)
|
||||
const [deposits, setDeposits] = useState<Map<string, BigNumber>>(new Map())
|
||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||
|
||||
const availablePrimaryAmount = getAmount(props.primaryAsset.denom, props.account.deposits)
|
||||
const availableSecondaryAmount = getAmount(props.secondaryAsset.denom, props.account.deposits)
|
||||
const primaryPrice = usePrice(props.primaryAsset.denom)
|
||||
const secondaryPrice = usePrice(props.secondaryAsset.denom)
|
||||
|
||||
const maxAssetValueNonCustom = BN(
|
||||
Math.min(availablePrimaryAmount.toNumber(), availableSecondaryAmount.toNumber()),
|
||||
const primaryValue = useMemo(
|
||||
() => props.primaryAmount.times(primaryPrice),
|
||||
[props.primaryAmount, primaryPrice],
|
||||
)
|
||||
const primaryMax = isCustomAmount
|
||||
? availablePrimaryAmount
|
||||
: maxAssetValueNonCustom.dividedBy(primaryPrice)
|
||||
const secondaryMax = isCustomAmount
|
||||
? availableSecondaryAmount
|
||||
: maxAssetValueNonCustom.dividedBy(secondaryPrice)
|
||||
const secondaryValue = useMemo(
|
||||
() => props.secondaryAmount.times(secondaryPrice),
|
||||
[props.secondaryAmount, secondaryPrice],
|
||||
)
|
||||
const totalValue = useMemo(
|
||||
() => primaryValue.plus(secondaryValue),
|
||||
[primaryValue, secondaryValue],
|
||||
)
|
||||
|
||||
const primaryValuePercentage = useMemo(
|
||||
() => primaryValue.div(totalValue).times(100).decimalPlaces(2).toNumber() || 50,
|
||||
[primaryValue, totalValue],
|
||||
)
|
||||
const secondaryValuePercentage = useMemo(
|
||||
() => new BigNumber(100).minus(primaryValuePercentage).decimalPlaces(2).toNumber() || 50,
|
||||
[primaryValuePercentage],
|
||||
)
|
||||
|
||||
const maxAssetValueNonCustom = useMemo(
|
||||
() =>
|
||||
BN(
|
||||
Math.min(
|
||||
availablePrimaryAmount.times(primaryPrice).toNumber(),
|
||||
availableSecondaryAmount.times(secondaryPrice).toNumber(),
|
||||
),
|
||||
),
|
||||
[availablePrimaryAmount, primaryPrice, availableSecondaryAmount, secondaryPrice],
|
||||
)
|
||||
const primaryMax = useMemo(
|
||||
() =>
|
||||
props.isCustomRatio ? availablePrimaryAmount : maxAssetValueNonCustom.dividedBy(primaryPrice),
|
||||
[props.isCustomRatio, availablePrimaryAmount, primaryPrice, maxAssetValueNonCustom],
|
||||
)
|
||||
const secondaryMax = useMemo(
|
||||
() =>
|
||||
props.isCustomRatio
|
||||
? availableSecondaryAmount
|
||||
: maxAssetValueNonCustom.dividedBy(secondaryPrice),
|
||||
[props.isCustomRatio, availableSecondaryAmount, secondaryPrice, maxAssetValueNonCustom],
|
||||
)
|
||||
|
||||
const [percentage, setPercentage] = useState(
|
||||
primaryValue.dividedBy(maxAssetValueNonCustom).times(100).decimalPlaces(0).toNumber(),
|
||||
)
|
||||
const disableInput =
|
||||
(availablePrimaryAmount.isZero() || availableSecondaryAmount.isZero()) && !props.isCustomRatio
|
||||
|
||||
function handleSwitch() {
|
||||
const isCustomAmountNew = !isCustomAmount
|
||||
if (!isCustomAmountNew) {
|
||||
setDeposits((deposits) => {
|
||||
deposits.clear()
|
||||
return new Map(deposits)
|
||||
})
|
||||
const isCustomRatioNew = !props.isCustomRatio
|
||||
if (!isCustomRatioNew) {
|
||||
props.onChangePrimaryAmount(BN(0))
|
||||
props.onChangeSecondaryAmount(BN(0))
|
||||
setPercentage(0)
|
||||
}
|
||||
setIsCustomAmount(isCustomAmountNew)
|
||||
props.onChangeIsCustomRatio(isCustomRatioNew)
|
||||
}
|
||||
|
||||
function onChangePrimaryDeposit(amount: BigNumber) {
|
||||
onChangeDeposit(props.primaryAsset.denom, amount)
|
||||
if (!isCustomAmount) {
|
||||
onChangeDeposit(
|
||||
props.secondaryAsset.denom,
|
||||
secondaryMax.multipliedBy(amount.dividedBy(primaryMax)),
|
||||
)
|
||||
if (amount.isGreaterThan(primaryMax)) {
|
||||
amount = primaryMax
|
||||
}
|
||||
props.onChangePrimaryAmount(amount)
|
||||
setPercentage(amount.dividedBy(primaryMax).times(100).decimalPlaces(0).toNumber())
|
||||
if (!props.isCustomRatio) {
|
||||
props.onChangeSecondaryAmount(secondaryMax.multipliedBy(amount.dividedBy(primaryMax)))
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeSecondaryDeposit(amount: BigNumber) {
|
||||
onChangeDeposit(props.secondaryAsset.denom, amount)
|
||||
if (!isCustomAmount) {
|
||||
onChangeDeposit(
|
||||
props.primaryAsset.denom,
|
||||
primaryMax.multipliedBy(amount.dividedBy(secondaryMax)),
|
||||
)
|
||||
if (amount.isGreaterThan(secondaryMax)) {
|
||||
amount = secondaryMax
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeDeposit(denom: string, amount: BigNumber) {
|
||||
if (amount.isZero()) {
|
||||
return setDeposits((deposits) => {
|
||||
deposits.delete(denom)
|
||||
return new Map(deposits)
|
||||
})
|
||||
props.onChangeSecondaryAmount(amount)
|
||||
setPercentage(amount.dividedBy(secondaryMax).times(100).decimalPlaces(0).toNumber())
|
||||
if (!props.isCustomRatio) {
|
||||
props.onChangePrimaryAmount(primaryMax.multipliedBy(amount.dividedBy(secondaryMax)))
|
||||
}
|
||||
|
||||
setDeposits((deposits) => {
|
||||
deposits.set(denom, amount)
|
||||
props.onChangeDeposits(deposits)
|
||||
return new Map(deposits)
|
||||
})
|
||||
}
|
||||
|
||||
function onChangeSlider(value: number) {
|
||||
setPercentage(value)
|
||||
setDeposits((deposits) => {
|
||||
deposits.set(props.primaryAsset.denom, primaryMax.multipliedBy(value / 100))
|
||||
deposits.set(props.secondaryAsset.denom, secondaryMax.multipliedBy(value / 100))
|
||||
return new Map(deposits)
|
||||
})
|
||||
props.onChangePrimaryAmount(primaryMax.multipliedBy(value / 100))
|
||||
props.onChangeSecondaryAmount(secondaryMax.multipliedBy(value / 100))
|
||||
}
|
||||
|
||||
function getWarningText(asset: Asset) {
|
||||
@ -102,39 +130,89 @@ export default function VaultDeposit(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex h-full flex-col justify-between gap-6 p-4'>
|
||||
<TokenInput
|
||||
onChange={onChangePrimaryDeposit}
|
||||
amount={deposits.get(props.primaryAsset.denom) ?? BN(0)}
|
||||
max={primaryMax}
|
||||
maxText='Balance'
|
||||
asset={props.primaryAsset}
|
||||
warning={primaryMax.isZero() ? getWarningText(props.primaryAsset) : undefined}
|
||||
/>
|
||||
{!isCustomAmount && <Slider value={percentage} onChange={onChangeSlider} />}
|
||||
<TokenInput
|
||||
onChange={onChangeSecondaryDeposit}
|
||||
amount={deposits.get(props.secondaryAsset.denom) ?? BN(0)}
|
||||
max={secondaryMax}
|
||||
maxText='Balance'
|
||||
asset={props.secondaryAsset}
|
||||
warning={secondaryMax.isZero() ? getWarningText(props.secondaryAsset) : undefined}
|
||||
/>
|
||||
<Divider />
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>Custom amount</Text>
|
||||
<Switch checked={isCustomAmount} onChange={handleSwitch} name='customAmount' />
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex gap-4 p-4'>
|
||||
<div className='flex flex-col items-center justify-between gap-1 pb-[30px] pt-2'>
|
||||
<Gauge
|
||||
percentage={primaryValuePercentage}
|
||||
tooltip={`${primaryValuePercentage}% of value is ${props.primaryAsset.symbol}`}
|
||||
labelClassName='text-martian-red'
|
||||
diameter={32}
|
||||
strokeColor='#FF645F'
|
||||
strokeWidth={3}
|
||||
/>
|
||||
<div className='h-full w-[1px] rounded-xl bg-white/10'></div>
|
||||
<Gauge
|
||||
percentage={secondaryValuePercentage}
|
||||
tooltip={`${secondaryValuePercentage}% of value is ${props.secondaryAsset.symbol}`}
|
||||
labelClassName='text-martian-red'
|
||||
diameter={32}
|
||||
strokeColor='#FF645F'
|
||||
strokeWidth={3}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex h-full flex-grow flex-col justify-between gap-6'>
|
||||
<TokenInput
|
||||
onChange={onChangePrimaryDeposit}
|
||||
amount={props.primaryAmount}
|
||||
max={availablePrimaryAmount}
|
||||
maxText='Balance'
|
||||
asset={props.primaryAsset}
|
||||
warning={
|
||||
availablePrimaryAmount.isZero() ? getWarningText(props.primaryAsset) : undefined
|
||||
}
|
||||
disabled={disableInput}
|
||||
/>
|
||||
{!props.isCustomRatio && (
|
||||
<Slider value={percentage} onChange={onChangeSlider} disabled={disableInput} />
|
||||
)}
|
||||
<TokenInput
|
||||
onChange={onChangeSecondaryDeposit}
|
||||
amount={props.secondaryAmount}
|
||||
max={availableSecondaryAmount}
|
||||
maxText='Balance'
|
||||
asset={props.secondaryAsset}
|
||||
warning={
|
||||
availableSecondaryAmount.isZero() ? getWarningText(props.secondaryAsset) : undefined
|
||||
}
|
||||
disabled={disableInput}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>{`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Position Value`}</Text>
|
||||
<FormattedNumber amount={0} options={{ prefix: '$' }} />
|
||||
<div className='flex flex-col gap-6 p-4 pt-2'>
|
||||
{disableInput ? (
|
||||
<div>
|
||||
<Divider />
|
||||
<div className='flex items-center gap-4 py-4'>
|
||||
<div className='w-5'>
|
||||
<ExclamationMarkCircled className='w-5 gap-3 text-white' />
|
||||
</div>
|
||||
<Text size='xs'>
|
||||
You currently have little to none of one asset. Toggle custom ratio to supply your
|
||||
assets asymmetrically.
|
||||
</Text>
|
||||
</div>
|
||||
<Divider />
|
||||
</div>
|
||||
) : (
|
||||
<Divider />
|
||||
)}
|
||||
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>Custom ratio</Text>
|
||||
<Switch checked={props.isCustomRatio} onChange={handleSwitch} name='customRatio' />
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>{`${props.primaryAsset.symbol}-${props.secondaryAsset.symbol} Deposit Value`}</Text>
|
||||
<DisplayCurrency coin={{ denom: baseCurrency.denom, amount: totalValue.toString() }} />
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => props.toggleOpen(1)}
|
||||
className='w-full'
|
||||
text='Continue'
|
||||
rightIcon={<ArrowRight />}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => props.toggleOpen(1)}
|
||||
className='w-full'
|
||||
text='Continue'
|
||||
rightIcon={<ArrowRight />}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
49
src/components/Modals/vault/VaultDepositSubTitle.tsx
Normal file
49
src/components/Modals/vault/VaultDepositSubTitle.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import usePrice from 'hooks/usePrice'
|
||||
import useStore from 'store'
|
||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
primaryAmount: BigNumber
|
||||
secondaryAmount: BigNumber
|
||||
primaryAsset: Asset
|
||||
secondaryAsset: Asset
|
||||
}
|
||||
|
||||
export default function VaultDepositSubTitle(props: Props) {
|
||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||
const primaryPrice = usePrice(props.primaryAsset.denom)
|
||||
const secondaryPrice = usePrice(props.secondaryAsset.denom)
|
||||
const primaryText = formatAmountWithSymbol({
|
||||
denom: props.primaryAsset.denom,
|
||||
amount: props.primaryAmount.toString(),
|
||||
})
|
||||
const secondaryText = formatAmountWithSymbol({
|
||||
denom: props.secondaryAsset.denom,
|
||||
amount: props.secondaryAmount.toString(),
|
||||
})
|
||||
|
||||
const positionValue = props.primaryAmount
|
||||
.times(primaryPrice)
|
||||
.plus(props.secondaryAmount.times(secondaryPrice))
|
||||
.toNumber()
|
||||
|
||||
const showPrimaryText = !props.primaryAmount.isZero()
|
||||
const showSecondaryText = !props.secondaryAmount.isZero()
|
||||
|
||||
return (
|
||||
<>
|
||||
{showPrimaryText && primaryText}
|
||||
{showPrimaryText && showSecondaryText && ' + '}
|
||||
{showSecondaryText && secondaryText}
|
||||
{(showPrimaryText || showSecondaryText) && (
|
||||
<>
|
||||
{` = `}
|
||||
<DisplayCurrency coin={{ denom: baseCurrency.denom, amount: positionValue.toString() }} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import Accordion from 'components/Accordion'
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import VaultBorrowings from 'components/Modals/vault/VaultBorrowings'
|
||||
import VaultDeposit from 'components/Modals/vault/VaultDeposit'
|
||||
import VaultDepositSubTitle from 'components/Modals/vault/VaultDepositSubTitle'
|
||||
import useIsOpenArray from 'hooks/useIsOpenArray'
|
||||
|
||||
import VaultDeposit from './VaultDeposit'
|
||||
import VaultBorrowings from './VaultBorrowings'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
vault: Vault
|
||||
@ -17,7 +18,23 @@ interface Props {
|
||||
|
||||
export default function VaultModalContent(props: Props) {
|
||||
const [isOpen, toggleOpen] = useIsOpenArray(2, false)
|
||||
const [deposits, setDeposits] = useState<Map<string, BigNumber>>(new Map())
|
||||
const [primaryAmount, setPrimaryAmount] = useState<BigNumber>(BN(0))
|
||||
const [secondaryAmount, setSecondaryAmount] = useState<BigNumber>(BN(0))
|
||||
const [isCustomRatio, setIsCustomRatio] = useState(false)
|
||||
|
||||
const onChangePrimaryAmount = useCallback(
|
||||
(amount: BigNumber) => setPrimaryAmount(amount),
|
||||
[setPrimaryAmount],
|
||||
)
|
||||
const onChangeSecondaryAmount = useCallback(
|
||||
(amount: BigNumber) => setSecondaryAmount(amount),
|
||||
[setSecondaryAmount],
|
||||
)
|
||||
|
||||
const onChangeIsCustomRatio = useCallback(
|
||||
(isCustomRatio: boolean) => setIsCustomRatio(isCustomRatio),
|
||||
[setIsCustomRatio],
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='flex flex-grow items-start gap-6 p-6'>
|
||||
@ -26,14 +43,27 @@ export default function VaultModalContent(props: Props) {
|
||||
{
|
||||
renderContent: () => (
|
||||
<VaultDeposit
|
||||
onChangeDeposits={(deposits) => setDeposits(deposits)}
|
||||
primaryAmount={primaryAmount}
|
||||
secondaryAmount={secondaryAmount}
|
||||
onChangePrimaryAmount={onChangePrimaryAmount}
|
||||
onChangeSecondaryAmount={onChangeSecondaryAmount}
|
||||
primaryAsset={props.primaryAsset}
|
||||
secondaryAsset={props.secondaryAsset}
|
||||
account={props.account}
|
||||
toggleOpen={toggleOpen}
|
||||
isCustomRatio={isCustomRatio}
|
||||
onChangeIsCustomRatio={onChangeIsCustomRatio}
|
||||
/>
|
||||
),
|
||||
title: 'Deposit',
|
||||
subTitle: (
|
||||
<VaultDepositSubTitle
|
||||
primaryAmount={primaryAmount}
|
||||
secondaryAmount={secondaryAmount}
|
||||
primaryAsset={props.primaryAsset}
|
||||
secondaryAsset={props.secondaryAsset}
|
||||
/>
|
||||
),
|
||||
isOpen: isOpen[0],
|
||||
toggleOpen: (index: number) => toggleOpen(index),
|
||||
},
|
||||
|
@ -25,16 +25,18 @@ interface Props {
|
||||
export default function NumberInput(props: Props) {
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
const cursorRef = React.useRef(0)
|
||||
// const max = props.max ? demagnify(props.max, props.asset) : undefined
|
||||
|
||||
const [formattedAmount, setFormattedAmount] = useState(
|
||||
props.amount.shiftedBy(-1 * props.asset.decimals).toString(),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (props.amount.isZero()) return setFormattedAmount('')
|
||||
|
||||
setFormattedAmount(
|
||||
formatValue(props.amount.toNumber(), {
|
||||
decimals: props.asset.decimals,
|
||||
minDecimals: 0,
|
||||
maxDecimals: props.asset.decimals,
|
||||
thousandSeparator: false,
|
||||
}),
|
||||
@ -95,6 +97,11 @@ export default function NumberInput(props: Props) {
|
||||
const isTooLong = props.maxLength !== undefined && numberCount > props.maxLength
|
||||
const exceedsMaxDecimals = props.maxDecimals !== undefined && decimals > props.maxDecimals
|
||||
|
||||
if (formattedAmount === '') {
|
||||
updateValues('0', BN(0))
|
||||
return
|
||||
}
|
||||
|
||||
if (isNegative && !props.allowNegative) return
|
||||
|
||||
if (isSeparator && formattedAmount.length === 1) {
|
||||
@ -140,7 +147,7 @@ export default function NumberInput(props: Props) {
|
||||
<input
|
||||
ref={inputRef}
|
||||
type='text'
|
||||
value={formattedAmount === '0' ? '' : formattedAmount}
|
||||
value={formattedAmount === '0' ? '0' : formattedAmount}
|
||||
onFocus={onInputFocus}
|
||||
onChange={(e) => onInputChange(e.target.value)}
|
||||
onBlur={props.onBlur}
|
||||
|
@ -83,7 +83,7 @@ export default function Slider(props: Props) {
|
||||
className={classNames(
|
||||
'relative min-h-3 w-full transition-opacity',
|
||||
props.className,
|
||||
props.disabled && 'pointer-events-none opacity-50',
|
||||
props.disabled && 'pointer-events-none',
|
||||
)}
|
||||
onMouseEnter={handleSliderRect}
|
||||
>
|
||||
@ -95,15 +95,40 @@ export default function Slider(props: Props) {
|
||||
className='absolute z-2 w-full 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 w-full items-center gap-1'>
|
||||
<Mark onClick={props.onChange} value={0} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={0}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={23} sliderValue={props.value} />
|
||||
<Mark onClick={props.onChange} value={25} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={25}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={48} sliderValue={props.value} />
|
||||
<Mark onClick={props.onChange} value={50} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={50}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={73} sliderValue={props.value} />
|
||||
<Mark onClick={props.onChange} value={75} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={75}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<Track maxValue={98} sliderValue={props.value} />
|
||||
<Mark onClick={props.onChange} value={100} sliderValue={props.value} />
|
||||
<Mark
|
||||
onClick={props.onChange}
|
||||
value={100}
|
||||
sliderValue={props.value}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</div>
|
||||
<div onMouseEnter={handleShowTooltip} onMouseLeave={handleHideTooltip}>
|
||||
<DraggableElement
|
||||
@ -139,6 +164,7 @@ interface MarkProps {
|
||||
value: number
|
||||
sliderValue: number
|
||||
onClick: (value: number) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
function Mark(props: MarkProps) {
|
||||
@ -148,6 +174,7 @@ function Mark(props: MarkProps) {
|
||||
className={`z-20 h-3 w-3 rotate-45 rounded-xs border-[1px] border-white/20 hover:border-[2px] hover:border-white ${
|
||||
props.sliderValue >= props.value ? 'bg-martian-red hover:border-white' : 'bg-grey-medium'
|
||||
}`}
|
||||
disabled={props.disabled}
|
||||
></button>
|
||||
)
|
||||
}
|
||||
|
@ -45,11 +45,7 @@ export default function TokenInput(props: Props) {
|
||||
return (
|
||||
<div
|
||||
data-testid='token-input-component'
|
||||
className={classNames(
|
||||
'flex w-full flex-col gap-2 transition-opacity',
|
||||
props.className,
|
||||
props.disabled && 'pointer-events-none opacity-50',
|
||||
)}
|
||||
className={classNames('flex w-full flex-col gap-2 transition-opacity', props.className)}
|
||||
>
|
||||
<div
|
||||
data-testid='token-input-wrapper'
|
||||
@ -109,9 +105,10 @@ export default function TokenInput(props: Props) {
|
||||
<Button
|
||||
dataTestId='token-input-max-button'
|
||||
color='tertiary'
|
||||
className='h-4 bg-white/20 px-1.5 py-0.5 text-2xs'
|
||||
className='!h-4 !min-h-0 bg-white/20 !px-2 !py-0.5 text-2xs'
|
||||
variant='transparent'
|
||||
onClick={onMaxBtnClick}
|
||||
disabled={props.disabled}
|
||||
>
|
||||
MAX
|
||||
</Button>
|
||||
|
Loading…
Reference in New Issue
Block a user