Falko Schindler 1 rok temu
rodzic
commit
95de7d8330

+ 17 - 15
nicegui/elements/tabs.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 from typing import Any, Callable, Optional, Union
 
 from .. import globals
@@ -8,7 +10,7 @@ from .mixins.value_element import ValueElement
 class Tabs(ValueElement):
 
     def __init__(self, *,
-                 value: Any = None,
+                 value: Union[Tab, TabPanel, None] = None,
                  on_change: Optional[Callable[..., Any]] = None,
                  ) -> None:
         """Tabs
@@ -16,15 +18,13 @@ class Tabs(ValueElement):
         This element represents `Quasar's QTabs <https://quasar.dev/vue-components/tabs#qtabs-api>`_ component.
         It contains individual tabs.
 
-        :param value: name of the tab to be initially selected
+        :param value: `ui.tab`, `ui.tab_panel`, or name of the tab to be initially selected
         :param on_change: callback to be executed when the selected tab changes
         """
         super().__init__(tag='q-tabs', value=value, on_value_change=on_change)
 
-    def on_value_change(self, value: Any) -> None:
-        if isinstance(value, Tab):
-            value = value._props['name']
-        super().on_value_change(value)
+    def _value_to_model_value(self, value: Any) -> Any:
+        return value._props['name'] if isinstance(value, Tab) or isinstance(value, TabPanel) else value
 
 
 class Tab(DisableableElement):
@@ -33,9 +33,9 @@ class Tab(DisableableElement):
         """Tab
 
         This element represents `Quasar's QTab <https://quasar.dev/vue-components/tabs#qtab-api>`_ component.
-        It is a child of a `Tabs` element.
+        It is a child of a `ui.tabs` element.
 
-        :param name: name of the tab (the value of the `Tabs` element)
+        :param name: name of the tab (will be the value of the `ui.tabs` element)
         :param label: label of the tab (default: `None`, meaning the same as `name`)
         :param icon: icon of the tab (default: `None`)
         """
@@ -51,7 +51,7 @@ class TabPanels(ValueElement):
 
     def __init__(self,
                  tabs: Tabs, *,
-                 value: Any = None,
+                 value: Union[Tab, TabPanel, None] = None,
                  on_change: Optional[Callable[..., Any]] = None,
                  animated: bool = True,
                  ) -> None:
@@ -60,16 +60,18 @@ class TabPanels(ValueElement):
         This element represents `Quasar's QTabPanels <https://quasar.dev/vue-components/tab-panels#qtabpanels-api>`_ component.
         It contains individual tab panels.
 
-        :param tabs: the `Tabs` element that controls this element
-        :param value: name of the tab panel to be initially visible
+        :param tabs: the `ui.tabs` element that controls this element
+        :param value: `ui.tab`, `ui.tab_panel`, or name of the tab panel to be initially visible
         :param on_change: callback to be executed when the visible tab panel changes
         :param animated: whether the tab panels should be animated (default: `True`)
         """
-        value_str = value._props['name'] if isinstance(value, Tab) else value
-        super().__init__(tag='q-tab-panels', value=value_str, on_value_change=on_change)
+        super().__init__(tag='q-tab-panels', value=value, on_value_change=on_change)
         tabs.bind_value(self, 'value')
         self._props['animated'] = animated
 
+    def _value_to_model_value(self, value: Any) -> Any:
+        return value._props['name'] if isinstance(value, Tab) or isinstance(value, TabPanel) else value
+
 
 class TabPanel(DisableableElement):
 
@@ -79,7 +81,7 @@ class TabPanel(DisableableElement):
         This element represents `Quasar's QTabPanel <https://quasar.dev/vue-components/tab-panels#qtabpanel-api>`_ component.
         It is a child of a `TabPanels` element.
 
-        :param name: a `ui.tab` object or the name of a `ui.tab` element as str
+        :param name: `ui.tab` or the name of a tab element
         """
         super().__init__(tag='q-tab-panel')
