too much
This commit is contained in:
@@ -1,45 +1,119 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Self, Any, Optional
|
||||
from abc import ABC
|
||||
from typing import Self, Type, Any, Optional
|
||||
from nicegui import ui
|
||||
import asyncio
|
||||
|
||||
|
||||
class AsyncElement(ui.element, ABC):
|
||||
class AsyncElement(ABC):
|
||||
"""Base class for UI elements with async initialization"""
|
||||
dialog: ui.dialog | None
|
||||
|
||||
def __init__(self, tag: str = 'div', dialog: Optional[ui.dialog] = None) -> None:
|
||||
super().__init__(tag)
|
||||
self.dialog = dialog
|
||||
def __init__(self, element_type: Type = ui.column, *element_args, **element_kwargs) -> None:
|
||||
self.element = element_type(*element_args, **element_kwargs)
|
||||
|
||||
@abstractmethod
|
||||
async def build(self, *args, **kwargs) -> None:
|
||||
"""Build/setup the element - must be implemented by subclasses"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
async def create(cls, *args, **kwargs) -> Self:
|
||||
async def create(cls, element_type: Type = ui.column, *args, **kwargs) -> Self:
|
||||
"""Factory method to create and build an element instance"""
|
||||
instance = cls()
|
||||
# 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
|
||||
return instance.element # Return the NiceGUI element directly
|
||||
|
||||
@classmethod
|
||||
async def as_dialog(cls, dialog_classes: str = '', card_classes: str = '', *args, **kwargs) -> Any:
|
||||
"""Create as dialog and return the awaited result"""
|
||||
with ui.dialog().classes(dialog_classes) as dialog:
|
||||
with ui.card().classes(card_classes):
|
||||
instance = cls(dialog=dialog)
|
||||
await instance.build(*args, **kwargs)
|
||||
def __getattr__(self, name):
|
||||
"""Delegate all attribute access to the wrapped element"""
|
||||
return getattr(self.element, name)
|
||||
|
||||
result = await dialog
|
||||
dialog.clear()
|
||||
return result
|
||||
|
||||
def submit(self, result: Any) -> None:
|
||||
if self.dialog:
|
||||
self.dialog.submit(result)
|
||||
if __name__ in {"__main__", "__mp_main__"}:
|
||||
|
||||
def close_dialog(self) -> None:
|
||||
"""Close the dialog with a result"""
|
||||
if self.dialog:
|
||||
self.dialog.close()
|
||||
# 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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user