Skip to content
2 changes: 1 addition & 1 deletion pyi_hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"reflex/components/core/window_events.pyi": "af33ccec866b9540ee7fbec6dbfbd151",
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
"reflex/components/datadisplay/code.pyi": "b86769987ef4d1cbdddb461be88539fd",
"reflex/components/datadisplay/dataeditor.pyi": "35391d4ba147cf20ce4ac7a782066d61",
"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",
Expand Down
85 changes: 84 additions & 1 deletion reflex/components/datadisplay/dataeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from reflex.utils.serializers import serializer
from reflex.vars import get_unique_variable_name
from reflex.vars.base import Var
from reflex.vars.function import FunctionStringVar
from reflex.vars.sequence import ArrayVar


Expand Down Expand Up @@ -260,6 +261,12 @@ class DataEditor(NoSSRComponent):
# Allow columns selections. ("none", "single", "multi")
column_select: Var[Literal["none", "single", "multi"]]

# Allow range selections. ("none", "cell", "rect", "multi-cell", "multi-rect").
range_select: Var[Literal["none", "cell", "rect", "multi-cell", "multi-rect"]]

# Allow row selections. ("none", "single", "multi").
row_select: Var[Literal["none", "single", "multi"]]

# Prevent diagonal scrolling.
prevent_diagonal_scrolling: Var[bool]

Expand All @@ -275,6 +282,18 @@ class DataEditor(NoSSRComponent):
# Initial scroll offset on the vertical axis.
scroll_offset_y: Var[int]

# Controls which types of range selections can exist at the same time. ("exclusive", "mixed").
range_selection_blending: Var[Literal["exclusive", "mixed"]]

# Controls which types of column selections can exist at the same time. ("exclusive", "mixed").
column_selection_blending: Var[Literal["exclusive", "mixed"]]

# Controls which types of row selections can exist at the same time. ("exclusive", "mixed").
row_selection_blending: Var[Literal["exclusive", "mixed"]]

# Controls how spans are handled in selections. ("default", "allowPartial").
span_range_behavior: Var[Literal["default", "allowPartial"]]

# global theme
theme: Var[DataEditorTheme | dict]

Expand Down Expand Up @@ -326,6 +345,12 @@ class DataEditor(NoSSRComponent):
# Fired when a row is appended.
on_row_appended: EventHandler[no_args_event_spec]

# The current grid selection state (columns, rows, and current cell/range). Must be used when on_grid_selection_change is used otherwise updates will not be reflected in the grid.
grid_selection: Var[GridSelection]

# Fired when the grid selection changes. Will pass the current selection, the selected columns and the selected rows.
on_grid_selection_change: EventHandler[passthrough_event_spec(GridSelection)]

# Fired when the selection is cleared.
on_selection_cleared: EventHandler[no_args_event_spec]

Expand All @@ -342,12 +367,61 @@ def add_imports(self) -> ImportDict:
return {}
return {
"": f"{format.format_library_name(self.library)}/dist/index.css",
self.library: "GridCellKind",
self.library: ["GridCellKind", "CompactSelection"],
"$/utils/helpers/dataeditor.js": ImportVar(
tag="formatDataEditorCells", is_default=False, install=False
),
}

def add_custom_code(self) -> list[str]:
"""Add custom code for reconstructing GridSelection with CompactSelection objects.

Note: When using on_grid_selection_change, Glide Data Grid will not update its internal selection state automatically. Instead,
the grid_selection prop must be updated with a GridSelection object that has CompactSelection objects for the columns and rows properties.
This function provides the necessary JavaScript code to reconstruct the GridSelection object from a dict representation.

Returns:
JavaScript code to reconstruct GridSelection.
"""
return [
"""
function reconstructGridSelection(selection) {
if (!selection || typeof selection !== 'object') {
return undefined;
}

const reconstructCompactSelection = (data) => {
if (!data || !data.items || !Array.isArray(data.items)) {
return CompactSelection.empty();
}

const items = data.items;
if (items.length === 0) {
return CompactSelection.empty();
}

let result = CompactSelection.empty();

// Items are stored as [start, end) ranges in CompactSelection internal format
for (const item of items) {
if (Array.isArray(item) && item.length === 2) {
const [start, end] = item;
result = result.add([start, end]);
}
}

return result;
};

return {
current: selection.current || undefined,
columns: reconstructCompactSelection(selection.columns),
rows: reconstructCompactSelection(selection.rows)
};
}
"""
]

def add_hooks(self) -> list[str]:
"""Get the hooks to render.

Expand Down Expand Up @@ -429,6 +503,15 @@ def create(cls, *children, **props) -> Component:
console.warn(
"get_cell_content is not user configurable, the provided value will be discarded"
)

# Apply the reconstruction function to grid_selection if it's a Var
if (grid_selection := props.get("grid_selection")) is not None and isinstance(
grid_selection, Var
):
props["grid_selection"] = FunctionStringVar.create(
"reconstructGridSelection"
).call(grid_selection)

grid = super().create(*children, **props)
return Div.create(
grid,
Expand Down
Loading