From a72568045db2c2b91d24cf6d6d8150586aba83da Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Fri, 20 Dec 2024 09:54:46 +0100 Subject: [PATCH] feat: transitions component for slick UI's Based on Vue's transitions --- solara/components/__init__.py | 1 + solara/components/_transitions.py | 49 +++++++++++++++ solara/components/transition.py | 21 +++++++ solara/components/transition.vue | 6 ++ solara/lab/__init__.py | 1 + .../examples/general/transitions.py | 63 +++++++++++++++++++ 6 files changed, 141 insertions(+) create mode 100644 solara/components/_transitions.py create mode 100644 solara/components/transition.py create mode 100644 solara/components/transition.vue create mode 100644 solara/website/pages/documentation/examples/general/transitions.py diff --git a/solara/components/__init__.py b/solara/components/__init__.py index 086006059..38cec34ae 100644 --- a/solara/components/__init__.py +++ b/solara/components/__init__.py @@ -55,6 +55,7 @@ from .switch import Switch # noqa: F401 F403 from .progress import ProgressLinear # noqa: F401 F403 from .component_vue import _component_vue, component_vue # noqa: F401 F403 +from .transition import Transition # noqa: F401 F403 import reacton.core try: diff --git a/solara/components/_transitions.py b/solara/components/_transitions.py new file mode 100644 index 000000000..a053495fa --- /dev/null +++ b/solara/components/_transitions.py @@ -0,0 +1,49 @@ +import solara + + +@solara.component +def TransitionFlip(axis: str, show_first=True, children=[], duration=0.2): + uid = solara.use_unique_key()[:6] + name = f"rotate-{axis}-{uid}" + css_code = f""" +.{name}-enter-active, .{name}-leave-active {{ + transition: all {duration}s ease-out; + backface-visibility: hidden; +}} + +.{name}-enter, .{name}-leave-to {{ + transform: rotate{axis}(90deg); +}} +.{name}-enter-to, .{name}-leave {{ + transform: rotate{axis}(0deg); +}} +""" + solara.Style(css_code) + solara.Transition(show_first=show_first, name=name, duration=duration, children=children, mode="out-in") + + +@solara.component +def TransitionSlide(axis: str = "X", show_first=True, children=[], duration=0.2, translate_enter="50px", translate_leave="-50px"): + uid = solara.use_unique_key()[:6] + name = f"slide-{axis}-{uid}" + css_code = f""" +.{name}-enter-active, .{name}-leave-active {{ + transition: all {duration}s ease-out !important; +}} + +.{name}-enter {{ + transform: translate{axis}({translate_enter}); + opacity: 0; +}} + +.{name}-leave-to {{ + transform: translate{axis}({translate_leave}); + opacity: 0; +}} +.{name}-enter-to, .{name}-leave {{ + transform: translate{axis}(0px); +}} +""" + print("css", css_code) + solara.Style(css_code) + solara.Transition(show_first=show_first, name=name, duration=duration, children=children, mode="out-in") diff --git a/solara/components/transition.py b/solara/components/transition.py new file mode 100644 index 000000000..337aeff85 --- /dev/null +++ b/solara/components/transition.py @@ -0,0 +1,21 @@ +import solara +from .component_vue import component_vue + + +@solara.component +def Transition(show_first=True, children=[], name="", mode="", duration=0.2): + """Transitions between two child elements with an animation. + + These transitions are based on Vue's transition system. + + * https://v2.vuejs.org/v2/guide/transitions + * https://vuejs.org/guide/built-ins/transition + + """ + # in Python land we like to work with seconds + return _Transition(show_first=show_first, children=children, name=name, mode=mode, duration=duration * 1000) + + +@component_vue("transition.vue") +def _Transition(show_first=True, children=[], name="", mode="", duration=200): + pass # just a dummy function diff --git a/solara/components/transition.vue b/solara/components/transition.vue new file mode 100644 index 000000000..ef05ef350 --- /dev/null +++ b/solara/components/transition.vue @@ -0,0 +1,6 @@ + diff --git a/solara/lab/__init__.py b/solara/lab/__init__.py index e0c6b94e1..56ddd6755 100644 --- a/solara/lab/__init__.py +++ b/solara/lab/__init__.py @@ -4,6 +4,7 @@ from ..lifecycle import on_kernel_start # noqa: F401 from ..tasks import task, use_task, Task, TaskResult # noqa: F401, F403 from ..toestand import computed # noqa: F401 +from ..components._transitions import TransitionFlip, TransitionSlide # noqa: F401 def __getattr__(name): diff --git a/solara/website/pages/documentation/examples/general/transitions.py b/solara/website/pages/documentation/examples/general/transitions.py new file mode 100644 index 000000000..7ec242a11 --- /dev/null +++ b/solara/website/pages/documentation/examples/general/transitions.py @@ -0,0 +1,63 @@ +# check out https://solara.dev/ for documentation +# or https://github.com/widgetti/solara/ +# And check out https://py.cafe/maartenbreddels for more examples + +import solara +import solara.lab + + +clicks = solara.reactive(0) + + +@solara.component +def Page(): + color = "green" + if clicks.value >= 5: + color = "red" + + def increment(): + clicks.value += 1 + + solara.Button(label=f"Clicked: {clicks}", on_click=increment, color=color) + + with solara.lab.TransitionFlip("X", show_first=(clicks.value % 2) == 0, duration=0.2): + with solara.Card("Even") as el1: + solara.Text("This number is even") + with solara.Card("Odd") as el2: + solara.Text("This number is even") + + with solara.Card("List"): + TodoItem("Write", True) + TodoItem("Read", False) + + RemovableCard() + RemovableCard() + solara.v.use_event(el1, "click", lambda *_ignore: increment()) + solara.v.use_event(el2, "click", lambda *_ignore: increment()) + + +@solara.component +def RemovableCard(): + show = solara.use_reactive(True) + with solara.lab.TransitionSlide("X", show_first=show.value, duration=0.5, translate_leave="100px"): + with solara.Card("Some report"): + with solara.Column(): + solara.Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + ) + solara.Markdown("*Close the card to see it animate away*") + with solara.CardActions(): + solara.v.Spacer() + solara.Button("Close", on_click=lambda: show.set(False), text=True) + + +@solara.component +def TodoItem(text, default_value): + done = solara.use_reactive(default_value) + + with solara.Row(style={"overflow": "hidden"}): + solara.Switch(label=text, value=done) + solara.v.Spacer() + + with solara.lab.TransitionSlide("X", show_first=done.value, duration=0.2): + solara.v.Icon(children=["mdi-check"], color="success")