Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 1 addition & 120 deletions pyi_hashes.json
Original file line number Diff line number Diff line change
@@ -1,122 +1,3 @@
{
"reflex/__init__.pyi": "0a3ae880e256b9fd3b960e12a2cb51a7",
"reflex/components/__init__.pyi": "ac05995852baa81062ba3d18fbc489fb",
"reflex/components/base/__init__.pyi": "16e47bf19e0d62835a605baa3d039c5a",
"reflex/components/base/app_wrap.pyi": "22e94feaa9fe675bcae51c412f5b67f1",
"reflex/components/base/body.pyi": "e8ab029a730824bab6d4211203609e6a",
"reflex/components/base/document.pyi": "311c53c90a60587a82e760103758a3cf",
"reflex/components/base/error_boundary.pyi": "a678cceea014cb16048647257cd24ba6",
"reflex/components/base/fragment.pyi": "745f1be02c23a0b25d7c52d7423ec76a",
"reflex/components/base/link.pyi": "0bc1d26ee29d8864aed14a12991bd47d",
"reflex/components/base/meta.pyi": "129aecf65ab53f756c4d1cbe1d0b188d",
"reflex/components/base/script.pyi": "e5f506d1d0d6712cb9e597a781eb3941",
"reflex/components/base/strict_mode.pyi": "6b72e16caadf7158ab744a0ab751b010",
"reflex/components/core/__init__.pyi": "007170b97e58bdf28b2aee381d91c0c7",
"reflex/components/core/auto_scroll.pyi": "18068d22aca7244a08cd0c5a897c0950",
"reflex/components/core/banner.pyi": "fd93e7a92961de8524718ad32135c37c",
"reflex/components/core/clipboard.pyi": "a844eb927d9bc2a43f5e88161b258539",
"reflex/components/core/debounce.pyi": "055da7aa890f44fb4d48bd5978f1a874",
"reflex/components/core/helmet.pyi": "43f8497c8fafe51e29dca1dd535d143a",
"reflex/components/core/html.pyi": "86eb9d4c1bb4807547b2950d9a32e9fd",
"reflex/components/core/sticky.pyi": "cb763b986a9b0654d1a3f33440dfcf60",
"reflex/components/core/upload.pyi": "6dc28804a6dddf903e31162e87c1b023",
"reflex/components/core/window_events.pyi": "af33ccec866b9540ee7fbec6dbfbd151",
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
"reflex/components/datadisplay/code.pyi": "b86769987ef4d1cbdddb461be88539fd",
"reflex/components/datadisplay/dataeditor.pyi": "fb26f3e702fcb885539d1cf82a854be3",
"reflex/components/datadisplay/shiki_code_block.pyi": "1d53e75b6be0d3385a342e7b3011babd",
"reflex/components/el/__init__.pyi": "0adfd001a926a2a40aee94f6fa725ecc",
"reflex/components/el/element.pyi": "c5974a92fbc310e42d0f6cfdd13472f4",
"reflex/components/el/elements/__init__.pyi": "29512d7a6b29c6dc5ff68d3b31f26528",
"reflex/components/el/elements/base.pyi": "3f74c7ea573ea29b055b0cd48b040d2c",
"reflex/components/el/elements/forms.pyi": "8b6bb2fbaf4bad828b076e2f7c8444d0",
"reflex/components/el/elements/inline.pyi": "3549cd6ad45217aa6387800911b641c3",
"reflex/components/el/elements/media.pyi": "9b97220aa99783d402b6e278c4069043",
"reflex/components/el/elements/metadata.pyi": "24448004b7aa07f1225028a85bd49fef",
"reflex/components/el/elements/other.pyi": "0c4d5d0b955d8596bf6cf4a48d7decdb",
"reflex/components/el/elements/scripts.pyi": "d33df9f21f7e838376b2b5024beef7c9",
"reflex/components/el/elements/sectioning.pyi": "3c5a7e4caa9c25da0ae788f02466eac4",
"reflex/components/el/elements/tables.pyi": "686eb70ea7d8c4dafb0cc5c284e76184",
"reflex/components/el/elements/typography.pyi": "684e83dde887dba12badd0fb75c87c04",
"reflex/components/gridjs/datatable.pyi": "98a7e1b3f3b60cafcdfcd8879750ee42",
"reflex/components/lucide/icon.pyi": "9cdd1107295f5c4b6d5d6516f487f237",
"reflex/components/markdown/markdown.pyi": "dd74e8e9665b2a813ff799a7aa190b44",
"reflex/components/moment/moment.pyi": "e1952f1c2c82cef85d91e970d1be64ab",
"reflex/components/plotly/plotly.pyi": "4311a0aae2abcc9226abb6a273f96372",
"reflex/components/radix/__init__.pyi": "5d8e3579912473e563676bfc71f29191",
"reflex/components/radix/primitives/__init__.pyi": "01c388fe7a1f5426a16676404344edf6",
"reflex/components/radix/primitives/accordion.pyi": "19484eca0ad53f538f5db04c09921738",
"reflex/components/radix/primitives/base.pyi": "9ef34884fb6028dc017df5e2db639c81",
"reflex/components/radix/primitives/dialog.pyi": "9ee73362bb59619c482b6b0d07033f37",
"reflex/components/radix/primitives/drawer.pyi": "921e45dfaf5b9131ef27c561c3acca2e",
"reflex/components/radix/primitives/form.pyi": "78055e820703c98c3b838aa889566365",
"reflex/components/radix/primitives/progress.pyi": "c917952d57ddb3e138a40c4005120d5e",
"reflex/components/radix/primitives/slider.pyi": "4ff06f0025d47f166132909b09ab96f8",
"reflex/components/radix/themes/__init__.pyi": "582b4a7ead62b2ae8605e17fa084c063",
"reflex/components/radix/themes/base.pyi": "3e1ccd5ce5fef0b2898025193ee3d069",
"reflex/components/radix/themes/color_mode.pyi": "dda570583355d8c0d8f607be457ba7a1",
"reflex/components/radix/themes/components/__init__.pyi": "efa279ee05479d7bb8a64d49da808d03",
"reflex/components/radix/themes/components/alert_dialog.pyi": "eed422fcc1ff5ccf3dbf6934699bd0b1",
"reflex/components/radix/themes/components/aspect_ratio.pyi": "71de4160d79840561c48b570197a4152",
"reflex/components/radix/themes/components/avatar.pyi": "e40c2f0fda6d2c028d83681a27f3fb96",
"reflex/components/radix/themes/components/badge.pyi": "58fd1a9c5d2f8762e2a0370311731ff5",
"reflex/components/radix/themes/components/button.pyi": "50f0b08ad5d1d1054ab537152f0f5c43",
"reflex/components/radix/themes/components/callout.pyi": "547f2570ffbd10db36b745566e9f1b17",
"reflex/components/radix/themes/components/card.pyi": "f7adb83f7b001a11bdd7fd6791fb3ffb",
"reflex/components/radix/themes/components/checkbox.pyi": "8eabb6887a5d0849a43e086a284814c2",
"reflex/components/radix/themes/components/checkbox_cards.pyi": "1d567fd04b4425abd5cc5aad10108aa9",
"reflex/components/radix/themes/components/checkbox_group.pyi": "8638582a623036f8893a3fa6080f2672",
"reflex/components/radix/themes/components/context_menu.pyi": "b9499d8bdd2c5565621fea5fe7d7a25a",
"reflex/components/radix/themes/components/data_list.pyi": "6f8d9c582e084c23966b992158193b72",
"reflex/components/radix/themes/components/dialog.pyi": "d2615f1a68c80ff930444d054b598c13",
"reflex/components/radix/themes/components/dropdown_menu.pyi": "43f8770c9adf93c73398d68f79048424",
"reflex/components/radix/themes/components/hover_card.pyi": "a96f4433237f9994decf935deff9f269",
"reflex/components/radix/themes/components/icon_button.pyi": "e930911d8ecbe61e5447e61c76a28ab6",
"reflex/components/radix/themes/components/inset.pyi": "bd7a2186b553bd4c86d83ff50c784066",
"reflex/components/radix/themes/components/popover.pyi": "91f8edefeb232cc6d48690b1838144c2",
"reflex/components/radix/themes/components/progress.pyi": "0e59587d5b3c8fe0d0067587f144e5b0",
"reflex/components/radix/themes/components/radio.pyi": "f375aa5ac746679618ea7dad257e3224",
"reflex/components/radix/themes/components/radio_cards.pyi": "9dc34a1ce2a1924eb1f41438ef84e80b",
"reflex/components/radix/themes/components/radio_group.pyi": "173254cf91908bcf6aa4fa21a747e2cf",
"reflex/components/radix/themes/components/scroll_area.pyi": "2e3539b0f6895dda127ee96e9864dbf9",
"reflex/components/radix/themes/components/segmented_control.pyi": "1776f1ad936bae402007802b1ee98906",
"reflex/components/radix/themes/components/select.pyi": "2c7aee592972ff5f05da08154aa981c8",
"reflex/components/radix/themes/components/separator.pyi": "79e550cc10ee455f35d75d0e236fedd2",
"reflex/components/radix/themes/components/skeleton.pyi": "a25d3ceb56f99f736ea463579845c454",
"reflex/components/radix/themes/components/slider.pyi": "305a34c14ca8656ca9267e4c31aaa388",
"reflex/components/radix/themes/components/spinner.pyi": "b7e689e7d75635e379242fd113a1ea9a",
"reflex/components/radix/themes/components/switch.pyi": "f1ba948750a74126cda990e89a3ec7ef",
"reflex/components/radix/themes/components/table.pyi": "eefbbd1904deae3d166fcad28b20fd4a",
"reflex/components/radix/themes/components/tabs.pyi": "a533d2509a6798fe0ab7275b0152519d",
"reflex/components/radix/themes/components/text_area.pyi": "4af55e5d18a5b9d56717bf31b23ea543",
"reflex/components/radix/themes/components/text_field.pyi": "232618b744076db98d861ea1b9eb3192",
"reflex/components/radix/themes/components/tooltip.pyi": "2b8366200ce92ec4784ca3ec4152e676",
"reflex/components/radix/themes/layout/__init__.pyi": "73eefc509a49215b1797b5b5d28d035e",
"reflex/components/radix/themes/layout/base.pyi": "5be31d7dadd23ab544e53762423d123e",
"reflex/components/radix/themes/layout/box.pyi": "dbaed1c50c668805fc7b71d22f878254",
"reflex/components/radix/themes/layout/center.pyi": "17323694217e8ad7611adb683f8d96ce",
"reflex/components/radix/themes/layout/container.pyi": "24222fd7ffa2dc05f709eab6c7b9643c",
"reflex/components/radix/themes/layout/flex.pyi": "0307e9dbe6a5784140121d77c8f67a86",
"reflex/components/radix/themes/layout/grid.pyi": "95c9edb8bdd4e39dc1bd6bc2a8ca0933",
"reflex/components/radix/themes/layout/list.pyi": "049ecf827ef0ba8de2d76dbf7b1c562c",
"reflex/components/radix/themes/layout/section.pyi": "a51952b9b5c8227aa3024373dedcad5d",
"reflex/components/radix/themes/layout/spacer.pyi": "c35accf0f2f742c90a23675ff1fb960d",
"reflex/components/radix/themes/layout/stack.pyi": "271d3315c6196356d3ced759520d4e7d",
"reflex/components/radix/themes/typography/__init__.pyi": "b8ef970530397e9984004961f3aaee62",
"reflex/components/radix/themes/typography/blockquote.pyi": "080c71899532f5dbf4cf143e7a5ad3bf",
"reflex/components/radix/themes/typography/code.pyi": "7ffe785d55979cf8ff97ea040f3e2b64",
"reflex/components/radix/themes/typography/heading.pyi": "0ebb38915cd0521fd59c569e04d288bb",
"reflex/components/radix/themes/typography/link.pyi": "e88c5d880a54548b6808c097ac62505b",
"reflex/components/radix/themes/typography/text.pyi": "50f9ca15a941e4b77ddd12e77aa3c03e",
"reflex/components/react_player/audio.pyi": "0e1690ff1f1f39bc748278d292238350",
"reflex/components/react_player/react_player.pyi": "5ccd373b94ed1d3934ae6afc46bd6fe4",
"reflex/components/react_player/video.pyi": "998671c06103d797c554d9278eb3b2a0",
"reflex/components/react_router/dom.pyi": "3042fa630b7e26a7378fe045d7fbf4af",
"reflex/components/recharts/__init__.pyi": "6ee7f1ca2c0912f389ba6f3251a74d99",
"reflex/components/recharts/cartesian.pyi": "cfca4f880239ffaecdf9fb4c7c8caed5",
"reflex/components/recharts/charts.pyi": "013036b9c00ad85a570efdb813c1bc40",
"reflex/components/recharts/general.pyi": "d87ff9b85b2a204be01753690df4fb11",
"reflex/components/recharts/polar.pyi": "ad24bd37c6acc0bc9bd4ac01af3ffe49",
"reflex/components/recharts/recharts.pyi": "c41d19ab67972246c574098929bea7ea",
"reflex/components/sonner/toast.pyi": "3c27bad1aaeb5183eaa6a41e77e8d7f0"
"reflex/components/gridjs/datatable.pyi": "e1f34ade3873a931770da4a35586f298"
}
137 changes: 91 additions & 46 deletions reflex/components/gridjs/datatable.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Table components."""
"""Table components for Reflex using Gridjs."""

from __future__ import annotations

Expand All @@ -14,58 +14,52 @@


class Gridjs(NoSSRComponent):
"""A component that wraps a nivo bar component."""
"""A base component that wraps Gridjs (JS library) for tables."""

library = "gridjs-react@6.1.1"

lib_dependencies: list[str] = ["gridjs@6.2.0"]


class DataTable(Gridjs):
"""A data table component."""
"""A flexible data table component for Reflex.

