Преглед на файлове

Introduce `ui.teleport` element (#3159)

* fix:add finished event

* fix tailwind classes problem

* code review

* fix pytest

* can run

* testing

* code format

* docs

* documentation

* code review

* fix disappear on auto-index page

* support UI element

* move teleport documentation to page layout section

* use existing update() method to force-update the teleport element

---------

Co-authored-by: Falko Schindler <falko@zauberzeug.com>
CrystalWindSnake преди 11 месеца
родител
ревизия
c30752af79

+ 20 - 0
nicegui/elements/teleport.js

@@ -0,0 +1,20 @@
+export default {
+  template: `<Teleport v-if="isLoaded" :to="to" :key="key"><slot></slot></Teleport>`,
+  props: {
+    to: String,
+  },
+  mounted() {
+    this.isLoaded = true;
+  },
+  data() {
+    return {
+      key: 0,
+      isLoaded: false,
+    };
+  },
+  methods: {
+    update() {
+      this.key++;
+    },
+  },
+};

+ 26 - 0
nicegui/elements/teleport.py

@@ -0,0 +1,26 @@
+from typing import Union
+
+from nicegui.element import Element
+
+
+class Teleport(Element, component='teleport.js'):
+
+    def __init__(self, to: Union[str, Element]) -> None:
+        """Teleport
+
+        An element that allows us to transmit the content from within a component to any location on the page.
+
+        :param to: NiceGUI element or CSS selector of the target element for the teleported content
+        """
+        super().__init__()
+        if isinstance(to, Element):
+            to = f'#c{to.id}'
+        self._props['to'] = to
+
+    def update(self) -> None:
+        """Force the internal content to be retransmitted to the specified location.
+
+        This method is usually called after the target container is rebuilt.
+        """
+        super().update()
+        self.run_method('update')

+ 4 - 0
nicegui/testing/screen.py

@@ -219,6 +219,10 @@ class Screen:
         """Find all elements with the given HTML tag."""
         return self.selenium.find_elements(By.TAG_NAME, name)
 
+    def find_by_css(self, selector: str) -> WebElement:
+        """Find the element with the given CSS selector."""
+        return self.selenium.find_element(By.CSS_SELECTOR, selector)
+
     def render_js_logs(self) -> str:
         """Render the browser console logs as a string."""
         console = '\n'.join(log['message'] for log in self.selenium.get_log('browser'))

+ 2 - 0
nicegui/ui.py

@@ -88,6 +88,7 @@ __all__ = [
     'tab_panel',
     'tab_panels',
     'tabs',
+    'teleport',
     'textarea',
     'time',
     'timer',
@@ -212,6 +213,7 @@ from .elements.tabs import Tab as tab
 from .elements.tabs import TabPanel as tab_panel
 from .elements.tabs import TabPanels as tab_panels
 from .elements.tabs import Tabs as tabs
+from .elements.teleport import Teleport as teleport
 from .elements.textarea import Textarea as textarea
 from .elements.time import Time as time
 from .elements.timeline import Timeline as timeline

+ 58 - 0
tests/test_teleport.py

@@ -0,0 +1,58 @@
+from typing import Optional
+
+from nicegui import ui
+from nicegui.testing import Screen
+
+
+def test_teleport(screen: Screen):
+    ui.card().classes('card')
+
+    def create_teleport():
+        with ui.teleport('.card'):
+            ui.label('Hello')
+
+    ui.button('create', on_click=create_teleport)
+
+    screen.open('/')
+    screen.click('create')
+    assert screen.find_by_css('.card > div').text == 'Hello'
+
+
+def test_teleport_with_element(screen: Screen):
+    card = ui.card().classes('card')
+
+    def create_teleport():
+        with ui.teleport(card):
+            ui.label('Hello')
+
+    ui.button('create', on_click=create_teleport)
+
+    screen.open('/')
+    screen.click('create')
+    assert screen.find_by_css('.card > div').text == 'Hello'
+
+
+def test_update(screen: Screen):
+    teleport: Optional[ui.teleport] = None
+
+    card = ui.card().classes('card')
+
+    def create_teleport():
+        nonlocal teleport
+        with ui.teleport('.card') as teleport:
+            ui.label('Hello')
+
+    ui.button('create', on_click=create_teleport)
+
+    def rebuild_card():
+        card.delete()
+        ui.card().classes('card')
+        teleport.update()  # type: ignore
+
+    ui.button('rebuild card', on_click=rebuild_card)
+
+    screen.open('/')
+    screen.click('create')
+    screen.should_contain('Hello')
+    screen.click('rebuild card')
+    assert screen.find_by_css('.card > div').text == 'Hello'

+ 2 - 0
website/documentation/content/section_page_layout.py

@@ -21,6 +21,7 @@ from . import (
     splitter_documentation,
     stepper_documentation,
     tabs_documentation,
+    teleport_documentation,
     timeline_documentation,
     tooltip_documentation,
 )
@@ -80,6 +81,7 @@ def clear_containers_demo():
     ui.button('Clear', on_click=container.clear)
 
 
+doc.intro(teleport_documentation)
 doc.intro(expansion_documentation)
 doc.intro(scroll_area_documentation)
 doc.intro(separator_documentation)

+ 17 - 0
website/documentation/content/teleport_documentation.py

@@ -0,0 +1,17 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.teleport)
+def main_demo() -> None:
+    markdown = ui.markdown('Enter your **name**!')
+
+    def inject_input():
+        with ui.teleport(f'#c{markdown.id} strong'):
+            ui.input('name').classes('inline-flex').props('dense outlined')
+
+    ui.button('inject input', on_click=inject_input)
+
+
+doc.reference(ui.teleport)