grid_state

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

Server-driven data-grid state helpers for chirp-ui (issue #200).

Deliberately modeled onchirp_ui.route_tabs: stdlib + dataclasses only, noimport chirp and no import kida. The module is fully unit-testable with plain…

Server-driven data-grid state helpers for chirp-ui (issue #200).

Deliberately modeled onchirp_ui.route_tabs: stdlib + dataclasses only, noimport chirp and no import kida. The module is fully unit-testable with plain pytest andty-checkable without a render or a Chirp app ("works without Chirp, better with Chirp").

Two concerns, two analogs ofroute_tabs.tab_is_active:

  • Sort.parse_sort() turns a raw ?sort=query value into a typed GridSort; sort_columns() projects a list of Column declarations intoColumnSort rows that carry the aria_sortvalue and the fully-built togglenext_urlthe macro renders but never computes. The sameGridSortthe route uses to actually order rows produces the renderedaria-sortand next-request, so server data and advertised UI cannot drift.

  • Selection.selection_state() normalizes request-derived ids into a SelectionState whose props (count, all_selected, none_selected, partial) seed the select-all checkbox server-side, so selection is correct on a full load even with JavaScript off. The Alpine factory owns live in-page toggling between requests.

Example (Chirp route)::

from chirp_ui import Column, parse_sort, sort_columns, selection_state

COLS = [
    Column("name", "Name", sortable=True),
    Column("status", "Status", sortable=True, align="center"),
    Column("seats", "Seats", sortable=True, align="right",
           width="1fr", mobile_width="80px", resizable=True),
]

width, mobile_width, and resizableare layout hints for the future opt-in ARIA-grid renderer (issue #261). Table mode ignores them today.

sort = parse_sort(req.query.get("sort"), default_key="name",
                  allowed=tuple(c.key for c in COLS))
rows = query_users(order_by=sort.key, desc=(sort.direction == "desc"))
cols = sort_columns(COLS, sort, base_url="/users",
                    extra_params={"q": req.query.get("q", "")})
sel = selection_state(req.query.getlist("ids"),
                      page_ids=[u.id for u in rows], total=count_users())

grid_state

Name Type Default Description
type
qualified_name
element_type
description
source_file
line_number
is_autodoc
autodoc_element
_autodoc_template
_autodoc_url_path
_autodoc_page_type
title
doc_content_hash

Symbols on this page

class Column

A column declaration — the developer-authored input to the grid.

key is the stable sort key sent to the server (never label|lower); renaming …

Jump to symbol
class GridSort

The typed current-sort state.direction is "asc" or "desc".

Jump to symbol
class ColumnSort

A projected column ready to render.

The macro readsaria_sort and next_urldirectly; it never derives sort state.aria_sort is one of "ascending", …

Jump to symbol
class SelectionState

Server-authoritative selection snapshot for the current page.

selected is the full cross-request selection; page_idsare the ids rendered on this page;totalis the…

Jump to symbol
function parse_sort

Turn a raw?sort= value into a typed GridSort.

"name" → ascending, "-name"→ descending. Unknown or empty keys clamp todefault_key—…

Jump to symbol
function column_aria_sort

Return thearia-sort value for column_key given sort.

Standalone projection primitive for callers who hand-render a single <th>. Returns "ascending" / "descending"…

Jump to symbol
function sort_query

Return the?param=value query string that toggles column_key.

Standalone projection primitive. An active ascending column requests -key(descending); an active descending column requests …

Jump to symbol
function sort_columns

Projectcolumns into renderable ColumnSortrows.

Thetab_is_active() analog for tables. For each column it computesis_active (col.sortable and col.key == sort.key…

Jump to symbol
function selection_state

Normalize request-derived selection into aSelectionState.

selected_ids (e.g. req.query.getlist("ids")) is coerced to a frozenset of strings; tolerant of None. page_idsare…

Jump to symbol
Column
class

A column declaration — the developer-authored input to the grid.

key is the stable sort key sent to the server (never label|lower); renaminglabelor shipping duplicate/i18n labels never breaks sorting.

v1 does not expose per-column pinning: the grid pins the first visual column viasticky_first_col=true (a CSS :first-childrule), not an arbitrary column. Afrozenflag was deliberately not shipped so the public surface advertises no pinning contract the renderer cannot honor.

width, mobile_width, and resizableseed the ARIA-grid-over-div variant (issue #261). Real-<table>mode ignores them — callers can set them now without changing table rendering.

GridSort
class

The typed current-sort state.direction is "asc" or "desc".

ColumnSort
class

A projected column ready to render.

The macro readsaria_sort and next_urldirectly; it never derives sort state.aria_sort is one of "ascending", "descending", "none"; next_urlis the fully-built toggle URL.

SelectionState
class

Server-authoritative selection snapshot for the current page.

selected is the full cross-request selection; page_idsare the ids rendered on this page;total is the grand result-set size (or None). Theall_selected / partialprops are page-scoped — they describe the visible page, not the entire result set (cross-page "select all N matching" is out of scope for v1).

parse_sort
function
def parse_sort(raw: str | None, *, default_key: str = '', default_direction: str = _ASC, allowed: Sequence[str] = ()) -> GridSort

Turn a raw?sort= value into a typed GridSort.

"name" → ascending, "-name"→ descending. Unknown or empty keys clamp todefault_key — the tab_is_active() empty-href guard analog, defensive against arbitrary query input. Whenallowedis given, a key outside it also clamps to the default.

Parameters

Name Type Default Description
raw str | None
default_key str ''
default_direction str _ASC
allowed Sequence[str] ()
column_aria_sort
function
def column_aria_sort(column_key: str, sort: GridSort) -> str

Return thearia-sort value for column_key given sort.

Standalone projection primitive for callers who hand-render a single <th>. Returns "ascending" / "descending"when this column is the active sort, else"none".

Parameters

Name Type Default Description
column_key str
sort GridSort
sort_query
function
def sort_query(column_key: str, sort: GridSort, param: str = 'sort') -> str

Return the?param=value query string that toggles column_key.

Standalone projection primitive. An active ascending column requests -key (descending); an active descending column requests key (ascending); an inactive column requestskey(ascending).

Parameters

Name Type Default Description
column_key str
sort GridSort
param str 'sort'
sort_columns
function
def sort_columns(columns: Sequence[Column | Mapping[str, object]], sort: GridSort, base_url: str, *, param: str = 'sort', extra_params: Mapping[str, str] | None = None) -> list[ColumnSort]

Projectcolumns into renderable ColumnSortrows.

Thetab_is_active() analog for tables. For each column it computesis_active (col.sortable and col.key == sort.key), aria_sort (ascending / descendingwhen active+sortable, else none) and the toggled next_url (active+asc → request -key; active+desc → requestkey; inactive → request keyascending), preservingextra_paramsso an active filter query survives a sort click.

Exactly one column is marked active for a givenGridSort, so the single-sort + singlearia-sortinvariant is structural, not asserted in a template branch.

Parameters

Name Type Default Description
columns Sequence[Column | Mapping[str, object]]
sort GridSort
base_url str
param str 'sort'
extra_params Mapping[str, str] | None None
selection_state
function
def selection_state(selected_ids: Iterable[str] | None, page_ids: Iterable[str], total: int | None = None) -> SelectionState

Normalize request-derived selection into aSelectionState.

selected_ids (e.g. req.query.getlist("ids")) is coerced to a frozenset of strings; tolerant of None. page_idsare the ids rendered on the current page.totalis the grand result-set size.

Parameters

Name Type Default Description
selected_ids Iterable[str] | None
page_ids Iterable[str]
total int | None None

View source · /home/runner/work/chirp-ui/chirp-ui/site/../src/chirp_ui/grid_state.py:1