From de6244483d97272290d841a00dbd9205f4964d29 Mon Sep 17 00:00:00 2001 From: Masen Furer Date: Wed, 7 Feb 2024 11:55:25 -0800 Subject: [PATCH] [REF-1743] Implement radix-native color mode switch and button (#2526) --- reflex/.templates/apps/blank/code/blank.py | 2 +- reflex/__init__.py | 6 +- reflex/__init__.pyi | 3 +- reflex/components/chakra/forms/__init__.py | 7 +- .../chakra/forms/colormodeswitch.py | 31 +- .../chakra/forms/colormodeswitch.pyi | 11 +- reflex/components/core/__init__.py | 2 +- reflex/components/core/cond.py | 18 + reflex/components/datadisplay/code.py | 3 +- reflex/components/datadisplay/code.pyi | 3 +- reflex/components/radix/themes/__init__.py | 1 + reflex/components/radix/themes/base.py | 18 + reflex/components/radix/themes/base.pyi | 72 +-- reflex/components/radix/themes/color_mode.py | 109 ++++ reflex/components/radix/themes/color_mode.pyi | 540 ++++++++++++++++++ reflex/style.py | 3 + 16 files changed, 712 insertions(+), 117 deletions(-) create mode 100644 reflex/components/radix/themes/color_mode.py create mode 100644 reflex/components/radix/themes/color_mode.pyi diff --git a/reflex/.templates/apps/blank/code/blank.py b/reflex/.templates/apps/blank/code/blank.py index 2d1f1b257..96e73e1af 100644 --- a/reflex/.templates/apps/blank/code/blank.py +++ b/reflex/.templates/apps/blank/code/blank.py @@ -15,7 +15,7 @@ class State(rx.State): def index() -> rx.Component: return rx.fragment( - rx.color_mode_button(rx.color_mode_icon(), float="right"), + rx.color_mode.button(rx.color_mode.icon(), float="right"), rx.vstack( rx.heading("Welcome to Reflex!", font_size="2em"), rx.box("Get started by editing ", rx.code(filename, font_size="1em")), diff --git a/reflex/__init__.py b/reflex/__init__.py index dbfa714ed..52b59f571 100644 --- a/reflex/__init__.py +++ b/reflex/__init__.py @@ -20,7 +20,7 @@ _ALL_COMPONENTS = [ "foreach", "html", "match", - # "color_mode_cond", + "color_mode_cond", "connection_banner", "connection_modal", "debounce_input", @@ -121,7 +121,7 @@ _MAPPING = { "reflex.components.el": ["el"], "reflex.components.lucide": ["lucide"], "reflex.components.next": ["next"], - "reflex.components.radix": ["radix"], + "reflex.components.radix": ["radix", "color_mode"], "reflex.components.recharts": ["recharts"], "reflex.components.moment.moment": ["MomentDelta"], "reflex.config": ["config", "Config", "DBConfig"], @@ -150,7 +150,7 @@ _MAPPING = { "reflex.page": ["page"], "reflex.route": ["route"], "reflex.state": ["state", "var", "Cookie", "LocalStorage", "State"], - "reflex.style": ["style", "color_mode", "toggle_color_mode"], + "reflex.style": ["style", "toggle_color_mode"], "reflex.testing": ["testing"], "reflex.utils": ["utils"], "reflex.vars": ["vars", "cached_var", "Var"], diff --git a/reflex/__init__.pyi b/reflex/__init__.pyi index 81557a034..977bcc698 100644 --- a/reflex/__init__.pyi +++ b/reflex/__init__.pyi @@ -12,6 +12,7 @@ from reflex.components import cond as cond from reflex.components import foreach as foreach from reflex.components import html as html from reflex.components import match as match +from reflex.components import color_mode_cond as color_mode_cond from reflex.components import connection_banner as connection_banner from reflex.components import connection_modal as connection_modal from reflex.components import debounce_input as debounce_input @@ -97,6 +98,7 @@ from reflex.components import el as el from reflex.components import lucide as lucide from reflex.components import next as next from reflex.components import radix as radix +from reflex.components.radix import color_mode as color_mode from reflex.components import recharts as recharts from reflex.components.moment.moment import MomentDelta as MomentDelta from reflex import config as config @@ -134,7 +136,6 @@ from reflex.state import Cookie as Cookie from reflex.state import LocalStorage as LocalStorage from reflex.state import State as State from reflex import style as style -from reflex.style import color_mode as color_mode from reflex.style import toggle_color_mode as toggle_color_mode from reflex import testing as testing from reflex import utils as utils diff --git a/reflex/components/chakra/forms/__init__.py b/reflex/components/chakra/forms/__init__.py index 56bf5b195..7bf225e35 100644 --- a/reflex/components/chakra/forms/__init__.py +++ b/reflex/components/chakra/forms/__init__.py @@ -7,7 +7,6 @@ from .colormodeswitch import ( ColorModeIcon, ColorModeScript, ColorModeSwitch, - color_mode_cond, ) from .date_picker import DatePicker from .date_time_picker import DateTimePicker @@ -47,8 +46,4 @@ from .switch import Switch from .textarea import TextArea from .time_picker import TimePicker -helpers = [ - "color_mode_cond", -] - -__all__ = [f for f in dir() if f[0].isupper()] + helpers # type: ignore +__all__ = [f for f in dir() if f[0].isupper()] # type: ignore diff --git a/reflex/components/chakra/forms/colormodeswitch.py b/reflex/components/chakra/forms/colormodeswitch.py index 10de7a5a2..b88e5895b 100644 --- a/reflex/components/chakra/forms/colormodeswitch.py +++ b/reflex/components/chakra/forms/colormodeswitch.py @@ -16,40 +16,19 @@ rx.text( """ from __future__ import annotations -from typing import Any - from reflex.components.chakra import ChakraComponent from reflex.components.chakra.media.icon import Icon -from reflex.components.component import BaseComponent, Component -from reflex.components.core.cond import Cond, cond -from reflex.style import color_mode, toggle_color_mode -from reflex.vars import Var +from reflex.components.component import BaseComponent +from reflex.components.core.cond import Cond, color_mode_cond +from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode from .button import Button from .switch import Switch -DEFAULT_COLOR_MODE: str = "light" DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun") DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon") -def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: - """Create a component or Prop based on color_mode. - - Args: - light: The component or prop to render if color_mode is default - dark: The component or prop to render if color_mode is non-default - - Returns: - The conditional component or prop. - """ - return cond( - color_mode == DEFAULT_COLOR_MODE, - light, - dark, - ) - - class ColorModeIcon(Cond): """Displays the current color mode as an icon.""" @@ -90,7 +69,7 @@ class ColorModeSwitch(Switch): """ return Switch.create( *children, - is_checked=color_mode != DEFAULT_COLOR_MODE, + is_checked=color_mode != LIGHT_COLOR_MODE, on_change=toggle_color_mode, **props, ) @@ -121,4 +100,4 @@ class ColorModeScript(ChakraComponent): """Chakra color mode script.""" tag = "ColorModeScript" - initialColorMode = "light" + initialColorMode = LIGHT_COLOR_MODE diff --git a/reflex/components/chakra/forms/colormodeswitch.pyi b/reflex/components/chakra/forms/colormodeswitch.pyi index 77fcff291..be3700b00 100644 --- a/reflex/components/chakra/forms/colormodeswitch.pyi +++ b/reflex/components/chakra/forms/colormodeswitch.pyi @@ -7,22 +7,17 @@ from typing import Any, Dict, Literal, Optional, Union, overload from reflex.vars import Var, BaseVar, ComputedVar from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style -from typing import Any from reflex.components.chakra import ChakraComponent from reflex.components.chakra.media.icon import Icon -from reflex.components.component import BaseComponent, Component -from reflex.components.core.cond import Cond, cond -from reflex.style import color_mode, toggle_color_mode -from reflex.vars import Var +from reflex.components.component import BaseComponent +from reflex.components.core.cond import Cond, color_mode_cond +from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode from .button import Button from .switch import Switch -DEFAULT_COLOR_MODE: str DEFAULT_LIGHT_ICON: Icon DEFAULT_DARK_ICON: Icon -def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: ... - class ColorModeIcon(Cond): @overload @classmethod diff --git a/reflex/components/core/__init__.py b/reflex/components/core/__init__.py index a3b4a0d1f..b223771ed 100644 --- a/reflex/components/core/__init__.py +++ b/reflex/components/core/__init__.py @@ -3,7 +3,7 @@ from . import layout as layout from .banner import ConnectionBanner, ConnectionModal from .colors import color -from .cond import Cond, cond +from .cond import Cond, color_mode_cond, cond from .debounce import DebounceInput from .foreach import Foreach from .html import Html diff --git a/reflex/components/core/cond.py b/reflex/components/core/cond.py index b17ab5409..174664c9c 100644 --- a/reflex/components/core/cond.py +++ b/reflex/components/core/cond.py @@ -7,6 +7,7 @@ from reflex.components.base.fragment import Fragment from reflex.components.component import BaseComponent, Component, MemoizationLeaf from reflex.components.tags import CondTag, Tag from reflex.constants import Dirs +from reflex.style import LIGHT_COLOR_MODE, color_mode from reflex.utils import format, imports from reflex.vars import BaseVar, Var, VarData @@ -180,3 +181,20 @@ def cond(condition: Any, c1: Any, c2: Any = None): _var_full_name_needs_state_prefix=False, merge_var_data=VarData.merge(*var_datas), ) + + +def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: + """Create a component or Prop based on color_mode. + + Args: + light: The component or prop to render if color_mode is default + dark: The component or prop to render if color_mode is non-default + + Returns: + The conditional component or prop. + """ + return cond( + color_mode == LIGHT_COLOR_MODE, + light, + dark, + ) diff --git a/reflex/components/datadisplay/code.py b/reflex/components/datadisplay/code.py index ec9633399..0ac0d4d27 100644 --- a/reflex/components/datadisplay/code.py +++ b/reflex/components/datadisplay/code.py @@ -2,10 +2,11 @@ import re from typing import Dict, Literal, Optional, Union -from reflex.components.chakra.forms import Button, color_mode_cond +from reflex.components.chakra.forms import Button from reflex.components.chakra.layout import Box from reflex.components.chakra.media import Icon from reflex.components.component import Component +from reflex.components.core.cond import color_mode_cond from reflex.event import set_clipboard from reflex.style import Style from reflex.utils import format, imports diff --git a/reflex/components/datadisplay/code.pyi b/reflex/components/datadisplay/code.pyi index e398da463..cdbf8454b 100644 --- a/reflex/components/datadisplay/code.pyi +++ b/reflex/components/datadisplay/code.pyi @@ -9,10 +9,11 @@ from reflex.event import EventChain, EventHandler, EventSpec from reflex.style import Style import re from typing import Dict, Literal, Optional, Union -from reflex.components.chakra.forms import Button, color_mode_cond +from reflex.components.chakra.forms import Button from reflex.components.chakra.layout import Box from reflex.components.chakra.media import Icon from reflex.components.component import Component +from reflex.components.core.cond import color_mode_cond from reflex.event import set_clipboard from reflex.style import Style from reflex.utils import format, imports diff --git a/reflex/components/radix/themes/__init__.py b/reflex/components/radix/themes/__init__.py index ec02436b1..e1d39fb52 100644 --- a/reflex/components/radix/themes/__init__.py +++ b/reflex/components/radix/themes/__init__.py @@ -1,6 +1,7 @@ """Namespace for components provided by the @radix-ui/themes library.""" from .base import theme as theme from .base import theme_panel as theme_panel +from .color_mode import color_mode_var_and_namespace as color_mode from .components import * from .layout import * from .typography import * diff --git a/reflex/components/radix/themes/base.py b/reflex/components/radix/themes/base.py index 4754b147f..f412607aa 100644 --- a/reflex/components/radix/themes/base.py +++ b/reflex/components/radix/themes/base.py @@ -169,6 +169,24 @@ class Theme(RadixThemesComponent): # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%" scaling: Var[LiteralScaling] + @classmethod + def create( + cls, *children, color_mode: LiteralAppearance | None = None, **props + ) -> Component: + """Create a new Radix Theme specification. + + Args: + *children: Child components. + color_mode: map to appearance prop. + **props: Component properties. + + Returns: + A new component instance. + """ + if color_mode is not None: + props["appearance"] = props.pop("color_mode") + return super().create(*children, **props) + def _get_imports(self) -> imports.ImportDict: return imports.merge_imports( super()._get_imports(), diff --git a/reflex/components/radix/themes/base.pyi b/reflex/components/radix/themes/base.pyi index b3a893b79..213afde02 100644 --- a/reflex/components/radix/themes/base.pyi +++ b/reflex/components/radix/themes/base.pyi @@ -335,69 +335,7 @@ class Theme(RadixThemesComponent): def create( # type: ignore cls, *children, - color: Optional[Union[Var[str], str]] = None, - color_scheme: Optional[ - Union[ - Var[ - Literal[ - "tomato", - "red", - "ruby", - "crimson", - "pink", - "plum", - "purple", - "violet", - "iris", - "indigo", - "blue", - "cyan", - "teal", - "jade", - "green", - "grass", - "brown", - "orange", - "sky", - "mint", - "lime", - "yellow", - "amber", - "gold", - "bronze", - "gray", - ] - ], - Literal[ - "tomato", - "red", - "ruby", - "crimson", - "pink", - "plum", - "purple", - "violet", - "iris", - "indigo", - "blue", - "cyan", - "teal", - "jade", - "green", - "grass", - "brown", - "orange", - "sky", - "mint", - "lime", - "yellow", - "amber", - "gold", - "bronze", - "gray", - ], - ] - ] = None, + color_mode: Optional[LiteralAppearance | None] = None, has_background: Optional[Union[Var[bool], bool]] = None, appearance: Optional[ Union[ @@ -542,15 +480,11 @@ class Theme(RadixThemesComponent): ] = None, **props ) -> "Theme": - """Create a new component instance. - - Will prepend "RadixThemes" to the component tag to avoid conflicts with - other UI libraries for common names, like Text and Button. + """Create a new Radix Theme specification. Args: *children: Child components. - color: map to CSS default color property. - color_scheme: map to radix color property. + color_mode: map to appearance prop. has_background: Whether to apply the themes background color to the theme node. Defaults to True. appearance: Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit". accent_color: The color used for default buttons, typography, backgrounds, etc diff --git a/reflex/components/radix/themes/color_mode.py b/reflex/components/radix/themes/color_mode.py new file mode 100644 index 000000000..b6b739870 --- /dev/null +++ b/reflex/components/radix/themes/color_mode.py @@ -0,0 +1,109 @@ +"""A switch component for toggling color_mode. + +To style components based on color mode, use style props with `color_mode_cond`: + +``` +rx.text( + "Hover over me", + _hover={ + "background": rx.color_mode_cond( + light="var(--accent-2)", + dark="var(--accent-4)", + ), + }, +) +``` +""" +from __future__ import annotations + +import dataclasses + +from reflex.components.component import BaseComponent +from reflex.components.core.cond import Cond, color_mode_cond +from reflex.components.lucide.icon import Icon +from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode +from reflex.vars import BaseVar + +from .components.button import Button +from .components.switch import Switch + +DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun") +DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon") + + +class ColorModeIcon(Cond): + """Displays the current color mode as an icon.""" + + @classmethod + def create( + cls, + light_component: BaseComponent | None = None, + dark_component: BaseComponent | None = None, + ): + """Create an icon component based on color_mode. + + Args: + light_component: the component to display when color mode is default + dark_component: the component to display when color mode is dark (non-default) + + Returns: + The conditionally rendered component + """ + return color_mode_cond( + light=light_component or DEFAULT_LIGHT_ICON, + dark=dark_component or DEFAULT_DARK_ICON, + ) + + +class ColorModeSwitch(Switch): + """Switch for toggling light / dark mode via toggle_color_mode.""" + + @classmethod + def create(cls, *children, **props): + """Create a switch component bound to color_mode. + + Args: + *children: The children of the component. + **props: The props to pass to the component. + + Returns: + The switch component. + """ + return Switch.create( + *children, + is_checked=color_mode != LIGHT_COLOR_MODE, + on_change=toggle_color_mode, + **props, + ) + + +class ColorModeButton(Button): + """Button for toggling chakra light / dark mode via toggle_color_mode.""" + + @classmethod + def create(cls, *children, **props): + """Create a button component that calls toggle_color_mode on click. + + Args: + *children: The children of the component. + **props: The props to pass to the component. + + Returns: + The button component. + """ + return Button.create( + *children, + on_click=toggle_color_mode, + **props, + ) + + +class ColorModeNamespace(BaseVar): + """Namespace for color mode components.""" + + icon = staticmethod(ColorModeIcon.create) + switch = staticmethod(ColorModeSwitch.create) + button = staticmethod(ColorModeButton.create) + + +color_mode_var_and_namespace = ColorModeNamespace(**dataclasses.asdict(color_mode)) diff --git a/reflex/components/radix/themes/color_mode.pyi b/reflex/components/radix/themes/color_mode.pyi new file mode 100644 index 000000000..50e4093c1 --- /dev/null +++ b/reflex/components/radix/themes/color_mode.pyi @@ -0,0 +1,540 @@ +"""Stub file for reflex/components/radix/themes/color_mode.py""" +# ------------------- DO NOT EDIT ---------------------- +# This file was generated by `scripts/pyi_generator.py`! +# ------------------------------------------------------ + +from typing import Any, Dict, Literal, Optional, Union, overload +from reflex.vars import Var, BaseVar, ComputedVar +from reflex.event import EventChain, EventHandler, EventSpec +from reflex.style import Style +import dataclasses +from reflex.components.component import BaseComponent +from reflex.components.core.cond import Cond, color_mode_cond +from reflex.components.lucide.icon import Icon +from reflex.style import LIGHT_COLOR_MODE, color_mode, toggle_color_mode +from reflex.vars import BaseVar +from .components.button import Button +from .components.switch import Switch + +DEFAULT_LIGHT_ICON: Icon +DEFAULT_DARK_ICON: Icon + +class ColorModeIcon(Cond): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + cond: Optional[Union[Var[Any], Any]] = None, + comp1: Optional[BaseComponent] = None, + comp2: Optional[BaseComponent] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + _rename_props: Optional[Dict[str, str]] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_focus: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_scroll: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + **props + ) -> "ColorModeIcon": + """Create an icon component based on color_mode. + + Args: + light_component: the component to display when color mode is default + dark_component: the component to display when color mode is dark (non-default) + + Returns: + The conditionally rendered component + """ + ... + +class ColorModeSwitch(Switch): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + as_child: Optional[Union[Var[bool], bool]] = None, + default_checked: Optional[Union[Var[bool], bool]] = None, + checked: Optional[Union[Var[bool], bool]] = None, + disabled: Optional[Union[Var[bool], bool]] = None, + required: Optional[Union[Var[bool], bool]] = None, + name: Optional[Union[Var[str], str]] = None, + value: Optional[Union[Var[str], str]] = None, + size: Optional[ + Union[Var[Literal["1", "2", "3", "4"]], Literal["1", "2", "3", "4"]] + ] = None, + variant: Optional[ + Union[ + Var[Literal["classic", "solid", "soft", "surface", "outline", "ghost"]], + Literal["classic", "solid", "soft", "surface", "outline", "ghost"], + ] + ] = None, + color_scheme: Optional[ + Union[ + Var[ + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ] + ], + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ], + ] + ] = None, + high_contrast: Optional[Union[Var[bool], bool]] = None, + radius: Optional[ + Union[ + Var[Literal["none", "small", "medium", "large", "full"]], + Literal["none", "small", "medium", "large", "full"], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + _rename_props: Optional[Dict[str, str]] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_change: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_focus: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_scroll: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + **props + ) -> "ColorModeSwitch": + """Create a switch component bound to color_mode. + + Args: + *children: The children of the component. + as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. + default_checked: Whether the switch is checked by default + checked: Whether the switch is checked + disabled: If true, prevent the user from interacting with the switch + required: If true, the user must interact with the switch to submit the form + name: The name of the switch (when submitting a form) + value: The value associated with the "on" position + size: Switch size "1" - "4" + variant: Variant of switch: "solid" | "soft" | "outline" | "ghost" + color_scheme: Override theme color for switch + high_contrast: Whether to render the switch with higher contrast color against background + radius: Override theme radius for switch: "none" | "small" | "medium" | "large" | "full" + style: Props to rename 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 + _rename_props: props to change the name of + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The switch component. + """ + ... + +class ColorModeButton(Button): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + as_child: Optional[Union[Var[bool], bool]] = None, + size: Optional[ + Union[Var[Literal["1", "2", "3", "4"]], Literal["1", "2", "3", "4"]] + ] = None, + variant: Optional[ + Union[ + Var[Literal["classic", "solid", "soft", "surface", "outline", "ghost"]], + Literal["classic", "solid", "soft", "surface", "outline", "ghost"], + ] + ] = None, + color_scheme: Optional[ + Union[ + Var[ + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ] + ], + Literal[ + "tomato", + "red", + "ruby", + "crimson", + "pink", + "plum", + "purple", + "violet", + "iris", + "indigo", + "blue", + "cyan", + "teal", + "jade", + "green", + "grass", + "brown", + "orange", + "sky", + "mint", + "lime", + "yellow", + "amber", + "gold", + "bronze", + "gray", + ], + ] + ] = None, + high_contrast: Optional[Union[Var[bool], bool]] = None, + radius: Optional[ + Union[ + Var[Literal["none", "small", "medium", "large", "full"]], + Literal["none", "small", "medium", "large", "full"], + ] + ] = None, + auto_focus: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + disabled: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + form: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + form_action: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + form_enc_type: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + form_method: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + form_no_validate: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + form_target: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + name: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + type: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + value: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + access_key: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + auto_capitalize: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + content_editable: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + context_menu: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + dir: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + draggable: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + enter_key_hint: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + hidden: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + input_mode: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + item_prop: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + lang: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + role: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + slot: Optional[Union[Var[Union[str, int, bool]], Union[str, int, bool]]] = None, + spell_check: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + tab_index: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + title: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + translate: Optional[ + Union[Var[Union[str, int, bool]], Union[str, int, bool]] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + _rename_props: Optional[Dict[str, str]] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_focus: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_scroll: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, function, BaseVar] + ] = None, + **props + ) -> "ColorModeButton": + """Create a button component that calls toggle_color_mode on click. + + Args: + *children: The children of the component. + as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. + size: Button size "1" - "4" + variant: Variant of button: "solid" | "soft" | "outline" | "ghost" + color_scheme: Override theme color for button + high_contrast: Whether to render the button with higher contrast color against background + radius: Override theme radius for button: "none" | "small" | "medium" | "large" | "full" + auto_focus: Automatically focuses the button when the page loads + disabled: Disables the button + form: Associates the button with a form (by id) + form_action: URL to send the form data to (for type="submit" buttons) + form_enc_type: How the form data should be encoded when submitting to the server (for type="submit" buttons) + form_method: HTTP method to use for sending form data (for type="submit" buttons) + form_no_validate: Bypasses form validation when submitting (for type="submit" buttons) + form_target: Specifies where to display the response after submitting the form (for type="submit" buttons) + name: Name of the button, used when sending form data + type: Type of the button (submit, reset, or button) + value: Value of the button, used when sending form data + access_key: Provides a hint for generating a keyboard shortcut for the current element. + auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. + content_editable: Indicates whether the element's content is editable. + context_menu: Defines the ID of a element which will serve as the element's context menu. + dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left) + draggable: Defines whether the element can be dragged. + enter_key_hint: Hints what media types the media element is able to play. + hidden: Defines whether the element is hidden. + input_mode: Defines the type of the element. + item_prop: Defines the name of the element for metadata purposes. + lang: Defines the language used in the element. + role: Defines the role of the element. + slot: Assigns a slot in a shadow DOM shadow tree to an element. + spell_check: Defines whether the element may be checked for spelling errors. + tab_index: Defines the position of the current element in the tabbing order. + title: Defines a tooltip for the element. + translate: Specifies whether the content of an element should be translated or not. + 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 + _rename_props: props to change the name of + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The button component. + """ + ... + +class ColorModeNamespace(BaseVar): + icon = staticmethod(ColorModeIcon.create) + switch = staticmethod(ColorModeSwitch.create) + button = staticmethod(ColorModeButton.create) + +color_mode_var_and_namespace = ColorModeNamespace(**dataclasses.asdict(color_mode)) diff --git a/reflex/style.py b/reflex/style.py index accd25db4..700aceb74 100644 --- a/reflex/style.py +++ b/reflex/style.py @@ -12,6 +12,9 @@ from reflex.vars import BaseVar, Var, VarData VarData.update_forward_refs() # Ensure all type definitions are resolved +LIGHT_COLOR_MODE: str = "light" +DARK_COLOR_MODE: str = "dark" + # Reference the global ColorModeContext color_mode_var_data = VarData( # type: ignore imports={