import QtQuick import qs.Common import qs.Widgets Column { id: root required property string settingKey required property string label property string description: "" property var fields: [] property var defaultValue: [] property var items: defaultValue width: parent.width spacing: Theme.spacingM property bool isLoading: false Component.onCompleted: { loadValue(); } function loadValue() { const settings = findSettings(); if (settings) { isLoading = true; items = settings.loadValue(settingKey, defaultValue); isLoading = false; } } onItemsChanged: { if (isLoading) { return; } const settings = findSettings(); if (settings) { settings.saveValue(settingKey, items); } } function findSettings() { let item = parent; while (item) { if (item.saveValue !== undefined && item.loadValue !== undefined) { return item; } item = item.parent; } return null; } function addItem(item) { items = items.concat([item]); } function removeItem(index) { const newItems = items.slice(); newItems.splice(index, 1); items = newItems; } StyledText { text: root.label font.pixelSize: Theme.fontSizeMedium font.weight: Font.Medium color: Theme.surfaceText } StyledText { text: root.description font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width wrapMode: Text.WordWrap visible: root.description !== "" } Flow { width: parent.width spacing: Theme.spacingS Repeater { model: root.fields StyledText { text: modelData.label font.pixelSize: Theme.fontSizeSmall font.weight: Font.Medium color: Theme.surfaceText width: modelData.width || 200 } } } Flow { id: inputRow width: parent.width spacing: Theme.spacingS property var inputFields: [] Repeater { id: inputRepeater model: root.fields DankTextField { width: modelData.width || 200 placeholderText: modelData.placeholder || "" Component.onCompleted: { inputRow.inputFields.push(this); } Keys.onReturnPressed: { addButton.clicked(); } } } DankButton { id: addButton width: 50 height: 36 text: I18n.tr("Add") onClicked: { let newItem = {}; let hasValue = false; for (let i = 0; i < root.fields.length; i++) { const field = root.fields[i]; const input = inputRow.inputFields[i]; const value = input.text.trim(); if (value !== "") { hasValue = true; } if (field.required && value === "") { return; } newItem[field.id] = value || (field.default || ""); } if (hasValue) { root.addItem(newItem); for (let i = 0; i < inputRow.inputFields.length; i++) { inputRow.inputFields[i].text = ""; } if (inputRow.inputFields.length > 0) { inputRow.inputFields[0].forceActiveFocus(); } } } } } StyledText { text: I18n.tr("Current Items") font.pixelSize: Theme.fontSizeMedium font.weight: Font.Medium color: Theme.surfaceText visible: root.items.length > 0 } Column { width: parent.width spacing: Theme.spacingS Repeater { model: root.items StyledRect { width: parent.width height: 40 radius: Theme.cornerRadius color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) border.width: 0 required property int index required property var modelData Row { id: itemRow anchors.left: parent.left anchors.leftMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingM property var itemData: parent.modelData Repeater { model: root.fields StyledText { required property int index required property var modelData text: { const field = modelData; const item = itemRow.itemData; if (!field || !field.id || !item) { return ""; } const value = item[field.id]; return value || ""; } color: Theme.surfaceText font.pixelSize: Theme.fontSizeMedium width: modelData ? (modelData.width || 200) : 200 elide: Text.ElideRight } } } Rectangle { anchors.right: parent.right anchors.rightMargin: Theme.spacingM anchors.verticalCenter: parent.verticalCenter width: 60 height: 28 color: removeArea.containsMouse ? Theme.errorHover : Theme.error radius: Theme.cornerRadius StyledText { anchors.centerIn: parent text: I18n.tr("Remove") color: Theme.onError font.pixelSize: Theme.fontSizeSmall font.weight: Font.Medium } MouseArea { id: removeArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { root.removeItem(index); } } } } } StyledText { text: I18n.tr("No items added yet") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText visible: root.items.length === 0 } } }