feat/tx-comps: Add RecipientSelector component
Based on the spec in https://www.notion.so/emizzle/Wallet-transaction-components-2003b78a8d0d41c4ab3d21eb2496fb20, this component handles user input for a recipient address, which can be sourced from manual address input, ENS name, contact selection, or another of the user's wallet accounts.
This commit is contained in:
parent
9466714d90
commit
d07daac377
|
@ -11,6 +11,8 @@ type
|
|||
Identicon = UserRole + 4
|
||||
IsContact = UserRole + 5
|
||||
IsBlocked = UserRole + 6
|
||||
Alias = UserRole + 7
|
||||
EnsVerified = UserRole + 8
|
||||
|
||||
QtObject:
|
||||
type ContactList* = ref object of QAbstractListModel
|
||||
|
@ -48,6 +50,8 @@ QtObject:
|
|||
of "pubKey": result = contact.id
|
||||
of "isContact": result = $contact.isContact()
|
||||
of "isBlocked": result = $contact.isBlocked()
|
||||
of "alias": result = contact.alias
|
||||
of "ensVerified": result = $contact.ensVerified
|
||||
|
||||
method data(self: ContactList, index: QModelIndex, role: int): QVariant =
|
||||
if not index.isValid:
|
||||
|
@ -62,6 +66,8 @@ QtObject:
|
|||
of ContactRoles.PubKey: result = newQVariant(contact.id)
|
||||
of ContactRoles.IsContact: result = newQVariant(contact.isContact())
|
||||
of ContactRoles.IsBlocked: result = newQVariant(contact.isBlocked())
|
||||
of ContactRoles.Alias: result = newQVariant(contact.alias)
|
||||
of ContactRoles.EnsVerified: result = newQVariant(contact.ensVerified)
|
||||
|
||||
method roleNames(self: ContactList): Table[int, string] =
|
||||
{
|
||||
|
@ -70,7 +76,9 @@ QtObject:
|
|||
ContactRoles.Identicon.int:"identicon",
|
||||
ContactRoles.PubKey.int:"pubKey",
|
||||
ContactRoles.IsContact.int:"isContact",
|
||||
ContactRoles.IsBlocked.int:"isBlocked"
|
||||
ContactRoles.IsBlocked.int:"isBlocked",
|
||||
ContactRoles.Alias.int:"alias",
|
||||
ContactRoles.EnsVerified.int:"ensVerified"
|
||||
}.toTable
|
||||
|
||||
proc addContactToList*(self: ContactList, contact: Profile) =
|
||||
|
|
|
@ -19,7 +19,7 @@ Item {
|
|||
return;
|
||||
}
|
||||
let result = walletModel.onSendTransaction(selectFromAccount.selectedAccount.address,
|
||||
txtTo.text,
|
||||
selectRecipient.selectedRecipient,
|
||||
selectAsset.selectedAsset.address,
|
||||
txtAmount.text,
|
||||
txtPassword.text)
|
||||
|
@ -35,6 +35,7 @@ Item {
|
|||
}
|
||||
|
||||
function validate() {
|
||||
selectRecipient.validate()
|
||||
if (txtPassword.text === "") {
|
||||
//% "You need to enter a password"
|
||||
passwordValidationError = qsTrId("you-need-to-enter-a-password")
|
||||
|
@ -45,16 +46,6 @@ Item {
|
|||
passwordValidationError = ""
|
||||
}
|
||||
|
||||
if (txtTo.text === "") {
|
||||
//% "You need to enter a destination address"
|
||||
toValidationError = qsTrId("you-need-to-enter-a-destination-address")
|
||||
} else if (!Utils.isAddress(txtTo.text)) {
|
||||
//% "This needs to be a valid address (starting with 0x)"
|
||||
toValidationError = qsTrId("this-needs-to-be-a-valid-address-(starting-with-0x)")
|
||||
} else {
|
||||
toValidationError = ""
|
||||
}
|
||||
|
||||
if (txtAmount.text === "") {
|
||||
//% "You need to enter an amount"
|
||||
amountValidationError = qsTrId("you-need-to-enter-an-amount")
|
||||
|
@ -114,15 +105,15 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Input {
|
||||
id: txtTo
|
||||
//% "Recipient"
|
||||
label: qsTrId("recipient")
|
||||
//% "Send to"
|
||||
placeholderText: qsTrId("send-to")
|
||||
RecipientSelector {
|
||||
id: selectRecipient
|
||||
accounts: walletModel.accounts
|
||||
contacts: profileModel.addedContacts
|
||||
label: qsTr("Recipient")
|
||||
anchors.top: selectFromAccount.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
validationError: toValidationError
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Input {
|
||||
|
@ -131,7 +122,7 @@ Item {
|
|||
label: qsTrId("password")
|
||||
//% "Enter Password"
|
||||
placeholderText: qsTrId("biometric-auth-login-ios-fallback-label")
|
||||
anchors.top: txtTo.bottom
|
||||
anchors.top: selectRecipient.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
textField.echoMode: TextInput.Password
|
||||
validationError: passwordValidationError
|
||||
|
|
|
@ -5,6 +5,7 @@ Theme {
|
|||
property color white: "#FFFFFF"
|
||||
property color white2: "#FCFCFC"
|
||||
property color black: "#000000"
|
||||
property color almostBlack: "#141414"
|
||||
property color grey: "#EEF2F5"
|
||||
property color lightBlue: "#ECEFFC"
|
||||
property color cyan: "#00FFFF"
|
||||
|
@ -18,10 +19,15 @@ Theme {
|
|||
property color lightRed: "#FFEAEE"
|
||||
property color green: "#4EBC60"
|
||||
property color turquoise: "#007b7d"
|
||||
property color tenPercentWhite: Qt.rgba(255, 255, 255, 0.1)
|
||||
property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1)
|
||||
|
||||
property color background: "#141414"
|
||||
property color background: almostBlack
|
||||
property color border: "#252528"
|
||||
property color borderSecondary: tenPercentWhite
|
||||
property color borderTertiary: blue
|
||||
property color textColor: white
|
||||
property color textColorTertiary: blue
|
||||
property color currentUserTextColor: white
|
||||
property color secondaryBackground: "#23252F"
|
||||
property color inputBackground: secondaryBackground
|
||||
|
@ -29,6 +35,9 @@ Theme {
|
|||
property color modalBackground: background
|
||||
property color backgroundHover: "#252528"
|
||||
property color secondaryText: darkGrey
|
||||
property color secondaryHover: Qt.rgba(255, 255, 255, 0.1)
|
||||
property color secondaryHover: tenPercentWhite
|
||||
property color danger: red
|
||||
property color primaryMenuItemHover: blue
|
||||
property color primaryMenuItemTextHover: almostBlack
|
||||
property color backgroundTertiary: tenPercentBlue
|
||||
}
|
||||
|
|
|
@ -18,10 +18,15 @@ Theme {
|
|||
property color lightRed: "#FFEAEE"
|
||||
property color green: "#4EBC60"
|
||||
property color turquoise: "#007b7d"
|
||||
property color tenPercentBlack: Qt.rgba(0, 0, 0, 0.1)
|
||||
property color tenPercentBlue: Qt.rgba(67, 96, 223, 0.1)
|
||||
|
||||
property color background: white
|
||||
property color border: grey
|
||||
property color borderSecondary: tenPercentBlack
|
||||
property color borderTertiary: blue
|
||||
property color textColor: black
|
||||
property color textColorTertiary: blue
|
||||
property color currentUserTextColor: white
|
||||
property color secondaryBackground: lightBlue
|
||||
property color inputBackground: grey
|
||||
|
@ -29,6 +34,9 @@ Theme {
|
|||
property color modalBackground: white2
|
||||
property color backgroundHover: grey
|
||||
property color secondaryText: darkGrey
|
||||
property color secondaryHover: Qt.rgba(0, 0, 0, 0.1)
|
||||
property color secondaryHover: tenPercentBlack
|
||||
property color danger: red
|
||||
property color primaryMenuItemHover: blue
|
||||
property color primaryMenuItemTextHover: white
|
||||
property color backgroundTertiary: tenPercentBlue
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ Item {
|
|||
// NOTE: if this asset is not selected as a wallet token in the UI, then
|
||||
// nothing will be displayed
|
||||
property string showAssetBalance: ""
|
||||
property int dropdownWidth: width
|
||||
|
||||
Repeater {
|
||||
visible: showAssetBalance !== ""
|
||||
|
@ -36,7 +37,7 @@ Item {
|
|||
id: select
|
||||
label: root.label
|
||||
model: root.accounts
|
||||
|
||||
menuAlignment: Select.MenuAlignment.Left
|
||||
menu.delegate: menuItem
|
||||
menu.onOpened: {
|
||||
selectedAccountDetails.visible = false
|
||||
|
@ -44,6 +45,7 @@ Item {
|
|||
menu.onClosed: {
|
||||
selectedAccountDetails.visible = true
|
||||
}
|
||||
menu.width: dropdownWidth
|
||||
selectedItemView: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -85,18 +87,11 @@ Item {
|
|||
|
||||
StyledText {
|
||||
id: textSelectedAddress
|
||||
text: selectedAccount.address
|
||||
text: selectedAccount.address + " • "
|
||||
font.pixelSize: 12
|
||||
elide: Text.ElideMiddle
|
||||
height: 16
|
||||
width: 85
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
StyledText {
|
||||
id: separator
|
||||
text: "• "
|
||||
font.pixelSize: 12
|
||||
height: 16
|
||||
width: 90
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
StyledText {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property string validationError: "Error"
|
||||
property alias label: inpAddress.label
|
||||
property string selectedAddress
|
||||
|
||||
height: inpAddress.height
|
||||
|
||||
function isValidAddress(inputValue) {
|
||||
return /0x[a-fA-F0-9]{40}/.test(inputValue)
|
||||
}
|
||||
function isValidEns(inputValue) {
|
||||
// TODO: Check if the entered value resolves to an address. Long operation.
|
||||
// Issue tracked: https://github.com/status-im/nim-status-client/issues/718
|
||||
const isEmail = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(inputValue)
|
||||
const isDomain = /(?:(?:(?<thld>[\w\-]*)(?:\.))?(?<sld>[\w\-]*))\.(?<tld>[\w\-]*)/.test(inputValue)
|
||||
return isEmail || isDomain
|
||||
}
|
||||
|
||||
function validate(inputValue) {
|
||||
if (!inputValue) inputValue = selectedAddress
|
||||
let isValid =
|
||||
(inputValue && inputValue.startsWith("0x") && isValidAddress(inputValue)) ||
|
||||
isValidEns(inputValue)
|
||||
inpAddress.validationError = isValid ? "" : validationError
|
||||
return isValid
|
||||
}
|
||||
|
||||
Input {
|
||||
id: inpAddress
|
||||
placeholderText: qsTr("eg. 0x1234 or ENS")
|
||||
customHeight: 56
|
||||
validationErrorAlignment: TextEdit.AlignRight
|
||||
validationErrorTopMargin: 8
|
||||
textField.onFocusChanged: {
|
||||
let isValid = true
|
||||
if (text !== "") {
|
||||
isValid = root.validate(metrics.text)
|
||||
}
|
||||
if (!isValid) {
|
||||
return
|
||||
}
|
||||
if (textField.focus) {
|
||||
text = metrics.text
|
||||
} else if (root.isValidAddress(metrics.text)) {
|
||||
text = metrics.elidedText
|
||||
}
|
||||
}
|
||||
textField.rightPadding: 73
|
||||
onTextEdited: {
|
||||
metrics.text = text
|
||||
const isValid = root.validate(inputValue)
|
||||
if (isValid) {
|
||||
root.selectedAddress = inputValue
|
||||
}
|
||||
}
|
||||
TextMetrics {
|
||||
id: metrics
|
||||
elideWidth: 97
|
||||
elide: Text.ElideMiddle
|
||||
}
|
||||
TertiaryButton {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 14
|
||||
label: qsTr("Paste")
|
||||
onClicked: {
|
||||
if (inpAddress.textField.canPaste) {
|
||||
inpAddress.textField.paste()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
|
@ -0,0 +1,67 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var sources: []
|
||||
property string selectedSource: sources[0] || "Address"
|
||||
property int dropdownWidth: 220
|
||||
height: select.height
|
||||
|
||||
Select {
|
||||
id: select
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
model: root.sources
|
||||
selectedItemView: Item {
|
||||
anchors.fill: parent
|
||||
StyledText {
|
||||
id: selectedTextField
|
||||
text: root.selectedSource
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
height: 24
|
||||
}
|
||||
}
|
||||
menu.width: dropdownWidth
|
||||
menu.topPadding: 8
|
||||
menu.bottomPadding: 8
|
||||
menu.delegate: Component {
|
||||
MenuItem {
|
||||
id: menuItem
|
||||
height: 40
|
||||
width: parent.width
|
||||
onTriggered: function () {
|
||||
root.selectedSource = root.sources[index]
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: itemText
|
||||
text: root.sources[index]
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
height: 22
|
||||
color: menuItem.highlighted ? Style.current.primaryMenuItemTextHover : Style.current.textColor
|
||||
}
|
||||
background: Rectangle {
|
||||
color: menuItem.highlighted ? Style.current.primaryMenuItemHover : Style.current.transparent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
|
@ -0,0 +1,186 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var contacts
|
||||
property var selectedContact
|
||||
height: select.height + (validationErrorText.visible ? validationErrorText.height : 0)
|
||||
property int dropdownWidth: width
|
||||
property string validationError: validationErrorText.text
|
||||
property alias validationErrorAlignment: validationErrorText.horizontalAlignment
|
||||
|
||||
onContactsChanged: {
|
||||
root.selectedContact = { name: qsTr("Select a contact") }
|
||||
}
|
||||
|
||||
function validate() {
|
||||
const isValid = root.selectedContact && root.selectedContact.address
|
||||
if (!isValid) {
|
||||
select.select.border.color = Style.current.danger
|
||||
select.select.border.width = 1
|
||||
validationErrorText.visible = true
|
||||
} else {
|
||||
select.select.border.color = Style.current.transparent
|
||||
select.select.border.width = 0
|
||||
validationErrorText.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Select {
|
||||
id: select
|
||||
label: ""
|
||||
model: root.contacts
|
||||
width: parent.width
|
||||
menuAlignment: Select.MenuAlignment.Left
|
||||
selectedItemView: Item {
|
||||
anchors.fill: parent
|
||||
Identicon {
|
||||
id: iconImg
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 14
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 32
|
||||
width: !!selectedContact.identicon ? 32 : 0
|
||||
visible: !!selectedContact.identicon
|
||||
source: selectedContact.identicon ? selectedContact.identicon : ""
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: selectedTextField
|
||||
text: selectedContact.name
|
||||
anchors.left: iconImg.right
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 15
|
||||
height: 22
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
zeroItemsView: Item {
|
||||
height: 186
|
||||
StyledText {
|
||||
anchors.fill: parent
|
||||
text: qsTr("You don't have any contacts yet")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: 13
|
||||
height: 18
|
||||
color: Style.current.secondaryText
|
||||
}
|
||||
}
|
||||
|
||||
menu.delegate: menuItem
|
||||
menu.width: dropdownWidth
|
||||
}
|
||||
TextEdit {
|
||||
id: validationErrorText
|
||||
visible: false
|
||||
text: qsTr("Please select a contact")
|
||||
anchors.top: select.bottom
|
||||
anchors.topMargin: 8
|
||||
selectByMouse: true
|
||||
readOnly: true
|
||||
font.pixelSize: 12
|
||||
height: 16
|
||||
color: Style.current.danger
|
||||
width: parent.width
|
||||
horizontalAlignment: TextEdit.AlignRight
|
||||
}
|
||||
|
||||
Component {
|
||||
id: menuItem
|
||||
MenuItem {
|
||||
id: itemContainer
|
||||
property bool isFirstItem: index === 0
|
||||
property bool isLastItem: index === contacts.rowCount() - 1
|
||||
|
||||
width: parent.width
|
||||
height: visible ? 72 : 0
|
||||
Identicon {
|
||||
id: iconImg
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40
|
||||
height: 40
|
||||
source: identicon
|
||||
}
|
||||
Column {
|
||||
anchors.left: iconImg.right
|
||||
anchors.leftMargin: 12
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: name
|
||||
font.pixelSize: 15
|
||||
font.family: Style.current.fontBold.name
|
||||
font.bold: true
|
||||
color: Style.current.textColor
|
||||
height: 22
|
||||
}
|
||||
|
||||
Row {
|
||||
StyledText {
|
||||
text: alias + " • "
|
||||
visible: ensVerified
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: 12
|
||||
height: 16
|
||||
}
|
||||
StyledText {
|
||||
text: address
|
||||
width: 85
|
||||
elide: Text.ElideMiddle
|
||||
color: Style.current.secondaryText
|
||||
font.pixelSize: 12
|
||||
height: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
color: itemContainer.highlighted ? Style.current.backgroundHover : Style.current.background
|
||||
radius: Style.current.radius
|
||||
|
||||
// cover bottom left/right corners with square corners
|
||||
Rectangle {
|
||||
visible: !isLastItem
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: parent.radius
|
||||
color: parent.color
|
||||
}
|
||||
|
||||
// cover top left/right corners with square corners
|
||||
Rectangle {
|
||||
visible: !isFirstItem
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: parent.radius
|
||||
color: parent.color
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: itemContainer
|
||||
onClicked: {
|
||||
root.selectedContact = { address, name, alias, isContact, identicon, ensVerified }
|
||||
select.menu.close()
|
||||
validate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
|
@ -9,7 +9,7 @@ Rectangle {
|
|||
color: Style.current.background
|
||||
radius: 50
|
||||
border.width: 1
|
||||
border.color: Style.current.border
|
||||
border.color: Style.current.borderSecondary
|
||||
|
||||
Image {
|
||||
width: parent.width
|
||||
|
|
|
@ -5,8 +5,11 @@ import "../imports"
|
|||
Item {
|
||||
property alias textField: inputValue
|
||||
property string placeholderText: "My placeholder"
|
||||
property string placeholderTextColor: Style.current.secondaryText
|
||||
property alias text: inputValue.text
|
||||
property string validationError: ""
|
||||
property alias validationErrorAlignment: validationErrorText.horizontalAlignment
|
||||
property int validationErrorTopMargin: 1
|
||||
property string label: ""
|
||||
readonly property bool hasLabel: label !== ""
|
||||
property color bgColor: Style.current.inputBackground
|
||||
|
@ -22,9 +25,10 @@ Item {
|
|||
property int customHeight: 44
|
||||
property int fontPixelSize: 15
|
||||
signal editingFinished(string inputValue)
|
||||
signal textEdited(string inputValue)
|
||||
|
||||
id: inputBox
|
||||
height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) + (!!validationError ? validationErrorText.height : 0)
|
||||
height: inputRectangle.height + (hasLabel ? inputLabel.height + labelMargin : 0) + (!!validationError ? (validationErrorText.height + validationErrorTopMargin) : 0)
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
|
||||
|
@ -56,6 +60,7 @@ Item {
|
|||
id: inputValue
|
||||
visible: !inputBox.isTextArea && !inputBox.isSelect
|
||||
placeholderText: inputBox.placeholderText
|
||||
placeholderTextColor: inputBox.placeholderTextColor
|
||||
text: inputBox.text
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 0
|
||||
|
@ -72,6 +77,7 @@ Item {
|
|||
color: Style.current.transparent
|
||||
}
|
||||
onEditingFinished: inputBox.editingFinished(inputBox.text)
|
||||
onTextEdited: inputBox.textEdited(inputBox.text)
|
||||
}
|
||||
|
||||
SVGImage {
|
||||
|
@ -91,12 +97,13 @@ Item {
|
|||
id: validationErrorText
|
||||
text: validationError
|
||||
anchors.top: inputRectangle.bottom
|
||||
anchors.topMargin: 1
|
||||
anchors.topMargin: validationErrorTopMargin
|
||||
selectByMouse: true
|
||||
readOnly: true
|
||||
font.pixelSize: 12
|
||||
color: Style.current.red
|
||||
|
||||
height: 16
|
||||
color: Style.current.danger
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,8 +82,8 @@ Menu {
|
|||
anchors.fill: parent
|
||||
source: parent
|
||||
color: popupMenuItem.highlighted ?
|
||||
Style.current.white :
|
||||
(popupMenuItem.action.icon.color != "#00000000" ? popupMenuItem.action.icon.color : Style.current.blue)
|
||||
Style.current.primaryMenuItemTextHover :
|
||||
(popupMenuItem.action.icon.color != "#00000000" ? popupMenuItem.action.icon.color : Style.current.primaryMenuItemHover)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ Menu {
|
|||
anchors.leftMargin: popupMenu.paddingSize
|
||||
text: popupMenuItem.text
|
||||
font: popupMenuItem.font
|
||||
color: popupMenuItem.highlighted ? Style.current.white : popupMenuItem.textColor
|
||||
color: popupMenuItem.highlighted ? Style.current.primaryMenuItemTextHover : popupMenuItem.textColor
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
opacity: enabled ? 1.0 : 0.3
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.13
|
||||
import QtGraphicalEffects 1.13
|
||||
import "../imports"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var accounts
|
||||
property var contacts
|
||||
property int inputWidth: 272
|
||||
property int sourceSelectWidth: 136
|
||||
property string label: qsTr("Recipient")
|
||||
property string selectedRecipient: ""
|
||||
height: inpAddress.height + txtLabel.height
|
||||
|
||||
function validate() {
|
||||
if (selAddressSource.selectedSource === "Address") {
|
||||
inpAddress.validate()
|
||||
} else if (selAddressSource.selectedSource === "Contact") {
|
||||
selContact.validate()
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: txtLabel
|
||||
visible: label !== ""
|
||||
text: root.label
|
||||
font.pixelSize: 13
|
||||
font.family: Style.current.fontBold.name
|
||||
color: Style.current.textColor
|
||||
height: 18
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.top: txtLabel.bottom
|
||||
anchors.topMargin: 7
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 8
|
||||
|
||||
AddressInput {
|
||||
id: inpAddress
|
||||
width: root.inputWidth
|
||||
label: ""
|
||||
Layout.preferredWidth: root.inputWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
validationError: qsTr("Invalid ethereum address")
|
||||
onSelectedAddressChanged: {
|
||||
root.selectedRecipient = selectedAddress
|
||||
}
|
||||
}
|
||||
|
||||
ContactSelector {
|
||||
id: selContact
|
||||
contacts: root.contacts
|
||||
visible: false
|
||||
width: root.inputWidth
|
||||
dropdownWidth: parent.width
|
||||
Layout.preferredWidth: root.inputWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onSelectedContactChanged: {
|
||||
if(selectedContact && selectedContact.address) {
|
||||
root.selectedRecipient = selectedContact.address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccountSelector {
|
||||
id: selAccount
|
||||
accounts: root.accounts
|
||||
visible: false
|
||||
width: root.inputWidth
|
||||
dropdownWidth: parent.width
|
||||
label: ""
|
||||
Layout.preferredWidth: root.inputWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
onSelectedAccountChanged: {
|
||||
root.selectedRecipient = selectedAccount.address
|
||||
}
|
||||
}
|
||||
AddressSourceSelector {
|
||||
id: selAddressSource
|
||||
sources: ["Address", "Contact", "My account"]
|
||||
width: sourceSelectWidth
|
||||
Layout.preferredWidth: root.sourceSelectWidth
|
||||
Layout.alignment: Qt.AlignTop
|
||||
|
||||
onSelectedSourceChanged: {
|
||||
switch (selectedSource) {
|
||||
case "Address":
|
||||
inpAddress.visible = true
|
||||
selContact.visible = selAccount.visible = false
|
||||
root.height = Qt.binding(function() { return inpAddress.height + txtLabel.height })
|
||||
break;
|
||||
case "Contact":
|
||||
selContact.visible = true
|
||||
inpAddress.visible = selAccount.visible = false
|
||||
root.height = Qt.binding(function() { return selContact.height + txtLabel.height })
|
||||
break;
|
||||
case "My account":
|
||||
selAccount.visible = true
|
||||
inpAddress.visible = selContact.visible = false
|
||||
root.height = Qt.binding(function() { return selAccount.height + txtLabel.height })
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*##^##
|
||||
Designer {
|
||||
D{i:0;autoSize:true;height:480;width:640}
|
||||
}
|
||||
##^##*/
|
|
@ -5,6 +5,10 @@ import QtGraphicalEffects 1.13
|
|||
import "../imports"
|
||||
|
||||
Item {
|
||||
enum MenuAlignment {
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
property string label: ""
|
||||
readonly property bool hasLabel: label !== ""
|
||||
property color bgColor: Style.current.inputBackground
|
||||
|
@ -15,6 +19,8 @@ Item {
|
|||
property alias selectedItemView: selectedItemContainer.children
|
||||
property int caretRightMargin: Style.current.padding
|
||||
property alias select: inputRectangle
|
||||
property int menuAlignment: Select.MenuAlignment.Right
|
||||
property Item zeroItemsView: Item {}
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
@ -105,7 +111,24 @@ Item {
|
|||
Repeater {
|
||||
id: menuItems
|
||||
model: root.model
|
||||
property int zeroItemsViewHeight
|
||||
delegate: selectMenu.delegate
|
||||
onItemAdded: {
|
||||
root.zeroItemsView.visible = false
|
||||
root.zeroItemsView.height = 0
|
||||
}
|
||||
onItemRemoved: {
|
||||
if (count === 0) {
|
||||
root.zeroItemsView.visible = true
|
||||
root.zeroItemsView.height = zeroItemsViewHeight
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
zeroItemsViewHeight = root.zeroItemsView.height
|
||||
root.zeroItemsView.visible = count === 0
|
||||
root.zeroItemsView.height = count !== 0 ? 0 : root.zeroItemsView.height
|
||||
selectMenu.insertItem(0, root.zeroItemsView)
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
|
@ -123,7 +146,8 @@ Item {
|
|||
if (selectMenu.opened) {
|
||||
selectMenu.close()
|
||||
} else {
|
||||
const offset = inputRectangle.width - selectMenu.width
|
||||
const rightOffset = inputRectangle.width - selectMenu.width
|
||||
const offset = root.menuAlignment === Select.MenuAlignment.Left ? 0 : rightOffset
|
||||
selectMenu.popup(inputRectangle.x + offset, inputRectangle.y + inputRectangle.height + 8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import "../imports"
|
||||
|
||||
Button {
|
||||
id: root
|
||||
property alias label: txtBtnLabel.text
|
||||
|
||||
width: txtBtnLabel.width + 2 * 12
|
||||
height: txtBtnLabel.height + 2 * 6
|
||||
|
||||
background: Rectangle {
|
||||
color: Style.current.backgroundTertiary
|
||||
radius: 6
|
||||
anchors.fill: parent
|
||||
border.color: Style.current.borderTertiary
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: txtBtnLabel
|
||||
color: Style.current.textColorTertiary
|
||||
font.pixelSize: 12
|
||||
height: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: qsTr("Paste")
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
parent.clicked()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue