import htmltools
import panel as pn
import param
from panel.custom import ReactComponent
from reactable import CellInfo, Column, Reactable
from reactable.data import cars_93
# Create Reusable Panel Reactable Component
## Should probably be local reactable versions to support airgapped environments
REACTABLE_CSS = "https://cdn.jsdelivr.net/gh/machow/reactable-py@main/reactable/static/reactable-py.esm.css"
REACTABLE_JS = "https://cdn.jsdelivr.net/gh/machow/reactable-py@main/reactable/static/reactable-py.esm.js"
class ReactableTable(ReactComponent):
"""ReactableTable Pane that renders Reactable objects."""
object: Reactable = param.ClassSelector(
class_=Reactable, doc="The Reactable object to render."
)
_data = param.Dict()
_rename = {"object": None}
def __init__(self, object: Reactable, **params):
super().__init__(object=object, **params)
@param.depends("object", watch=True, on_init=True)
def _handle_object_change(self):
if not self.object:
self._data = {}
else:
## One day we should extract the data object directly and transfer it more efficiently via Bokeh ColumnDataSource
## self._data = self.object.data
## self._other_props = self.object.to_props(include_data=False)
self._data = self.object.to_props()
_importmap = {
"imports": {
"reactable": REACTABLE_JS,
}
}
_esm = """
import Reactable from "reactable"
export function render({model}) {
const [props] = model.useState('_data');
return <Reactable {...props} />
}
"""
_stylesheets = [REACTABLE_CSS]
# Create custom reactable
pn.extension(sizing_mode="stretch_width")
ACCENT = "#fc5185"
HEADER = """
# reactable-py
[reactable-py](https://github.com/machow/reactable-py) generates interactive tables in Python. \
It's a port of the R package [reactable](https://github.com/glin/reactable) by [@glin](https://github.com/glin).
❤️ **It works with [Panel](https://panel.holoviz.org/reference/index.html)** as you can see below:"""
DETAILS = """
## Documentation
See these handy documentation pages:
- [📚 User guide](https://machow.github.io/reactable-py/get-started)
- [🧩 Examples](https://machow.github.io/reactable-py/demos/)
## Features
- **controls**: sorting, filtering, and pagination.
- **structure**: grouping, aggregating, expanding rows.
- **format**: represent numbers, dates, currencies.
- **style**: conditional styling for headers, cell values, and more.
## Installing
```bash
pip install reactable
```
"""
data = cars_93[:5, ["make", "mpg_city", "mpg_highway"]]
def html_barchart(label, width="100%", height="1rem", fill="#00bfc4", background=None):
"""Create general purpose html fill bar."""
bar = htmltools.div(style=f"background: {fill}; width: {width}; height: {height}")
chart = htmltools.div(
bar,
style=htmltools.css(
flex_grow=1,
margin_left="0.5rem",
background=background,
),
)
return htmltools.div(
label,
chart,
style=htmltools.css(
display="flex",
align_items="center",
),
)
def fmt_barchart(ci: CellInfo, **kwargs):
"""Format cell value into html fill bar."""
width = f"{ci.value / max(data['mpg_city']) * 100}%"
return html_barchart(ci.value, width=width, **kwargs)
reactable = Reactable(
data,
columns={
"mpg_city": Column(
name="MPG (city)",
align="left",
cell=fmt_barchart,
),
"mpg_highway": Column(
name="MPG (highway)",
align="left",
cell=lambda ci: fmt_barchart(ci, fill="#fc5185", background="#e1e1e1"),
),
},
default_page_size=5,
)
reactable_table = ReactableTable(reactable)
# Layout the components
## Should support dark FastListTemplate table css one day
pn.template.FastListTemplate(
title="Panel reactable-py",
main=[HEADER, reactable_table, DETAILS],
accent=ACCENT,
main_layout=None,
main_max_width="800px",
).servable()