menu.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. """Menu components."""
  2. from __future__ import annotations
  3. from typing import Any, List, Optional, Union
  4. from reflex.components.component import Component
  5. from reflex.components.forms.button import Button
  6. from reflex.components.libs.chakra import (
  7. ChakraComponent,
  8. LiteralChakraDirection,
  9. LiteralMenuOption,
  10. LiteralMenuStrategy,
  11. )
  12. from reflex.vars import Var
  13. class Menu(ChakraComponent):
  14. """The wrapper component provides context, state, and focus management."""
  15. tag = "Menu"
  16. # The padding required to prevent the arrow from reaching the very edge of the popper.
  17. arrow_padding: Var[int]
  18. # If true, the first enabled menu item will receive focus and be selected when the menu opens.
  19. auto_select: Var[bool]
  20. # The boundary area for the popper. Used within the preventOverflow modifier
  21. boundary: Var[str]
  22. # If true, the menu will close when you click outside the menu list
  23. close_on_blur: Var[bool]
  24. # If true, the menu will close when a menu item is clicked
  25. close_on_select: Var[bool]
  26. # If by default the menu is open.
  27. default_is_open: Var[bool]
  28. # If rtl, popper placement positions will be flipped i.e. 'top-right' will become 'top-left' and vice-verse ("ltr" | "rtl")
  29. direction: Var[LiteralChakraDirection]
  30. # If true, the popper will change its placement and flip when it's about to overflow its boundary area.
  31. flip: Var[bool]
  32. # The distance or margin between the reference and popper. It is used internally to create an offset modifier. NB: If you define offset prop, it'll override the gutter.
  33. gutter: Var[int]
  34. # Performance 🚀: If true, the MenuItem rendering will be deferred until the menu is open.
  35. is_lazy: Var[bool]
  36. # Performance 🚀: The lazy behavior of menu's content when not visible. Only works when `isLazy={true}` - "unmount": The menu's content is always unmounted when not open. - "keepMounted": The menu's content initially unmounted, but stays mounted when menu is open.
  37. lazy_behavior: Var[str]
  38. # Determines if the menu is open or not.
  39. is_open: Var[bool]
  40. # If true, the popper will match the width of the reference at all times. It's useful for autocomplete, `date-picker` and select patterns.
  41. match_width: Var[bool]
  42. # The placement of the popper relative to its reference.
  43. placement: Var[str]
  44. # If true, will prevent the popper from being cut off and ensure it's visible within the boundary area.
  45. prevent_overflow: Var[bool]
  46. # The CSS positioning strategy to use. ("fixed" | "absolute")
  47. strategy: Var[LiteralMenuStrategy]
  48. def get_event_triggers(self) -> dict[str, Union[Var, Any]]:
  49. """Get the event triggers for the component.
  50. Returns:
  51. The event triggers.
  52. """
  53. return {
  54. **super().get_event_triggers(),
  55. "on_close": lambda: [],
  56. "on_open": lambda: [],
  57. }
  58. @classmethod
  59. def create(
  60. cls,
  61. *children,
  62. button: Optional[Component] = None,
  63. items: Optional[List] = None,
  64. **props,
  65. ) -> Component:
  66. """Create a menu component.
  67. Args:
  68. *children: The children of the component.
  69. button: the button that open the menu.
  70. items (list): The items of the menu.
  71. **props: The properties of the component.
  72. Returns:
  73. The menu component.
  74. """
  75. if len(children) == 0:
  76. children = []
  77. if button:
  78. if not isinstance(button, (MenuButton, Button)):
  79. children.append(MenuButton.create(button))
  80. else:
  81. children.append(button)
  82. if not items:
  83. items = []
  84. children.append(MenuList.create(items=items))
  85. return super().create(*children, **props)
  86. class MenuButton(ChakraComponent):
  87. """The trigger for the menu list. Must be a direct child of Menu."""
  88. tag = "MenuButton"
  89. # The variant of the menu button.
  90. variant: Var[str]
  91. # Components that are not allowed as children.
  92. invalid_children: List[str] = ["Button", "MenuButton"]
  93. # The tag to use for the menu button.
  94. as_: Var[str]
  95. class MenuList(ChakraComponent):
  96. """The wrapper for the menu items. Must be a direct child of Menu."""
  97. tag = "MenuList"
  98. @classmethod
  99. def create(
  100. cls, *children, items: Optional[list] = None, **props
  101. ) -> ChakraComponent:
  102. """Create a MenuList component, and automatically wrap in MenuItem if not already one.
  103. Args:
  104. *children: The children of the component.
  105. items: A list of item to add as child of the component.
  106. **props: The properties of the component.
  107. Returns:
  108. The MenuList component.
  109. """
  110. if len(children) == 0:
  111. if items is None:
  112. items = []
  113. children = [
  114. child if isinstance(child, MenuItem) else MenuItem.create(child)
  115. for child in items
  116. ]
  117. return super().create(*children, **props)
  118. class MenuItem(ChakraComponent):
  119. """The trigger that handles menu selection. Must be a direct child of a MenuList."""
  120. tag = "MenuItem"
  121. # Overrides the parent menu's closeOnSelect prop.
  122. close_on_select: Var[bool]
  123. # Right-aligned label text content, useful for displaying hotkeys.
  124. command: Var[str]
  125. # The spacing between the command and menu item's label.
  126. command_spacing: Var[int]
  127. # If true, the menuitem will be disabled.
  128. is_disabled: Var[bool]
  129. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  130. is_focusable: Var[bool]
  131. class MenuItemOption(ChakraComponent):
  132. """The checkable menu item, to be used with MenuOptionGroup."""
  133. tag = "MenuItemOption"
  134. # Overrides the parent menu's closeOnSelect prop.
  135. close_on_select: Var[bool]
  136. # Right-aligned label text content, useful for displaying hotkeys.
  137. command: Var[str]
  138. # The spacing between the command and menu item's label.
  139. command_spacing: Var[int]
  140. # Determines if menu item is checked.
  141. is_checked: Var[bool]
  142. # If true, the menuitem will be disabled.
  143. is_disabled: Var[bool]
  144. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  145. is_focusable: Var[bool]
  146. # "checkbox" | "radio"
  147. type_: Var[LiteralMenuOption]
  148. # Value of the menu item.
  149. value: Var[str]
  150. class MenuGroup(ChakraComponent):
  151. """A wrapper to group related menu items."""
  152. tag = "MenuGroup"
  153. class MenuOptionGroup(ChakraComponent):
  154. """A wrapper for checkable menu items (radio and checkbox)."""
  155. tag = "MenuOptionGroup"
  156. # "checkbox" | "radio"
  157. type_: Var[LiteralMenuOption]
  158. # Value of the option group.
  159. value: Var[str]
  160. class MenuDivider(ChakraComponent):
  161. """A visual separator for menu items and groups."""
  162. tag = "MenuDivider"