tag = "Grid"
Supports:
- Pandas DataFrames
- Python lists
- Reflex Vars (state variables)
"""

tag = "Grid"
alias = "DataTableGrid"

# The data to display. Either a list of lists or a pandas dataframe.
data: Any

# The list of columns to display. Required if data is a list and should not be provided
# if the data field is a dataframe
columns: Var[Sequence]

# Enable a search bar.
search: Var[bool]

# Enable sorting on columns.
sort: Var[bool]

# Enable resizable columns.
resizable: Var[bool]

# Enable pagination.
pagination: Var[bool | dict]

# -----------------------------
# Component Props
# -----------------------------
data: Any # The data to display (list of lists or DataFrame)
columns: Var[Sequence] # Columns to display (optional if using DataFrame)
search: Var[bool] # Enable search
sort: Var[bool] # Enable sorting
resizable: Var[bool] # Enable column resizing
pagination: Var[bool | dict] # Enable pagination

# -----------------------------
# Component creation
# -----------------------------
@classmethod
def create(cls, *children, **props):
"""Create a datatable component.
"""Create a DataTable component with proper validation.

Args:
*children: The children of the component.
**props: The props to pass to the component.
Raises:
ValueError: If both DataFrame and columns are provided, or
if columns are missing for a list-type data field.

Returns:
The datatable component.

Raises:
ValueError: If a pandas dataframe is passed in and columns are also provided.
DataTable: The created DataTable component.
"""
data = props.get("data")
columns = props.get("columns")

