menu.py 6.7 KB

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