# `Drafter.Widget.Digits`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/widget/digits.ex#L1)

Renders text as large characters, drawn with box outlines or with pixels
packed into braille, quadrant, or half-block cells.

Covers the digits `0`–`9`, the full alphabet, and common punctuation. Lower
case falls back to the upper-case form, and a character no font can draw
renders as blanks of the same width.

The glyphs live in `Drafter.Widget.Digits.Font`, which also accepts FIGlet
fonts loaded at runtime. See the [large text guide](large_text.md) for the
catalogue and how to choose between fonts.

## Component tag

Tag `:digits`, built by `Drafter.App` as `{:digits, value, opts}`:

    digits(value, opts)

The positional argument is passed through `to_string/1` to become `:text`, so
it may be any term implementing `String.Chars`. All other props come from
`opts`.

## Options

  * `:text` - `t:String.t/0` of characters to render. Default `""`. Supplied
    positionally through the `digits/2` element, passed through `to_string/1`.
    Empty text renders nothing at all
  * `:style` - `t:map/0` of style properties applied to all characters. Default
    `%{}`
  * `:align` - horizontal alignment within the available width: `:left` (default),
    `:center`, `:right`
  * `:font` - a name from `Drafter.Widget.Digits.Font.names/0`, or a font map.
    Default `nil`. Overrides `:size` when set
  * `:size` - coarse size when no `:font` is given: `:large` (default, the
    `:block` font) or `:small` (the `:compact` font)
  * `:renderer` - `:text` (default) draws cells; any other value transmits an
    image on a terminal supporting kitty, iTerm2, or sixel, falling back to cells
    where none is available
  * `:bg_data` - list of numbers. Default `nil`. When set, an area-chart fill is
    drawn behind the digits using per-cell background colours
  * `:color` - `{r, g, b}` fill colour for the area chart. Default
    `{0, 150, 255}`. Digit glyphs are drawn in an auto-contrasting foreground
  * `:bg_min` - value mapped to the bottom of the `:bg_data` area fill. Default `0`
  * `:bg_max` - value mapped to the top of the `:bg_data` area fill. Default `nil`,
    which uses the largest sampled value, or `1` when `:bg_data` is empty

`update/2` merges the props map into the state, so every option is live, and a
re-render passes all of them through.

## Widget value

`Drafter.get_widget_value/1` returns the rendered `:text` as a `t:String.t/0`,
because the value extractor reads the `:text` field.

## Data channel

When the widget is declared with a data buffer, `apply_data_buffer/3` sets `:text`
to `to_string/1` of the last item in the buffer.

## Usage

    digits("12:34", size: :large, style: %{fg: {0, 200, 100}})
    digits("99%", size: :small, align: :center)
    digits("CPU 42", font: :braille)
    digits("Vellum", font: :slant, renderer: :graphics)
    digits("42%", bg_data: history, color: {0, 180, 120}, size: :large, align: :center)

# `t`

```elixir
@type t() :: %{
  text: String.t(),
  style: map(),
  align: :left | :center | :right,
  size: :large | :small,
  font: atom() | map() | nil,
  renderer: atom(),
  bg_data: [number()] | nil,
  color: {0..255, 0..255, 0..255},
  bg_min: number(),
  bg_max: number() | nil
}
```

# `apply_data_buffer`

```elixir
@spec apply_data_buffer(t(), Drafter.RingBuffer.t(), Drafter.Widget.rect()) :: t()
```

Sets `:text` to `to_string/1` of the newest item in the widget's data buffer.

Everything buffered before the last item is discarded. An empty buffer leaves the
state alone.

# `component_tag`

```elixir
@spec component_tag() :: :digits
```

The registry tag for this widget.

    iex> Drafter.Widget.Digits.component_tag()
    :digits

# `from_component_opts`

```elixir
@spec from_component_opts(
  term(),
  keyword()
) :: Drafter.Widget.props()
```

Turns the `{:digits, value, opts}` element into a props map for `mount/1`.

`value` becomes `:text` through `to_string/1`, so it may be any term implementing
`String.Chars`.

    iex> props = Drafter.Widget.Digits.from_component_opts(42, align: :center)
    iex> {props.text, props.align, props.size, props.color, props.bg_min}
    {"42", :center, :large, {0, 150, 255}, 0}

# `handle_event`

```elixir
@spec handle_event(Drafter.Event.t(), t()) :: {:noreply, t()}
```

Ignores every event and returns `{:noreply, state}`. The widget is not focusable
and never consumes input.

# `image`

```elixir
@spec image(t(), Drafter.Widget.rect(), term()) :: {iodata(), iodata(), map()} | nil
```

Builds the terminal-graphics payload for the text, or `nil`.

Returns `nil` when `:renderer` is `:text`, when `rect` has no area, or when no
supported graphics protocol is available. Otherwise returns
`{paint, clear, placement}`, where `placement` is
`%{dx: 0, dy: 0, cols: rect.width, rows: rect.height}`.

# `image_active?`

```elixir
@spec image_active?(t()) :: boolean()
```

Whether these digits are drawing a transmitted image rather than cells.

True when `:renderer` is anything but `:text` and the terminal has a graphics
protocol to draw it with.

# `mount`

```elixir
@spec mount(Drafter.Widget.props()) :: t()
```

Builds the digits state from `props`. The state is a plain map, not a struct.

    iex> d = Drafter.Widget.Digits.mount(%{text: "42", size: :small})
    iex> {d.text, d.size, d.font, d.align}
    {"42", :small, nil, :left}

    iex> Drafter.Widget.Digits.mount(%{})
    %{text: "", style: %{}, align: :left, size: :large, font: nil, renderer: :text, bg_data: nil, color: {0, 150, 255}, bg_min: 0, bg_max: nil}

# `preferred_height`

```elixir
@spec preferred_height(
  term(),
  keyword()
) :: pos_integer()
```

The row height of the font `opts` selects.

`opts[:font]` wins; otherwise `opts[:size]` picks `:block` for `:large` (the
default) and `:compact` for `:small`.

    iex> Drafter.Widget.Digits.preferred_height("42", size: :small)
    3

    iex> Drafter.Widget.Digits.preferred_height("42", [])
    5

# `render`

```elixir
@spec render(t(), Drafter.Widget.rect()) :: [Drafter.Draw.Strip.t()]
```

Draws the large characters into `rect`.

Returns `[]` for empty `:text`. Otherwise it emits one strip per row of the
selected font's height, positioned horizontally according to `:align`. With
`:bg_data` set, each cell also carries the area-chart background colour for its
column.

    iex> Drafter.Widget.Digits.render(Drafter.Widget.Digits.mount(%{}), %{x: 0, y: 0, width: 20, height: 7})
    []

# `update`

```elixir
@spec update(Drafter.Widget.props(), t()) :: t()
```

Merges `props` into `state`, so every option is live-updatable.

    iex> d = Drafter.Widget.Digits.mount(%{text: "1"})
    iex> Drafter.Widget.Digits.update(%{text: "2", align: :center}, d).text
    "2"

# `update_props_from_mount`

```elixir
@spec update_props_from_mount(Drafter.Widget.props(), t(), keyword()) ::
  Drafter.Widget.props()
```

Passes every option through to `update/2` on a re-render, so nothing about a
digits widget is mount-only.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
