from typing import cast
import solara
import solara.lab
from ipyaggrid import Grid
import pandas as pd
from reacton.core import ValueElement
import reacton
aggrid_css: str = """
.ag-header-cell {
background-color: #1c87c9;
color: #FFF;
}
.ag-theme-balham {
--ag-odd-row-background-color: #C6E6FB;
--ag-value-change-value-highlight-background-color: lightgreen;
--ag-row-hover-color: deepskyblue;
font-family: 'Open Sans', sans-serif;
font-size: 13px;
}
.ag-theme-balham-dark {
font-family: 'Open Sans', sans-serif;
font-size: 13px;
}
"""
default_col_def = {
'sortable': True,
'resizable': True,
'minWidth': 100,
'filter': 'agSetColumnFilter',
'editable': True,
'filterParams': {
'excelMode': 'windows'
}
}
default_grid_options = {
'animateRows': True,
'rowDragManaged': True,
'enableRangeSelection': True,
'rowSelection': True,
'defaultColDef': default_col_def
}
class SolaraAgGrid(Grid):
def __init__(self, *args, **kwargs):
height = None
if 'height' in kwargs:
height = kwargs.pop('height')
if 'grid_data_out' in kwargs:
kwargs.pop('grid_data_out')
if 'css_rules' not in kwargs:
kwargs['css_rules'] = aggrid_css
super().__init__(*args, **kwargs)
self.height = f'{height}' if height is not None else '500px'
def AgGrid(dark_mode, **kwargs) -> ValueElement[Grid, dict]:
theme = 'ag-theme-balham' if not dark_mode else 'ag-theme-balham-dark'
kwargs['theme'] = theme
df = kwargs['grid_data']
grid_options = kwargs['grid_options']
def update_df():
widget = cast(SolaraAgGrid, solara.get_widget(el))
if widget is not None:
widget.grid_options = grid_options
# without this, the height would go back to default
if 'height' in kwargs:
widget.height_in = int(kwargs['height'].split('px')[0])
widget.update_grid_data(df) # this also updates the grid_options
# when df changes, grid_data will be updated, however, ...
# grid_data and grid_options are not traits, so letting them update by reacton/solara has no effect
# instead, we need to get a reference to the widget and call .update_grid_data in a use_effect
solara.use_effect(effect=update_df, dependencies=[df, grid_options])
comp = reacton.core.ComponentWidget(widget=SolaraAgGrid)
el = ValueElement(value_property="grid_data_out", component=comp, kwargs=kwargs).key(f'theme-{theme}')
return el
@solara.component
def Page():
edit_dict = solara.Reactive(dict())
data = [
{"make": "Toyota", "model": "Celica", "price": 35000},
{"make": "Ford", "model": "Mondeo", "price": 32000},
{"make": "Porsche", "model": "Boxster", "price": 72000}
]
column_defs = [
{"headerName": "Make", "field": "make", "sortable": True},
{"headerName": "Model", "field": "model"},
{"headerName": "Price", "field": "price"}
]
grid_options = default_grid_options.copy()
grid_options['columnDefs'] = column_defs
df, df_set = solara.use_state(pd.DataFrame.from_dict(data))
with solara.AppBar():
solara.lab.ThemeToggle()
with solara.Card('Cars'):
edittable_grid = AgGrid(
grid_data=df,
grid_options=grid_options,
columns_fit = 'size_to_fit',
dark_mode = solara.lab.use_dark_effective(),
sync_on_edit = True,
export_mode = 'auto',
export_to_df = True,
sync_grid = True,
)
edittable_grid.connect(edit_dict)
if 'grid' in edit_dict.value:
print(edit_dict.value)
with solara.Card('Results'):
solara.DataFrame(edit_dict.value['grid'])