/nicegui-style-demo-1.py Secret
Created
February 5, 2023 13:29
Demonstrates a new style definition API using a builder pattern and literal keyword strings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from copy import deepcopy | |
from typing import Literal, Optional, overload | |
from nicegui import ui | |
# This example demonstrates how to replace the style method with an instance of a Style class. | |
# The instance can be called like the original method. | |
# But it also has methods itself for manipulating individual style properties. | |
# Each property has overloads for different units. | |
# For string values there are overloads with literal types for predefined values. | |
Color = Literal['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'black', 'white', 'gray'] | |
class Style: | |
def __init__(self, element: ui.element) -> None: | |
self.element = element | |
def __call__(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None): | |
'''CSS style sheet definitions to modify the look of the element. | |
Every style in the `remove` parameter will be removed from the element. | |
Styles are separated with a semicolon. | |
This can be helpful if the predefined style sheet definitions by NiceGUI are not wanted in a particular styling. | |
''' | |
# NOTE: This is the original implementation of the style method. | |
style_dict = deepcopy(self.element._style) if replace is None else {} | |
for key in self.element._parse_style(remove): | |
if key in style_dict: | |
del style_dict[key] | |
style_dict.update(self.element._parse_style(add)) | |
style_dict.update(self.element._parse_style(replace)) | |
if self.element._style != style_dict: | |
self.element._style = style_dict | |
self.element.update() | |
return self.element | |
@overload | |
def color(self, color: Color) -> 'Style': | |
'''Set the color of the element to a predefined color name.''' | |
@overload | |
def color(self, value: str) -> 'Style': | |
'''Set the color of the element to an arbitrary value.''' | |
def color(self, value: str) -> 'Style': | |
'''Set the color of the element.''' | |
self.element._style['color'] = value | |
return self | |
@overload | |
def background_color(self, color: Color) -> 'Style': | |
'''Set the background color of the element to a predefined color name.''' | |
@overload | |
def background_color(self, value: str) -> 'Style': | |
'''Set the color of the element to an arbitrary value.''' | |
def background_color(self, value: str) -> 'Style': | |
'''Set the background color of the element.''' | |
self.element._style['background-color'] = value | |
return self | |
@overload | |
def width(self, px: float) -> 'Style': | |
'''Set the width of the element in pixels.''' | |
@overload | |
def width(self, rem: float) -> 'Style': | |
'''Set the width of the element in rem.''' | |
@overload | |
def width(self, percent: float) -> 'Style': | |
'''Set the width of the element in percent.''' | |
@overload | |
def width(self, value: Literal['auto', 'max-content', 'min-content', 'fit-content']) -> 'Style': | |
'''Set the width of the element to a predefined value.''' | |
@overload | |
def width(self, value: str) -> 'Style': | |
'''Set the width of the element to an arbitrary value.''' | |
def width(self, value: str = ..., *, px: float = ..., rem: float = ..., percent: float = ...) -> 'Style': | |
return self._set_dimension('width', value, px, rem, percent) | |
@overload | |
def height(self, px: float) -> 'Style': | |
'''Set the height of the element in pixels.''' | |
@overload | |
def height(self, rem: float) -> 'Style': | |
'''Set the height of the element in rem.''' | |
@overload | |
def height(self, percent: float) -> 'Style': | |
'''Set the height of the element in percent.''' | |
@overload | |
def height(self, value: Literal['auto', 'max-content', 'min-content', 'fit-content']) -> 'Style': | |
'''Set the height of the element to a predefined value.''' | |
@overload | |
def height(self, value: str) -> 'Style': | |
'''Set the height of the element to an arbitrary value.''' | |
def height(self, value: str = ..., *, px: float = ..., rem: float = ..., percent: float = ...) -> 'Style': | |
'''Set the height of the element.''' | |
return self._set_dimension('height', value, px, rem, percent) | |
@overload | |
def margin(self, px: float) -> 'Style': | |
'''Set the margin of the element in pixels.''' | |
@overload | |
def margin(self, rem: float) -> 'Style': | |
'''Set the margin of the element in rem.''' | |
@overload | |
def margin(self, percent: float) -> 'Style': | |
'''Set the margin of the element in percent.''' | |
@overload | |
def margin(self, value: str) -> 'Style': | |
'''Set the margin of the element to an arbitrary value.''' | |
def margin(self, value: str = ..., *, px: float = ..., rem: float = ..., percent: float = ...) -> 'Style': | |
return self._set_dimension('margin', value, px, rem, percent) | |
@overload | |
def padding(self, px: float) -> 'Style': | |
'''Set the padding of the element in pixels.''' | |
@overload | |
def padding(self, rem: float) -> 'Style': | |
'''Set the padding of the element in rem.''' | |
@overload | |
def padding(self, percent: float) -> 'Style': | |
'''Set the padding of the element in percent.''' | |
@overload | |
def padding(self, value: str) -> 'Style': | |
'''Set the padding of the element to an arbitrary value.''' | |
def padding(self, value: str = ..., *, px: float = ..., rem: float = ..., percent: float = ...) -> 'Style': | |
'''Set the padding of the element.''' | |
return self._set_dimension('padding', value, px, rem, percent) | |
def _set_dimension(self, key: str, value: str = ..., px: float = ..., rem: float = ..., percent: float = ...) -> 'Style': | |
if value is not ...: | |
self.element._style[key] = value | |
elif px is not ...: | |
self.element._style[key] = f'{px}px' | |
elif rem is not ...: | |
self.element._style[key] = f'{rem}rem' | |
elif percent is not ...: | |
self.element._style[key] = f'{percent}%' | |
else: | |
raise TypeError(f'{key} dimension requires either px, rem or percent') | |
return self | |
label = ui.label('Hello World!') | |
label.style = Style(label) # NOTE: monkey-patch label.style, will be done in ui.element | |
label.style.color('blue').background_color('orange').width(px=150).padding(rem=1).margin('auto') | |
ui.run(port=4321) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment