menu.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. """Menu components."""
  2. from __future__ import annotations
  3. from typing import List, Optional
  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.event import EventHandler
  13. from reflex.vars import Var
  14. class Menu(ChakraComponent):
  15. """The wrapper component provides context, state, and focus management."""
  16. tag = "Menu"
  17. # The padding required to prevent the arrow from reaching the very edge of the popper.
  18. arrow_padding: Var[int]
  19. # If true, the first enabled menu item will receive focus and be selected when the menu opens.
  20. auto_select: Var[bool]
  21. # The boundary area for the popper. Used within the preventOverflow modifier
  22. boundary: Var[str]
  23. # If true, the menu will close when you click outside the menu list
  24. close_on_blur: Var[bool]
  25. # If true, the menu will close when a menu item is clicked
  26. close_on_select: Var[bool]
  27. # If by default the menu is open.
  28. default_is_open: Var[bool]
  29. # If rtl, popper placement positions will be flipped i.e. 'top-right' will become 'top-left' and vice-verse ("ltr" | "rtl")
  30. direction: Var[LiteralChakraDirection]
  31. # If true, the popper will change its placement and flip when it's about to overflow its boundary area.
  32. flip: Var[bool]
  33. # 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.
  34. gutter: Var[int]
  35. # Performance 🚀: If true, the MenuItem rendering will be deferred until the menu is open.
  36. is_lazy: Var[bool]
  37. # 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.
  38. lazy_behavior: Var[str]
  39. # Determines if the menu is open or not.
  40. is_open: Var[bool]
  41. # If true, the popper will match the width of the reference at all times. It's useful for autocomplete, `date-picker` and select patterns.
  42. match_width: Var[bool]
  43. # The placement of the popper relative to its reference.
  44. placement: Var[str]
  45. # If true, will prevent the popper from being cut off and ensure it's visible within the boundary area.
  46. prevent_overflow: Var[bool]
  47. # The CSS positioning strategy to use. ("fixed" | "absolute")
  48. strategy: Var[LiteralMenuStrategy]
  49. # Fired when the menu is closed.
  50. on_close: EventHandler[lambda: []]
  51. # Fired when the menu is opened.
  52. on_open: EventHandler[lambda: []]
  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(cls, *children, items: Optional[list] = None, **props) -> Component:
  95. """Create a MenuList component, and automatically wrap in MenuItem if not already one.
  96. Args:
  97. *children: The children of the component.
  98. items: A list of item to add as child of the component.
  99. **props: The properties of the component.
  100. Returns:
  101. The MenuList component.
  102. """
  103. if len(children) == 0:
  104. if items is None:
  105. items = []
  106. children = [
  107. child if isinstance(child, MenuItem) else MenuItem.create(child)
  108. for child in items
  109. ]
  110. return super().create(*children, **props)
  111. class MenuItem(ChakraComponent):
  112. """The trigger that handles menu selection. Must be a direct child of a MenuList."""
  113. tag = "MenuItem"
  114. # Overrides the parent menu's closeOnSelect prop.
  115. close_on_select: Var[bool]
  116. # Right-aligned label text content, useful for displaying hotkeys.
  117. command: Var[str]
  118. # The spacing between the command and menu item's label.
  119. command_spacing: Var[int]
  120. # If true, the menuitem will be disabled.
  121. is_disabled: Var[bool]
  122. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  123. is_focusable: Var[bool]
  124. class MenuItemOption(ChakraComponent):
  125. """The checkable menu item, to be used with MenuOptionGroup."""
  126. tag = "MenuItemOption"
  127. # Overrides the parent menu's closeOnSelect prop.
  128. close_on_select: Var[bool]
  129. # Right-aligned label text content, useful for displaying hotkeys.
  130. command: Var[str]
  131. # The spacing between the command and menu item's label.
  132. command_spacing: Var[int]
  133. # Determines if menu item is checked.
  134. is_checked: Var[bool]
  135. # If true, the menuitem will be disabled.
  136. is_disabled: Var[bool]
  137. # If true and the menuitem is disabled, it'll remain keyboard-focusable
  138. is_focusable: Var[bool]
  139. # "checkbox" | "radio"
  140. type_: Var[LiteralMenuOption]
  141. # Value of the menu item.
  142. value: Var[str]
  143. class MenuGroup(ChakraComponent):
  144. """A wrapper to group related menu items."""
  145. tag = "MenuGroup"
  146. class MenuOptionGroup(ChakraComponent):
  147. """A wrapper for checkable menu items (radio and checkbox)."""
  148. tag = "MenuOptionGroup"
  149. # "checkbox" | "radio"
  150. type_: Var[LiteralMenuOption]
  151. # Value of the option group.
  152. value: Var[str]
  153. class MenuDivider(ChakraComponent):
  154. """A visual separator for menu items and groups."""
  155. tag = "MenuDivider"