Skip to content

Commit

Permalink
feat: add use_trait_observe and use_dark_effective
Browse files Browse the repository at this point in the history
Makes it easy to observe traits, and the example application of this
is use_dark_effective.
  • Loading branch information
maartenbreddels committed Mar 11, 2024
1 parent 7577df5 commit e97313e
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 31 deletions.
29 changes: 29 additions & 0 deletions solara/hooks/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"use_unique_key",
"use_state_or_update",
"use_previous",
"use_trait_observe",
]
T = TypeVar("T")
U = TypeVar("U")
Expand Down Expand Up @@ -232,3 +233,31 @@ def assign():

solara.use_effect(assign, [value])
return ref.current


def use_trait_observe(has_trait_object, name):
"""Observe a trait on an object, and return its value.
This is useful when you want your component to be in sync with a trait
of a widget or [HasTraits object](https://traitlets.readthedocs.io/en/stable/).
When the trait changes, your component will be re-rendered.
See [use_dark_effective](/api/use_dark_effective) for an example.
"""
counter = solara.use_reactive(0)
counter.get() # make the main component depend on this counter

def connect():
def update(change):
counter.value += 1

has_trait_object.observe(update, name)

def cleanup():
has_trait_object.unobserve(update, name)

return cleanup

solara.use_effect(connect, [has_trait_object, name])
return getattr(has_trait_object, name)
2 changes: 1 addition & 1 deletion solara/lab/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from .input_date import InputDate, InputDateRange # noqa: F401
from .menu import ClickMenu, ContextMenu, Menu # noqa: F401 F403
from .tabs import Tab, Tabs # noqa: F401
from .theming import ThemeToggle, theme # noqa: F401
from .theming import ThemeToggle, theme, use_dark_effective # noqa: F401
15 changes: 15 additions & 0 deletions solara/lab/components/theming.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@
ipyvuetify.Themes.theme = cast(ipyvuetify.Themes.Theme, theme)


def use_dark_effective():
"""Return True if the frontend is using a dark theme.
Equivalent of
```python
solara.use_trait_observe(solara.lab.theme, "dark_effective")
```
See [use_trait_observe](/api/use_trait_observe).
"""
return solara.use_trait_observe(solara.lab.theme, "dark_effective")


def _set_theme(themes: Union[Dict[str, Dict[str, str]], None]):
if themes is None:
return
Expand Down
6 changes: 2 additions & 4 deletions solara/website/components/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
from solara.alias import rv
from solara.server import settings

# TODO: remove import once function is included in solara
from solara.website.pages.apps.scatter import use_dark_effective


@solara._component_vue("algolia.vue")
def Algolia(app_id: str, index_name: str, api_key: str, debug=False):
Expand All @@ -22,6 +19,7 @@ def Header(
# use routes of parent (assuming we are a child of a layout)
route_current, all_routes = solara.use_route(level=-1)
router = solara.use_router()
dark_effective = solara.lab.use_dark_effective()

# set states for menu
with solara.Column(gap="0px"):
Expand All @@ -36,7 +34,7 @@ def Header(
with solara.Button(icon=True, class_="hidden-md-and-up", on_click=lambda: on_toggle_left_menu and on_toggle_left_menu()):
rv.Icon(children=["mdi-menu"])
with solara.Link(path_or_route="/"):
solara.Image(router.root_path + f"/static/assets/images/logo{'_white' if use_dark_effective() else ''}.svg")
solara.Image(router.root_path + f"/static/assets/images/logo{'_white' if dark_effective else ''}.svg")
rv.Spacer()

if settings.search.enabled:
Expand Down
2 changes: 2 additions & 0 deletions solara/website/pages/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"use_reactive",
"use_state",
"use_state_or_update",
"use_trait_observe",
],
},
{
Expand Down Expand Up @@ -136,6 +137,7 @@
"task",
"theming",
"use_task",
"use_dark_effective",
],
},
]
Expand Down
13 changes: 13 additions & 0 deletions solara/website/pages/api/use_dark_effective.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""# use_dark_effective
"""
import solara
import solara.autorouting
import solara.lab
from solara.website.utils import apidoc

from . import NoPage

title = "use_dark_effective"
Page = NoPage
__doc__ += apidoc(solara.lab.use_dark_effective) # type: ignore
13 changes: 13 additions & 0 deletions solara/website/pages/api/use_trait_observe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""# use_trait_observe
"""
import solara
import solara.autorouting
import solara.lab
from solara.website.utils import apidoc

from . import NoPage

title = "use_trait_observe"
Page = NoPage
__doc__ += apidoc(solara.use_trait_observe) # type: ignore
28 changes: 2 additions & 26 deletions solara/website/pages/apps/scatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,34 +49,10 @@ def reset():
State.df.value = None


def use_trait_observe(has_trait_object, name):
# TODO: this hook should go into solara
counter = solara.use_reactive(0)
counter.get() # make the main component depend on this counter

def connect():
def update(change):
counter.value += 1

has_trait_object.observe(update, name)

def cleanup():
has_trait_object.unobserve(update, name)

return cleanup

solara.use_effect(connect, [])
return getattr(has_trait_object, name)


def use_dark_effective():
return use_trait_observe(solara.lab.theme, "dark_effective")


@solara.component
def Page():
df = State.df.value
dark_effective = use_dark_effective()
dark_effective = solara.lab.use_dark_effective()

# the .scatter will set this cross filter
filter, _set_filter = solara.use_cross_filter(id(df))
Expand Down Expand Up @@ -142,5 +118,5 @@ def get_data():
@solara.component
def Layout(children):
route, routes = solara.use_route()
dark_effective = use_dark_effective()
dark_effective = solara.lab.use_dark_effective()
return solara.AppLayout(children=children, toolbar_dark=dark_effective, color=None) # if dark_effective else "primary")

0 comments on commit e97313e

Please sign in to comment.