reflex-nivo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reflex_nivo-0.1.0/LICENSE +21 -0
- reflex_nivo-0.1.0/PKG-INFO +283 -0
- reflex_nivo-0.1.0/README.md +247 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/__init__.py +41 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/_css.py +808 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/base.py +374 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/base.pyi +94 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/__init__.py +160 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bar.py +294 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bar.pyi +400 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/boxplot.py +168 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/boxplot.pyi +223 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bullet.py +110 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bullet.pyi +175 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bump.py +249 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/bump.pyi +334 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/calendar.py +297 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/calendar.pyi +478 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/chord.py +230 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/chord.pyi +344 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/circle_packing.py +281 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/circle_packing.pyi +424 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/funnel.py +119 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/funnel.pyi +163 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/geo.py +321 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/geo.pyi +625 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/heatmap.py +239 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/heatmap.pyi +334 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/icicle.py +310 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/icicle.pyi +393 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/line.py +282 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/line.pyi +373 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/marimekko.py +111 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/marimekko.pyi +155 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/network.py +203 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/network.pyi +294 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/parallel_coordinates.py +168 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/parallel_coordinates.pyi +329 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/pie.py +270 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/pie.pyi +392 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/polar_bar.py +129 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/polar_bar.pyi +197 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/radar.py +128 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/radar.pyi +179 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/radial_bar.py +133 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/radial_bar.pyi +201 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/sankey.py +130 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/sankey.pyi +181 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/scatterplot.py +218 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/scatterplot.pyi +300 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/stream.py +133 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/stream.pyi +181 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/sunburst.py +109 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/sunburst.pyi +173 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/swarmplot.py +236 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/swarmplot.pyi +324 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/tree.py +269 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/tree.pyi +392 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/treemap.py +325 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/treemap.pyi +478 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/voronoi.py +77 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/voronoi.pyi +123 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/waffle.py +287 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/charts/waffle.pyi +430 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/constants.py +103 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/helpers.py +451 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/py.typed +0 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo/themes.py +167 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo.egg-info/PKG-INFO +283 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo.egg-info/SOURCES.txt +74 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo.egg-info/dependency_links.txt +1 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo.egg-info/requires.txt +13 -0
- reflex_nivo-0.1.0/custom_components/reflex_nivo.egg-info/top_level.txt +1 -0
- reflex_nivo-0.1.0/pyproject.toml +79 -0
- reflex_nivo-0.1.0/setup.cfg +4 -0
- reflex_nivo-0.1.0/tests/test_components.py +258 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ernesto Crespo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reflex-nivo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Every nivo chart (bar, line, pie, sankey, treemap, choropleth... 51 components) as a Reflex component.
|
|
5
|
+
Author-email: Ernesto Crespo <ecrespo@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ecrespo/reflex-nivo
|
|
8
|
+
Project-URL: Source, https://github.com/ecrespo/reflex-nivo
|
|
9
|
+
Project-URL: Issues, https://github.com/ecrespo/reflex-nivo/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/ecrespo/reflex-nivo/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: nivo, https://nivo.rocks
|
|
12
|
+
Keywords: reflex,reflex-custom-components,nivo,d3,charts,dataviz,visualization
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: reflex>=0.9.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build; extra == "dev"
|
|
28
|
+
Requires-Dist: twine; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest; extra == "dev"
|
|
30
|
+
Provides-Extra: lint
|
|
31
|
+
Requires-Dist: ruff==0.16.8; extra == "lint"
|
|
32
|
+
Provides-Extra: security
|
|
33
|
+
Requires-Dist: bandit[toml]==1.9.4; extra == "security"
|
|
34
|
+
Requires-Dist: pip-audit==2.10.1; extra == "security"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# reflex-nivo
|
|
38
|
+
|
|
39
|
+
Every chart of [nivo](https://nivo.rocks) — the React data-visualization library built on d3 — as a
|
|
40
|
+
[Reflex](https://reflex.dev) custom component.
|
|
41
|
+
|
|
42
|
+
- **51 components** from **28 nivo packages**, SVG, Canvas and HTML renderers (nivo `0.99.0`).
|
|
43
|
+
- **snake_case props** generated from nivo's TypeScript declarations, with types and nivo docs links.
|
|
44
|
+
- **Events land in Python**: `on_click`, `on_mouse_enter`, `on_node_click`, `on_arc_click`… receive the
|
|
45
|
+
datum as a JSON-safe `dict` (cycles, DOM nodes and React events are stripped in the browser).
|
|
46
|
+
- **Follows Reflex's light/dark mode** out of the box.
|
|
47
|
+
- Helpers for axes, legends, scales, color configs, patterns/gradients, markers and annotations,
|
|
48
|
+
plus `nivo.tooltip("<b>{id}</b>: {formattedValue}")` templates and a `nivo.js(...)` escape hatch.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install reflex-nivo
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import reflex as rx
|
|
58
|
+
import reflex_nivo as nivo
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class State(rx.State):
|
|
62
|
+
rows: list[dict] = [
|
|
63
|
+
{"country": "AD", "burger": 120, "fries": 80},
|
|
64
|
+
{"country": "AE", "burger": 95, "fries": 130},
|
|
65
|
+
]
|
|
66
|
+
selected: str = ""
|
|
67
|
+
|
|
68
|
+
@rx.event
|
|
69
|
+
def on_bar_click(self, datum: dict):
|
|
70
|
+
self.selected = f"{datum['indexValue']} · {datum['id']} = {datum['value']}"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def index():
|
|
74
|
+
return rx.vstack(
|
|
75
|
+
nivo.bar(
|
|
76
|
+
data=State.rows,
|
|
77
|
+
keys=["burger", "fries"],
|
|
78
|
+
index_by="country",
|
|
79
|
+
group_mode="grouped",
|
|
80
|
+
colors=nivo.scheme("nivo"),
|
|
81
|
+
margin=nivo.margin(top=20, right=110, bottom=50, left=60),
|
|
82
|
+
axis_bottom=nivo.axis(legend="country", legend_position="middle", legend_offset=36),
|
|
83
|
+
legends=[nivo.legend(data_from="keys", anchor="bottom-right", translate_x=100)],
|
|
84
|
+
tooltip=nivo.tooltip("<b>{indexValue}</b> · {id}: {formattedValue}"),
|
|
85
|
+
on_click=State.on_bar_click,
|
|
86
|
+
height="400px",
|
|
87
|
+
),
|
|
88
|
+
rx.text(State.selected),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
app = rx.App()
|
|
93
|
+
app.add_page(index)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Components
|
|
97
|
+
|
|
98
|
+
Each factory wraps nivo's `Responsive*` export. Classes use the nivo name (`nivo.Bar`, `nivo.HeatMapCanvas`…).
|
|
99
|
+
|
|
100
|
+
| Family | SVG | Canvas | HTML |
|
|
101
|
+
| --- | --- | --- | --- |
|
|
102
|
+
| Bar | `bar` | `bar_canvas` | |
|
|
103
|
+
| Line | `line` | `line_canvas` | |
|
|
104
|
+
| Pie | `pie` | `pie_canvas` | |
|
|
105
|
+
| Radar · RadialBar · PolarBar | `radar` · `radial_bar` · `polar_bar` | | |
|
|
106
|
+
| Waffle | `waffle` | `waffle_canvas` | `waffle_html` |
|
|
107
|
+
| ScatterPlot | `scatterplot` | `scatterplot_canvas` | |
|
|
108
|
+
| SwarmPlot | `swarmplot` | `swarmplot_canvas` | |
|
|
109
|
+
| BoxPlot · Voronoi | `boxplot` · `voronoi` | | |
|
|
110
|
+
| HeatMap | `heatmap` | `heatmap_canvas` | |
|
|
111
|
+
| TreeMap | `treemap` | `treemap_canvas` | `treemap_html` |
|
|
112
|
+
| CirclePacking | `circle_packing` | `circle_packing_canvas` | `circle_packing_html` |
|
|
113
|
+
| Sunburst | `sunburst` | | |
|
|
114
|
+
| Icicle | `icicle` | | `icicle_html` |
|
|
115
|
+
| Tree | `tree` | `tree_canvas` | |
|
|
116
|
+
| Sankey · Funnel · Marimekko | `sankey` · `funnel` · `marimekko` | | |
|
|
117
|
+
| Chord | `chord` | `chord_canvas` | |
|
|
118
|
+
| Network | `network` | `network_canvas` | |
|
|
119
|
+
| ParallelCoordinates | `parallel_coordinates` | `parallel_coordinates_canvas` | |
|
|
120
|
+
| Calendar | `calendar` | `calendar_canvas` | |
|
|
121
|
+
| TimeRange · Stream · Bump · AreaBump · Bullet | `time_range` · `stream` · `bump` · `area_bump` · `bullet` | | |
|
|
122
|
+
| GeoMap | `geo_map` | `geo_map_canvas` | |
|
|
123
|
+
| Choropleth | `choropleth` | `choropleth_canvas` | |
|
|
124
|
+
|
|
125
|
+
The data shape and every prop are documented on nivo.rocks (each class docstring links to its page).
|
|
126
|
+
Convert prop names to snake_case: `indexBy` → `index_by`, `enableGridX` → `enable_grid_x`.
|
|
127
|
+
|
|
128
|
+
### Renamed props
|
|
129
|
+
|
|
130
|
+
| nivo | reflex-nivo | why |
|
|
131
|
+
| --- | --- | --- |
|
|
132
|
+
| `id` (Pie, Sunburst, CirclePacking, SwarmPlot, Marimekko) | `id_by` | `id` is the HTML id of the container |
|
|
133
|
+
| `from` / `to` (Calendar, TimeRange) | `from_date` / `to_date` | `from` is a Python keyword |
|
|
134
|
+
|
|
135
|
+
Plain `YYYY-MM-DD` values for `from_date`/`to_date` are sent as local midnight, so calendars do not
|
|
136
|
+
shift one day (and one year) back west of Greenwich.
|
|
137
|
+
|
|
138
|
+
## Sizing
|
|
139
|
+
|
|
140
|
+
nivo's responsive charts fill their parent, so every factory returns the chart inside a `<div>`.
|
|
141
|
+
`width` (default `"100%"`), `height` (default `"400px"`) and any other CSS prop, `class_name`, `id`
|
|
142
|
+
or `style` go to that container. Anything that is neither a nivo prop nor CSS raises a `TypeError`
|
|
143
|
+
with a suggestion, so typos like `enable_gridx` do not fail silently.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
nivo.pie(data=State.pie, height="320px", max_width="600px", margin_x="auto")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
(`margin` itself is nivo's chart margin: use `nivo.margin(top=.., right=.., bottom=.., left=..)`.)
|
|
150
|
+
|
|
151
|
+
## Events
|
|
152
|
+
|
|
153
|
+
All nivo callbacks are exposed as event triggers. The handler receives nivo's first callback argument
|
|
154
|
+
(datum, point, slice, node, cell, serie, link, feature, dimensions, active id…), serialized in the
|
|
155
|
+
browser: functions, DOM nodes and React events are dropped, repeated/cyclic objects are replaced by
|
|
156
|
+
their `id`, and depth is bounded.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
class State(rx.State):
|
|
160
|
+
@rx.event
|
|
161
|
+
def drill_down(self, node: dict):
|
|
162
|
+
if node["data"].get("children"):
|
|
163
|
+
self.sunburst = node["data"] # the raw datum travels with the event
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
nivo.sunburst(data=State.sunburst, id_by="name", value="loc", on_click=State.drill_down)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Only the callbacks a chart actually declares carry a datum, and they differ per chart: `@nivo/bar`
|
|
170
|
+
exposes `on_click`, `on_mouse_enter` and `on_mouse_leave`, while `@nivo/stream` exposes none of them.
|
|
171
|
+
Any other `on_*` is attached to the wrapping `<div>` as a plain DOM event, so it fires for the whole
|
|
172
|
+
chart area but receives no datum (write the handler without arguments). An `on_*` that is neither
|
|
173
|
+
raises a `TypeError` listing the chart's nivo callbacks.
|
|
174
|
+
|
|
175
|
+
`on_mouse_move` fires very often; on the charts that expose it, prefer click/enter/leave or throttle
|
|
176
|
+
it: `on_mouse_move=State.hover.throttle(100)`.
|
|
177
|
+
|
|
178
|
+
### Hiding a prop
|
|
179
|
+
|
|
180
|
+
Passing `None` sends JavaScript `null`, which turns the feature off instead of falling back to nivo's
|
|
181
|
+
default — that is how you hide an axis:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
nivo.bar(data=State.rows, axis_bottom=None, axis_left=None) # no axes
|
|
185
|
+
nivo.pie(data=State.pie, theme=None) # nivo's own theme instead of the Reflex one
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Helpers
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
nivo.margin(top=20, right=20, bottom=40, left=50)
|
|
192
|
+
nivo.axis(legend="price", legend_position="middle", legend_offset=-40, tick_rotation=-45, format=" >-$.2f")
|
|
193
|
+
nivo.legend(
|
|
194
|
+
anchor="bottom-right", direction="column", translate_x=100, effects=[nivo.hover_effect(item_opacity=1)]
|
|
195
|
+
)
|
|
196
|
+
nivo.scale("time", format="%Y-%m-%d", precision="day")
|
|
197
|
+
nivo.scheme("category10") # {"scheme": "category10"}
|
|
198
|
+
nivo.inherit("color", ("darker", 1.6)) # inherited color
|
|
199
|
+
nivo.from_theme("background") # color from theme
|
|
200
|
+
nivo.marker("y", 100, legend="target")
|
|
201
|
+
nivo.annotation({"id": "fries"}, type_="circle", note="max", note_x=40, note_y=-30)
|
|
202
|
+
nivo.pattern_dots_def("dots", background="inherit", color="#38bcb2")
|
|
203
|
+
nivo.pattern_lines_def("lines", rotation=-45) / nivo.pattern_squares_def(...) / nivo.linear_gradient_def(...)
|
|
204
|
+
nivo.fill_rule("dots", {"id": "fries"})
|
|
205
|
+
nivo.props(any_snake_case="option") # generic camelCase dict builder
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Constants: `nivo.COLOR_SCHEMES` (categorical/diverging/sequential), `nivo.CURVES`, `nivo.MOTION_CONFIGS`.
|
|
209
|
+
|
|
210
|
+
### Functions: templates, tooltips and raw JS
|
|
211
|
+
|
|
212
|
+
nivo accepts functions for labels, formatters, accessors, tooltips and custom layers.
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
# Formatter/label from a {path} template, evaluated in the browser:
|
|
216
|
+
label = nivo.template("{id}: {formattedValue}")
|
|
217
|
+
|
|
218
|
+
# Tooltip component from a template. It may use attribute-less <b>, <strong>, <i>, <em>, <u>,
|
|
219
|
+
# <s>, <small>, <code>, <span>, <div>, <p> and <br>; anything else, and every interpolated
|
|
220
|
+
# value, is rendered as text, so template and values are safe to build from a state Var.
|
|
221
|
+
tooltip = nivo.tooltip("<b>{datum.label}</b><br/>{datum.formattedValue}", style={"min_width": "120px"})
|
|
222
|
+
|
|
223
|
+
# Anything else: a raw JavaScript expression (never build it from user input).
|
|
224
|
+
value_format = nivo.js("v => `${v.toLocaleString()} €`")
|
|
225
|
+
node_size = nivo.js("n => n.size")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Most formatters also accept d3-format / d3-time-format strings directly (`value_format=".2s"`,
|
|
229
|
+
`axis(format="%b %d")`).
|
|
230
|
+
|
|
231
|
+
## Themes
|
|
232
|
+
|
|
233
|
+
Charts use `nivo.themes.auto()` unless you pass `theme=`: a light/dark pair built on the Radix gray
|
|
234
|
+
scale that switches with `rx.color_mode`. Customize or pin it:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
theme = nivo.themes.auto({"grid": {"line": {"strokeDasharray": "4 4"}}}) # both modes
|
|
238
|
+
theme = nivo.themes.auto(light={"text": {"fill": "#333"}}, dark={"text": {"fill": "#eee"}})
|
|
239
|
+
theme = nivo.themes.DARK # fixed
|
|
240
|
+
theme = State.computed_theme # any nivo theme dict
|
|
241
|
+
theme = {} # nivo's own default
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Demo
|
|
245
|
+
|
|
246
|
+
`nivo_demo/` is a 10-page app that uses all 51 components with live state controls, an event
|
|
247
|
+
inspector, drill-down/zoom interactions, geo projections, canvas variants with thousands of marks and
|
|
248
|
+
customization examples.
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
uv venv && uv pip install -e . && cd nivo_demo && uv pip install -r requirements.txt
|
|
252
|
+
uv run reflex run # http://localhost:3010 (backend on 8010)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
World country shapes in the demo come from the nivo website dataset (Natural Earth).
|
|
256
|
+
|
|
257
|
+
## Regenerating the wrappers
|
|
258
|
+
|
|
259
|
+
The chart modules in `custom_components/reflex_nivo/charts/` are generated from nivo's typings:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# in a scratch directory
|
|
263
|
+
npm i typescript@5 @types/react@19 react@19 react-dom@19 @nivo/{bar,line,pie,...}@0.99.0
|
|
264
|
+
node /path/to/reflex-nivo/scripts/extract_nivo_props.cjs bar boxplot bullet bump calendar chord \
|
|
265
|
+
circle-packing funnel heatmap icicle line marimekko network pie polar-bar radar radial-bar sankey \
|
|
266
|
+
scatterplot stream sunburst swarmplot tree treemap voronoi waffle > /path/to/reflex-nivo/scripts/nivo_props.json
|
|
267
|
+
# back in the repo (geo and parallel-coordinates ship no usable typings: see scripts/nivo_manual_props.json)
|
|
268
|
+
python scripts/generate_components.py
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Bump `NIVO_VERSION` in `custom_components/reflex_nivo/constants.py` together with the extraction.
|
|
272
|
+
|
|
273
|
+
## Development
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
uv pip install -e ".[dev]"
|
|
277
|
+
uv run pytest
|
|
278
|
+
uv run reflex component build # generates .pyi stubs, then sdist + wheel in dist/
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT © Ernesto Crespo. nivo is MIT © Raphaël Benitte.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# reflex-nivo
|
|
2
|
+
|
|
3
|
+
Every chart of [nivo](https://nivo.rocks) — the React data-visualization library built on d3 — as a
|
|
4
|
+
[Reflex](https://reflex.dev) custom component.
|
|
5
|
+
|
|
6
|
+
- **51 components** from **28 nivo packages**, SVG, Canvas and HTML renderers (nivo `0.99.0`).
|
|
7
|
+
- **snake_case props** generated from nivo's TypeScript declarations, with types and nivo docs links.
|
|
8
|
+
- **Events land in Python**: `on_click`, `on_mouse_enter`, `on_node_click`, `on_arc_click`… receive the
|
|
9
|
+
datum as a JSON-safe `dict` (cycles, DOM nodes and React events are stripped in the browser).
|
|
10
|
+
- **Follows Reflex's light/dark mode** out of the box.
|
|
11
|
+
- Helpers for axes, legends, scales, color configs, patterns/gradients, markers and annotations,
|
|
12
|
+
plus `nivo.tooltip("<b>{id}</b>: {formattedValue}")` templates and a `nivo.js(...)` escape hatch.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install reflex-nivo
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import reflex as rx
|
|
22
|
+
import reflex_nivo as nivo
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class State(rx.State):
|
|
26
|
+
rows: list[dict] = [
|
|
27
|
+
{"country": "AD", "burger": 120, "fries": 80},
|
|
28
|
+
{"country": "AE", "burger": 95, "fries": 130},
|
|
29
|
+
]
|
|
30
|
+
selected: str = ""
|
|
31
|
+
|
|
32
|
+
@rx.event
|
|
33
|
+
def on_bar_click(self, datum: dict):
|
|
34
|
+
self.selected = f"{datum['indexValue']} · {datum['id']} = {datum['value']}"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def index():
|
|
38
|
+
return rx.vstack(
|
|
39
|
+
nivo.bar(
|
|
40
|
+
data=State.rows,
|
|
41
|
+
keys=["burger", "fries"],
|
|
42
|
+
index_by="country",
|
|
43
|
+
group_mode="grouped",
|
|
44
|
+
colors=nivo.scheme("nivo"),
|
|
45
|
+
margin=nivo.margin(top=20, right=110, bottom=50, left=60),
|
|
46
|
+
axis_bottom=nivo.axis(legend="country", legend_position="middle", legend_offset=36),
|
|
47
|
+
legends=[nivo.legend(data_from="keys", anchor="bottom-right", translate_x=100)],
|
|
48
|
+
tooltip=nivo.tooltip("<b>{indexValue}</b> · {id}: {formattedValue}"),
|
|
49
|
+
on_click=State.on_bar_click,
|
|
50
|
+
height="400px",
|
|
51
|
+
),
|
|
52
|
+
rx.text(State.selected),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
app = rx.App()
|
|
57
|
+
app.add_page(index)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Components
|
|
61
|
+
|
|
62
|
+
Each factory wraps nivo's `Responsive*` export. Classes use the nivo name (`nivo.Bar`, `nivo.HeatMapCanvas`…).
|
|
63
|
+
|
|
64
|
+
| Family | SVG | Canvas | HTML |
|
|
65
|
+
| --- | --- | --- | --- |
|
|
66
|
+
| Bar | `bar` | `bar_canvas` | |
|
|
67
|
+
| Line | `line` | `line_canvas` | |
|
|
68
|
+
| Pie | `pie` | `pie_canvas` | |
|
|
69
|
+
| Radar · RadialBar · PolarBar | `radar` · `radial_bar` · `polar_bar` | | |
|
|
70
|
+
| Waffle | `waffle` | `waffle_canvas` | `waffle_html` |
|
|
71
|
+
| ScatterPlot | `scatterplot` | `scatterplot_canvas` | |
|
|
72
|
+
| SwarmPlot | `swarmplot` | `swarmplot_canvas` | |
|
|
73
|
+
| BoxPlot · Voronoi | `boxplot` · `voronoi` | | |
|
|
74
|
+
| HeatMap | `heatmap` | `heatmap_canvas` | |
|
|
75
|
+
| TreeMap | `treemap` | `treemap_canvas` | `treemap_html` |
|
|
76
|
+
| CirclePacking | `circle_packing` | `circle_packing_canvas` | `circle_packing_html` |
|
|
77
|
+
| Sunburst | `sunburst` | | |
|
|
78
|
+
| Icicle | `icicle` | | `icicle_html` |
|
|
79
|
+
| Tree | `tree` | `tree_canvas` | |
|
|
80
|
+
| Sankey · Funnel · Marimekko | `sankey` · `funnel` · `marimekko` | | |
|
|
81
|
+
| Chord | `chord` | `chord_canvas` | |
|
|
82
|
+
| Network | `network` | `network_canvas` | |
|
|
83
|
+
| ParallelCoordinates | `parallel_coordinates` | `parallel_coordinates_canvas` | |
|
|
84
|
+
| Calendar | `calendar` | `calendar_canvas` | |
|
|
85
|
+
| TimeRange · Stream · Bump · AreaBump · Bullet | `time_range` · `stream` · `bump` · `area_bump` · `bullet` | | |
|
|
86
|
+
| GeoMap | `geo_map` | `geo_map_canvas` | |
|
|
87
|
+
| Choropleth | `choropleth` | `choropleth_canvas` | |
|
|
88
|
+
|
|
89
|
+
The data shape and every prop are documented on nivo.rocks (each class docstring links to its page).
|
|
90
|
+
Convert prop names to snake_case: `indexBy` → `index_by`, `enableGridX` → `enable_grid_x`.
|
|
91
|
+
|
|
92
|
+
### Renamed props
|
|
93
|
+
|
|
94
|
+
| nivo | reflex-nivo | why |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| `id` (Pie, Sunburst, CirclePacking, SwarmPlot, Marimekko) | `id_by` | `id` is the HTML id of the container |
|
|
97
|
+
| `from` / `to` (Calendar, TimeRange) | `from_date` / `to_date` | `from` is a Python keyword |
|
|
98
|
+
|
|
99
|
+
Plain `YYYY-MM-DD` values for `from_date`/`to_date` are sent as local midnight, so calendars do not
|
|
100
|
+
shift one day (and one year) back west of Greenwich.
|
|
101
|
+
|
|
102
|
+
## Sizing
|
|
103
|
+
|
|
104
|
+
nivo's responsive charts fill their parent, so every factory returns the chart inside a `<div>`.
|
|
105
|
+
`width` (default `"100%"`), `height` (default `"400px"`) and any other CSS prop, `class_name`, `id`
|
|
106
|
+
or `style` go to that container. Anything that is neither a nivo prop nor CSS raises a `TypeError`
|
|
107
|
+
with a suggestion, so typos like `enable_gridx` do not fail silently.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
nivo.pie(data=State.pie, height="320px", max_width="600px", margin_x="auto")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
(`margin` itself is nivo's chart margin: use `nivo.margin(top=.., right=.., bottom=.., left=..)`.)
|
|
114
|
+
|
|
115
|
+
## Events
|
|
116
|
+
|
|
117
|
+
All nivo callbacks are exposed as event triggers. The handler receives nivo's first callback argument
|
|
118
|
+
(datum, point, slice, node, cell, serie, link, feature, dimensions, active id…), serialized in the
|
|
119
|
+
browser: functions, DOM nodes and React events are dropped, repeated/cyclic objects are replaced by
|
|
120
|
+
their `id`, and depth is bounded.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
class State(rx.State):
|
|
124
|
+
@rx.event
|
|
125
|
+
def drill_down(self, node: dict):
|
|
126
|
+
if node["data"].get("children"):
|
|
127
|
+
self.sunburst = node["data"] # the raw datum travels with the event
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
nivo.sunburst(data=State.sunburst, id_by="name", value="loc", on_click=State.drill_down)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Only the callbacks a chart actually declares carry a datum, and they differ per chart: `@nivo/bar`
|
|
134
|
+
exposes `on_click`, `on_mouse_enter` and `on_mouse_leave`, while `@nivo/stream` exposes none of them.
|
|
135
|
+
Any other `on_*` is attached to the wrapping `<div>` as a plain DOM event, so it fires for the whole
|
|
136
|
+
chart area but receives no datum (write the handler without arguments). An `on_*` that is neither
|
|
137
|
+
raises a `TypeError` listing the chart's nivo callbacks.
|
|
138
|
+
|
|
139
|
+
`on_mouse_move` fires very often; on the charts that expose it, prefer click/enter/leave or throttle
|
|
140
|
+
it: `on_mouse_move=State.hover.throttle(100)`.
|
|
141
|
+
|
|
142
|
+
### Hiding a prop
|
|
143
|
+
|
|
144
|
+
Passing `None` sends JavaScript `null`, which turns the feature off instead of falling back to nivo's
|
|
145
|
+
default — that is how you hide an axis:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
nivo.bar(data=State.rows, axis_bottom=None, axis_left=None) # no axes
|
|
149
|
+
nivo.pie(data=State.pie, theme=None) # nivo's own theme instead of the Reflex one
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Helpers
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
nivo.margin(top=20, right=20, bottom=40, left=50)
|
|
156
|
+
nivo.axis(legend="price", legend_position="middle", legend_offset=-40, tick_rotation=-45, format=" >-$.2f")
|
|
157
|
+
nivo.legend(
|
|
158
|
+
anchor="bottom-right", direction="column", translate_x=100, effects=[nivo.hover_effect(item_opacity=1)]
|
|
159
|
+
)
|
|
160
|
+
nivo.scale("time", format="%Y-%m-%d", precision="day")
|
|
161
|
+
nivo.scheme("category10") # {"scheme": "category10"}
|
|
162
|
+
nivo.inherit("color", ("darker", 1.6)) # inherited color
|
|
163
|
+
nivo.from_theme("background") # color from theme
|
|
164
|
+
nivo.marker("y", 100, legend="target")
|
|
165
|
+
nivo.annotation({"id": "fries"}, type_="circle", note="max", note_x=40, note_y=-30)
|
|
166
|
+
nivo.pattern_dots_def("dots", background="inherit", color="#38bcb2")
|
|
167
|
+
nivo.pattern_lines_def("lines", rotation=-45) / nivo.pattern_squares_def(...) / nivo.linear_gradient_def(...)
|
|
168
|
+
nivo.fill_rule("dots", {"id": "fries"})
|
|
169
|
+
nivo.props(any_snake_case="option") # generic camelCase dict builder
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Constants: `nivo.COLOR_SCHEMES` (categorical/diverging/sequential), `nivo.CURVES`, `nivo.MOTION_CONFIGS`.
|
|
173
|
+
|
|
174
|
+
### Functions: templates, tooltips and raw JS
|
|
175
|
+
|
|
176
|
+
nivo accepts functions for labels, formatters, accessors, tooltips and custom layers.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# Formatter/label from a {path} template, evaluated in the browser:
|
|
180
|
+
label = nivo.template("{id}: {formattedValue}")
|
|
181
|
+
|
|
182
|
+
# Tooltip component from a template. It may use attribute-less <b>, <strong>, <i>, <em>, <u>,
|
|
183
|
+
# <s>, <small>, <code>, <span>, <div>, <p> and <br>; anything else, and every interpolated
|
|
184
|
+
# value, is rendered as text, so template and values are safe to build from a state Var.
|
|
185
|
+
tooltip = nivo.tooltip("<b>{datum.label}</b><br/>{datum.formattedValue}", style={"min_width": "120px"})
|
|
186
|
+
|
|
187
|
+
# Anything else: a raw JavaScript expression (never build it from user input).
|
|
188
|
+
value_format = nivo.js("v => `${v.toLocaleString()} €`")
|
|
189
|
+
node_size = nivo.js("n => n.size")
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Most formatters also accept d3-format / d3-time-format strings directly (`value_format=".2s"`,
|
|
193
|
+
`axis(format="%b %d")`).
|
|
194
|
+
|
|
195
|
+
## Themes
|
|
196
|
+
|
|
197
|
+
Charts use `nivo.themes.auto()` unless you pass `theme=`: a light/dark pair built on the Radix gray
|
|
198
|
+
scale that switches with `rx.color_mode`. Customize or pin it:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
theme = nivo.themes.auto({"grid": {"line": {"strokeDasharray": "4 4"}}}) # both modes
|
|
202
|
+
theme = nivo.themes.auto(light={"text": {"fill": "#333"}}, dark={"text": {"fill": "#eee"}})
|
|
203
|
+
theme = nivo.themes.DARK # fixed
|
|
204
|
+
theme = State.computed_theme # any nivo theme dict
|
|
205
|
+
theme = {} # nivo's own default
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Demo
|
|
209
|
+
|
|
210
|
+
`nivo_demo/` is a 10-page app that uses all 51 components with live state controls, an event
|
|
211
|
+
inspector, drill-down/zoom interactions, geo projections, canvas variants with thousands of marks and
|
|
212
|
+
customization examples.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
uv venv && uv pip install -e . && cd nivo_demo && uv pip install -r requirements.txt
|
|
216
|
+
uv run reflex run # http://localhost:3010 (backend on 8010)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
World country shapes in the demo come from the nivo website dataset (Natural Earth).
|
|
220
|
+
|
|
221
|
+
## Regenerating the wrappers
|
|
222
|
+
|
|
223
|
+
The chart modules in `custom_components/reflex_nivo/charts/` are generated from nivo's typings:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# in a scratch directory
|
|
227
|
+
npm i typescript@5 @types/react@19 react@19 react-dom@19 @nivo/{bar,line,pie,...}@0.99.0
|
|
228
|
+
node /path/to/reflex-nivo/scripts/extract_nivo_props.cjs bar boxplot bullet bump calendar chord \
|
|
229
|
+
circle-packing funnel heatmap icicle line marimekko network pie polar-bar radar radial-bar sankey \
|
|
230
|
+
scatterplot stream sunburst swarmplot tree treemap voronoi waffle > /path/to/reflex-nivo/scripts/nivo_props.json
|
|
231
|
+
# back in the repo (geo and parallel-coordinates ship no usable typings: see scripts/nivo_manual_props.json)
|
|
232
|
+
python scripts/generate_components.py
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Bump `NIVO_VERSION` in `custom_components/reflex_nivo/constants.py` together with the extraction.
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
uv pip install -e ".[dev]"
|
|
241
|
+
uv run pytest
|
|
242
|
+
uv run reflex component build # generates .pyi stubs, then sdist + wheel in dist/
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT © Ernesto Crespo. nivo is MIT © Raphaël Benitte.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""reflex-nivo: every nivo (https://nivo.rocks) chart as a Reflex component.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
import reflex_nivo as nivo
|
|
6
|
+
|
|
7
|
+
nivo.bar(data=State.rows, keys=["a", "b"], index_by="country", height="420px")
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from . import themes
|
|
11
|
+
from .base import NivoComponent, nivo_event_spec
|
|
12
|
+
from .charts import * # noqa: F403
|
|
13
|
+
from .charts import __all__ as _charts_all
|
|
14
|
+
from .constants import (
|
|
15
|
+
CATEGORICAL_COLOR_SCHEMES,
|
|
16
|
+
COLOR_SCHEMES,
|
|
17
|
+
CURVES,
|
|
18
|
+
DIVERGING_COLOR_SCHEMES,
|
|
19
|
+
MOTION_CONFIGS,
|
|
20
|
+
NIVO_VERSION,
|
|
21
|
+
SEQUENTIAL_COLOR_SCHEMES,
|
|
22
|
+
)
|
|
23
|
+
from .helpers import * # noqa: F403
|
|
24
|
+
from .helpers import __all__ as _helpers_all
|
|
25
|
+
|
|
26
|
+
__version__ = "0.1.0"
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"CATEGORICAL_COLOR_SCHEMES",
|
|
30
|
+
"COLOR_SCHEMES",
|
|
31
|
+
"CURVES",
|
|
32
|
+
"DIVERGING_COLOR_SCHEMES",
|
|
33
|
+
"MOTION_CONFIGS",
|
|
34
|
+
"NIVO_VERSION",
|
|
35
|
+
"SEQUENTIAL_COLOR_SCHEMES",
|
|
36
|
+
"NivoComponent",
|
|
37
|
+
"nivo_event_spec",
|
|
38
|
+
"themes",
|
|
39
|
+
*_charts_all,
|
|
40
|
+
*_helpers_all,
|
|
41
|
+
]
|