# The annotation should be provided if data is a computed var. We need this to know how to
# render pandas dataframes.
# 1️⃣ Ensure computed Vars have type annotations
if is_computed_var(data) and data._var_type == Any:
msg = "Annotation of the computed var assigned to the data field should be provided."
raise ValueError(msg)
Expand All @@ -78,38 +72,50 @@ def create(cls, *children, **props):
msg = "Annotation of the computed var assigned to the column field should be provided."
raise ValueError(msg)

# If data is a pandas dataframe and columns are provided throw an error.
# 2️⃣ Disallow DataFrame + columns (columns auto-detected from DataFrame)
if (
types.is_dataframe(type(data))
or (isinstance(data, Var) and types.is_dataframe(data._var_type))
) and columns is not None:
msg = "Cannot pass in both a pandas dataframe and columns to the data_table component."
raise ValueError(msg)

# If data is a list and columns are not provided, throw an error
# 3️⃣ Require columns if data is a list
if (
(isinstance(data, Var) and types.typehint_issubclass(data._var_type, list))
or isinstance(data, list)
) and columns is None:
msg = "column field should be specified when the data field is a list type"
msg = "Column field should be specified when the data field is a list type"
raise ValueError(msg)

# Create the component.
return super().create(
*children,
**props,
)
# 4️⃣ Call parent create method
return super().create(*children, **props)