-        self._props['name'] = name if isinstance(name, str) else name._props['name']
+        self._props['name'] = name._props['name'] if isinstance(name, Tab) else name

+ 2 - 3
tests/test_tabs.py

@@ -8,7 +8,7 @@ def test_with_strings(screen: Screen):
         ui.tab('One')
         ui.tab('Two')
 
-    with ui.tab_panels(tabs, value='One') as panels:
+    with ui.tab_panels(tabs, value='One'):
         with ui.tab_panel('One'):
             ui.label('First tab')
         with ui.tab_panel('Two'):
@@ -34,7 +34,6 @@ def test_with_tab_objects(screen: Screen):
     screen.open('/')
     screen.should_contain('One')
     screen.should_contain('Two')
-    # TODO initial value selects tab but does not show its content
-    # screen.should_contain('Second tab')
+    screen.should_contain('Second tab')
     screen.click('One')
     screen.should_contain('First tab')

+ 1 - 2
website/demo.py

@@ -17,7 +17,6 @@ BROWSER_COLOR = '#ffffff'
 
 
 uncomment_pattern = re.compile(r'^(\s*)# ?')
-docstring_pattern = re.compile(r'\"\"\".*?\"\"\"', flags=re.DOTALL)
 
 
 def uncomment(text: str) -> str:
@@ -27,7 +26,7 @@ def uncomment(text: str) -> str:
 
 def demo(f: Callable) -> Callable:
     with ui.column().classes('w-full items-stretch gap-8 no-wrap min-[1500px]:flex-row'):
-        code = docstring_pattern.sub('', inspect.getsource(f)).split('# END OF DEMO')[0].strip().splitlines()
+        code = inspect.getsource(f).split('# END OF DEMO')[0].strip().splitlines()
         code = [line for line in code if not line.endswith("# HIDE")]
         while not code[0].strip().startswith('def') and not code[0].strip().startswith('async def'):
             del code[0]

+ 1 - 1
website/documentation.py

@@ -166,7 +166,7 @@ def create_full() -> None:
 
     load_demo(ui.expansion)
     load_demo(ui.splitter)
-    load_demo(ui.tabs)
+    load_demo('tabs')
     load_demo(ui.menu)
 
     @text_demo('Tooltips', '''

+ 8 - 7
website/more_documentation/tabs_documentation.py

@@ -6,13 +6,13 @@ from ..documentation_tools import text_demo
 def main_demo() -> None:
     """Tabs
 
-        The elements `ui.tabs`, `ui.tab`, `ui.tab_panels`, and `ui.tab_panel` resemble
-        `Quasar's tabs <https://quasar.dev/vue-components/tabs>`_
-        and `tab panels <https://quasar.dev/vue-components/tab-panels>`_ API.
+    The elements `ui.tabs`, `ui.tab`, `ui.tab_panels`, and `ui.tab_panel` resemble
+    `Quasar's tabs <https://quasar.dev/vue-components/tabs>`_
+    and `tab panels <https://quasar.dev/vue-components/tab-panels>`_ API.
 
-        `ui.tabs` creates a container for the tabs. This could be placed in a `ui.header` for example.
-        `ui.tab_panels` creates a container for the tab panels with the actual content.
-        Each `ui.tab_panel` is associated with a `ui.tab` element.
+    `ui.tabs` creates a container for the tabs. This could be placed in a `ui.header` for example.
+    `ui.tab_panels` creates a container for the tab panels with the actual content.
+    Each `ui.tab_panel` is associated with a `ui.tab` element.
     """
     with ui.tabs().classes('w-full') as tabs:
         one = ui.tab('One')
@@ -47,7 +47,8 @@ def more() -> None:
     def switch_tabs():
         content = {'Tab 1': 'Content 1', 'Tab 2': 'Content 2', 'Tab 3': 'Content 3'}
         with ui.tabs() as tabs:
-            [ui.tab(title) for title in content.keys()]
+            for title in content:
+                ui.tab(title)
         with ui.tab_panels(tabs).classes('w-full') as panels:
             for title, text in content.items():
                 with ui.tab_panel(title):