Drafter.Widget.Markdown (drafter v0.3.2)

Copy Markdown View Source

Renders a subset of Markdown to the terminal with themed styling.

Supported syntax: # and ## headings, **bold**, *italic*, and `inline code`. Block elements are styled via the theme system using the :h1, :h2, and :text theme parts. A configurable horizontal padding is applied inside the widget boundaries.

Component tag

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

markdown(content, opts)

The positional argument becomes :content, falling back to opts[:content] when nil.

Options

  • :content - String.t/0, the Markdown to render. Default "". Supplied positionally through the markdown/2 element; the positional argument wins unless it is nil.
  • :padding - non_neg_integer/0, left and right padding in columns. Default 1. A rect narrower than 2 * padding renders nothing.
  • :style - map/0 of base style attributes merged under the computed theme styles. Default %{}.
  • :height - pos_integer/0 read only by preferred_height/2, never by mount/1. Default is the line count of the content, at least 3.

Every option is live-updatable: update/2 merges the props map straight into the state. update_props_from_mount/3 narrows a re-render to :content alone, so a :padding or :style change made after mount through the component tree is not picked up.

Usage

markdown(content: "# Title\n\nSome **bold** and *italic* text with `code`.")
markdown(content: readme_text, padding: 2)

Summary

Functions

The component tag this widget registers under.

Builds the props map for a {:markdown, content, 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.

Renders the parsed Markdown into rect, one Drafter.Draw.Strip per source line.

Merges props into state and returns the result.

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

Types

t()

@type t() :: %{content: String.t(), style: map(), padding: non_neg_integer()}

Functions

component_tag()

@spec component_tag() :: :markdown

The component tag this widget registers under.

iex> Drafter.Widget.Markdown.component_tag()
:markdown

from_component_opts(content, opts)

@spec from_component_opts(
  String.t() | nil,
  keyword()
) :: t()

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

content is the positional argument; when it is nil, opts[:content] is used instead, defaulting to "". Also reads :style (default %{}) and :padding (default 1).

iex> Drafter.Widget.Markdown.from_component_opts("# Title", padding: 0)
%{content: "# Title", padding: 0, style: %{}}

iex> Drafter.Widget.Markdown.from_component_opts(nil, content: "fallback")
%{content: "fallback", padding: 1, style: %{}}

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 :content (default ""), :style (default %{}) and :padding (default 1).

iex> Drafter.Widget.Markdown.mount(%{})
%{content: "", padding: 1, style: %{}}

iex> Drafter.Widget.Markdown.mount(%{content: "# Title", padding: 2})
%{content: "# Title", padding: 2, style: %{}}

preferred_height(args, opts)

@spec preferred_height(
  String.t() | nil,
  keyword()
) :: pos_integer()

The number of rows the element asks for.

args is the positional content string, or nil. Returns opts[:height] when given, otherwise the line count of args with a floor of 3.

iex> Drafter.Widget.Markdown.preferred_height("# Title", [])
3

iex> Drafter.Widget.Markdown.preferred_height("a\nb\nc\nd", [])
4

iex> Drafter.Widget.Markdown.preferred_height(nil, height: 10)
10

render(state, rect)

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

Renders the parsed Markdown into rect, one Drafter.Draw.Strip per source line.

Headings are styled through the :h1 and :h2 theme parts and every other line through :text. Returns [] when rect.width leaves no room after padding.

update(props, state)

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

Merges props into state and returns the result.

Any key present in props replaces the one in the state, so :content, :padding and :style are all live-updatable.

iex> state = Drafter.Widget.Markdown.mount(%{content: "a"})
iex> Drafter.Widget.Markdown.update(%{content: "b"}, state)
%{content: "b", padding: 1, style: %{}}

update_props_from_mount(mount_props, existing_state, opts)

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

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

Only :content is carried over, so a :padding or :style change made through the component tree after the first mount is not picked up.

iex> props = Drafter.Widget.Markdown.from_component_opts("# New", padding: 4)
iex> Drafter.Widget.Markdown.update_props_from_mount(props, %{}, [])
%{content: "# New"}