Procházet zdrojové kódy

refactoring (again)

Falko Schindler před 1 rokem
rodič
revize
cc2c5c2267
100 změnil soubory, kde provedl 1803 přidání a 1617 odebrání
  1. 2 3
      main.py
  2. 2 3
      website/documentation/__init__.py
  3. 6 5
      website/documentation/content/__init__.py
  4. 14 0
      website/documentation/content/add_static_files_documentation.py
  5. 198 0
      website/documentation/content/aggrid_documentation.py
  6. 25 0
      website/documentation/content/audio_documentation.py
  7. 20 0
      website/documentation/content/avatar_documentation.py
  8. 12 0
      website/documentation/content/badge_documentation.py
  9. 64 0
      website/documentation/content/button_documentation.py
  10. 53 0
      website/documentation/content/card_documentation.py
  11. 17 0
      website/documentation/content/carousel_documentation.py
  12. 46 0
      website/documentation/content/chat_message_documentation.py
  13. 12 0
      website/documentation/content/checkbox_documentation.py
  14. 26 0
      website/documentation/content/circular_progress_documentation.py
  15. 17 0
      website/documentation/content/code_documentation.py
  16. 13 0
      website/documentation/content/color_input_documentation.py
  17. 12 0
      website/documentation/content/color_picker_documentation.py
  18. 15 0
      website/documentation/content/colors_documentation.py
  19. 26 0
      website/documentation/content/column_documentation.py
  20. 16 0
      website/documentation/content/context_menu_documentation.py
  21. 26 0
      website/documentation/content/dark_mode_documentation.py
  22. 37 0
      website/documentation/content/date_documentation.py
  23. 51 0
      website/documentation/content/dialog_documentation.py
  24. 104 0
      website/documentation/content/doc/__init__.py
  25. 20 0
      website/documentation/content/doc/page.py
  26. 23 0
      website/documentation/content/doc/part.py
  27. 15 0
      website/documentation/content/download_documentation.py
  28. 50 0
      website/documentation/content/echart_documentation.py
  29. 13 0
      website/documentation/content/editor_documentation.py
  30. 73 0
      website/documentation/content/element_documentation.py
  31. 22 0
      website/documentation/content/expansion_documentation.py
  32. 124 0
      website/documentation/content/generic_events_documentation.py
  33. 19 0
      website/documentation/content/grid_documentation.py
  34. 76 0
      website/documentation/content/highchart_documentation.py
  35. 11 0
      website/documentation/content/html_documentation.py
  36. 34 0
      website/documentation/content/icon_documentation.py
  37. 68 0
      website/documentation/content/image_documentation.py
  38. 45 0
      website/documentation/content/input_documentation.py
  39. 41 0
      website/documentation/content/interactive_image_documentation.py
  40. 14 0
      website/documentation/content/joystick_documentation.py
  41. 26 0
      website/documentation/content/json_editor_documentation.py
  42. 31 0
      website/documentation/content/keyboard_documentation.py
  43. 14 0
      website/documentation/content/knob_documentation.py
  44. 29 0
      website/documentation/content/label_documentation.py
  45. 35 0
      website/documentation/content/line_plot_documentation.py
  46. 12 0
      website/documentation/content/linear_progress_documentation.py
  47. 59 0
      website/documentation/content/link_documentation.py
  48. 42 0
      website/documentation/content/log_documentation.py
  49. 58 0
      website/documentation/content/markdown_documentation.py
  50. 20 0
      website/documentation/content/menu_documentation.py
  51. 15 0
      website/documentation/content/mermaid_documentation.py
  52. 0 0
      website/documentation/content/more/__init__.py
  53. 0 15
      website/documentation/content/more/add_static_files_documentation.py
  54. 0 189
      website/documentation/content/more/aggrid_documentation.py
  55. 0 23
      website/documentation/content/more/audio_documentation.py
  56. 0 18
      website/documentation/content/more/avatar_documentation.py
  57. 0 10
      website/documentation/content/more/badge_documentation.py
  58. 0 60
      website/documentation/content/more/button_documentation.py
  59. 0 50
      website/documentation/content/more/card_documentation.py
  60. 0 15
      website/documentation/content/more/carousel_documentation.py
  61. 0 40
      website/documentation/content/more/chat_message_documentation.py
  62. 0 10
      website/documentation/content/more/checkbox_documentation.py
  63. 0 24
      website/documentation/content/more/circular_progress_documentation.py
  64. 0 15
      website/documentation/content/more/code_documentation.py
  65. 0 11
      website/documentation/content/more/color_input_documentation.py
  66. 0 10
      website/documentation/content/more/color_picker_documentation.py
  67. 0 13
      website/documentation/content/more/colors_documentation.py
  68. 0 24
      website/documentation/content/more/column_documentation.py
  69. 0 14
      website/documentation/content/more/context_menu_documentation.py
  70. 0 24
      website/documentation/content/more/dark_mode_documentation.py
  71. 0 34
      website/documentation/content/more/date_documentation.py
  72. 0 48
      website/documentation/content/more/dialog_documentation.py
  73. 0 16
      website/documentation/content/more/download_documentation.py
  74. 0 47
      website/documentation/content/more/echart_documentation.py
  75. 0 11
      website/documentation/content/more/editor_documentation.py
  76. 0 68
      website/documentation/content/more/element_documentation.py
  77. 0 20
      website/documentation/content/more/expansion_documentation.py
  78. 0 122
      website/documentation/content/more/generic_events_documentation.py
  79. 0 17
      website/documentation/content/more/grid_documentation.py
  80. 0 73
      website/documentation/content/more/highchart_documentation.py
  81. 0 9
      website/documentation/content/more/html_documentation.py
  82. 0 31
      website/documentation/content/more/icon_documentation.py
  83. 0 61
      website/documentation/content/more/image_documentation.py
  84. 0 42
      website/documentation/content/more/input_documentation.py
  85. 0 38
      website/documentation/content/more/interactive_image_documentation.py
  86. 0 12
      website/documentation/content/more/joystick_documentation.py
  87. 0 24
      website/documentation/content/more/json_editor_documentation.py
  88. 0 29
      website/documentation/content/more/keyboard_documentation.py
  89. 0 12
      website/documentation/content/more/knob_documentation.py
  90. 0 27
      website/documentation/content/more/label_documentation.py
  91. 0 33
      website/documentation/content/more/line_plot_documentation.py
  92. 0 10
      website/documentation/content/more/linear_progress_documentation.py
  93. 0 55
      website/documentation/content/more/link_documentation.py
  94. 0 40
      website/documentation/content/more/log_documentation.py
  95. 0 54
      website/documentation/content/more/markdown_documentation.py
  96. 0 18
      website/documentation/content/more/menu_documentation.py
  97. 0 13
      website/documentation/content/more/mermaid_documentation.py
  98. 0 33
      website/documentation/content/more/notify_documentation.py
  99. 0 30
      website/documentation/content/more/number_documentation.py
  100. 0 14
      website/documentation/content/more/open_documentation.py

+ 2 - 3
main.py

@@ -23,7 +23,6 @@ app.add_static_files('/static', str(Path(__file__).parent / 'website' / 'static'
 app.add_static_file(local_file=svg.PATH / 'logo.png', url_path='/logo.png')
 app.add_static_file(local_file=svg.PATH / 'logo_square.png', url_path='/logo_square.png')
 
-documentation.content.generate()
 documentation.build_search_index()
 
 
@@ -39,12 +38,12 @@ def _main_page() -> None:
 
 @ui.page('/documentation')
 def _documentation_page() -> None:
-    documentation.render_page(documentation.registry.get(''), is_main=True)
+    documentation.render_page(documentation.registry['overview'], is_main=True)
 
 
 @ui.page('/documentation/{name}')
 def _documentation_detail_page(name: str) -> None:
-    documentation.render_page(documentation.registry.get(name))
+    documentation.render_page(documentation.registry[name])
 
 
 @app.get('/status')

+ 2 - 3
website/documentation/__init__.py

@@ -1,4 +1,4 @@
-from . import content, registry, search
+from .content import overview, registry
 from .intro import create_intro
 from .rendering import render_page
 from .search import build_search_index
@@ -8,10 +8,9 @@ __all__ = [
     'bash_window',
     'browser_window',
     'build_search_index',
-    'content',
     'create_intro',
+    'overview',  # ensure documentation tree is built
     'python_window',
     'registry',
     'render_page',
-    'search',
 ]

+ 6 - 5
website/documentation/content/__init__.py

@@ -1,6 +1,7 @@
-from .overview import Overview
+from .doc.page import DocumentationPage
+from .doc import registry
 
-
-def generate() -> None:
-    """Generate documentation content."""
-    Overview()
+__all__ = [
+    'DocumentationPage',
+    'registry',
+]

+ 14 - 0
website/documentation/content/add_static_files_documentation.py

@@ -0,0 +1,14 @@
+from nicegui import app, ui
+
+from . import doc
+
+
+@doc.demo(app.add_static_files)
+def main_demo() -> None:
+    from nicegui import app
+
+    app.add_static_files('/examples', 'examples')
+    ui.label('Some NiceGUI Examples').classes('text-h5')
+    ui.link('AI interface', '/examples/ai_interface/main.py')
+    ui.link('Custom FastAPI app', '/examples/fastapi/main.py')
+    ui.link('Authentication', '/examples/authentication/main.py')

+ 198 - 0
website/documentation/content/aggrid_documentation.py

@@ -0,0 +1,198 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.aggrid)
+def main_demo() -> None:
+    grid = ui.aggrid({
+        'defaultColDef': {'flex': 1},
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name'},
+            {'headerName': 'Age', 'field': 'age'},
+            {'headerName': 'Parent', 'field': 'parent', 'hide': True},
+        ],
+        'rowData': [
+            {'name': 'Alice', 'age': 18, 'parent': 'David'},
+            {'name': 'Bob', 'age': 21, 'parent': 'Eve'},
+            {'name': 'Carol', 'age': 42, 'parent': 'Frank'},
+        ],
+        'rowSelection': 'multiple',
+    }).classes('max-h-40')
+
+    def update():
+        grid.options['rowData'][0]['age'] += 1
+        grid.update()
+
+    ui.button('Update', on_click=update)
+    ui.button('Select all', on_click=lambda: grid.call_api_method('selectAll'))
+    ui.button('Show parent', on_click=lambda: grid.call_column_api_method('setColumnVisible', 'parent', True))
+
+
+@doc.demo('Select AG Grid Rows', '''
+    You can add checkboxes to grid cells to allow the user to select single or multiple rows.
+
+    To retrieve the currently selected rows, use the `get_selected_rows` method.
+    This method returns a list of rows as dictionaries.
+
+    If `rowSelection` is set to `'single'` or to get the first selected row,
+    you can also use the `get_selected_row` method.
+    This method returns a single row as a dictionary or `None` if no row is selected.
+
+    See the [AG Grid documentation](https://www.ag-grid.com/javascript-data-grid/row-selection/#example-single-row-selection) for more information.
+''')
+def aggrid_with_selectable_rows():
+    grid = ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name', 'checkboxSelection': True},
+            {'headerName': 'Age', 'field': 'age'},
+        ],
+        'rowData': [
+            {'name': 'Alice', 'age': 18},
+            {'name': 'Bob', 'age': 21},
+            {'name': 'Carol', 'age': 42},
+        ],
+        'rowSelection': 'multiple',
+    }).classes('max-h-40')
+
+    async def output_selected_rows():
+        rows = await grid.get_selected_rows()
+        if rows:
+            for row in rows:
+                ui.notify(f"{row['name']}, {row['age']}")
+        else:
+            ui.notify('No rows selected.')
+
+    async def output_selected_row():
+        row = await grid.get_selected_row()
+        if row:
+            ui.notify(f"{row['name']}, {row['age']}")
+        else:
+            ui.notify('No row selected!')
+
+    ui.button('Output selected rows', on_click=output_selected_rows)
+    ui.button('Output selected row', on_click=output_selected_row)
+
+
+@doc.demo('Filter Rows using Mini Filters', '''
+    You can add [mini filters](https://ag-grid.com/javascript-data-grid/filter-set-mini-filter/)
+    to the header of each column to filter the rows.
+    
+    Note how the "agTextColumnFilter" matches individual characters, like "a" in "Alice" and "Carol",
+    while the "agNumberColumnFilter" matches the entire number, like "18" and "21", but not "1".
+''')
+def aggrid_with_minifilters():
+    ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name', 'filter': 'agTextColumnFilter', 'floatingFilter': True},
+            {'headerName': 'Age', 'field': 'age', 'filter': 'agNumberColumnFilter', 'floatingFilter': True},
+        ],
+        'rowData': [
+            {'name': 'Alice', 'age': 18},
+            {'name': 'Bob', 'age': 21},
+            {'name': 'Carol', 'age': 42},
+        ],
+    }).classes('max-h-40')
+
+
+@doc.demo('AG Grid with Conditional Cell Formatting', '''
+    This demo shows how to use [cellClassRules](https://www.ag-grid.com/javascript-grid-cell-styles/#cell-class-rules)
+    to conditionally format cells based on their values.
+''')
+def aggrid_with_conditional_cell_formatting():
+    ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name'},
+            {'headerName': 'Age', 'field': 'age', 'cellClassRules': {
+                'bg-red-300': 'x < 21',
+                'bg-green-300': 'x >= 21',
+            }},
+        ],
+        'rowData': [
+            {'name': 'Alice', 'age': 18},
+            {'name': 'Bob', 'age': 21},
+            {'name': 'Carol', 'age': 42},
+        ],
+    })
+
+
+@doc.demo('Create Grid from Pandas DataFrame', '''
+    You can create an AG Grid from a Pandas DataFrame using the `from_pandas` method.
+    This method takes a Pandas DataFrame as input and returns an AG Grid.
+''')
+def aggrid_from_pandas():
+    import pandas as pd
+
+    df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
+    ui.aggrid.from_pandas(df).classes('max-h-40')
+
+
+@doc.demo('Render columns as HTML', '''
+    You can render columns as HTML by passing a list of column indices to the `html_columns` argument.
+''')
+def aggrid_with_html_columns():
+    ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name'},
+            {'headerName': 'URL', 'field': 'url'},
+        ],
+        'rowData': [
+            {'name': 'Google', 'url': '<a href="https://google.com">https://google.com</a>'},
+            {'name': 'Facebook', 'url': '<a href="https://facebook.com">https://facebook.com</a>'},
+        ],
+    }, html_columns=[1])
+
+
+@doc.demo('Respond to an AG Grid event', '''
+    All AG Grid events are passed through to NiceGUI via the AG Grid global listener.
+    These events can be subscribed to using the `.on()` method.
+''')
+def aggrid_respond_to_event():
+    ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'Name', 'field': 'name'},
+            {'headerName': 'Age', 'field': 'age'},
+        ],
+        'rowData': [
+            {'name': 'Alice', 'age': 18},
+            {'name': 'Bob', 'age': 21},
+            {'name': 'Carol', 'age': 42},
+        ],
+    }).on('cellClicked', lambda event: ui.notify(f'Cell value: {event.args["value"]}'))
+
+
+@doc.demo('AG Grid with complex objects', '''
+    You can use nested complex objects in AG Grid by separating the field names with a period.
+    (This is the reason why keys in `rowData` are not allowed to contain periods.)
+''')
+def aggrid_with_complex_objects():
+    ui.aggrid({
+        'columnDefs': [
+            {'headerName': 'First name', 'field': 'name.first'},
+            {'headerName': 'Last name', 'field': 'name.last'},
+            {'headerName': 'Age', 'field': 'age'}
+        ],
+        'rowData': [
+            {'name': {'first': 'Alice', 'last': 'Adams'}, 'age': 18},
+            {'name': {'first': 'Bob', 'last': 'Brown'}, 'age': 21},
+            {'name': {'first': 'Carol', 'last': 'Clark'}, 'age': 42},
+        ],
+    }).classes('max-h-40')
+
+
+@doc.demo('AG Grid with dynamic row height', '''
+    You can set the height of individual rows by passing a function to the `getRowHeight` argument.
+''')
+def aggrid_with_dynamic_row_height():
+    ui.aggrid({
+        'columnDefs': [{'field': 'name'}, {'field': 'age'}],
+        'rowData': [
+            {'name': 'Alice', 'age': '18'},
+            {'name': 'Bob', 'age': '21'},
+            {'name': 'Carol', 'age': '42'},
+        ],
+        ':getRowHeight': 'params => params.data.age > 35 ? 50 : 25',
+    }).classes('max-h-40')
+
+
+doc.reference(ui.aggrid)

