forked from zauberzeug/nicegui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
example.py
158 lines (123 loc) · 6.43 KB
/
example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import contextlib
import inspect
import re
from typing import Callable, Optional, Union
import docutils.core
import isort
from nicegui import ui
from nicegui.elements.markdown import apply_tailwind
from .intersection_observer import IntersectionObserver as intersection_observer
REGEX_H4 = re.compile(r'<h4.*?>(.*?)</h4>')
SPECIAL_CHARACTERS = re.compile('[^(a-z)(A-Z)(0-9)-]')
PYTHON_BGCOLOR = '#00000010'
PYTHON_COLOR = '#eef5fb'
BASH_BGCOLOR = '#00000010'
BASH_COLOR = '#e8e8e8'
BROWSER_BGCOLOR = '#00000010'
BROWSER_COLOR = '#ffffff'
def remove_prefix(text: str, prefix: str) -> str:
return text[len(prefix):] if text.startswith(prefix) else text
class example:
def __init__(self,
content: Union[Callable, type, str],
menu: Optional[ui.element],
browser_title: Optional[str] = None,
immediate: bool = False) -> None:
self.content = content
self.menu = menu
self.browser_title = browser_title
self.immediate = immediate
def __call__(self, f: Callable) -> Callable:
with ui.column().classes('w-full mb-8'):
if isinstance(self.content, str):
documentation = ui.markdown(self.content)
_add_markdown_anchor(documentation, self.menu)
else:
doc = self.content.__doc__ or self.content.__init__.__doc__
html: str = docutils.core.publish_parts(doc, writer_name='html5_polyglot')['html_body']
html = html.replace('<p>', '<h4>', 1)
html = html.replace('</p>', '</h4>', 1)
html = html.replace('param ', '')
html = apply_tailwind(html)
documentation = ui.html(html)
_add_html_anchor(documentation.classes('documentation bold-links arrow-links'), self.menu)
with ui.column().classes('w-full items-stretch gap-8 no-wrap min-[1500px]:flex-row'):
code = inspect.getsource(f).split('# END OF EXAMPLE')[0].strip().splitlines()
while not code[0].startswith(' ' * 8):
del code[0]
code = ['from nicegui import ui'] + [remove_prefix(line[8:], '# ') for line in code]
code = ['' if line == '#' else line for line in code]
if not code[-1].startswith('ui.run('):
code.append('')
code.append('ui.run()')
code = isort.code('\n'.join(code), no_sections=True, lines_after_imports=1)
with python_window(classes='w-full max-w-[44rem]'):
ui.markdown(f'```python\n{code}\n```')
with browser_window(self.browser_title,
classes='w-full max-w-[44rem] min-[1500px]:max-w-[20rem] min-h-[10rem] browser-window'):
if self.immediate:
f()
else:
intersection_observer(on_intersection=f)
return f
def _add_markdown_anchor(element: ui.markdown, menu: Optional[ui.element]) -> None:
first_line, _ = element.content.split('\n', 1)
assert first_line.startswith('#### ')
headline = first_line[5:].strip()
headline_id = SPECIAL_CHARACTERS.sub('_', headline).lower()
icon = '<span class="material-icons">link</span>'
link = f'<a href="#{headline_id}" class="hover:text-black auto-link" style="color: #ddd">{icon}</a>'
target = f'<div id="{headline_id}" style="position: relative; top: -90px"></div>'
title = f'{target}<h4>{headline} {link}</h4>'
element.content = title + '\n' + element.content.split('\n', 1)[1]
with menu or contextlib.nullcontext():
ui.link(headline, f'#{headline_id}')
def _add_html_anchor(element: ui.html, menu: Optional[ui.element]) -> None:
html = element.content
match = REGEX_H4.search(html)
if not match:
return
headline = match.groups()[0].strip()
headline_id = SPECIAL_CHARACTERS.sub('_', headline).lower()
if not headline_id:
return
icon = '<span class="material-icons">link</span>'
link = f'<a href="#{headline_id}" class="hover:text-black auto-link" style="color: #ddd">{icon}</a>'
target = f'<div id="{headline_id}" style="position: relative; top: -90px"></div>'
html = html.replace('<h4', f'{target}<h4', 1)
html = html.replace('</h4>', f' {link}</h4>', 1)
element.content = html
with menu or contextlib.nullcontext():
ui.link(headline, f'#{headline_id}')
def _window_header(bgcolor: str) -> ui.row():
return ui.row().classes(f'w-full h-8 p-2 bg-[{bgcolor}]')
def _dots() -> None:
with ui.row().classes('gap-1 relative left-[1px] top-[1px]'):
ui.icon('circle').classes('text-[13px] text-red-400')
ui.icon('circle').classes('text-[13px] text-yellow-400')
ui.icon('circle').classes('text-[13px] text-green-400')
def _title(title: str) -> None:
ui.label(title).classes('text-sm text-gray-600 absolute left-1/2 top-[6px]').style('transform: translateX(-50%)')
def _tab(name: str, color: str, bgcolor: str) -> None:
with ui.row().classes('gap-0'):
with ui.label().classes(f'w-2 h-[24px] bg-[{color}]'):
ui.label().classes(f'w-full h-full bg-[{bgcolor}] rounded-br-[6px]')
ui.label(name).classes(f'text-sm text-gray-600 px-6 py-1 h-[24px] rounded-t-[6px] bg-[{color}]')
with ui.label().classes(f'w-2 h-[24px] bg-[{color}]'):
ui.label().classes(f'w-full h-full bg-[{bgcolor}] rounded-bl-[6px]')
def window(color: str, bgcolor: str, *, title: str = '', tab: str = '', classes: str = '') -> ui.column:
with ui.card().classes(f'no-wrap bg-[{color}] rounded-xl p-0 gap-0 {classes}') \
.style('box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1)'):
with _window_header(bgcolor):
_dots()
if title:
_title(title)
if tab:
_tab(tab, color, bgcolor)
return ui.column().classes('w-full h-full overflow-auto')
def python_window(*, classes: str = '') -> ui.card:
return window(PYTHON_COLOR, PYTHON_BGCOLOR, title='main.py', classes=classes).classes('p-2 python-window')
def bash_window(*, classes: str = '') -> ui.card:
return window(BASH_COLOR, BASH_BGCOLOR, title='bash', classes=classes).classes('p-2 bash-window')
def browser_window(title: Optional[str] = None, *, classes: str = '') -> ui.card:
return window(BROWSER_COLOR, BROWSER_BGCOLOR, tab=title or 'NiceGUI', classes=classes).classes('p-4 browser-window')