select.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import re
  2. from copy import deepcopy
  3. from pathlib import Path
  4. from typing import Any, Callable, Dict, List, Optional, Union
  5. from ..dependencies import register_vue_component
  6. from ..events import GenericEventArguments
  7. from .choice_element import ChoiceElement
  8. from .mixins.disableable_element import DisableableElement
  9. register_vue_component('select', Path(__file__).parent / 'select.js')
  10. class Select(ChoiceElement, DisableableElement):
  11. def __init__(self,
  12. options: Union[List, Dict], *,
  13. label: Optional[str] = None,
  14. value: Any = None,
  15. on_change: Optional[Callable[..., Any]] = None,
  16. with_input: bool = False,
  17. multiple: bool = False,
  18. clearable: bool = False,
  19. ) -> None:
  20. """Dropdown Selection
  21. The options can be specified as a list of values, or as a dictionary mapping values to labels.
  22. After manipulating the options, call `update()` to update the options in the UI.
  23. :param options: a list ['value1', ...] or dictionary `{'value1':'label1', ...}` specifying the options
  24. :param value: the initial value
  25. :param on_change: callback to execute when selection changes
  26. :param with_input: whether to show an input field to filter the options
  27. :param multiple: whether to allow multiple selections
  28. :param clearable: whether to add a button to clear the selection
  29. """
  30. self.multiple = multiple
  31. if multiple:
  32. self.EVENT_ARGS = None
  33. if value is None:
  34. value = []
  35. elif not isinstance(value, list):
  36. value = [value]
  37. super().__init__(tag='select', options=options, value=value, on_change=on_change)
  38. self.use_component('select')
  39. if label is not None:
  40. self._props['label'] = label
  41. if with_input:
  42. self.original_options = deepcopy(options)
  43. self._props['use-input'] = True
  44. self._props['hide-selected'] = not multiple
  45. self._props['fill-input'] = True
  46. self._props['input-debounce'] = 0
  47. self._props['multiple'] = multiple
  48. self._props['clearable'] = clearable
  49. def on_filter(self, e: GenericEventArguments) -> None:
  50. self.options = [
  51. option
  52. for option in self.original_options
  53. if not e.args or re.search(e.args, option, re.IGNORECASE)
  54. ]
  55. self.update()
  56. def _msg_to_value(self, e: GenericEventArguments) -> Any:
  57. if self.multiple:
  58. if e.args is None:
  59. return []
  60. else:
  61. return [self._values[arg['value']] for arg in e.args]
  62. else:
  63. if e.args is None:
  64. return None
  65. else:
  66. return self._values[e.args['value']]
  67. def _value_to_model_value(self, value: Any) -> Any:
  68. if self.multiple:
  69. result = []
  70. for item in value or []:
  71. try:
  72. index = self._values.index(item)
  73. result.append({'value': index, 'label': self._labels[index]})
  74. except ValueError:
  75. pass
  76. return result
  77. else:
  78. try:
  79. index = self._values.index(value)
  80. return {'value': index, 'label': self._labels[index]}
  81. except ValueError:
  82. return None