BasecoatUI components for htmy.

Spinner

Example:

Spinner before the message.
Spinner after the message.

Code example:

from htmy import ComponentType, html

from htmui.basecoat.spinner import spinner


def example() -> ComponentType:
    return html.div(
        spinner(),
        spinner("Spinner before the message.", class_="flex gap-2"),
        spinner("Spinner after the message.", class_="flex gap-2", position="after"),
        class_="flex flex-col items-center justify-center gap-2",
    )

Component implementation:

For more details, see the BasecoatUI documentation.

from collections.abc import Iterable
from typing import Literal

from htmy import ComponentType, SafeStr, html, join_classes

__version__ = "0.1.0"
__framework__ = "BasecoatUI"
__framework_version__ = "0.3"
__framework_url__ = "https://basecoatui.com/components/spinner/"

spinner_icon = SafeStr(
    '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" '
    'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="status" '
    'aria-label="Loading" class="animate-spin"><path d="M21 12a9 9 0 1 1-6.219-8.56" /></svg>'
)
"""`loader-circle` icon from https://ludice.dev."""


def spinner(
    *children: ComponentType,
    class_: str | None = "flex shrink-0 items-center justify-center",
    svg_class: Iterable[str] | None = None,
    position: Literal["before", "after"] = "before",
) -> ComponentType:
    """
    Returns a `div` that wraps an animated spinner icon.

    Arguments:
        *children: Child items to display in the wrapper `div` next to the spinner.
        class_: The CSS class for the div that wraps the spinner icon.
        svg_class: CSS classes to apply to the icon through its wrapper `div`. Each of these CSS
            classes will be prefixed with `[&_svg]` automatically when applied to the `div`.
        position: Where to place the spinner relative to `children`.
    """
    svg_class = " ".join(f"[&_svg]:{c}" for c in svg_class) if svg_class else None
    children = (spinner_icon, *children) if position == "before" else (*children, spinner_icon)
    return html.div(*children, class_=join_classes(class_, svg_class))