瀏覽代碼

#411 introduce ui.background as a singleton element

Falko Schindler 2 年之前
父節點
當前提交
cb58c164f0
共有 4 個文件被更改,包括 113 次插入8 次删除
  1. 13 8
      nicegui/element.py
  2. 32 0
      nicegui/elements/background.js
  3. 67 0
      nicegui/elements/background.py
  4. 1 0
      nicegui/ui.py

+ 13 - 8
nicegui/element.py

@@ -125,6 +125,16 @@ class Element(ABC, Visibility):
                 raise ValueError(f'Unknown key {key}')
                 raise ValueError(f'Unknown key {key}')
         return dict_
         return dict_
 
 
+    @staticmethod
+    def _update_classes_list(
+            classes: List[str],
+            add: Optional[str] = None, remove: Optional[str] = None, replace: Optional[str] = None) -> List[str]:
+        class_list = classes if replace is None else []
+        class_list = [c for c in class_list if c not in (remove or '').split()]
+        class_list += (add or '').split()
+        class_list += (replace or '').split()
+        return list(dict.fromkeys(class_list))  # NOTE: remove duplicates while preserving order
+
     def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \
     def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \
             -> Self:
             -> Self:
         """Apply, remove, or replace HTML classes.
         """Apply, remove, or replace HTML classes.
@@ -137,11 +147,7 @@ class Element(ABC, Visibility):
         :param remove: whitespace-delimited string of classes to remove from the element
         :param remove: whitespace-delimited string of classes to remove from the element
         :param replace: whitespace-delimited string of classes to use instead of existing ones
         :param replace: whitespace-delimited string of classes to use instead of existing ones
         """
         """
-        class_list = self._classes if replace is None else []
-        class_list = [c for c in class_list if c not in (remove or '').split()]
-        class_list += (add or '').split()
-        class_list += (replace or '').split()
-        new_classes = list(dict.fromkeys(class_list))  # NOTE: remove duplicates while preserving order
+        new_classes = self._update_classes_list(self._classes, add, remove, replace)
         if self._classes != new_classes:
         if self._classes != new_classes:
             self._classes = new_classes
             self._classes = new_classes
             self.update()
             self.update()
@@ -169,11 +175,10 @@ class Element(ABC, Visibility):
         :param add: semicolon-separated list of styles to add to the element
         :param add: semicolon-separated list of styles to add to the element
         :param remove: semicolon-separated list of styles to remove from the element
         :param remove: semicolon-separated list of styles to remove from the element
         :param replace: semicolon-separated list of styles to use instead of existing ones
         :param replace: semicolon-separated list of styles to use instead of existing ones
-         """
+        """
         style_dict = deepcopy(self._style) if replace is None else {}
         style_dict = deepcopy(self._style) if replace is None else {}
         for key in self._parse_style(remove):
         for key in self._parse_style(remove):
-            if key in style_dict:
-                del style_dict[key]
+            style_dict.pop(key, None)
         style_dict.update(self._parse_style(add))
         style_dict.update(self._parse_style(add))
         style_dict.update(self._parse_style(replace))
         style_dict.update(self._parse_style(replace))
         if self._style != style_dict:
         if self._style != style_dict:

+ 32 - 0
nicegui/elements/background.js

@@ -0,0 +1,32 @@
+export default {
+  mounted() {
+    this.add_classes(this.classes);
+    this.add_style(this.style);
+    this.add_props(this.props);
+  },
+  methods: {
+    add_classes(classes) {
+      document.body.classList.add(...classes);
+    },
+    remove_classes(classes) {
+      document.body.classList.remove(...classes);
+    },
+    add_style(style) {
+      Object.entries(style).forEach(([key, value]) => (document.body.style[key] = value));
+    },
+    remove_style(keys) {
+      keys.forEach((key) => document.body.style.removeProperty(key));
+    },
+    add_props(props) {
+      Object.entries(props).forEach(([key, value]) => document.body.setAttribute(key, value));
+    },
+    remove_props(keys) {
+      keys.forEach((key) => document.body.removeAttribute(key));
+    },
+  },
+  props: {
+    classes: Array,
+    style: Object,
+    props: Object,
+  },
+};

+ 67 - 0
nicegui/elements/background.py

@@ -0,0 +1,67 @@
+from abc import ABCMeta
+from typing import Any, Dict, Optional, Tuple
+
+from typing_extensions import Self
+
+from ..dependencies import register_component
+from ..element import Element
+
+register_component('background', __file__, 'background.js')
+
+
+class AbstractSingleton(ABCMeta):
+
+    def __init__(cls, name: str, bases: Tuple, dict: Dict[str, Any]) -> None:
+        super().__init__(name, bases, dict)
+        cls.instance = None
+
+    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
+        if cls.instance is None:
+            cls.instance = super().__call__(*args, **kwargs)
+        return cls.instance
+
+
+class Background(Element, metaclass=AbstractSingleton):
+
+    def __init__(self) -> None:
+        super().__init__('background')
+        self._props['classes'] = []
+        self._props['style'] = {}
+        self._props['props'] = {}
+
+    def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \
+            -> Self:
+        classes = self._update_classes_list(self._props['classes'], add, remove, replace)
+        new_classes = [c for c in classes if c not in self._props['classes']]
+        old_classes = [c for c in self._props['classes'] if c not in classes]
+        if new_classes:
+            self.run_method('add_classes', new_classes)
+        if old_classes:
+            self.run_method('remove_classes', old_classes)
+        self._props['classes'] = classes
+        return self
+
+    def style(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \
+            -> Self:
+        old_style = Element._parse_style(remove)
+        for key in old_style:
+            self._props['style'].pop(key, None)
+        if old_style:
+            self.run_method('remove_style', list(old_style))
+        self._props['style'].update(Element._parse_style(add))
+        self._props['style'].update(Element._parse_style(replace))
+        if self._props['style']:
+            self.run_method('add_style', self._props['style'])
+        return self
+
+    def props(self, add: Optional[str] = None, *, remove: Optional[str] = None) -> Self:
+        old_props = self._parse_props(remove)
+        for key in old_props:
+            self._props['props'].pop(key, None)
+        if old_props:
+            self.run_method('remove_props', list(old_props))
+        new_props = self._parse_props(add)
+        self._props['props'].update(new_props)
+        if self._props['props']:
+            self.run_method('add_props', self._props['props'])
+        return self

+ 1 - 0
nicegui/ui.py

@@ -3,6 +3,7 @@ import os
 from .element import Element as element
 from .element import Element as element
 from .elements.audio import Audio as audio
 from .elements.audio import Audio as audio
 from .elements.avatar import Avatar as avatar
 from .elements.avatar import Avatar as avatar
+from .elements.background import Background as background
 from .elements.badge import Badge as badge
 from .elements.badge import Badge as badge
 from .elements.button import Button as button
 from .elements.button import Button as button
 from .elements.card import Card as card
 from .elements.card import Card as card