page_layout.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from typing import Literal, Optional
  2. from . import globals # pylint: disable=redefined-builtin
  3. from .element import Element
  4. from .elements.mixins.value_element import ValueElement
  5. from .functions.html import add_body_html
  6. DrawerSides = Literal['left', 'right']
  7. PageStickyPositions = Literal[
  8. 'top-right',
  9. 'top-left',
  10. 'bottom-right',
  11. 'bottom-left',
  12. 'top',
  13. 'right',
  14. 'bottom',
  15. 'left',
  16. ]
  17. class Header(ValueElement):
  18. def __init__(self, *,
  19. value: bool = True,
  20. fixed: bool = True,
  21. bordered: bool = False,
  22. elevated: bool = False,
  23. add_scroll_padding: bool = False, # DEPRECATED: will be True in v1.4
  24. ) -> None:
  25. """Header
  26. This element is based on Quasar's `QHeader <https://quasar.dev/layout/header-and-footer#qheader-api>`_ component.
  27. Note: The header is automatically placed above other layout elements in the DOM to improve accessibility.
  28. To change the order, use the `move` method.
  29. :param value: whether the header is already opened (default: `True`)
  30. :param fixed: whether the header should be fixed to the top of the page (default: `True`)
  31. :param bordered: whether the header should have a border (default: `False`)
  32. :param elevated: whether the header should have a shadow (default: `False`)
  33. :param add_scroll_padding: whether to automatically prevent link targets from being hidden behind the header (default: `False`, will be `True` in v1.4)
  34. """
  35. with globals.get_client().layout:
  36. super().__init__(tag='q-header', value=value, on_value_change=None)
  37. self._classes = ['nicegui-header']
  38. self._props['bordered'] = bordered
  39. self._props['elevated'] = elevated
  40. code = list(self.client.layout._props['view'])
  41. code[1] = 'H' if fixed else 'h'
  42. self.client.layout._props['view'] = ''.join(code)
  43. self.move(target_index=0)
  44. if add_scroll_padding:
  45. add_body_html(f'''
  46. <script>
  47. window.onload = () => {{
  48. const header = getElement({self.id}).$el;
  49. new ResizeObserver(() => {{
  50. document.documentElement.style.scrollPaddingTop = `${{header.offsetHeight}}px`;
  51. }}).observe(header);
  52. }};
  53. </script>
  54. ''')
  55. def toggle(self):
  56. """Toggle the header"""
  57. self.value = not self.value
  58. def show(self):
  59. """Show the header"""
  60. self.value = True
  61. def hide(self):
  62. """Hide the header"""
  63. self.value = False
  64. class Drawer(Element):
  65. def __init__(self,
  66. side: DrawerSides, *,
  67. value: Optional[bool] = None,
  68. fixed: bool = True,
  69. bordered: bool = False,
  70. elevated: bool = False,
  71. top_corner: bool = False,
  72. bottom_corner: bool = False) -> None:
  73. """Drawer
  74. This element is based on Quasar's `QDrawer <https://quasar.dev/layout/drawer>`_ component.
  75. Note: Depending on the side, the drawer is automatically placed above or below the main page container in the DOM to improve accessibility.
  76. To change the order, use the `move` method.
  77. :param side: side of the page where the drawer should be placed (`left` or `right`)
  78. :param value: whether the drawer is already opened (default: `None`, i.e. if layout width is above threshold)
  79. :param fixed: whether the drawer is fixed or scrolls with the content (default: `True`)
  80. :param bordered: whether the drawer should have a border (default: `False`)
  81. :param elevated: whether the drawer should have a shadow (default: `False`)
  82. :param top_corner: whether the drawer expands into the top corner (default: `False`)
  83. :param bottom_corner: whether the drawer expands into the bottom corner (default: `False`)
  84. """
  85. with globals.get_client().layout:
  86. super().__init__('q-drawer')
  87. if value is None:
  88. self._props['show-if-above'] = True
  89. else:
  90. self._props['model-value'] = value
  91. self._props['side'] = side
  92. self._props['bordered'] = bordered
  93. self._props['elevated'] = elevated
  94. self._classes = ['nicegui-drawer']
  95. code = list(self.client.layout._props['view'])
  96. code[0 if side == 'left' else 2] = side[0].lower() if top_corner else 'h'
  97. code[4 if side == 'left' else 6] = side[0].upper() if fixed else side[0].lower()
  98. code[8 if side == 'left' else 10] = side[0].lower() if bottom_corner else 'f'
  99. self.client.layout._props['view'] = ''.join(code)
  100. page_container_index = self.client.layout.default_slot.children.index(self.client.page_container)
  101. self.move(target_index=page_container_index if side == 'left' else page_container_index + 1)
  102. def toggle(self) -> None:
  103. """Toggle the drawer"""
  104. self.run_method('toggle')
  105. def show(self) -> None:
  106. """Show the drawer"""
  107. self.run_method('show')
  108. def hide(self) -> None:
  109. """Hide the drawer"""
  110. self.run_method('hide')
  111. class LeftDrawer(Drawer):
  112. def __init__(self, *,
  113. value: Optional[bool] = None,
  114. fixed: bool = True,
  115. bordered: bool = False,
  116. elevated: bool = False,
  117. top_corner: bool = False,
  118. bottom_corner: bool = False) -> None:
  119. """Left drawer
  120. This element is based on Quasar's `QDrawer <https://quasar.dev/layout/drawer>`_ component.
  121. Note: The left drawer is automatically placed above the main page container in the DOM to improve accessibility.
  122. To change the order, use the `move` method.
  123. :param value: whether the drawer is already opened (default: `None`, i.e. if layout width is above threshold)
  124. :param fixed: whether the drawer is fixed or scrolls with the content (default: `True`)
  125. :param bordered: whether the drawer should have a border (default: `False`)
  126. :param elevated: whether the drawer should have a shadow (default: `False`)
  127. :param top_corner: whether the drawer expands into the top corner (default: `False`)
  128. :param bottom_corner: whether the drawer expands into the bottom corner (default: `False`)
  129. """
  130. super().__init__('left',
  131. value=value,
  132. fixed=fixed,
  133. bordered=bordered,
  134. elevated=elevated,
  135. top_corner=top_corner,
  136. bottom_corner=bottom_corner)
  137. class RightDrawer(Drawer):
  138. def __init__(self, *,
  139. value: Optional[bool] = None,
  140. fixed: bool = True,
  141. bordered: bool = False,
  142. elevated: bool = False,
  143. top_corner: bool = False,
  144. bottom_corner: bool = False) -> None:
  145. """Right drawer
  146. This element is based on Quasar's `QDrawer <https://quasar.dev/layout/drawer>`_ component.
  147. Note: The right drawer is automatically placed below the main page container in the DOM to improve accessibility.
  148. To change the order, use the `move` method.
  149. :param value: whether the drawer is already opened (default: `None`, i.e. if layout width is above threshold)
  150. :param fixed: whether the drawer is fixed or scrolls with the content (default: `True`)
  151. :param bordered: whether the drawer should have a border (default: `False`)
  152. :param elevated: whether the drawer should have a shadow (default: `False`)
  153. :param top_corner: whether the drawer expands into the top corner (default: `False`)
  154. :param bottom_corner: whether the drawer expands into the bottom corner (default: `False`)
  155. """
  156. super().__init__('right',
  157. value=value,
  158. fixed=fixed,
  159. bordered=bordered,
  160. elevated=elevated,
  161. top_corner=top_corner,
  162. bottom_corner=bottom_corner)
  163. class Footer(ValueElement):
  164. def __init__(self, *,
  165. value: bool = True,
  166. fixed: bool = True,
  167. bordered: bool = False,
  168. elevated: bool = False) -> None:
  169. """Footer
  170. This element is based on Quasar's `QFooter <https://quasar.dev/layout/header-and-footer#qfooter-api>`_ component.
  171. Note: The footer is automatically placed below other layout elements in the DOM to improve accessibility.
  172. To change the order, use the `move` method.
  173. :param value: whether the footer is already opened (default: `True`)
  174. :param fixed: whether the footer is fixed or scrolls with the content (default: `True`)
  175. :param bordered: whether the footer should have a border (default: `False`)
  176. :param elevated: whether the footer should have a shadow (default: `False`)
  177. """
  178. with globals.get_client().layout:
  179. super().__init__(tag='q-footer', value=value, on_value_change=None)
  180. self.classes('nicegui-footer')
  181. self._props['bordered'] = bordered
  182. self._props['elevated'] = elevated
  183. code = list(self.client.layout._props['view'])
  184. code[9] = 'F' if fixed else 'f'
  185. self.client.layout._props['view'] = ''.join(code)
  186. self.move(target_index=-1)
  187. def toggle(self) -> None:
  188. """Toggle the footer"""
  189. self.value = not self.value
  190. def show(self) -> None:
  191. """Show the footer"""
  192. self.value = True
  193. def hide(self) -> None:
  194. """Hide the footer"""
  195. self.value = False
  196. class PageSticky(Element):
  197. def __init__(self, position: PageStickyPositions = 'bottom-right', x_offset: float = 0, y_offset: float = 0) -> None:
  198. """Page sticky
  199. A sticky element that is always visible at the bottom of the page.
  200. :param position: position of the sticky element (default: `'bottom-right'`)
  201. :param x_offset: horizontal offset of the sticky element (default: `0`)
  202. :param y_offset: vertical offset of the sticky element (default: `0`)
  203. """
  204. super().__init__('q-page-sticky')
  205. self._props['position'] = position
  206. self._props['offset'] = [x_offset, y_offset]