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: def index() -> pc.Component:
return pc.center( return pc.fragment(
pc.color_mode_button(pc.color_mode_icon(), float="right"),
pc.vstack( pc.vstack(
pc.heading("Welcome to Pynecone!", font_size="2em"), pc.heading("Welcome to Pynecone!", font_size="2em"),
pc.box("Get started by editing ", pc.code(filename, font_size="1em")), pc.box("Get started by editing ", pc.code(filename, font_size="1em")),
@ -25,13 +26,16 @@ def index() -> pc.Component:
padding="0.5em", padding="0.5em",
border_radius="0.5em", border_radius="0.5em",
_hover={ _hover={
"color": "rgb(107,99,246)", "color": pc.color_mode_cond(
light="rgb(107,99,246)",
dark="rgb(179, 175, 255)",
)
}, },
), ),
spacing="1.5em", spacing="1.5em",
font_size="2em", 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 .route import route as route
from .state import ComputedVar as var from .state import ComputedVar as var
from .state import State as State 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 .style import toggle_color_mode as toggle_color_mode
from .vars import Var as Var from .vars import Var as Var
from .vars import cached_var as cached_var from .vars import cached_var as cached_var

View File

@ -238,3 +238,6 @@ text = Text.create
script = ScriptTag.create script = ScriptTag.create
aspect_ratio = AspectRatio.create aspect_ratio = AspectRatio.create
kbd = KeyboardKey.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 .button import Button, ButtonGroup
from .checkbox import Checkbox, CheckboxGroup from .checkbox import Checkbox, CheckboxGroup
from .colormodeswitch import (
ColorModeButton,
ColorModeIcon,
ColorModeSwitch,
color_mode_cond,
)
from .copytoclipboard import CopyToClipboard from .copytoclipboard import CopyToClipboard
from .date_picker import DatePicker from .date_picker import DatePicker
from .date_time_picker import DateTimePicker from .date_time_picker import DateTimePicker
@ -34,4 +40,8 @@ from .switch import Switch
from .textarea import TextArea from .textarea import TextArea
from .upload import Upload 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.utils import format
from pynecone.vars import BaseVar, Var 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) toggle_color_mode = BaseVar(name=constants.TOGGLE_COLOR_MODE, type_=EventChain)