diff --git a/ag_grid_demo/ag_grid_demo/__init__.py b/ag_grid_demo/ag_grid_demo/__init__.py index 483f926..0f63252 100644 --- a/ag_grid_demo/ag_grid_demo/__init__.py +++ b/ag_grid_demo/ag_grid_demo/__init__.py @@ -1 +1,2 @@ from . import model_dm as model_dm +from . import tree as tree diff --git a/ag_grid_demo/ag_grid_demo/ag_grid_demo.py b/ag_grid_demo/ag_grid_demo/ag_grid_demo.py index 943f63f..a5901d7 100644 --- a/ag_grid_demo/ag_grid_demo/ag_grid_demo.py +++ b/ag_grid_demo/ag_grid_demo/ag_grid_demo.py @@ -53,6 +53,7 @@ def index(): rx.link("Customized ModelWrapper", href="/model-auth"), " (Generate data)", ), + rx.link("Tree (enterprise)", href="/tree"), ), rx.vstack( rx.heading("Selected Items"), diff --git a/ag_grid_demo/ag_grid_demo/model_dm.py b/ag_grid_demo/ag_grid_demo/model_dm.py index 5a19160..5c9a75c 100644 --- a/ag_grid_demo/ag_grid_demo/model_dm.py +++ b/ag_grid_demo/ag_grid_demo/model_dm.py @@ -105,7 +105,7 @@ async def _get_data(self, start, end, filter_model, sort_model): auth_state = await self.get_state(AuthState) if not auth_state.logged_in: return [] # no records for logged out users - return super()._get_data( + return await super()._get_data( start, end, filter_model=filter_model, sort_model=sort_model ) diff --git a/ag_grid_demo/ag_grid_demo/tree.py b/ag_grid_demo/ag_grid_demo/tree.py new file mode 100644 index 0000000..c535c8e --- /dev/null +++ b/ag_grid_demo/ag_grid_demo/tree.py @@ -0,0 +1,261 @@ +from typing import Any +import reflex as rx + +from reflex_ag_grid import ag_grid + + +human_size = rx.vars.FunctionStringVar(""" +(params) => { + const sizeInKb = params.value / 1024; + + if (sizeInKb > 1024) { + return `${+(sizeInKb / 1024).toFixed(2)} MB`; + } else { + return `${+sizeInKb.toFixed(2)} KB`; + } +}""") + + +class TreeDisplayState(rx.State): + data: list[dict[str, Any]] = [ + { + "host": "vali", + "path": ["Desktop", "ProjectAlpha", "Proposal.docx"], + "size": 512000, + "created": "2023-07-10", + "modified": "2023-08-01", + }, + { + "host": "vali", + "path": ["Desktop", "ProjectAlpha", "Timeline.xlsx"], + "size": 1048576, + "created": "2023-07-12", + "modified": "2023-08-03", + }, + { + "host": "vali", + "path": ["Desktop", "ToDoList.txt"], + "size": 51200, + "created": "2023-08-05", + "modified": "2023-08-10", + }, + { + "host": "vali", + "path": ["Desktop", "MeetingNotes_August.pdf"], + "size": 460800, + "created": "2023-08-15", + "modified": "2023-08-15", + }, + { + "host": "vidar", + "path": ["Desktop", "LaunchCodes.txt"], + "size": 32500, + "created": "1973-08-05", + "modified": "2023-08-10", + }, + { + "host": "vidar", + "path": ["Desktop", "funtime.pdf"], + "size": 460800, + "created": "2023-08-15", + "modified": "2023-08-15", + }, + { + "host": "vali", + "path": ["Documents", "Work", "ProjectAlpha", "Proposal.docx"], + "size": 512000, + "created": "2023-07-10", + "modified": "2023-08-01", + }, + { + "host": "vali", + "path": ["Documents", "Work", "ProjectAlpha", "Timeline.xlsx"], + "size": 1048576, + "created": "2023-07-12", + "modified": "2023-08-03", + }, + { + "host": "vali", + "path": ["Documents", "Work", "ProjectBeta", "Report.pdf"], + "size": 1024000, + "created": "2023-06-22", + "modified": "2023-07-15", + }, + { + "host": "vali", + "path": ["Documents", "Work", "ProjectBeta", "Budget.xlsx"], + "size": 1048576, + "created": "2023-06-25", + "modified": "2023-07-18", + }, + { + "host": "vidar", + "path": ["Documents", "Work", "Meetings", "TeamMeeting_August.pdf"], + "size": 512000, + "created": "2023-08-20", + "modified": "2023-08-21", + }, + { + "host": "vidar", + "path": ["Documents", "Work", "Meetings", "ClientMeeting_July.pdf"], + "size": 1048576, + "created": "2023-07-15", + "modified": "2023-07-16", + }, + { + "host": "vali", + "path": ["Documents", "Personal", "Taxes", "2022.pdf"], + "size": 1024000, + "created": "2023-04-10", + "modified": "2023-04-10", + }, + { + "host": "vali", + "path": ["Documents", "Personal", "Taxes", "2021.pdf"], + "size": 1048576, + "created": "2022-04-05", + "modified": "2022-04-06", + }, + { + "host": "vali", + "path": ["Documents", "Personal", "Taxes", "2020.pdf"], + "size": 1024000, + "created": "2021-04-03", + "modified": "2021-04-03", + }, + { + "host": "vali", + "path": ["Pictures", "Vacation2019", "Beach.jpg"], + "size": 1048576, + "created": "2019-07-10", + "modified": "2019-07-12", + }, + { + "host": "vali", + "path": ["Pictures", "Vacation2019", "Mountain.png"], + "size": 2048000, + "created": "2019-07-11", + "modified": "2019-07-13", + }, + { + "host": "vali", + "path": ["Pictures", "Family", "Birthday2022.jpg"], + "size": 3072000, + "created": "2022-12-15", + "modified": "2022-12-20", + }, + { + "host": "vali", + "path": ["Pictures", "Family", "Christmas2021.png"], + "size": 2048000, + "created": "2021-12-25", + "modified": "2021-12-26", + }, + { + "host": "vali", + "path": ["Videos", "Vacation2019", "Beach.mov"], + "size": 4194304, + "created": "2019-07-10", + "modified": "2019-07-12", + }, + { + "host": "vali", + "path": ["Videos", "Vacation2019", "Hiking.mp4"], + "size": 4194304, + "created": "2019-07-15", + "modified": "2019-07-16", + }, + { + "host": "vali", + "path": ["Videos", "Family", "Birthday2022.mp4"], + "size": 6291456, + "created": "2022-12-15", + "modified": "2022-12-20", + }, + { + "host": "vali", + "path": ["Videos", "Family", "Christmas2021.mov"], + "size": 6291456, + "created": "2021-12-25", + "modified": "2021-12-26", + }, + { + "host": "vidar", + "path": ["Downloads", "SoftwareInstaller.exe"], + "size": 2097152, + "created": "2023-08-01", + "modified": "2023-08-01", + }, + { + "host": "vidar", + "path": ["Downloads", "Receipt_OnlineStore.pdf"], + "size": 1048576, + "created": "2023-08-05", + "modified": "2023-08-05", + }, + { + "host": "vali", + "path": ["Downloads", "Ebook.pdf"], + "size": 1048576, + "created": "2023-08-08", + "modified": "2023-08-08", + }, + ] + combine_hosts: rx.Field[bool] = rx.field(True) + + +@rx.page("/tree") +def tree_example(): + return rx.box( + rx.hstack( + "Combine Hosts", + rx.switch( + checked=TreeDisplayState.combine_hosts, + on_change=TreeDisplayState.set_combine_hosts, + ), + align="center", + ), + ag_grid.root( + id="ag_grid_tree_1", + row_data=TreeDisplayState.data, + auto_group_column_def=ag_grid.column_def( + field="", + header_name="File Explorer", + min_width=280, + cell_renderer_params={"suppressCount": True}, + ), + column_defs=[ + ag_grid.column_def(field="created"), + ag_grid.column_def(field="modified"), + ag_grid.column_def( + field="size", + agg_func="sum", + value_formatter=human_size, + ), + ag_grid.column_def( + field="host", + hide=~TreeDisplayState.combine_hosts, + agg_func=rx.vars.FunctionStringVar( + "(params) => params.values.join(', ').split(', ').filter((value, index, self) => self.indexOf(value) === index).join(', ')" + ), + ), + ], + default_column_def={"flex": 1}, + group_default_expanded=False, + tree_data=True, + # get_data_path is an Initial option, it cannot be set after initialization. + get_data_path=rx.cond( + TreeDisplayState.combine_hosts, + rx.vars.FunctionStringVar( + "(data) => data.path", + ), + rx.vars.FunctionStringVar( + "(data) => [data.host, ...data.path]", + ), + ).to(rx.vars.FunctionVar), + # This key causes the grid to re-initialize when `combine_hosts` var changes + key=f"ag_grid_{TreeDisplayState.combine_hosts}", + ), + width="100vw", + height="100vh", + ) diff --git a/custom_components/reflex_ag_grid/ag_grid.py b/custom_components/reflex_ag_grid/ag_grid.py index 6d2007f..4a19912 100644 --- a/custom_components/reflex_ag_grid/ag_grid.py +++ b/custom_components/reflex_ag_grid/ag_grid.py @@ -13,6 +13,9 @@ from reflex.components.el import Div +AG_GRID_VERSION = "32.2.0" + + def callback_content(iterable: list[str]) -> str: return "; ".join(iterable) @@ -184,6 +187,10 @@ class ColumnDef(PropsBase): cell_renderer: rx.Var | None = None flex: int | rx.Var[int] | None = None + def dict(self, *args, **kwargs): + kwargs.setdefault("exclude_none", True) + return super().dict(*args, **kwargs) + class ColumnGroup(PropsBase): children: list["ColumnDef | ColumnGroup"] | rx.Var[list["ColumnDef | ColumnGroup"]] @@ -238,7 +245,7 @@ class AgGrid(rx.Component): """Reflex AgGrid component is a high-performance and highly customizable component that wraps AG Grid, designed for creating rich datagrids.""" # The library name for the ag-grid-react component - library: str = "ag-grid-react@32.1.0" + library: str = f"ag-grid-react@{AG_GRID_VERSION}" # The tag name for the AgGridReact component tag: str = "AgGridReact" @@ -298,7 +305,7 @@ class AgGrid(rx.Component): suppress_row_click_selection: rx.Var[bool] = rx.Var.create(False) # Event handler for getting the data path - get_data_path: rx.EventHandler[lambda e0: [e0]] + get_data_path: rx.vars.FunctionVar[Any] # Variable to allow unbalanced groups group_allow_unbalanced: rx.Var[bool] = rx.Var.create(False) @@ -418,8 +425,8 @@ class AgGrid(rx.Component): on_first_data_rendered: rx.EventHandler[_on_cell_event_spec] lib_dependencies: list[str] = [ - "ag-grid-community@32.1.0", - "ag-grid-enterprise@32.1.0", + f"ag-grid-community@{AG_GRID_VERSION}", + f"ag-grid-enterprise@{AG_GRID_VERSION}", ] # Change the aesthetic theme of the grid theme: rx.Var[Literal["quartz", "balham", "alpine", "material"]] @@ -444,8 +451,8 @@ def create( # handle hierarchical data if data_path_key is not None: - props["get_data_path"] = rx.Var(f"(data) => data.{data_path_key}").to( - rx.EventChain + props["get_data_path"] = rx.vars.FunctionStringVar( + f"(data) => data.{data_path_key}" ) if is_server_side_group_key is not None: @@ -501,7 +508,7 @@ def add_imports(self): "ag-grid-enterprise", ], "d3-format": ["format"], - "ag-grid-enterprise": [ + f"ag-grid-enterprise@{AG_GRID_VERSION}": [ "LicenseManager", ], } diff --git a/custom_components/reflex_ag_grid/wrapper.py b/custom_components/reflex_ag_grid/wrapper.py index 7ec1f05..6a76e7f 100644 --- a/custom_components/reflex_ag_grid/wrapper.py +++ b/custom_components/reflex_ag_grid/wrapper.py @@ -434,6 +434,7 @@ def create(cls, *children, model_class: Type[M], **props) -> rx.Component: return rx.fragment( comp.State._top_toolbar(), comp, + State=comp.State, )