285 lines
9.7 KiB
QML
285 lines
9.7 KiB
QML
import QtQuick 2.15
|
|
import QtQuick.Controls 2.15
|
|
import QtQuick.Layouts 1.15
|
|
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
import StatusQ.Core.Utils 0.1 as SQUtils
|
|
import StatusQ.Components 0.1
|
|
import StatusQ.Validators 0.1
|
|
|
|
import utils 1.0
|
|
import shared.controls 1.0
|
|
|
|
Control {
|
|
id: root
|
|
|
|
/* Crypto value in a base unit as a string integer, e.g. 1000000000000000000
|
|
* for 1 ETH */
|
|
readonly property alias amount: d.amountBaseUnit
|
|
|
|
/* In fiat mode the input value is meant to be a fiat value, conversely,
|
|
* crypto value otherwise. */
|
|
readonly property alias fiatMode: d.fiatMode
|
|
|
|
/* Indicates whether toggling the fiatMode is enabled for the user */
|
|
property bool fiatInputInteractive: interactive
|
|
|
|
/* Indicates if input represent valid number. E.g. empty input or containing
|
|
* only decimal point is not valid. */
|
|
readonly property alias valid: textField.acceptableInput
|
|
readonly property bool empty: textField.length === 0
|
|
|
|
// TODO: remove, temporarily for backward compatibility. External components
|
|
// should not rely on formatted amount because formatting rules are internal
|
|
// detail of that component.
|
|
readonly property alias text: textField.text
|
|
|
|
/* Decimal point character to be displayed. Both "." and "," will be
|
|
* replaced by the provided decimal point on the fly */
|
|
property alias decimalPoint: validator.decimalPoint
|
|
|
|
/* Number of fiat decimal places used to limit allowed decimal places in
|
|
* fiatMode */
|
|
property int fiatDecimalPlaces: 2
|
|
|
|
/* Specifies how divisible given cryptocurrency is, e.g. 18 for ETH. Used
|
|
* for limiting allowed decimal places and computing final amount as an
|
|
* integer value */
|
|
property int multiplierIndex: 18
|
|
|
|
/* Price of one unit of given cryptocurrency (e.g. price for 1 ETH) */
|
|
property real price: 1.0
|
|
|
|
property alias caption: captionText.text
|
|
property bool interactive: true
|
|
|
|
readonly property bool cursorVisible: textField.cursorVisible
|
|
readonly property alias placeholderText: textField.placeholderText
|
|
|
|
/* Loading states for the input and text below */
|
|
property bool mainInputLoading
|
|
property bool bottomTextLoading
|
|
|
|
/* Allows mark input as invalid when it's valid number but doesn't satisfy
|
|
* arbitrary external criteria, e.g. is higher than maximum expected value. */
|
|
property bool markAsInvalid: false
|
|
|
|
/* Methods for formatting crypto and fiat value expecting double values,
|
|
e.g. 1.0 for 1 ETH or 1.0 for 1 USD. */
|
|
property var formatFiat: balance =>
|
|
`${balance.toLocaleString(Qt.locale())} FIAT`
|
|
property var formatBalance: balance =>
|
|
`${balance.toLocaleString(Qt.locale())} CRYPTO`
|
|
|
|
/* Allows to set value to be displayed. The value is expected to be a not
|
|
localized string like "1", "1.1" or "0.000000023400234222". Provided
|
|
value will be formatted and displayed. Depending on the fiatMode flag
|
|
it will affect output amount appropriately. */
|
|
function setValue(valueString) {
|
|
if (!valueString)
|
|
valueString = "0"
|
|
|
|
const decimalPlaces = d.fiatMode ? root.fiatDecimalPlaces
|
|
: root.multiplierIndex
|
|
|
|
const stringNumber = SQUtils.AmountsArithmetic.fromString(
|
|
valueString).toFixed(decimalPlaces)
|
|
|
|
const trimmed = d.fiatMode
|
|
? stringNumber
|
|
: d.removeDecimalTrailingZeros(stringNumber)
|
|
|
|
textField.text = d.localize(trimmed)
|
|
}
|
|
|
|
function clear() {
|
|
textField.clear()
|
|
}
|
|
|
|
function forceActiveFocus() {
|
|
textField.forceActiveFocus()
|
|
}
|
|
|
|
QtObject {
|
|
id: d
|
|
|
|
property bool fiatMode: false
|
|
|
|
readonly property string inputDelocalized:
|
|
root.valid && textField.length !== 0
|
|
? textField.text.replace(root.decimalPoint, ".") : "0"
|
|
|
|
function removeDecimalTrailingZeros(num) {
|
|
if (!num.includes("."))
|
|
return num
|
|
|
|
return num.replace(/\.?0*$/g, "")
|
|
}
|
|
|
|
function localize(num) {
|
|
return num.replace(".", root.decimalPoint)
|
|
}
|
|
|
|
readonly property string amountBaseUnit: {
|
|
if (d.fiatMode)
|
|
return secondaryValue
|
|
|
|
const multiplier = SQUtils.AmountsArithmetic.fromExponent(
|
|
root.multiplierIndex)
|
|
|
|
return SQUtils.AmountsArithmetic.times(
|
|
SQUtils.AmountsArithmetic.fromString(inputDelocalized),
|
|
multiplier).toFixed()
|
|
}
|
|
|
|
readonly property string secondaryValue: {
|
|
const price = isNaN(root.price) ? 0 : root.price
|
|
|
|
if (!d.fiatMode)
|
|
return SQUtils.AmountsArithmetic.times(
|
|
SQUtils.AmountsArithmetic.fromString(inputDelocalized),
|
|
SQUtils.AmountsArithmetic.fromNumber(
|
|
price * (10 ** root.fiatDecimalPlaces))).round().toFixed()
|
|
|
|
if (!price)
|
|
return 0
|
|
|
|
const multiplier = SQUtils.AmountsArithmetic.fromExponent(
|
|
root.multiplierIndex)
|
|
|
|
return SQUtils.AmountsArithmetic.div(
|
|
SQUtils.AmountsArithmetic.times(
|
|
SQUtils.AmountsArithmetic.fromString(inputDelocalized),
|
|
multiplier),
|
|
SQUtils.AmountsArithmetic.fromNumber(price)).round().toFixed()
|
|
}
|
|
}
|
|
|
|
contentItem: ColumnLayout {
|
|
StatusBaseText {
|
|
id: captionText
|
|
|
|
Layout.fillWidth: true
|
|
|
|
visible: text.length > 0
|
|
|
|
font.pixelSize: 13
|
|
lineHeight: 18
|
|
lineHeightMode: Text.FixedHeight
|
|
color: Theme.palette.directColor1
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
RowLayout {
|
|
StyledTextField {
|
|
id: textField
|
|
|
|
objectName: "amountToSend_textField"
|
|
|
|
Layout.fillWidth: true
|
|
|
|
implicitHeight: 44
|
|
padding: 0
|
|
background: null
|
|
|
|
readOnly: !root.interactive
|
|
|
|
color: text.length === 0 || (root.valid && !root.markAsInvalid)
|
|
? Theme.palette.directColor1
|
|
: Theme.palette.dangerColor1
|
|
|
|
placeholderText: {
|
|
if (!d.fiatMode || root.fiatDecimalPlaces === 0)
|
|
return "0"
|
|
|
|
return "0" + root.decimalPoint
|
|
+ "0".repeat(root.fiatDecimalPlaces)
|
|
}
|
|
|
|
font.pixelSize: Utils.getFontSizeBasedOnLetterCount(text)
|
|
|
|
validator: AmountValidator {
|
|
id: validator
|
|
|
|
maxDecimalDigits: d.fiatMode ? root.fiatDecimalPlaces
|
|
: root.multiplierIndex
|
|
locale: root.locale.name
|
|
}
|
|
visible: !root.mainInputLoading
|
|
}
|
|
LoadingComponent {
|
|
objectName: "topAmountToSendInputLoadingComponent"
|
|
Layout.preferredWidth: textField.width
|
|
Layout.preferredHeight: textField.height
|
|
visible: root.mainInputLoading
|
|
}
|
|
}
|
|
|
|
StatusBaseText {
|
|
id: bottomItem
|
|
|
|
objectName: "bottomItemText"
|
|
|
|
Layout.fillWidth: true
|
|
|
|
text: {
|
|
const divisor = SQUtils.AmountsArithmetic.fromExponent(
|
|
d.fiatMode ? root.multiplierIndex
|
|
: root.fiatDecimalPlaces)
|
|
const divided = SQUtils.AmountsArithmetic.div(
|
|
SQUtils.AmountsArithmetic.fromString(
|
|
d.secondaryValue), divisor)
|
|
const asNumber = SQUtils.AmountsArithmetic.toNumber(divided)
|
|
|
|
return d.fiatMode ? root.formatBalance(asNumber)
|
|
: root.formatFiat(asNumber)
|
|
}
|
|
|
|
elide: Text.ElideMiddle
|
|
font.pixelSize: 13
|
|
color: Theme.palette.directColor5
|
|
|
|
MouseArea {
|
|
objectName: "amountToSend_mouseArea"
|
|
|
|
anchors.fill: parent
|
|
cursorShape: enabled ? Qt.PointingHandCursor : undefined
|
|
enabled: root.fiatInputInteractive
|
|
|
|
onClicked: {
|
|
const secondaryValue = d.secondaryValue
|
|
|
|
d.fiatMode = !d.fiatMode
|
|
|
|
if (textField.length === 0)
|
|
return
|
|
|
|
const decimalPlaces = d.fiatMode ? root.fiatDecimalPlaces
|
|
: root.multiplierIndex
|
|
const divisor = SQUtils.AmountsArithmetic.fromExponent(
|
|
decimalPlaces)
|
|
|
|
const stringNumber = SQUtils.AmountsArithmetic.div(
|
|
SQUtils.AmountsArithmetic.fromString(secondaryValue),
|
|
divisor).toFixed(decimalPlaces)
|
|
|
|
const trimmed = d.fiatMode
|
|
? stringNumber
|
|
: d.removeDecimalTrailingZeros(stringNumber)
|
|
|
|
textField.text = d.localize(trimmed)
|
|
}
|
|
}
|
|
visible: !root.bottomTextLoading
|
|
}
|
|
|
|
LoadingComponent {
|
|
objectName: "bottomItemTextLoadingComponent"
|
|
Layout.preferredWidth: bottomItem.width
|
|
Layout.preferredHeight: bottomItem.height
|
|
visible: root.bottomTextLoading
|
|
}
|
|
}
|
|
}
|