QML
← All components

Table

Display records in rows and columns, with sorting, filtering and selection for comparing data.

import Syidax.Core.Table

Supported QML types

TypeDefinition / role
Data table with column definitions, sorting, filtering and selection.

Learn by example

Previews are images of native Qt runs. Use the buttons below each image to view a result.

01 Basic composition

Compare the configuration and result of Basic composition.

Qt capture · image
Basic composition · Default
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "none"
        property bool striped: false
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

02 autoMerge · rowspan · colspan

Two ways to merge cells, both computed on the visible rows (after filtering and sorting), so what is adjacent after a sort is what merges.

Qt capture · image
autoMerge · rowspan · colspan · Default
QML code
import QtQuick
import Syidax.Core

Item {
    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    // Focused Gallery routes: keep only the selected API and its prerequisites.

    id: story
    property QtObject settings: QtObject { id: storyArgs; property bool autoMerge: true; property int rowspan: 2; property int colspan: 2 }

    readonly property var columns: [
        { key: "team", label: "Team", autoMerge: true && storyArgs.autoMerge, width: 120 },
        { key: "name", label: "Name" },
        { key: "score", label: "Score", align: "right", width: 100 }
    ]
    readonly property var rows: [
        { id: 1, team: "Platform", name: "Kim Minjun", score: 92, rowspan: "" === "rowspan" ? Math.max(1, Math.min(4, storyArgs.rowspan)) : 1 },
        { id: 2, team: "Platform", name: "Lee Seoyeon", score: 85 },
        { id: 3, team: "Design", name: "Park Jiho", score: 78 },
        { id: 4, team: "Design", name: "Choi Yuna", score: 96 },
        { id: 5, team: "Total", name: "", score: 351, colspan: 2 }
    ]

    SyTable {
        width: 560
        variant: "full"
        columns: story.columns
        rows: story.rows
    }
}

03 sortChanged · filterChanged · selectionChanged · rowClicked · columnResized

Every signal passes one detail object.

Qt capture · image
sortChanged · filterChanged · selectionChanged · rowClicked · columnResized · Default
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    // Focused Gallery routes: keep only the selected API and its prerequisites.

    id: story

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team", sortable: true },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            selectionMode: "multiple"; resizable: true
            onSortChanged: (detail) => { if (true) { output.log(("sortChanged: %1 %2").arg(detail.key).arg(detail.direction)) } }
            onFilterChanged: (detail) => { if (true) { output.log(("filterChanged: %1 = %2").arg(detail.key).arg(detail.value)) } }
            onSelectionChanged: (detail) => { if (true) { output.log(("selectionChanged: %1").arg(detail.selectedKeys.join(", "))) } }
            onRowClicked: (detail) => { if (true) { output.log(("rowClicked: %1").arg(detail.row.name)) } }
            onColumnResized: (detail) => { if (true) { output.log(("columnResized: %1 %2").arg(detail.key).arg(detail.width)) } }
            columns: story.columns
            rows: story.rows
        }
        EventLog { id: output }
    }
}

04 setSort · setColumnFilter · clearFilters · setSelectedKeys · clearSelection · setColumnWidth · setLoading

- setSort(key, direction) — sets sortKey and sortDirection (direction defaults to "asc"; "none" leaves the key set but turns sorting off) and emits sortChanged.

