mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
Core: CButton, CIconButton, CFab, CChip, CListItem — Button roles, activeFocusOnTab Forms: CTextField, CSelect, CCheckbox, CSwitch, CRadio, CRating — EditableText, CheckBox, ComboBox, Slider Feedback: CAlert, CDialog, CSnackbar — AlertMessage, Dialog roles Navigation: CTabBar — PageTabList + PageTab on delegates Data: CAvatar, CBadge, CTable, CStatBadge, CStatusBadge — Graphic, StaticText, Table, Row Surfaces: CCard (Pane), CAccordionItem (Button + expanded), CAppBar (ToolBar) Progress: CProgress (ProgressBar + value), CSpinner (Animation) Divider: CDivider (Separator) 28 files, 157 lines of a11y properties added. Zero to full coverage on core library. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
211 lines
7.1 KiB
QML
211 lines
7.1 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QmlComponents 1.0
|
|
|
|
/**
|
|
* CTable.qml - Material Design 3 data table
|
|
* Surface container header, 48px rows, hover state layer, outlineVariant
|
|
// borders
|
|
*
|
|
* Usage:
|
|
* CTable {
|
|
* headers: ["Name", "Email", "Role"]
|
|
* rows: [["Alice", "alice@co", "Admin"], ["Bob", "bob@co", "User"]]
|
|
* sortColumn: 0; sortAscending: true
|
|
* }
|
|
*/
|
|
Rectangle {
|
|
id: root
|
|
|
|
property var headers: [] // Array of header strings
|
|
property var rows: [] // Array of row arrays
|
|
property var columnWidths: [] // Optional column width ratios
|
|
property bool striped: true
|
|
property bool bordered: true
|
|
|
|
// MD3 sort support
|
|
// Column index currently sorted (-1 = none)
|
|
property int sortColumn: -1
|
|
property bool sortAscending: true
|
|
signal headerClicked(int columnIndex)
|
|
|
|
Accessible.role: Accessible.Table
|
|
|
|
color: "transparent"
|
|
radius: StyleVariables.radiusSm
|
|
border.width: bordered ? 1 : 0
|
|
border.color: Theme.border
|
|
|
|
implicitWidth: parent ? parent.width : 400
|
|
implicitHeight: tableCol.implicitHeight
|
|
|
|
clip: true
|
|
|
|
ColumnLayout {
|
|
id: tableCol
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
// Header row - MD3 surfaceContainer background
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
implicitHeight: 48
|
|
color: Theme.mode === "dark"
|
|
? Qt.rgba(1, 1, 1, 0.08)
|
|
: Qt.rgba(0, 0, 0, 0.04)
|
|
|
|
RowLayout {
|
|
id: headerRow
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
Repeater {
|
|
model: root.headers
|
|
|
|
Item {
|
|
Layout.fillWidth: root.columnWidths.length === 0
|
|
Layout.preferredWidth: root.columnWidths.length > index
|
|
? root.columnWidths[index] : -1
|
|
implicitHeight: 48
|
|
|
|
// MD3 header cell content
|
|
Row {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 16
|
|
spacing: 4
|
|
|
|
Text {
|
|
text: modelData
|
|
color: Theme.textSecondary
|
|
font.pixelSize: 14
|
|
font.weight: Font.DemiBold
|
|
font.family: Theme.fontFamily
|
|
elide: Text.ElideRight
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
// Sort indicator arrow
|
|
Text {
|
|
visible: root.sortColumn === index
|
|
text: root.sortAscending ? "\u25B2" : "\u25BC"
|
|
color: Theme.textSecondary
|
|
font.pixelSize: 10
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
|
|
// Clickable area for sorting
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: root.headerClicked(index)
|
|
}
|
|
|
|
// Column separator
|
|
Rectangle {
|
|
visible: root.bordered && index > 0
|
|
width: 1
|
|
height: parent.height
|
|
anchors.left: parent.left
|
|
color: Theme.border
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bottom border under header
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
height: 1
|
|
color: Theme.border
|
|
}
|
|
|
|
// Data rows
|
|
Repeater {
|
|
model: root.rows
|
|
|
|
Rectangle {
|
|
id: rowDelegate
|
|
Layout.fillWidth: true
|
|
implicitHeight: 48
|
|
|
|
Accessible.role: Accessible.Row
|
|
|
|
property bool hovered: rowMouse.containsMouse
|
|
|
|
// MD3: alternating tint + hover state layer (4%)
|
|
color: {
|
|
if (hovered)
|
|
return Theme.mode === "dark"
|
|
? Qt.rgba(1, 1, 1, 0.04)
|
|
: Qt.rgba(0, 0, 0, 0.04)
|
|
if (root.striped && index % 2 === 1)
|
|
return Theme.mode === "dark"
|
|
? Qt.rgba(1, 1, 1, 0.02)
|
|
: Qt.rgba(0, 0, 0, 0.02)
|
|
return "transparent"
|
|
}
|
|
|
|
MouseArea {
|
|
id: rowMouse
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.NoButton
|
|
}
|
|
|
|
// Row border (outlineVariant between rows)
|
|
Rectangle {
|
|
width: parent.width
|
|
height: root.bordered ? 1 : 0
|
|
anchors.bottom: parent.bottom
|
|
color: Theme.border
|
|
}
|
|
|
|
RowLayout {
|
|
id: dataRow
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
Repeater {
|
|
model: modelData
|
|
|
|
Item {
|
|
Layout.fillWidth: root.columnWidths.length === 0
|
|
Layout.preferredWidth: root.columnWidths.length >
|
|
index ? root.columnWidths[index] : -1
|
|
implicitHeight: 48
|
|
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.leftMargin: 16
|
|
anchors.rightMargin: 16
|
|
text: modelData
|
|
color: Theme.text
|
|
font.pixelSize: 14
|
|
font.family: Theme.fontFamily
|
|
elide: Text.ElideRight
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
|
|
// Column separator
|
|
Rectangle {
|
|
visible: root.bordered && index > 0
|
|
width: 1
|
|
height: parent.height
|
|
anchors.left: parent.left
|
|
color: Theme.border
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|