mirror of
https://github.com/logos-blockchain/lez-programs.git
synced 2026-07-26 00:33:17 +00:00
Add a "Create Pool" flow to the AMM app that lets a user open a new liquidity position — seeding a pool's initial liquidity — from token selection and amount entry, through a confirmation dialog, to on-chain submission. - client crate (apps/amm/client): pure, testable protocol logic — account decoding, pair/position modelling, and quote/plan computation — exposed to the app over a C ABI (config/networks.json drives network selection). - C++ runtime + backend: AmmClient, ActiveNetwork, and NewPositionRuntime, wired into AmmUiBackend (new resolve/quote/submit slots). - QML flow: NewPositionForm, NewPositionFlow state, NewPositionConfirmation- Dialog, TokenSelectorModal, and reusable Amm* presentational components (theme, surfaces, buttons) + AmountMath.js. - tests: C++ (NewPositionRuntimeTest, ActiveNetworkTest) and QML (tst_NewPositionForm, tst_LiquidityPage, tst_TokenAmountInput, …).
196 lines
6.5 KiB
QML
196 lines
6.5 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
|
|
Rectangle {
|
|
id: root
|
|
|
|
required property var theme
|
|
property string label: ""
|
|
property string amount: ""
|
|
property string supportingText: ""
|
|
property string errorText: ""
|
|
property string supportingActionText: ""
|
|
property bool readOnly: false
|
|
property bool muted: false
|
|
property bool invalid: root.errorText.length > 0
|
|
property Component accessory
|
|
property real accessoryWidth: 0
|
|
property real accessoryHeight: 0
|
|
property Component adjustment
|
|
property real adjustmentWidth: 0
|
|
property real adjustmentHeight: 0
|
|
|
|
signal amountEdited(string value)
|
|
signal amountEditingFinished(string value)
|
|
signal supportingActionClicked
|
|
|
|
implicitHeight: Math.max(110, contentRow.implicitHeight + 24)
|
|
radius: 16
|
|
color: root.muted ? root.theme.colors.panelBg : root.theme.colors.inputBg
|
|
border.color: root.invalid
|
|
? root.theme.colors.error
|
|
: amountInput.activeFocus
|
|
? root.theme.colors.ctaBg
|
|
: "transparent"
|
|
border.width: 1
|
|
|
|
Binding {
|
|
target: amountInput
|
|
property: "text"
|
|
value: root.amount
|
|
}
|
|
|
|
RowLayout {
|
|
id: contentRow
|
|
|
|
anchors.fill: parent
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 16
|
|
anchors.topMargin: 12
|
|
anchors.bottomMargin: 12
|
|
spacing: 10
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 2
|
|
|
|
Text {
|
|
text: root.label
|
|
color: root.theme.colors.textSecondary
|
|
font.pixelSize: 13
|
|
elide: Text.ElideRight
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
visible: root.errorText.length > 0
|
|
text: root.errorText
|
|
color: root.theme.colors.error
|
|
font.pixelSize: 11
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 44
|
|
|
|
TextInput {
|
|
id: amountInput
|
|
|
|
anchors.fill: parent
|
|
readOnly: root.readOnly
|
|
color: root.readOnly || root.muted
|
|
? root.theme.colors.textSecondary
|
|
: root.theme.colors.textPrimary
|
|
font.pixelSize: {
|
|
var length = Math.max(1, String(root.amount || "0").length)
|
|
return Math.max(14, Math.min(34, Math.floor(width / (length * 0.68))))
|
|
}
|
|
font.weight: Font.Bold
|
|
horizontalAlignment: Text.AlignLeft
|
|
selectionColor: root.theme.colors.selection
|
|
selectedTextColor: root.theme.colors.textPrimary
|
|
clip: true
|
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
|
maximumLength: 80
|
|
validator: RegularExpressionValidator {
|
|
regularExpression: /^[0-9]*\.?[0-9]*$/
|
|
}
|
|
|
|
Accessible.name: root.label
|
|
|
|
onTextEdited: {
|
|
if (!root.readOnly)
|
|
root.amountEdited(text)
|
|
}
|
|
onEditingFinished: {
|
|
if (!root.readOnly)
|
|
root.amountEditingFinished(text)
|
|
}
|
|
}
|
|
|
|
Text {
|
|
anchors.fill: parent
|
|
text: "0"
|
|
color: root.theme.colors.textPlaceholder
|
|
font: amountInput.font
|
|
visible: amountInput.text === "" && !amountInput.activeFocus
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 6
|
|
|
|
Loader {
|
|
active: root.adjustment !== null
|
|
visible: active
|
|
sourceComponent: root.adjustment
|
|
Layout.preferredWidth: active ? root.adjustmentWidth : 0
|
|
Layout.preferredHeight: active ? root.adjustmentHeight : 0
|
|
}
|
|
|
|
Button {
|
|
id: supportingAction
|
|
|
|
visible: root.supportingActionText.length > 0
|
|
enabled: visible && !root.readOnly
|
|
implicitWidth: 40
|
|
implicitHeight: 24
|
|
text: root.supportingActionText
|
|
hoverEnabled: true
|
|
Accessible.name: text
|
|
onClicked: root.supportingActionClicked()
|
|
|
|
contentItem: Text {
|
|
text: supportingAction.text
|
|
color: supportingAction.enabled
|
|
? root.theme.colors.ctaBg
|
|
: root.theme.colors.textPlaceholder
|
|
font.pixelSize: 11
|
|
font.weight: Font.Bold
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
background: Rectangle {
|
|
radius: 6
|
|
color: supportingAction.hovered || supportingAction.activeFocus
|
|
? root.theme.colors.selection : "transparent"
|
|
}
|
|
}
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
text: root.supportingText
|
|
color: root.theme.colors.textSecondary
|
|
font.pixelSize: 11
|
|
horizontalAlignment: root.adjustment !== null
|
|
|| root.supportingActionText.length > 0
|
|
? Text.AlignRight : Text.AlignLeft
|
|
elide: Text.ElideRight
|
|
visible: text.length > 0
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
sourceComponent: root.accessory
|
|
visible: status === Loader.Ready
|
|
Layout.alignment: Qt.AlignTop
|
|
Layout.topMargin: 17
|
|
Layout.preferredWidth: root.accessoryWidth
|
|
Layout.preferredHeight: root.accessoryHeight
|
|
}
|
|
}
|
|
|
|
Behavior on color {
|
|
ColorAnimation { duration: 180 }
|
|
}
|
|
}
|