Browse Source

Introduce QRating element (#4347)

This PR introduces a Rating element based on Quasars QRating
https://quasar.dev/vue-components/rating

---------

Co-authored-by: Falko Schindler <falko@zauberzeug.com>
Simon Robinson 2 months ago
parent
commit
13b63b24c6

+ 52 - 0
nicegui/elements/rating.py

@@ -0,0 +1,52 @@
+from typing import List, Optional, Union
+
+from ..events import Handler, ValueChangeEventArguments
+from .mixins.disableable_element import DisableableElement
+from .mixins.value_element import ValueElement
+
+
+class Rating(ValueElement, DisableableElement):
+
+    def __init__(self,
+                 value: Optional[float] = None,
+                 max: int = 5,  # pylint: disable=redefined-builtin
+                 icon: Optional[str] = None,
+                 icon_selected: Optional[str] = None,
+                 icon_half: Optional[str] = None,
+                 color: Optional[Union[str, List[str]]] = 'primary',
+                 size: Optional[str] = None,
+                 on_change: Optional[Handler[ValueChangeEventArguments]] = None,
+                 ) -> None:
+        """Rating
+
+        This element is based on Quasar's `QRating <https://quasar.dev/vue-components/rating>`_ component.
+
+        *Added in version 2.12.0*
+
+        :param value: initial value (default: ``None``)
+        :param max: maximum rating, number of icons (default: 5)
+        :param icon: name of icons to be displayed (default: star)
+        :param icon_selected: name of an icon to be displayed when selected (default: same as ``icon``)
+        :param icon_half: name of an icon to be displayed when half-selected (default: same as ``icon``)
+        :param color: color(s) of the icons (Quasar, Tailwind, or CSS colors or ``None``, default: "primary")
+        :param size: size in CSS units, including unit name or standard size name (xs|sm|md|lg|xl), examples: 16px, 2rem
+        :param on_change: callback to execute when selection changes
+        """
+        super().__init__(tag='q-rating', value=value, on_value_change=on_change)
+
+        self._props['max'] = max
+
+        if icon:
+            self._props['icon'] = icon
+
+        if color:
+            self._props['color'] = color
+
+        if icon_selected:
+            self._props['icon-selected'] = icon_selected
+
+        if icon_half:
+            self._props['icon-half'] = icon_half
+
+        if size:
+            self._props['size'] = size

+ 2 - 0
nicegui/ui.py

@@ -83,6 +83,7 @@ __all__ = [
     'query',
     'query',
     'radio',
     'radio',
     'range',
     'range',
+    'rating',
     'refreshable',
     'refreshable',
     'refreshable_method',
     'refreshable_method',
     'restructured_text',
     'restructured_text',
@@ -192,6 +193,7 @@ from .elements.pyplot import Pyplot as pyplot
 from .elements.query import Query as query
 from .elements.query import Query as query
 from .elements.radio import Radio as radio
 from .elements.radio import Radio as radio
 from .elements.range import Range as range  # pylint: disable=redefined-builtin
 from .elements.range import Range as range  # pylint: disable=redefined-builtin
+from .elements.rating import Rating as rating
 from .elements.restructured_text import ReStructuredText as restructured_text
 from .elements.restructured_text import ReStructuredText as restructured_text
 from .elements.row import Row as row
 from .elements.row import Row as row
 from .elements.scene import Scene as scene
 from .elements.scene import Scene as scene

+ 18 - 0
tests/test_rating.py

@@ -0,0 +1,18 @@
+from nicegui import ui
+from nicegui.testing import Screen
+
+
+def test_rating_click(screen: Screen):
+    rating = ui.rating(value=2)
+    ui.label().bind_text_from(rating, 'value', lambda x: f'Value: {x}')
+
+    screen.open('/')
+    rating_icons = screen.find_all_by_class('q-rating__icon-container')
+    rating_icons[0].click()
+    screen.should_contain('Value: 1')
+
+    rating_icons[3].click()
+    screen.should_contain('Value: 4')
+
+    rating_icons[3].click()  # already selected, should unselect
+    screen.should_contain('Value: 0')

+ 49 - 0
website/documentation/content/rating_documentation.py

@@ -0,0 +1,49 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.rating)
+def main_demo() -> None:
+    ui.rating(value=4)
+
+
+@doc.demo('Customize icons', '''
+    You can customize name and size of the icons.
+    Optionally, unselected, selected or half-selected values can have different icons.
+''')
+def customize_icons():
+    ui.rating(
+        value=3.5,
+        size='lg',
+        icon='sentiment_dissatisfied',
+        icon_selected='sentiment_satisfied',
+        icon_half='sentiment_neutral',
+    )
+    ui.rating(
+        value=3.5,
+        size='lg',
+        icon='star',
+        icon_selected='star',
+        icon_half='star_half',
+    )
+
+
+@doc.demo('Customize color', '''
+    You can customize the color of the rating either by providing a single color or a range of different colors.
+''')
+def rating_color():
+    ui.rating(value=3, color='red-10')
+    ui.rating(value=5, color=['green-2', 'green-4', 'green-6', 'green-8', 'green-10'])
+
+
+@doc.demo('Maximum rating', '''
+    This demo shows how to change the maximum possible rating
+    as well as binding the value to a slider.
+''')
+def rating_scale():
+    slider = ui.slider(value=5, min=0, max=10)
+    ui.rating(max=10, icon='circle').bind_value(slider)
+
+
+doc.reference(ui.range)

+ 2 - 0
website/documentation/content/section_controls.py

@@ -16,6 +16,7 @@ from . import (
     number_documentation,
     number_documentation,
     radio_documentation,
     radio_documentation,
     range_documentation,
     range_documentation,
+    rating_documentation,
     select_documentation,
     select_documentation,
     slider_documentation,
     slider_documentation,
     switch_documentation,
     switch_documentation,
@@ -39,6 +40,7 @@ doc.intro(checkbox_documentation)
 doc.intro(switch_documentation)
 doc.intro(switch_documentation)
 doc.intro(slider_documentation)
 doc.intro(slider_documentation)
 doc.intro(range_documentation)
 doc.intro(range_documentation)
+doc.intro(rating_documentation)
 doc.intro(joystick_documentation)
 doc.intro(joystick_documentation)
 doc.intro(input_documentation)
 doc.intro(input_documentation)
 doc.intro(textarea_documentation)
 doc.intro(textarea_documentation)