Theme
Version
GitHub PyPI Discord
On this page

Tree Grid

CTreeGrid combines a finite Row hierarchy with Data Grid columns. It is for account trees, threaded records, work breakdowns, and similar structured data, not spreadsheet formulas or inline editing.

Present an account hierarchy
Show code
# ruff: noqa: ANN001, ANN201 - public snippets keep focus on component use

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)
COLUMNS = [CTreeGridColumn("name", "Account", width=240), CTreeGridColumn("owner", "Owner")]
ROWS = [
    CTreeGridRow(
        "north",
        "Northern region",
        {"name": "Northern region", "owner": "Ada"},
        children=[
            CTreeGridRow("prague", "Prague", {"name": "Prague", "owner": "Mira"}),
            CTreeGridRow("berlin", "Berlin", {"name": "Berlin", "owner": "Noah"}),
        ],
    )
]


class TreeGridAtAGlance(Component):
    def template_data(self, _kwargs, _slots):
        return {"columns": COLUMNS, "rows": ROWS}

    template = '<c-CTreeGrid c-columns="columns" c-rows="rows" label="Account hierarchy" c-expanded="[\'north\']" />'


preview = TreeGridAtAGlance()
preview  # noqa: B018

Expand nested Rows

Put child CTreeGridRow records in children and list initially open branch keys in expanded. The first Column owns indentation and expansion.

Control visible project levels
Show code
# ruff: noqa: ANN001, ANN201 - public snippets keep focus on component use

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)


class TreeGridExpansion(Component):
    def template_data(self, _kwargs, _slots):
        return {
            "columns": [CTreeGridColumn("work", "Work item", 260), CTreeGridColumn("state", "State")],
            "rows": [
                CTreeGridRow(
                    "launch",
                    "Launch",
                    {"work": "Launch", "state": "Active"},
                    children=[
                        CTreeGridRow("design", "Design", {"work": "Design", "state": "Done"}),
                        CTreeGridRow("build", "Build", {"work": "Build", "state": "Active"}),
                    ],
                )
            ],
        }

    template = '<c-CTreeGrid c-columns="columns" c-rows="rows" label="Project plan" c-expanded="[\'launch\']" />'


preview = TreeGridExpansion()
preview  # noqa: B018

Select and submit Rows

Choose single or multiple selection and set name to emit repeated hidden Row keys in preorder. Shift+Space toggles the focused Row, including unselect.

Select organization units
Show code
# ruff: noqa: ANN001, ANN201, E501 - public template stays readable

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)


class TreeGridSelection(Component):
    def template_data(self, _kwargs, _slots):
        return {
            "columns": [CTreeGridColumn("team", "Team"), CTreeGridColumn("people", "People")],
            "rows": [
                CTreeGridRow("product", "Product", {"team": "Product", "people": 18}),
                CTreeGridRow("ops", "Operations", {"team": "Operations", "people": 12}),
            ],
        }

    template = '<form><c-CTreeGrid c-columns="columns" c-rows="rows" label="Teams" selection="multiple" c-selected="[\'product\']" name="team" /></form>'


preview = TreeGridSelection()
preview  # noqa: B018

Own state in Alpine

Client expanded and selected props are controlled. Their callbacks report the requested vector, previous vector, Row key, requested boolean state, controlled flag, source, and native event.

Own expansion and selection
Show code
# ruff: noqa: ANN001, ANN201, E501 - public template stays readable

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)


class TreeGridControlled(Component):
    def template_data(self, _kwargs, _slots):
        return {
            "columns": [CTreeGridColumn("name", "Name")],
            "rows": [
                CTreeGridRow(
                    "root", "Root", {"name": "Root"}, children=[CTreeGridRow("child", "Child", {"name": "Child"})]
                )
            ],
        }

    template = """<div x-data="{open:[],chosen:[]}"><c-CTreeGrid c-columns="columns" c-rows="rows" label="Controlled tree" selection="multiple" $c-props="{expanded:open,selected:chosen,onExpandedChange:value=>open=value,onSelectionChange:value=>chosen=value}" /></div>"""


