0ae70899e5
* tidy: refactor text * feat: added unstyled select * tidy: useToggle * tidy: useToggle * MP-2344: first unstyled version of Select * fix: fixed the build * MP-2344: progress on the Select * MP-2344: almost finished the Select * env: update wallet-connector * fix: relative imports * env: started adding osmo-test-5 contracts * refactor: rename stargate.ts to d.ts * env: adjusted tsconfig.json * feat: updated modals to use the dialog element * env: added mainnet config * env: enabled osmosis-1 * tidy: refactor * fix: fixed decimals * fix: fixed the NaN issue for ETH * fix: fixed price calculations for large decimals * feat: finished conversion to <dialog> * fix: fixed some logic issues * fix: layout fix * fix: fixed token slider and input * tidy: format * fix: added currentAccount hook * Mp 2345 withdraw from credit account flow (#180) * MP-2345: created the barebone for withdraw * MP-2351: changed the AccountHealth logic * MP-2345: enabled withdraw function * MP-2351: added animation to Accordion * fix: adjusted according to feedback * fix: reduced complexity * tidy: format * env: enabled osmo-test-5 support * feat: added USDC.n * env: updated dependencies * fix: hotfixed react-draggable * fix: fixed vault info
158 lines
4.6 KiB
TypeScript
158 lines
4.6 KiB
TypeScript
'use client'
|
|
|
|
import BigNumber from 'bignumber.js'
|
|
import classNames from 'classnames'
|
|
import React, { useEffect, useState } from 'react'
|
|
|
|
import { demagnify, formatValue, magnify } from 'utils/formatters'
|
|
import { BN } from 'utils/helpers'
|
|
|
|
interface Props {
|
|
asset: Asset
|
|
amount: BigNumber
|
|
min?: BigNumber
|
|
max?: BigNumber
|
|
className: string
|
|
maxDecimals: number
|
|
maxLength?: number
|
|
allowNegative?: boolean
|
|
style?: {}
|
|
disabled?: boolean
|
|
onChange: (amount: BigNumber) => void
|
|
onBlur?: () => void
|
|
onFocus?: () => void
|
|
onRef?: (ref: React.RefObject<HTMLInputElement>) => void
|
|
}
|
|
|
|
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(() => {
|
|
setFormattedAmount(
|
|
formatValue(props.amount.toNumber(), {
|
|
decimals: props.asset.decimals,
|
|
maxDecimals: props.asset.decimals,
|
|
thousandSeparator: false,
|
|
}),
|
|
)
|
|
}, [props.amount, props.asset])
|
|
|
|
useEffect(() => {
|
|
if (!props.onRef) return
|
|
props.onRef(inputRef)
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [inputRef, props.onRef])
|
|
|
|
const onInputFocus = () => {
|
|
inputRef.current?.select()
|
|
props.onFocus && props.onFocus()
|
|
}
|
|
|
|
const updateValues = (formatted: string, amount: BigNumber) => {
|
|
const lastChar = formatted.charAt(formatted.length - 1)
|
|
if (lastChar === '.') {
|
|
cursorRef.current = (inputRef.current?.selectionEnd || 0) + 1
|
|
} else {
|
|
cursorRef.current = inputRef.current?.selectionEnd || 0
|
|
}
|
|
setFormattedAmount(formatted)
|
|
|
|
if (!props.amount.isEqualTo(amount)) {
|
|
props.onChange(amount)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!inputRef.current) return
|
|
|
|
const cursor = cursorRef.current
|
|
const length = formattedAmount.length
|
|
|
|
if (cursor > length) {
|
|
inputRef.current.setSelectionRange(length, length)
|
|
return
|
|
}
|
|
|
|
inputRef.current.setSelectionRange(cursor, cursor)
|
|
}, [formattedAmount, inputRef])
|
|
|
|
const onInputChange = (formattedAmount: string) => {
|
|
const numberCount = formattedAmount.match(/[0-9]/g)?.length || 0
|
|
const decimals = formattedAmount.split('.')[1]?.length || 0
|
|
const lastChar = formattedAmount.charAt(formattedAmount.length - 1)
|
|
const isNumber = !isNaN(Number(formattedAmount))
|
|
const hasMultipleDots = (formattedAmount.match(/[.,]/g)?.length || 0) > 1
|
|
const isSeparator = lastChar === '.' || lastChar === ','
|
|
const isNegative = formattedAmount.indexOf('-') > -1
|
|
const isLowerThanMinimum =
|
|
props.min !== undefined && props.min.isGreaterThan(magnify(formattedAmount, props.asset))
|
|
const isHigherThanMaximum =
|
|
props.max !== undefined && props.max.isLessThan(magnify(formattedAmount, props.asset))
|
|
const isTooLong = props.maxLength !== undefined && numberCount > props.maxLength
|
|
const exceedsMaxDecimals = props.maxDecimals !== undefined && decimals > props.maxDecimals
|
|
|
|
if (isNegative && !props.allowNegative) return
|
|
|
|
if (isSeparator && formattedAmount.length === 1) {
|
|
updateValues('0.', BN(0))
|
|
return
|
|
}
|
|
|
|
if (isSeparator && !hasMultipleDots) {
|
|
updateValues(formattedAmount.replace(',', '.'), props.amount)
|
|
return
|
|
}
|
|
|
|
if (!isNumber || hasMultipleDots) return
|
|
|
|
if (exceedsMaxDecimals) {
|
|
formattedAmount = formattedAmount.substring(0, formattedAmount.length - 1)
|
|
}
|
|
|
|
if (isTooLong) return
|
|
|
|
if (isLowerThanMinimum && props.min) {
|
|
updateValues(String(demagnify(props.min, props.asset)), props.min)
|
|
return
|
|
}
|
|
|
|
if (isHigherThanMaximum && props.max) {
|
|
updateValues(String(demagnify(props.max, props.asset)), props.max)
|
|
return
|
|
}
|
|
|
|
const amount = BN(formattedAmount).shiftedBy(props.asset.decimals)
|
|
|
|
if (lastChar === '0' && amount.isEqualTo(props.amount)) {
|
|
cursorRef.current = (inputRef.current?.selectionEnd || 0) + 1
|
|
setFormattedAmount(formattedAmount)
|
|
return
|
|
}
|
|
|
|
updateValues(amount.shiftedBy(-1 * props.asset.decimals).toString(), amount)
|
|
}
|
|
|
|
return (
|
|
<input
|
|
ref={inputRef}
|
|
type='text'
|
|
value={formattedAmount === '0' ? '' : formattedAmount}
|
|
onFocus={onInputFocus}
|
|
onChange={(e) => onInputChange(e.target.value)}
|
|
onBlur={props.onBlur}
|
|
disabled={props.disabled}
|
|
className={classNames(
|
|
'w-full cursor-pointer appearance-none border-none bg-transparent text-right outline-none',
|
|
props.className,
|
|
)}
|
|
style={props.style}
|
|
/>
|
|
)
|
|
}
|