浏览代码

rewrite label documentation

Falko Schindler 1 年之前
父节点
当前提交
5458b842bf

+ 3 - 18
main.py

@@ -30,24 +30,9 @@ async def post_dark_mode(request: Request) -> None:
     app.storage.browser['dark_mode'] = (await request.json()).get('value')
     app.storage.browser['dark_mode'] = (await request.json()).get('value')
 
 
 
 
-@ui.page('/')
-def main_page_() -> None:
-    main_page.create()
-
-
-@ui.page('/documentation')
-def documentation_page() -> None:
-    documentation_pages.create_overview()
-
-
-@ui.page('/documentation/section_{name}')
-def documentation_section(name: str) -> None:
-    documentation_pages.create_section(name)
-
-
-@ui.page('/documentation/{name}')
-async def documentation_page_more(name: str) -> None:
-    await documentation_pages.create_more(name)
+ui.page('/')(main_page.create)
+ui.page('/documentation')(documentation_pages.create_overview)
+ui.page('/documentation/{name}')(documentation_pages.create_page)
 
 
 
 
 @app.get('/status')
 @app.get('/status')

+ 9 - 9
website/documentation/content/overview.py

@@ -11,15 +11,15 @@ SECTIONS: Dict[str, Section] = {
     section.name: section
     section.name: section
     for section in [
     for section in [
         text_elements,
         text_elements,
-        controls,
-        audiovisual_elements,
-        data_elements,
-        binding_properties,
-        page_layout,
-        styling_appearance,
-        action_events,
-        pages_routing,
-        configuration_deployment,
+        # controls,
+        # audiovisual_elements,
+        # data_elements,
+        # binding_properties,
+        # page_layout,
+        # styling_appearance,
+        # action_events,
+        # pages_routing,
+        # configuration_deployment,
     ]
     ]
 }
 }
 
 

+ 67 - 5
website/documentation/model.py

@@ -1,20 +1,30 @@
+from __future__ import annotations
+
 import abc
 import abc
+import re
 from dataclasses import dataclass
 from dataclasses import dataclass
 from typing import Callable, Iterator, List, Optional
 from typing import Callable, Iterator, List, Optional
 
 
+import docutils.core
+
 from nicegui.dataclasses import KWONLY_SLOTS
 from nicegui.dataclasses import KWONLY_SLOTS
+from nicegui.elements.markdown import apply_tailwind, remove_indentation
 
 
 
 
 @dataclass(**KWONLY_SLOTS)
 @dataclass(**KWONLY_SLOTS)
 class DocumentationPart:
 class DocumentationPart:
     title: Optional[str] = None
     title: Optional[str] = None
     description: Optional[str] = None
     description: Optional[str] = None
+    link: Optional[str] = None
     function: Optional[Callable] = None
     function: Optional[Callable] = None
 
 
 
 
 class Documentation(abc.ABC):
 class Documentation(abc.ABC):
+    TITLE: Optional[str] = None
 
 
-    def __init__(self) -> None:
+    def __init__(self, route: str, back_link: Optional[str] = None) -> None:
+        self.route = route
+        self.back_link = back_link
         self._content: List[DocumentationPart] = []
         self._content: List[DocumentationPart] = []
         self.content()
         self.content()
 
 
@@ -25,14 +35,31 @@ class Documentation(abc.ABC):
         """Add a markdown section to the documentation."""
         """Add a markdown section to the documentation."""
         self._content.append(DocumentationPart(title=title, description=description))
         self._content.append(DocumentationPart(title=title, description=description))
 
 
-    def add_element_demo(self, element: type) -> Callable[[Callable], Callable]:
-        """Add a demo section for an element to the documentation."""
+    def add_markdown_demo(self, title: str, description: str) -> Callable[[Callable], Callable]:
+        """Add a markdown section to the documentation."""
         def decorator(function: Callable) -> Callable:
         def decorator(function: Callable) -> Callable:
-            part = DocumentationPart(title=element.__name__, description=element.__doc__ or '', function=function)
-            self._content.append(part)
+            self._content.append(DocumentationPart(title=title, description=description, function=function))
             return function
             return function
         return decorator
         return decorator
 
 