+ 25 - 0
website/documentation/content/audio_documentation.py

@@ -0,0 +1,25 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.audio)
+def main_demo() -> None:
+    a = ui.audio('https://cdn.pixabay.com/download/audio/2022/02/22/audio_d1718ab41b.mp3')
+    a.on('ended', lambda _: ui.notify('Audio playback completed'))
+
+    ui.button(on_click=lambda: a.props('muted'), icon='volume_off').props('outline')
+    ui.button(on_click=lambda: a.props(remove='muted'), icon='volume_up').props('outline')
+
+
+@doc.demo('Control the audio element', '''
+    This demo shows how to play, pause and seek programmatically.
+''')
+def control_demo() -> None:
+    a = ui.audio('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3')
+    ui.button('Play', on_click=a.play)
+    ui.button('Pause', on_click=a.pause)
+    ui.button('Jump to 0:30', on_click=lambda: a.seek(30))
+
+
+doc.reference(ui.audio)

+ 20 - 0
website/documentation/content/avatar_documentation.py

@@ -0,0 +1,20 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.avatar)
+def main_demo() -> None:
+    ui.avatar('favorite_border', text_color='grey-11', square=True)
+    ui.avatar('img:https://nicegui.io/logo_square.png', color='blue-2')
+
+
+@doc.demo('Photos', '''
+    To use a photo as an avatar, you can use `ui.image` within `ui.avatar`.
+''')
+def photos() -> None:
+    with ui.avatar():
+        ui.image('https://robohash.org/robot?bgset=bg2')
+
+
+doc.reference(ui.avatar)

+ 12 - 0
website/documentation/content/badge_documentation.py

@@ -0,0 +1,12 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.badge)
+def main_demo() -> None:
+    with ui.button('Click me!', on_click=lambda: badge.set_text(int(badge.text) + 1)):
+        badge = ui.badge('0', color='red').props('floating')
+
+
+doc.reference(ui.badge)

+ 64 - 0
website/documentation/content/button_documentation.py

@@ -0,0 +1,64 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.button)
+def main_demo() -> None:
+    ui.button('Click me!', on_click=lambda: ui.notify('You clicked me!'))
+
+
+@doc.demo('Icons', '''
+    You can also add an icon to a button.
+''')
+def icons() -> None:
+    with ui.row():
+        ui.button('demo', icon='history')
+        ui.button(icon='thumb_up')
+        with ui.button():
+            ui.label('sub-elements')
+            ui.image('https://picsum.photos/id/377/640/360') \
+                .classes('rounded-full w-16 h-16 ml-4')
+
+
+@doc.demo('Await button click', '''
+    Sometimes it is convenient to wait for a button click before continuing the execution.
+''')
+async def await_button_click() -> None:
+    # @ui.page('/')
+    # async def index():
+    b = ui.button('Step')
+    await b.clicked()
+    ui.label('One')
+    await b.clicked()
+    ui.label('Two')
+    await b.clicked()
+    ui.label('Three')
+
+
+@doc.demo('Disable button with a context manager', '''
+    This showcases a context manager that can be used to disable a button for the duration of an async process.
+''')
+def disable_context_manager() -> None:
+    from contextlib import contextmanager
+
+    import httpx
+
+    @contextmanager
+    def disable(button: ui.button):
+        button.disable()
+        try:
+            yield
+        finally:
+            button.enable()
+
+    async def get_slow_response(button: ui.button) -> None:
+        with disable(button):
+            async with httpx.AsyncClient() as client:
+                response = await client.get('https://httpbin.org/delay/1', timeout=5)
+                ui.notify(f'Response code: {response.status_code}')
+
+    ui.button('Get slow response', on_click=lambda e: get_slow_response(e.sender))
+
+
+doc.reference(ui.button)

+ 53 - 0
website/documentation/content/card_documentation.py

@@ -0,0 +1,53 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.card)
+def main_demo() -> None:
+    with ui.card().tight():
+        ui.image('https://picsum.photos/id/684/640/360')
+        with ui.card_section():
+            ui.label('Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...')
+
+
+@doc.demo('Card without shadow', '''
+    You can remove the shadow from a card by adding the `no-shadow` class.
+    The following demo shows a 1 pixel wide border instead.
+''')
+def card_without_shadow() -> None:
+    with ui.card().classes('no-shadow border-[1px]'):
+        ui.label('See, no shadow!')
+
+
+@doc.demo('The issue with nested borders', '''
+    The following example shows a table nested in a card.
+    Cards have a default padding in NiceGUI, so the table is not flush with the card's border.
+    The table has the `flat` and `bordered` props set, so it should have a border.
+    However, due to the way QCard is designed, the border is not visible (first card).
+    There are two ways to fix this:
+
+    - To get the original QCard behavior, use the `tight` method (second card).
+        It removes the padding and the table border collapses with the card border.
+    
+    - To preserve the padding _and_ the table border, move the table into another container like a `ui.row` (third card).
+
+    See https://github.com/zauberzeug/nicegui/issues/726 for more information.
+''')
+def custom_context_menu() -> None:
+    columns = [{'name': 'age', 'label': 'Age', 'field': 'age'}]
+    rows = [{'age': '16'}, {'age': '18'}, {'age': '21'}]
+
+    with ui.row():
+        with ui.card():
+            ui.table(columns, rows).props('flat bordered')
+
+        with ui.card().tight():
+            ui.table(columns, rows).props('flat bordered')
+
+        with ui.card():
+            with ui.row():
+                ui.table(columns, rows).props('flat bordered')
+
+
+doc.reference(ui.card)

+ 17 - 0
website/documentation/content/carousel_documentation.py

@@ -0,0 +1,17 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.carousel)
+def main_demo() -> None:
+    with ui.carousel(animated=True, arrows=True, navigation=True).props('height=180px'):
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/30/270/180').classes('w-[270px]')
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/31/270/180').classes('w-[270px]')
+        with ui.carousel_slide().classes('p-0'):
+            ui.image('https://picsum.photos/id/32/270/180').classes('w-[270px]')
+
+
+doc.reference(ui.carousel)

+ 46 - 0
website/documentation/content/chat_message_documentation.py

@@ -0,0 +1,46 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.chat_message)
+def main_demo() -> None:
+    ui.chat_message('Hello NiceGUI!',
+                    name='Robot',
+                    stamp='now',
+                    avatar='https://robohash.org/ui')
+
+
+@doc.demo('HTML text', '''
+    Using the `text_html` parameter, you can send HTML text to the chat.
+''')
+def html_text():
+    ui.chat_message('Without <strong>HTML</strong>')
+    ui.chat_message('With <strong>HTML</strong>', text_html=True)
+
+
+@doc.demo('Newline', '''
+    You can use newlines in the chat message.
+''')
+def newline():
+    ui.chat_message('This is a\nlong line!')
+
+
+@doc.demo('Multi-part messages', '''
+    You can send multiple message parts by passing a list of strings.
+''')
+def multiple_messages():
+    ui.chat_message(['Hi! 😀', 'How are you?']
+                    )
+
+
+@doc.demo('Chat message with child elements', '''
+    You can add child elements to a chat message.
+''')
+def child_elements():
+    with ui.chat_message():
+        ui.label('Guess where I am!')
+        ui.image('https://picsum.photos/id/249/640/360').classes('w-64')
+
+
+doc.reference(ui.chat_message)

+ 12 - 0
website/documentation/content/checkbox_documentation.py

@@ -0,0 +1,12 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.checkbox)
+def main_demo() -> None:
+    checkbox = ui.checkbox('check me')
+    ui.label('Check!').bind_visibility_from(checkbox, 'value')
+
+
+doc.reference(ui.checkbox)

+ 26 - 0
website/documentation/content/circular_progress_documentation.py

@@ -0,0 +1,26 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.circular_progress)
+def main_demo() -> None:
+    slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
+    ui.circular_progress().bind_value_from(slider, 'value')
+
+
+@doc.demo('Nested Elements', '''
+    You can put any element like icon, button etc inside a circular progress using the `with` statement.
+    Just make sure it fits the bounds and disable the default behavior of showing the value.
+''')
+def icon() -> None:
+    with ui.row().classes('items-center m-auto'):
+        with ui.circular_progress(value=0.1, show_value=False) as progress:
+            ui.button(
+                icon='star',
+                on_click=lambda: progress.set_value(progress.value + 0.1)
+            ).props('flat round')
+        ui.label('click to increase progress')
+
+
+doc.reference(ui.circular_progress)

+ 17 - 0
website/documentation/content/code_documentation.py

@@ -0,0 +1,17 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.code)
+def main_demo() -> None:
+    ui.code('''
+        from nicegui import ui
+        
+        ui.label('Code inception!')
+            
+        ui.run()
+    ''').classes('w-full')
+
+
+doc.reference(ui.code)

+ 13 - 0
website/documentation/content/color_input_documentation.py

@@ -0,0 +1,13 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.color_input)
+def main_demo() -> None:
+    label = ui.label('Change my color!')
+    ui.color_input(label='Color', value='#000000',
+                   on_change=lambda e: label.style(f'color:{e.value}'))
+
+
+doc.reference(ui.color_input)

+ 12 - 0
website/documentation/content/color_picker_documentation.py

@@ -0,0 +1,12 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.color_picker)
+def main_demo() -> None:
+    with ui.button(icon='colorize') as button:
+        ui.color_picker(on_pick=lambda e: button.style(f'background-color:{e.color}!important'))
+
+
+doc.reference(ui.color_picker)

+ 15 - 0
website/documentation/content/colors_documentation.py

@@ -0,0 +1,15 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.colors)
+def main_demo() -> None:
+    # ui.button('Default', on_click=lambda: ui.colors())
+    # ui.button('Gray', on_click=lambda: ui.colors(primary='#555'))
+    # END OF DEMO
+    b1 = ui.button('Default', on_click=lambda: [b.classes(replace='!bg-primary') for b in [b1, b2]])
+    b2 = ui.button('Gray', on_click=lambda: [b.classes(replace='!bg-[#555]') for b in [b1, b2]])
+
+
+doc.reference(ui.colors)

+ 26 - 0
website/documentation/content/column_documentation.py

@@ -0,0 +1,26 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.column)
+def main_demo() -> None:
+    with ui.column():
+        ui.label('label 1')
+        ui.label('label 2')
+        ui.label('label 3')
+
+
+@doc.demo('Masonry or Pinterest-Style Layout', '''
+    To create a masonry/Pinterest layout, the normal `ui.column` can not be used.
+    But it can be achieved with a few TailwindCSS classes.
+''')
+def masonry() -> None:
+    with ui.element('div').classes('columns-3 w-full gap-2'):
+        for i, height in enumerate([50, 50, 50, 150, 100, 50]):
+            tailwind = f'mb-2 p-2 h-[{height}px] bg-blue-100 break-inside-avoid'
+            with ui.card().classes(tailwind):
+                ui.label(f'Card #{i+1}')
+
+
+doc.reference(ui.column)

+ 16 - 0
website/documentation/content/context_menu_documentation.py

@@ -0,0 +1,16 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.context_menu)
+def main_demo() -> None:
+    with ui.image('https://picsum.photos/id/377/640/360'):
+        with ui.context_menu():
+            ui.menu_item('Flip horizontally')
+            ui.menu_item('Flip vertically')
+            ui.separator()
+            ui.menu_item('Reset')
+
+
+doc.reference(ui.context_menu)

+ 26 - 0
website/documentation/content/dark_mode_documentation.py

@@ -0,0 +1,26 @@
+from nicegui import ui
+
+from ..windows import WINDOW_BG_COLORS
+from . import doc
+
+
+@doc.demo(ui.dark_mode)
+def main_demo() -> None:
+    # dark = ui.dark_mode()
+    # ui.label('Switch mode:')
+    # ui.button('Dark', on_click=dark.enable)
+    # ui.button('Light', on_click=dark.disable)
+    # END OF DEMO
+    l = ui.label('Switch mode:')
+    c = l.parent_slot.parent
+    ui.button('Dark', on_click=lambda: (
+        l.style('color: white'),
+        c.style(f'background-color: {WINDOW_BG_COLORS["browser"][1]}'),
+    ))
+    ui.button('Light', on_click=lambda: (
+        l.style('color: black'),
+        c.style(f'background-color: {WINDOW_BG_COLORS["browser"][0]}'),
+    ))
+
+
+doc.reference(ui.dark_mode)

+ 37 - 0
website/documentation/content/date_documentation.py

@@ -0,0 +1,37 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.date)
+def main_demo() -> None:
+    ui.date(value='2023-01-01', on_change=lambda e: result.set_text(e.value))
+    result = ui.label()
+
+
+@doc.demo('Input element with date picker', '''
+    This demo shows how to implement a date picker with an input element.
+    We place an icon in the input element's append slot.
+    When the icon is clicked, we open a menu with a date picker.
+
+    The date is bound to the input element's value.
+    So both the input element and the date picker will stay in sync whenever the date is changed.
+''')
+def date():
+    with ui.input('Date') as date:
+        with date.add_slot('append'):
+            ui.icon('edit_calendar').on('click', lambda: menu.open()).classes('cursor-pointer')
+        with ui.menu() as menu:
+            ui.date().bind_value(date)
+
+
+@doc.demo('Date filter', '''
+    This demo shows how to filter the dates in a date picker.
+    In order to pass a function to the date picker, we use the `:options` property.
+    The leading `:` tells NiceGUI that the value is a JavaScript expression.
+''')
+def date_filter():
+    ui.date().props('''default-year-month=2023/01 :options="date => date <= '2023/01/15'"''')
+
+
+doc.reference(ui.date)

+ 51 - 0
website/documentation/content/dialog_documentation.py

@@ -0,0 +1,51 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.dialog)
+def main_demo() -> None:
+    with ui.dialog() as dialog, ui.card():
+        ui.label('Hello world!')
+        ui.button('Close', on_click=dialog.close)
+
+    ui.button('Open a dialog', on_click=dialog.open)
+
+
+@doc.demo('Awaitable dialog', '''
+    Dialogs can be awaited.
+    Use the `submit` method to close the dialog and return a result.
+    Canceling the dialog by clicking in the background or pressing the escape key yields `None`.
+''')
+def async_dialog_demo():
+    with ui.dialog() as dialog, ui.card():
+        ui.label('Are you sure?')
+        with ui.row():
+            ui.button('Yes', on_click=lambda: dialog.submit('Yes'))
+            ui.button('No', on_click=lambda: dialog.submit('No'))
+
+    async def show():
+        result = await dialog
+        ui.notify(f'You chose {result}')
+
+    ui.button('Await a dialog', on_click=show)
+
+
+@doc.demo('Replacing content', '''
+    The content of a dialog can be changed.
+''')
+def replace_content():
+    def replace():
+        dialog.clear()
+        with dialog, ui.card().classes('w-64 h-64'):
+            ui.label('New Content')
+        dialog.open()
+
+    with ui.dialog() as dialog, ui.card():
+        ui.label('Hello world!')
+
+    ui.button('Open', on_click=dialog.open)
+    ui.button('Replace', on_click=replace)
+
+
+doc.reference(ui.dialog)

+ 104 - 0
website/documentation/content/doc/__init__.py

