diff --git a/src/aiidalab_qe/app/infobox.py b/src/aiidalab_qe/app/infobox.py index 5ee3d9cb9..b3f9c3b34 100644 --- a/src/aiidalab_qe/app/infobox.py +++ b/src/aiidalab_qe/app/infobox.py @@ -12,3 +12,13 @@ def __init__(self, **kwargs): self.add_class("info-box") if "custom-css" in kwargs: self.add_class(kwargs.pop("custom-css")) + + +class InAppGuide(InfoBox): + """A view for `InfoAppGuide`.""" + + def __init__(self, guide_class="", **kwargs): + """`InAppGuide` constructor.""" + super().__init__(**kwargs) + self.add_class("in-app-guide") + self.add_class(guide_class) diff --git a/src/aiidalab_qe/app/static/custom.css b/src/aiidalab_qe/app/static/custom.css index e3e3ffd44..12cb018f7 100644 --- a/src/aiidalab_qe/app/static/custom.css +++ b/src/aiidalab_qe/app/static/custom.css @@ -49,6 +49,10 @@ margin-bottom: 0.5em; } +.in-app-guide.show { + display: flex !important; +} + #loading { text-align: center; font-size: large; diff --git a/src/aiidalab_qe/app/static/custom.scss b/src/aiidalab_qe/app/static/custom.scss index 40a11a295..e03ab5ce7 100644 --- a/src/aiidalab_qe/app/static/custom.scss +++ b/src/aiidalab_qe/app/static/custom.scss @@ -54,6 +54,12 @@ } } +.in-app-guide { + &.show { + display: flex !important; + } +} + #loading { text-align: center; font-size: large; diff --git a/src/aiidalab_qe/app/static/guide.jinja b/src/aiidalab_qe/app/static/guide.jinja index 14b10f914..8fe74669e 100644 --- a/src/aiidalab_qe/app/static/guide.jinja +++ b/src/aiidalab_qe/app/static/guide.jinja @@ -38,5 +38,8 @@ For a more in-depth dive into the app's features, please refer to the how-to guides.
+ ++ Alternatively, you can select one of our in-app guides to walk through an example use-case.
diff --git a/src/aiidalab_qe/app/wrapper.py b/src/aiidalab_qe/app/wrapper.py index 8e36ebf1e..b531afaf7 100644 --- a/src/aiidalab_qe/app/wrapper.py +++ b/src/aiidalab_qe/app/wrapper.py @@ -5,7 +5,7 @@ import ipywidgets as ipw import traitlets from importlib_resources import files -from IPython.display import Image, display +from IPython.display import Image, Javascript, display from jinja2 import Environment from aiidalab_qe.app import static @@ -61,7 +61,14 @@ def enable_toggles(self) -> None: @without_triggering("about_toggle") def _on_guide_toggle(self, change: dict): """Toggle the guide section.""" - self._view.info_container.children = [self._view.guide] if change["new"] else [] + self._view.info_container.children = ( + [ + self._view.guide, + self._view.guide_selection, + ] + if change["new"] + else [] + ) self._view.info_container.layout.display = "flex" if change["new"] else "none" @without_triggering("guide_toggle") @@ -70,10 +77,29 @@ def _on_about_toggle(self, change: dict): self._view.info_container.children = [self._view.about] if change["new"] else [] self._view.info_container.layout.display = "flex" if change["new"] else "none" + def _on_guide_select(self, change: dict): + """Toggle the guide section.""" + display( + Javascript(f""" + document.querySelectorAll('.{change["old"]}').forEach((guide) => {'{'} + guide.classList.remove('show'); + {'}'}); + """) + ) + if (guide_class := change["new"]) != "none": + display( + Javascript(f""" + document.querySelectorAll('.{guide_class}').forEach((guide) => {'{'} + guide.classList.add('show'); + {'}'}); + """) + ) + def _set_event_handlers(self) -> None: """Set up event handlers.""" self._view.guide_toggle.observe(self._on_guide_toggle, "value") self._view.about_toggle.observe(self._on_about_toggle, "value") + self._view.guide_selection.observe(self._on_guide_select, "value") class AppWrapperModel(traitlets.HasTraits): @@ -134,6 +160,16 @@ def __init__(self) -> None: self.guide = ipw.HTML(env.from_string(guide_template).render()) self.about = ipw.HTML(env.from_string(about_template).render()) + self.guide_selection = ipw.RadioButtons( + options=[ + "none", + "relaxation", + "bands", + ], + description="Guides", + value="none", + ) + self.info_container = InfoBox() header = ipw.VBox(