diff --git a/src/ui/components/loading.py b/src/ui/components/loading.py index 574e11f..58e4cb7 100644 --- a/src/ui/components/loading.py +++ b/src/ui/components/loading.py @@ -1,19 +1,46 @@ +"""Loading spinner component for async operations.""" + +from textual.widget import Widget from textual.widgets import Static -class LoadingSpinner(Static): - def __init__(self, **kwargs): - super().__init__("Loading...", **kwargs) - self._spinner_chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏⠫⠽⠘⠰ ⠁⠂⠄⡀⢀⠠⠐⠈" - self._index = 0 +class LoadingSpinner(Widget): + """A loading spinner widget for indicating async operations.""" - async def on_mount(self) -> None: - self.update_timer = self.set_interval(0.1, self._spin) + DEFAULT_CSS = """ + LoadingSpinner { + height: 3; + width: 20; + align: center middle; + } + + LoadingSpinner > Static { + content: "Loading..."; + color: $accent; + } + """ + + def __init__(self, message: str = "Loading...", *args, **kwargs): + super().__init__(*args, **kwargs) + self.message = message + self._spinner_chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] + self._frame = 0 + + def watch_is_running(self, is_running: bool) -> None: + """Handle running state changes.""" + if not is_running: + self.remove_class("loading") + + def compose(self): + yield Static(self.message, id="loading-text") + + def on_mount(self) -> None: + self.set_interval(0.1, self._spin) def _spin(self) -> None: - self._index = (self._index + 1) % len(self._spinner_chars) - self.update(self._spinner_chars[self._index]) - - def on_unmount(self) -> None: - if hasattr(self, 'update_timer'): - self.update_timer.stop() + """Animate the spinner.""" + if self.display: + self._frame = (self._frame + 1) % len(self._spinner_chars) + spinner = self._spinner_chars[self._frame] + loading_text = self.query_one("#loading-text", Static) + loading_text.update(f"{spinner} {self.message}")