element.py 3.9 KB

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