浏览代码

Adds scoped slots feature.

Dominique CLAUSE 2 年之前
父节点
当前提交
84c680bc2a
共有 5 个文件被更改,包括 42 次插入10 次删除
  1. 17 0
      examples/slots/main.py
  2. 1 0
      main.py
  3. 4 3
      nicegui/element.py
  4. 3 2
      nicegui/slot.py
  5. 17 5
      nicegui/templates/index.html

+ 17 - 0
examples/slots/main.py

@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+from nicegui import ui
+
+with ui.tree([
+    {'id': 'numbers', 'icon': 'tag', 'children': [{'id': '1'}, {'id': '2'}]},
+    {'id': 'letters', 'icon': 'text_fields', 'children': [{'id': 'A'}, {'id': 'B'}]},
+], label_key='id', on_select=lambda e: ui.notify(e.value)) as tree:
+    tree.add_slot('default-header', '''
+        <div class="row items-center">
+            <q-icon :name="props.node.icon || 'share'" color="orange" size="28px" class="q-mr-sm" />
+            <div class="text-weight-bold text-primary">{{ props.node.id }}</div>
+        </div>
+    ''')
+    with tree.add_slot('default-body'):
+        ui.label('This is some default content.').classes('ml-8 text-weight-light text-black')
+
+ui.run()

+ 1 - 0
main.py

@@ -244,6 +244,7 @@ The command searches for `main.py` in in your current directory and makes the ap
             example_link('Search as you type', 'using public API of thecocktaildb.com to search for cocktails')
             example_link('Menu and Tabs', 'uses Quasar to create foldable menu and tabs inside a header bar')
             example_link('Trello Cards', 'shows Trello-like cards that can be dragged and dropped into columns')
+            example_link('Slots', 'shows how to use scoped slots to customize Quasar elements')
 
     with ui.row().classes('bg-primary w-full min-h-screen mt-16'):
         link_target('why')

+ 4 - 3
nicegui/element.py

@@ -54,13 +54,14 @@ class Element(ABC, Visibility):
         if self.parent_slot:
             outbox.enqueue_update(self.parent_slot.parent)
 
-    def add_slot(self, name: str) -> Slot:
+    def add_slot(self, name: str, template: Optional[str] = None) -> Slot:
         """Add a slot to the element.
 
         :param name: name of the slot
+        :param template: vueJS template of the slot
         :return: the slot
         """
-        self.slots[name] = Slot(self, name)
+        self.slots[name] = Slot(self, name, template)
         return self.slots[name]
 
     def __enter__(self) -> Self:
@@ -90,7 +91,7 @@ class Element(ABC, Visibility):
         return events
 
     def _collect_slot_dict(self) -> Dict[str, List[int]]:
-        return {name: [child.id for child in slot.children] for name, slot in self.slots.items()}
+        return {name: {'template': slot.template, 'ids': [child.id for child in slot.children]} for name, slot in self.slots.items()}
 
     def _to_dict(self, *keys: str) -> Dict:
         if not keys:

+ 3 - 2
nicegui/slot.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, List
+from typing import Optional, TYPE_CHECKING, List
 
 from . import globals
 
@@ -8,10 +8,11 @@ if TYPE_CHECKING:
 
 class Slot:
 
-    def __init__(self, parent: 'Element', name: str) -> None:
+    def __init__(self, parent: 'Element', name: str, template: Optional[str] = None) -> None:
         self.name = name
         self.parent = parent
         self.children: List['Element'] = []
+        self.template: str = template
 
     def __enter__(self):
         globals.get_slot_stack().append(self)

+ 17 - 5
nicegui/templates/index.html

@@ -70,12 +70,24 @@
           props[event_name] = handler;
         });
         const slots = {};
-        Object.entries(element.slots).forEach(([name, ids]) => {
-          const children = ids.map(id => renderRecursively(elements, id));
-          if (name == 'default' && element.text) {
-            children.unshift(element.text);
+        Object.entries(element.slots).forEach(([name, data]) => {
+          slots[name] = (props) => {
+            const rendered = [];
+            if (data.template) {
+              rendered.push(Vue.h({
+                props: { props: { type: Object, default: {}} },
+                template: data.template,
+              }, {
+                props: props
+              }))
+            }
+            const ids = ('ids' in data) ? data.ids : data;
+            const children = ids.map(id => renderRecursively(elements, id));
+            if (name === 'default' && element.text) {
+              children.unshift(element.text);
+            }
+            return [ ...rendered, ...children]
           }
-          slots[name] = () => children;
         });
         return Vue.h(Vue.resolveComponent(element.tag), props, slots);
       }