Add datagrid editor (#1941)
This commit is contained in:
parent
21dbdc0103
commit
9a5579e1ef
56
reflex/.templates/web/utils/helpers/dataeditor.js
Normal file
56
reflex/.templates/web/utils/helpers/dataeditor.js
Normal file
@ -0,0 +1,56 @@
|
||||
import { GridCellKind } from "@glideapps/glide-data-grid"
|
||||
|
||||
export function getDEColumn(columns, col) {
|
||||
let c = columns[col];
|
||||
c.pos = col;
|
||||
return c;
|
||||
}
|
||||
|
||||
export function getDERow(data, row) {
|
||||
return data[row];
|
||||
}
|
||||
|
||||
export function locateCell(row, column) {
|
||||
if (Array.isArray(row)) {
|
||||
return row[column.pos];
|
||||
} else {
|
||||
return row[column.id];
|
||||
}
|
||||
}
|
||||
|
||||
export function formatCell(value, column) {
|
||||
switch (column.type) {
|
||||
case "int":
|
||||
case "float":
|
||||
return {
|
||||
kind: GridCellKind.Number,
|
||||
data: value,
|
||||
displayData: value + "",
|
||||
readonly: false,
|
||||
allowOverlay: false
|
||||
}
|
||||
case "datetime":
|
||||
// value = moment format?
|
||||
case "str":
|
||||
return {
|
||||
kind: GridCellKind.Text,
|
||||
data: value,
|
||||
displayData: value,
|
||||
readonly: false,
|
||||
allowOverlay: true
|
||||
}
|
||||
case "bool":
|
||||
return {
|
||||
kind: GridCellKind.Boolean,
|
||||
data: value,
|
||||
readonly: false,
|
||||
// allowOverlay: true
|
||||
}
|
||||
default:
|
||||
return {
|
||||
kind: GridCellKind.Text,
|
||||
data: value,
|
||||
displayData: "type not specified in column definition"
|
||||
}
|
||||
};
|
||||
};
|
@ -32,6 +32,7 @@ code = Code.create
|
||||
code_block = CodeBlock.create
|
||||
connection_banner = ConnectionBanner.create
|
||||
connection_modal = ConnectionModal.create
|
||||
data_editor = DataEditor.create
|
||||
data_table = DataTable.create
|
||||
divider = Divider.create
|
||||
list = List.create
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
from .badge import Badge
|
||||
from .code import Code, CodeBlock
|
||||
from .dataeditor import DataEditor
|
||||
from .datatable import DataTable
|
||||
from .divider import Divider
|
||||
from .keyboard_key import KeyboardKey
|
||||
|
408
reflex/components/datadisplay/dataeditor.py
Normal file
408
reflex/components/datadisplay/dataeditor.py
Normal file
@ -0,0 +1,408 @@
|
||||
"""Data Editor component from glide-data-grid."""
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component, NoSSRComponent
|
||||
from reflex.components.literals import LiteralRowMarker
|
||||
from reflex.utils import console, format, imports, types
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import ImportVar, Var, get_unique_variable_name
|
||||
|
||||
|
||||
# TODO: Fix the serialization issue for custom types.
|
||||
class GridColumnIcons(Enum):
|
||||
"""An Enum for the available icons in DataEditor."""
|
||||
|
||||
Array = "array"
|
||||
AudioUri = "audio_uri"
|
||||
Boolean = "boolean"
|
||||
HeaderCode = "code"
|
||||
Date = "date"
|
||||
Email = "email"
|
||||
Emoji = "emoji"
|
||||
GeoDistance = "geo_distance"
|
||||
IfThenElse = "if_then_else"
|
||||
Image = "image"
|
||||
JoinStrings = "join_strings"
|
||||
Lookup = "lookup"
|
||||
Markdown = "markdown"
|
||||
Math = "math"
|
||||
Number = "number"
|
||||
Phone = "phone"
|
||||
Reference = "reference"
|
||||
Rollup = "rollup"
|
||||
RowID = "row_id"
|
||||
SingleValue = "single_value"
|
||||
SplitString = "split_string"
|
||||
String = "string"
|
||||
TextTemplate = "text_template"
|
||||
Time = "time"
|
||||
Uri = "uri"
|
||||
VideoUri = "video_uri"
|
||||
|
||||
|
||||
# @serializer
|
||||
# def serialize_gridcolumn_icon(icon: GridColumnIcons) -> str:
|
||||
# """Serialize grid column icon.
|
||||
|
||||
# Args:
|
||||
# icon: the Icon to serialize.
|
||||
|
||||
# Returns:
|
||||
# The serialized value.
|
||||
# """
|
||||
# return "prefix" + str(icon)
|
||||
|
||||
|
||||
# class DataEditorColumn(Base):
|
||||
# """Column."""
|
||||
|
||||
# title: str
|
||||
# id: Optional[str] = None
|
||||
# type_: str = "str"
|
||||
|
||||
|
||||
class DataEditorTheme(Base):
|
||||
"""The theme for the DataEditor component."""
|
||||
|
||||
accentColor: Optional[str] = None
|
||||
accentFg: Optional[str] = None
|
||||
accentLight: Optional[str] = None
|
||||
baseFontStyle: Optional[str] = None
|
||||
bgBubble: Optional[str] = None
|
||||
bgBubbleSelected: Optional[str] = None
|
||||
bgCell: Optional[str] = None
|
||||
bgCellMedium: Optional[str] = None
|
||||
bgHeader: Optional[str] = None
|
||||
bgHeaderHasFocus: Optional[str] = None
|
||||
bgHeaderHovered: Optional[str] = None
|
||||
bgIconHeader: Optional[str] = None
|
||||
bgSearchResult: Optional[str] = None
|
||||
borderColor: Optional[str] = None
|
||||
cellHorizontalPadding: Optional[int] = None
|
||||
cellVerticalPadding: Optional[int] = None
|
||||
drilldownBorder: Optional[str] = None
|
||||
editorFontSize: Optional[str] = None
|
||||
fgIconHeader: Optional[str] = None
|
||||
fontFamily: Optional[str] = None
|
||||
headerBottomBorderColor: Optional[str] = None
|
||||
headerFontStyle: Optional[str] = None
|
||||
horizontalBorderColor: Optional[str] = None
|
||||
lineHeight: Optional[int] = None
|
||||
linkColor: Optional[str] = None
|
||||
textBubble: Optional[str] = None
|
||||
textDark: Optional[str] = None
|
||||
textGroupHeader: Optional[str] = None
|
||||
textHeader: Optional[str] = None
|
||||
textHeaderSelected: Optional[str] = None
|
||||
textLight: Optional[str] = None
|
||||
textMedium: Optional[str] = None
|
||||
|
||||
|
||||
class DataEditor(NoSSRComponent):
|
||||
"""The DataEditor Component."""
|
||||
|
||||
tag = "DataEditor"
|
||||
is_default = True
|
||||
library: str = "@glideapps/glide-data-grid@^5.3.0"
|
||||
lib_dependencies: List[str] = ["lodash", "marked", "react-responsive-carousel"]
|
||||
|
||||
# Number of rows.
|
||||
rows: Var[int]
|
||||
|
||||
# Headers of the columns for the data grid.
|
||||
columns: Var[List[Dict[str, Any]]]
|
||||
|
||||
# The data.
|
||||
data: Var[List[List[Any]]]
|
||||
|
||||
# The name of the callback used to find the data to display.
|
||||
get_cell_content: Var[str]
|
||||
|
||||
# Allow selection for copying.
|
||||
get_cell_for_selection: Var[bool]
|
||||
|
||||
# Allow paste.
|
||||
on_paste: Var[bool]
|
||||
|
||||
# Controls the drawing of the focus ring.
|
||||
draw_focus_ring: Var[bool]
|
||||
|
||||
# Enables or disables the overlay shadow when scrolling horizontally.
|
||||
fixed_shadow_x: Var[bool]
|
||||
|
||||
# Enables or disables the overlay shadow when scrolling vertically.
|
||||
fixed_shadow_y: Var[bool]
|
||||
|
||||
# The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers.
|
||||
freeze_columns: Var[int]
|
||||
|
||||
# Controls the header of the group header row.
|
||||
group_header_height: Var[int]
|
||||
|
||||
# Controls the height of the header row.
|
||||
header_height: Var[int]
|
||||
|
||||
# Additional header icons:
|
||||
# header_icons: Var[Any] # (TODO: must be a map of name: svg)
|
||||
|
||||
# The maximum width a column can be automatically sized to.
|
||||
max_column_auto_width: Var[int]
|
||||
|
||||
# The maximum width a column can be resized to.
|
||||
max_column_width: Var[int]
|
||||
|
||||
# The minimum width a column can be resized to.
|
||||
min_column_width: Var[int]
|
||||
|
||||
# Determins the height of each row.
|
||||
row_height: Var[int]
|
||||
|
||||
# Kind of row markers.
|
||||
row_markers: Var[LiteralRowMarker]
|
||||
|
||||
# Changes the starting index for row markers.
|
||||
row_marker_start_index: Var[int]
|
||||
|
||||
# Sets the width of row markers in pixels, if unset row markers will automatically size.
|
||||
row_marker_width: Var[int]
|
||||
|
||||
# Enable horizontal smooth scrolling.
|
||||
smooth_scroll_x: Var[bool]
|
||||
|
||||
# Enable vertical smooth scrolling.
|
||||
smooth_scroll_y: Var[bool]
|
||||
|
||||
# Controls the drawing of the left hand vertical border of a column. If set to a boolean value it controls all borders.
|
||||
vertical_border: Var[bool] # TODO: support a mapping (dict[int, bool])
|
||||
|
||||
# Allow columns selections. ("none", "single", "multiple")
|
||||
column_select: Var[str]
|
||||
|
||||
# Prevent diagonal scrolling.
|
||||
prevent_diagonal_scrolling: Var[bool]
|
||||
|
||||
# Allow to scroll past the limit of the actual content on the horizontal axis.
|
||||
overscroll_x: Var[int]
|
||||
|
||||
# Allow to scroll past the limit of the actual content on the vertical axis.
|
||||
overscroll_y: Var[int]
|
||||
|
||||
# Initial scroll offset on the horizontal axis.
|
||||
scroll_offset_x: Var[int]
|
||||
|
||||
# Initial scroll offset on the vertical axis.
|
||||
scroll_offset_y: Var[int]
|
||||
|
||||
# global theme
|
||||
theme: Var[DataEditorTheme]
|
||||
|
||||
def _get_imports(self):
|
||||
return imports.merge_imports(
|
||||
super()._get_imports(),
|
||||
{
|
||||
"": {
|
||||
ImportVar(
|
||||
tag=f"{format.format_library_name(self.library)}/dist/index.css"
|
||||
)
|
||||
},
|
||||
self.library: {ImportVar(tag="GridCellKind")},
|
||||
"/utils/helpers/dataeditor.js": {
|
||||
ImportVar(tag=f"getDEColumn", is_default=False, install=False),
|
||||
ImportVar(tag=f"getDERow", is_default=False, install=False),
|
||||
ImportVar(tag=f"locateCell", is_default=False, install=False),
|
||||
ImportVar(tag=f"formatCell", is_default=False, install=False),
|
||||
ImportVar(tag=f"onEditCell", is_default=False, install=False),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def get_event_triggers(self) -> Dict[str, Callable]:
|
||||
"""The event triggers of the component.
|
||||
|
||||
Returns:
|
||||
The dict describing the event triggers.
|
||||
"""
|
||||
|
||||
def edit_sig(pos, data: dict[str, Any]):
|
||||
return [pos, data]
|
||||
|
||||
return {
|
||||
"on_cell_activated": lambda pos: [pos],
|
||||
"on_cell_clicked": lambda pos: [pos],
|
||||
"on_cell_context_menu": lambda pos: [pos],
|
||||
"on_cell_edited": edit_sig,
|
||||
"on_group_header_clicked": edit_sig,
|
||||
"on_group_header_context_menu": lambda grp_idx, data: [grp_idx, data],
|
||||
"on_group_header_renamed": lambda idx, val: [idx, val],
|
||||
"on_header_clicked": lambda pos: [pos],
|
||||
"on_header_context_menu": lambda pos: [pos],
|
||||
"on_header_menu_click": lambda col, pos: [col, pos],
|
||||
"on_item_hovered": lambda pos: [pos],
|
||||
"on_delete": lambda selection: [selection],
|
||||
"on_finished_editing": lambda new_value, movement: [new_value, movement],
|
||||
"on_row_appended": lambda: [],
|
||||
"on_selection_cleared": lambda: [],
|
||||
}
|
||||
|
||||
def _get_hooks(self) -> str | None:
|
||||
# Define the id of the component in case multiple are used in the same page.
|
||||
editor_id = get_unique_variable_name()
|
||||
|
||||
# Define the name of the getData callback associated with this component and assign to get_cell_content.
|
||||
data_callback = f"getData_{editor_id}"
|
||||
self.get_cell_content = Var.create(data_callback, _var_is_local=False) # type: ignore
|
||||
|
||||
code = [f"function {data_callback}([col, row])" "{"]
|
||||
|
||||
code.extend(
|
||||
[
|
||||
f" if (row < {self.data._var_full_name}.length && col < {self.columns._var_full_name}.length)"
|
||||
" {",
|
||||
f" const column = getDEColumn({self.columns._var_full_name}, col);",
|
||||
f" const rowData = getDERow({self.data._var_full_name}, row);",
|
||||
f" const cellData = locateCell(rowData, column);",
|
||||
" return formatCell(cellData, column);",
|
||||
" }",
|
||||
" return { kind: GridCellKind.Loading};",
|
||||
]
|
||||
)
|
||||
|
||||
code.append("}")
|
||||
|
||||
return "\n".join(code)
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props) -> Component:
|
||||
"""Create the DataEditor component.
|
||||
|
||||
Args:
|
||||
*children: The children of the data editor.
|
||||
**props: The props of the data editor.
|
||||
|
||||
Raises:
|
||||
ValueError: invalid input.
|
||||
|
||||
Returns:
|
||||
The DataEditor component.&
|
||||
"""
|
||||
from reflex.el.elements import Div
|
||||
|
||||
columns = props.get("columns", [])
|
||||
data = props.get("data", [])
|
||||
rows = props.get("rows", None)
|
||||
|
||||
# If rows is not provided, determine from data.
|
||||
if rows is None:
|
||||
props["rows"] = (
|
||||
data.length() # BaseVar.create(value=f"{data}.length()", is_local=False)
|
||||
if isinstance(data, Var)
|
||||
else len(data)
|
||||
)
|
||||
|
||||
if not isinstance(columns, Var) and len(columns):
|
||||
if (
|
||||
types.is_dataframe(type(data))
|
||||
or isinstance(data, Var)
|
||||
and types.is_dataframe(data._var_type)
|
||||
):
|
||||
raise ValueError(
|
||||
"Cannot pass in both a pandas dataframe and columns to the data_editor component."
|
||||
)
|
||||
else:
|
||||
props["columns"] = [
|
||||
format.format_data_editor_column(col) for col in columns
|
||||
]
|
||||
|
||||
# Allow by default to select a region of cells in the grid.
|
||||
props.setdefault("getCellForSelection", True)
|
||||
|
||||
# Disable on_paste by default if not provided.
|
||||
props.setdefault("onPaste", False)
|
||||
|
||||
if props.pop("getCellContent", None) is not None:
|
||||
console.warn(
|
||||
"getCellContent is not user configurable, the provided value will be discarded"
|
||||
)
|
||||
grid = super().create(*children, **props)
|
||||
return Div.create(
|
||||
grid,
|
||||
Div.create(id="portal"),
|
||||
width=props.pop("width", "100%"),
|
||||
height=props.pop("height", "100%"),
|
||||
)
|
||||
|
||||
# def _render(self) -> Tag:
|
||||
# if isinstance(self.data, Var) and types.is_dataframe(self.data.type_):
|
||||
# self.columns = BaseVar(
|
||||
# name=f"{self.data.name}.columns",
|
||||
# type_=List[Any],
|
||||
# state=self.data.state,
|
||||
# )
|
||||
# self.data = BaseVar(
|
||||
# name=f"{self.data.name}.data",
|
||||
# type_=List[List[Any]],
|
||||
# state=self.data.state,
|
||||
# )
|
||||
# if types.is_dataframe(type(self.data)):
|
||||
# # If given a pandas df break up the data and columns
|
||||
# data = serialize(self.data)
|
||||
# assert isinstance(data, dict), "Serialized dataframe should be a dict."
|
||||
# self.columns = Var.create_safe(data["columns"])
|
||||
# self.data = Var.create_safe(data["data"])
|
||||
|
||||
# # Render the table.
|
||||
# return super()._render()
|
||||
|
||||
|
||||
# try:
|
||||
# pass
|
||||
|
||||
# # def format_dataframe_values(df: DataFrame) -> list[list[Any]]:
|
||||
# # """Format dataframe values to a list of lists.
|
||||
|
||||
# # Args:
|
||||
# # df: The dataframe to format.
|
||||
|
||||
# # Returns:
|
||||
# # The dataframe as a list of lists.
|
||||
# # """
|
||||
# # return [
|
||||
# # [str(d) if isinstance(d, (list, tuple)) else d for d in data]
|
||||
# # for data in list(df.values.tolist())
|
||||
# # ]
|
||||
# # ...
|
||||
|
||||
# # @serializer
|
||||
# # def serialize_dataframe(df: DataFrame) -> dict:
|
||||
# # """Serialize a pandas dataframe.
|
||||
|
||||
# # Args:
|
||||
# # df: The dataframe to serialize.
|
||||
|
||||
# # Returns:
|
||||
# # The serialized dataframe.
|
||||
# # """
|
||||
# # return {
|
||||
# # "columns": df.columns.tolist(),
|
||||
# # "data": format_dataframe_values(df),
|
||||
# # }
|
||||
|
||||
# except ImportError:
|
||||
# pass
|
||||
|
||||
|
||||
@serializer
|
||||
def serialize_dataeditortheme(theme: DataEditorTheme):
|
||||
"""The serializer for the data editor theme.
|
||||
|
||||
Args:
|
||||
theme: The theme to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized theme.
|
||||
"""
|
||||
return format.json_dumps({k: v for k, v in theme.__dict__.items() if v is not None})
|
201
reflex/components/datadisplay/dataeditor.pyi
Normal file
201
reflex/components/datadisplay/dataeditor.pyi
Normal file
@ -0,0 +1,201 @@
|
||||
"""Stub file for reflex/components/datadisplay/dataeditor.py"""
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `scripts/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
|
||||
from typing import Any, Dict, Optional, overload, Literal, Union, List
|
||||
from reflex.vars import Var, BaseVar, ComputedVar
|
||||
from reflex.event import EventChain, EventHandler, EventSpec
|
||||
from reflex.style import Style
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component, NoSSRComponent
|
||||
from reflex.components.literals import LiteralRowMarker
|
||||
from reflex.utils import console, format, imports, types
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import ImportVar, Var, get_unique_variable_name
|
||||
|
||||
class GridColumnIcons(Enum): ...
|
||||
|
||||
class DataEditorTheme(Base):
|
||||
accentColor: Optional[str]
|
||||
accentFg: Optional[str]
|
||||
accentLight: Optional[str]
|
||||
baseFontStyle: Optional[str]
|
||||
bgBubble: Optional[str]
|
||||
bgBubbleSelected: Optional[str]
|
||||
bgCell: Optional[str]
|
||||
bgCellMedium: Optional[str]
|
||||
bgHeader: Optional[str]
|
||||
bgHeaderHasFocus: Optional[str]
|
||||
bgHeaderHovered: Optional[str]
|
||||
bgIconHeader: Optional[str]
|
||||
bgSearchResult: Optional[str]
|
||||
borderColor: Optional[str]
|
||||
cellHorizontalPadding: Optional[int]
|
||||
cellVerticalPadding: Optional[int]
|
||||
drilldownBorder: Optional[str]
|
||||
editorFontSize: Optional[str]
|
||||
fgIconHeader: Optional[str]
|
||||
fontFamily: Optional[str]
|
||||
headerBottomBorderColor: Optional[str]
|
||||
headerFontStyle: Optional[str]
|
||||
horizontalBorderColor: Optional[str]
|
||||
lineHeight: Optional[int]
|
||||
linkColor: Optional[str]
|
||||
textBubble: Optional[str]
|
||||
textDark: Optional[str]
|
||||
textGroupHeader: Optional[str]
|
||||
textHeader: Optional[str]
|
||||
textHeaderSelected: Optional[str]
|
||||
textLight: Optional[str]
|
||||
textMedium: Optional[str]
|
||||
|
||||
class DataEditor(NoSSRComponent):
|
||||
def get_event_triggers(self) -> Dict[str, Callable]: ...
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
rows: Optional[Union[Var[int], int]] = None,
|
||||
columns: Optional[
|
||||
Union[Var[List[Dict[str, Any]]], List[Dict[str, Any]]]
|
||||
] = None,
|
||||
data: Optional[Union[Var[List[List[Any]]], List[List[Any]]]] = None,
|
||||
get_cell_content: Optional[Union[Var[str], str]] = None,
|
||||
get_cell_for_selection: Optional[Union[Var[bool], bool]] = None,
|
||||
on_paste: Optional[Union[Var[bool], bool]] = None,
|
||||
draw_focus_ring: Optional[Union[Var[bool], bool]] = None,
|
||||
fixed_shadow_x: Optional[Union[Var[bool], bool]] = None,
|
||||
fixed_shadow_y: Optional[Union[Var[bool], bool]] = None,
|
||||
freeze_columns: Optional[Union[Var[int], int]] = None,
|
||||
group_header_height: Optional[Union[Var[int], int]] = None,
|
||||
header_height: Optional[Union[Var[int], int]] = None,
|
||||
max_column_auto_width: Optional[Union[Var[int], int]] = None,
|
||||
max_column_width: Optional[Union[Var[int], int]] = None,
|
||||
min_column_width: Optional[Union[Var[int], int]] = None,
|
||||
row_height: Optional[Union[Var[int], int]] = None,
|
||||
row_markers: Optional[
|
||||
Union[
|
||||
Var[Literal["none", "number", "checkbox", "both", "clickable-number"]],
|
||||
Literal["none", "number", "checkbox", "both", "clickable-number"],
|
||||
]
|
||||
] = None,
|
||||
row_marker_start_index: Optional[Union[Var[int], int]] = None,
|
||||
row_marker_width: Optional[Union[Var[int], int]] = None,
|
||||
smooth_scroll_x: Optional[Union[Var[bool], bool]] = None,
|
||||
smooth_scroll_y: Optional[Union[Var[bool], bool]] = None,
|
||||
vertical_border: Optional[Union[Var[bool], bool]] = None,
|
||||
column_select: Optional[Union[Var[str], str]] = None,
|
||||
prevent_diagonal_scrolling: Optional[Union[Var[bool], bool]] = None,
|
||||
overscroll_x: Optional[Union[Var[int], int]] = None,
|
||||
overscroll_y: Optional[Union[Var[int], int]] = None,
|
||||
scroll_offset_x: Optional[Union[Var[int], int]] = None,
|
||||
scroll_offset_y: Optional[Union[Var[int], int]] = None,
|
||||
theme: Optional[Union[Var[DataEditorTheme], DataEditorTheme]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, str]] = None,
|
||||
on_cell_activated: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_cell_clicked: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_cell_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_cell_edited: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_delete: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_finished_editing: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_group_header_clicked: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_group_header_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_group_header_renamed: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_header_clicked: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_header_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_header_menu_click: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_item_hovered: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_row_appended: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
on_selection_cleared: Optional[
|
||||
Union[EventHandler, EventSpec, List, function, BaseVar]
|
||||
] = None,
|
||||
**props
|
||||
) -> "DataEditor":
|
||||
"""Create the DataEditor component.
|
||||
|
||||
Args:
|
||||
*children: The children of the data editor.
|
||||
rows: Number of rows.
|
||||
columns: Headers of the columns for the data grid.
|
||||
data: The data.
|
||||
get_cell_content: The name of the callback used to find the data to display.
|
||||
get_cell_for_selection: Allow selection for copying.
|
||||
on_paste: Allow paste.
|
||||
draw_focus_ring: Controls the drawing of the focus ring.
|
||||
fixed_shadow_x: Enables or disables the overlay shadow when scrolling horizontally.
|
||||
fixed_shadow_y: Enables or disables the overlay shadow when scrolling vertically.
|
||||
freeze_columns: The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers.
|
||||
group_header_height: Controls the header of the group header row.
|
||||
header_height: Controls the height of the header row.
|
||||
max_column_auto_width: Additional header icons: header_icons: Var[Any] # (TODO: must be a map of name: svg) The maximum width a column can be automatically sized to.
|
||||
max_column_width: The maximum width a column can be resized to.
|
||||
min_column_width: The minimum width a column can be resized to.
|
||||
row_height: Determins the height of each row.
|
||||
row_markers: Kind of row markers.
|
||||
row_marker_start_index: Changes the starting index for row markers.
|
||||
row_marker_width: Sets the width of row markers in pixels, if unset row markers will automatically size.
|
||||
smooth_scroll_x: Enable horizontal smooth scrolling.
|
||||
smooth_scroll_y: Enable vertical smooth scrolling.
|
||||
vertical_border: Controls the drawing of the left hand vertical border of a column. If set to a boolean value it controls all borders.
|
||||
column_select: Allow columns selections. ("none", "single", "multiple")
|
||||
prevent_diagonal_scrolling: Prevent diagonal scrolling.
|
||||
overscroll_x: Allow to scroll past the limit of the actual content on the horizontal axis.
|
||||
overscroll_y: Allow to scroll past the limit of the actual content on the vertical axis.
|
||||
scroll_offset_x: Initial scroll offset on the horizontal axis.
|
||||
scroll_offset_y: Initial scroll offset on the vertical axis.
|
||||
theme: global theme
|
||||
style: The style of the component.
|
||||
key: A unique key for the component.
|
||||
id: The id for the component.
|
||||
class_name: The class name for the component.
|
||||
autofocus: Whether the component should take the focus once the page is loaded
|
||||
custom_attrs: custom attribute
|
||||
**props: The props of the data editor.
|
||||
|
||||
Raises:
|
||||
ValueError: invalid input.
|
||||
|
||||
Returns:
|
||||
The DataEditor component.&
|
||||
"""
|
||||
...
|
||||
|
||||
@serializer
|
||||
def serialize_dataeditortheme(theme: DataEditorTheme): ...
|
5
reflex/components/literals.py
Normal file
5
reflex/components/literals.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""Literal custom type used by Reflex."""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
LiteralRowMarker = Literal["none", "number", "checkbox", "both", "clickable-number"]
|
@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any, Union
|
||||
from reflex import constants
|
||||
from reflex.utils import exceptions, serializers, types
|
||||
from reflex.utils.serializers import serialize
|
||||
from reflex.vars import Var
|
||||
from reflex.vars import BaseVar, Var
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.components.component import ComponentStyle
|
||||
@ -625,3 +625,47 @@ def unwrap_vars(value: str) -> str:
|
||||
string=value,
|
||||
flags=re.VERBOSE,
|
||||
)
|
||||
|
||||
|
||||
def format_data_editor_column(col: str | dict):
|
||||
"""Format a given column into the proper format.
|
||||
|
||||
Args:
|
||||
col: The column.
|
||||
|
||||
Raises:
|
||||
ValueError: invalid type provided for column.
|
||||
|
||||
Returns:
|
||||
The formatted column.
|
||||
"""
|
||||
if isinstance(col, str):
|
||||
return {"title": col, "id": col.lower(), "type": "str"}
|
||||
|
||||
if isinstance(col, (dict,)):
|
||||
if "id" not in col:
|
||||
col["id"] = col["title"].lower()
|
||||
if "type" not in col:
|
||||
col["type"] = "str"
|
||||
if "overlayIcon" not in col:
|
||||
col["overlayIcon"] = None
|
||||
return col
|
||||
|
||||
if isinstance(col, BaseVar):
|
||||
return col
|
||||
|
||||
raise ValueError(
|
||||
f"unexpected type ({(type(col).__name__)}: {col}) for column header in data_editor"
|
||||
)
|
||||
|
||||
|
||||
def format_data_editor_cell(cell: Any):
|
||||
"""Format a given data into a renderable cell for data_editor.
|
||||
|
||||
Args:
|
||||
cell: The data to format.
|
||||
|
||||
Returns:
|
||||
The formatted cell.
|
||||
"""
|
||||
return {"kind": Var.create(value="GridCellKind.Text"), "data": cell}
|
||||
|
@ -113,6 +113,19 @@ def has_serializer(type_: Type) -> bool:
|
||||
return get_serializer(type_) is not None
|
||||
|
||||
|
||||
@serializer
|
||||
def serialize_type(value: type) -> str:
|
||||
"""Serialize a python type.
|
||||
|
||||
Args:
|
||||
value: the type to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized type.
|
||||
"""
|
||||
return value.__name__
|
||||
|
||||
|
||||
@serializer
|
||||
def serialize_str(value: str) -> str:
|
||||
"""Serialize a string.
|
||||
|
@ -30,6 +30,7 @@ EXCLUDED_FILES = [
|
||||
"foreach.py",
|
||||
"cond.py",
|
||||
"multiselect.py",
|
||||
"literals.py",
|
||||
]
|
||||
|
||||
# These props exist on the base component, but should not be exposed in create methods.
|
||||
|
Loading…
Reference in New Issue
Block a user