Drafter.Widget.PieChart (drafter v0.3.1)

Copy Markdown View Source

Renders a circular pie chart using Unicode block and braille characters.

Each slice is proportional to its value relative to the total. The chart uses quarter-block and half-block Unicode characters for sub-cell resolution rendering, and braille characters where finer detail is needed.

Component tag

Tag :pie_chart, built by Drafter.App as {:pie_chart, data, opts}:

pie_chart(data, opts)

The positional argument becomes :data when it is a non-empty list that is not a keyword list; otherwise :data is read from opts.

Options

  • :data - [{label, value} | {label, value, rgb}]. Default []. A slice without an explicit colour takes the palette entry at its index, cycling. A total of zero gives every slice a percentage of 0.0.

  • :show_legend - boolean/0, draw labels down the right-hand side. Default true. The legend takes min(longest_entry + 4, 30) columns and the pie takes the rest, with at least one column.
  • :show_percentages - boolean/0, append (12.5%) to each legend label. Default true. Only read when the legend is drawn.
  • :colors - [{r, g, b}] palette. Default [{100, 180, 255}, {255, 130, 80}, {100, 220, 140}, {220, 100, 220}, {255, 220, 80}, {120, 220, 220}, {255, 100, 100}, {180, 140, 255}].
  • :renderer - :text (default) draws block and braille cells. Any other atom is treated as a terminal graphics protocol and passed to Drafter.Widget.Chart.Pixel, falling back to cells when that protocol is unavailable.
  • :style - map/0 of style overrides passed to the theme computation. Default %{}.
  • :class - theme class atom or list of them, normalised by Drafter.Style.normalize_classes/1 and reaching mount/1 as :classes. Default [].
  • :height - pos_integer/0 read only by preferred_height/2, never by mount/1. Default 10.

Every option except :height is live-updatable: update_props_from_mount/3 passes the full mount props through.

Usage

pie_chart([{"Elixir", 45}, {"Rust", 30}, {"Go", 25}])
pie_chart([{"A", 60, {255, 100, 100}}, {"B", 40, {100, 100, 255}}])
pie_chart([{"X", 10}, {"Y", 20}], show_legend: false)

Summary

Functions

Replaces :data with a non-empty list of slice entries.

The component tag this widget registers under.

Builds the props map for a {:pie_chart, data, opts} element.

Ignores every event and returns {:bubble, state}, letting it continue to the parent widget and then to the app.

Whether this pie chart is drawing a transmitted image rather than cells.

Builds the widget state from props.

The number of rows the element asks for: opts[:height], default 10.

Draws the chart into rect, always returning exactly rect.height strips.

Callback implementation for Drafter.Widget.unmount/1.

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

Passes the mount props through unchanged, so every option is live-updatable through the component tree.

Types

entry()

@type entry() :: {String.t(), number()} | {String.t(), number(), rgb()}

rgb()

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

t()

@type t() :: %Drafter.Widget.PieChart{
  app_module: module() | nil,
  classes: [atom()],
  colors: [rgb()],
  data: [entry()],
  renderer: atom(),
  show_legend: boolean(),
  show_percentages: boolean(),
  style: map()
}

Functions

apply_data_buffer(state, data, rect)

@spec apply_data_buffer(t(), [entry() | term()], Drafter.Widget.rect() | nil) :: t()

Replaces :data with a non-empty list of slice entries.

Unlike the other data-driven widgets this expects a plain list rather than a Drafter.RingBuffer: {label, value} and {label, value, color} tuples pass through, and anything else becomes {"", term}. An empty list or a value that is not a list returns state unchanged.

iex> state = Drafter.Widget.PieChart.mount(%{})
iex> Drafter.Widget.PieChart.apply_data_buffer(state, [{"A", 1}, 7], nil).data
[{"A", 1}, {"", 7}]

iex> state = Drafter.Widget.PieChart.mount(%{data: [{"A", 1}]})
iex> Drafter.Widget.PieChart.apply_data_buffer(state, [], nil).data
[{"A", 1}]

component_tag()

@spec component_tag() :: :pie_chart

The component tag this widget registers under.

iex> Drafter.Widget.PieChart.component_tag()
:pie_chart

focused(state)

from_component_opts(data, opts)

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

Builds the props map for a {:pie_chart, data, opts} element.

data becomes :data when it is a non-empty list that is not a keyword list; otherwise opts[:data] is used, defaulting to []. :class is normalised into :classes and :__app_module__ becomes :app_module.

iex> props = Drafter.Widget.PieChart.from_component_opts([{"A", 1}], show_legend: false)
iex> {props.data, props.show_legend, props.renderer}
{[{"A", 1}], false, :text}

iex> Drafter.Widget.PieChart.from_component_opts(nil, data: [{"B", 2}]).data
[{"B", 2}]

handle_event(event, state)

@spec handle_event(Drafter.Event.t(), t()) :: {:bubble, t()}

Ignores every event and returns {:bubble, state}, letting it continue to the parent widget and then to the app.

image_active?(state)

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

Whether this pie chart is drawing a transmitted image rather than cells.

mount(props)

@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> state = Drafter.Widget.PieChart.mount(%{data: [{"A", 60}, {"B", 40}]})
iex> {state.data, state.show_legend, state.show_percentages, state.renderer}
{[{"A", 60}, {"B", 40}], true, true, :text}

iex> Drafter.Widget.PieChart.mount(%{}).colors |> length()
8

preferred_height(args, opts)

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

The number of rows the element asks for: opts[:height], default 10.

iex> Drafter.Widget.PieChart.preferred_height(nil, [])
10

iex> Drafter.Widget.PieChart.preferred_height(nil, height: 20)
20

render(state, rect)

Draws the chart into rect, always returning exactly rect.height strips.

state may be a plain props map, in which case it is passed through mount/1 first. Slices are laid out clockwise starting at twelve o'clock. This is the cell renderer and is used whatever :renderer says; image/3 is the graphics path.

unmount(state)

Callback implementation for Drafter.Widget.unmount/1.

update(props, state)

@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 :data, :show_legend, :show_percentages, :colors, :style, :classes, :app_module and :renderer.

iex> state = Drafter.Widget.PieChart.mount(%{data: [{"A", 1}]})
iex> updated = Drafter.Widget.PieChart.update(%{show_legend: false}, state)
iex> {updated.data, updated.show_legend}
{[{"A", 1}], false}

update_props_from_mount(mount_props, existing_state, opts)

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

Passes the mount props through unchanged, so every option is live-updatable through the component tree.

iex> props = Drafter.Widget.PieChart.from_component_opts([{"A", 1}], [])
iex> Drafter.Widget.PieChart.update_props_from_mount(props, %{}, []) == props
true