瀏覽代碼

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.
Masen Furer 1 年之前
父節點
當前提交
aa2a1df201

+ 7 - 3
pynecone/.templates/apps/default/default.py

@@ -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%",
     )
 
 

+ 1 - 0
pynecone/__init__.py

@@ -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

+ 3 - 0
pynecone/components/__init__.py

@@ -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

+ 11 - 1
pynecone/components/forms/__init__.py

@@ -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

+ 116 - 0
pynecone/components/forms/colormodeswitch.py

@@ -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,
+        )

+ 1 - 0
pynecone/style.py

@@ -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)