element.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. from __future__ import annotations
  2. import asyncio
  3. import justpy as jp
  4. from ..binding import BindableProperty, bind_from, bind_to
  5. from ..globals import page_stack, view_stack
  6. from ..task_logger import create_task
  7. def _handle_visibility_change(sender: Element, visible: bool) -> None:
  8. (sender.view.remove_class if visible else sender.view.set_class)('hidden')
  9. try:
  10. asyncio.get_running_loop() # NOTe only run the update if we already have an event loop
  11. create_task(sender.view.update(), name='update view after visibility changed')
  12. except RuntimeError:
  13. pass
  14. class Element:
  15. visible = BindableProperty(on_change=_handle_visibility_change)
  16. def __init__(self, view: jp.HTMLBaseComponent):
  17. self.parent_view = view_stack[-1]
  18. self.parent_view.add(view)
  19. self.view = view
  20. self.page = page_stack[-1]
  21. self.view.add_page(self.page)
  22. self.visible = True
  23. def bind_visibility_to(self, target_object, target_name, forward=lambda x: x):
  24. bind_to(self, 'visible', target_object, target_name, forward=forward)
  25. return self
  26. def bind_visibility_from(self, target_object, target_name, backward=lambda x: x, *, value=None):
  27. if value is not None:
  28. def backward(x): return x == value
  29. bind_from(self, 'visible', target_object, target_name, backward=backward)
  30. return self
  31. def bind_visibility(self, target_object, target_name, forward=lambda x: x, backward=None, *, value=None):
  32. if value is not None:
  33. def backward(x): return x == value
  34. bind_from(self, 'visible', target_object, target_name, backward=backward)
  35. bind_to(self, 'visible', target_object, target_name, forward=forward)
  36. return self
  37. def classes(self, add: str = None, *, remove: str = None, replace: str = None):
  38. '''HTML classes to modify the look of the element.
  39. Every class in the `remove` parameter will be removed from the element.
  40. Classes are seperated with a blank space.
  41. This can be helpful if the predefined classes by NiceGUI are not wanted in a particular styling.
  42. '''
  43. class_list = [] if replace is not None else self.view.classes.split()
  44. class_list = [c for c in class_list if c not in (remove or '')]
  45. class_list += (add or '').split()
  46. class_list += (replace or '').split()
  47. self.view.classes = ' '.join(class_list)
  48. return self
  49. def style(self, add: str = None, *, remove: str = None, replace: str = None):
  50. '''CSS style sheet definitions to modify the look of the element.
  51. Every style in the `remove` parameter will be removed from the element.
  52. Styles are seperated with a semicolon.
  53. This can be helpful if the predefined style sheet definitions by NiceGUI are not wanted in a particular styling.
  54. '''
  55. style_list = [] if replace is not None else self.view.style.split(';')
  56. style_list = [c for c in style_list if c not in (remove or '').split(';')]
  57. style_list += (add or '').split(';')
  58. style_list += (replace or '').split(';')
  59. self.view.style = ';'.join(style_list)
  60. return self
  61. def props(self, add: str = None, *, remove: str = None, replace: str = None):
  62. '''Quasar props https://quasar.dev/vue-components/button#design to modify the look of the element.
  63. Boolean props will automatically activated if they appear in the list of the `add` property.
  64. Props are seperated with a blank space.
  65. Every prop passed to the `remove` parameter will be removed from the element.
  66. This can be helpful if the predefined props by NiceGUI are not wanted in a particular styling.
  67. '''
  68. for prop in (remove or '').split() + (replace or '').split():
  69. setattr(self.view, prop.split('=')[0], None)
  70. for prop in (add or '').split() + (replace or '').split():
  71. if '=' in prop:
  72. setattr(self.view, *prop.split('='))
  73. else:
  74. setattr(self.view, prop, True)
  75. return self
  76. def tooltip(self, text: str, *, props: str = ''):
  77. tooltip = jp.QTooltip(text=text, temp=False)
  78. for prop in props.split():
  79. if '=' in prop:
  80. setattr(tooltip, *prop.split('='))
  81. else:
  82. setattr(tooltip, prop, True)
  83. tooltip.add_page(self.page)
  84. self.view.add(tooltip)
  85. return self