# `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)
  * `:fit` - list of font names, largest first. Default `nil`. When set it
    overrides `:font` and `:size`: each render uses the first font whose height
    and text width fit the widget's rect. When none fits, the text is written in
    plain characters on one row, cut to the width with `…`. FIGlet
    fonts must be registered first (see `Drafter.Widget.Digits.Figlet`)
  * `: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,
  fit: [atom()] | 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_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.

# `image_raster`

```elixir
@spec image_raster(t(), Drafter.Widget.rect()) :: Drafter.Render.Picture.t() | nil
```

The text as a `Drafter.Render.Picture` filling `rect`, or `nil`.

Returns `nil` when `:renderer` is `:text`, when `rect` has no area, or when no
supported graphics protocol is available.

# `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, fit: 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*
