Tela.Component.Spinner (tela v0.1.0)

Copy Markdown View Source

An animated spinner component.

Tela.Component.Spinner is a display-only widget that cycles through a list of animation frames at a fixed interval. It is driven by a tick cmd that the parent starts via tick_cmd/1 and routes back via handle_tick/2.

Usage

defmodule MyApp do
  use Tela
  alias Tela.Component.Spinner

  @impl Tela
  def init(_args) do
    spinner = Spinner.init(spinner: :dot)
    {%{spinner: spinner}, Spinner.tick_cmd(spinner)}
  end

  @impl Tela
  def handle_event(model, %Tela.Key{key: {:char, "q"}}), do: {model, :quit}
  def handle_event(model, _key), do: {model, nil}

  @impl Tela
  def handle_info(model, msg) do
    {spinner, cmd} = Spinner.handle_tick(model.spinner, msg)
    {%{model | spinner: spinner}, cmd}
  end

  @impl Tela
  def view(model) do
    Spinner.view(model.spinner) <> " Loading..."
  end
end

Presets

The following preset atoms are available:

  • :line|, /, -, \
  • :dot — Braille dot spinner
  • :mini_dot — smaller Braille dot spinner
  • :jump — Braille jump spinner
  • :pulse — block pulse █▓▒░
  • :points — moving dot ∙∙∙ ●∙∙ ∙●∙ ∙∙●
  • :globe — rotating globe emoji
  • :moon — moon phase emoji
  • :monkey — monkey emoji sequence
  • :meter — filling bar ▱▱▱ → ▰▰▰
  • :hamburger — stacked lines ☱ ☲ ☴
  • :ellipsis — growing dots . .. ...

Custom spinners can be passed as a {frames, interval_ms} tuple.

Tick ownership

Components do not emit startup cmds from init/1. The parent must call tick_cmd/1 to obtain the initial cmd and include it in its own init/1 return value. The tick self-re-arms: each successful handle_tick/2 call returns a new cmd that advances the animation by one more frame.

Each spinner is assigned a unique id on init/1. Ticks carry that id, and handle_tick/2 rejects any tick whose id does not match the current spinner's id — silently dropping stale ticks from replaced spinners without needing explicit cancellation.

Summary

Types

t()

The spinner model. Build with init/1; treat as opaque.

Functions

Ignores all key events. The spinner is display-only.

Processes a tick message for this spinner.

Initialises a new spinner model.

Returns a {:task, fun} cmd that, when dispatched, sleeps for interval_ms and then returns {:spinner_tick, id}.

Returns the current animation frame as a Tela.Frame.

Types

t()

@type t() :: %Tela.Component.Spinner{
  frame: non_neg_integer(),
  frames: [String.t()],
  id: non_neg_integer(),
  interval_ms: pos_integer(),
  style: Tela.Style.t()
}

The spinner model. Build with init/1; treat as opaque.

Functions

handle_event(spinner, key)

@spec handle_event(t(), Tela.Key.t()) :: {t(), Tela.cmd()}

Ignores all key events. The spinner is display-only.

handle_tick(spinner, arg2)

@spec handle_tick(t(), term()) :: {t(), Tela.cmd()}

Processes a tick message for this spinner.

Matches {:spinner_tick, id} where id equals the spinner's current id. On a match, advances the frame by one (wrapping around), assigns a new unique id, and returns {new_spinner, tick_cmd(new_spinner)}.

Any non-matching message — including ticks with a stale id from a replaced spinner — returns {spinner, nil} unchanged.

init(opts)

@spec init(keyword()) :: t()

Initialises a new spinner model.

Options

  • spinner: — a preset atom (default :line) or a {frames, interval_ms} tuple for a custom spinner. See the module doc for available presets.
  • style: — a Tela.Style.t() applied when rendering (default Tela.Style.new()).

Raises ArgumentError if the spinner option is an unrecognised atom.

tick_cmd(spinner)

@spec tick_cmd(t()) :: Tela.cmd()

Returns a {:task, fun} cmd that, when dispatched, sleeps for interval_ms and then returns {:spinner_tick, id}.

Pass the result directly as the cmd in your parent's init/1 or handle_info/2 return to drive the animation.

view(spinner)

@spec view(t()) :: Tela.Frame.t()

Returns the current animation frame as a Tela.Frame.

The frame carries no cursor — Tela.Component.Spinner is a display-only component. Compose the returned frame with surrounding content using Tela.Frame.join/2 in the parent's view/1.