Pārlūkot izejas kodu

Merge pull request #995 from zauberzeug/multi-select

let ui.select support "multiple" prop
Rodja Trappe 1 gadu atpakaļ
vecāks
revīzija
fad3b0b028

+ 31 - 7
nicegui/elements/select.py

@@ -12,11 +12,13 @@ register_component('select', __file__, 'select.js')
 
 class Select(ChoiceElement, DisableableElement):
 
-    def __init__(self, options: Union[List, Dict], *,
+    def __init__(self,
+                 options: Union[List, Dict], *,
                  label: Optional[str] = None,
                  value: Any = None,
                  on_change: Optional[Callable[..., Any]] = None,
                  with_input: bool = False,
+                 multiple: bool = False,
                  ) -> None:
         """Dropdown Selection
 
@@ -27,7 +29,15 @@ class Select(ChoiceElement, DisableableElement):
         :param value: the initial value
         :param on_change: callback to execute when selection changes
         :param with_input: whether to show an input field to filter the options
+        :param multiple: whether to allow multiple selections
         """
+        self.multiple = multiple
+        if multiple:
+            self.EVENT_ARGS = None
+            if value is None:
+                value = []
+            elif not isinstance(value, list):
+                value = [value]
         super().__init__(tag='select', options=options, value=value, on_change=on_change)
         if label is not None:
             self._props['label'] = label
@@ -37,6 +47,7 @@ class Select(ChoiceElement, DisableableElement):
             self._props['hide-selected'] = True
             self._props['fill-input'] = True
             self._props['input-debounce'] = 0
+        self._props['multiple'] = multiple
 
     def on_filter(self, event: Dict) -> None:
         self.options = [
@@ -47,11 +58,24 @@ class Select(ChoiceElement, DisableableElement):
         self.update()
 
     def _msg_to_value(self, msg: Dict) -> Any:
-        return self._values[msg['args']['value']]
+        if self.multiple:
+            return [self._values[arg['value']] for arg in msg['args']]
+        else:
+            return self._values[msg['args']['value']]
 
     def _value_to_model_value(self, value: Any) -> Any:
-        try:
-            index = self._values.index(value)
-            return {'value': index, 'label': self._labels[index]}
-        except ValueError:
-            return None
+        if self.multiple:
+            result = []
+            for item in value or []:
+                try:
+                    index = self._values.index(item)
+                    result.append({'value': index, 'label': self._labels[index]})
+                except ValueError:
+                    pass
+            return result
+        else:
+            try:
+                index = self._values.index(value)
+                return {'value': index, 'label': self._labels[index]}
+            except ValueError:
+                return None

+ 14 - 0
tests/test_select.py

@@ -48,3 +48,17 @@ def test_replace_select(screen: Screen):
     screen.click('Replace')
     screen.should_contain('B')
     screen.should_not_contain('A')
+
+
+def test_multi_select(screen: Screen):
+    s = ui.select(['Alice', 'Bob', 'Carol'], value='Alice', multiple=True).props('use-chips')
+    ui.label().bind_text_from(s, 'value', backward=str)
+
+    screen.open('/')
+    screen.should_contain("['Alice']")
+    screen.click('Alice')
+    screen.click('Bob')
+    screen.should_contain("['Alice', 'Bob']")
+
+    screen.click('cancel')  # remove icon
+    screen.should_contain("['Bob']")

+ 10 - 0
website/more_documentation/select_documentation.py

@@ -25,3 +25,13 @@ def more() -> None:
         ]
         ui.select(options=continents, with_input=True,
                   on_change=lambda e: ui.notify(e.value)).classes('w-40')
+
+    @text_demo('Multi selection', '''
+        You can activate `multiple` to allow the selection of more than one item.
+    ''')
+    def multi_select():
+        names = ['Alice', 'Bob', 'Carol']
+        ui.select(names, multiple=True, value=names[:2],  label='comma-separated') \
+            .classes('w-64')
+        ui.select(names, multiple=True, value=names[:2], label='with chips') \
+            .props('use-chips').classes('w-64')