menu.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """Menu components."""
  2. from __future__ import annotations
  3. from typing import Any, List, Optional, Union
  4. from reflex.components.chakra import (
  5. ChakraComponent,
  6. LiteralChakraDirection,
  7. LiteralMenuOption,
  8. LiteralMenuStrategy,
  9. )
  10. from reflex.components.chakra.forms.button import Button
  11. from reflex.components.component import Component
  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: Optional[Var[int]] = None
  18. # If true, the first enabled menu item will receive focus and be selected when the menu opens.
  19. auto_select: Optional[Var[bool]] = None
  20. # The boundary area for the popper. Used within the preventOverflow modifier
  21. boundary: Optional[Var[str]] = None
  22. # If true, the menu will close when you click outside the menu list
  23. close_on_blur: Optional[Var[bool]] = None
  24. # If true, the menu will close when a menu item is clicked
  25. close_on_select: Optional[Var[bool]] = None
  26. # If by default the menu is open.
  27. default_is_open: Optional[Var[bool]] = None
  28. # If rtl, popper placement positions will be flipped i.e. 'top-right' will become 'top-left' and vice-verse ("ltr" | "rtl")
  29. direction: Optional[Var[LiteralChakraDirection]] = None
  30. # If true, the popper will change its placement and flip when it's about to overflow its boundary area.
  31. flip: Optional[Var[bool]] = None
  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: Optional[Var[int]] = None
  34. # Performance 🚀: If true, the MenuItem rendering will be deferred until the menu is open.
  35. is_lazy: Optional[Var[bool]] = None
  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: Optional[Var[str]] = None
  38. # Determines if the menu is open or not.
  39. is_open: Optional[Var[bool]] = None
  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: Optional[Var[bool]] = None
  42. # The placement of the popper relative to its reference.
  43. placement: Optional[Var[str]] = None
  44. # If true, will prevent the popper from being cut off and ensure it's visible within the boundary area.
  45. prevent_overflow: Optional[Var[bool]] = None
  46. # The CSS positioning strategy to use. ("fixed" | "absolute")
  47. strategy: Optional[Var[LiteralMenuStrategy]] = None
  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: Optional[Var[str]] = None
  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_: Optional[Var[str]] = None
  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(cls, *children, items: Optional[list] = None, **props) -> Component:
  100. """Create a MenuList component, and automatically wrap in MenuItem if not already one.
  101. Args:
  102. *children: The children of the component.
  103. items: A list of item to add as child of the component.
  104. **props: The properties of the component.
  105. Returns:
  106. The MenuList component.
  107. """
  108. if len(children) == 0:
  109. if items is None:
  110. items = []
  111. children = [
  112. child if isinstance(child, MenuItem) else MenuItem.create(child)
  113. for child in items
  114. ]
  115. return super().create(*children, **props)
  116. class MenuItem(ChakraComponent):
  117. """The trigger that handles menu selection. Must be a direct child of a MenuList."""
  118. tag = "MenuItem"
  119. # Overrides the parent menu's closeOnSelect prop.
  120. close_on_select: Optional[Var[bool]] = None
  121. # Right-aligned label text content, useful for displaying hotkeys.
  122. command: Optional[Var[str]] = None
  123. # The spacing between the command and menu item's label.
  124. command_spacing: Optional[Var[int]] = None
  125. # If true, the menuitem will be disabled.
  126. is_disabled: Optional[Var[bool]] = None
  127. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  128. is_focusable: Optional[Var[bool]] = None
  129. class MenuItemOption(ChakraComponent):
  130. """The checkable menu item, to be used with MenuOptionGroup."""
  131. tag = "MenuItemOption"
  132. # Overrides the parent menu's closeOnSelect prop.
  133. close_on_select: Optional[Var[bool]] = None
  134. # Right-aligned label text content, useful for displaying hotkeys.
  135. command: Optional[Var[str]] = None
  136. # The spacing between the command and menu item's label.
  137. command_spacing: Optional[Var[int]] = None
  138. # Determines if menu item is checked.
  139. is_checked: Optional[Var[bool]] = None
  140. # If true, the menuitem will be disabled.
  141. is_disabled: Optional[Var[bool]] = None
  142. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  143. is_focusable: Optional[Var[bool]] = None
  144. # "checkbox" | "radio"
  145. type_: Optional[Var[LiteralMenuOption]] = None
  146. # Value of the menu item.
  147. value: Optional[Var[str]] = None
  148. class MenuGroup(ChakraComponent):
  149. """A wrapper to group related menu items."""
  150. tag = "MenuGroup"
  151. class MenuOptionGroup(ChakraComponent):
  152. """A wrapper for checkable menu items (radio and checkbox)."""
  153. tag = "MenuOptionGroup"
  154. # "checkbox" | "radio"
  155. type_: Optional[Var[LiteralMenuOption]] = None
  156. # Value of the option group.
  157. value: Optional[Var[str]] = None
  158. class MenuDivider(ChakraComponent):
  159. """A visual separator for menu items and groups."""
  160. tag = "MenuDivider"