multiselect.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. """Provides a feature-rich Select and some (not all) related components."""
  2. from __future__ import annotations
  3. from typing import Any, Dict, List, Optional, Set, Union
  4. from reflex.base import Base
  5. from reflex.components.component import Component
  6. from reflex.constants import EventTriggers
  7. from reflex.vars import Var
  8. class Option(Base):
  9. """An option component for the chakra-react-select Select."""
  10. # What is displayed to the user
  11. label: str
  12. # The value of the option, must be serializable
  13. value: Any
  14. # the variant of the option tag
  15. variant: Optional[str] = None
  16. # [not working yet]
  17. # Whether the option is disabled
  18. # is_disabled: Optional[bool] = None
  19. # [not working yet]
  20. # The visual color appearance of the component
  21. # options: "whiteAlpha" | "blackAlpha" | "gray" | "red" |
  22. # "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" |
  23. # "purple" | "pink" | "linkedin" | "facebook" | "messenger" |
  24. # "whatsapp" | "twitter" | "telegram"
  25. # default: "gray"
  26. # color_scheme: Optional[str] = None
  27. # [not working yet]
  28. # The icon of the option tag
  29. # icon: Optional[str] = None
  30. class Select(Component):
  31. """The default chakra-react-select Select component.
  32. Not every available prop is listed here,
  33. for a complete overview check the react-select/chakra-react-select docs.
  34. Props added by chakra-react-select are marked with "[chakra]".
  35. """
  36. library = "chakra-react-select@4.7.5"
  37. tag = "Select"
  38. alias = "MultiSelect"
  39. # Focus the control when it is mounted
  40. auto_focus: Var[bool]
  41. # Remove the currently focused option when the user presses backspace
  42. # when Select isClearable or isMulti
  43. backspace_removes_value: Var[bool]
  44. # Remove focus from the input when the user selects an option
  45. # (handy for dismissing the keyboard on touch devices)
  46. blur_input_on_select: Var[bool]
  47. # When the user reaches the top/bottom of the menu,
  48. # prevent scroll on the scroll-parent
  49. capture_menu_scroll: Var[bool]
  50. # [chakra]
  51. # To use the chakraStyles prop, first,
  52. # check the documentation for the original styles prop from the react-select docs.
  53. # This package offers an identical API for the chakraStyles prop, however,
  54. # the provided and output style objects use Chakra's sx prop
  55. # instead of the default emotion styles the original package offers.
  56. # This allows you to both use the shorthand styling props you'd normally use
  57. # to style Chakra components, as well as tokens from your theme such as named colors.
  58. # All of the style keys offered in the original package can be used in the chakraStyles prop
  59. # except for menuPortal. Along with some other caveats, this is explained below.
  60. # Most of the components rendered by this package use the basic Chakra <Box /> component with a few exceptions.
  61. # Here are the style keys offered and the corresponding Chakra component that is rendered:
  62. # - clearIndicator - Box (uses theme styles for Chakra's CloseButton)
  63. # - container - Box
  64. # - control - Box (uses theme styles for Chakra's Input)
  65. # - dropdownIndicator - Box (uses theme styles for Chrakra's InputRightAddon)
  66. # - downChevron - Icon
  67. # - crossIcon - Icon
  68. # - group - Box
  69. # - groupHeading - Box (uses theme styles for Chakra's Menu group title)
  70. # - indicatorsContainer - Box
  71. # - indicatorSeparator - Divider
  72. # - input - chakra.input (wrapped in a Box)
  73. # - inputContainer - Box
  74. # - loadingIndicator - Spinner
  75. # - loadingMessage - Box
  76. # - menu - Box
  77. # - menuList - Box (uses theme styles for Chakra's Menu)
  78. # - multiValue - chakra.span (uses theme styles for Chakra's Tag)
  79. # - multiValueLabel - chakra.span (uses theme styles for Chakra's TagLabel)
  80. # - multiValueRemove - Box (uses theme styles for Chakra's TagCloseButton)
  81. # - noOptionsMessage - Box
  82. # - option - Box (uses theme styles for Chakra's MenuItem)
  83. # - placeholder - Box
  84. # - singleValue - Box
  85. # - valueContainer - Box
  86. chakra_styles: Var[str]
  87. # Close the select menu when the user selects an option
  88. close_menu_on_select: Var[bool]
  89. # If true, close the select menu when the user scrolls the document/body.
  90. close_menu_on_scroll: Var[bool]
  91. # [chakra]
  92. # The visual color appearance of the component
  93. # options: "whiteAlpha" | "blackAlpha" | "gray" | "red" |
  94. # "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" |
  95. # "purple" | "pink" | "linkedin" | "facebook" | "messenger" |
  96. # "whatsapp" | "twitter" | "telegram"
  97. # default: "gray"
  98. color_scheme: Var[str]
  99. # This complex object includes all the compositional components
  100. # that are used in react-select. If you wish to overwrite a component,
  101. # pass in an object with the appropriate namespace.
  102. # If you only wish to restyle a component,
  103. # we recommend using the styles prop instead.
  104. components: Var[Dict[str, Component]]
  105. # Whether the value of the select, e.g. SingleValue,
  106. # should be displayed in the control.
  107. control_should_render_value: Var[bool]
  108. # Delimiter used to join multiple values into a single HTML Input value
  109. delimiter: Var[str]
  110. # [chakra]
  111. # Colors the component border with the given chakra color string on error state
  112. # default: "red.500"
  113. error_border_color: Var[str]
  114. # Clear all values when the user presses escape AND the menu is closed
  115. escape_clears_value: Var[bool]
  116. # [chakra]
  117. # Colors the component border with the given chakra color string on focus
  118. # default: "blue.500"
  119. focus_border_color: Var[str]
  120. # Sets the form attribute on the input
  121. form: Var[str]
  122. # Hide the selected option from the menu
  123. hide_selected_options: Var[bool]
  124. # The id to set on the SelectContainer component.
  125. # id: Var[str]
  126. # The value of the search input
  127. input_value: Var[str]
  128. # The id of the search input
  129. input_id: Var[str]
  130. # Is the select value clearable
  131. is_clearable: Var[bool]
  132. # Is the select disabled
  133. is_disabled: Var[bool]
  134. # [chakra]
  135. # Style component in the chakra invalid style
  136. # default: False
  137. is_invalid: Var[bool]
  138. # Support multiple selected options
  139. is_multi: Var[bool]
  140. # [chakra]
  141. # Style component as disabled (chakra style)
  142. # default: False
  143. is_read_only: Var[bool]
  144. # Is the select direction right-to-left
  145. is_rtl: Var[bool]
  146. # Whether to enable search functionality
  147. is_searchable: Var[bool]
  148. # Minimum height of the menu before flipping
  149. min_menu_height: Var[int]
  150. # Maximum height of the menu before scrolling
  151. max_menu_height: Var[int]
  152. # Default placement of the menu in relation to the control.
  153. # 'auto' will flip when there isn't enough space below the control.
  154. # options: "bottom" | "auto" | "top"
  155. menu_placement: Var[str]
  156. # The CSS position value of the menu,
  157. # when "fixed" extra layout management is required
  158. # options: "absolute" | "fixed"
  159. menu_position: Var[str]
  160. # Whether to block scroll events when the menu is open
  161. menu_should_block_scroll: Var[bool]
  162. # Whether the menu should be scrolled into view when it opens
  163. menu_should_scroll_into_view: Var[bool]
  164. # Name of the HTML Input (optional - without this, no input will be rendered)
  165. name: Var[str]
  166. # Allows control of whether the menu is opened when the Select is focused
  167. open_menu_on_focus: Var[bool]
  168. # Allows control of whether the menu is opened when the Select is clicked
  169. open_menu_on_click: Var[bool]
  170. # Array of options that populate the select menu
  171. options: Var[List[Dict]]
  172. # Number of options to jump in menu when page{up|down} keys are used
  173. page_size: Var[int]
  174. # Placeholder for the select value
  175. placeholder: Var[Optional[str]]
  176. # Marks the value-holding input as required for form validation
  177. required: Var[bool]
  178. # [chakra]
  179. # If you choose to stick with the default selectedOptionStyle="color",
  180. # you have one additional styling option.
  181. # If you do not like the default of blue for the highlight color,
  182. # you can pass the selectedOptionColorScheme prop to change it.
  183. # This prop will accept any named color from your theme's color palette,
  184. # and it will use the 500 value in light mode or the 300 value in dark mode.
  185. # This prop can only be used for named colors from your theme, not arbitrary hex/rgb colors.
  186. # If you would like to use a specific color for the background that's not a part of your theme,
  187. # use the chakraStyles prop to customize it.
  188. # default: "blue"
  189. selected_option_color_scheme: Var[str]
  190. # [chakra]
  191. # The default option "color" will style a selected option
  192. # similar to how react-select does it,
  193. # by highlighting the selected option in the color blue.
  194. # Alternatively, if you pass "check" for the value,
  195. # the selected option will be styled like the Chakra UI Menu component
  196. # and include a check icon next to the selected option(s).
  197. # If is_multi and selected_option_style="check" are passed,
  198. # space will only be added for the check marks
  199. # if hide_selected_options=False is also passed.
  200. # options: "color" | "check"
  201. # default: "color"
  202. selected_option_style: Var[str]
  203. # [chakra]
  204. # The size of the component.
  205. # options: "sm" | "md" | "lg"
  206. # default: "md"
  207. size: Var[str]
  208. # Sets the tabIndex attribute on the input
  209. tab_index: Var[int]
  210. # Select the currently focused option when the user presses tab
  211. tab_selects_value: Var[bool]
  212. # [chakra]
  213. # Variant of multi-select tags
  214. # options: "subtle" | "solid" | "outline"
  215. # default: "subtle"
  216. tag_variant: Var[str]
  217. # Remove all non-essential styles
  218. unstyled: Var[bool]
  219. # [chakra]
  220. # If this prop is passed,
  221. # the dropdown indicator at the right of the component will be styled
  222. # in the same way the original Chakra Select component is styled,
  223. # instead of being styled as an InputRightAddon.
  224. # The original purpose of styling it as an addon
  225. # was to create a visual separation between the dropdown indicator
  226. # and the button for clearing the selected options.
  227. # However, as this button only appears when isMulti is passed,
  228. # using this style could make more sense for a single select.
  229. # default: False
  230. use_basic_style: Var[bool]
  231. # [chakra]
  232. # The variant of the Select. If no variant is passed,
  233. # it will default to defaultProps.variant from the theme for Chakra's Input component.
  234. # If your component theme for Input is not modified, it will be outline.
  235. # options: "outline" | "filled" | "flushed" | "unstyled"
  236. # default: "outline"
  237. variant: Var[str]
  238. # How the options should be displayed in the menu.
  239. menu_position: Var[str] = "fixed" # type: ignore
  240. def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
  241. """Get the event triggers that pass the component's value to the handler.
  242. Returns:
  243. A dict mapping the event trigger to the var that is passed to the handler.
  244. """
  245. return {
  246. **super().get_event_triggers(),
  247. EventTriggers.ON_CHANGE: (
  248. lambda e0: [
  249. Var.create_safe(f"{e0}.map(e => e.value)", _var_is_local=True)
  250. ]
  251. if self.is_multi.equals(Var.create_safe(True))
  252. else lambda e0: [e0]
  253. ),
  254. }
  255. @classmethod
  256. def get_initial_props(cls) -> Set[str]:
  257. """Get the initial props to set for the component.
  258. Returns:
  259. The initial props to set.
  260. """
  261. return super().get_initial_props() | {"is_multi"}
  262. @classmethod
  263. def create(
  264. cls, options: List[Union[Option, str, int, float, bool]], **props
  265. ) -> Component:
  266. """Takes a list of options and additional properties, checks if each option is an
  267. instance of Option, and returns a Select component with the given options and
  268. properties. No children allowed.
  269. Args:
  270. options (List[Option | str | int | float | bool]): A list of values.
  271. **props: Additional properties to be passed to the Select component.
  272. Returns:
  273. The `create` method is returning an instance of the `Select` class.
  274. """
  275. converted_options: List[Option] = []
  276. if not isinstance(options, Var):
  277. for option in options:
  278. if not isinstance(option, Option):
  279. converted_options.append(Option(label=str(option), value=option))
  280. else:
  281. converted_options.append(option)
  282. props["options"] = [o.dict() for o in converted_options]
  283. else:
  284. props["options"] = options
  285. return super().create(*[], **props)