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.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 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 dispalyed. 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 amout 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 /* 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(",", ".") : "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() 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 } } } 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.interactive 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) } } } } }