From b10cab04cb7d6287c73253502a98550d7bff02e1 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 6 Nov 2023 12:38:08 +0000 Subject: [PATCH] Data Editor Icon header to Column --- reflex/components/datadisplay/dataeditor.py | 152 ++++++++++--------- reflex/components/datadisplay/dataeditor.pyi | 122 ++++++++++----- reflex/utils/serializers.py | 4 +- 3 files changed, 171 insertions(+), 107 deletions(-) diff --git a/reflex/components/datadisplay/dataeditor.py b/reflex/components/datadisplay/dataeditor.py index ecef03b38..edb8eb113 100644 --- a/reflex/components/datadisplay/dataeditor.py +++ b/reflex/components/datadisplay/dataeditor.py @@ -1,71 +1,61 @@ """Data Editor component from glide-data-grid.""" 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.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" +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"] -# @serializer -# def serialize_gridcolumn_icon(icon: GridColumnIcons) -> str: -# """Serialize grid column icon. +class DataEditorProp(Base): + """Base class for Data Editor custom prop class.""" -# Args: -# icon: the Icon to serialize. + def dict(self) -> dict: + """Retrieve dict and format keys to camel case. -# Returns: -# The serialized value. -# """ -# return "prefix" + str(icon) + 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 DataEditorColumn(Base): -# """Column.""" - -# title: str -# id: Optional[str] = None -# type_: str = "str" - - -class DataEditorTheme(Base): +class DataEditorTheme(DataEditorProp): """The theme for the DataEditor component.""" accent_color: Optional[str] = None @@ -102,6 +92,33 @@ class DataEditorTheme(Base): 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): """The DataEditor Component.""" @@ -114,7 +131,7 @@ class DataEditor(NoSSRComponent): rows: Var[int] # Headers of the columns for the data grid. - columns: Var[List[Dict[str, Any]]] + columns: Var[List[DataEditorColumn]] # The data. 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." ) 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: theme = props.get("theme") @@ -383,18 +408,3 @@ class DataEditor(NoSSRComponent): # 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( - {format.to_camel_case(k): v for k, v in theme.__dict__.items() if v is not None} - ) diff --git a/reflex/components/datadisplay/dataeditor.pyi b/reflex/components/datadisplay/dataeditor.pyi index cddc7ec86..38da766b2 100644 --- a/reflex/components/datadisplay/dataeditor.pyi +++ b/reflex/components/datadisplay/dataeditor.pyi @@ -16,41 +16,95 @@ 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): ... +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", +] -class DataEditorTheme(Base): - accent_color: Optional[str] - accent_fg: Optional[str] - accent_light: Optional[str] - base_font_style: Optional[str] - bg_bubble: Optional[str] - bg_bubble_selected: Optional[str] - bg_cell: Optional[str] - bg_cell_medium: Optional[str] - bg_header: Optional[str] - bg_header_has_focus: Optional[str] - bg_header_hovered: Optional[str] - bg_icon_header: Optional[str] - bg_search_result: Optional[str] - border_color: Optional[str] - cell_horizontal_padding: Optional[int] - cell_vertical_padding: Optional[int] - drilldown_border: Optional[str] - editor_font_size: Optional[str] - fg_icon_header: Optional[str] - font_family: Optional[str] - header_bottom_border_color: Optional[str] - header_font_style: Optional[str] - horizontal_border_color: Optional[str] - line_height: Optional[int] - link_color: Optional[str] - text_bubble: Optional[str] - text_dark: Optional[str] - text_group_header: Optional[str] - text_header: Optional[str] - text_header_selected: Optional[str] - text_light: Optional[str] - text_medium: Optional[str] +LiteralDataEditorColumnStyle = Literal["normal", "highlight"] + +class DataEditorProp(Base): ... + +class DataEditorTheme(DataEditorProp): + accent_color: Optional[str] = None + accent_fg: Optional[str] = None + accent_light: Optional[str] = None + base_font_style: Optional[str] = None + bg_bubble: Optional[str] = None + bg_bubble_selected: Optional[str] = None + bg_cell: Optional[str] = None + bg_cell_medium: Optional[str] = None + bg_header: Optional[str] = None + bg_header_has_focus: Optional[str] = None + bg_header_hovered: Optional[str] = None + bg_icon_header: Optional[str] = None + bg_search_result: Optional[str] = None + border_color: Optional[str] = None + cell_horizontal_padding: Optional[int] = None + cell_vertical_padding: Optional[int] = None + drilldown_border: Optional[str] = None + editor_font_size: Optional[str] = None + fg_icon_header: Optional[str] = None + font_family: Optional[str] = None + header_bottom_border_color: Optional[str] = None + header_font_style: Optional[str] = None + horizontal_border_color: Optional[str] = None + line_height: Optional[int] = None + link_color: Optional[str] = None + text_bubble: Optional[str] = None + text_dark: Optional[str] = None + text_group_header: Optional[str] = None + text_header: Optional[str] = None + text_header_selected: Optional[str] = None + text_light: Optional[str] = None + text_medium: Optional[str] = None + +class TrailingRowOptions(DataEditorProp): + hint: Optional[str] + add_icon: Optional[str] + target_column: Optional[int] + theme_override: Optional[DataEditorTheme] + disabled: Optional[bool] + +class DataEditorColumn(DataEditorProp): + 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): def get_event_triggers(self) -> Dict[str, Callable]: ... diff --git a/reflex/utils/serializers.py b/reflex/utils/serializers.py index 665aa10d3..40b5c8ffa 100644 --- a/reflex/utils/serializers.py +++ b/reflex/utils/serializers.py @@ -153,7 +153,7 @@ def serialize_primitive(value: Union[bool, int, float, None]) -> str: @serializer -def serialize_base(value: Base) -> str: +def serialize_base(value: Base) -> dict: """Serialize a Base instance. Args: @@ -162,7 +162,7 @@ def serialize_base(value: Base) -> str: Returns: The serialized Base. """ - return value.json() + return value.dict() @serializer