Visualize.Event (Visualize v0.6.0)

Copy Markdown View Source

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)}
end

Every field is always present:

  • event - a string for an event a design declares (a mark's tooltip event, spec/14 §5.7, an atom there delivered as its string), an atom for one the runtime raises: :drill, :brush, :select
  • cell - the board cell's name; nil outside a board and for the board's own events
  • frame - the design's frame the item was drawn in, or nil
  • item - {mark, identity}: the mark's id, 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 — or nil when the row names nothing
  • path - 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 it
  • at - the pointer in the frame's plot area, %{x, y} in pixels, or nil
  • value - the event's data: %{} for a declared event, the runtime event's own
  • modifiers - the keys held: :shift, :alt, :ctrl and :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.

t()

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

click()

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

item()

@type item() :: {atom() | String.t(), term()}

modifier()

@type modifier() :: :shift | :alt | :ctrl | :meta

t()

@type t() :: %Visualize.Event{
  at: %{x: number(), y: number()} | nil,
  cell: atom() | nil,
  event: String.t() | atom(),
  frame: atom() | nil,
  item: item() | nil,
  modifiers: [modifier()],
  path: [item()],
  row: map() | nil,
  value: map()
}

Functions

declared(arg1)

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"]

decode(params)

@spec decode(map()) :: {:ok, click()} | :error

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

identity(mark, row, schema)

@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

item(mark, identity)

@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

name(name)

@spec name(atom() | String.t()) :: String.t()

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"

resolve(click, applied)

@spec resolve(click(), Visualize.Chart.Applied.t()) :: {:ok, t()} | :error

As resolve/3 with [] options.

resolve(click, applied, opts)

@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, nil by default; a click carrying another is dropped, never resolved against newer rows
  • :cell - the event's cell (default nil)