Source code for quirl.components.spinner

"""
A loading spinner with theme-aware color.
"""

from duck.html.components.container import Container

from quirl.theme import Theme


# Pixel diameter for each spinner size
SIZE_MAP = {
    "sm": "16px",
    "md": "24px",
    "lg": "32px",
}


[docs] class Spinner(Container): """ A loading spinner with theme-aware color. Renders as a rotating ring by default, or a trio of bouncing dots in the iOS activity-indicator style. Note: requires `quirl.styles.quirl_global_styles()` added to the page head for the animation to run. Usage: ```python Spinner(size="md", color="var(--theme-accent-color)") Spinner(variant="dots") ``` Optional Props: - variant: One of ring, dots. Defaults to ring. - size: One of sm, md, lg. Defaults to md. - color: Spinner color, defaults to var(--theme-accent-color) """ docs_preview_kwargs = { "size": "md", }
[docs] def on_create(self): """ Build the spinner as a rotating ring or bouncing dots. """ super().on_create() variant = self.kwargs.get("variant", "ring") if variant == "dots": self.build_dots() else: self.build_ring()
[docs] def build_ring(self) -> None: """ Style this component as a rotating ring indicator. """ size = self.kwargs.get("size", "md") dim = SIZE_MAP.get(size, SIZE_MAP["md"]) color = self.kwargs.get("color", Theme.current.accent_color) # Apply spinner container styles self.style.update({ "width": dim, "height": dim, "border": f"3px solid {Theme.current.border_color}", "border-top-color": color, "border-radius": "50%", "animation": "quirl-spin 0.8s linear infinite", }) self.klass = "quirl-spinner"
[docs] def build_dots(self) -> None: """ Style this component as three bouncing dots. """ size = self.kwargs.get("size", "md") dot_size = {"sm": "5px", "md": "7px", "lg": "9px"}.get(size, "7px") color = self.kwargs.get("color", Theme.current.accent_color) # Set the class self.klass = "quirl-spinner-dots" # Update style self.style.update({ "display": "inline-flex", "align-items": "center", "gap": "4px", }) # Stagger each dot's bounce so they animate in sequence for delay in ("0s", "0.15s", "0.3s"): self.add_child(Container( style={ "width": dot_size, "height": dot_size, "border-radius": "50%", "background": color, "animation": f"quirl-bounce-dot 1s ease-in-out {delay} infinite", }, ))