Skip to content

Commit

Permalink
Merge pull request #1176 from zauberzeug/carousel
Browse files Browse the repository at this point in the history
Introduce a carousel element
  • Loading branch information
rodja committed Jul 16, 2023
2 parents 824abac + cc674ee commit a3e1e96
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 0 deletions.
67 changes: 67 additions & 0 deletions nicegui/elements/carousel.py
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions nicegui/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
'card',
'card_actions',
'card_section',
'carousel',
'carousel_slide',
'chart',
'chat_message',
'checkbox',
Expand Down Expand Up @@ -99,6 +101,8 @@
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
Expand Down
21 changes: 21 additions & 0 deletions tests/test_carousel.py
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions website/documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ def add_face():
load_demo(ui.splitter)
load_demo('tabs')
load_demo(ui.stepper)
load_demo(ui.carousel)
load_demo(ui.menu)

@text_demo('Tooltips', '''
Expand Down
11 changes: 11 additions & 0 deletions website/more_documentation/carousel_documentation.py
Original file line number Diff line number Diff line change
@@ -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]')

2 comments on commit a3e1e96

@frankhuurman
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rodja you're so fast pushing out the awesome updates! love the carousel :)
Did you take some time to set up a way to donate to the project yet?
I've built a nice(GUI) looking webshop using this library and I'd love to support the continued development of it.

@rodja
Copy link
Member Author

@rodja rodja commented on a3e1e96 Jul 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @frankhuurman. We are on our way to build a saas solution around NiceGUI: https://nicegui.io/documentation#nicegui_on_air
It's quite new and not ready for receiving payments, but it will be done. That's gonna be a great way to support us. And spread the word. There are way too many Python developers still struggling with GUI and web.

Please sign in to comment.