@@ -0,0 +1,104 @@
+from __future__ import annotations
+
+import inspect
+import types
+from copy import deepcopy
+from pathlib import Path
+from types import ModuleType
+from typing import Callable, Dict, Optional, Union, overload
+
+from nicegui.elements.markdown import remove_indentation
+
+from .page import DocumentationPage
+from .part import DocumentationPart
+
+registry: Dict[str, DocumentationPage] = {}
+
+
+def get_page(documentation: ModuleType) -> DocumentationPage:
+    """Return the documentation page for the given documentation module."""
+    target_name = documentation.__name__.split('.')[-1].removesuffix('_documentation')
+    assert target_name in registry, f'Documentation page {target_name} does not exist'
+    return registry[target_name]
+
+
+def _get_current_page() -> DocumentationPage:
+    frame = inspect.stack()[2]
+    module = inspect.getmodule(frame[0])
+    assert module is not None and module.__file__ is not None
+    name = Path(module.__file__).stem.removesuffix('_documentation')
+    if name not in registry:
+        registry[name] = DocumentationPage(name=name)
+    return registry[name]
+
+
+def title(title_: Optional[str] = None, subtitle: Optional[str] = None) -> None:
+    """Set the title of the current documentation page."""
+    page = _get_current_page()
+    page.title = title_
+    page.subtitle = subtitle
+
+
+def text(title_: str, description: str) -> None:
+    """Add a text block to the current documentation page."""
+    _get_current_page().parts.append(DocumentationPart(title=title_, description=description))
+
+
+@overload
+def demo(title_: str, description: str, /, *, tab: Optional[Union[str, Callable]] = None) -> Callable[[Callable], Callable]:
+    ...
+
+
+@overload
+def demo(element: type, /, tab: Optional[Union[str, Callable]] = None) -> Callable[[Callable], Callable]:
+    ...
+
+
+@overload
+def demo(function: Callable, /, tab: Optional[Union[str, Callable]] = None) -> Callable[[Callable], Callable]:
+    ...
+
+
+def demo(*args, **kwargs) -> Callable[[Callable], Callable]:
+    """Add a demo section to the current documentation page."""
+    if len(args) == 2:
+        title_, description = args
+        is_markdown = True
+    else:
+        doc = args[0].__init__.__doc__ if isinstance(args[0], type) else args[0].__doc__  # type: ignore
+        title_, description = doc.split('\n', 1)
+        is_markdown = False
+
+    description = remove_indentation(description)
+    page = _get_current_page()
+
+    def decorator(function: Callable) -> Callable:
+        page.parts.append(DocumentationPart(
+            title=title_,
+            description=description,
+            description_format='md' if is_markdown else 'rst',
+            demo=function,
+        ))
+        return function
+    return decorator
+
+
+def ui(function: Callable) -> Callable:
+    """Add arbitrary UI to the current documentation page."""
+    _get_current_page().parts.append(DocumentationPart(ui=function))
+    return function
+
+
+def intro(documentation: types.ModuleType) -> None:
+    """Add an intro section to the current documentation page."""
+    current_page = _get_current_page()
+    target_page = get_page(documentation)
+    target_page.back_link = current_page.name
+    part = deepcopy(target_page.parts[0])
+    part.link = target_page.name
+    current_page.parts.append(part)
+
+
+def reference(element: type, *, title: str = 'Reference') -> None:
+    """Add a reference section to the current documentation page."""
+    _get_current_page().parts.append(DocumentationPart(title=title, reference=element))

+ 20 - 0
website/documentation/content/doc/page.py

@@ -0,0 +1,20 @@
+from dataclasses import dataclass, field
+from typing import List, Optional
+
+from nicegui.dataclasses import KWONLY_SLOTS
+
+from .part import DocumentationPart
+
+
+@dataclass(**KWONLY_SLOTS)
+class DocumentationPage:
+    name: str
+    title: Optional[str] = None
+    subtitle: Optional[str] = None
+    back_link: Optional[str] = None
+    parts: List[DocumentationPart] = field(default_factory=list)
+
+    @property
+    def heading(self) -> str:
+        """Return the heading of the page."""
+        return self.title or self.parts[0].title or ''

+ 23 - 0
website/documentation/content/doc/part.py

@@ -0,0 +1,23 @@
+
+from dataclasses import dataclass
+from typing import Callable, Literal, Optional
+
+from nicegui.dataclasses import KWONLY_SLOTS
+
+from ....style import create_anchor_name
+
+
+@dataclass(**KWONLY_SLOTS)
+class DocumentationPart:
+    title: Optional[str] = None
+    description: Optional[str] = None
+    description_format: Literal['md', 'rst'] = 'md'
+    link: Optional[str] = None
+    ui: Optional[Callable] = None
+    demo: Optional[Callable] = None
+    reference: Optional[type] = None
+
+    @property
+    def link_target(self) -> Optional[str]:
+        """Return the link target for in-page navigation."""
+        return create_anchor_name(self.title) if self.title else None

+ 15 - 0
website/documentation/content/download_documentation.py

@@ -0,0 +1,15 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.download)
+def main_demo() -> None:
+    ui.button('Logo', on_click=lambda: ui.download('https://nicegui.io/logo.png'))
+
+
+@doc.demo('Download raw bytes from memory', '''
+    The `download` function can also be used to download raw bytes from memory.
+''')
+def raw_bytes():
+    ui.button('Download', on_click=lambda: ui.download(b'Hello World', 'hello.txt'))

+ 50 - 0
website/documentation/content/echart_documentation.py

@@ -0,0 +1,50 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.echart)
+def main_demo() -> None:
+    from random import random
+
+    echart = ui.echart({
+        'xAxis': {'type': 'value'},
+        'yAxis': {'type': 'category', 'data': ['A', 'B'], 'inverse': True},
+        'legend': {'textStyle': {'color': 'gray'}},
+        'series': [
+            {'type': 'bar', 'name': 'Alpha', 'data': [0.1, 0.2]},
+            {'type': 'bar', 'name': 'Beta', 'data': [0.3, 0.4]},
+        ],
+    })
+
+    def update():
+        echart.options['series'][0]['data'][0] = random()
+        echart.update()
+
+    ui.button('Update', on_click=update)
+
+
+@doc.demo('EChart with clickable points', '''
+    You can register a callback for an event when a series point is clicked.
+''')
+def clickable_points() -> None:
+    ui.echart({
+        'xAxis': {'type': 'category'},
+        'yAxis': {'type': 'value'},
+        'series': [{'type': 'line', 'data': [20, 10, 30, 50, 40, 30]}],
+    }, on_point_click=ui.notify)
+
+
+@doc.demo('EChart with dynamic properties', '''
+    Dynamic properties can be passed to chart elements to customize them such as apply an axis label format.
+    To make a property dynamic, prefix a colon ":" to the property name.
+''')
+def dynamic_properties() -> None:
+    ui.echart({
+        'xAxis': {'type': 'category'},
+        'yAxis': {'axisLabel': {':formatter': 'value => "$" + value'}},
+        'series': [{'type': 'line', 'data': [5, 8, 13, 21, 34, 55]}],
+    })
+
+
+doc.reference(ui.echart)

+ 13 - 0
website/documentation/content/editor_documentation.py

@@ -0,0 +1,13 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.editor)
+def main_demo() -> None:
+    editor = ui.editor(placeholder='Type something here')
+    ui.markdown().bind_content_from(editor, 'value',
+                                    backward=lambda v: f'HTML code:\n```\n{v}\n```')
+
+
+doc.reference(ui.editor)

+ 73 - 0
website/documentation/content/element_documentation.py

@@ -0,0 +1,73 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.element)
+def main_demo() -> None:
+    with ui.element('div').classes('p-2 bg-blue-100'):
+        ui.label('inside a colored div')
+
+
+@doc.demo('Move elements', '''
+    This demo shows how to move elements between or within containers.
+''')
+def move_elements() -> None:
+    with ui.card() as a:
+        ui.label('A')
+        x = ui.label('X')
+
+    with ui.card() as b:
+        ui.label('B')
+
+    ui.button('Move X to A', on_click=lambda: x.move(a))
+    ui.button('Move X to B', on_click=lambda: x.move(b))
+    ui.button('Move X to top', on_click=lambda: x.move(target_index=0))
+
+
+@doc.demo('Default props', '''
+    You can set default props for all elements of a certain class.
+    This way you can avoid repeating the same props over and over again.
+    
+    Default props only apply to elements created after the default props were set.
+    Subclasses inherit the default props of their parent class.
+''')
+def default_props() -> None:
+    ui.button.default_props('rounded outline')
+    ui.button('Button A')
+    ui.button('Button B')
+    # END OF DEMO
+    ui.button.default_props(remove='rounded outline')
+
+
+@doc.demo('Default classes', '''
+    You can set default classes for all elements of a certain class.
+    This way you can avoid repeating the same classes over and over again.
+    
+    Default classes only apply to elements created after the default classes were set.
+    Subclasses inherit the default classes of their parent class.
+''')
+def default_classes() -> None:
+    ui.label.default_classes('bg-blue-100 p-2')
+    ui.label('Label A')
+    ui.label('Label B')
+    # END OF DEMO
+    ui.label.default_classes(remove='bg-blue-100 p-2')
+
+
+@doc.demo('Default style', '''
+    You can set a default style for all elements of a certain class.
+    This way you can avoid repeating the same style over and over again.
+    
+    A default style only applies to elements created after the default style was set.
+    Subclasses inherit the default style of their parent class.
+''')
+def default_style() -> None:
+    ui.label.default_style('color: tomato')
+    ui.label('Label A')
+    ui.label('Label B')
+    # END OF DEMO
+    ui.label.default_style(remove='color: tomato')
+
+
+doc.reference(ui.element)

+ 22 - 0
website/documentation/content/expansion_documentation.py

@@ -0,0 +1,22 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.expansion)
+def main_demo() -> None:
+    with ui.expansion('Expand!', icon='work').classes('w-full'):
+        ui.label('inside the expansion')
+
+
+@doc.demo('Expansion with Custom Header', '''
+    Instead of setting a plain-text title, you can fill the expansion header with UI elements by adding them to the "header" slot.
+''')
+def expansion_with_custom_header():
+    with ui.expansion() as expansion:
+        with expansion.add_slot('header'):
+            ui.image('https://nicegui.io/logo.png').classes('w-16')
+        ui.label('What a nice GUI!')
+
+
+doc.reference(ui.expansion)

+ 124 - 0
website/documentation/content/generic_events_documentation.py

@@ -0,0 +1,124 @@
+from nicegui import context, ui
+
+from . import doc
+
+doc.title('Generic Events')
+
+
+@doc.demo('Generic Events', '''
+    Most UI elements come with predefined events.
+    For example, a `ui.button` like "A" in the demo has an `on_click` parameter that expects a coroutine or function.
+    But you can also use the `on` method to register a generic event handler like for "B".
+    This allows you to register handlers for any event that is supported by JavaScript and Quasar.
+
+    For example, you can register a handler for the `mousemove` event like for "C", even though there is no `on_mousemove` parameter for `ui.button`.
+    Some events, like `mousemove`, are fired very often.
+    To avoid performance issues, you can use the `throttle` parameter to only call the handler every `throttle` seconds ("D").
+
+    The generic event handler can be synchronous or asynchronous and optionally takes `GenericEventArguments` as argument ("E").
+    You can also specify which attributes of the JavaScript or Quasar event should be passed to the handler ("F").
+    This can reduce the amount of data that needs to be transferred between the server and the client.
+
+    Here you can find more information about the events that are supported:
+
+    - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement#events for HTML elements
+    - https://quasar.dev/vue-components for Quasar-based elements (see the "Events" tab on the individual component page)
+''')
+def generic_events_demo() -> None:
+    with ui.row():
+        ui.button('A', on_click=lambda: ui.notify('You clicked the button A.'))
+        ui.button('B').on('click', lambda: ui.notify('You clicked the button B.'))
+    with ui.row():
+        ui.button('C').on('mousemove', lambda: ui.notify('You moved on button C.'))
+        ui.button('D').on('mousemove', lambda: ui.notify('You moved on button D.'), throttle=0.5)
+    with ui.row():
+        ui.button('E').on('mousedown', lambda e: ui.notify(e))
+        ui.button('F').on('mousedown', lambda e: ui.notify(e), ['ctrlKey', 'shiftKey'])
+
+
+@doc.demo('Specifying event attributes', '''
+    **A list of strings** names the attributes of the JavaScript event object:
+    ```py
+    ui.button().on('click', handle_click, ['clientX', 'clientY'])
+    ```
+
+    **An empty list** requests _no_ attributes:
+    ```py
+    ui.button().on('click', handle_click, [])
+    ```
+
+    **The value `None`** represents _all_ attributes (the default):
+    ```py
+    ui.button().on('click', handle_click, None)
+    ```
+
+    **If the event is called with multiple arguments** like QTable's "row-click" `(evt, row, index) => void`,
+    you can define a list of argument definitions:
+    ```py
+    ui.table(...).on('rowClick', handle_click, [[], ['name'], None])
+    ```
+    In this example the "row-click" event will omit all arguments of the first `evt` argument,
+    send only the "name" attribute of the `row` argument and send the full `index`.
+
+    If the retrieved list of event arguments has length 1, the argument is automatically unpacked.
+    So you can write
+    ```py
+    ui.button().on('click', lambda e: print(e.args['clientX'], flush=True))
+    ```
+    instead of
+    ```py
+    ui.button().on('click', lambda e: print(e.args[0]['clientX'], flush=True))
+    ```
+
+    Note that by default all JSON-serializable attributes of all arguments are sent.
+    This is to simplify registering for new events and discovering their attributes.
+    If bandwidth is an issue, the arguments should be limited to what is actually needed on the server.
+''')
+def event_attributes() -> None:
+    columns = [
+        {'name': 'name', 'label': 'Name', 'field': 'name'},
+        {'name': 'age', 'label': 'Age', 'field': 'age'},
+    ]
+    rows = [
+        {'name': 'Alice', 'age': 42},
+        {'name': 'Bob', 'age': 23},
+    ]
+    ui.table(columns, rows, 'name').on('rowClick', ui.notify, [[], ['name'], None])
+
+
+@doc.demo('Modifiers', '''
+    You can also include [key modifiers](https://vuejs.org/guide/essentials/event-handling.html#key-modifiers>) (shown in input "A"),
+    modifier combinations (shown in input "B"),
+    and [event modifiers](https://vuejs.org/guide/essentials/event-handling.html#mouse-button-modifiers>) (shown in input "C").
+''')
+def modifiers() -> None:
+    with ui.row():
+        ui.input('A').classes('w-12').on('keydown.space', lambda: ui.notify('You pressed space.'))
+        ui.input('B').classes('w-12').on('keydown.y.shift', lambda: ui.notify('You pressed Shift+Y'))
+        ui.input('C').classes('w-12').on('keydown.once', lambda: ui.notify('You started typing.'))
+
+
+@doc.demo('Custom events', '''
+    It is fairly easy to emit custom events from JavaScript which can be listened to with `element.on(...)`.
+    This can be useful if you want to call Python code when something happens in JavaScript.
+    In this example we are listening to the `visibilitychange` event of the browser tab.
+''')
+async def custom_events() -> None:
+    tabwatch = ui.checkbox('Watch browser tab re-entering') \
+        .on('tabvisible', lambda: ui.notify('Welcome back!') if tabwatch.value else None, args=[])
+    ui.add_head_html(f'''
+        <script>
+        document.addEventListener('visibilitychange', () => {{
+            if (document.visibilityState === 'visible')
+                getElement({tabwatch.id}).$emit('tabvisible');
+        }});
+        </script>
+    ''')
+    # END OF DEMO
+    await context.get_client().connected()
+    ui.run_javascript(f'''
+        document.addEventListener('visibilitychange', () => {{
+            if (document.visibilityState === 'visible')
+                getElement({tabwatch.id}).$emit('tabvisible');
+        }});
+    ''')

