|
@@ -1,71 +1,61 @@
|
|
"""Data Editor component from glide-data-grid."""
|
|
"""Data Editor component from glide-data-grid."""
|
|
from __future__ import annotations
|
|
from __future__ import annotations
|
|
|
|
|
|
-from enum import Enum
|
|
|
|
-from typing import Any, Callable, Dict, List, Optional, Union
|
|
|
|
|
|
+from typing import Any, Callable, Dict, List, Literal, Optional, Union
|
|
|
|
|
|
from reflex.base import Base
|
|
from reflex.base import Base
|
|
from reflex.components.component import Component, NoSSRComponent
|
|
from reflex.components.component import Component, NoSSRComponent
|
|
from reflex.components.literals import LiteralRowMarker
|
|
from reflex.components.literals import LiteralRowMarker
|
|
from reflex.utils import console, format, imports, types
|
|
from reflex.utils import console, format, imports, types
|
|
-from reflex.utils.serializers import serializer
|
|
|
|
from reflex.vars import ImportVar, Var, get_unique_variable_name
|
|
from reflex.vars import ImportVar, Var, get_unique_variable_name
|
|
|
|
|
|
|
|
+LiteralDataEditorGridColumnIcons = Literal[
|
|
|
|
+ "headerRowID",
|
|
|
|
+ "headerCode",
|
|
|
|
+ "headerNumber",
|
|
|
|
+ "headerString",
|
|
|
|
+ "headerBoolean",
|
|
|
|
+ "headerAudioUri",
|
|
|
|
+ "headerVideoUri",
|
|
|
|
+ "headerEmoji",
|
|
|
|
+ "headerImage",
|
|
|
|
+ "headerUri",
|
|
|
|
+ "headerPhone",
|
|
|
|
+ "headerMarkdown",
|
|
|
|
+ "headerDate",
|
|
|
|
+ "headerTime",
|
|
|
|
+ "headerEmail",
|
|
|
|
+ "headerReference",
|
|
|
|
+ "headerIfThenElse",
|
|
|
|
+ "headerSingleValue",
|
|
|
|
+ "headerLookup",
|
|
|
|
+ "headerTextTemplate",
|
|
|
|
+ "headerMath",
|
|
|
|
+ "headerRollup",
|
|
|
|
+ "headerJoinStrings",
|
|
|
|
+ "headerSplitString",
|
|
|
|
+ "headerGeoDistance",
|
|
|
|
+ "headerArray",
|
|
|
|
+ "rowOwnerOverlay",
|
|
|
|
+ "protectedColumnOverlay",
|
|
|
|
+]
|
|
|
|
+LiteralDataEditorColumnStyle = Literal["normal", "highlight"]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class DataEditorProp(Base):
|
|
|
|
+ """Base class for Data Editor custom prop class."""
|
|
|
|
+
|
|
|
|
+ def dict(self) -> dict:
|
|
|
|
+ """Retrieve dict and format keys to camel case.
|
|
|
|
|
|
-# 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):
|
|
|
|
|
|
+ Returns:
|
|
|
|
+ Formatted dict.
|
|
|
|
+ """
|
|
|
|
+ res = super().dict()
|
|
|
|
+ return {format.to_camel_case(k): v for k, v in res.items() if v is not None}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class DataEditorTheme(DataEditorProp):
|
|
"""The theme for the DataEditor component."""
|
|
"""The theme for the DataEditor component."""
|
|
|
|
|
|
accent_color: Optional[str] = None
|
|
accent_color: Optional[str] = None
|
|
@@ -102,6 +92,33 @@ class DataEditorTheme(Base):
|
|
text_medium: Optional[str] = None
|
|
text_medium: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
+class TrailingRowOptions(DataEditorProp):
|
|
|
|
+ """Trailing Row options."""
|
|
|
|
+
|
|
|
|
+ hint: Optional[str]
|
|
|
|
+ add_icon: Optional[str]
|
|
|
|
+ target_column: Optional[int]
|
|
|
|
+ theme_override: Optional[DataEditorTheme]
|
|
|
|
+ disabled: Optional[bool]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class DataEditorColumn(DataEditorProp):
|
|
|
|
+ """Column."""
|
|
|
|
+
|
|
|
|
+ title: str
|
|
|
|
+ id: Optional[str] = None
|
|
|
|
+ type_: str = "str"
|
|
|
|
+ group: Optional[str]
|
|
|
|
+ icon: Optional[LiteralDataEditorGridColumnIcons]
|
|
|
|
+ overlay_icon: Optional[LiteralDataEditorGridColumnIcons]
|
|
|
|
+ has_menu: Optional[bool]
|
|
|
|
+ grow: Optional[int]
|
|
|
|
+ style: Optional[LiteralDataEditorColumnStyle]
|
|
|
|
+ theme_override: Optional[DataEditorTheme]
|
|
|
|
+ trailing_row_options: Optional[TrailingRowOptions]
|
|
|
|
+ grow_offset: Optional[int]
|
|
|
|
+
|
|
|
|
+
|
|
class DataEditor(NoSSRComponent):
|
|
class DataEditor(NoSSRComponent):
|
|
"""The DataEditor Component."""
|
|
"""The DataEditor Component."""
|
|
|
|
|
|
@@ -114,7 +131,7 @@ class DataEditor(NoSSRComponent):
|
|
rows: Var[int]
|
|
rows: Var[int]
|
|
|
|
|
|
# Headers of the columns for the data grid.
|
|
# Headers of the columns for the data grid.
|
|
- columns: Var[List[Dict[str, Any]]]
|
|
|
|
|
|
+ columns: Var[List[DataEditorColumn]]
|
|
|
|
|
|
# The data.
|
|
# The data.
|
|
data: Var[List[List[Any]]]
|
|
data: Var[List[List[Any]]]
|
|
@@ -307,9 +324,17 @@ class DataEditor(NoSSRComponent):
|
|
"Cannot pass in both a pandas dataframe and columns to the data_editor component."
|
|
"Cannot pass in both a pandas dataframe and columns to the data_editor component."
|
|
)
|
|
)
|
|
else:
|
|
else:
|
|
- props["columns"] = [
|
|
|
|
- format.format_data_editor_column(col) for col in columns
|
|
|
|
- ]
|
|
|
|
|
|
+ if (
|
|
|
|
+ not isinstance(columns, list)
|
|
|
|
+ or isinstance(columns, list)
|
|
|
|
+ and columns
|
|
|
|
+ and not isinstance(columns[0], dict)
|
|
|
|
+ ):
|
|
|
|
+ raise ValueError(
|
|
|
|
+ "Data Editor columns field should be a list of dictionaries"
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ props["columns"] = [DataEditorColumn(**c) for c in columns]
|
|
|
|
|
|
if "theme" in props:
|
|
if "theme" in props:
|
|
theme = props.get("theme")
|
|
theme = props.get("theme")
|
|
@@ -383,18 +408,3 @@ class DataEditor(NoSSRComponent):
|
|
|
|
|
|
# except ImportError:
|
|
# except ImportError:
|
|
# pass
|
|
# 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(
|
|
|
|
- {format.to_camel_case(k): v for k, v in theme.__dict__.items() if v is not None}
|
|
|
|
- )
|
|
|