+    def add_element_intro(self, documentation: ElementDocumentation) -> None:
+        """Add an element intro section to the documentation."""
+        self.add_main_element_demo(documentation, intro_only=True)
+
+    def add_main_element_demo(self, documentation: ElementDocumentation, *, intro_only: bool = False) -> None:
+        """Add a demo section for an element to the documentation."""
+        title, doc = documentation.element.__init__.__doc__.split('\n', 1)  # type: ignore
+        doc = remove_indentation(doc).replace('param ', '')
+        html = apply_tailwind(docutils.core.publish_parts(doc, writer_name='html5_polyglot')['html_body'])
+        if intro_only:
+            html = re.sub(r'<dl class=".* simple">.*?</dl>', '', html, flags=re.DOTALL)
+        self._content.append(DocumentationPart(
+            title=title,
+            description=html,
+            link=documentation.route if intro_only else None,
+            function=documentation.main_demo,
+        ))
+
     def add_raw_nicegui(self, function: Callable) -> Callable:
     def add_raw_nicegui(self, function: Callable) -> Callable:
         """Add a raw NiceGUI section to the documentation."""
         """Add a raw NiceGUI section to the documentation."""
         self._content.append(DocumentationPart(function=function))
         self._content.append(DocumentationPart(function=function))
@@ -41,3 +68,38 @@ class Documentation(abc.ABC):
     @abc.abstractmethod
     @abc.abstractmethod
     def content(self) -> None:
     def content(self) -> None:
         """Add documentation content here."""
         """Add documentation content here."""
+
+
+class SectionDocumentation(Documentation):
+    element_documentations: List[ElementDocumentation]
+
+    def __init_subclass__(cls, title: str) -> None:
+        cls.TITLE = title
+        cls.element_documentations = []
+        return super().__init_subclass__()
+
+    def add_element_intro(self, documentation: ElementDocumentation) -> None:
+        self.element_documentations.append(documentation)
+        super().add_element_intro(documentation)
+
+
+class ElementDocumentation(Documentation):
+    element: type
+
+    def __init_subclass__(cls, element: type) -> None:
+        cls.element = element
+        return super().__init_subclass__()
+
+    def __init__(self) -> None:
+        super().__init__(self.element.__name__.lower())
+
+    @abc.abstractmethod
+    def main_demo(self) -> None:
+        """Add a demo for the element here."""
+
+    def more_demos(self) -> None:
+        """Add more demos for the element here."""
+
+    def content(self) -> None:
+        self.add_main_element_demo(self)
+        self.more_demos()

+ 20 - 19
website/documentation/more/label_documentation.py

@@ -1,26 +1,27 @@
 from nicegui import ui
 from nicegui import ui
 
 
-from ..tools import text_demo
+from ..model import ElementDocumentation
 
 
 
 
-def main_demo() -> None:
-    ui.label('some label')
+class LabelDocumentation(ElementDocumentation, element=ui.label):
 
 
+    def main_demo(self) -> None:
+        ui.label('some label')
 
 
-def more() -> None:
-    @text_demo('Change Appearance Depending on the Content', '''
-        You can overwrite the `_handle_text_change` method to update other attributes of a label depending on its content. 
-        This technique also works for bindings as shown in the example below.
-    ''')
-    def status():
-        class status_label(ui.label):
-            def _handle_text_change(self, text: str) -> None:
-                super()._handle_text_change(text)
-                if text == 'ok':
-                    self.classes(replace='text-positive')
-                else:
-                    self.classes(replace='text-negative')
+    def more_demos(self) -> None:
+        @self.add_markdown_demo('Change Appearance Depending on the Content', '''
+            You can overwrite the `_handle_text_change` method to update other attributes of a label depending on its content. 
+            This technique also works for bindings as shown in the example below.
+        ''')
+        def status():
+            class status_label(ui.label):
+                def _handle_text_change(self, text: str) -> None:
+                    super()._handle_text_change(text)
+                    if text == 'ok':
+                        self.classes(replace='text-positive')
+                    else:
+                        self.classes(replace='text-negative')
 
 
-        model = {'status': 'error'}
-        status_label().bind_text_from(model, 'status')
-        ui.switch(on_change=lambda e: model.update(status='ok' if e.value else 'error'))
+            model = {'status': 'error'}
+            status_label().bind_text_from(model, 'status')
+            ui.switch(on_change=lambda e: model.update(status='ok' if e.value else 'error'))