+ 19 - 0
website/documentation/content/grid_documentation.py

@@ -0,0 +1,19 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.grid)
+def main_demo() -> None:
+    with ui.grid(columns=2):
+        ui.label('Name:')
+        ui.label('Tom')
+
+        ui.label('Age:')
+        ui.label('42')
+
+        ui.label('Height:')
+        ui.label('1.80m')
+
+
+doc.reference(ui.grid)

+ 76 - 0
website/documentation/content/highchart_documentation.py

@@ -0,0 +1,76 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.highchart)
+def main_demo() -> None:
+    from random import random
+
+    chart = ui.highchart({
+        'title': False,
+        'chart': {'type': 'bar'},
+        'xAxis': {'categories': ['A', 'B']},
+        'series': [
+            {'name': 'Alpha', 'data': [0.1, 0.2]},
+            {'name': 'Beta', 'data': [0.3, 0.4]},
+        ],
+    }).classes('w-full h-64')
+
+    def update():
+        chart.options['series'][0]['data'][0] = random()
+        chart.update()
+
+    ui.button('Update', on_click=update)
+
+
+@doc.demo('Chart with extra dependencies', '''
+    To use a chart type that is not included in the default dependencies, you can specify extra dependencies.
+    This demo shows a solid gauge chart.
+''')
+def extra_dependencies() -> None:
+    ui.highchart({
+        'title': False,
+        'chart': {'type': 'solidgauge'},
+        'yAxis': {
+            'min': 0,
+            'max': 1,
+        },
+        'series': [
+            {'data': [0.42]},
+        ],
+    }, extras=['solid-gauge']).classes('w-full h-64')
+
+
+@doc.demo('Chart with draggable points', '''
+    This chart allows dragging the series points.
+    You can register callbacks for the following events:
+    
+    - `on_point_click`: called when a point is clicked
+    - `on_point_drag_start`: called when a point drag starts
+    - `on_point_drag`: called when a point is dragged
+    - `on_point_drop`: called when a point is dropped
+''')
+def drag() -> None:
+    ui.highchart(
+        {
+            'title': False,
+            'plotOptions': {
+                'series': {
+                    'stickyTracking': False,
+                    'dragDrop': {'draggableY': True, 'dragPrecisionY': 1},
+                },
+            },
+            'series': [
+                {'name': 'A', 'data': [[20, 10], [30, 20], [40, 30]]},
+                {'name': 'B', 'data': [[50, 40], [60, 50], [70, 60]]},
+            ],
+        },
+        extras=['draggable-points'],
+        on_point_click=lambda e: ui.notify(f'Click: {e}'),
+        on_point_drag_start=lambda e: ui.notify(f'Drag start: {e}'),
+        on_point_drop=lambda e: ui.notify(f'Drop: {e}')
+    ).classes('w-full h-64')
+
+
+doc.reference(ui.highchart)

+ 11 - 0
website/documentation/content/html_documentation.py

@@ -0,0 +1,11 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.html)
+def main_demo() -> None:
+    ui.html('This is <strong>HTML</strong>.')
+
+
+doc.reference(ui.html)

+ 34 - 0
website/documentation/content/icon_documentation.py

@@ -0,0 +1,34 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.icon)
+def main_demo() -> None:
+    ui.icon('thumb_up', color='primary').classes('text-5xl')
+
+
+ui.add_head_html('<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet">')  # TODO
+ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')  # TODO
+
+
+@doc.demo('Eva icons', '''
+    You can use [Eva icons](https://akveo.github.io/eva-icons/) in your app.
+''')
+def eva_icons():
+    # ui.add_head_html('<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet">')
+
+    ui.element('i').classes('eva eva-github').classes('text-5xl')
+
+
+@doc.demo('Lottie files', '''
+    You can also use [Lottie files](https://lottiefiles.com/) with animations.
+''')
+def lottie():
+    # ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
+
+    src = 'https://assets5.lottiefiles.com/packages/lf20_MKCnqtNQvg.json'
+    ui.html(f'<lottie-player src="{src}" loop autoplay />').classes('w-24')
+
+
+doc.reference(ui.icon)

+ 68 - 0
website/documentation/content/image_documentation.py

@@ -0,0 +1,68 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.image)
+def main_demo() -> None:
+    ui.image('https://picsum.photos/id/377/640/360')
+
+
+ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')  # TODO
+
+
+@doc.demo('Local files', '''
+    You can use local images as well by passing a path to the image file.
+''')
+def local():
+    ui.image('website/static/logo.png').classes('w-16')
+
+
+@doc.demo('Base64 string', '''
+    You can also use a Base64 string as image source.
+''')
+def base64():
+    base64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
+    ui.image(base64).classes('w-2 h-2 m-auto')
+
+
+@doc.demo('PIL image', '''
+    You can also use a PIL image as image source.
+''')
+def pil():
+    import numpy as np
+    from PIL import Image
+
+    image = Image.fromarray(np.random.randint(0, 255, (100, 100), dtype=np.uint8))
+    ui.image(image).classes('w-32')
+
+
+@doc.demo('Lottie files', '''
+    You can also use [Lottie files](https://lottiefiles.com/) with animations.
+''')
+def lottie():
+    # ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
+
+    src = 'https://assets1.lottiefiles.com/datafiles/HN7OcWNnoqje6iXIiZdWzKxvLIbfeCGTmvXmEm1h/data.json'
+    ui.html(f'<lottie-player src="{src}" loop autoplay />').classes('w-full')
+
+
+@doc.demo('Image link', '''
+    Images can link to another page by wrapping them in a [ui.link](https://nicegui.io/documentation/link).
+''')
+def link():
+    with ui.link(target='https://github.com/zauberzeug/nicegui'):
+        ui.image('https://picsum.photos/id/41/640/360').classes('w-64')
+
+
+@doc.demo('Force reload', '''
+    You can force an image to reload by calling the `force_reload` method.
+    It will append a timestamp to the image URL, which will make the browser reload the image.
+''')
+def force_reload():
+    img = ui.image('https://picsum.photos/640/360').classes('w-64')
+
+    ui.button('Force reload', on_click=img.force_reload)
+
+
+doc.reference(ui.image)

+ 45 - 0
website/documentation/content/input_documentation.py

@@ -0,0 +1,45 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.input)
+def main_demo() -> None:
+    ui.input(label='Text', placeholder='start typing',
+             on_change=lambda e: result.set_text('you typed: ' + e.value),
+             validation={'Input too long': lambda value: len(value) < 20})
+    result = ui.label()
+
+
+@doc.demo('Autocompletion', '''
+    The `autocomplete` feature provides suggestions as you type, making input easier and faster.
+    The parameter `options` is a list of strings that contains the available options that will appear.
+''')
+def autocomplete_demo():
+    options = ['AutoComplete', 'NiceGUI', 'Awesome']
+    ui.input(label='Text', placeholder='start typing', autocomplete=options)
+
+
+@doc.demo('Clearable', '''
+    The `clearable` prop from [Quasar](https://quasar.dev/) adds a button to the input that clears the text.    
+''')
+def clearable():
+    i = ui.input(value='some text').props('clearable')
+    ui.label().bind_text_from(i, 'value')
+
+
+@doc.demo('Styling', '''
+    Quasar has a lot of [props to change the appearance](https://quasar.dev/vue-components/input).
+    It is even possible to style the underlying input with `input-style` and `input-class` props
+    and use the provided slots to add custom elements.
+''')
+def styling():
+    ui.input(placeholder='start typing').props('rounded outlined dense')
+    ui.input('styling', value='some text') \
+        .props('input-style="color: blue" input-class="font-mono"')
+    with ui.input(value='custom clear button').classes('w-64') as i:
+        ui.button(color='orange-8', on_click=lambda: i.set_value(None), icon='delete') \
+            .props('flat dense').bind_visibility_from(i, 'value')
+
+
+doc.reference(ui.input)

+ 41 - 0
website/documentation/content/interactive_image_documentation.py

@@ -0,0 +1,41 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.interactive_image)
+def main_demo() -> None:
+    from nicegui.events import MouseEventArguments
+
+    def mouse_handler(e: MouseEventArguments):
+        color = 'SkyBlue' if e.type == 'mousedown' else 'SteelBlue'
+        ii.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="15" fill="none" stroke="{color}" stroke-width="4" />'
+        ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')
+
+    src = 'https://picsum.photos/id/565/640/360'
+    ii = ui.interactive_image(src, on_mouse=mouse_handler, events=['mousedown', 'mouseup'], cross=True)
+
+
+@doc.demo('Nesting elements', '''
+    You can nest elements inside an interactive image.
+    Use Tailwind classes like "absolute top-0 left-0" to position the label absolutely with respect to the image.
+    Of course this can be done with plain CSS as well.
+''')
+def nesting_elements():
+    with ui.interactive_image('https://picsum.photos/id/147/640/360'):
+        ui.button(on_click=lambda: ui.notify('thumbs up'), icon='thumb_up') \
+            .props('flat fab color=white') \
+            .classes('absolute bottom-0 left-0 m-2')
+
+
+@doc.demo('Force reload', '''
+    You can force an image to reload by calling the `force_reload` method.
+    It will append a timestamp to the image URL, which will make the browser reload the image.
+''')
+def force_reload():
+    img = ui.interactive_image('https://picsum.photos/640/360').classes('w-64')
+
+    ui.button('Force reload', on_click=img.force_reload)
+
+
+doc.reference(ui.interactive_image)

+ 14 - 0
website/documentation/content/joystick_documentation.py

@@ -0,0 +1,14 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.joystick)
+def main_demo() -> None:
+    ui.joystick(color='blue', size=50,
+                on_move=lambda e: coordinates.set_text(f"{e.x:.3f}, {e.y:.3f}"),
+                on_end=lambda _: coordinates.set_text('0, 0'))
+    coordinates = ui.label('0, 0')
+
+
+doc.reference(ui.joystick)

+ 26 - 0
website/documentation/content/json_editor_documentation.py

@@ -0,0 +1,26 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.json_editor)
+def main_demo() -> None:
+    json = {
+        'array': [1, 2, 3],
+        'boolean': True,
+        'color': '#82b92c',
+        None: None,
+        'number': 123,
+        'object': {
+            'a': 'b',
+            'c': 'd',
+        },
+        'time': 1575599819000,
+        'string': 'Hello World',
+    }
+    ui.json_editor({'content': {'json': json}},
+                   on_select=lambda e: ui.notify(f'Select: {e}'),
+                   on_change=lambda e: ui.notify(f'Change: {e}'))
+
+
+doc.reference(ui.json_editor)

+ 31 - 0
website/documentation/content/keyboard_documentation.py

@@ -0,0 +1,31 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.keyboard)
+def main_demo() -> None:
+    from nicegui.events import KeyEventArguments
+
+    def handle_key(e: KeyEventArguments):
+        if e.key == 'f' and not e.action.repeat:
+            if e.action.keyup:
+                ui.notify('f was just released')
+            elif e.action.keydown:
+                ui.notify('f was just pressed')
+        if e.modifiers.shift and e.action.keydown:
+            if e.key.arrow_left:
+                ui.notify('going left')
+            elif e.key.arrow_right:
+                ui.notify('going right')
+            elif e.key.arrow_up:
+                ui.notify('going up')
+            elif e.key.arrow_down:
+                ui.notify('going down')
+
+    keyboard = ui.keyboard(on_key=handle_key)
+    ui.label('Key events can be caught globally by using the keyboard element.')
+    ui.checkbox('Track key events').bind_value_to(keyboard, 'active')
+
+
+doc.reference(ui.keyboard)

+ 14 - 0
website/documentation/content/knob_documentation.py

@@ -0,0 +1,14 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.knob)
+def main_demo() -> None:
+    knob = ui.knob(0.3, show_value=True)
+
+    with ui.knob(color='orange', track_color='grey-2').bind_value(knob, 'value'):
+        ui.icon('volume_up')
+
+
+doc.reference(ui.knob)

+ 29 - 0
website/documentation/content/label_documentation.py

@@ -0,0 +1,29 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.label)
+def main_demo() -> None:
+    ui.label('some label')
+
+
+@doc.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'))
+
+
+doc.reference(ui.label)

+ 35 - 0
website/documentation/content/line_plot_documentation.py

@@ -0,0 +1,35 @@
+from nicegui import events, ui
+
+from . import doc
+
+
+@doc.demo(ui.line_plot)
+def main_demo() -> None:
+    import math
+    from datetime import datetime
+
+    line_plot = ui.line_plot(n=2, limit=20, figsize=(3, 2), update_every=5) \
+        .with_legend(['sin', 'cos'], loc='upper center', ncol=2)
+
+    def update_line_plot() -> None:
+        now = datetime.now()
+        x = now.timestamp()
+        y1 = math.sin(x)
+        y2 = math.cos(x)
+        line_plot.push([now], [[y1], [y2]])
+
+    line_updates = ui.timer(0.1, update_line_plot, active=False)
+    line_checkbox = ui.checkbox('active').bind_value(line_updates, 'active')
+
+    # END OF DEMO
+    def handle_change(e: events.GenericEventArguments) -> None:
+        def turn_off() -> None:
+            line_checkbox.set_value(False)
+            ui.notify('Turning off that line plot to save resources on our live demo server. 😎')
+        line_checkbox.value = e.args
+        if line_checkbox.value:
+            ui.timer(10.0, turn_off, once=True)
+    line_checkbox.on('update:model-value', handle_change, args=[None])
+
+
+doc.reference(ui.line_plot)

+ 12 - 0
website/documentation/content/linear_progress_documentation.py

@@ -0,0 +1,12 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.linear_progress)
+def main_demo() -> None:
+    slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
+    ui.linear_progress().bind_value_from(slider, 'value')
+
+
+doc.reference(ui.linear_progress)

+ 59 - 0
website/documentation/content/link_documentation.py

@@ -0,0 +1,59 @@
+from nicegui import ui
+
+from ...style import link_target
+from . import doc
+
+
+@doc.demo(ui.link)
+def main_demo() -> None:
+    ui.link('NiceGUI on GitHub', 'https://github.com/zauberzeug/nicegui')
+
+
+@doc.demo('Navigate on large pages', '''
+    To jump to a specific location within a page you can place linkable anchors with `ui.link_target('target_name')`
+    or simply pass a NiceGUI element as link target.
+''')
+def same_page_links():
+    navigation = ui.row()
+    # ui.link_target('target_A')
+    link_target('target_A', '-10px')  # HIDE
+    ui.label(
+        'Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
+        'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
+    )
+    link_target('target_B', '70px')  # HIDE
+    label_B = ui.label(
+        'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. '
+        'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. '
+        'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
+    )
+    with navigation:
+        ui.link('Goto A', '#target_A')
+        # ui.link('Goto B', label_B)
+        ui.link('Goto B', '#target_B')  # HIDE
+
+
+@doc.demo('Links to other pages', '''
+    You can link to other pages by providing the link target as path or function reference.
+''')
+def link_to_other_page():
+    @ui.page('/some_other_page')
+    def my_page():
+        ui.label('This is another page')
+
+    ui.label('Go to other page')
+    ui.link('... with path', '/some_other_page')
+    ui.link('... with function reference', my_page)
+
+
+@doc.demo('Link from images and other elements', '''
+    By nesting elements inside a link you can make the whole element clickable.
+    This works with all elements but is most useful for non-interactive elements like 
+    [ui.image](/documentation/image), [ui.avatar](/documentation/image) etc.
+''')
+def link_from_elements():
+    with ui.link(target='https://github.com/zauberzeug/nicegui'):
+        ui.image('https://picsum.photos/id/41/640/360').classes('w-64')
+
+
+doc.reference(ui.link)

+ 42 - 0
website/documentation/content/log_documentation.py

