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

Renders a horizontal or vertical meter with threshold-based color zones.

Uses Unicode eighth-block characters for sub-character precision on horizontal
meters and vertical block characters for vertical meters. Color zones are
defined via configurable thresholds.

## Component tag

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

    meter(opts)

There is no positional argument; every prop comes from `opts`.

## Options

  * `:value` - `t:float/0` fill fraction, clamped into `0.0..1.0` when rendering.
    Default `0.0`.
  * `:label` - `t:String.t/0` title, or `nil`. Default `nil`. Truncated to the
    rect width.
  * `:orientation` - `:horizontal | :vertical`. Default `:horizontal`; any value
    other than `:vertical` renders horizontally.
  * `:thresholds` - `[{float(), {r, g, b}}]` upper bounds sorted ascending before
    use; the first bound the value is `<=` supplies the colour, and the last entry
    covers everything above it. Default
    `[{0.6, {80, 200, 100}}, {0.8, {255, 200, 0}}, {1.0, {255, 60, 60}}]`.
  * `:show_value` - `t:boolean/0`, draw the rounded percentage. Default `true`.
  * `:show_label` - `t:boolean/0`, draw `:label`. Default `true`; a `nil` label
    draws nothing either way.
  * `:style` - `t:map/0`. Default `%{}`. Held on the state and never read by
    `render/2`, which uses fixed track and text colours.
  * `:class` - theme class atom or list of them, normalised by
    `Drafter.Style.normalize_classes/1` and reaching `mount/1` as `:classes`.
    Default `[]`. Held on the state and never read by `render/2`.

`update/2` accepts every key above plus `:app_module`. Through the component tree
only `:value`, `:label`, `:orientation`, `:thresholds`, `:show_value` and
`:show_label` are live-updatable — `update_props_from_mount/3` drops `:style`,
`:classes` and `:app_module`, making them mount-only.

## Usage

    meter(value: 0.72)
    meter(value: 0.45, label: "CPU", orientation: :vertical)
    meter(value: 0.95, thresholds: [{0.5, {0, 200, 0}}, {0.75, {255, 200, 0}}, {1.0, {255, 0, 0}}])

# `rgb`

```elixir
@type rgb() :: {0..255, 0..255, 0..255}
```

# `t`

```elixir
@type t() :: %Drafter.Widget.Meter{
  app_module: module() | nil,
  classes: [atom()],
  label: String.t() | nil,
  orientation: :horizontal | :vertical,
  show_label: boolean(),
  show_value: boolean(),
  style: map(),
  thresholds: [threshold()],
  value: float()
}
```

# `threshold`

```elixir
@type threshold() :: {float(), rgb()}
```

# `apply_data_buffer`

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

Sets `:value` from the newest entry of a `Drafter.RingBuffer`.

Returns `state` unchanged when the buffer is empty. The rect is ignored.

# `component_tag`

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

The component tag this widget registers under.

    iex> Drafter.Widget.Meter.component_tag()
    :meter

# `focused`

# `from_component_opts`

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

Builds the props map for a `{:meter, opts}` element.

The positional argument is ignored. `:class` is normalised into the `:classes`
key and `:__app_module__` into `:app_module`; every other option keeps its name
and the default stated in the module doc.

    iex> props = Drafter.Widget.Meter.from_component_opts(nil, value: 0.5, class: :danger)
    iex> {props.value, props.classes, props.orientation}
    {0.5, [:danger], :horizontal}

# `handle_event`

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

Ignores every event and returns `{:noreply, state}`. The meter is not focusable.

# `mount`

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

Builds the widget state from `props`.

Every option listed in the module doc is read here with the default stated there.

    iex> Drafter.Widget.Meter.mount(%{}).value
    0.0

    iex> state = Drafter.Widget.Meter.mount(%{value: 0.4, label: "CPU"})
    iex> {state.label, state.orientation, state.show_value, state.show_label}
    {"CPU", :horizontal, true, true}

    iex> Drafter.Widget.Meter.mount(%{}).thresholds
    [{0.6, {80, 200, 100}}, {0.8, {255, 200, 0}}, {1.0, {255, 60, 60}}]

# `preferred_height`

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

The number of rows the element asks for.

A vertical meter asks for `opts[:height]`, default `8`. A horizontal meter asks
for `2` when a non-`nil` `:label` is given and `:show_label` is not `false`, and
`1` otherwise. `:height` is ignored for a horizontal meter.

    iex> Drafter.Widget.Meter.preferred_height(nil, [])
    1

    iex> Drafter.Widget.Meter.preferred_height(nil, label: "CPU")
    2

    iex> Drafter.Widget.Meter.preferred_height(nil, label: "CPU", show_label: false)
    1

    iex> Drafter.Widget.Meter.preferred_height(nil, orientation: :vertical)
    8

# `render`

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

Draws the meter into `rect`.

`state` may be a plain props map, in which case it is passed through `mount/1`
first. A horizontal meter returns one strip, or two when a label is drawn. A
vertical meter returns an optional label row, `rect.height` minus the label and
value rows of bar rows, and an optional percentage row.

# `unmount`

# `update`

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

Replaces the state fields named in `props`, keeping the current value for any key
that is absent.

Accepts `:value`, `:label`, `:orientation`, `:thresholds`, `:show_value`,
`:show_label`, `:style`, `:classes` and `:app_module`.

    iex> state = Drafter.Widget.Meter.mount(%{value: 0.2, label: "CPU"})
    iex> updated = Drafter.Widget.Meter.update(%{value: 0.9}, state)
    iex> {updated.value, updated.label}
    {0.9, "CPU"}

# `update_props_from_mount`

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

Narrows a re-render to the props that may change after mount.

Returns `:value`, `:label`, `:orientation`, `:thresholds`, `:show_value` and
`:show_label`. `:style`, `:classes` and `:app_module` are dropped, so they are
mount-only through the component tree.

    iex> props = Drafter.Widget.Meter.from_component_opts(nil, value: 0.5, class: :danger)
    iex> Drafter.Widget.Meter.update_props_from_mount(props, %{}, []) |> Map.keys() |> Enum.sort()
    [:label, :orientation, :show_label, :show_value, :thresholds, :value]

---

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