Py.Cafe

awesome.panel.org/

panel-reactable-table

Interactive Tabular Data with Reactable in Panel

DocsPricing
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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()
requirements.txt
1
2
3
4
reactable
urllib3
sqlite3
panel