reference_tools.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import re
  2. from typing import Callable
  3. import docutils.core
  4. from nicegui import globals, ui
  5. from nicegui.elements.markdown import apply_tailwind
  6. from .example import example
  7. SPECIAL_CHARACTERS = re.compile('[^(a-z)(A-Z)(0-9)-]')
  8. def remove_indentation(text: str) -> str:
  9. """Remove indentation from a multi-line string based on the indentation of the first line."""
  10. lines = text.splitlines()
  11. while lines and not lines[0].strip():
  12. lines.pop(0)
  13. if not lines:
  14. return ''
  15. indentation = len(lines[0]) - len(lines[0].lstrip())
  16. return '\n'.join(line[indentation:] for line in lines)
  17. def get_menu() -> ui.left_drawer:
  18. return [element for element in globals.get_client().elements.values() if isinstance(element, ui.left_drawer)][0]
  19. def heading(text: str, *, make_menu_entry: bool = True) -> None:
  20. ui.html(f'<em>{text}</em>').classes('mt-8 text-3xl font-weight-500')
  21. if make_menu_entry:
  22. with get_menu():
  23. ui.label(text).classes('font-bold mt-4')
  24. def subheading(text: str, *, make_menu_entry: bool = True) -> None:
  25. name = SPECIAL_CHARACTERS.sub('_', text).lower()
  26. target = ui.link_target(name).style('position: relative; top: -90px')
  27. with ui.row().classes('gap-2 items-center'):
  28. ui.label(text).classes('text-2xl')
  29. with ui.link(target=f'#{target.id}'):
  30. ui.icon('link', size='sm').classes('text-gray-400 hover:text-gray-800')
  31. if make_menu_entry:
  32. with get_menu() as menu:
  33. async def click():
  34. if await ui.run_javascript(f'!!document.querySelector("div.q-drawer__backdrop")'):
  35. menu.hide()
  36. ui.open(f'#{target.id}')
  37. ui.link(text, target=f'#{target.id}').props('data-close-overlay').on('click', click)
  38. class intro_example:
  39. def __init__(self, title: str, explanation: str) -> None:
  40. self.title = title
  41. self.explanation = explanation
  42. def __call__(self, f: Callable) -> Callable:
  43. subheading(self.title, make_menu_entry=False)
  44. ui.label(self.explanation)
  45. return example(None, None)(f)
  46. class element_example:
  47. def __init__(self, element_class: type) -> None:
  48. self.element_class = element_class
  49. def __call__(self, f: Callable) -> Callable:
  50. doc = self.element_class.__init__.__doc__
  51. title, documentation = doc.split('\n', 1)
  52. documentation = remove_indentation(documentation)
  53. documentation = documentation.replace('param ', '')
  54. html = docutils.core.publish_parts(documentation, writer_name='html5_polyglot')['html_body']
  55. html = apply_tailwind(html)
  56. with ui.column().classes('w-full mb-8 gap-2'):
  57. subheading(title)
  58. ui.html(html).classes('documentation bold-links arrow-links')
  59. return example(None, None)(f)