select.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. """Interactive components provided by @radix-ui/themes."""
  2. from types import SimpleNamespace
  3. from typing import Any, Dict, List, Literal, Union
  4. import reflex as rx
  5. from reflex.components.component import Component
  6. from reflex.vars import Var
  7. from ..base import (
  8. LiteralAccentColor,
  9. LiteralRadius,
  10. RadixThemesComponent,
  11. )
  12. LiteralButtonSize = Literal[1, 2, 3, 4]
  13. class SelectRoot(RadixThemesComponent):
  14. """Displays a list of options for the user to pick from, triggered by a button."""
  15. tag = "Select.Root"
  16. # The size of the select: "1" | "2" | "3"
  17. size: Var[Literal["1", "2", "3"]]
  18. # The value of the select when initially rendered. Use when you do not need to control the state of the select.
  19. default_value: Var[str]
  20. # The controlled value of the select. Should be used in conjunction with on_value_change.
  21. value: Var[str]
  22. # The open state of the select when it is initially rendered. Use when you do not need to control its open state.
  23. default_open: Var[bool]
  24. # The controlled open state of the select. Must be used in conjunction with on_open_change.
  25. open: Var[bool]
  26. # The name of the select control when submitting the form.
  27. name: Var[str]
  28. # When True, prevents the user from interacting with select.
  29. disabled: Var[bool]
  30. # When True, indicates that the user must select a value before the owning form can be submitted.
  31. required: Var[bool]
  32. def get_event_triggers(self) -> Dict[str, Any]:
  33. """Get the events triggers signatures for the component.
  34. Returns:
  35. The signatures of the event triggers.
  36. """
  37. return {
  38. **super().get_event_triggers(),
  39. "on_open_change": lambda e0: [e0],
  40. "on_value_change": lambda e0: [e0],
  41. }
  42. class SelectTrigger(RadixThemesComponent):
  43. """The button that toggles the select."""
  44. tag = "Select.Trigger"
  45. # Variant of the select trigger
  46. variant: Var[Literal["classic", "surface", "soft", "ghost"]]
  47. # The color of the select trigger
  48. color_scheme: Var[LiteralAccentColor]
  49. # The radius of the select trigger
  50. radius: Var[LiteralRadius]
  51. # The placeholder of the select trigger
  52. placeholder: Var[str]
  53. class SelectContent(RadixThemesComponent):
  54. """The component that pops out when the select is open."""
  55. tag = "Select.Content"
  56. # The variant of the select content
  57. variant: Var[Literal["solid", "soft"]]
  58. # The color of the select content
  59. color_scheme: Var[LiteralAccentColor]
  60. # Whether to render the select content with higher contrast color against background
  61. high_contrast: Var[bool]
  62. # The positioning mode to use, item-aligned is the default and behaves similarly to a native MacOS menu by positioning content relative to the active item. popper positions content in the same way as our other primitives, for example Popover or DropdownMenu.
  63. position: Var[Literal["item-aligned", "popper"]]
  64. # The preferred side of the anchor to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. Only available when position is set to popper.
  65. side: Var[Literal["top", "right", "bottom", "left"]]
  66. # The distance in pixels from the anchor. Only available when position is set to popper.
  67. side_offset: Var[int]
  68. # The preferred alignment against the anchor. May change when collisions occur. Only available when position is set to popper.
  69. align: Var[Literal["start", "center", "end"]]
  70. # The vertical distance in pixels from the anchor. Only available when position is set to popper.
  71. align_offset: Var[int]
  72. def get_event_triggers(self) -> Dict[str, Any]:
  73. """Get the events triggers signatures for the component.
  74. Returns:
  75. The signatures of the event triggers.
  76. """
  77. return {
  78. **super().get_event_triggers(),
  79. "on_close_auto_focus": lambda e0: [e0],
  80. "on_escape_key_down": lambda e0: [e0],
  81. "on_pointer_down_outside": lambda e0: [e0],
  82. }
  83. class SelectGroup(RadixThemesComponent):
  84. """Used to group multiple items."""
  85. tag = "Select.Group"
  86. class SelectItem(RadixThemesComponent):
  87. """The component that contains the select items."""
  88. tag = "Select.Item"
  89. # The value given as data when submitting a form with a name.
  90. value: Var[str]
  91. # Whether the select item is disabled
  92. disabled: Var[bool]
  93. class SelectLabel(RadixThemesComponent):
  94. """Used to render the label of a group, it isn't focusable using arrow keys."""
  95. tag = "Select.Label"
  96. class SelectSeparator(RadixThemesComponent):
  97. """Used to visually separate items in the Select."""
  98. tag = "Select.Separator"
  99. class HighLevelSelect(SelectRoot):
  100. """High level wrapper for the Select component."""
  101. # The items of the select.
  102. items: Var[List[str]]
  103. # The placeholder of the select.
  104. placeholder: Var[str]
  105. # The label of the select.
  106. label: Var[str]
  107. # The color of the select.
  108. color_scheme: Var[LiteralAccentColor]
  109. # Whether to render the select with higher contrast color against background.
  110. high_contrast: Var[bool]
  111. # The variant of the select.
  112. variant: Var[Literal["classic", "surface", "soft", "ghost"]]
  113. # The radius of the select.
  114. radius: Var[LiteralRadius]
  115. # The width of the select.
  116. width: Var[str]
  117. @classmethod
  118. def create(cls, items: Union[List[str], Var[List[str]]], **props) -> Component:
  119. """Create a select component.
  120. Args:
  121. items: The items of the select.
  122. **props: Additional properties to apply to the select component.
  123. Returns:
  124. The select component.
  125. """
  126. content_props = {
  127. prop: props.pop(prop) for prop in ["high_contrast"] if prop in props
  128. }
  129. trigger_props = {
  130. prop: props.pop(prop)
  131. for prop in ["placeholder", "variant", "radius", "width"]
  132. if prop in props
  133. }
  134. color = props.pop("color", None)
  135. if color is not None:
  136. content_props["color_scheme"] = color
  137. trigger_props["color_scheme"] = color
  138. label = props.pop("label", None)
  139. if isinstance(items, Var):
  140. child = [
  141. rx.foreach(items, lambda item: SelectItem.create(item, value=item))
  142. ]
  143. else:
  144. child = [SelectItem.create(item, value=item) for item in items]
  145. return SelectRoot.create(
  146. SelectTrigger.create(
  147. **trigger_props,
  148. ),
  149. SelectContent.create(
  150. SelectGroup.create(
  151. SelectLabel.create(label) if label is not None else "",
  152. *child,
  153. ),
  154. **content_props,
  155. ),
  156. **props,
  157. )
  158. class Select(SimpleNamespace):
  159. """Select components namespace."""
  160. root = staticmethod(SelectRoot.create)
  161. trigger = staticmethod(SelectTrigger.create)
  162. content = staticmethod(SelectContent.create)
  163. group = staticmethod(SelectGroup.create)
  164. item = staticmethod(SelectItem.create)
  165. separator = staticmethod(SelectSeparator.create)
  166. label = staticmethod(SelectLabel.create)
  167. __call__ = staticmethod(HighLevelSelect.create)
  168. select = Select()