mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-05-18 23:19:30 +00:00
chore(amm-ui): add basic swap UI for Token Pair Selector & Swap Direction
closes #55
This commit is contained in:
parent
29d949d75a
commit
ee2ecfb80a
@ -1,30 +0,0 @@
|
|||||||
import QtQuick 2.15
|
|
||||||
import QtQuick.Controls 2.15
|
|
||||||
import QtQuick.Layouts 1.15
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: 400
|
|
||||||
height: 300
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
Text {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: "Hello from ui_qml_example!"
|
|
||||||
font.pixelSize: 18
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
text: "Call Core Module"
|
|
||||||
onClicked: {
|
|
||||||
// The logos bridge is injected by the host application.
|
|
||||||
// Uncomment to call a backend module:
|
|
||||||
// var result = logos.callModule("my_module", "myMethod", ["arg"])
|
|
||||||
console.log("Button clicked")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,7 +4,7 @@
|
|||||||
"type": "ui_qml",
|
"type": "ui_qml",
|
||||||
"category": "amm",
|
"category": "amm",
|
||||||
"description": "UI module for the AMM program",
|
"description": "UI module for the AMM program",
|
||||||
"view": "Main.qml",
|
"view": "qml/Main.qml",
|
||||||
"icon": "icons/amm.png",
|
"icon": "icons/amm.png",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
|
|
||||||
|
|||||||
152
amm-ui/qml/Main.qml
Normal file
152
amm-ui/qml/Main.qml
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var tokenData: [
|
||||||
|
{ symbol: "TOK1", name: "Token 1", color: "#627eea", letter: "E", address: "0x0000000000000000000000000000000000000000", usdPrice: 2392.70 },
|
||||||
|
{ symbol: "TOK2", name: "Token 2", color: "#2775ca", letter: "$", address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", usdPrice: 1.00 },
|
||||||
|
{ symbol: "TOK3", name: "Token 3", color: "#26a17b", letter: "T", address: "0xdac17f958d2ee523a2206206994597c13d831ec7", usdPrice: 1.00 },
|
||||||
|
{ symbol: "TOK4", name: "Token 4", color: "#f7931a", letter: "B", address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", usdPrice: 63500 },
|
||||||
|
{ symbol: "TOK5", name: "Token 5", color: "#627eea", letter: "E", address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", usdPrice: 2392.70 },
|
||||||
|
{ symbol: "TOK6", name: "Token 6", color: "#9b59b6", letter: "L", address: "0x1337000000000000000000000000000000000cafe", usdPrice: 0.42 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// ── Theme ─────────────────────────────────────────────────────────────────
|
||||||
|
QtObject {
|
||||||
|
id: theme
|
||||||
|
property bool isDark: false
|
||||||
|
property var colors: isDark ? dark : light
|
||||||
|
|
||||||
|
readonly property var light: ({
|
||||||
|
background: "#f7f7f5",
|
||||||
|
cardBg: "#ffffff",
|
||||||
|
inputBg: "#f0f0ee",
|
||||||
|
panelBg: "#e8e8e4",
|
||||||
|
panelHoverBg: "#ddddd8",
|
||||||
|
textPrimary: "#111111",
|
||||||
|
textSecondary: "#777770",
|
||||||
|
textPlaceholder: "#bbbbb5",
|
||||||
|
border: Qt.rgba(0,0,0,0.08),
|
||||||
|
borderStrong: Qt.rgba(0,0,0,0.10),
|
||||||
|
divider: Qt.rgba(0,0,0,0.06),
|
||||||
|
ctaBg: "#111111",
|
||||||
|
ctaHoverBg: "#2a2a28",
|
||||||
|
selection: "#b5c4a5",
|
||||||
|
noTokenCircle: "#c8c8c4",
|
||||||
|
orb1: "#7a8c6a",
|
||||||
|
orb2: "#b5c4a5",
|
||||||
|
orb3: "#7a8c6a",
|
||||||
|
orb4: "#c8d4b8"
|
||||||
|
})
|
||||||
|
|
||||||
|
readonly property var dark: ({
|
||||||
|
background: "#0d0d12",
|
||||||
|
cardBg: "#1a1a22",
|
||||||
|
inputBg: "#222230",
|
||||||
|
panelBg: "#2a2a38",
|
||||||
|
panelHoverBg: "#363650",
|
||||||
|
textPrimary: "#ffffff",
|
||||||
|
textSecondary: "#888899",
|
||||||
|
textPlaceholder: "#444455",
|
||||||
|
border: Qt.rgba(1,1,1,0.08),
|
||||||
|
borderStrong: Qt.rgba(1,1,1,0.10),
|
||||||
|
divider: Qt.rgba(1,1,1,0.06),
|
||||||
|
ctaBg: "#2d1530",
|
||||||
|
ctaHoverBg: "#3d1f40",
|
||||||
|
selection: "#4c1d4b",
|
||||||
|
noTokenCircle: "#444455",
|
||||||
|
orb1: "#627eea",
|
||||||
|
orb2: "#9b59b6",
|
||||||
|
orb3: "#fc72ff",
|
||||||
|
orb4: "#26a17b"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Root background ───────────────────────────────────────────────────────
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: theme.colors.background
|
||||||
|
Behavior on color { ColorAnimation { duration: 300 } }
|
||||||
|
|
||||||
|
// Theme toggle
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: 16
|
||||||
|
width: 44; height: 24; radius: 12
|
||||||
|
color: theme.colors.panelBg
|
||||||
|
border.color: theme.colors.border
|
||||||
|
border.width: 1
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: theme.isDark ? "☀" : "☾"
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: theme.colors.textSecondary
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: theme.isDark = !theme.isDark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorative orbs
|
||||||
|
Rectangle { x: -180; y: -120; width: 560; height: 560; radius: 280; color: theme.colors.orb1; opacity: 0.07 }
|
||||||
|
Rectangle { x: parent.width - 280; y: parent.height - 320; width: 480; height: 480; radius: 240; color: theme.colors.orb2; opacity: 0.09 }
|
||||||
|
Rectangle { x: parent.width - 200; y: -80; width: 380; height: 380; radius: 190; color: theme.colors.orb3; opacity: 0.05 }
|
||||||
|
Rectangle { x: 40; y: parent.height - 260; width: 320; height: 320; radius: 160; color: theme.colors.orb4; opacity: 0.08 }
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 28
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: "Logos AMM"
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 48
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
|
||||||
|
SwapCard {
|
||||||
|
id: swapCard
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
theme: theme
|
||||||
|
tokens: root.tokenData
|
||||||
|
width: Math.min(480, root.width - 32)
|
||||||
|
|
||||||
|
onRequestTokenSelect: function(side) {
|
||||||
|
tokenModal.targetSide = side
|
||||||
|
tokenModal.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: "Buy and sell crypto on <font color='" + theme.colors.textPrimary + "'>LEZ</font>."
|
||||||
|
textFormat: Text.RichText
|
||||||
|
color: theme.colors.textSecondary
|
||||||
|
font.pixelSize: 15
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenSelectorModal {
|
||||||
|
id: tokenModal
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 10
|
||||||
|
theme: theme
|
||||||
|
tokens: root.tokenData
|
||||||
|
|
||||||
|
property string targetSide: "sell"
|
||||||
|
|
||||||
|
onTokenSelected: function(tok) {
|
||||||
|
swapCard.setToken(targetSide, tok)
|
||||||
|
tokenModal.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
amm-ui/qml/SwapCard.qml
Normal file
153
amm-ui/qml/SwapCard.qml
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var theme
|
||||||
|
property var tokens: []
|
||||||
|
property var sellToken: null
|
||||||
|
property var buyToken: null
|
||||||
|
property string sellAmount: ""
|
||||||
|
|
||||||
|
signal requestTokenSelect(string side)
|
||||||
|
|
||||||
|
function setToken(side, token) {
|
||||||
|
if (side === "sell") root.sellToken = token
|
||||||
|
else root.buyToken = token
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string buyAmount: {
|
||||||
|
if (!sellToken || !buyToken || sellAmount === "") return ""
|
||||||
|
var amt = parseFloat(sellAmount)
|
||||||
|
if (isNaN(amt) || amt <= 0) return ""
|
||||||
|
var result = amt * sellToken.usdPrice / buyToken.usdPrice
|
||||||
|
return result >= 1 ? result.toFixed(2) : result.toFixed(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string sellUsd: {
|
||||||
|
if (!sellToken || sellAmount === "") return ""
|
||||||
|
var amt = parseFloat(sellAmount)
|
||||||
|
if (isNaN(amt)) return ""
|
||||||
|
var val = amt * sellToken.usdPrice
|
||||||
|
return "~$" + val.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property string buyUsd: {
|
||||||
|
if (!buyToken || buyAmount === "") return ""
|
||||||
|
var amt = parseFloat(buyAmount)
|
||||||
|
if (isNaN(amt)) return ""
|
||||||
|
var val = amt * buyToken.usdPrice
|
||||||
|
return "~$" + val.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: 24
|
||||||
|
color: theme.colors.cardBg
|
||||||
|
border.color: theme.colors.border
|
||||||
|
border.width: 1
|
||||||
|
implicitWidth: 480
|
||||||
|
implicitHeight: cardLayout.implicitHeight + 16
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 300 } }
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: cardLayout
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: 8
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
TokenInput {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
theme: root.theme
|
||||||
|
label: "Sell"
|
||||||
|
amount: root.sellAmount
|
||||||
|
usdValue: root.sellUsd
|
||||||
|
token: root.sellToken
|
||||||
|
readOnly: false
|
||||||
|
onInputEdited: function(v) { root.sellAmount = v }
|
||||||
|
onTokenClicked: root.requestTokenSelect("sell")
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 1
|
||||||
|
color: theme.colors.divider
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 36; height: 36; radius: 18
|
||||||
|
color: swapHover.containsMouse ? theme.colors.panelHoverBg : theme.colors.panelBg
|
||||||
|
border.color: theme.colors.borderStrong
|
||||||
|
border.width: 1
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "↓"
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: swapHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
var tmp = root.sellToken
|
||||||
|
root.sellToken = root.buyToken
|
||||||
|
root.buyToken = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenInput {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
theme: root.theme
|
||||||
|
label: "Buy"
|
||||||
|
amount: root.buyAmount
|
||||||
|
usdValue: root.buyUsd
|
||||||
|
token: root.buyToken
|
||||||
|
readOnly: true
|
||||||
|
onTokenClicked: root.requestTokenSelect("buy")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 8
|
||||||
|
Layout.bottomMargin: 8
|
||||||
|
Layout.leftMargin: 8
|
||||||
|
Layout.rightMargin: 8
|
||||||
|
Layout.preferredHeight: 56
|
||||||
|
radius: 20
|
||||||
|
color: ctaHover.containsMouse ? theme.colors.ctaHoverBg : theme.colors.ctaBg
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Swap"
|
||||||
|
color: "#ffffff"
|
||||||
|
font.pixelSize: 17
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ctaHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
amm-ui/qml/TokenInput.qml
Normal file
133
amm-ui/qml/TokenInput.qml
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var theme
|
||||||
|
property string label: ""
|
||||||
|
property string amount: ""
|
||||||
|
property string usdValue: ""
|
||||||
|
property var token: null
|
||||||
|
property bool readOnly: false
|
||||||
|
|
||||||
|
signal tokenClicked()
|
||||||
|
signal inputEdited(string newValue)
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: tiInput
|
||||||
|
property: "text"
|
||||||
|
value: root.amount
|
||||||
|
when: root.readOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: 16
|
||||||
|
color: theme.colors.inputBg
|
||||||
|
implicitHeight: 110
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 300 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.rightMargin: 16
|
||||||
|
anchors.topMargin: 14
|
||||||
|
anchors.bottomMargin: 14
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.label
|
||||||
|
color: theme.colors.textSecondary
|
||||||
|
font.pixelSize: 14
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 44
|
||||||
|
|
||||||
|
TextInput {
|
||||||
|
id: tiInput
|
||||||
|
anchors.fill: parent
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 36
|
||||||
|
font.weight: Font.Bold
|
||||||
|
readOnly: root.readOnly
|
||||||
|
selectionColor: theme.colors.selection
|
||||||
|
clip: true
|
||||||
|
onTextChanged: { if (!root.readOnly) root.inputEdited(text) }
|
||||||
|
validator: RegularExpressionValidator {
|
||||||
|
regularExpression: /^[0-9]*\.?[0-9]*$/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
text: "0"
|
||||||
|
color: theme.colors.textPlaceholder
|
||||||
|
font: tiInput.font
|
||||||
|
visible: tiInput.text === "" && !tiInput.activeFocus
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.usdValue
|
||||||
|
color: theme.colors.textSecondary
|
||||||
|
font.pixelSize: 13
|
||||||
|
visible: root.usdValue !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 40
|
||||||
|
radius: 20
|
||||||
|
color: tokenBtnHover.containsMouse ? theme.colors.panelHoverBg : theme.colors.panelBg
|
||||||
|
implicitWidth: tokenBtnRow.implicitWidth + 24
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: tokenBtnRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 24; height: 24; radius: 12
|
||||||
|
color: root.token ? root.token.color : theme.colors.noTokenCircle
|
||||||
|
visible: root.token !== null
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.token ? root.token.letter : ""
|
||||||
|
color: "#ffffff"
|
||||||
|
font.pixelSize: 10
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.token ? root.token.symbol : "Select token"
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 15
|
||||||
|
font.weight: root.token ? Font.Medium : Font.Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "▼"
|
||||||
|
color: theme.colors.textSecondary
|
||||||
|
font.pixelSize: 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: tokenBtnHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.tokenClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
amm-ui/qml/TokenListItem.qml
Normal file
76
amm-ui/qml/TokenListItem.qml
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var theme
|
||||||
|
property string tokenName: ""
|
||||||
|
property string tokenSymbol: ""
|
||||||
|
property string tokenAddress: ""
|
||||||
|
property string tokenColor: "#627eea"
|
||||||
|
property string tokenLetter: ""
|
||||||
|
|
||||||
|
signal clicked()
|
||||||
|
|
||||||
|
implicitHeight: 56
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: 12
|
||||||
|
color: hoverArea.containsMouse ? theme.colors.panelBg : "transparent"
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 12
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 36; height: 36; radius: 18
|
||||||
|
color: root.tokenColor
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.tokenLetter
|
||||||
|
color: "#ffffff"
|
||||||
|
font.pixelSize: 14
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.tokenName
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 15
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 6
|
||||||
|
Text { text: root.tokenSymbol; color: theme.colors.textSecondary; font.pixelSize: 12 }
|
||||||
|
Text {
|
||||||
|
text: root.tokenAddress !== ""
|
||||||
|
? root.tokenAddress.substring(0, 6) + "..." + root.tokenAddress.slice(-4)
|
||||||
|
: ""
|
||||||
|
color: theme.colors.textPlaceholder
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: hoverArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
173
amm-ui/qml/TokenSelectorModal.qml
Normal file
173
amm-ui/qml/TokenSelectorModal.qml
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var theme
|
||||||
|
property var tokens: []
|
||||||
|
property string searchText: ""
|
||||||
|
|
||||||
|
signal tokenSelected(var token)
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
root.visible = true
|
||||||
|
searchText = ""
|
||||||
|
searchField.text = ""
|
||||||
|
searchField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
root.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.4)
|
||||||
|
MouseArea { anchors.fill: parent; onClicked: root.close() }
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(480, root.width - 32)
|
||||||
|
height: Math.min(600, root.height - 64)
|
||||||
|
radius: 24
|
||||||
|
color: theme.colors.cardBg
|
||||||
|
border.color: theme.colors.border
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on color { ColorAnimation { duration: 300 } }
|
||||||
|
|
||||||
|
MouseArea { anchors.fill: parent; onClicked: {} }
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 20
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Text {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: "Select a token"
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 18
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
width: 32; height: 32; radius: 16
|
||||||
|
color: closeHover.containsMouse ? theme.colors.panelHoverBg : theme.colors.panelBg
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
Text { anchors.centerIn: parent; text: "✕"; color: theme.colors.textSecondary; font.pixelSize: 14 }
|
||||||
|
MouseArea {
|
||||||
|
id: closeHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 48
|
||||||
|
radius: 16
|
||||||
|
color: theme.colors.inputBg
|
||||||
|
border.color: searchField.activeFocus ? theme.colors.borderStrong : theme.colors.border
|
||||||
|
border.width: 1
|
||||||
|
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 14
|
||||||
|
anchors.rightMargin: 14
|
||||||
|
spacing: 8
|
||||||
|
Text { text: "⌕"; color: theme.colors.textSecondary; font.pixelSize: 20 }
|
||||||
|
TextInput {
|
||||||
|
id: searchField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: theme.colors.textPrimary
|
||||||
|
font.pixelSize: 15
|
||||||
|
selectionColor: theme.colors.selection
|
||||||
|
onTextChanged: root.searchText = text
|
||||||
|
Text {
|
||||||
|
anchors.fill: parent
|
||||||
|
text: "Search tokens"
|
||||||
|
color: theme.colors.textPlaceholder
|
||||||
|
font: searchField.font
|
||||||
|
visible: searchField.text === "" && !searchField.activeFocus
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text { text: "Popular tokens"; color: theme.colors.textSecondary; font.pixelSize: 13 }
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 8
|
||||||
|
Repeater {
|
||||||
|
model: root.tokens.slice(0, 5)
|
||||||
|
delegate: Rectangle {
|
||||||
|
height: 40
|
||||||
|
radius: 20
|
||||||
|
color: pillHover.containsMouse ? theme.colors.panelHoverBg : theme.colors.panelBg
|
||||||
|
border.color: theme.colors.border
|
||||||
|
border.width: 1
|
||||||
|
width: pillRow.implicitWidth + 24
|
||||||
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||||||
|
RowLayout {
|
||||||
|
id: pillRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 6
|
||||||
|
Rectangle {
|
||||||
|
width: 22; height: 22; radius: 11
|
||||||
|
color: modelData.color
|
||||||
|
Text { anchors.centerIn: parent; text: modelData.letter; color: "#ffffff"; font.pixelSize: 10; font.weight: Font.Bold }
|
||||||
|
}
|
||||||
|
Text { text: modelData.symbol; color: theme.colors.textPrimary; font.pixelSize: 13; font.weight: Font.Medium }
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: pillHover
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.tokenSelected(modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text { text: "Tokens by 24H volume"; color: theme.colors.textSecondary; font.pixelSize: 13 }
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: tokenList
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 2
|
||||||
|
model: root.tokens.filter(function(t) {
|
||||||
|
if (root.searchText === "") return true
|
||||||
|
var q = root.searchText.toLowerCase()
|
||||||
|
return t.symbol.toLowerCase().indexOf(q) !== -1 ||
|
||||||
|
t.name.toLowerCase().indexOf(q) !== -1
|
||||||
|
})
|
||||||
|
delegate: TokenListItem {
|
||||||
|
width: tokenList.width
|
||||||
|
theme: root.theme
|
||||||
|
tokenName: modelData.name
|
||||||
|
tokenSymbol: modelData.symbol
|
||||||
|
tokenAddress: modelData.address
|
||||||
|
tokenColor: modelData.color
|
||||||
|
tokenLetter: modelData.letter
|
||||||
|
onClicked: root.tokenSelected(modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user