Add vars and components for working with color_mode (#1132)

* `pc.color_mode`: a BaseVar that accesses colorMode on the frontend
* `pc.color_mode_cond`: a `pc.cond` wrapper that conditionally renders
  components or props based on the value of `color_mode`
* `pc.color_mode_icon`: by default "sun" if light mode, "moon" if dark mode
* `pc.color_mode_switch`: a Switch component where is_checked depends on the
  color_mode and changing the value calls toggle_color_mode
* `pc.color_mode_button`: a Button component that calls toggle_color_mode on click

The default template has been updated to include a color_mode_button with
color_mode_icon for toggling light/dark mode. The inline hover style has also
been updated to use color_mode_cond to show a different highlight color based
on the color_mode.
This commit is contained in:
Masen Furer 2023-06-11 23:28:33 -07:00 committed by GitHub
parent 2bc45b000c
commit aa2a1df201
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 4 deletions

View File

@ -14,7 +14,8 @@ class State(pc.State):
def index() -> pc.Component:
return pc.center(
return pc.fragment(
pc.color_mode_button(pc.color_mode_icon(), float="right"),
pc.vstack(
pc.heading("Welcome to Pynecone!", font_size="2em"),
pc.box("Get started by editing ", pc.code(filename, font_size="1em")),
@ -25,13 +26,16 @@ def index() -> pc.Component:
padding="0.5em",
border_radius="0.5em",
_hover={
"color": "rgb(107,99,246)",
"color": pc.color_mode_cond(
light="rgb(107,99,246)",
dark="rgb(179, 175, 255)",
)
},
),
spacing="1.5em",
font_size="2em",
padding_top="10%",
),
padding_top="10%",
)

View File

@ -32,6 +32,7 @@ from .model import session as session
from .route import route as route
from .state import ComputedVar as var
from .state import State as State
from .style import color_mode as color_mode
from .style import toggle_color_mode as toggle_color_mode
from .vars import Var as Var
from .vars import cached_var as cached_var

View File

@ -238,3 +238,6 @@ text = Text.create
script = ScriptTag.create
aspect_ratio = AspectRatio.create
kbd = KeyboardKey.create
color_mode_button = ColorModeButton.create
color_mode_icon = ColorModeIcon.create
color_mode_switch = ColorModeSwitch.create

View File

@ -2,6 +2,12 @@
from .button import Button, ButtonGroup
from .checkbox import Checkbox, CheckboxGroup
from .colormodeswitch import (
ColorModeButton,
ColorModeIcon,
ColorModeSwitch,
color_mode_cond,
)
from .copytoclipboard import CopyToClipboard
from .date_picker import DatePicker
from .date_time_picker import DateTimePicker
@ -34,4 +40,8 @@ from .switch import Switch
from .textarea import TextArea
from .upload import Upload
__all__ = [f for f in dir() if f[0].isupper()] # type: ignore
helpers = [
"color_mode_cond",
]
__all__ = [f for f in dir() if f[0].isupper()] + helpers # type: ignore

View File

@ -0,0 +1,116 @@
"""A switch component for toggling color_mode.
To style components based on color mode, use style props with `color_mode_cond`:
```
pc.text(
"Hover over me",
_hover={
"background": pc.color_mode_cond(
light="var(--chakra-colors-gray-200)",
dark="var(--chakra-colors-gray-700)",
),
},
)
```
"""
from __future__ import annotations
from typing import Any
from pynecone.components.component import Component
from pynecone.components.layout.cond import Cond, cond
from pynecone.components.media.icon import Icon
from pynecone.style import color_mode, toggle_color_mode
from pynecone.vars import BaseVar
from .button import Button
from .switch import Switch
DEFAULT_COLOR_MODE = "light"
DEFAULT_LIGHT_ICON = Icon.create(tag="sun")
DEFAULT_DARK_ICON = Icon.create(tag="moon")
def color_mode_cond(light: Any, dark: Any = None) -> BaseVar | 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."""
@classmethod
def create(
cls,
light_component: Component | None = None,
dark_component: Component | 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 chakra 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 != DEFAULT_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 switch component.
"""
return Button.create(
*children,
on_click=toggle_color_mode,
**props,
)

View File

@ -7,6 +7,7 @@ from pynecone.event import EventChain
from pynecone.utils import format
from pynecone.vars import BaseVar, Var
color_mode = BaseVar(name=constants.COLOR_MODE, type_="str")
toggle_color_mode = BaseVar(name=constants.TOGGLE_COLOR_MODE, type_=EventChain)