/
AssetAmountInput.svelte
154 lines (145 loc) · 6.12 KB
/
AssetAmountInput.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<script lang="typescript">
import Big from 'big.js'
import { Text, AssetDropdown, InputContainer, SliderInput, AmountInput, TooltipIcon } from 'shared/components'
import UnitInput from './UnitInput.svelte'
import { formatCurrency, localize, parseCurrency } from '@core/i18n'
import {
formatTokenAmountBestMatch,
convertToRawAmount,
IAsset,
formatTokenAmountDefault,
visibleSelectedAccountAssets,
} from '@core/wallet'
import { IOTA_UNIT_MAP } from '@core/utils'
import { getMarketAmountFromAssetValue } from '@core/market/utils'
export let inputElement: HTMLInputElement = undefined
export let disabled = false
export let isFocused = false
export let votingPower: number = 0
export let asset: IAsset = $visibleSelectedAccountAssets?.baseCoin
export let rawAmount: string = undefined
export let unit: string = undefined
export let containsSlider: boolean = false
export let disableAssetSelection: boolean = null
let amount: string = rawAmount
? formatTokenAmountDefault(Number(rawAmount), asset?.metadata, unit, false)
: undefined
let amountInputElement: HTMLInputElement
let error: string
$: isFocused && (error = '')
let allowedDecimals = 0
$: if (!asset?.metadata?.useMetricPrefix) {
if (unit === asset?.metadata.unit) {
allowedDecimals = Math.min(asset?.metadata.decimals, 18)
} else if (unit === asset?.metadata?.subunit) {
allowedDecimals = 0
}
} else if (asset?.metadata?.useMetricPrefix) {
allowedDecimals = IOTA_UNIT_MAP?.[unit?.substring(0, 1)] ?? 0
}
$: availableBalance = asset?.balance?.available + votingPower
$: bigAmount = convertToRawAmount(amount, unit, asset?.metadata)
$: marketAmount = getMarketAmountFromAssetValue(bigAmount, asset)
$: max = parseCurrency(formatTokenAmountDefault(availableBalance, asset.metadata, unit, false))
function onClickAvailableBalance(): void {
const isRawAmount = asset?.metadata?.decimals && asset?.metadata?.unit
if (isRawAmount) {
const parsedAmount = formatTokenAmountDefault(availableBalance, asset?.metadata, unit, false)
amount = parsedAmount
return
}
amount = availableBalance.toString() ?? '0'
unit = undefined
}
export function validate(allowZeroOrNull = false): Promise<void> {
const amountAsFloat = parseCurrency(amount)
const isAmountZeroOrNull = !Number(amountAsFloat)
// Zero value transactions can still contain metadata/tags
if (allowZeroOrNull && isAmountZeroOrNull) {
return
} else if (isAmountZeroOrNull) {
error = localize('error.send.amountInvalidFormat')
} else if (
(unit === asset?.metadata?.subunit ||
(unit === asset?.metadata?.unit && asset?.metadata?.decimals === 0)) &&
Number.parseInt(amount, 10).toString() !== amount
) {
error = localize('error.send.amountNoFloat')
} else if (bigAmount.gt(Big(availableBalance))) {
error = localize('error.send.amountTooHigh')
} else if (bigAmount.lte(Big(0))) {
error = localize('error.send.amountZero')
} else if (!bigAmount.mod(1).eq(Big(0))) {
error = localize('error.send.amountSmallerThanSubunit')
}
if (error) {
return Promise.reject(error)
}
rawAmount = bigAmount.toString()
}
</script>
<InputContainer
bind:this={inputElement}
bind:inputElement={amountInputElement}
col
{isFocused}
{error}
on:clickOutside={() => (isFocused = false)}
>
<div class="flex flex-row w-full items-center space-x-0.5 relative">
<AssetDropdown bind:asset readonly={disableAssetSelection} />
<AmountInput
bind:inputElement={amountInputElement}
bind:amount
bind:hasFocus={isFocused}
maxDecimals={allowedDecimals}
isInteger={allowedDecimals === 0}
clearBackground
clearPadding
clearBorder
{disabled}
/>
{#if asset?.metadata?.unit}
<UnitInput bind:unit bind:isFocused tokenMetadata={asset?.metadata} />
{/if}
</div>
{#if containsSlider}
<div class="flex flex-col mt-5">
<SliderInput bind:value={amount} {max} decimals={asset.metadata.decimals} {disabled} />
<div class="flex flex-row justify-between">
<Text color="gray-800" darkColor="gray-500" fontSize="xs"
>{formatTokenAmountBestMatch(0, asset?.metadata)}</Text
>
<Text color="gray-800" darkColor="gray-500" fontSize="xs"
>{formatTokenAmountBestMatch(availableBalance, asset?.metadata)}</Text
>
</div>
</div>
{:else}
<div class="flex flex-row w-full items-end justify-between mt-2">
{#if asset}
<div class="flex flex-row items-center">
<button on:click={onClickAvailableBalance}>
<Text color="gray-600" darkColor="gray-500" fontSize="xs" classes="cursor-pointer">
{localize('general.availableBalanceWithValue', {
values: {
balance: formatTokenAmountBestMatch(availableBalance, asset?.metadata),
},
})}
</Text>
</button>
<TooltipIcon
title={localize('general.availableBalance')}
text={localize('general.availableBalanceTooltip')}
width={15}
height={15}
classes="ml-1"
/>
</div>
{/if}
<!-- Placeholder for asset USD value -->
<Text color="gray-600" darkColor="gray-500" fontSize="xs">{formatCurrency(marketAmount) ?? ''}</Text>
</div>
<!-- else content here -->
{/if}
</InputContainer>