@@ -0,0 +1,42 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.log)
+def main_demo() -> None:
+    from datetime import datetime
+
+    log = ui.log(max_lines=10).classes('w-full h-20')
+    ui.button('Log time', on_click=lambda: log.push(datetime.now().strftime('%X.%f')[:-5]))
+
+
+@doc.demo('Attach to a logger', '''
+    You can attach a `ui.log` element to a Python logger object so that log messages are pushed to the log element.
+''')
+def logger_handler():
+    import logging
+    from datetime import datetime
+
+    logger = logging.getLogger()
+
+    class LogElementHandler(logging.Handler):
+        """A logging handler that emits messages to a log element."""
+
+        def __init__(self, element: ui.log, level: int = logging.NOTSET) -> None:
+            self.element = element
+            super().__init__(level)
+
+        def emit(self, record: logging.LogRecord) -> None:
+            try:
+                msg = self.format(record)
+                self.element.push(msg)
+            except Exception:
+                self.handleError(record)
+
+    log = ui.log(max_lines=10).classes('w-full')
+    logger.addHandler(LogElementHandler(log))
+    ui.button('Log time', on_click=lambda: logger.warning(datetime.now().strftime('%X.%f')[:-5]))
+
+
+doc.reference(ui.log)

+ 58 - 0
website/documentation/content/markdown_documentation.py

@@ -0,0 +1,58 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.markdown)
+def main_demo() -> None:
+    ui.markdown('''This is **Markdown**.''')
+
+
+@doc.demo('Markdown with indentation', '''
+    Common indentation is automatically stripped from the beginning of each line.
+    So you can indent markdown elements, and they will still be rendered correctly.
+''')
+def markdown_with_indentation():
+    ui.markdown('''
+        ### Example
+
+        This line is not indented.
+
+            This block is indented.
+            Thus it is rendered as source code.
+        
+        This is normal text again.
+    ''')
+
+
+@doc.demo('Markdown with code blocks', '''
+    You can use code blocks to show code examples.
+    If you specify the language after the opening triple backticks, the code will be syntax highlighted.
+    See [the Pygments website](https://pygments.org/languages/) for a list of supported languages.
+''')
+def markdown_with_code_blocks():
+    ui.markdown('''
+        ```python
+        from nicegui import ui
+
+        ui.label('Hello World!')
+
+        ui.run(dark=True)
+        ```
+    ''')
+
+
+@doc.demo('Markdown tables', '''
+    By activating the "tables" extra, you can use Markdown tables.
+    See the [markdown2 documentation](https://github.com/trentm/python-markdown2/wiki/Extras#implemented-extras) for a list of available extras.
+''')
+def markdown_tables():
+    ui.markdown('''
+        | First name | Last name |
+        | ---------- | --------- |
+        | Max        | Planck    |
+        | Marie      | Curie     |
+    ''', extras=['tables'])
+
+
+doc.reference(ui.markdown)

+ 20 - 0
website/documentation/content/menu_documentation.py

@@ -0,0 +1,20 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.menu)
+def main_demo() -> None:
+    with ui.row().classes('w-full items-center'):
+        result = ui.label().classes('mr-auto')
+        with ui.button(icon='menu'):
+            with ui.menu() as menu:
+                ui.menu_item('Menu item 1', lambda: result.set_text('Selected item 1'))
+                ui.menu_item('Menu item 2', lambda: result.set_text('Selected item 2'))
+                ui.menu_item('Menu item 3 (keep open)',
+                             lambda: result.set_text('Selected item 3'), auto_close=False)
+                ui.separator()
+                ui.menu_item('Close', on_click=menu.close)
+
+
+doc.reference(ui.menu)

+ 15 - 0
website/documentation/content/mermaid_documentation.py

@@ -0,0 +1,15 @@
+from nicegui import ui
+
+from . import doc
+
+
+@doc.demo(ui.mermaid)
+def main_demo() -> None:
+    ui.mermaid('''
+    graph LR;
+        A --> B;
+        A --> C;
+    ''')
+
+
+doc.reference(ui.mermaid)

+ 0 - 0
website/documentation/content/more/__init__.py


+ 0 - 15
website/documentation/content/more/add_static_files_documentation.py

@@ -1,15 +0,0 @@
-from nicegui import app, ui
-
-from ...model import UiElementDocumentation
-
-
-class AddStaticFilesDocumentation(UiElementDocumentation, element=app.add_static_files):
-
-    def main_demo(self) -> None:
-        from nicegui import app
-
-        app.add_static_files('/examples', 'examples')
-        ui.label('Some NiceGUI Examples').classes('text-h5')
-        ui.link('AI interface', '/examples/ai_interface/main.py')
-        ui.link('Custom FastAPI app', '/examples/fastapi/main.py')
-        ui.link('Authentication', '/examples/authentication/main.py')

+ 0 - 189
website/documentation/content/more/aggrid_documentation.py

@@ -1,189 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class AgGridDocumentation(UiElementDocumentation, element=ui.aggrid):
-
-    def main_demo(self) -> None:
-        grid = ui.aggrid({
-            'defaultColDef': {'flex': 1},
-            'columnDefs': [
-                {'headerName': 'Name', 'field': 'name'},
-                {'headerName': 'Age', 'field': 'age'},
-                {'headerName': 'Parent', 'field': 'parent', 'hide': True},
-            ],
-            'rowData': [
-                {'name': 'Alice', 'age': 18, 'parent': 'David'},
-                {'name': 'Bob', 'age': 21, 'parent': 'Eve'},
-                {'name': 'Carol', 'age': 42, 'parent': 'Frank'},
-            ],
-            'rowSelection': 'multiple',
-        }).classes('max-h-40')
-
-        def update():
-            grid.options['rowData'][0]['age'] += 1
-            grid.update()
-
-        ui.button('Update', on_click=update)
-        ui.button('Select all', on_click=lambda: grid.call_api_method('selectAll'))
-        ui.button('Show parent', on_click=lambda: grid.call_column_api_method('setColumnVisible', 'parent', True))
-
-    def more(self) -> None:
-        @self.demo('Select AG Grid Rows', '''
-            You can add checkboxes to grid cells to allow the user to select single or multiple rows.
-
-            To retrieve the currently selected rows, use the `get_selected_rows` method.
-            This method returns a list of rows as dictionaries.
-
-            If `rowSelection` is set to `'single'` or to get the first selected row,
-            you can also use the `get_selected_row` method.
-            This method returns a single row as a dictionary or `None` if no row is selected.
-
-            See the [AG Grid documentation](https://www.ag-grid.com/javascript-data-grid/row-selection/#example-single-row-selection) for more information.
-        ''')
-        def aggrid_with_selectable_rows():
-            grid = ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'Name', 'field': 'name', 'checkboxSelection': True},
-                    {'headerName': 'Age', 'field': 'age'},
-                ],
-                'rowData': [
-                    {'name': 'Alice', 'age': 18},
-                    {'name': 'Bob', 'age': 21},
-                    {'name': 'Carol', 'age': 42},
-                ],
-                'rowSelection': 'multiple',
-            }).classes('max-h-40')
-
-            async def output_selected_rows():
-                rows = await grid.get_selected_rows()
-                if rows:
-                    for row in rows:
-                        ui.notify(f"{row['name']}, {row['age']}")
-                else:
-                    ui.notify('No rows selected.')
-
-            async def output_selected_row():
-                row = await grid.get_selected_row()
-                if row:
-                    ui.notify(f"{row['name']}, {row['age']}")
-                else:
-                    ui.notify('No row selected!')
-
-            ui.button('Output selected rows', on_click=output_selected_rows)
-            ui.button('Output selected row', on_click=output_selected_row)
-
-        @self.demo('Filter Rows using Mini Filters', '''
-            You can add [mini filters](https://ag-grid.com/javascript-data-grid/filter-set-mini-filter/)
-            to the header of each column to filter the rows.
-            
-            Note how the "agTextColumnFilter" matches individual characters, like "a" in "Alice" and "Carol",
-            while the "agNumberColumnFilter" matches the entire number, like "18" and "21", but not "1".
-        ''')
-        def aggrid_with_minifilters():
-            ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'Name', 'field': 'name', 'filter': 'agTextColumnFilter', 'floatingFilter': True},
-                    {'headerName': 'Age', 'field': 'age', 'filter': 'agNumberColumnFilter', 'floatingFilter': True},
-                ],
-                'rowData': [
-                    {'name': 'Alice', 'age': 18},
-                    {'name': 'Bob', 'age': 21},
-                    {'name': 'Carol', 'age': 42},
-                ],
-            }).classes('max-h-40')
-
-        @self.demo('AG Grid with Conditional Cell Formatting', '''
-            This demo shows how to use [cellClassRules](https://www.ag-grid.com/javascript-grid-cell-styles/#cell-class-rules)
-            to conditionally format cells based on their values.
-        ''')
-        def aggrid_with_conditional_cell_formatting():
-            ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'Name', 'field': 'name'},
-                    {'headerName': 'Age', 'field': 'age', 'cellClassRules': {
-                        'bg-red-300': 'x < 21',
-                        'bg-green-300': 'x >= 21',
-                    }},
-                ],
-                'rowData': [
-                    {'name': 'Alice', 'age': 18},
-                    {'name': 'Bob', 'age': 21},
-                    {'name': 'Carol', 'age': 42},
-                ],
-            })
-
-        @self.demo('Create Grid from Pandas DataFrame', '''
-            You can create an AG Grid from a Pandas DataFrame using the `from_pandas` method.
-            This method takes a Pandas DataFrame as input and returns an AG Grid.
-        ''')
-        def aggrid_from_pandas():
-            import pandas as pd
-
-            df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
-            ui.aggrid.from_pandas(df).classes('max-h-40')
-
-        @self.demo('Render columns as HTML', '''
-            You can render columns as HTML by passing a list of column indices to the `html_columns` argument.
-        ''')
-        def aggrid_with_html_columns():
-            ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'Name', 'field': 'name'},
-                    {'headerName': 'URL', 'field': 'url'},
-                ],
-                'rowData': [
-                    {'name': 'Google', 'url': '<a href="https://google.com">https://google.com</a>'},
-                    {'name': 'Facebook', 'url': '<a href="https://facebook.com">https://facebook.com</a>'},
-                ],
-            }, html_columns=[1])
-
-        @self.demo('Respond to an AG Grid event', '''
-            All AG Grid events are passed through to NiceGUI via the AG Grid global listener.
-            These events can be subscribed to using the `.on()` method.
-        ''')
-        def aggrid_respond_to_event():
-            ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'Name', 'field': 'name'},
-                    {'headerName': 'Age', 'field': 'age'},
-                ],
-                'rowData': [
-                    {'name': 'Alice', 'age': 18},
-                    {'name': 'Bob', 'age': 21},
-                    {'name': 'Carol', 'age': 42},
-                ],
-            }).on('cellClicked', lambda event: ui.notify(f'Cell value: {event.args["value"]}'))
-
-        @self.demo('AG Grid with complex objects', '''
-            You can use nested complex objects in AG Grid by separating the field names with a period.
-            (This is the reason why keys in `rowData` are not allowed to contain periods.)
-        ''')
-        def aggrid_with_complex_objects():
-            ui.aggrid({
-                'columnDefs': [
-                    {'headerName': 'First name', 'field': 'name.first'},
-                    {'headerName': 'Last name', 'field': 'name.last'},
-                    {'headerName': 'Age', 'field': 'age'}
-                ],
-                'rowData': [
-                    {'name': {'first': 'Alice', 'last': 'Adams'}, 'age': 18},
-                    {'name': {'first': 'Bob', 'last': 'Brown'}, 'age': 21},
-                    {'name': {'first': 'Carol', 'last': 'Clark'}, 'age': 42},
-                ],
-            }).classes('max-h-40')
-
-        @self.demo('AG Grid with dynamic row height', '''
-            You can set the height of individual rows by passing a function to the `getRowHeight` argument.
-        ''')
-        def aggrid_with_dynamic_row_height():
-            ui.aggrid({
-                'columnDefs': [{'field': 'name'}, {'field': 'age'}],
-                'rowData': [
-                    {'name': 'Alice', 'age': '18'},
-                    {'name': 'Bob', 'age': '21'},
-                    {'name': 'Carol', 'age': '42'},
-                ],
-                ':getRowHeight': 'params => params.data.age > 35 ? 50 : 25',
-            }).classes('max-h-40')

+ 0 - 23
website/documentation/content/more/audio_documentation.py

@@ -1,23 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class AudioDocumentation(UiElementDocumentation, element=ui.audio):
-
-    def main_demo(self) -> None:
-        a = ui.audio('https://cdn.pixabay.com/download/audio/2022/02/22/audio_d1718ab41b.mp3')
-        a.on('ended', lambda _: ui.notify('Audio playback completed'))
-
-        ui.button(on_click=lambda: a.props('muted'), icon='volume_off').props('outline')
-        ui.button(on_click=lambda: a.props(remove='muted'), icon='volume_up').props('outline')
-
-    def more(self) -> None:
-        @self.demo('Control the audio element', '''
-            This demo shows how to play, pause and seek programmatically.
-        ''')
-        def control_demo() -> None:
-            a = ui.audio('https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3')
-            ui.button('Play', on_click=a.play)
-            ui.button('Pause', on_click=a.pause)
-            ui.button('Jump to 0:30', on_click=lambda: a.seek(30))

+ 0 - 18
website/documentation/content/more/avatar_documentation.py

@@ -1,18 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class AvatarDocumentation(UiElementDocumentation, element=ui.avatar):
-
-    def main_demo(self) -> None:
-        ui.avatar('favorite_border', text_color='grey-11', square=True)
-        ui.avatar('img:https://nicegui.io/logo_square.png', color='blue-2')
-
-    def more(self) -> None:
-        @self.demo('Photos', '''
-            To use a photo as an avatar, you can use `ui.image` within `ui.avatar`.
-        ''')
-        def photos() -> None:
-            with ui.avatar():
-                ui.image('https://robohash.org/robot?bgset=bg2')

+ 0 - 10
website/documentation/content/more/badge_documentation.py

@@ -1,10 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class BadgeDocumentation(UiElementDocumentation, element=ui.badge):
-
-    def main_demo(self) -> None:
-        with ui.button('Click me!', on_click=lambda: badge.set_text(int(badge.text) + 1)):
-            badge = ui.badge('0', color='red').props('floating')

+ 0 - 60
website/documentation/content/more/button_documentation.py

@@ -1,60 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ButtonDocumentation(UiElementDocumentation, element=ui.button):
-
-    def main_demo(self) -> None:
-        ui.button('Click me!', on_click=lambda: ui.notify('You clicked me!'))
-
-    def more(self) -> None:
-        @self.demo('Icons', '''
-            You can also add an icon to a button.
-        ''')
-        def icons() -> None:
-            with ui.row():
-                ui.button('demo', icon='history')
-                ui.button(icon='thumb_up')
-                with ui.button():
-                    ui.label('sub-elements')
-                    ui.image('https://picsum.photos/id/377/640/360') \
-                        .classes('rounded-full w-16 h-16 ml-4')
-
-        @self.demo('Await button click', '''
-            Sometimes it is convenient to wait for a button click before continuing the execution.
-        ''')
-        async def await_button_click() -> None:
-            # @ui.page('/')
-            # async def index():
-                b = ui.button('Step')
-                await b.clicked()
-                ui.label('One')
-                await b.clicked()
-                ui.label('Two')
-                await b.clicked()
-                ui.label('Three')
-
-        @self.demo('Disable button with a context manager', '''
-            This showcases a context manager that can be used to disable a button for the duration of an async process.
-        ''')
-        def disable_context_manager() -> None:
-            from contextlib import contextmanager
-
-            import httpx
-
-            @contextmanager
-            def disable(button: ui.button):
-                button.disable()
-                try:
-                    yield
-                finally:
-                    button.enable()
-
-            async def get_slow_response(button: ui.button) -> None:
-                with disable(button):
-                    async with httpx.AsyncClient() as client:
-                        response = await client.get('https://httpbin.org/delay/1', timeout=5)
-                        ui.notify(f'Response code: {response.status_code}')
-
-            ui.button('Get slow response', on_click=lambda e: get_slow_response(e.sender))