Qt capture · image
setSort · setColumnFilter · clearFilters · setSelectedKeys · clearSelection · setColumnWidth · setLoading · Default
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    // Focused Gallery routes: keep only the selected API and its prerequisites.

    id: story
    readonly property var columns: [
        { key: "name", label: "Name", sortable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 }
    ]

    Column {
        spacing: 24
        SyTable { id: grid; width: 560; selectionMode: "multiple"; columns: story.columns; rows: story.rows }
        Flow {
            width: 560
            spacing: 8
            SyButton { id: exampleControl1;  visible: true; text: "grid.setSort(\"score\", \"desc\")"; variant: "secondary"; size: "small"; onClicked: grid.setSort("score", "desc") }
            SyButton { id: exampleControl2;  visible: true; text: "grid.setColumnFilter(\"name\", \"Kim\")"; variant: "secondary"; size: "small"; onClicked: grid.setColumnFilter("name", "Kim") }
            SyButton { id: exampleControl3;  visible: true; text: "grid.clearFilters()"; variant: "secondary"; size: "small"; onClicked: grid.clearFilters() }
            SyButton { visible: true; text: "grid.setSelectedKeys([1, 3])"; variant: "secondary"; size: "small"; onClicked: grid.setSelectedKeys([1, 3]) }
            SyButton { visible: true; text: "grid.clearSelection()"; variant: "secondary"; size: "small"; onClicked: grid.clearSelection() }
            SyButton { visible: true; text: "grid.setColumnWidth(\"name\", 220)"; variant: "secondary"; size: "small"; onClicked: grid.setColumnWidth("name", 220) }
            SyButton { visible: true; text: "grid.setLoading(!grid.loading)"; variant: "secondary"; size: "small"; onClicked: grid.setLoading(!grid.loading) }
            SyButton { visible: true; text: "console.log(grid.getSelectedRows().length)"; variant: "secondary"; size: "small"; onClicked: output.log(grid.getSelectedRows().length) }
        }
        Text {
            text: "grid.sortKey === " + JSON.stringify(grid.sortKey) + "   grid.selectedKeys.length === " + grid.selectedKeys.length + "   grid.visibleRows.length === " + grid.visibleRows.length
            font.family: Theme.monoFontFamily
            font.pixelSize: 12
            color: Theme.semantic.textSubtle
        }
        EventLog { id: output }
    }
}

05 Multiple row selection

Multiple row selection, configured with selectionMode.

Qt capture · image
Multiple row selection · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "multiple"
        property bool striped: false
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

06 Single row selection

Single row selection, configured with selectionMode.

Qt capture · image
Single row selection · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "single"
        property bool striped: false
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

07 Compact density

Compact density, configured with compact.

Qt capture · image
Compact density · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "none"
        property bool striped: false
        property bool compact: true
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

08 Resize columns

Resize columns, configured with resizable.

Qt capture · image
Resize columns · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "none"
        property bool striped: false
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: true
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

09 Striped rows

Striped rows, configured with striped.

Qt capture · image
Striped rows · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "none"
        property bool striped: true
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: false
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

10 Loading rows

Loading rows, configured with loading.

Qt capture · image
Loading rows · Result
Default execution
QML code
import QtQuick
import Syidax.Core

Item {
    component EventLog: Rectangle {
        id: logBox
        property var lines: []
        implicitWidth: 440
        implicitHeight: Math.max(56, logText.implicitHeight + 24)
        radius: 4; color: Theme.semantic.backgroundBold
        function log(value) { lines = lines.slice(-3).concat([String(value)]) }
        Text {
            id: logText; x: 12; y: 12; width: parent.width - 24
            text: logBox.lines.length ? logBox.lines.join("\n") : "Interact with the controls to see event results."
            wrapMode: Text.Wrap; color: Theme.semantic.textDefault
            font.family: Theme.monoFontFamily; font.pixelSize: 12
        }
    }

    implicitWidth: Math.max(360, childrenRect.width)
    implicitHeight: childrenRect.height
    id: story

    property QtObject settings: QtObject {
        id: storyArgs
        property string variant: "horizontal"
        property string selectionMode: "none"
        property bool striped: false
        property bool compact: false
        property bool hideHeader: false
        property bool rowHeader: false
        property bool resizable: false
        property bool loading: true
    }

    readonly property var columns: [
        { key: "name", label: "Name", sortable: true, filterable: true },
        { key: "team", label: "Team" },
        { key: "score", label: "Score", align: "right", sortable: true, width: 100 }
    ]
    readonly property var rows: [
        { id: 1, name: "Kim Minjun", team: "Platform", score: 92 },
        { id: 2, name: "Lee Seoyeon", team: "Platform", score: 85 },
        { id: 3, name: "Park Jiho", team: "Design", score: 78 },
        { id: 4, name: "Choi Yuna", team: "Design", score: 96 }
    ]

    Column {
        spacing: 12
        SyTable {
            width: 560
            variant: storyArgs.variant
            selectionMode: storyArgs.selectionMode
            striped: storyArgs.striped
            compact: storyArgs.compact
            hideHeader: storyArgs.hideHeader
            rowHeader: storyArgs.rowHeader
            resizable: storyArgs.resizable
            loading: storyArgs.loading
            columns: story.columns
            rows: story.rows
            onRowClicked: (detail) => output.log(detail.row.name)
        }
        EventLog { id: output }
    }

}