+ 7 - 1
website/documentation/rendering.py

@@ -10,9 +10,15 @@ def render(documentation: Documentation) -> None:
     add_header()
     add_header()
     ui.add_head_html('<style>html {scroll-behavior: auto;}</style>')
     ui.add_head_html('<style>html {scroll-behavior: auto;}</style>')
     with ui.column().classes('w-full p-8 lg:p-16 max-w-[1250px] mx-auto'):
     with ui.column().classes('w-full p-8 lg:p-16 max-w-[1250px] mx-auto'):
+        if documentation.TITLE:
+            ui.markdown(f'# {documentation.TITLE}')
         for part in documentation:
         for part in documentation:
             if part.title:
             if part.title:
-                ui.markdown(f'### {part.title}')
+                if part.link:
+                    with ui.link(target=part.link):
+                        ui.markdown(f'### {part.title}')
+                else:
+                    ui.markdown(f'### {part.title}')
             if part.description:
             if part.description:
                 ui.markdown(part.description)
                 ui.markdown(part.description)
             if part.function:
             if part.function:

+ 8 - 0
website/documentation/sections/text_elements.py

@@ -1,5 +1,7 @@
 from nicegui import ui
 from nicegui import ui
 
 
+from ..model import SectionDocumentation
+from ..more.label_documentation import LabelDocumentation
 from ..tools import load_demo
 from ..tools import load_demo
 
 
 name = 'text_elements'
 name = 'text_elements'
@@ -17,3 +19,9 @@ def content() -> None:
     load_demo(ui.markdown)
     load_demo(ui.markdown)
     load_demo(ui.mermaid)
     load_demo(ui.mermaid)
     load_demo(ui.html)
     load_demo(ui.html)
+
+
+class TextElementsDocumentation(SectionDocumentation, title='Text Elements'):
+
+    def content(self) -> None:
+        self.add_element_intro(LabelDocumentation())

+ 22 - 1
website/documentation_pages.py

@@ -1,15 +1,29 @@
 import importlib
 import importlib
 import inspect
 import inspect
 import logging
 import logging
+from typing import Dict
 
 
 from nicegui import context, ui
 from nicegui import context, ui
 
 
 from . import documentation
 from . import documentation
 from .documentation.content.overview import Overview
 from .documentation.content.overview import Overview
+from .documentation.model import ElementDocumentation, SectionDocumentation
+from .documentation.sections.text_elements import TextElementsDocumentation
 from .header import add_head_html, add_header
 from .header import add_head_html, add_header
 from .style import section_heading, side_menu
 from .style import section_heading, side_menu
 
 
-overview = Overview()
+overview = Overview('/documentation')
+sections: Dict[str, SectionDocumentation] = {
+    d.route.split('/')[-1]: d
+    for d in [
+        TextElementsDocumentation('/documentation/section_text_elements'),
+    ]
+}
+elements: Dict[str, ElementDocumentation] = {
+    element_documentation.route.split('/')[-1]: element_documentation
+    for section in sections.values()
+    for element_documentation in section.element_documentations
+}
 
 
 
 
 def create_overview() -> None:
 def create_overview() -> None:
@@ -17,6 +31,13 @@ def create_overview() -> None:
     documentation.render(overview)
     documentation.render(overview)
 
 
 
 
+def create_page(name: str) -> None:
+    doc = elements.get(name) or sections.get(name)
+    if not doc:
+        raise ValueError(f'unknown documentation page: {name}')
+    documentation.render(doc)
+
+
 def create_section(name: str) -> None:
 def create_section(name: str) -> None:
     """Create a documentation section."""
     """Create a documentation section."""
     add_head_html()
     add_head_html()