+ 0 - 50
website/documentation/content/more/card_documentation.py

@@ -1,50 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class CardDocumentation(UiElementDocumentation, element=ui.card):
-
-    def main_demo(self) -> None:
-        with ui.card().tight():
-            ui.image('https://picsum.photos/id/684/640/360')
-            with ui.card_section():
-                ui.label('Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...')
-
-    def more(self) -> None:
-        @self.demo('Card without shadow', '''
-            You can remove the shadow from a card by adding the `no-shadow` class.
-            The following demo shows a 1 pixel wide border instead.
-        ''')
-        def card_without_shadow() -> None:
-            with ui.card().classes('no-shadow border-[1px]'):
-                ui.label('See, no shadow!')
-
-        @self.demo('The issue with nested borders', '''
-            The following example shows a table nested in a card.
-            Cards have a default padding in NiceGUI, so the table is not flush with the card's border.
-            The table has the `flat` and `bordered` props set, so it should have a border.
-            However, due to the way QCard is designed, the border is not visible (first card).
-            There are two ways to fix this:
-
-            - To get the original QCard behavior, use the `tight` method (second card).
-                It removes the padding and the table border collapses with the card border.
-            
-            - To preserve the padding _and_ the table border, move the table into another container like a `ui.row` (third card).
-
-            See https://github.com/zauberzeug/nicegui/issues/726 for more information.
-        ''')
-        def custom_context_menu() -> None:
-            columns = [{'name': 'age', 'label': 'Age', 'field': 'age'}]
-            rows = [{'age': '16'}, {'age': '18'}, {'age': '21'}]
-
-            with ui.row():
-                with ui.card():
-                    ui.table(columns, rows).props('flat bordered')
-
-                with ui.card().tight():
-                    ui.table(columns, rows).props('flat bordered')
-
-                with ui.card():
-                    with ui.row():
-                        ui.table(columns, rows).props('flat bordered')

+ 0 - 15
website/documentation/content/more/carousel_documentation.py

@@ -1,15 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class CarouselDocumentation(UiElementDocumentation, element=ui.carousel):
-
-    def main_demo(self) -> None:
-        with ui.carousel(animated=True, arrows=True, navigation=True).props('height=180px'):
-            with ui.carousel_slide().classes('p-0'):
-                ui.image('https://picsum.photos/id/30/270/180').classes('w-[270px]')
-            with ui.carousel_slide().classes('p-0'):
-                ui.image('https://picsum.photos/id/31/270/180').classes('w-[270px]')
-            with ui.carousel_slide().classes('p-0'):
-                ui.image('https://picsum.photos/id/32/270/180').classes('w-[270px]')

+ 0 - 40
website/documentation/content/more/chat_message_documentation.py

@@ -1,40 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ChatMessageDocumentation(UiElementDocumentation, element=ui.chat_message):
-
-    def main_demo(self) -> None:
-        ui.chat_message('Hello NiceGUI!',
-                        name='Robot',
-                        stamp='now',
-                        avatar='https://robohash.org/ui')
-
-    def more(self) -> None:
-        @self.demo('HTML text', '''
-            Using the `text_html` parameter, you can send HTML text to the chat.
-        ''')
-        def html_text():
-            ui.chat_message('Without <strong>HTML</strong>')
-            ui.chat_message('With <strong>HTML</strong>', text_html=True)
-
-        @self.demo('Newline', '''
-            You can use newlines in the chat message.
-        ''')
-        def newline():
-            ui.chat_message('This is a\nlong line!')
-
-        @self.demo('Multi-part messages', '''
-            You can send multiple message parts by passing a list of strings.
-        ''')
-        def multiple_messages():
-            ui.chat_message(['Hi! 😀', 'How are you?'])
-
-        @self.demo('Chat message with child elements', '''
-            You can add child elements to a chat message.
-        ''')
-        def child_elements():
-            with ui.chat_message():
-                ui.label('Guess where I am!')
-                ui.image('https://picsum.photos/id/249/640/360').classes('w-64')

+ 0 - 10
website/documentation/content/more/checkbox_documentation.py

@@ -1,10 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class CheckboxDocumentation(UiElementDocumentation, element=ui.checkbox):
-
-    def main_demo(self) -> None:
-        checkbox = ui.checkbox('check me')
-        ui.label('Check!').bind_visibility_from(checkbox, 'value')

+ 0 - 24
website/documentation/content/more/circular_progress_documentation.py

@@ -1,24 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class CircularProgressDocumentation(UiElementDocumentation, element=ui.circular_progress):
-
-    def main_demo(self) -> None:
-        slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
-        ui.circular_progress().bind_value_from(slider, 'value')
-
-    def more(self) -> None:
-        @self.demo('Nested Elements', '''
-            You can put any element like icon, button etc inside a circular progress using the `with` statement.
-            Just make sure it fits the bounds and disable the default behavior of showing the value.
-        ''')
-        def icon() -> None:
-            with ui.row().classes('items-center m-auto'):
-                with ui.circular_progress(value=0.1, show_value=False) as progress:
-                    ui.button(
-                        icon='star',
-                        on_click=lambda: progress.set_value(progress.value + 0.1)
-                    ).props('flat round')
-                ui.label('click to increase progress')

+ 0 - 15
website/documentation/content/more/code_documentation.py

@@ -1,15 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class CodeDocumentation(UiElementDocumentation, element=ui.code):
-
-    def main_demo(self) -> None:
-        ui.code('''
-            from nicegui import ui
-            
-            ui.label('Code inception!')
-                
-            ui.run()
-        ''').classes('w-full')

+ 0 - 11
website/documentation/content/more/color_input_documentation.py

@@ -1,11 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ColorInputDocumentation(UiElementDocumentation, element=ui.color_input):
-
-    def main_demo(self) -> None:
-        label = ui.label('Change my color!')
-        ui.color_input(label='Color', value='#000000',
-                       on_change=lambda e: label.style(f'color:{e.value}'))

+ 0 - 10
website/documentation/content/more/color_picker_documentation.py

@@ -1,10 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ColorPickerDocumentation(UiElementDocumentation, element=ui.color_picker):
-
-    def main_demo(self) -> None:
-        with ui.button(icon='colorize') as button:
-            ui.color_picker(on_pick=lambda e: button.style(f'background-color:{e.color}!important'))

+ 0 - 13
website/documentation/content/more/colors_documentation.py

@@ -1,13 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ColorsDocumentation(UiElementDocumentation, element=ui.colors):
-
-    def main_demo(self) -> None:
-        # ui.button('Default', on_click=lambda: ui.colors())
-        # ui.button('Gray', on_click=lambda: ui.colors(primary='#555'))
-        # END OF DEMO
-        b1 = ui.button('Default', on_click=lambda: [b.classes(replace='!bg-primary') for b in [b1, b2]])
-        b2 = ui.button('Gray', on_click=lambda: [b.classes(replace='!bg-[#555]') for b in [b1, b2]])

+ 0 - 24
website/documentation/content/more/column_documentation.py

@@ -1,24 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ColumnDocumentation(UiElementDocumentation, element=ui.column):
-
-    def main_demo(self) -> None:
-        with ui.column():
-            ui.label('label 1')
-            ui.label('label 2')
-            ui.label('label 3')
-
-    def more(self) -> None:
-        @self.demo('Masonry or Pinterest-Style Layout', '''
-            To create a masonry/Pinterest layout, the normal `ui.column` can not be used.
-            But it can be achieved with a few TailwindCSS classes.
-        ''')
-        def masonry() -> None:
-            with ui.element('div').classes('columns-3 w-full gap-2'):
-                for i, height in enumerate([50, 50, 50, 150, 100, 50]):
-                    tailwind = f'mb-2 p-2 h-[{height}px] bg-blue-100 break-inside-avoid'
-                    with ui.card().classes(tailwind):
-                        ui.label(f'Card #{i+1}')

+ 0 - 14
website/documentation/content/more/context_menu_documentation.py

@@ -1,14 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ContextMenuDocumentation(UiElementDocumentation, element=ui.context_menu):
-
-    def main_demo(self) -> None:
-        with ui.image('https://picsum.photos/id/377/640/360'):
-            with ui.context_menu():
-                ui.menu_item('Flip horizontally')
-                ui.menu_item('Flip vertically')
-                ui.separator()
-                ui.menu_item('Reset')

+ 0 - 24
website/documentation/content/more/dark_mode_documentation.py

@@ -1,24 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-from ...windows import WINDOW_BG_COLORS
-
-
-class DarkModeDocumentation(UiElementDocumentation, element=ui.dark_mode):
-
-    def main_demo(self) -> None:
-        # dark = ui.dark_mode()
-        # ui.label('Switch mode:')
-        # ui.button('Dark', on_click=dark.enable)
-        # ui.button('Light', on_click=dark.disable)
-        # END OF DEMO
-        l = ui.label('Switch mode:')
-        c = l.parent_slot.parent
-        ui.button('Dark', on_click=lambda: (
-            l.style('color: white'),
-            c.style(f'background-color: {WINDOW_BG_COLORS["browser"][1]}'),
-        ))
-        ui.button('Light', on_click=lambda: (
-            l.style('color: black'),
-            c.style(f'background-color: {WINDOW_BG_COLORS["browser"][0]}'),
-        ))

+ 0 - 34
website/documentation/content/more/date_documentation.py

@@ -1,34 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class DateDocumentation(UiElementDocumentation, element=ui.date):
-
-    def main_demo(self) -> None:
-        ui.date(value='2023-01-01', on_change=lambda e: result.set_text(e.value))
-        result = ui.label()
-
-    def more(self) -> None:
-        @self.demo('Input element with date picker', '''
-            This demo shows how to implement a date picker with an input element.
-            We place an icon in the input element's append slot.
-            When the icon is clicked, we open a menu with a date picker.
-
-            The date is bound to the input element's value.
-            So both the input element and the date picker will stay in sync whenever the date is changed.
-        ''')
-        def date():
-            with ui.input('Date') as date:
-                with date.add_slot('append'):
-                    ui.icon('edit_calendar').on('click', lambda: menu.open()).classes('cursor-pointer')
-                with ui.menu() as menu:
-                    ui.date().bind_value(date)
-
-        @self.demo('Date filter', '''
-            This demo shows how to filter the dates in a date picker.
-            In order to pass a function to the date picker, we use the `:options` property.
-            The leading `:` tells NiceGUI that the value is a JavaScript expression.
-        ''')
-        def date_filter():
-            ui.date().props('''default-year-month=2023/01 :options="date => date <= '2023/01/15'"''')

+ 0 - 48
website/documentation/content/more/dialog_documentation.py

@@ -1,48 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class DialogDocumentation(UiElementDocumentation, element=ui.dialog):
-
-    def main_demo(self) -> None:
-        with ui.dialog() as dialog, ui.card():
-            ui.label('Hello world!')
-            ui.button('Close', on_click=dialog.close)
-
-        ui.button('Open a dialog', on_click=dialog.open)
-
-    def more(self) -> None:
-        @self.demo('Awaitable dialog', '''
-            Dialogs can be awaited.
-            Use the `submit` method to close the dialog and return a result.
-            Canceling the dialog by clicking in the background or pressing the escape key yields `None`.
-        ''')
-        def async_dialog_demo():
-            with ui.dialog() as dialog, ui.card():
-                ui.label('Are you sure?')
-                with ui.row():
-                    ui.button('Yes', on_click=lambda: dialog.submit('Yes'))
-                    ui.button('No', on_click=lambda: dialog.submit('No'))
-
-            async def show():
-                result = await dialog
-                ui.notify(f'You chose {result}')
-
-            ui.button('Await a dialog', on_click=show)
-
-        @self.demo('Replacing content', '''
-            The content of a dialog can be changed.
-        ''')
-        def replace_content():
-            def replace():
-                dialog.clear()
-                with dialog, ui.card().classes('w-64 h-64'):
-                    ui.label('New Content')
-                dialog.open()
-
-            with ui.dialog() as dialog, ui.card():
-                ui.label('Hello world!')
-
-            ui.button('Open', on_click=dialog.open)
-            ui.button('Replace', on_click=replace)

+ 0 - 16
website/documentation/content/more/download_documentation.py

@@ -1,16 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class DownloadDocumentation(UiElementDocumentation, element=ui.download):
-
-    def main_demo(self) -> None:
-        ui.button('Logo', on_click=lambda: ui.download('https://nicegui.io/logo.png'))
-
-    def more(self) -> None:
-        @self.demo('Download raw bytes from memory', '''
-            The `download` function can also be used to download raw bytes from memory.
-        ''')
-        def raw_bytes():
-            ui.button('Download', on_click=lambda: ui.download(b'Hello World', 'hello.txt'))

+ 0 - 47
website/documentation/content/more/echart_documentation.py

@@ -1,47 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class EChartDocumentation(UiElementDocumentation, element=ui.echart):
-
-    def main_demo(self) -> None:
-        from random import random
-
-        echart = ui.echart({
-            'xAxis': {'type': 'value'},
-            'yAxis': {'type': 'category', 'data': ['A', 'B'], 'inverse': True},
-            'legend': {'textStyle': {'color': 'gray'}},
-            'series': [
-                {'type': 'bar', 'name': 'Alpha', 'data': [0.1, 0.2]},
-                {'type': 'bar', 'name': 'Beta', 'data': [0.3, 0.4]},
-            ],
-        })
-
-        def update():
-            echart.options['series'][0]['data'][0] = random()
-            echart.update()
-
-        ui.button('Update', on_click=update)
-
-    def more(self) -> None:
-        @self.demo('EChart with clickable points', '''
-            You can register a callback for an event when a series point is clicked.
-        ''')
-        def clickable_points() -> None:
-            ui.echart({
-                'xAxis': {'type': 'category'},
-                'yAxis': {'type': 'value'},
-                'series': [{'type': 'line', 'data': [20, 10, 30, 50, 40, 30]}],
-            }, on_point_click=ui.notify)
-
-        @self.demo('EChart with dynamic properties', '''
-            Dynamic properties can be passed to chart elements to customize them such as apply an axis label format.
-            To make a property dynamic, prefix a colon ":" to the property name.
-        ''')
-        def dynamic_properties() -> None:
-            ui.echart({
-                'xAxis': {'type': 'category'},
-                'yAxis': {'axisLabel': {':formatter': 'value => "$" + value'}},
-                'series': [{'type': 'line', 'data': [5, 8, 13, 21, 34, 55]}],
-            })

+ 0 - 11
website/documentation/content/more/editor_documentation.py

@@ -1,11 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class EditorDocumentation(UiElementDocumentation, element=ui.editor):
-
-    def main_demo(self) -> None:
-        editor = ui.editor(placeholder='Type something here')
-        ui.markdown().bind_content_from(editor, 'value',
-                                        backward=lambda v: f'HTML code:\n```\n{v}\n```')

+ 0 - 68
website/documentation/content/more/element_documentation.py

