Skip to content

data_editor_enhance API#

data_editor_enhance has the same small eager-data, editable-column, and edited-value contract as exp_data_editor. Its additional wrapped_columns option enables the experimental rough resize strategy adapted from marimo's feat/data-editor-wrap branch.

It accepts the same dataframe-like, scalar, record, and column-oriented inputs as marimo.ui.data_editor, plus its documented label, on_change, column_sizing_mode, pagination, and page_size options. Edited values are synchronized as row dictionaries for AnyWidget.

import marimo as mo
from lumut import data_editor_enhance

editor = mo.ui.anywidget(data_editor_enhance(
    [{"notes": "Resize this wrapped column."}],
    editable_columns=["notes"],
    wrapped_columns=["notes"],
    max_row_height=160,
))

On column resize, the widget measures only the visible rows plus a 20-row buffer. On pointer release it reuses the sampled maximum for all rows. This is bounded and responsive, but it is not exact; do not use it for million-row datasets or content-fit layout requirements.

Bases: AnyWidget

Edit tabular data with Glide Data Grid and approximate wrapped heights.

This is deliberately separate from :class:lumut.ExpDataEditor. It carries the experimental Glide strategy from marimo's feat/data-editor-wrap: when a wrapped column is resized, it estimates the maximum height in the visible window (plus a small buffer), temporarily applies it there, then applies that sampled height to all data rows when the pointer is released.

The method avoids measuring every row during a resize, but it is not exact: rows outside the sample can be too tall or too short. Glide's variable-row callback also has poor scroll-geometry characteristics for huge datasets, so this widget is for experimentation and small-to-medium eager data only.

Source code in lumut/data_editor_enhance.py
def __init__(
    self,
    data: Sequence[Mapping[str, Any]] | Mapping[str, Sequence[Any]] | Any,
    *,
    label: str = "",
    on_change: Callable[[list[Row]], None] | None = None,
    editable_columns: EditableColumns = "all",
    wrapped_columns: WrappedColumns = (),
    max_row_height: int = 240,
    estimated_row_height: int = 34,
    column_sizing_mode: ColumnSizingMode = None,
    pagination: bool | None = None,
    page_size: int | None = None,
    width: str = "100%",
    height: int = 450,
) -> None:
    if max_row_height < estimated_row_height:
        raise ValueError("max_row_height must be at least estimated_row_height")
    if height <= 0:
        raise ValueError("height must be positive")
    if column_sizing_mode not in (None, "auto", "fit"):
        raise ValueError("column_sizing_mode must be 'auto', 'fit', or None")
    if page_size is not None and page_size <= 0:
        raise ValueError("page_size must be positive")

    rows: list[Row] = _to_rows(data)
    columns = list(rows[0]) if rows else _column_names(data)
    _validate_columns(editable_columns, columns, "editable_columns")
    _validate_columns(wrapped_columns, columns, "wrapped_columns")

    super().__init__(
        data=rows,
        value=rows,
        editable_columns=(
            "all" if editable_columns == "all" else list(editable_columns)
        ),
        wrapped_columns=(
            "all" if wrapped_columns == "all" else list(wrapped_columns)
        ),
        field_types=_infer_field_types(rows, columns),
        label=label,
        width=width,
        height=height,
        max_row_height=max_row_height,
        estimated_row_height=estimated_row_height,
        column_sizing_mode=column_sizing_mode,
        pagination=bool(pagination),
        page_size=page_size or 25,
    )
    self._on_change = on_change
    if on_change is not None:
        self.observe(self._notify_change, names="value")