preview = TreeGridControlled()
preview  # noqa: B018

Customize cells

Use header, cell, toolbar, and caption slots. Cell navigation stays on the gridcell; interactive editing remains the separate Data Grid contract.

Format hierarchical metrics
Show code
# ruff: noqa: ANN001, ANN201, E501 - public template stays readable

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)


class TreeGridCustomCells(Component):
    def template_data(self, _kwargs, _slots):
        return {
            "columns": [CTreeGridColumn("name", "Initiative", 240), CTreeGridColumn("score", "Score", align="end")],
            "rows": [
                CTreeGridRow(
                    "quality",
                    "Quality",
                    {"name": "Quality", "score": 92},
                    children=[CTreeGridRow("a11y", "Accessibility", {"name": "Accessibility", "score": 98})],
                )
            ],
        }

    template = """<c-CTreeGrid c-columns="columns" c-rows="rows" label="Initiatives" c-expanded="['quality']"><c-fill name="cell" data="{ column, cell }"><strong c-if="column.key == 'score'">{{ cell.value }}%</strong><span c-else>{{ cell.value }}</span></c-fill></c-CTreeGrid>"""


preview = TreeGridCustomCells()
preview  # noqa: B018

Arrow keys move through visible Rows and Columns. Left and Right also collapse, expand, and return to parents from the hierarchy cell. Disabled Rows remain readable but cannot mutate or activate.

Keep focus and selection distinct
Show code
# ruff: noqa: ANN001, ANN201 - public snippets keep focus on component use

import citry_ui
from citry import Component, citry
from citry_ui import CTreeGridColumn, CTreeGridRow

citry.register_library(citry_ui)


class TreeGridAccessibility(Component):
    def template_data(self, _kwargs, _slots):
        return {
            "columns": [CTreeGridColumn("name", "Record", 260), CTreeGridColumn("status", "Status")],
            "rows": [
                CTreeGridRow("available", "Available record", {"name": "Available record", "status": "Ready"}),
                CTreeGridRow(
                    "locked", "Locked record", {"name": "Locked record", "status": "Archived"}, disabled=True
                ),
            ],
        }

    template = (
        '<c-CTreeGrid c-columns="columns" c-rows="rows" label="Records" selection="multiple" density="spacious" />'
    )


preview = TreeGridAccessibility()
preview  # noqa: B018

Hierarchical sorting, async children, virtual Rows, and editing are explicit future or adjacent contracts, not hidden Tree Grid modes.

API reference

Inputs

CTreeGrid server inputs

Server inputs are passed in a template through <c-CTreeGrid ... /> or in Python through CTreeGrid(...).

InputTypeDefaultEffect
columnsSequence[CTreeGridColumn]requiredDefines ordered aligned Columns; the first owns hierarchy controls.
rowsSequence[CTreeGridRow]requiredDefines a finite recursive Row hierarchy.
labelstrrequiredNames the treegrid.
idstr | NonegeneratedSets the root ID.
expandedSequence[str]"()"Supplies initially expanded branch Row keys.
selectionCTreeGridSelection (CTreeGridSelection)"none"Enables no single or multiple Row selection.
selectedSequence[str]"()"Supplies initially selected Row keys.
namestr | NoneNoneEmits selected keys as repeated hidden inputs.
formstr | NoneNoneAssociates hidden inputs with an external form.
disabledboolFalseDisables mutation activation and form output.
densityCTreeGridDensity (CTreeGridDensity)"comfortable"Selects Row height.
expand_labelstr"Expand {row}"Overrides branch Expand names and must retain row.
collapse_labelstr"Collapse {row}"Overrides branch Collapse names and must retain row.
expanded_labelstr"Expanded {row}"Overrides expanded announcements and must retain row.
collapsed_labelstr"Collapsed {row}"Overrides collapsed announcements and must retain row.
selected_labelstr"Selected {row}"Overrides selected announcements and must retain row.
unselected_labelstr"Unselected {row}"Overrides unselected announcements and must retain row.
class_CClassValue | None (CClassValue)NoneAdds root classes.
styleCStyleValue | None (CStyleValue)NoneAdds root styles.
attrsMapping[str, object] | NoneNoneAdds copied allowed root attributes.
table_attrsMapping[str, object] | NoneNoneAdds copied allowed table attributes.