@@ -1,68 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ElementDocumentation(UiElementDocumentation, element=ui.element):
-
-    def main_demo(self) -> None:
-        with ui.element('div').classes('p-2 bg-blue-100'):
-            ui.label('inside a colored div')
-
-    def more(self) -> None:
-        @self.demo('Move elements', '''
-            This demo shows how to move elements between or within containers.
-        ''')
-        def move_elements() -> None:
-            with ui.card() as a:
-                ui.label('A')
-                x = ui.label('X')
-
-            with ui.card() as b:
-                ui.label('B')
-
-            ui.button('Move X to A', on_click=lambda: x.move(a))
-            ui.button('Move X to B', on_click=lambda: x.move(b))
-            ui.button('Move X to top', on_click=lambda: x.move(target_index=0))
-
-        @self.demo('Default props', '''
-            You can set default props for all elements of a certain class.
-            This way you can avoid repeating the same props over and over again.
-            
-            Default props only apply to elements created after the default props were set.
-            Subclasses inherit the default props of their parent class.
-        ''')
-        def default_props() -> None:
-            ui.button.default_props('rounded outline')
-            ui.button('Button A')
-            ui.button('Button B')
-            # END OF DEMO
-            ui.button.default_props(remove='rounded outline')
-
-        @self.demo('Default classes', '''
-            You can set default classes for all elements of a certain class.
-            This way you can avoid repeating the same classes over and over again.
-            
-            Default classes only apply to elements created after the default classes were set.
-            Subclasses inherit the default classes of their parent class.
-        ''')
-        def default_classes() -> None:
-            ui.label.default_classes('bg-blue-100 p-2')
-            ui.label('Label A')
-            ui.label('Label B')
-            # END OF DEMO
-            ui.label.default_classes(remove='bg-blue-100 p-2')
-
-        @self.demo('Default style', '''
-            You can set a default style for all elements of a certain class.
-            This way you can avoid repeating the same style over and over again.
-            
-            A default style only applies to elements created after the default style was set.
-            Subclasses inherit the default style of their parent class.
-        ''')
-        def default_style() -> None:
-            ui.label.default_style('color: tomato')
-            ui.label('Label A')
-            ui.label('Label B')
-            # END OF DEMO
-            ui.label.default_style(remove='color: tomato')

+ 0 - 20
website/documentation/content/more/expansion_documentation.py

@@ -1,20 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ExpansionDocumentation(UiElementDocumentation, element=ui.expansion):
-
-    def main_demo(self) -> None:
-        with ui.expansion('Expand!', icon='work').classes('w-full'):
-            ui.label('inside the expansion')
-
-    def more(self) -> None:
-        @self.demo('Expansion with Custom Header', '''
-            Instead of setting a plain-text title, you can fill the expansion header with UI elements by adding them to the "header" slot.
-        ''')
-        def expansion_with_custom_header():
-            with ui.expansion() as expansion:
-                with expansion.add_slot('header'):
-                    ui.image('https://nicegui.io/logo.png').classes('w-16')
-                ui.label('What a nice GUI!')

+ 0 - 122
website/documentation/content/more/generic_events_documentation.py

@@ -1,122 +0,0 @@
-from nicegui import context, ui
-
-from ...model import DetailDocumentation
-
-
-class GenericEventsDocumentation(DetailDocumentation, title='Generic Events', name_='generic_events'):
-
-    def content(self) -> None:
-        @self.demo('Generic Events', '''
-            Most UI elements come with predefined events.
-            For example, a `ui.button` like "A" in the demo has an `on_click` parameter that expects a coroutine or function.
-            But you can also use the `on` method to register a generic event handler like for "B".
-            This allows you to register handlers for any event that is supported by JavaScript and Quasar.
-
-            For example, you can register a handler for the `mousemove` event like for "C", even though there is no `on_mousemove` parameter for `ui.button`.
-            Some events, like `mousemove`, are fired very often.
-            To avoid performance issues, you can use the `throttle` parameter to only call the handler every `throttle` seconds ("D").
-
-            The generic event handler can be synchronous or asynchronous and optionally takes `GenericEventArguments` as argument ("E").
-            You can also specify which attributes of the JavaScript or Quasar event should be passed to the handler ("F").
-            This can reduce the amount of data that needs to be transferred between the server and the client.
-
-            Here you can find more information about the events that are supported:
-
-            - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement#events for HTML elements
-            - https://quasar.dev/vue-components for Quasar-based elements (see the "Events" tab on the individual component page)
-        ''')
-        def generic_events_demo() -> None:
-            with ui.row():
-                ui.button('A', on_click=lambda: ui.notify('You clicked the button A.'))
-                ui.button('B').on('click', lambda: ui.notify('You clicked the button B.'))
-            with ui.row():
-                ui.button('C').on('mousemove', lambda: ui.notify('You moved on button C.'))
-                ui.button('D').on('mousemove', lambda: ui.notify('You moved on button D.'), throttle=0.5)
-            with ui.row():
-                ui.button('E').on('mousedown', lambda e: ui.notify(e))
-                ui.button('F').on('mousedown', lambda e: ui.notify(e), ['ctrlKey', 'shiftKey'])
-
-        @self.demo('Specifying event attributes', '''
-            **A list of strings** names the attributes of the JavaScript event object:
-            ```py
-            ui.button().on('click', handle_click, ['clientX', 'clientY'])
-            ```
-
-            **An empty list** requests _no_ attributes:
-            ```py
-            ui.button().on('click', handle_click, [])
-            ```
-
-            **The value `None`** represents _all_ attributes (the default):
-            ```py
-            ui.button().on('click', handle_click, None)
-            ```
-
-            **If the event is called with multiple arguments** like QTable's "row-click" `(evt, row, index) => void`,
-            you can define a list of argument definitions:
-            ```py
-            ui.table(...).on('rowClick', handle_click, [[], ['name'], None])
-            ```
-            In this example the "row-click" event will omit all arguments of the first `evt` argument,
-            send only the "name" attribute of the `row` argument and send the full `index`.
-
-            If the retrieved list of event arguments has length 1, the argument is automatically unpacked.
-            So you can write
-            ```py
-            ui.button().on('click', lambda e: print(e.args['clientX'], flush=True))
-            ```
-            instead of
-            ```py
-            ui.button().on('click', lambda e: print(e.args[0]['clientX'], flush=True))
-            ```
-
-            Note that by default all JSON-serializable attributes of all arguments are sent.
-            This is to simplify registering for new events and discovering their attributes.
-            If bandwidth is an issue, the arguments should be limited to what is actually needed on the server.
-        ''')
-        def event_attributes() -> None:
-            columns = [
-                {'name': 'name', 'label': 'Name', 'field': 'name'},
-                {'name': 'age', 'label': 'Age', 'field': 'age'},
-            ]
-            rows = [
-                {'name': 'Alice', 'age': 42},
-                {'name': 'Bob', 'age': 23},
-            ]
-            ui.table(columns, rows, 'name').on('rowClick', ui.notify, [[], ['name'], None])
-
-        @self.demo('Modifiers', '''
-            You can also include [key modifiers](https://vuejs.org/guide/essentials/event-handling.html#key-modifiers>) (shown in input "A"),
-            modifier combinations (shown in input "B"),
-            and [event modifiers](https://vuejs.org/guide/essentials/event-handling.html#mouse-button-modifiers>) (shown in input "C").
-        ''')
-        def modifiers() -> None:
-            with ui.row():
-                ui.input('A').classes('w-12').on('keydown.space', lambda: ui.notify('You pressed space.'))
-                ui.input('B').classes('w-12').on('keydown.y.shift', lambda: ui.notify('You pressed Shift+Y'))
-                ui.input('C').classes('w-12').on('keydown.once', lambda: ui.notify('You started typing.'))
-
-        @self.demo('Custom events', '''
-            It is fairly easy to emit custom events from JavaScript which can be listened to with `element.on(...)`.
-            This can be useful if you want to call Python code when something happens in JavaScript.
-            In this example we are listening to the `visibilitychange` event of the browser tab.
-        ''')
-        async def custom_events() -> None:
-            tabwatch = ui.checkbox('Watch browser tab re-entering') \
-                .on('tabvisible', lambda: ui.notify('Welcome back!') if tabwatch.value else None, args=[])
-            ui.add_head_html(f'''
-                <script>
-                document.addEventListener('visibilitychange', () => {{
-                    if (document.visibilityState === 'visible')
-                        getElement({tabwatch.id}).$emit('tabvisible');
-                }});
-                </script>
-            ''')
-            # END OF DEMO
-            await context.get_client().connected()
-            ui.run_javascript(f'''
-                document.addEventListener('visibilitychange', () => {{
-                    if (document.visibilityState === 'visible')
-                        getElement({tabwatch.id}).$emit('tabvisible');
-                }});
-            ''')

+ 0 - 17
website/documentation/content/more/grid_documentation.py

@@ -1,17 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class GridDocumentation(UiElementDocumentation, element=ui.grid):
-
-    def main_demo(self) -> None:
-        with ui.grid(columns=2):
-            ui.label('Name:')
-            ui.label('Tom')
-
-            ui.label('Age:')
-            ui.label('42')
-
-            ui.label('Height:')
-            ui.label('1.80m')

+ 0 - 73
website/documentation/content/more/highchart_documentation.py

@@ -1,73 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class HighchartDocumentation(UiElementDocumentation, element=ui.highchart):
-
-    def main_demo(self) -> None:
-        from random import random
-
-        chart = ui.highchart({
-            'title': False,
-            'chart': {'type': 'bar'},
-            'xAxis': {'categories': ['A', 'B']},
-            'series': [
-                {'name': 'Alpha', 'data': [0.1, 0.2]},
-                {'name': 'Beta', 'data': [0.3, 0.4]},
-            ],
-        }).classes('w-full h-64')
-
-        def update():
-            chart.options['series'][0]['data'][0] = random()
-            chart.update()
-
-        ui.button('Update', on_click=update)
-
-    def more(self) -> None:
-        @self.demo('Chart with extra dependencies', '''
-            To use a chart type that is not included in the default dependencies, you can specify extra dependencies.
-            This demo shows a solid gauge chart.
-        ''')
-        def extra_dependencies() -> None:
-            ui.highchart({
-                'title': False,
-                'chart': {'type': 'solidgauge'},
-                'yAxis': {
-                    'min': 0,
-                    'max': 1,
-                },
-                'series': [
-                    {'data': [0.42]},
-                ],
-            }, extras=['solid-gauge']).classes('w-full h-64')
-
-        @self.demo('Chart with draggable points', '''
-            This chart allows dragging the series points.
-            You can register callbacks for the following events:
-            
-            - `on_point_click`: called when a point is clicked
-            - `on_point_drag_start`: called when a point drag starts
-            - `on_point_drag`: called when a point is dragged
-            - `on_point_drop`: called when a point is dropped
-        ''')
-        def drag() -> None:
-            ui.highchart(
-                {
-                    'title': False,
-                    'plotOptions': {
-                        'series': {
-                            'stickyTracking': False,
-                            'dragDrop': {'draggableY': True, 'dragPrecisionY': 1},
-                        },
-                    },
-                    'series': [
-                        {'name': 'A', 'data': [[20, 10], [30, 20], [40, 30]]},
-                        {'name': 'B', 'data': [[50, 40], [60, 50], [70, 60]]},
-                    ],
-                },
-                extras=['draggable-points'],
-                on_point_click=lambda e: ui.notify(f'Click: {e}'),
-                on_point_drag_start=lambda e: ui.notify(f'Drag start: {e}'),
-                on_point_drop=lambda e: ui.notify(f'Drop: {e}')
-            ).classes('w-full h-64')

+ 0 - 9
website/documentation/content/more/html_documentation.py

@@ -1,9 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class HtmlDocumentation(UiElementDocumentation, element=ui.html):
-
-    def main_demo(self) -> None:
-        ui.html('This is <strong>HTML</strong>.')

+ 0 - 31
website/documentation/content/more/icon_documentation.py

