Drafter.Widget.Placeholder (drafter v0.3.2)

Copy Markdown View Source

Renders a coloured placeholder block useful during development and layout design.

Each placeholder is assigned a distinct pastel background colour derived from a number embedded in its text label. Text is centered vertically and horizontally inside the block, and an optional border can be drawn around the content area.

Component tag

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

placeholder(opts)

placeholder/1 takes a keyword list only — there is no positional argument, so the label must be given as text: (or label:, which the element accepts as an alias).

Options

  • :text - String.t/0 label drawn in the middle row. Default "Placeholder". The first run of digits in the text picks the pastel colour: "Placeholder 3" takes the third entry, text with no digits takes the first.
  • :label - String.t/0 alias for :text, read only by from_component_opts/2 and taking precedence over :text. Default: the value of :text.
  • :padding - non_neg_integer/0 padding applied on all four sides when computing the content area. Default 2. Nothing is drawn once 2 * padding reaches the rect width or height.
  • :align - :left | :center | :right. Default :center. Stored on the state and returned by mount/1, but render/2 always centres the text.

  • :border - boolean/0, draw a box border around the content area. Default false.
  • :style - map/0 of style attributes applied to every segment. Default through mount/1 is the auto-generated %{fg: ..., bg: ...} pastel pair; default through the component tag is %{}, which discards the pastel colour.

Only :text is live-updatable through the component tree: update_props_from_mount/3 returns :text alone. update/2 called directly merges any key.

Usage

placeholder(text: "Placeholder 1")
placeholder(text: "Placeholder 2", border: true, padding: 4)

Summary

Functions

The component tag this widget registers under.

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

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

Builds the widget state from props.

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

Draws the block into rect.

Merges props into state and returns the result.

Narrows a re-render to :text, the only prop that reaches an already-mounted placeholder through the component tree.

Types

t()

@type t() :: %{
  text: String.t(),
  style: map(),
  padding: non_neg_integer(),
  align: :left | :center | :right,
  border: boolean()
}

Functions

component_tag()

@spec component_tag() :: :placeholder

The component tag this widget registers under.

iex> Drafter.Widget.Placeholder.component_tag()
:placeholder

from_component_opts(args, opts)

@spec from_component_opts(
  term(),
  keyword()
) :: t()

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

The positional argument is ignored. Reads :label first and falls back to :text, both defaulting to "Placeholder"; then :padding (default 2), :align (default :center), :border (default false) and :style (default %{}). Because :style is always present in the result, a placeholder built from the component tag never picks up the pastel colour mount/1 would otherwise generate.

iex> Drafter.Widget.Placeholder.from_component_opts(nil, [])
%{align: :center, border: false, padding: 2, style: %{}, text: "Placeholder"}

iex> Drafter.Widget.Placeholder.from_component_opts(nil, label: "Left", text: "Right")
%{align: :center, border: false, padding: 2, style: %{}, text: "Left"}

handle_event(event, state)

@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.

mount(props)

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

Builds the widget state from props.

Reads :text (default "Placeholder"), :style (default: a pastel %{fg: rgb, bg: rgb} pair chosen from the digits in the text), :padding (default 2), :align (default :center) and :border (default false).

iex> Drafter.Widget.Placeholder.mount(%{text: "Placeholder 1"})
%{align: :center, border: false, padding: 2, style: %{bg: {77, 17, 68}, fg: {230, 230, 230}}, text: "Placeholder 1"}

iex> Drafter.Widget.Placeholder.mount(%{text: "x", style: %{}, padding: 0, border: true})
%{align: :center, border: true, padding: 0, style: %{}, text: "x"}

preferred_height(args, opts)

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

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

iex> Drafter.Widget.Placeholder.preferred_height(nil, [])
3

iex> Drafter.Widget.Placeholder.preferred_height(nil, height: 12)
12

render(state, rect)

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

Draws the block into rect.

Returns [] when 2 * padding leaves no content width or height. Without a border the result has one strip per content row, with the text centred on the middle row. With a border the result has content_height + 2 strips: a top rule, the bordered rows, and a bottom rule.

update(props, state)

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

Merges props into state and returns the result.

Every key in props replaces the one on the state, including :style, so the pastel colour chosen at mount is only kept while :style stays absent.

iex> state = Drafter.Widget.Placeholder.mount(%{text: "x", style: %{}})
iex> Drafter.Widget.Placeholder.update(%{text: "y", border: true}, state)
%{align: :center, border: true, padding: 2, style: %{}, text: "y"}

update_props_from_mount(mount_props, existing_state, opts)

@spec update_props_from_mount(t(), term(), keyword()) :: %{text: String.t()}

Narrows a re-render to :text, the only prop that reaches an already-mounted placeholder through the component tree.

iex> props = Drafter.Widget.Placeholder.from_component_opts(nil, text: "New", border: true)
iex> Drafter.Widget.Placeholder.update_props_from_mount(props, %{}, [])
%{text: "New"}