浏览代码

Merge pull request #1176 from zauberzeug/carousel

Introduce a carousel element
Rodja Trappe 1 年之前
父节点
当前提交
a3e1e96b34
共有 5 个文件被更改,包括 104 次插入0 次删除
  1. 67 0
      nicegui/elements/carousel.py
  2. 4 0
      nicegui/ui.py
  3. 21 0
      tests/test_carousel.py
  4. 1 0
      website/documentation.py
  5. 11 0
      website/more_documentation/carousel_documentation.py

+ 67 - 0
nicegui/elements/carousel.py

@@ -0,0 +1,67 @@
+from __future__ import annotations
+
+from typing import Any, Callable, Optional, Union, cast
+
+from .. import globals
+from .mixins.disableable_element import DisableableElement
+from .mixins.value_element import ValueElement
+
+
+class Carousel(ValueElement):
+
+    def __init__(self, *,
+                 value: Union[str, CarouselSlide, None] = None,
+                 on_value_change: Optional[Callable[..., Any]] = None,
+                 animated: bool = False,
+                 arrows: bool = False,
+                 navigation: bool = False,
+                 ) -> None:
+        """Carousel
+
+        This element represents `Quasar's QCarousel <https://quasar.dev/vue-components/carousel#qcarousel-api>`_ component.
+        It contains individual carousel slides.
+
+        :param value: `ui.carousel_slide` or name of the slide to be initially selected (default: `None` meaning the first slide)
+        :param on_value_change: callback to be executed when the selected slide changes
+        :param animated: whether to animate slide transitions (default: `False`)
+        :param arrows: whether to show arrows for manual slide navigation (default: `False`)
+        :param navigation: whether to show navigation dots for manual slide navigation (default: `False`)
+        """
+        super().__init__(tag='q-carousel', value=value, on_value_change=on_value_change)
+        self._props['animated'] = animated
+        self._props['arrows'] = arrows
+        self._props['navigation'] = navigation
+
+    def _value_to_model_value(self, value: Any) -> Any:
+        return value._props['name'] if isinstance(value, CarouselSlide) else value
+
+    def on_value_change(self, value: Any) -> None:
+        super().on_value_change(value)
+        names = [slide._props['name'] for slide in self]
+        for i, slide in enumerate(self):
+            done = i < names.index(value) if value in names else False
+            slide.props(f':done={done}')
+
+    def next(self) -> None:
+        self.run_method('next')
+
+    def previous(self) -> None:
+        self.run_method('previous')
+
+
+class CarouselSlide(DisableableElement):
+
+    def __init__(self, name: Optional[str] = None) -> None:
+        """Carousel Slide
+
+        This element represents `Quasar's QCarouselSlide <https://quasar.dev/vue-components/carousel#qcarouselslide-api>`_ component.
+        It is a child of a `ui.carousel` element.
+
+        :param name: name of the slide (will be the value of the `ui.carousel` element, auto-generated if `None`)
+        """
+        super().__init__(tag='q-carousel-slide')
+        self.carousel = cast(ValueElement, globals.get_slot().parent)
+        name = name or f'slide_{len(self.carousel.default_slot.children)}'
+        self._props['name'] = name
+        if self.carousel.value is None:
+            self.carousel.value = name

+ 4 - 0
nicegui/ui.py

@@ -11,6 +11,8 @@ __all__ = [
     'card',
     'card_actions',
     'card_section',
+    'carousel',
+    'carousel_slide',
     'chart',
     'chat_message',
     'checkbox',
@@ -99,6 +101,8 @@ from .elements.button import Button as button
 from .elements.card import Card as card
 from .elements.card import CardActions as card_actions
 from .elements.card import CardSection as card_section
+from .elements.carousel import Carousel as carousel
+from .elements.carousel import CarouselSlide as carousel_slide
 from .elements.chart import Chart as chart
 from .elements.chat_message import ChatMessage as chat_message
 from .elements.checkbox import Checkbox as checkbox

+ 21 - 0
tests/test_carousel.py

@@ -0,0 +1,21 @@
+from nicegui import ui
+
+from .screen import Screen
+
+
+def test_carousel(screen: Screen):
+    with ui.carousel(arrows=True).props('control-color=primary'):
+        for name in ['Alice', 'Bob', 'Carol']:
+            with ui.carousel_slide():
+                ui.label(name).classes('w-32')
+
+    screen.open('/')
+    screen.should_contain('Alice')
+    screen.click('chevron_right')
+    screen.should_contain('Bob')
+    screen.click('chevron_right')
+    screen.should_contain('Carol')
+    screen.click('chevron_left')
+    screen.should_contain('Bob')
+    screen.click('chevron_left')
+    screen.should_contain('Alice')

+ 1 - 0
website/documentation.py

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

+ 11 - 0
website/more_documentation/carousel_documentation.py

@@ -0,0 +1,11 @@
+from nicegui import ui
+
+
+def main_demo() -> None:
+    with ui.carousel(animated=True, arrows=True, navigation=True).props('height=180px'):
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/30/270/180').classes('w-[270px]')
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/31/270/180').classes('w-[270px]')
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/32/270/180').classes('w-[270px]')