CTreeGrid client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CTreeGrid />.

InputTypeOmitted behaviorEffect
expandedstring[]Uncontrolled server branch state.Controls expanded branch keys.
selectedstring[]Uncontrolled server selection.Controls selected Row keys.
disabledbooleanUses the server value.Reactively disables behavior and inputs.
onExpandedChangefunctionNo component callback runs.Receives expansion requests.
onSelectionChangefunctionNo component callback runs.Receives selection requests.
onCellActivatefunctionNo component callback runs.Receives Enter or double-click activation outside the hierarchy toggle.

Slots

Slots are passed as nested content or <c-fill> tags in a template, or through the slots={...} argument in Python.

CTreeGrid slots

SlotRequiredDataFallback
captionno{} (CTreeGridCaptionSlotData)No native caption.
toolbarno{} (CTreeGridToolbarSlotData)No toolbar.
headerno{column, column_index} (CTreeGridHeaderSlotData)Column label.
cellno{row, column, cell, row_index, column_index, level, expanded, selected} (CTreeGridCellSlotData)Cell value.

Events

Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.

CTreeGrid events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onExpandedChange(expanded: string[], detail: CTreeGridExpandedChangeDetail) => void (CTreeGridExpandedChangeDetail)A branch changes.{expanded, previousExpanded, rowKey, rowExpanded, controlled, source, sourceEvent} (CTreeGridExpandedChangeDetail)Commits only while uncontrolled.
onSelectionChange(selected: string[], detail: CTreeGridSelectionChangeDetail) => void (CTreeGridSelectionChangeDetail)A Row selection toggles.{selected, previousSelected, rowKey, rowSelected, controlled, source, sourceEvent} (CTreeGridSelectionChangeDetail)Commits only while uncontrolled.
onCellActivate(detail: CTreeGridCellActivateDetail) => void (CTreeGridCellActivateDetail)Enter or double-click activates a non-hierarchy Cell.{rowKey, columnKey, rowIndex, columnIndex, sourceEvent} (CTreeGridCellActivateDetail)Reports without changing data.

Methods

-

CSS

CSS variables to theme the components. Set them on an ancestor or the component itself.

CTreeGrid CSS variables

Apply these variables to CTreeGrid or one of its ancestors.

VariableTypePurposeDefault
--cui-tree-grid-min-widthlengthComputed minimum table width.Sum of Column widths
--cui-tree-grid-row-heightlengthComfortable Row height.3rem
--cui-tree-grid-indentlengthPer-level logical indent.1.25rem
--cui-tree-grid-bordercomplete borderViewport Row and header boundaries.Adaptive 1px neutral
--cui-tree-grid-surfacecolorBody surface.Canvas
--cui-tree-grid-header-surfacecolorHeader surface.Adaptive neutral
--cui-tree-grid-selected-surfacecolorSelected Row surface.Adaptive indigo
--cui-tree-grid-focuscolorGridcell and expander focus.Highlight

Attributes

HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.

CTreeGrid attributes

AttributeElementTypeMeaning
data-densityRootCTreeGridDensity (CTreeGridDensity)Reflects Row density.
data-selectionRootCTreeGridSelection (CTreeGridSelection)Reflects selection policy.
data-disabledRoot and Rowpresent | absentReflects unavailable behavior.
data-expandedRowpresent | absentReflects expanded branch state.
data-selectedRowpresent | absentReflects selected state.
data-row-keyRow and CellstringExposes stable Row identity.
data-parent-keyRowstring | absentExposes parent identity.
data-levelRowpositive integer stringExposes hierarchy depth.

Selectors

Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.

CTreeGrid selectors