# -----------------------------
# Add external imports (CSS)
# -----------------------------
def add_imports(self) -> ImportDict:
"""Add the imports for the datatable component.
"""Add CSS for Gridjs.

Returns:
The import dict for the component.
ImportDict: The import dictionary required for the component.
"""
return {"": "gridjs/dist/theme/mermaid.css"}

# -----------------------------
# Render component
# -----------------------------
def _render(self) -> Tag:
"""Normalize columns and prepare data for front-end rendering.

Returns:
Tag: The rendered table component.
"""
# -----------------------------
# Case 1: DataFrame coming from State (Var)
# -----------------------------
if isinstance(self.data, Var) and types.is_dataframe(self.data._var_type):
# Convert DataFrame to front-end-safe Vars
self.columns = self.data._replace(
_js_expr=f"{self.data._js_expr}.columns",
_var_type=list[Any],
Expand All @@ -118,14 +124,53 @@ def _render(self) -> Tag:
_js_expr=f"{self.data._js_expr}.data",
_var_type=list[list[Any]],
)

# -----------------------------
# Case 2: DataFrame passed directly from Python
# -----------------------------
if types.is_dataframe(type(self.data)):
# If given a pandas df break up the data and columns
data = serialize(self.data)
if not isinstance(data, dict):
msg = "Serialized dataframe should be a dict."
raise ValueError(msg)

# Convert Python lists to LiteralVars for front-end rendering
self.columns = LiteralVar.create(data["columns"])
self.data = LiteralVar.create(data["data"])

# Render the table.
# -----------------------------
# Case 3: Normalize columns for all other scenarios
# -----------------------------
if self.columns is not None:
# Python list → LiteralVar
if isinstance(self.columns, list):
self.columns = LiteralVar.create([
{"id": col, "name": col} if isinstance(col, str) else col
for col in self.columns
])

# LiteralVar[list] → normalized LiteralVar
elif isinstance(self.columns, LiteralVar) and isinstance(
self.columns._var_value, list
):
self.columns = LiteralVar.create([
{"id": col, "name": col} if isinstance(col, str) else col
for col in self.columns._var_value
])

# Var[list] → frontend-safe JS mapping (compile-time + runtime safe)
elif isinstance(self.columns, Var):
self.columns = self.columns._replace(
_js_expr=(
f"{self.columns._js_expr}.map("
"(col) => typeof col === 'string' "
"? ({ id: col, name: col }) "
": col)"
),
_var_type=list[Any],
)

# -----------------------------
# Case 4: Render component
# -----------------------------
return super()._render()
1 change: 1 addition & 0 deletions tests/units/components/gridjs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""gridjs component tests."""
Loading