From 536351cc8de7f9cee9d7835e8fec56991310fed6 Mon Sep 17 00:00:00 2001 From: Richard To Date: Fri, 24 May 2024 06:51:14 -0700 Subject: [PATCH] Add some new style properties to the Style class (#300) New properties - visibility - opacity - top / left / right / bottom - Add remaining grid properties: - grid_auto_columns - grid_auto_flow - grid_auto_rows - grid_column - grid_row - Exception: left out grid_template property Also update animations using opacity and position properties Also some minor style property fixes - Allow string values for: - grid_column_start - grid_column_end - grid_row_start - grid_row_end - Fix potential bug with direct str conversions - If the value is not set, setting str(property) would yield 'None' - That seems undesirable, so add _int_str and _float_str wrappers --- demo/basic_animation.py | 75 +++++++++++++---------------- mesop/component_helpers/style.py | 79 +++++++++++++++++++++++++++---- mesop/editor/component_configs.py | 25 +++++++--- mesop/protos/ui.proto | 21 ++++++-- mesop/web/src/utils/styles.ts | 33 +++++++++++++ 5 files changed, 169 insertions(+), 64 deletions(-) diff --git a/demo/basic_animation.py b/demo/basic_animation.py index 795e2eab..418154cc 100644 --- a/demo/basic_animation.py +++ b/demo/basic_animation.py @@ -1,14 +1,15 @@ import time +from dataclasses import field import mesop as me @me.stateclass class State: - ex1_rgba: list[int] - ex2_rgba: list[int | float] + ex1_rgba: list[int] = field(default_factory=lambda: [255, 0, 0, 1]) + ex2_opacity: float = 1.0 ex3_width: int - ex4_margin: int + ex4_left: int DEFAULT_MARGIN = me.Style(margin=me.Margin.all(30)) @@ -19,11 +20,6 @@ class State: def app(): state = me.state(State) - # Initialize default values - if not state.ex1_rgba: - state.ex1_rgba = [255, 0, 0, 1] - state.ex2_rgba = [255, 0, 0, 1] - with me.box(style=DEFAULT_MARGIN): me.text("Transform color", type="headline-5") me.text( @@ -56,7 +52,8 @@ def app(): ) with me.box( style=me.Style( - background=f"rgba({','.join(map(str, state.ex2_rgba))})", + background="red", + opacity=state.ex2_opacity, width=100, height=100, margin=me.Margin.all(10), @@ -95,15 +92,17 @@ def app(): me.button( "Transform", type="flat", on_click=transform_margin, style=BUTTON_MARGIN ) - with me.box( - style=me.Style( - background="rgba(255, 0, 0, 1)", - margin=me.Margin(left=state.ex4_margin, top=20), - width=30, - height=30, - ) - ): - me.text("") + with me.box(): + with me.box( + style=me.Style( + position="relative", + background="rgba(255, 0, 0, 1)", + left=state.ex4_left, + width=30, + height=30, + ) + ): + me.text("") def transform_red_yellow(e: me.ClickEvent): @@ -127,25 +126,21 @@ def transform_red_yellow(e: me.ClickEvent): def transform_fade_in_out(e: me.ClickEvent): - """Update alpha of the rgba. - - The better option would be to use opacity, but Mesop does not have that property - available yet. - """ + """Update opacity""" state = me.state(State) - if state.ex2_rgba[3] == 0: - while state.ex2_rgba[3] < 1: - state.ex2_rgba[3] += 0.05 + if state.ex2_opacity == 0: + while state.ex2_opacity < 1: + state.ex2_opacity += 0.05 yield time.sleep(0.1) - state.ex2_rgba[3] = 1 + state.ex2_opacity = 1.0 yield else: - while state.ex2_rgba[3] > 0: - state.ex2_rgba[3] -= 0.05 + while state.ex2_opacity > 0: + state.ex2_opacity -= 0.05 yield time.sleep(0.1) - state.ex2_rgba[3] = 0 + state.ex2_opacity = 0 yield @@ -169,23 +164,19 @@ def transform_width(e: me.ClickEvent): def transform_margin(e: me.ClickEvent): - """Update the margin to create sense of movement. - - Mesop does not yet have the top/bottom/left/right properties available, - so just modifying the margin for this example. - """ + """Update the position to create sense of movement.""" state = me.state(State) - if state.ex4_margin == 0: - while state.ex4_margin < 200: - state.ex4_margin += 10 + if state.ex4_left == 0: + while state.ex4_left < 200: + state.ex4_left += 10 yield time.sleep(0.1) - state.ex4_margin = 200 + state.ex4_left = 200 yield else: - while state.ex4_margin > 0: - state.ex4_margin -= 10 + while state.ex4_left > 0: + state.ex4_left -= 10 yield time.sleep(0.1) - state.ex4_margin = 0 + state.ex4_left = 0 yield diff --git a/mesop/component_helpers/style.py b/mesop/component_helpers/style.py index 9e88b9a5..194633a1 100644 --- a/mesop/component_helpers/style.py +++ b/mesop/component_helpers/style.py @@ -212,6 +212,7 @@ class Style: background: Sets the background color or image of the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/background). border: Defines the border properties for each side of the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/border). border_radius: Defines the border radius. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius). + bottom: Helps set vertical position of a positioned element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/bottom). box_shadow: Defines the box shadow. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow). box_sizing: Defines the box sizing. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing). color: Sets the color of the text inside the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/color). @@ -230,8 +231,13 @@ class Style: font_weight: Sets the weight (or boldness) of the font. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight). gap: Sets the gap. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/gap). grid_area: Sets the grid area. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-area). + grid_auto_columns: CSS property specifies the size of an implicitly-created grid column track or pattern of tracks. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-columns). + grid_auto_flow: CSS property controls how the auto-placement algorithm works, specifying exactly how auto-placed items get flowed into the grid. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow). + grid_auto_rows: CSS property specifies the size of an implicitly-created grid row track or pattern of tracks. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-rows). + grid_column: CSS shorthand property specifies a grid item's size and location within a grid column. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column). grid_column_start: Sets the grid column start. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column-start). grid_column_end: Sets the grid column end. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column-end). + grid_row: CSS shorthand property specifies a grid item's size and location within a grid row. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row). grid_row_start: Sets the grid row start. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start). grid_row_end: Sets the grid row end. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-end). grid_template_areas: Sets the grid template areas; each element is a row. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-areas). @@ -239,19 +245,24 @@ class Style: grid_template_rows: Sets the grid template rows. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows). height: Sets the height of the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/height). justify_content: Aligns the flexible container's items on the main-axis. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content). + left: Helps set horizontal position of a positioned element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/left). letter_spacing: Increases or decreases the space between characters in text. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/letter-spacing). line height: Set the line height (relative to the font size). See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height). margin: Sets the margin space required on each side of an element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/margin). + opacity: Sets the opacity property. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/opacity). outline: Sets the outline property. Note: `input` component has default browser stylings. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/outline). overflow_wrap: Specifies how long text can be broken up by new lines to prevent overflowing. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap). overflow_x: Specifies the handling of overflow in the horizontal direction. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-x). overflow_y: Specifies the handling of overflow in the vertical direction. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-y). padding: Sets the padding space required on each side of an element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/padding). position: Specifies the type of positioning method used for an element (static, relative, absolute, fixed, or sticky). See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/position). + right: Helps set horizontal position of a positioned element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/right). row_gap: Sets the gap between rows. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/row-gap). text_align: Specifies the horizontal alignment of text in an element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align). text_decoration: Specifies the decoration added to text. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration). text_overflow: Specifies how overflowed content that is not displayed should be signaled to the user. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow). + top: Helps set vertical position of a positioned element. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/top). + visibility: Sets the visibility property. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/visibility). white_space: Specifies how white space inside an element is handled. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space). width: Sets the width of the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/width). z-index: Sets the z-index of the component. See [MDN doc](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index). @@ -266,6 +277,7 @@ class Style: background: str | None = None border: Border | None = None border_radius: int | str | None = None + bottom: int | str | None = None box_shadow: str | None = None box_sizing: str | None = None color: str | None = None @@ -322,19 +334,26 @@ class Style: ) = None gap: int | str | None = None grid_area: str | None = None - grid_column_start: int | None = None - grid_column_end: int | None = None - grid_row_start: int | None = None - grid_row_end: int | None = None + grid_auto_columns: str | None = None + grid_auto_flow: str | None = None + grid_auto_rows: str | None = None + grid_column: str | None = None + grid_column_start: int | str | None = None + grid_column_end: int | str | None = None + grid_row: str | None = None + grid_row_start: int | str | None = None + grid_row_end: int | str | None = None grid_template_areas: list[str] | None = None grid_template_columns: str | None = None grid_template_rows: str | None = None height: int | str | None = None justify_content: ContentAlignmentValues | None = None justify_items: ItemAlignmentValues | None = None + left: int | str | None = None letter_spacing: int | str | None = None line_height: str | None = None margin: Margin | None = None + opacity: float | str | None = None outline: str | None = None overflow_wrap: OverflowWrapValues | None = None overflow_x: OverflowValues | None = None @@ -350,6 +369,7 @@ class Style: ] | None ) = None + right: int | str | None = None row_gap: int | str | None = None text_align: ( Literal[ @@ -363,6 +383,20 @@ class Style: ) = None text_decoration: Literal["underline", "none"] | None = None text_overflow: Literal["ellipsis", "clip"] | None = None + top: int | str | None = None + visibility: ( + Literal[ + "visible", + "hidden", + "collapse", + "inherit", + "initial", + "revert", + "revert-layer", + "unset", + ] + | None + ) = None white_space: ( Literal[ "normal", @@ -386,17 +420,18 @@ def to_style_proto(s: Style) -> pb.Style: background=s.background, border=_map_border(s.border), border_radius=_px_str(s.border_radius), + bottom=_px_str(s.bottom), box_shadow=s.box_shadow, box_sizing=s.box_sizing, color=s.color, column_gap=_px_str(s.column_gap), - columns=str(s.columns), + columns=_int_str(s.columns), cursor=s.cursor, display=s.display, flex_basis=s.flex_basis, flex_direction=s.flex_direction, flex_grow=s.flex_grow, - flex_shrink=str(s.flex_shrink), + flex_shrink=_int_str(s.flex_shrink), flex_wrap=s.flex_wrap, font_family=s.font_family, font_size=_px_str(s.font_size), @@ -404,29 +439,39 @@ def to_style_proto(s: Style) -> pb.Style: font_weight=_map_font_weight(s.font_weight), gap=_px_str(s.gap), grid_area=s.grid_area, - grid_column_start=s.grid_column_start, - grid_column_end=s.grid_column_end, - grid_row_start=s.grid_row_start, - grid_row_end=s.grid_row_end, + grid_auto_columns=s.grid_auto_columns, + grid_auto_flow=s.grid_auto_flow, + grid_auto_rows=s.grid_auto_rows, + grid_column=s.grid_column, + grid_column_start=_int_str(s.grid_column_start), + grid_column_end=_int_str(s.grid_column_end), + grid_row=s.grid_row, + grid_row_start=_int_str(s.grid_row_start), + grid_row_end=_int_str(s.grid_row_end), grid_template_areas=s.grid_template_areas, grid_template_columns=s.grid_template_columns, grid_template_rows=s.grid_template_rows, height=_px_str(s.height), justify_content=s.justify_content, justify_items=s.justify_items, + left=_px_str(s.left), letter_spacing=_px_str(s.letter_spacing), line_height=str(s.line_height), margin=_map_edge_insets(s.margin), + opacity=_float_str(s.opacity), outline=s.outline, overflow_wrap=s.overflow_wrap, overflow_x=s.overflow_x, overflow_y=s.overflow_y, padding=_map_edge_insets(s.padding), position=s.position, + right=_px_str(s.right), row_gap=_px_str(s.row_gap), text_align=s.text_align, text_decoration=s.text_decoration, text_overflow=s.text_overflow, + top=_px_str(s.top), + visibility=s.visibility, white_space=s.white_space, width=_px_str(s.width), z_index=s.z_index, @@ -471,3 +516,17 @@ def _px_str(int_or_str: int | str | None) -> str | None: if isinstance(int_or_str, int): return str(int_or_str) + "px" return int_or_str + + +def _int_str(int_or_str: int | str | None) -> str | None: + if isinstance(int_or_str, int): + return str(int_or_str) + return int_or_str + + +def _float_str(float_or_str: float | str | None) -> str | None: + # Int is included to fix type check warning: + # Expression of type "int | str | None" cannot be assigned to return type "str | None" + if isinstance(float_or_str, (float, int)): + return str(float_or_str) + return float_or_str diff --git a/mesop/editor/component_configs.py b/mesop/editor/component_configs.py index 22f2d867..936f56e4 100644 --- a/mesop/editor/component_configs.py +++ b/mesop/editor/component_configs.py @@ -62,13 +62,24 @@ def get_fields( field_type = pb.FieldType(int_type=pb.IntType(default_value=0)) elif param_type is float: field_type = pb.FieldType(float_type=pb.FloatType(default_value=0)) - elif param_type is str or ( - # special case, for int|str|None (used for styles, e.g. pixel value), use str - args - and len(args) == 3 - and args[0] is int - and args[1] is str - and args[2] is NoneType + elif ( + param_type is str + or ( + # special case, for int|str|None (used for styles, e.g. pixel value), use str + args + and len(args) == 3 + and args[0] is int + and args[1] is str + and args[2] is NoneType + ) + or ( + # special case, for float|str|None (used for styles, e.g. opacity), use str + args + and len(args) == 3 + and args[0] is float + and args[1] is str + and args[2] is NoneType + ) ): field_type = pb.FieldType(string_type=pb.StringType()) elif getattr(param_type, "__origin__", None) is Literal: diff --git a/mesop/protos/ui.proto b/mesop/protos/ui.proto index 70a8692c..84487784 100644 --- a/mesop/protos/ui.proto +++ b/mesop/protos/ui.proto @@ -274,7 +274,7 @@ message UserDefinedType { repeated Arg args = 1; } -// Next id: 51 +// Next id: 62 message Style { optional string align_content = 32; optional string align_items = 1; @@ -282,6 +282,7 @@ message Style { optional string background = 2; optional Border border = 3; optional string border_radius = 28; + optional string bottom = 53; optional string box_shadow = 29; optional string box_sizing = 47; optional string color = 4; @@ -300,29 +301,39 @@ message Style { optional string font_weight = 14; optional string gap = 30; optional string grid_area = 34; - optional int32 grid_column_start = 35; - optional int32 grid_column_end = 36; - optional int32 grid_row_start = 37; - optional int32 grid_row_end = 38; + optional string grid_auto_columns = 57; + optional string grid_auto_flow = 58; + optional string grid_auto_rows = 59; + optional string grid_column = 60; + optional string grid_column_start = 35; + optional string grid_column_end = 36; + optional string grid_row = 61; + optional string grid_row_start = 37; + optional string grid_row_end = 38; repeated string grid_template_areas = 39; optional string grid_template_columns = 40; optional string grid_template_rows = 41; optional string height = 15; optional string justify_content = 16; optional string justify_items = 42; + optional string left = 54; optional string letter_spacing = 17; optional string line_height = 31; optional EdgeInsets margin = 18; + optional string opacity = 52; optional string outline = 44; optional string overflow_wrap = 49; optional string overflow_x = 19; optional string overflow_y = 20; optional EdgeInsets padding = 21; optional string position = 22; + optional string right = 55; optional string row_gap = 43; optional string text_align = 23; optional string text_decoration = 24; optional string text_overflow = 25; + optional string top = 56; + optional string visibility = 51; optional string white_space = 26; optional string width = 27; optional int32 z_index = 48; diff --git a/mesop/web/src/utils/styles.ts b/mesop/web/src/utils/styles.ts index 8814c4dc..f10c88f9 100644 --- a/mesop/web/src/utils/styles.ts +++ b/mesop/web/src/utils/styles.ts @@ -39,6 +39,9 @@ export function formatStyle(styleObj: Style): string { if (styleObj.getBorderRadius()) { style += `border-radius: ${styleObj.getBorderRadius()};`; } + if (styleObj.getBottom()) { + style += `bottom: ${styleObj.getBottom()};`; + } if (styleObj.getBoxShadow()) { style += `box-shadow: ${styleObj.getBoxShadow()};`; } @@ -93,12 +96,27 @@ export function formatStyle(styleObj: Style): string { if (styleObj.getGridArea()) { style += `grid-area: ${styleObj.getGridArea()};`; } + if (styleObj.getGridAutoColumns()) { + style += `grid-auto-columns: ${styleObj.getGridAutoColumns()};`; + } + if (styleObj.getGridAutoFlow()) { + style += `grid-auto-flow: ${styleObj.getGridAutoFlow()};`; + } + if (styleObj.getGridAutoRows()) { + style += `grid-auto-rows: ${styleObj.getGridAutoRows()};`; + } + if (styleObj.getGridColumn()) { + style += `grid-column: ${styleObj.getGridColumn()};`; + } if (styleObj.getGridColumnStart()) { style += `grid-column-start: ${styleObj.getGridColumnStart()};`; } if (styleObj.getGridColumnEnd()) { style += `grid-column-end: ${styleObj.getGridColumnEnd()};`; } + if (styleObj.getGridRow()) { + style += `grid-row: ${styleObj.getGridRow()};`; + } if (styleObj.getGridRowStart()) { style += `grid-row-start: ${styleObj.getGridRowStart()};`; } @@ -126,6 +144,9 @@ export function formatStyle(styleObj: Style): string { if (styleObj.getJustifyItems()) { style += `justify-items: ${styleObj.getJustifyItems()};`; } + if (styleObj.getLeft()) { + style += `left: ${styleObj.getLeft()};`; + } if (styleObj.getLetterSpacing()) { style += `letter-spacing: ${styleObj.getLetterSpacing()};`; } @@ -138,6 +159,9 @@ export function formatStyle(styleObj: Style): string { margin.getBottom() || 0 } ${margin.getLeft() || 0};`; } + if (styleObj.getOpacity()) { + style += `opacity: ${styleObj.getOpacity()};`; + } if (styleObj.getOutline()) { style += `outline: ${styleObj.getOutline()};`; } @@ -159,6 +183,9 @@ export function formatStyle(styleObj: Style): string { if (styleObj.getPosition()) { style += `position: ${styleObj.getPosition()};`; } + if (styleObj.getRight()) { + style += `right: ${styleObj.getRight()};`; + } if (styleObj.getRowGap()) { style += `row-gap: ${styleObj.getRowGap()};`; } @@ -171,6 +198,12 @@ export function formatStyle(styleObj: Style): string { if (styleObj.getTextOverflow()) { style += `text-overflow: ${styleObj.getTextOverflow()};`; } + if (styleObj.getTop()) { + style += `top: ${styleObj.getTop()};`; + } + if (styleObj.getVisibility()) { + style += `visibility: ${styleObj.getVisibility()};`; + } if (styleObj.getWhiteSpace()) { style += `white-space: ${styleObj.getWhiteSpace()};`; }