From 02d7513b77eaa5d7b6c5590f2e4fa2ce060b3aaf Mon Sep 17 00:00:00 2001 From: Alexander Thiess Date: Tue, 23 Sep 2025 05:26:18 +0200 Subject: [PATCH] dialog with property --- example_async_dialog.py | 34 ++++++ src/niceguiex/async_elements/__init__.py | 3 +- src/niceguiex/async_elements/base.py | 2 +- src/niceguiex/async_elements/elements.py | 128 ++++++++++++++--------- 4 files changed, 116 insertions(+), 51 deletions(-) diff --git a/example_async_dialog.py b/example_async_dialog.py index ca18058..aca022f 100644 --- a/example_async_dialog.py +++ b/example_async_dialog.py @@ -10,6 +10,9 @@ class ConfirmDialog(AsyncColumn): async def build(self, message: str, title: str = "Confirm"): """Build the dialog content""" + # Set dialog size using the property + self.dialog_size = 'w-96' + ui.label(title).classes('text-h6') ui.label(message) ui.space() @@ -24,6 +27,10 @@ class FormDialog(AsyncColumn): async def build(self, title: str = "Enter Information"): """Build the form dialog""" + # Set a wider dialog size for the form + if hasattr(self, '_dialog'): + self.dialog_size = 'w-1/2' + ui.label(title).classes('text-h6') # Create input fields @@ -93,6 +100,9 @@ async def main(): async def show_inline_dialog(): class QuickDialog(AsyncColumn): async def build(self): + # Smaller dialog size for simple questions + self.dialog_size = 'w-72' + ui.label('Quick Question').classes('text-h6') ui.label('Do you like this feature?') @@ -106,6 +116,30 @@ async def main(): ui.button('Show Quick Dialog', on_click=show_inline_dialog) + # Example with dynamic sizing based on content + async def show_dynamic_dialog(): + class DynamicDialog(AsyncColumn): + async def build(self, items_count: int = 3): + # Adjust size based on content + if items_count <= 3: + self.dialog_size = 'w-96' + else: + self.dialog_size = 'w-1/2' + + ui.label(f'Dynamic Dialog with {items_count} items').classes('text-h6') + + for i in range(items_count): + ui.label(f'Item {i + 1}: This is some content for item {i + 1}') + + ui.space() + ui.button('Close', on_click=lambda: self._dialog.submit(f'Closed with {items_count} items')) + + for count in [2, 5]: + result = await DynamicDialog.as_dialog(items_count=count) + ui.notify(f'Result: {result}') + + ui.button('Show Dynamic Dialogs', on_click=show_dynamic_dialog) + if __name__ in {"__main__", "__mp_main__"}: ui.run(title='Async Dialog Example', port=8085) diff --git a/src/niceguiex/async_elements/__init__.py b/src/niceguiex/async_elements/__init__.py index 4d2f857..96e9471 100644 --- a/src/niceguiex/async_elements/__init__.py +++ b/src/niceguiex/async_elements/__init__.py @@ -1,10 +1,9 @@ from .base import AsyncElement -from .elements import (AsyncColumn, AsyncRow, AsyncCard, AsyncScrollArea) +from .elements import (AsyncColumn, AsyncRow, AsyncCard) __all__ = [ 'AsyncElement', 'AsyncColumn', 'AsyncRow', 'AsyncCard', - 'AsyncScrollArea' ] diff --git a/src/niceguiex/async_elements/base.py b/src/niceguiex/async_elements/base.py index ad2db6b..db3ce61 100644 --- a/src/niceguiex/async_elements/base.py +++ b/src/niceguiex/async_elements/base.py @@ -85,4 +85,4 @@ class AsyncElement(ABC, Generic[T]): def __exit__(self, *args): """Support context manager protocol""" - return self._element.__exit__(*args) \ No newline at end of file + return self._element.__exit__(*args) diff --git a/src/niceguiex/async_elements/elements.py b/src/niceguiex/async_elements/elements.py index 015912b..beadf96 100644 --- a/src/niceguiex/async_elements/elements.py +++ b/src/niceguiex/async_elements/elements.py @@ -1,19 +1,35 @@ from abc import ABC, abstractmethod -from typing import Self, Any +from typing import Self, Any, Optional from nicegui import ui class AsyncColumn(ui.column, ABC): """Async column that inherits from ui.column for perfect typing""" + _dialog: Optional[ui.dialog] = None + _dialog_card: Optional[ui.card] = None + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self._dialog_size: str | None = None @abstractmethod async def build(self, *args, **kwargs) -> None: """Build/setup the element - must be implemented by subclasses""" ... + @property + def dialog_size(self) -> str | None: + """Get or set the dialog size (e.g., 'w-1/2', 'w-96', 'max-w-4xl')""" + return self._dialog_size + + @dialog_size.setter + def dialog_size(self, value: str | None) -> None: + """Set the dialog size - applies to the dialog if it exists""" + self._dialog_size = value + if self._dialog_card: + self._dialog_card.classes(value) + @classmethod async def create(cls, *args, **kwargs) -> Self: """Factory method to create and build a column instance""" @@ -38,13 +54,19 @@ class AsyncColumn(ui.column, ABC): dialog.open() # Build the element inside the dialog with a card - with dialog, ui.card(): - instance = cls() + with dialog: + with ui.card().style('max-width: none;') as dialog_card: + instance = cls() - # Store dialog reference for potential use in build() - instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] + # Store dialog reference for potential use in build() + instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] + instance._dialog_card = dialog_card - await instance.build(*args, **kwargs) + await instance.build(*args, **kwargs) + + # Apply dialog size if set + if hasattr(instance, '_dialog_size') and instance._dialog_size: + dialog.classes(add=instance._dialog_size) # Await the dialog result result = await dialog @@ -58,14 +80,30 @@ class AsyncColumn(ui.column, ABC): class AsyncRow(ui.row, ABC): """Async row that inherits from ui.row for perfect typing""" + _dialog: Optional[ui.dialog] = None + _dialog_card: Optional[ui.card] = None + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self._dialog_size: str | None = None @abstractmethod async def build(self, *args, **kwargs) -> None: """Build/setup the element - must be implemented by subclasses""" ... + @property + def dialog_size(self) -> str | None: + """Get or set the dialog size (e.g., 'w-1/2', 'w-96', 'max-w-4xl')""" + return self._dialog_size + + @dialog_size.setter + def dialog_size(self, value: str | None) -> None: + """Set the dialog size - applies to the dialog if it exists""" + self._dialog_size = value + if self._dialog_card: + self._dialog_card.classes(value) + @classmethod async def create(cls, *args, **kwargs) -> Self: """Factory method to create and build a column instance""" @@ -79,10 +117,16 @@ class AsyncRow(ui.row, ABC): dialog = ui.dialog() dialog.open() - with dialog, ui.card(): - instance = cls() - instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] - await instance.build(*args, **kwargs) + with dialog: + with ui.card().style('max-width: none;') as dialog_card: + instance = cls() + instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] + instance._dialog_card = dialog_card + await instance.build(*args, **kwargs) + + # Apply dialog size if set + if hasattr(instance, '_dialog_size') and instance._dialog_size: + dialog.classes(instance._dialog_size) result = await dialog dialog.clear() @@ -92,14 +136,30 @@ class AsyncRow(ui.row, ABC): class AsyncCard(ui.card, ABC): """Async card that inherits from ui.card for perfect typing""" + _dialog: Optional[ui.dialog] = None + _dialog_card: Optional[ui.card] = None + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self._dialog_size: str | None = None @abstractmethod async def build(self, *args, **kwargs) -> None: """Build/setup the element - must be implemented by subclasses""" ... + @property + def dialog_size(self) -> str | None: + """Get or set the dialog size (e.g., 'w-1/2', 'w-96', 'max-w-4xl')""" + return self._dialog_size + + @dialog_size.setter + def dialog_size(self, value: str | None) -> None: + """Set the dialog size - applies to the dialog if it exists""" + self._dialog_size = value + if self._dialog_card: + self._dialog_card.classes(value) + @classmethod async def create(cls, *args, **kwargs) -> Self: """Factory method to create and build a card instance""" @@ -113,44 +173,16 @@ class AsyncCard(ui.card, ABC): dialog = ui.dialog() dialog.open() - with dialog, ui.card(): - instance = cls() - instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] - await instance.build(*args, **kwargs) - - result = await dialog - dialog.clear() - return result - - -class AsyncScrollArea(ui.scroll_area, ABC): - """Async ScrollArea that inherits from ui.scrol_area for perfect typing""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - @abstractmethod - async def build(self, *args, **kwargs) -> None: - """Build/setup the element - must be implemented by subclasses""" - ... - - @classmethod - async def create(cls, *args, **kwargs) -> Self: - """Factory method to create and build a column instance""" - instance = cls() - await instance.build(*args, **kwargs) - return instance - - @classmethod - async def as_dialog(cls, *args, **kwargs) -> Any: - """Create element inside a dialog and await its result""" - dialog = ui.dialog() - dialog.open() - - with dialog, ui.card(): - instance = cls() - instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] - await instance.build(*args, **kwargs) + with dialog: + with ui.card().style('max-width: none;') as dialog_card: + instance = cls() + instance._dialog = dialog # pyright: ignore[reportAttributeAccessIssue] + instance._dialog_card = dialog_card + await instance.build(*args, **kwargs) + + # Apply dialog size if set + if hasattr(instance, '_dialog_size') and instance._dialog_size: + dialog.classes(instance._dialog_size) result = await dialog dialog.clear()