Renders a horizontal progress bar with optional percentage, value, and ETA display.
Supports both a determinate mode (showing progress toward a known maximum) and an indeterminate mode that animates a sliding block when the total is unknown.
Component tag
Tag :progress_bar, built by Drafter.App as {:progress_bar, opts}:
progress_bar(opts)There is no positional argument; every prop comes from opts.
Options
:progress-number/0current progress. Default0.0. The filled fraction isprogress / max_value, clamped into0.0..1.0.:max_value-number/0value representing 100%. Default100.0. Amax_valueof0or less renders as 0%.:show_percentage-boolean/0, append the rounded percentage. Defaulttrue.:show_eta-boolean/0, append an estimated time remaining. Defaulttrue. Shows"..."until at least one second of wall clock has passed since mount and progress is above zero, then"12s","3m 4s"or"1h 2m", and"∞"when the computed rate is not positive.:indeterminate-boolean/0. Defaultfalse. Animates a sliding block whose position advances by one on eachupdate/2and ignores:progress,:show_percentageand:show_eta.:label-String.t/0ornil. Defaultnil. Held on the state and never drawn.:show_value-boolean/0. Defaultfalse. Held on the state and never drawn; the status text is built from:show_percentageand:show_etaonly.:width-pos_integer/0. Default50when mounting directly, and the width ofopts[:__rect__]through the element. Held on the state and never read byrender/2, which uses the rect it is given.:height-pos_integer/0. Default1when mounting directly, and the height ofopts[:__rect__]through the element. Held on the state and never read byrender/2.:pulse- read byfrom_component_opts/2with defaultfalseand dropped bymount/1; the state has no such field.:class- theme class atom or list of them, normalised into:classesbyfrom_component_opts/2with default[]and dropped bymount/1.
update/2 accepts :progress, :max_value, :label, :show_percentage,
:show_value, :show_eta, :width, :height and :indeterminate, and
refreshes the animation clock on every call. Through the component tree,
update_props_from_mount/3 narrows that to :progress, :max_value, :label,
:show_percentage, :show_value, :indeterminate and :classes — :show_eta,
:width and :height are mount-only.
Usage
progress_bar(progress: 42.0, max_value: 100.0)
progress_bar(progress: 7, max_value: 20, show_percentage: false, show_value: true)
progress_bar(indeterminate: true)
Summary
Functions
Sets :progress from the newest entry of a Drafter.RingBuffer.
The component tag this widget registers under.
Builds the props map for a {:progress_bar, opts} element.
Ignores every event and returns {:noreply, state}. The bar is not focusable.
Builds the widget state from props.
The number of rows the element asks for: 1, or 8 when opts[:orientation] is
:vertical.
Draws the bar into rect.
Callback implementation for Drafter.Widget.unmount/1.
Replaces the state fields named in props, keeping the current value for any key
that is absent.
Narrows a re-render to the props that may change after mount.
Types
@type t() :: %Drafter.Widget.ProgressBar{ height: pos_integer(), indeterminate: boolean(), label: String.t() | nil, last_progress: number(), last_update_time: integer(), max_value: number(), progress: number(), show_eta: boolean(), show_percentage: boolean(), show_value: boolean(), spin_position: non_neg_integer(), start_time: integer(), width: pos_integer() }
Functions
@spec apply_data_buffer(t(), Drafter.RingBuffer.t(), Drafter.Widget.rect()) :: t()
Sets :progress from the newest entry of a Drafter.RingBuffer.
Returns state unchanged when the buffer is empty. The rect is ignored.
@spec component_tag() :: :progress_bar
The component tag this widget registers under.
iex> Drafter.Widget.ProgressBar.component_tag()
:progress_bar
@spec from_component_opts( term(), keyword() ) :: Drafter.Widget.props()
Builds the props map for a {:progress_bar, opts} element.
The positional argument is ignored. :width and :height fall back to the width
and height of opts[:__rect__], itself defaulting to %{width: 50, height: 1}.
:class is normalised into :classes. The result also carries :pulse and
:classes, which mount/1 drops.
iex> props = Drafter.Widget.ProgressBar.from_component_opts(nil, progress: 3, max_value: 6)
iex> {props.progress, props.max_value, props.width, props.height}
{3, 6, 50, 1}
iex> props = Drafter.Widget.ProgressBar.from_component_opts(nil, __rect__: %{width: 80, height: 2})
iex> {props.width, props.height}
{80, 2}
@spec handle_event(Drafter.Event.t(), t()) :: {:noreply, t()}
Ignores every event and returns {:noreply, state}. The bar is not focusable.
@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.
:start_time and :last_update_time are set to the current monotonic
millisecond, which is what the ETA is measured against, and :spin_position
starts at 0.
iex> state = Drafter.Widget.ProgressBar.mount(%{})
iex> {state.progress, state.max_value, state.indeterminate, state.spin_position}
{0.0, 100.0, false, 0}
iex> state = Drafter.Widget.ProgressBar.mount(%{progress: 7, max_value: 20})
iex> {state.progress, state.last_progress, state.show_percentage, state.show_eta}
{7, 7, true, true}
@spec preferred_height( term(), keyword() ) :: pos_integer()
The number of rows the element asks for: 1, or 8 when opts[:orientation] is
:vertical.
:orientation is not otherwise an option of this widget — render/2 always
draws horizontally — so a progress bar built from the component tree asks for one
row.
iex> Drafter.Widget.ProgressBar.preferred_height(nil, [])
1
iex> Drafter.Widget.ProgressBar.preferred_height(nil, orientation: :vertical)
8
@spec render(t() | Drafter.Widget.props(), Drafter.Widget.rect()) :: [ Drafter.Draw.Strip.t() ]
Draws the bar into rect.
state may be a plain props map, in which case it is passed through mount/1
first. The bar always fills rect.width; the status text is drawn at the right
and the track takes what is left. Returns rect.height strips, the first the bar
and the rest blank.
Callback implementation for Drafter.Widget.unmount/1.
@spec update(Drafter.Widget.props(), t()) :: t()
Replaces the state fields named in props, keeping the current value for any key
that is absent.
Also refreshes :last_update_time and :last_progress, and advances
:spin_position by one modulo 40 while the bar is indeterminate.
iex> state = Drafter.Widget.ProgressBar.mount(%{indeterminate: true})
iex> Drafter.Widget.ProgressBar.update(%{}, state).spin_position
1
iex> state = Drafter.Widget.ProgressBar.mount(%{progress: 1})
iex> updated = Drafter.Widget.ProgressBar.update(%{progress: 40}, state)
iex> {updated.progress, updated.last_progress, updated.spin_position}
{40, 40, 0}
@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 :progress, :max_value, :label, :show_percentage, :show_value,
:indeterminate and :classes. :show_eta, :width and :height are dropped,
so they are mount-only through the component tree.
iex> props = Drafter.Widget.ProgressBar.from_component_opts(nil, progress: 3)
iex> Drafter.Widget.ProgressBar.update_props_from_mount(props, %{}, []) |> Map.keys() |> Enum.sort()
[:classes, :indeterminate, :label, :max_value, :progress, :show_percentage, :show_value]