SelectorElementPurpose
[data-citry-ui-part="tree-grid"]RootTheme and state destination.
[data-citry-ui-part="toolbar"]Optional divApplication controls.
[data-citry-ui-part="status"]Polite statusExpansion and selection announcements.
[data-citry-ui-part="viewport"]Scroll containerNarrow horizontal overflow.
[data-citry-ui-part="table"]Native table with treegrid roleComposite owner.
[data-citry-ui-part="header-cell"]ColumnheaderColumn label.
[data-citry-ui-part="row"]Hierarchical RowExpansion selection and hierarchy metadata.
[data-citry-ui-part="cell"]GridcellRoving focus unit.
[data-citry-ui-part="hierarchy"]First-Cell wrapperIndent branch control and content.
[data-citry-ui-part="expander"]Native buttonPointer branch toggle.
[data-citry-ui-part="cell-content"]SpanCell slot destination.
[data-citry-ui-part="inputs"]Hidden spanNative selected-key controls.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CTreeGridSelectionLiteral["none", "single", "multiple"]
CTreeGridDensityLiteral["compact", "comfortable", "spacious"]
CTreeGridAlignLiteral["start", "center", "end"]
CTreeGridSourceLiteral["pointer", "keyboard", "reset"]
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, object] | Sequence[CStyleValue]

CTreeGridCaptionSlotData

Empty dataclass: {}.

CTreeGridToolbarSlotData

Empty dataclass: {}.

CTreeGridHeaderSlotData

FieldTypeDefaultMeaning
columnCTreeGridColumn-Current Column.
column_indexint-Zero-based Column index.

CTreeGridCellSlotData

FieldTypeDefaultMeaning
rowCTreeGridRow-Current Row.
columnCTreeGridColumn-Current Column.
cellCTreeGridCell-Current Cell.
row_indexint-Zero-based flattened Row index.
column_indexint-Zero-based Column index.
levelint-One-based hierarchy depth.
expandedbool-Initial branch expansion.
selectedbool-Initial Row selection.

CTreeGridExpandedChangeDetail

FieldTypeDefaultMeaning
expandedlist[str]-Requested expanded keys.
previousExpandedlist[str]-Previous keys.
rowKeystr-Changed Row.
rowExpandedbool-Requested Row state.
controlledbool-Whether client state is controlled.
sourceCTreeGridSource-Interaction source.
sourceEventobject-Native Event.

CTreeGridSelectionChangeDetail

FieldTypeDefaultMeaning
selectedlist[str]-Requested selected keys.
previousSelectedlist[str]-Previous keys.
rowKeystr-Changed Row.
rowSelectedbool-Requested Row state.
controlledbool-Whether client state is controlled.
sourceCTreeGridSource-Interaction source.
sourceEventobject-Native Event.

CTreeGridCellActivateDetail

FieldTypeDefaultMeaning
rowKeystr-Activated Row.
columnKeystr-Activated Column.
rowIndexint-Flattened Row index.
columnIndexint-Column index.
sourceEventobject-Native Event.

Translation keys

Catalog keys used by this family. An explicit component input or slot listed in Override takes precedence over the catalog for that instance.

CTreeGrid translation keys

KeyPurposeVariablesOverrideBrowser updates
citry-ui-tree-grid-expandNames a collapsed branch control.row: strexpand_label with {row}Imperative reactive i18n.bind().
citry-ui-tree-grid-collapseNames an expanded branch control.row: strcollapse_label with {row}Imperative reactive i18n.bind().
citry-ui-tree-grid-expandedAnnounces branch expansion.row: strexpanded_label with {row}Browser-created one-shot i18n.tr().
citry-ui-tree-grid-collapsedAnnounces branch collapse.row: strcollapsed_label with {row}Browser-created one-shot i18n.tr().
citry-ui-tree-grid-selectedAnnounces Row selection.row: strselected_label with {row}Browser-created one-shot i18n.tr().
citry-ui-tree-grid-unselectedAnnounces Row unselection.row: strunselected_label with {row}Browser-created one-shot i18n.tr().