API 79

Hover or select an item for its QML example and copy button.

Properties

PropertyTypeDefaultDescription
var (array)[][{ key, label, sortable, filterable, width, minWidth, align, cellAlign, autoMerge, render }] — see Behavior for the defaults of each field.
var (array)[]One object per row, read by the column keys; may carry rowspan / colspan.
string"id"The field that identifies a row (compared as a string).
string"none""none" · "single" (round marker) · "multiple" (check boxes + header box).
string"horizontal"Lines and frame: "horizontal" · "vertical" · "full" · "outline" · "none".
string"solid""solid" draws the lines with the cells' borders; "dashed" / "dotted" draw them with a dash pattern in one line layer over the body.
boolfalseShades every second row.
boolfalse32 px rows instead of 40 (header included).
booltrueKeeps the header above the scrolling rows; false scrolls it away with them.
boolfalseHides the header row (sorting and filtering become unreachable from the pointer).
boolfalseDraws the first column like a header (background + medium weight).
string"No data"Centred in the body when no row is visible and loading is off.
string""The sorted column; assign it directly to sort without a signal.
string"none""none" · "asc" · "desc".
var (array)[]The selected row keys as strings; assigning it changes the selection without selectionChanged.
boolfalseShows the spinner overlay over the body.
boolfalseDrag grips at the right edge of every header cell.
var (object){}{ key: text } — the active filters; normally changed through setColumnFilter / clearFilters.
var (object){}{ key: px } — widths set by a drag or setColumnWidth; they override the columns' own width.
var (read-only)—The rows after filtering and sorting.
var (read-only)—The columns with every default filled in.
var (read-only)—{ widths, xs, total } — the column widths in px, their x offsets and the total width.
var (read-only)—The drawn cells: { r, c, x, y, w, h, text, merged, align, header }.
bool (read-only)—selectionMode !== "none" · no visible row and not loading.
bool (read-only)—selectionMode !== "none" · no visible row and not loading.
real (read-only)—Row height (40 / 32)
real (read-only)—the width of the selection column (40 or 0)
real (read-only)—the full content width.
int (read-only)—How many visible rows are selected (drives the header box).
var (read-only)—Alias of rows.

Methods

MethodDescription
Sets sortKey / sortDirection and emits sortChanged; "none" turns the sort off.
Sets (or, with an empty / null value, removes) a column filter and emits filterChanged.
A copy of columnFilters.
Removes every filter and emits filterChanged({ key: "", value: "", filters: {} }).
Replaces the selection (keys converted to strings, not validated) and emits selectionChanged.
Empties the selection and emits selectionChanged.
The row objects whose key is selected, in rows order.
Pins the column to width px (≤ 0 removes the pin) and emits columnResized.
Sets loading.
Scrolls the body to the first row.
Whether the row object is selected
its key as a string.

Signals