@@ -1,31 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class IconDocumentation(UiElementDocumentation, element=ui.icon):
-
-    def main_demo(self) -> None:
-        ui.icon('thumb_up', color='primary').classes('text-5xl')
-
-    def more(self) -> None:
-        ui.add_head_html('<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet">')
-        ui.add_body_html(
-            '<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
-
-        @self.demo('Eva icons', '''
-            You can use [Eva icons](https://akveo.github.io/eva-icons/) in your app.
-        ''')
-        def eva_icons():
-            # ui.add_head_html('<link href="https://unpkg.com/eva-icons@1.1.3/style/eva-icons.css" rel="stylesheet">')
-
-            ui.element('i').classes('eva eva-github').classes('text-5xl')
-
-        @self.demo('Lottie files', '''
-            You can also use [Lottie files](https://lottiefiles.com/) with animations.
-        ''')
-        def lottie():
-            # ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
-
-            src = 'https://assets5.lottiefiles.com/packages/lf20_MKCnqtNQvg.json'
-            ui.html(f'<lottie-player src="{src}" loop autoplay />').classes('w-24')

+ 0 - 61
website/documentation/content/more/image_documentation.py

@@ -1,61 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class ImageDocumentation(UiElementDocumentation, element=ui.image):
-
-    def main_demo(self) -> None:
-        ui.image('https://picsum.photos/id/377/640/360')
-
-    def more(self) -> None:
-        ui.add_body_html(
-            '<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
-
-        @self.demo('Local files', '''
-            You can use local images as well by passing a path to the image file.
-        ''')
-        def local():
-            ui.image('website/static/logo.png').classes('w-16')
-
-        @self.demo('Base64 string', '''
-            You can also use a Base64 string as image source.
-        ''')
-        def base64():
-            base64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
-            ui.image(base64).classes('w-2 h-2 m-auto')
-
-        @self.demo('PIL image', '''
-            You can also use a PIL image as image source.
-        ''')
-        def pil():
-            import numpy as np
-            from PIL import Image
-
-            image = Image.fromarray(np.random.randint(0, 255, (100, 100), dtype=np.uint8))
-            ui.image(image).classes('w-32')
-
-        @self.demo('Lottie files', '''
-            You can also use [Lottie files](https://lottiefiles.com/) with animations.
-        ''')
-        def lottie():
-            # ui.add_body_html('<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>')
-
-            src = 'https://assets1.lottiefiles.com/datafiles/HN7OcWNnoqje6iXIiZdWzKxvLIbfeCGTmvXmEm1h/data.json'
-            ui.html(f'<lottie-player src="{src}" loop autoplay />').classes('w-full')
-
-        @self.demo('Image link', '''
-            Images can link to another page by wrapping them in a [ui.link](https://nicegui.io/documentation/link).
-        ''')
-        def link():
-            with ui.link(target='https://github.com/zauberzeug/nicegui'):
-                ui.image('https://picsum.photos/id/41/640/360').classes('w-64')
-
-        @self.demo('Force reload', '''
-            You can force an image to reload by calling the `force_reload` method.
-            It will append a timestamp to the image URL, which will make the browser reload the image.
-        ''')
-        def force_reload():
-            img = ui.image('https://picsum.photos/640/360').classes('w-64')
-
-            ui.button('Force reload', on_click=img.force_reload)

+ 0 - 42
website/documentation/content/more/input_documentation.py

@@ -1,42 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class InputDocumentation(UiElementDocumentation, element=ui.input):
-
-    def main_demo(self) -> None:
-        ui.input(label='Text', placeholder='start typing',
-                 on_change=lambda e: result.set_text('you typed: ' + e.value),
-                 validation={'Input too long': lambda value: len(value) < 20})
-        result = ui.label()
-
-    def more(self) -> None:
-
-        @self.demo('Autocompletion', '''
-            The `autocomplete` feature provides suggestions as you type, making input easier and faster.
-            The parameter `options` is a list of strings that contains the available options that will appear.
-        ''')
-        def autocomplete_demo():
-            options = ['AutoComplete', 'NiceGUI', 'Awesome']
-            ui.input(label='Text', placeholder='start typing', autocomplete=options)
-
-        @self.demo('Clearable', '''
-            The `clearable` prop from [Quasar](https://quasar.dev/) adds a button to the input that clears the text.    
-        ''')
-        def clearable():
-            i = ui.input(value='some text').props('clearable')
-            ui.label().bind_text_from(i, 'value')
-
-        @self.demo('Styling', '''
-            Quasar has a lot of [props to change the appearance](https://quasar.dev/vue-components/input).
-            It is even possible to style the underlying input with `input-style` and `input-class` props
-            and use the provided slots to add custom elements.
-        ''')
-        def styling():
-            ui.input(placeholder='start typing').props('rounded outlined dense')
-            ui.input('styling', value='some text') \
-                .props('input-style="color: blue" input-class="font-mono"')
-            with ui.input(value='custom clear button').classes('w-64') as i:
-                ui.button(color='orange-8', on_click=lambda: i.set_value(None), icon='delete') \
-                    .props('flat dense').bind_visibility_from(i, 'value')

+ 0 - 38
website/documentation/content/more/interactive_image_documentation.py

@@ -1,38 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class InteractiveImageDocumentation(UiElementDocumentation, element=ui.interactive_image):
-
-    def main_demo(self) -> None:
-        from nicegui.events import MouseEventArguments
-
-        def mouse_handler(e: MouseEventArguments):
-            color = 'SkyBlue' if e.type == 'mousedown' else 'SteelBlue'
-            ii.content += f'<circle cx="{e.image_x}" cy="{e.image_y}" r="15" fill="none" stroke="{color}" stroke-width="4" />'
-            ui.notify(f'{e.type} at ({e.image_x:.1f}, {e.image_y:.1f})')
-
-        src = 'https://picsum.photos/id/565/640/360'
-        ii = ui.interactive_image(src, on_mouse=mouse_handler, events=['mousedown', 'mouseup'], cross=True)
-
-    def more(self) -> None:
-        @self.demo('Nesting elements', '''
-            You can nest elements inside an interactive image.
-            Use Tailwind classes like "absolute top-0 left-0" to position the label absolutely with respect to the image.
-            Of course this can be done with plain CSS as well.
-        ''')
-        def nesting_elements():
-            with ui.interactive_image('https://picsum.photos/id/147/640/360'):
-                ui.button(on_click=lambda: ui.notify('thumbs up'), icon='thumb_up') \
-                    .props('flat fab color=white') \
-                    .classes('absolute bottom-0 left-0 m-2')
-
-        @self.demo('Force reload', '''
-            You can force an image to reload by calling the `force_reload` method.
-            It will append a timestamp to the image URL, which will make the browser reload the image.
-        ''')
-        def force_reload():
-            img = ui.interactive_image('https://picsum.photos/640/360').classes('w-64')
-
-            ui.button('Force reload', on_click=img.force_reload)

+ 0 - 12
website/documentation/content/more/joystick_documentation.py

@@ -1,12 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class JoystickDocumentation(UiElementDocumentation, element=ui.joystick):
-
-    def main_demo(self) -> None:
-        ui.joystick(color='blue', size=50,
-                    on_move=lambda e: coordinates.set_text(f"{e.x:.3f}, {e.y:.3f}"),
-                    on_end=lambda _: coordinates.set_text('0, 0'))
-        coordinates = ui.label('0, 0')

+ 0 - 24
website/documentation/content/more/json_editor_documentation.py

@@ -1,24 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class JsonEditorDocumentation(UiElementDocumentation, element=ui.json_editor):
-
-    def main_demo(self) -> None:
-        json = {
-            'array': [1, 2, 3],
-            'boolean': True,
-            'color': '#82b92c',
-            None: None,
-            'number': 123,
-            'object': {
-                'a': 'b',
-                'c': 'd',
-            },
-            'time': 1575599819000,
-            'string': 'Hello World',
-        }
-        ui.json_editor({'content': {'json': json}},
-                       on_select=lambda e: ui.notify(f'Select: {e}'),
-                       on_change=lambda e: ui.notify(f'Change: {e}'))

+ 0 - 29
website/documentation/content/more/keyboard_documentation.py

@@ -1,29 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class KeyboardDocumentation(UiElementDocumentation, element=ui.keyboard):
-
-    def main_demo(self) -> None:
-        from nicegui.events import KeyEventArguments
-
-        def handle_key(e: KeyEventArguments):
-            if e.key == 'f' and not e.action.repeat:
-                if e.action.keyup:
-                    ui.notify('f was just released')
-                elif e.action.keydown:
-                    ui.notify('f was just pressed')
-            if e.modifiers.shift and e.action.keydown:
-                if e.key.arrow_left:
-                    ui.notify('going left')
-                elif e.key.arrow_right:
-                    ui.notify('going right')
-                elif e.key.arrow_up:
-                    ui.notify('going up')
-                elif e.key.arrow_down:
-                    ui.notify('going down')
-
-        keyboard = ui.keyboard(on_key=handle_key)
-        ui.label('Key events can be caught globally by using the keyboard element.')
-        ui.checkbox('Track key events').bind_value_to(keyboard, 'active')

+ 0 - 12
website/documentation/content/more/knob_documentation.py

@@ -1,12 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class KnobDocumentation(UiElementDocumentation, element=ui.knob):
-
-    def main_demo(self) -> None:
-        knob = ui.knob(0.3, show_value=True)
-
-        with ui.knob(color='orange', track_color='grey-2').bind_value(knob, 'value'):
-            ui.icon('volume_up')

+ 0 - 27
website/documentation/content/more/label_documentation.py

@@ -1,27 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class LabelDocumentation(UiElementDocumentation, element=ui.label):
-
-    def main_demo(self) -> None:
-        ui.label('some label')
-
-    def more(self) -> None:
-        @self.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'))

+ 0 - 33
website/documentation/content/more/line_plot_documentation.py

@@ -1,33 +0,0 @@
-from nicegui import events, ui
-
-from ...model import UiElementDocumentation
-
-
-class LinePlotDocumentation(UiElementDocumentation, element=ui.line_plot):
-
-    def main_demo(self) -> None:
-        import math
-        from datetime import datetime
-
-        line_plot = ui.line_plot(n=2, limit=20, figsize=(3, 2), update_every=5) \
-            .with_legend(['sin', 'cos'], loc='upper center', ncol=2)
-
-        def update_line_plot() -> None:
-            now = datetime.now()
-            x = now.timestamp()
-            y1 = math.sin(x)
-            y2 = math.cos(x)
-            line_plot.push([now], [[y1], [y2]])
-
-        line_updates = ui.timer(0.1, update_line_plot, active=False)
-        line_checkbox = ui.checkbox('active').bind_value(line_updates, 'active')
-
-        # END OF DEMO
-        def handle_change(e: events.GenericEventArguments) -> None:
-            def turn_off() -> None:
-                line_checkbox.set_value(False)
-                ui.notify('Turning off that line plot to save resources on our live demo server. 😎')
-            line_checkbox.value = e.args
-            if line_checkbox.value:
-                ui.timer(10.0, turn_off, once=True)
-        line_checkbox.on('update:model-value', handle_change, args=[None])

+ 0 - 10
website/documentation/content/more/linear_progress_documentation.py

@@ -1,10 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class LinearProgressDocumentation(UiElementDocumentation, element=ui.linear_progress):
-
-    def main_demo(self) -> None:
-        slider = ui.slider(min=0, max=1, step=0.01, value=0.5)
-        ui.linear_progress().bind_value_from(slider, 'value')

+ 0 - 55
website/documentation/content/more/link_documentation.py

@@ -1,55 +0,0 @@
-from nicegui import ui
-
-from ....style import link_target
-from ...model import UiElementDocumentation
-
-
-class LinkDocumentation(UiElementDocumentation, element=ui.link):
-
-    def main_demo(self) -> None:
-        ui.link('NiceGUI on GitHub', 'https://github.com/zauberzeug/nicegui')
-
-    def more(self) -> None:
-        @self.demo('Navigate on large pages', '''
-            To jump to a specific location within a page you can place linkable anchors with `ui.link_target('target_name')`
-            or simply pass a NiceGUI element as link target.
-        ''')
-        def same_page_links():
-            navigation = ui.row()
-            # ui.link_target('target_A')
-            link_target('target_A', '-10px')  # HIDE
-            ui.label(
-                'Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
-                'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
-            )
-            link_target('target_B', '70px')  # HIDE
-            label_B = ui.label(
-                'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. '
-                'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. '
-                'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
-            )
-            with navigation:
-                ui.link('Goto A', '#target_A')
-                # ui.link('Goto B', label_B)
-                ui.link('Goto B', '#target_B')  # HIDE
-
-        @self.demo('Links to other pages', '''
-            You can link to other pages by providing the link target as path or function reference.
-        ''')
-        def link_to_other_page():
-            @ui.page('/some_other_page')
-            def my_page():
-                ui.label('This is another page')
-
-            ui.label('Go to other page')
-            ui.link('... with path', '/some_other_page')
-            ui.link('... with function reference', my_page)
-
-        @self.demo('Link from images and other elements', '''
-            By nesting elements inside a link you can make the whole element clickable.
-            This works with all elements but is most useful for non-interactive elements like 
-            [ui.image](/documentation/image), [ui.avatar](/documentation/image) etc.
-        ''')
-        def link_from_elements():
-            with ui.link(target='https://github.com/zauberzeug/nicegui'):
-                ui.image('https://picsum.photos/id/41/640/360').classes('w-64')

+ 0 - 40
website/documentation/content/more/log_documentation.py

@@ -1,40 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class LogDocumentation(UiElementDocumentation, element=ui.log):
-
-    def main_demo(self) -> None:
-        from datetime import datetime
-
-        log = ui.log(max_lines=10).classes('w-full h-20')
-        ui.button('Log time', on_click=lambda: log.push(datetime.now().strftime('%X.%f')[:-5]))
-
-    def more(self) -> None:
-        @self.demo('Attach to a logger', '''
-            You can attach a `ui.log` element to a Python logger object so that log messages are pushed to the log element.
-        ''')
-        def logger_handler():
-            import logging
-            from datetime import datetime
-
-            logger = logging.getLogger()
-
-            class LogElementHandler(logging.Handler):
-                """A logging handler that emits messages to a log element."""
-
-                def __init__(self, element: ui.log, level: int = logging.NOTSET) -> None:
-                    self.element = element
-                    super().__init__(level)
-
-                def emit(self, record: logging.LogRecord) -> None:
-                    try:
-                        msg = self.format(record)
-                        self.element.push(msg)
-                    except Exception:
-                        self.handleError(record)
-
-            log = ui.log(max_lines=10).classes('w-full')
-            logger.addHandler(LogElementHandler(log))
-            ui.button('Log time', on_click=lambda: logger.warning(datetime.now().strftime('%X.%f')[:-5]))

+ 0 - 54
website/documentation/content/more/markdown_documentation.py

@@ -1,54 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class MarkdownDocumentation(UiElementDocumentation, element=ui.markdown):
-
-    def main_demo(self) -> None:
-        ui.markdown('''This is **Markdown**.''')
-
-    def more(self) -> None:
-        @self.demo('Markdown with indentation', '''
-            Common indentation is automatically stripped from the beginning of each line.
-            So you can indent markdown elements, and they will still be rendered correctly.
-        ''')
-        def markdown_with_indentation():
-            ui.markdown('''
-                ### Example
-
-                This line is not indented.
-
-                    This block is indented.
-                    Thus it is rendered as source code.
-                
-                This is normal text again.
-            ''')
-
-        @self.demo('Markdown with code blocks', '''
-            You can use code blocks to show code examples.
-            If you specify the language after the opening triple backticks, the code will be syntax highlighted.
-            See [the Pygments website](https://pygments.org/languages/) for a list of supported languages.
-        ''')
-        def markdown_with_code_blocks():
-            ui.markdown('''
-                ```python
-                from nicegui import ui
-
-                ui.label('Hello World!')
-
-                ui.run(dark=True)
-                ```
-            ''')
-
-        @self.demo('Markdown tables', '''
-            By activating the "tables" extra, you can use Markdown tables.
-            See the [markdown2 documentation](https://github.com/trentm/python-markdown2/wiki/Extras#implemented-extras) for a list of available extras.
-        ''')
-        def markdown_tables():
-            ui.markdown('''
-                | First name | Last name |
-                | ---------- | --------- |
-                | Max        | Planck    |
-                | Marie      | Curie     |
-            ''', extras=['tables'])

+ 0 - 18
website/documentation/content/more/menu_documentation.py

@@ -1,18 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class MenuDocumentation(UiElementDocumentation, element=ui.menu):
-
-    def main_demo(self) -> None:
-        with ui.row().classes('w-full items-center'):
-            result = ui.label().classes('mr-auto')
-            with ui.button(icon='menu'):
-                with ui.menu() as menu:
-                    ui.menu_item('Menu item 1', lambda: result.set_text('Selected item 1'))
-                    ui.menu_item('Menu item 2', lambda: result.set_text('Selected item 2'))
-                    ui.menu_item('Menu item 3 (keep open)',
-                                 lambda: result.set_text('Selected item 3'), auto_close=False)
-                    ui.separator()
-                    ui.menu_item('Close', on_click=menu.close)

+ 0 - 13
website/documentation/content/more/mermaid_documentation.py

@@ -1,13 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class MermaidDocumentation(UiElementDocumentation, element=ui.mermaid):
-
-    def main_demo(self) -> None:
-        ui.mermaid('''
-        graph LR;
-            A --> B;
-            A --> C;
-        ''')

+ 0 - 33
website/documentation/content/more/notify_documentation.py

@@ -1,33 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class NotifyDocumentation(UiElementDocumentation, element=ui.notify):
-
-    def main_demo(self) -> None:
-        ui.button('Say hi!', on_click=lambda: ui.notify('Hi!', close_button='OK'))
-
-    def more(self) -> None:
-        @self.demo('Notification Types', '''
-            There are different types that can be used to indicate the nature of the notification.
-        ''')
-        def notify_colors():
-            ui.button('negative', on_click=lambda: ui.notify('error', type='negative'))
-            ui.button('positive', on_click=lambda: ui.notify('success', type='positive'))
-            ui.button('warning', on_click=lambda: ui.notify('warning', type='warning'))
-
-        @self.demo('Multiline Notifications', '''
-            To allow a notification text to span multiple lines, it is sufficient to set `multi_line=True`.
-            If manual newline breaks are required (e.g. `\\n`), you need to define a CSS style and pass it to the notification as shown in the example.
-        ''')
-        def multiline():
-            ui.html('<style>.multi-line-notification { white-space: pre-line; }</style>')
-            ui.button('show', on_click=lambda: ui.notify(
-                'Lorem ipsum dolor sit amet, consectetur adipisicing elit. \n'
-                'Hic quisquam non ad sit assumenda consequuntur esse inventore officia. \n'
-                'Corrupti reiciendis impedit vel, '
-                'fugit odit quisquam quae porro exercitationem eveniet quasi.',
-                multi_line=True,
-                classes='multi-line-notification',
-            ))

+ 0 - 30
website/documentation/content/more/number_documentation.py

@@ -1,30 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class NumberDocumentation(UiElementDocumentation, element=ui.number):
-
-    def main_demo(self) -> None:
-        ui.number(label='Number', value=3.1415927, format='%.2f',
-                  on_change=lambda e: result.set_text(f'you entered: {e.value}'))
-        result = ui.label()
-
-    def more(self) -> None:
-        @self.demo('Clearable', '''
-            The `clearable` prop from [Quasar](https://quasar.dev/) adds a button to the input that clears the text.    
-        ''')
-        def clearable():
-            i = ui.number(value=42).props('clearable')
-            ui.label().bind_text_from(i, 'value')
-
-        @self.demo('Number of decimal places', '''
-            You can specify the number of decimal places using the `precision` parameter.
-            A negative value means decimal places before the dot.
-            The rounding takes place when the input loses focus,
-            when sanitization parameters like min, max or precision change,
-            or when `sanitize()` is called manually.
-        ''')
-        def integer():
-            n = ui.number(value=3.14159265359, precision=5)
-            n.sanitize()

+ 0 - 14
website/documentation/content/more/open_documentation.py

@@ -1,14 +0,0 @@
-from nicegui import ui
-
-from ...model import UiElementDocumentation
-
-
-class OpenDocumentation(UiElementDocumentation, element=ui.open):
-
-    def main_demo(self) -> None:
-        @ui.page('/yet_another_page')
-        def yet_another_page():
-            ui.label('Welcome to yet another page')
-            ui.button('RETURN', on_click=lambda: ui.open('documentation#open'))
-
-        ui.button('REDIRECT', on_click=lambda: ui.open(yet_another_page))

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů