Explorar el Código

Merge pull request #1033 from zauberzeug/stepper

Implement QStepper element
Rodja Trappe hace 1 año
padre
commit
fd0bbca92d

+ 69 - 0
nicegui/elements/stepper.py

@@ -0,0 +1,69 @@
+from __future__ import annotations
+
+from typing import Any, Callable, Optional, Union, cast
+
+from .. import globals
+from ..element import Element
+from .mixins.disableable_element import DisableableElement
+from .mixins.value_element import ValueElement
+
+
+class Stepper(ValueElement):
+
+    def __init__(self, *,
+                 value: Union[str, Step, None] = None,
+                 on_value_change: Optional[Callable[..., Any]] = None,
+                 ) -> None:
+        """Stepper
+
+        This element represents `Quasar's QStepper <https://quasar.dev/vue-components/stepper#qstepper-api>`_ component.
+        It contains individual steps.
+
+        :param value: `ui.step` or name of the step to be initially selected (default: `None` meaning the first step)
+        :param on_value_change: callback to be executed when the selected step changes
+        """
+        super().__init__(tag='q-stepper', value=value, on_value_change=on_value_change)
+
+    def _value_to_model_value(self, value: Any) -> Any:
+        return value._props['name'] if isinstance(value, Step) else value
+
+    def on_value_change(self, value: Any) -> None:
+        super().on_value_change(value)
+        names = [step._props['name'] for step in self]
+        for i, step in enumerate(self):
+            done = i < names.index(value) if value in names else False
+            step.props(f':done={done}')
+
+    def next(self) -> None:
+        self.run_method('next')
+
+    def previous(self) -> None:
+        self.run_method('previous')
+
+
+class Step(DisableableElement):
+
+    def __init__(self, name: str, title: Optional[str] = None, icon: Optional[str] = None) -> None:
+        """Step
+
+        This element represents `Quasar's QStep <https://quasar.dev/vue-components/stepper#qstep-api>`_ component.
+        It is a child of a `ui.stepper` element.
+
+        :param name: name of the step (will be the value of the `ui.stepper` element)
+        :param title: title of the step (default: `None`, meaning the same as `name`)
+        :param icon: icon of the step (default: `None`)
+        """
+        super().__init__(tag='q-step')
+        self._props['name'] = name
+        self._props['title'] = title if title is not None else name
+        if icon:
+            self._props['icon'] = icon
+        self.stepper = cast(ValueElement, globals.get_slot().parent)
+        if self.stepper.value is None:
+            self.stepper.value = name
+
+
+class StepperNavigation(Element):
+
+    def __init__(self) -> None:
+        super().__init__('q-stepper-navigation')

+ 6 - 0
nicegui/ui.py

@@ -52,6 +52,9 @@ __all__ = [
     'slider',
     'slider',
     'spinner',
     'spinner',
     'splitter',
     'splitter',
+    'step',
+    'stepper',
+    'stepper_navigation',
     'switch',
     'switch',
     'table',
     'table',
     'tab',
     'tab',
@@ -136,6 +139,9 @@ from .elements.separator import Separator as separator
 from .elements.slider import Slider as slider
 from .elements.slider import Slider as slider
 from .elements.spinner import Spinner as spinner
 from .elements.spinner import Spinner as spinner
 from .elements.splitter import Splitter as splitter
 from .elements.splitter import Splitter as splitter
+from .elements.stepper import Step as step
+from .elements.stepper import Stepper as stepper
+from .elements.stepper import StepperNavigation as stepper_navigation
 from .elements.switch import Switch as switch
 from .elements.switch import Switch as switch
 from .elements.table import Table as table
 from .elements.table import Table as table
 from .elements.tabs import Tab as tab
 from .elements.tabs import Tab as tab

+ 27 - 0
tests/test_stepper.py

@@ -0,0 +1,27 @@
+from nicegui import ui
+
+from .screen import Screen
+
+
+def test_stepper(screen: Screen):
+    with ui.stepper() as stepper:
+        with ui.step('One'):
+            ui.label('First step')
+            with ui.stepper_navigation():
+                ui.button('Next', on_click=stepper.next)
+                ui.button('Back', on_click=stepper.previous)
+        with ui.step('Two'):
+            ui.label('Second step')
+            with ui.stepper_navigation():
+                ui.button('Next', on_click=stepper.next)
+                ui.button('Back', on_click=stepper.previous)
+
+    screen.open('/')
+    screen.should_contain('First step')
+    screen.should_not_contain('Second step')
+    screen.click('Next')
+    screen.should_contain('Second step')
+    screen.should_not_contain('First step')
+    screen.click('Back')
+    screen.should_contain('First step')
+    screen.should_not_contain('Second step')

+ 1 - 0
website/documentation.py

@@ -167,6 +167,7 @@ def create_full() -> None:
     load_demo(ui.expansion)
     load_demo(ui.expansion)
     load_demo(ui.splitter)
     load_demo(ui.splitter)
     load_demo('tabs')
     load_demo('tabs')
+    load_demo(ui.stepper)
     load_demo(ui.menu)
     load_demo(ui.menu)
 
 
     @text_demo('Tooltips', '''
     @text_demo('Tooltips', '''

+ 19 - 0
website/more_documentation/stepper_documentation.py

@@ -0,0 +1,19 @@
+from nicegui import ui
+
+
+def main_demo() -> None:
+    with ui.stepper().props('vertical').classes('w-full') as stepper:
+        with ui.step('Preheat'):
+            ui.label('Preheat the oven to 350 degrees')
+            with ui.stepper_navigation():
+                ui.button('Next', on_click=stepper.next)
+        with ui.step('Ingredients'):
+            ui.label('Mix the ingredients')
+            with ui.stepper_navigation():
+                ui.button('Next', on_click=stepper.next)
+                ui.button('Back', on_click=stepper.previous).props('flat')
+        with ui.step('Bake'):
+            ui.label('Bake for 20 minutes')
+            with ui.stepper_navigation():
+                ui.button('Done', on_click=lambda: ui.notify('Yay!', type='positive'))
+                ui.button('Back', on_click=stepper.previous).props('flat')