Skip to content

Pivot UI configurator

Let end users reshape the cross-tab themselves: drag fields between Rows, Columns, and Values and the grid re-pivots. In Dash this is the built-in configurator (enable_pivot_ui) — the panel only emits a pivot_state ({rows, columns, values}); a callback feeds it to engine.pivot so the backend does the aggregation, never the browser. In the adapters you own a small picker UI and change the component’s rows / cols / measures, which re-pivots client-side.

from dash import Dash, Input, Output, callback, no_update
from dash_tensor_grid import GridDataEngine, TensorGrid
ENGINE = GridDataEngine(sales)
FIELDS = {
"dimensions": ["region", "country", "year", "quarter", "category"],
"values": ["revenue", "cost"],
"labels": {"revenue": "Revenue", "cost": "Cost"},
}
INITIAL = {"rows": ["region", "country"], "columns": ["year"], "values": ["revenue"]}
FORMATS = {"revenue": "currency:USD", "cost": "currency:USD"}
out = ENGINE.pivot(rows=INITIAL["rows"], cols=INITIAL["columns"],
agg=dict.fromkeys(INITIAL["values"], "sum"), formats=FORMATS)
app = Dash(__name__)
app.layout = TensorGrid(
id="grid",
row_data=out["row_data"], column_defs=out["column_defs"],
enable_pivot_ui=True, # show the drag-and-drop configurator
pivot_fields=FIELDS, # the fields it offers
pivot_state=INITIAL, # seed the layout (the panel writes it back)
)
@callback(Output("grid", "row_data"), Output("grid", "column_defs"),
Input("grid", "pivot_state"), prevent_initial_call=True)
def repivot(state):
rows, cols, vals = state.get("rows"), state.get("columns"), state.get("values")
if not rows or not vals:
return no_update, no_update
out = ENGINE.pivot(rows=rows, cols=cols, agg=dict.fromkeys(vals, "sum"), formats=FORMATS)
return out["row_data"], out["column_defs"]
  • pivot_state is two-way in Dash — seed the initial layout with it, and the configurator writes it back on every change. Use it as a callback Input to recompute row_data / column_defs with engine.pivot. See the Dash props reference.
  • pivot_fields declares what the panel offers: {dimensions, values, labels?}. Dimensions can be dropped into Rows or Columns; values become the aggregated measures.
  • enable_group_collapse pairs well here — the reshaped column groups stay collapsible.
  • Adapters have no built-in panel: the drag-and-drop UI is a Dash affordance. In React / Vue / Vanilla you supply the picker and drive rows / cols (props) or handle.update({ rows, cols }), and the component re-pivots. Start from a plain pivot.