Where a drop on the graph lands, and what a component means there (spec/14 §18.16).
A drop is a point in chart coordinates; region/2 classifies it against the frame the
preview drew — the plot area, or one of the four margins — and node/4 says what the
dropped component becomes in that region: an axis on that side with the scale it needs,
a title or a label at the anchor the region implies, a legend at the nearest position, a
mark of a type. home/1 is the region the keyboard twin and the + menu place in.
Every function here is pure; the builder inserts what node/4 returns.
This module compiles only when Phoenix.Component is loaded (spec/10 §1.1, D-45, D-85).
Summary
Types
What the palette offers: a component, or a mark of a type.
A region of the frame a drop lands in.
A site to insert: its kind, its body under the kind's key, and its key if named.
Functions
A component from the word its row carries, or :error.
The palette's rows in the order shown: the four components, then a mark of every type.
The size and margin the preview draws a design at: the size is the render's own
default, since a design records none (spec/14 §4.2), and the margin is the design's
over the schema's default.
A mark type's data and channels guessed over the composed design (spec/14 §18.16):
the chart's first source — the one the last mark binds, else the first declared by
name — and its fields in order over the type's required channels; fewer fields leave
the rest unbound; no source, nothing.
The region a component is placed in from the keyboard or the + menu: an axis at the
bottom, a title at the top, a label at the bottom, a legend and a mark in the plot.
Whether a drawn node's label names a node that is moved on the graph (spec/14 §18.16): an axis, the legend, a label. A mark's position is its data; the frame does not move.
A node moved to a region at a point over the composed design (spec/14 §18.16): its body
with the placement key rewritten — an axis's side, a legend's position, a label's
anchor — or :same when the region names what it already is, or :unmovable for a
kind with no placement. An axis whose orientation changes takes the scale its new side
reads — the one another axis on that orientation reads, else x or y — since a
scale's range is set by its name (§4.3); a scale the design lacks is brought as a drop
brings it, {:ok, body, [scale_site]}.
What a component becomes in a region of the composed design, dropped at a point:
{:sites, sites} — lowest first, so an axis's scale comes before the axis — or
{:legend, node} for the frame's legend, or {:error, :no_scale} for a legend on a
design with no ordinal scale.
A library entry's body placed: an axis's side, a label's anchor set from the region
where the region names one; anything else, and a drop in the plot, as the entry wrote it.
The region a point in chart coordinates falls in: inside the plot area is :plot;
otherwise the top or bottom band, then the left or right — so a corner is the top's or
the bottom's, where the title and the x axis live.
Types
@type component() :: :axis | :legend | :title | :label | {:mark, atom()}
What the palette offers: a component, or a mark of a type.
@type region() :: :plot | :top | :bottom | :left | :right
A region of the frame a drop lands in.
A site to insert: its kind, its body under the kind's key, and its key if named.
Functions
A component from the word its row carries, or :error.
iex> Visualize.Chart.Builder.Place.component("mark:line")
{:ok, {:mark, :line}}
iex> Visualize.Chart.Builder.Place.component("axis")
{:ok, :axis}
iex> Visualize.Chart.Builder.Place.component("mark:banana")
:error
The palette's rows in the order shown: the four components, then a mark of every type.
iex> Visualize.Chart.Builder.Place.components() |> Enum.take(5)
[axis: "axis", legend: "legend", title: "title", label: "label", "mark:line": "mark: line"]
The size and margin the preview draws a design at: the size is the render's own
default, since a design records none (spec/14 §4.2), and the margin is the design's
over the schema's default.
iex> Visualize.Chart.Builder.Place.geometry(%{})
%{size: %{width: 600, height: 400}, margin: %{top: 20, right: 20, bottom: 30, left: 40}}
A mark type's data and channels guessed over the composed design (spec/14 §18.16):
the chart's first source — the one the last mark binds, else the first declared by
name — and its fields in order over the type's required channels; fewer fields leave
the rest unbound; no source, nothing.
iex> design = %{sources: %{s: %{fields: [:hour, :value, :extra]}}}
iex> Visualize.Chart.Builder.Place.guess(design, :line)
%{data: :s, channels: %{x: :hour, y: :value}}
iex> Visualize.Chart.Builder.Place.guess(design, :rect)
%{data: :s, channels: %{x0: :hour, x1: :value, y0: :extra}}
iex> Visualize.Chart.Builder.Place.guess(%{}, :line)
%{}
The region a component is placed in from the keyboard or the + menu: an axis at the
bottom, a title at the top, a label at the bottom, a legend and a mark in the plot.
Whether a drawn node's label names a node that is moved on the graph (spec/14 §18.16): an axis, the legend, a label. A mark's position is its data; the frame does not move.
iex> Visualize.Chart.Builder.Place.movable?("frames.main.axes[1]")
true
iex> Visualize.Chart.Builder.Place.movable?("marks[0]")
false
@spec moved(atom(), map(), region(), {number(), number()}, map()) :: {:ok, map()} | {:ok, map(), [site()]} | :same | :unmovable
A node moved to a region at a point over the composed design (spec/14 §18.16): its body
with the placement key rewritten — an axis's side, a legend's position, a label's
anchor — or :same when the region names what it already is, or :unmovable for a
kind with no placement. An axis whose orientation changes takes the scale its new side
reads — the one another axis on that orientation reads, else x or y — since a
scale's range is set by its name (§4.3); a scale the design lacks is brought as a drop
brings it, {:ok, body, [scale_site]}.
iex> Visualize.Chart.Builder.Place.moved(:axis, %{scale: :x, side: :bottom}, :top, {0, 0})
{:ok, %{scale: :x, side: :top}}
iex> Visualize.Chart.Builder.Place.moved(:axis, %{scale: :x, side: :bottom}, :left, {0, 0}, %{frames: %{main: %{scales: %{x: %{}, y: %{}}}}})
{:ok, %{scale: :y, side: :left}}
iex> Visualize.Chart.Builder.Place.moved(:axis, %{scale: :x, side: :bottom}, :left, {0, 0})
{:ok, %{scale: :y, side: :left}, [%{kind: :scale, body: %{kind: :linear, domain: :auto}, key: :y}]}
iex> Visualize.Chart.Builder.Place.moved(:axis, %{scale: :x, side: :bottom}, :bottom, {0, 0})
:same
iex> Visualize.Chart.Builder.Place.moved(:mark, %{type: :line}, :top, {0, 0})
:unmovable
@spec node(component(), region(), map(), {number(), number()}) :: {:sites, [site()]} | {:legend, map()} | {:error, :no_scale}
What a component becomes in a region of the composed design, dropped at a point:
{:sites, sites} — lowest first, so an axis's scale comes before the axis — or
{:legend, node} for the frame's legend, or {:error, :no_scale} for a legend on a
design with no ordinal scale.
iex> design = %{frames: %{main: %{scales: %{x: %{kind: :linear}}}}}
iex> Visualize.Chart.Builder.Place.node(:axis, :bottom, design, {0, 0})
{:sites, [%{kind: :axis, body: %{scale: :x, side: :bottom}, key: nil}]}
iex> Visualize.Chart.Builder.Place.node(:axis, :left, design, {0, 0})
{:sites, [%{kind: :scale, body: %{kind: :linear, domain: :auto}, key: :y}, %{kind: :axis, body: %{scale: :y, side: :left}, key: nil}]}
iex> Visualize.Chart.Builder.Place.node(:legend, :plot, design, {0, 0})
{:error, :no_scale}
A library entry's body placed: an axis's side, a label's anchor set from the region
where the region names one; anything else, and a drop in the plot, as the entry wrote it.
iex> Visualize.Chart.Builder.Place.placed(%{axis: %{scale: :y, side: :left}}, :right)
%{axis: %{scale: :y, side: :right}}
iex> Visualize.Chart.Builder.Place.placed(%{axis: %{scale: :y, side: :left}}, :plot)
%{axis: %{scale: :y, side: :left}}
The region a point in chart coordinates falls in: inside the plot area is :plot;
otherwise the top or bottom band, then the left or right — so a corner is the top's or
the bottom's, where the title and the x axis live.
iex> frame = %{size: %{width: 400, height: 200}, margin: %{top: 20, right: 20, bottom: 30, left: 40}}
iex> Visualize.Chart.Builder.Place.region({200, 100}, frame)
:plot
iex> Visualize.Chart.Builder.Place.region({200, 190}, frame)
:bottom
iex> Visualize.Chart.Builder.Place.region({5, 5}, frame)
:top
iex> Visualize.Chart.Builder.Place.region({5, 100}, frame)
:left