123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- from __future__ import annotations
- import abc
- import re
- from dataclasses import dataclass
- from typing import Callable, Iterator, List, Optional
- import docutils.core
- from nicegui.dataclasses import KWONLY_SLOTS
- from nicegui.elements.markdown import apply_tailwind, remove_indentation
- from . import registry
- @dataclass(**KWONLY_SLOTS)
- class DocumentationPart:
- title: Optional[str] = None
- description: Optional[str] = None
- link: Optional[str] = None
- function: Optional[Callable] = None
- @property
- def link_target(self) -> Optional[str]:
- """Return the link target for in-page navigation."""
- return self.link.lower().replace(' ', '_') if self.link else None
- class Documentation(abc.ABC):
- title: Optional[str] = None
- description: Optional[str] = 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()
- registry.add(self)
- def __iter__(self) -> Iterator[DocumentationPart]:
- return iter(self._content)
- def add_markdown(self, title: str, description: str) -> None:
- """Add a markdown section to the documentation."""
- self._content.append(DocumentationPart(title=title, description=description))
- def add_markdown_demo(self, title: str, description: str) -> Callable[[Callable], Callable]:
- """Add a markdown section to the documentation."""
- def decorator(function: Callable) -> Callable:
- self._content.append(DocumentationPart(title=title, description=description, function=function))
- return function
- return decorator
- def add_element_intro(self, documentation: ElementDocumentation) -> None:
- """Add an element intro section to the documentation."""
- documentation.back_link = self.route
- 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:
- """Add a raw NiceGUI section to the documentation."""
- self._content.append(DocumentationPart(function=function))
- return function
- @abc.abstractmethod
- def content(self) -> None:
- """Add documentation content here."""
- class SectionDocumentation(Documentation):
- route: str
- def __init_subclass__(cls, title: str, name: str) -> None:
- cls.title = title
- cls.route = f'/documentation/section_{name}'
- return super().__init_subclass__()
- def __init__(self) -> None:
- super().__init__(self.route, back_link='/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__(f'/documentation/{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()
|