The one shape of every interaction the library delivers (spec/10 §19, D-170).
A host receives each as {:visualize_event, id, %Visualize.Event{}}, id the chart's or
the board's component id: a mark's declared event on a plain chart through
Visualize.Components.Chart, and on a board a mark's event, a drill, a host-owned brush
and a selection change through Visualize.Components.Board (spec/15 §9.2).
def handle_info({:visualize_event, "sales", %Visualize.Event{event: "point_click", row: row}}, socket) do
{:noreply, assign(socket, picked: row)}
endEvery field is always present:
event- a string for an event a design declares (a mark's tooltipevent, spec/14 §5.7, an atom there delivered as its string), an atom for one the runtime raises::drill,:brush,:selectcell- the board cell's name;niloutside a board and for the board's own eventsframe- the design's frame the item was drawn in, ornilitem-{mark, identity}: the mark'sid, else its type, and the row's identity — the declared one (D-167), bare for one field and a map for several, else the inference a board's pick uses — ornilwhen the row names nothingpath- the items from the innermost out to the frame:[item], or[]row- the row the clicked element was drawn from, typed as the source holds itat- the pointer in the frame's plot area,%{x, y}in pixels, ornilvalue- the event's data:%{}for a declared event, the runtime event's ownmodifiers- the keys held::shift,:alt,:ctrland:meta, in that order
A click arrives from TooltipHook as the wire event visualize_event (spec/10 §12.3),
carrying the clicked element's key and the token of the drawing it landed on.
decode/1 reads it and resolve/3 resolves it against the applied chart that was drawn,
or drops it: a click never names a row other than the one drawn.
Summary
Types
A click as decode/1 reads it from the wire: the declared name, the frame's name as
sent, the mark's position among its frame's marks, the row's ordinal in the mark's
realised rows, the drawing's token, the pointer in the chart's user units and the
modifiers.
Functions
The event names a chart's marks declare (spec/14 §5.7), as strings, sorted and unique.
The wire's visualize_event params as a click (spec/10 §19.2): {:ok, click}, or
:error for params of any other shape. No atom is created: the frame stays the string
it was sent as, and a modifier other than the four is dropped.
A row's identity in the board's selection form (spec/15 §13.2, D-167): {field, value}
for one field, a map for a composite declared identity, or nil.
An event's item (spec/10 §19.1): {mark, identity}, the mark's id, else its type,
and the identity bare for one field, or nil for a nil identity.
A declared event name as the event's string (spec/14 §5.7): an atom's name, or the string itself.
As resolve/3 with [] options.
A click resolved against the drawing of an applied chart (spec/10 §19.2, D-170):
{:ok, event}, or :error when the click's drawn is not the drawing's token, or the
applied chart has no frame of the click's name, no mark at its position, no row at its
ordinal of that mark's realised rows (Visualize.Chart.Mark.rows/3 over the applied
chart's sources), or when that mark declares no event of the click's name.
Types
@type click() :: %{ event: String.t(), frame: String.t() | nil, mark: non_neg_integer(), row: non_neg_integer(), drawn: String.t() | nil, at: %{x: number(), y: number()} | nil, modifiers: [modifier()] }
A click as decode/1 reads it from the wire: the declared name, the frame's name as
sent, the mark's position among its frame's marks, the row's ordinal in the mark's
realised rows, the drawing's token, the pointer in the chart's user units and the
modifiers.
@type modifier() :: :shift | :alt | :ctrl | :meta
Functions
@spec declared(Visualize.Chart.t() | Visualize.Chart.Applied.t()) :: [String.t()]
The event names a chart's marks declare (spec/14 §5.7), as strings, sorted and unique.
iex> chart = Visualize.Chart.from_map!(%{version: 2, sources: %{s: %{fields: [:x, :y]}},
...> frames: %{main: %{kind: :cartesian, scales: %{x: %{kind: :linear}, y: %{kind: :linear}}}},
...> marks: [%{type: :circle, data: :s, channels: %{x: :x, y: :y}, tooltip: %{event: :point_click}},
...> %{type: :line, data: :s, channels: %{x: :x, y: :y}, tooltip: %{event: "line_click"}}]})
iex> Visualize.Event.declared(chart)
["line_click", "point_click"]
The wire's visualize_event params as a click (spec/10 §19.2): {:ok, click}, or
:error for params of any other shape. No atom is created: the frame stays the string
it was sent as, and a modifier other than the four is dropped.
iex> Visualize.Event.decode(%{"event" => "point_click", "frame" => "main", "mark" => 1,
...> "row" => 4, "drawn" => "7", "at" => %{"x" => 12.5, "y" => 3}, "modifiers" => ["shift", "hyper"]})
{:ok, %{event: "point_click", frame: "main", mark: 1, row: 4, drawn: "7",
at: %{x: 12.5, y: 3}, modifiers: [:shift]}}
iex> Visualize.Event.decode(%{"event" => "point_click", "mark" => -1, "row" => 0})
:error
@spec identity(map(), map(), Visualize.Data.Schema.t() | nil) :: Visualize.Board.identity() | nil
A row's identity in the board's selection form (spec/15 §13.2, D-167): {field, value}
for one field, a map for a composite declared identity, or nil.
The identity the schema declares comes first, and a row that lacks it names nothing
rather than being guessed at. When the schema declares none — or there is no schema — it
is inferred: the mark's series channel when it reads a field, else the first field by
name that the schema types :category and the row has a value for.
iex> schema = Visualize.Data.Schema.new!(%{svc: %{type: :category}, v: %{type: :number}})
iex> Visualize.Event.identity(%{type: :circle}, %{svc: "api", v: 3}, schema)
{:svc, "api"}
iex> Visualize.Event.identity(%{type: :line, channels: %{series: :host}}, %{host: "mx1"}, nil)
{:host, "mx1"}
iex> Visualize.Event.identity(%{type: :circle}, %{v: 3}, nil)
nil
@spec item(map(), Visualize.Board.identity() | nil) :: item() | nil
An event's item (spec/10 §19.1): {mark, identity}, the mark's id, else its type,
and the identity bare for one field, or nil for a nil identity.
iex> Visualize.Event.item(%{type: :circle, id: :point}, {:service, "api"})
{:point, "api"}
iex> Visualize.Event.item(%{type: :circle}, %{host: "mx1", iface: "eth0"})
{:circle, %{host: "mx1", iface: "eth0"}}
iex> Visualize.Event.item(%{type: :circle}, nil)
nil
A declared event name as the event's string (spec/14 §5.7): an atom's name, or the string itself.
iex> Visualize.Event.name(:point_click)
"point_click"
iex> Visualize.Event.name("point_click")
"point_click"
@spec resolve(click(), Visualize.Chart.Applied.t()) :: {:ok, t()} | :error
As resolve/3 with [] options.
@spec resolve(click(), Visualize.Chart.Applied.t(), keyword()) :: {:ok, t()} | :error
A click resolved against the drawing of an applied chart (spec/10 §19.2, D-170):
{:ok, event}, or :error when the click's drawn is not the drawing's token, or the
applied chart has no frame of the click's name, no mark at its position, no row at its
ordinal of that mark's realised rows (Visualize.Chart.Mark.rows/3 over the applied
chart's sources), or when that mark declares no event of the click's name.
The row is the realised row, typed as the source holds it; the item is the slot's
declared identity, else the inference of identity/3; at is the pointer less the
frame's box and margins, in its plot area.
Options
:drawn- the token of the drawing the caller holds,nilby default; a click carrying another is dropped, never resolved against newer rows:cell- the event'scell(defaultnil)