File size: 3,954 Bytes
2d876d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
"""Module contains spinner related resources.

Note:
    The spinner is not a standalone spinner to run in the terminal
    but rather a `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner.

    Use library such as `yaspin <https://github.com/pavdmyt/yaspin>`_ if you need a plain spinner.
"""
import asyncio
from typing import TYPE_CHECKING, Callable, List, NamedTuple, Optional, Tuple, Union

from prompt_toolkit.filters.utils import to_filter
from prompt_toolkit.layout.containers import ConditionalContainer, Window
from prompt_toolkit.layout.controls import FormattedTextControl

if TYPE_CHECKING:
    from prompt_toolkit.filters.base import Filter

__all__ = ["SPINNERS", "SpinnerWindow"]


class SPINNERS(NamedTuple):
    """Presets of spinner patterns.

    See Also:
        https://github.com/pavdmyt/yaspin/blob/master/yaspin/data/spinners.json

    This only contains some basic ones thats ready to use. For more patterns, checkout the
    URL above.

    Examples:
        >>> from InquirerPy import inquirer
        >>> from InquirerPy.spinner import SPINNERS
        >>> inquirer.select(message="", choices=lambda _: [1, 2, 3], spinner_pattern=SPINNERS.dots)
    """

    dots = ["β ‹", "β ™", "β Ή", "β Έ", "β Ό", "β ΄", "β ¦", "β §", "β ‡", "⠏"]
    dots2 = ["⣾", "⣽", "⣻", "Ⓙ", "⑿", "⣟", "⣯", "⣷"]
    line = ["-", "\\", "|", "/"]
    line2 = ["β ‚", "-", "–", "β€”", "–", "-"]
    pipe = ["─", "β”˜", "β”΄", "β””", "β”œ", "β”Œ", "┬", "┐"]
    star = ["✢", "✸", "✹", "✺", "✹", "✷"]
    star2 = ["+", "x", "*"]
    flip = ["_", "_", "_", "-", "`", "`", "'", "Β΄", "-", "_", "_", "_"]
    hamburger = ["☱", "☲", "☴"]
    grow_vertical = ["▁", "β–ƒ", "β–„", "β–…", "β–†", "β–‡", "β–†", "β–…", "β–„", "β–ƒ"]
    grow_horizontal = ["▏", "β–Ž", "▍", "β–Œ", "β–‹", "β–Š", "β–‰", "β–Š", "β–‹", "β–Œ", "▍", "β–Ž"]
    box_bounce = ["β––", "β–˜", "▝", "β–—"]
    triangle = ["β—’", "β—£", "β—€", "β—₯"]
    arc = ["β—œ", "β— ", "◝", "β—ž", "β—‘", "β—Ÿ"]
    circle = ["β—‘", "βŠ™", "β— "]


class SpinnerWindow(ConditionalContainer):
    """Conditional `prompt_toolkit` :class:`~prompt_toolkit.layout.Window` that displays a spinner.

    Args:
        loading: A :class:`~prompt_toolkit.filters.Condition` to indicate if the spinner should be visible.
        redraw: A redraw function (i.e. :meth:`~prompt_toolkit.application.Application.invalidate`) to refresh the UI.
        pattern: List of pattern to display as the spinner.
        delay: Spinner refresh frequency.
        text: Loading text to display.
    """

    def __init__(
        self,
        loading: "Filter",
        redraw: Callable[[], None],
        pattern: Optional[Union[List[str], SPINNERS]] = None,
        delay: float = 0.1,
        text: str = "",
    ) -> None:
        self._loading = to_filter(loading)
        self._spinning = False
        self._redraw = redraw
        self._pattern = pattern or SPINNERS.line
        self._char = self._pattern[0]
        self._delay = delay
        self._text = text or "Loading ..."

        super().__init__(
            content=Window(content=FormattedTextControl(text=self._get_text)),
            filter=self._loading,
        )

    def _get_text(self) -> List[Tuple[str, str]]:
        """Dynamically get the text for the :class:`~prompt_toolkit.layout.Window`.

        Returns:
            Formatted text.
        """
        return [
            ("class:spinner_pattern", self._char),
            ("", " "),
            ("class:spinner_text", self._text),
        ]

    async def start(self) -> None:
        """Start the spinner."""
        if self._spinning:
            return
        self._spinning = True
        while self._loading():
            for char in self._pattern:
                await asyncio.sleep(self._delay)
                self._char = char
                self._redraw()
        self._spinning = False