SignalArgumentsWhen
detail: { key, direction }A sortable header was clicked or setSort() ran. Not when sortKey / sortDirection are assigned.
detail: { key, value, filters }Every change in a filter popup, setColumnFilter(), and clearFilters() (then key: "", value: "", filters: {}).
detail: { selectedKeys, selectedRows }A row / box / header-box click, setSelectedKeys(), clearSelection(). Not when selectedKeys is assigned; never from clicks while selectionMode is "none".
detail: { row, index, key }Any click on a row body (index among the visible rows), before the selection toggles. Not from the selection cell.
detail: { key, width, widths }A resize drag ended, or setColumnWidth() ran.

Behavior

ItemBehavior / conditions
[{ key, label, sortable, filterable, width, minWidth, align, cellAlign, autoMerge, render }] — see Behavior for the defaults of each field.
Each entry of columns is normalised: label defaults to the key in title case; sortable, filterable and autoMerge default to false; width (a number, or a numeric string) 0 means *flexible*; minWidth defaults to 80; align ("left" · "center" · "right") positions the header text and is the default for cellAlign, which positions the cells; render(value, row, index) turns a raw value into the text to show (its result is converted to a string). For each column, a resized width wins, then the column's own width, otherwise the column is flexible: the flexible ones share the table width minus the selection column (40 px when selectionMode is not "none") minus the fixed widths, each at least its minWidth.
One object per row, read by the column keys; may carry rowspan / colspan.
The field that identifies a row (compared as a string).
Replaces the selection (keys converted to strings, not validated) and emits selectionChanged.
The rows after filtering and sorting.
{ key: text } — the active filters; normally changed through setColumnFilter / clearFilters.
The sorted column; assign it directly to sort without a signal.
"none" · "asc" · "desc".
A sortable header was clicked or setSort() ran. Not when sortKey / sortDirection are assigned.
Sets (or, with an empty / null value, removes) a column filter and emits filterChanged.
Drag grips at the right edge of every header cell.
{ key: px } — widths set by a drag or setColumnWidth; they override the columns' own width.
A resize drag ended, or setColumnWidth() ran.
"none" · "single" (round marker) · "multiple" (check boxes + header box).
Any click on a row body (index among the visible rows), before the selection toggles. Not from the selection cell.
The selected row keys as strings; assigning it changes the selection without selectionChanged.
Computed on the visible rows: first the row hints — a row with rowspan > 1 or colspan > 1 makes its column-0 cell cover that many rows and columns and marks the covered cells as taken — then autoMerge columns, where runs of consecutive rows with an identical value (and no taken cell) become one cell. Covered cells are not drawn; a merged cell gets the default background and, with row lines, its own border; its text follows the origin column's cellAlign.
Lines and frame: "horizontal" · "vertical" · "full" · "outline" · "none".
Shades every second row.
32 px rows instead of 40 (header included).
Draws the first column like a header (background + medium weight).
Hides the header row (sorting and filtering become unreachable from the pointer).
"solid" draws the lines with the cells' borders; "dashed" / "dotted" draw them with a dash pattern in one line layer over the body.
Keeps the header above the scrolling rows; false scrolls it away with them.
Shows the spinner overlay over the body.
Centred in the body when no row is visible and loading is off.
implicitWidth is 480 and implicitHeight is the header plus the rows (at least 64 px).
implicitWidth is 480 and implicitHeight is the header plus the rows (at least 64 px).
Give the table a fixed height to get a scrolling body — the header stays put.

Keyboard

ItemBehavior / conditions
SyCheckbox (the selection boxes), SySpinner (the loading overlay), SyInput (the filter field), SyPagination (to page long data sets), SyTree (hierarchies).

Filter and selection contract

ItemBehavior / conditions
Filters apply as you type. Escape/outside click dismisses without reverting. Reopening restores text without another filterChanged. Filter clicks do not sort. Supply stable unique rowKey values. Missing/duplicate keys use per-row-object identities: sorting/filtering retains selection, replacing objects clears it. width/minWidth use positive logical-pixel numbers; CSS width strings are unsupported.