status-desktop/ui/imports/shared/controls/AmountInput.qml

140 lines
4.5 KiB
QML

import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import utils 1.0
Input {
id: root
property var locale: LocaleUtils.userInputLocale
readonly property alias amount: d.amount
property alias multiplierIndex: d.multiplierIndex
readonly property bool valid: validationError.length === 0
property bool allowDecimals: true
property int tokenDecimals: 0
property bool validateMaximumAmount: false
property string maximumAmount: "0"
property bool allowZero: true
property alias labelText: labelText.text
property string maximumExceededErrorText: qsTr("Amount exceeds balance")
validationErrorTopMargin: 8
fontPixelSize: 13
customHeight: 36
placeholderText: locale.zeroDigit
textField.rightPadding: labelText.implicitWidth + labelText.anchors.rightMargin
+ textField.leftPadding
function setAmount(amount, multiplierIndex = 0) {
console.assert(typeof amount === "string")
d.multiplierIndex = multiplierIndex
const amountNumber = SQUtils.AmountsArithmetic.toNumber(
amount, multiplierIndex)
const precision = LocaleUtils.fractionalPartLength(amountNumber)
root.text = LocaleUtils.numberToLocaleString(amountNumber, precision, root.locale)
}
onTextChanged: d.validate()
onValidateMaximumAmountChanged: d.validate()
onMaximumAmountChanged: d.validate()
onMultiplierIndexChanged: d.validate()
QtObject {
id: d
property string amount: "0"
property int multiplierIndex: 0
function getEffectiveDigitsCount(str) {
const digits = LocaleUtils.getLocalizedDigitsCount(text, root.locale)
return str.startsWith(root.locale.decimalPoint) ? digits + 1 : digits
}
function validate() {
if (!root.allowDecimals)
root.text = root.text.replace(root.locale.decimalPoint, "")
if (root.text.length === 0) {
d.amount = "0"
root.validationError = ""
return
}
const amountNumber = LocaleUtils.numberFromLocaleString(root.text, root.locale)
if (isNaN(amountNumber)) {
d.amount = "0"
root.validationError = qsTr("Invalid amount format")
return
}
const fractionalPartLength = LocaleUtils.fractionalPartLength(amountNumber)
if (fractionalPartLength > root.tokenDecimals) {
d.amount = "0"
root.validationError = qsTr("Max %n decimal place(s) for this asset", "", root.tokenDecimals)
return
}
if (!root.allowZero && amountNumber === 0) {
d.amount = "0"
root.validationError = qsTr("Amount must be greater than 0")
return
}
const amount = SQUtils.AmountsArithmetic.fromNumber(amountNumber, d.multiplierIndex)
if (root.validateMaximumAmount && root.maximumAmount && root.maximumAmount.length > 0) {
const maximumAmount = SQUtils.AmountsArithmetic.fromString(root.maximumAmount)
const maxExceeded = SQUtils.AmountsArithmetic.cmp(amount, maximumAmount) === 1
if (maxExceeded) {
root.validationError = root.maximumExceededErrorText
return
}
}
// Fallback to handle float amounts for permissions
// As a target amount should be always integer number
if (!Number.isInteger(amountNumber) && d.multiplierIndex === 0) {
d.amount = amount.toString()
} else {
d.amount = amount.toFixed(0)
}
root.validationError = ""
}
}
validator: DoubleValidator {
decimals: root.allowDecimals ? 100 : 0
bottom: 0
notation: DoubleValidator.StandardNotation
locale: root.locale.name.split("_")[0] // For whatever reason, this doesn't work properly when being
// passed "language_country". We pass only the language part.
}
StatusBaseText {
id: labelText
parent: root.textField
anchors.right: parent.right
anchors.rightMargin: 13
anchors.verticalCenter: parent.verticalCenter
text: qsTr("Amount")
color: Theme.palette.baseColor1
font.pixelSize: 13
}
}