Metadata-Version: 2.5
Name: sp-reflex-components
Version: 0.5.0
Summary: Reflex component wrappers for AG Grid (community + enterprise modules) and React Flow - self-host on core Reflex without reflex-enterprise.
Project-URL: Repository, https://github.com/MIM-SP/sp-reflex-components
Project-URL: Issues, https://github.com/MIM-SP/sp-reflex-components/issues
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: ag-grid,components,react-flow,reflex
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.12
Requires-Dist: reflex<0.10,>=0.9.5
Description-Content-Type: text/markdown

# sp-reflex-components

Reflex component wrappers: **AG Grid** (ag-grid-react +
community + enterprise modules) and **React Flow** (@xyflow/react). These
replace `reflex-enterprise`'s component wrappers so apps self-host on core
Reflex (Apache-2.0) with **no Reflex tier, token, badge, or reflex.dev
dependency**.

Built and maintained by Solid Power for internal Reflex apps; published in
case others hit the same wall. Apache-2.0.

## Install

```bash
uv add sp-reflex-components
# or: pip install sp-reflex-components
```

## Use

```python
from sp_reflex_components import export_grid_csv, grid, flow

# AG Grid — drop-in for reflex_enterprise's ag_grid.root(...)
grid(
    id="my-grid",
    theme="quartz",  # or "alpine" (default); unknown values fall back to alpine
    row_data=State.rows,
    column_defs=[{"field": "name", "header_name": "Name", "editable": True}],
    on_selection_changed=State.on_select,  # (rows, source, type)
)

# Client-side CSV export of exactly the grid's own rows — no server surface.
# Requires the grid to carry an explicit unique id; safely no-ops until the
# grid is ready. (Replaces reflex-enterprise's AgGridAPI.export_data_as_csv.)
rx.button("Export CSV", on_click=export_grid_csv("my-grid"))

# React Flow — drop-in for reflex_enterprise's flow(...)
flow(
    flow.controls(),
    flow.background(variant="dots", gap=18, size=1),
    nodes=State.nodes,
    edges=State.edges,
    fit_view=True,
)
```

Column defs, default_col_def, and detail_cell_renderer_params accept snake_case
keys everywhere — `AgGrid.create` camelizes them (`header_name` → `headerName`);
`row_data` keys are never touched. Var values (formatters, `get_detail_row_data`)
pass through untouched.

Master/detail (0.4.0): pass `master_detail=True` plus
`detail_cell_renderer_params={"detail_grid_options": {...}, "get_detail_row_data": Var(...)}`
(and optionally `detail_row_auto_height=True`).

Mixed master/detail (0.5.0): `is_row_master=Var("(data) => bool")` limits the
expander to matching rows; pass `detail_cell_renderer_params` as a whole-prop
Var callback (`(params) => ({detailGridOptions, getDetailRowData})`) to select
a different detail schema per row.

AG Grid Enterprise features (clipboard, cell selection, column/context menus,
Excel export, master/detail) need `AG_GRID_LICENSE_KEY` in the environment **at compile
time** — that is AG Grid's own per-developer license, unrelated to Reflex.
Without a key, **every** grid loads in trial mode: a watermark on the grid
plus a "License Key Not Found" console notice at load. The enterprise
modules are registered globally, so this applies to all grids rendered by
this package, not only the ones exercising enterprise features
(browser-verified 2026-08-11 on a plain grid and a menus/Excel grid;
re-verified 2026-08-27 with the 0.4.0 module set including MasterDetailModule
— watermark + console notice on a plain grid, registered set confirmed in the
compiled frontend).

## Scope doctrine

- A wrapper exposes **exactly** the prop/event surface consuming apps use —
  no speculative props. Need a new prop? Add it here **with a test**, in the
  same PR that uses it; every app inherits it.
- npm versions are **pinned exactly** in this package. A bump is a deliberate
  release with a changelog read (AG Grid majors change behavior: v35 added
  no-matching-rows/exporting overlays, v36 overhauled the DOM containers).
- Legacy CSS theming (`ag-theme-alpine` + `theme: "legacy"`) is deprecated
  upstream; some future AG Grid major will force a Theming API migration —
  that lands here once, for everyone.
- Each consuming app should keep an adapter/wrapper **prop-equality test**
  (both directions, derived from a real render) — see ATLAS's
  `tests/unit/reflex_runtime/components/test_vendor_ag_grid.py` for the
  pattern.

## AG Grid pitfall: grid geometry via props, not CSS

The new AG Grid DOM (the v36 container overhaul noted above) positions the
row container using the grid's **internal** header height — the
`headerHeight` grid option plus border (e.g. `40` + 1px = 41px offset) —
not the header element's rendered height. If CSS forces the header smaller:

```css
.ag-header { height: 34px !important; }
```

the header shrinks but rows still start at the internal offset, leaving a
visible gap between the header and the first row.

Set grid geometry (`header_height`, `row_height`) via grid props; use CSS
only for paint (colors, fonts, borders). Real case: ATLAS hit exactly this
(commit `e49ce07`) — fixed by setting `header_height: 34` on the grid and
deleting the CSS element-height `!important` overrides.

## Known issues

- React Flow renders nodes but **edge rendering has an unresolved defect**
  (edges reach the ReactFlow component as props but no edge elements render;
  reproduces identically under reflex-enterprise's own wrapper on
  @xyflow/react 12.8.4 and 12.11.2, so it is not specific to this package).
  Root cause TBD; issues/PRs welcome.

## Dev

```bash
uv sync
uv run pytest -q
uv run ruff check src tests && uv run ruff format --check .
```
