from abc import ABC from typing import Self, Type, Any, Optional from nicegui import ui import asyncio class AsyncElement(ABC): """Base class for UI elements with async initialization""" def __init__(self, element_type: Type = ui.column, *element_args, **element_kwargs) -> None: self.element = element_type(*element_args, **element_kwargs) async def build(self, *args, **kwargs) -> None: """Build/setup the element - must be implemented by subclasses""" pass @classmethod async def create(cls, element_type: Type = ui.column, *args, **kwargs) -> Self: """Factory method to create and build an element instance""" # Separate element constructor args from build args element_args = kwargs.pop('element_args', ()) element_kwargs = kwargs.pop('element_kwargs', {}) # Create and build the instance instance = cls(element_type, *element_args, **element_kwargs) await instance.build(*args, **kwargs) return instance.element # Return the NiceGUI element directly def __getattr__(self, name): """Delegate all attribute access to the wrapped element""" return getattr(self.element, name) if __name__ in {"__main__", "__mp_main__"}: # Example implementations class UserCard(AsyncElement): async def build(self, user_id: int, *args, **kwargs) -> None: # Simulate loaded user data user_data = { 'name': f'User {user_id}', 'email': f'user{user_id}@example.com', 'status': 'online' if user_id % 2 == 0 else 'offline' } # Build content directly in element with self.element: with ui.card().classes('w-full max-w-md'): ui.label(user_data['name']).classes('text-h6') ui.label(user_data['email']).classes('text-caption') with ui.row().classes('w-full justify-between items-center'): status_color = 'green' if user_data['status'] == 'online' else 'grey' ui.badge(user_data['status']).props(f'color={status_color}') ui.button('Edit', icon='edit').props('flat dense') class DataTable(AsyncElement): async def build(self, data_source: str, *args, **kwargs) -> None: # Simulate loaded data columns = [ {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'}, {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True}, {'name': 'city', 'label': 'City', 'field': 'city'}, ] rows = [ {'id': 1, 'name': 'Alice', 'age': 25, 'city': 'New York'}, {'id': 2, 'name': 'Bob', 'age': 30, 'city': 'San Francisco'}, {'id': 3, 'name': 'Charlie', 'age': 35, 'city': 'London'}, ] with self.element: ui.label(f'Data from {data_source}').classes('text-caption mb-2') ui.table(columns=columns, rows=rows, row_key='id').classes('w-full') class LoadingSection(AsyncElement): async def build(self, title: str, delay: float = 1.0, *args, **kwargs) -> None: with self.element: ui.label(title).classes('text-h6 mb-2') ui.label('Content loaded successfully!').classes('text-positive') with ui.row().classes('gap-2'): ui.button('Action 1', icon='star') ui.button('Action 2', icon='favorite') @ui.page('/') async def main_page(): ui.label('Simple Async Elements Demo').classes('text-h4 mb-4') # Example 1: User card as a column with method chaining (await UserCard.create(element_type=ui.column, user_id=123)).classes('w-full mb-8') # Example 2: Data table as a row (await DataTable.create(element_type=ui.row, data_source="users_api")).classes('w-full mb-8') # Example 3: Loading section as a card (await LoadingSection.create(element_type=ui.card, title="Dashboard Section", delay=0.8)).classes('w-full p-4') # Example 4: Multiple elements in a row with ui.row().classes('w-full gap-4'): for i in range(1, 4): (await UserCard.create(element_type=ui.column, user_id=i)).classes('flex-1') # Example 5: Nested usage with ui.column().classes('w-full mt-8'): ui.label('Nested Example').classes('text-h6') (await LoadingSection.create(element_type=ui.row, title=f"Nested Section", delay=0.3)).classes( 'w-full items-center gap-4') ui.run( title='Simple AsyncElements', favicon='🔒', show=False, dark=False, port=8080 )