Visualize.Chart.Builder.Stack (Visualize v0.2.25)

Copy Markdown View Source

The builder's stack as a list of use sites, and the pure operations over it (spec/14 §19.3, §19.6, §19.9).

The builder's state is uses — %Visualize.Chart.Use{} structs in stack order — and everything else the panels read is derived from them: the resolved layers, each use fetched through the store and its mask, bindings and local applied (§19.3); the design, which is the enabled ones stacked (§19.6); and the labels a person sees, which are a use's entry name or the name the host gave a layer it seeded.

A host's layers assign is fragments, and a fragment the host gave is seeded as an inline-only use — ref: nil, the fragment as local — so the host's contract of §18.2 is unchanged: it gives a stack of fragments, and the builder makes sites of them.

Disabling a layer is mask: :all (D-87 restated in §19.3). Selection is a use's id, so it survives every insert and move without being remapped.

This module compiles only when Phoenix.Component is loaded (spec/10 §1.1, D-45, D-85).

Summary

Types

A use's id to the name a person sees for it.

A resolved layer, as every panel has always read one: its label and its fragment.

A row of the tree: the site, its depth, its parent's id, its flat position and its owner — nil for a site of the tree, the referenced site's id for a row of a referenced group's inside (§18.8, #388), whose index is the owner's.

Functions

Every site of the tree, depth first, as rows — the flat order the panel draws and a flat position names.

The rows of all/1 with each referenced group's inside beneath its row (spec/14 §18.8, #388): the sites its entry holds, through the fetch, depth first — a group the entry holds followed by what it holds, a composite it references by that entry's sites. Those sites are the library's and their ids the entry's, not the tree's, so they have no flat position of their own: each row carries owner, the referenced site's id, and the owner's flat position as its index, so a gesture on one is a gesture on the group, and a flat position still indexes all/1. A row directly under the owner names it as its parent; one deeper names the entry's site holding it. An entry met again below itself is not opened twice.

The ids from an anchor to a sibling, in tree order — the range a Shift+click selects (spec/14 §18.8, #382) — or :error when the two are not siblings.

The lowest tier that holds the stack (D-99): one bare inline site is its body, values and nothing else; anything more — a ref, a mask, a binding, a second site, a var — is structure.

The sites an inline group holds; [] for an object or a referenced site.

One use's contribution placed where the design keeps its kind (spec/14 §18.8, #283): resolved through the flattening fetch, as placed/2 resolves it, so a reference to a composite is what the composite flattens to; its mask ignored, since a disabled layer still declares what it declares; a missing entry its local alone; a keyless site %{}.

An entry as the inline sites a copy of it is (spec/14 §18.17): a composite's sites, each holding what its use contributed — the referenced fragment (flattened, when it is a composite) stacked under the use's local, the use's mask, vars and key kept — labelled with the referenced entry's name, or the composite's own for a site it declares in place, with the composite's own vars and signals; any other body as one inline site named for the entry. The sites carry no id: the caller issues one each among the stack they join.

The design the stack stands for: the enabled uses as a composite over the stack's own vars, flattened through the store, every id at a reference site resolved, so the frame can draw it (spec/14 §19.6).

A copy of a site and what it holds, every id fresh among the tree, with the {copy, original} id pairs so labels follow (spec/14 §18.8, #382).

The empty, valid fragment of a kind, for new fragment of a kind (spec/14 §19.8).

Whether a use contributes to the design: anything but a mask of everything.

The fetch a store gives resolution: an id to {:ok, fragment} or :error.

The use with an id anywhere in the tree, or nil.

A group's local: an inline composite with no objects.

Whether a use is a container: an inline composite — a layer or a group.

Sibling sites wrapped in a group at the first one's position, in their tree order (spec/14 §18.8, #382); :error when the ids are not siblings, or none.

The flat position of a use in the tree, or nil.

The name a site of a composite is shown by (spec/14 §18.8, §18.17): a referenced site by its entry's name — its id when the store has none — and a site the composite declares in place by its kind (#380), or by own, the composite's name, when it declares several. A copy's sites are labelled so, and so are a referenced group's rows.

A use inserted as the sibling after another — at the top, at the end, for nil — or, with {:into, id}, appended inside that group.

A use inserted before the row at a flat position, in that row's own container — at the end of the top past the last row (spec/14 §18.17, #380).

The sites a referenced composite's entry holds (spec/14 §18.8, #388) — the library's, read-only in the chart — or nil for a use that is not a reference to a composite or whose entry the fetch cannot find.

The kinds new fragment of a kind offers, the composite first and never a design: a design is the deployed tier, and a composite deploys to one (spec/14 §19.6, §19.8).

The name a person sees for a use: its label, or its id when it has none.

The site that copies or overrides a node the use contributes (spec/14 §18.16, #379): %{local, key, mask, lifted} — the inline site's local and key, the mask path to add to the owner or nil, and how many members were lifted — or :error when the use does not contribute the node. The node carries its body as the design composes it, since a list member's index in the design is not its index in the owner's list. A :copy takes a fresh id or key, avoiding taken; an :override keeps the node's identity and, for a node with none — a label, a mark with no id — lifts the owner's whole list and masks it at the finest path the owner's contribution has for it.

A use's state toward the library (spec/14 §18.8, §19.3, #381): :inline with no ref and no origin, :linked with a ref and no local, :edited with a ref over a local — locked again after edits — and :unlocked with an origin and no ref.

The path a design path has inside a use: a site of one kind holds its body under the kind's key (spec/14 §19.3), so the design's sources.primary is a source site's [:source], and a design-level site's path is its own (§18.15).

A use with an origin linked to it again, its local kept over the link (spec/14 §18.8, #381); a use with no origin has nothing to link to and is unchanged.

Whether a use is the library's word — it references an entry — and so is locked in the chart: read-only, edited by opening the entry (spec/14 §18.8, §18.15).

A move within the tree by flat positions (§19.3, #380): the row at from — a group with everything it holds — taken out, then put before the row that stands at to once it is out, in that row's own container; past the last row, at the end of the top. A row cannot land inside what it holds, since those rows are out with it.

An entry as a stack (spec/14 §19.9): a composite's sites, labelled by the store's names, with its vars and its signals (§19.10); any other body as one inline site named for the entry.

The id of the group holding a use, or nil at the top.

The enabled uses as the layers the design is stacked from — {id, fragment} in stack order, each resolved and placed where the design keeps its kind (spec/14 §18.15, §19.6) — so Visualize.Chart.explain/1 over them says which use gave every value. A name-keyed site with no key has nowhere to be placed and is left out, as design/3 leaves it out.

The tree without a use (and what it holds); a missing id changes nothing.

Every use resolved to a layer the panels can read — {label, fragment} — and the refs no store could find (spec/14 §19.2, §19.3). A disabled use resolves as it would enabled: the panels edit what a layer says whether or not the design is drawing it, and its mask of everything has its whole effect in design/2.

A use as the library has it (spec/14 §18.8, #381): its local dropped, its ref restored from its origin where it was unlocked, mask, vars and key kept — they are the site's.

A host's layers as use sites, with their labels (spec/14 §19.9).

Each site that carries variables: the free variables of its fragment before the site's bindings — the names the fragment itself uses — and what the site binds them to, for the Variables tab's per-site rows (spec/14 §19.4, §19.10).

The stack as a composite: what saving sends and a composite stores (spec/14 §19.6).

As structure/2, with the chart's signals when it has any (spec/14 §19.10).

A use with its enabled state flipped: disabling is a mask of everything (§19.3).

An inline group's sites spliced in at its position, the group gone (spec/14 §18.8, #382); any other site leaves the tree unchanged.

A use unlocked from its entry (spec/14 §18.8, #381): the ref moved to origin and the entry's body copied into local under whatever the site already held there; mask, vars and key stay the site's, so the chart draws as it did. A composite entry unlocks to a group of copies of its sites, as copies/3 reads them, the composite's vars the group's; the inner sites come with nil ids for the caller to issue, and their labels and the entry's signals are returned beside the use.

What a site contributes before its mask — the referenced body, or nothing, under its local — so the context panel can offer every key the mask could remove (spec/14 §19.5).

The tree with one use, anywhere, replaced by a function of it; a missing id changes nothing.

A write into a use's local at a path (spec/14 §19.3): a {:ok, value} sets the key, :error removes it. A container the local does not carry yet is an empty node on the way (§18.5).

A write into any fragment at a path, as write/4 does into a use's local.

Types

labels()

@type labels() :: %{required(Visualize.Chart.Use.id()) => String.t()}

A use's id to the name a person sees for it.

resolved()

@type resolved() :: {String.t(), map()}

A resolved layer, as every panel has always read one: its label and its fragment.

row()

@type row() :: %{
  use: Visualize.Chart.Use.t(),
  depth: non_neg_integer(),
  parent: Visualize.Chart.Use.id() | nil,
  index: non_neg_integer(),
  owner: Visualize.Chart.Use.id() | nil
}

A row of the tree: the site, its depth, its parent's id, its flat position and its owner — nil for a site of the tree, the referenced site's id for a row of a referenced group's inside (§18.8, #388), whose index is the owner's.

Functions

all(uses)

@spec all([Visualize.Chart.Use.t()]) :: [row()]

Every site of the tree, depth first, as rows — the flat order the panel draws and a flat position names.

iex> inner = [%Visualize.Chart.Use{id: {:use, 3}, ref: nil, local: %{style: %{}}}]
iex> uses = [%Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{composite: %{uses: inner, vars: %{}}}}, %Visualize.Chart.Use{id: {:use, 2}, ref: nil, local: %{mark: %{}}}]
iex> for row <- Visualize.Chart.Builder.Stack.all(uses), do: {row.use.id, row.depth, row.parent, row.index}
[{{:use, 1}, 0, nil, 0}, {{:use, 3}, 1, {:use, 1}, 1}, {{:use, 2}, 0, nil, 2}]

all(uses, fetch)

@spec all([Visualize.Chart.Use.t()], (Visualize.Chart.Fragment.id() ->
                                  {:ok, map()} | :error)) :: [
  row()
]

The rows of all/1 with each referenced group's inside beneath its row (spec/14 §18.8, #388): the sites its entry holds, through the fetch, depth first — a group the entry holds followed by what it holds, a composite it references by that entry's sites. Those sites are the library's and their ids the entry's, not the tree's, so they have no flat position of their own: each row carries owner, the referenced site's id, and the owner's flat position as its index, so a gesture on one is a gesture on the group, and a flat position still indexes all/1. A row directly under the owner names it as its parent; one deeper names the entry's site holding it. An entry met again below itself is not opened twice.

iex> pair = %{composite: %{uses: [%Visualize.Chart.Use{id: {:use, 1}, ref: {:frame, 1}}, %Visualize.Chart.Use{id: {:use, 2}, ref: nil, local: %{mark: %{}}}], vars: %{}}}
iex> fetch = fn {:composite, 1} -> {:ok, pair}; _id -> :error end
iex> uses = [%Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{style: %{}}}, %Visualize.Chart.Use{id: {:use, 2}, ref: {:composite, 1}}]
iex> for row <- Visualize.Chart.Builder.Stack.all(uses, fetch), do: {row.use.id, row.depth, row.index, row.owner}
[{{:use, 1}, 0, 0, nil}, {{:use, 2}, 0, 1, nil}, {{:use, 1}, 1, 1, {:use, 2}}, {{:use, 2}, 1, 1, {:use, 2}}]

between(uses, anchor, id)

The ids from an anchor to a sibling, in tree order — the range a Shift+click selects (spec/14 §18.8, #382) — or :error when the two are not siblings.

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> uses = for n <- 1..3, do: %Use{id: {:use, n}, ref: nil, local: %{mark: %{}}}
iex> Stack.between(uses, {:use, 3}, {:use, 1})
{:ok, [{:use, 1}, {:use, 2}, {:use, 3}]}
iex> Stack.between([%Use{id: {:use, 4}, ref: nil, local: %{composite: %{uses: uses, vars: %{}}}}], {:use, 4}, {:use, 1})
:error

body(uses, stack_vars)

@spec body([Visualize.Chart.Use.t()], map()) :: map()

The lowest tier that holds the stack (D-99): one bare inline site is its body, values and nothing else; anything more — a ref, a mask, a binding, a second site, a var — is structure.

children(use)

@spec children(Visualize.Chart.Use.t()) :: [Visualize.Chart.Use.t()]

The sites an inline group holds; [] for an object or a referenced site.

contribution(use, fetch)

@spec contribution(Visualize.Chart.Use.t(), (Visualize.Chart.Fragment.id() ->
                                         {:ok, map()} | :error)) ::
  map()

One use's contribution placed where the design keeps its kind (spec/14 §18.8, #283): resolved through the flattening fetch, as placed/2 resolves it, so a reference to a composite is what the composite flattens to; its mask ignored, since a disabled layer still declares what it declares; a missing entry its local alone; a keyless site %{}.

iex> use = %Visualize.Chart.Use{id: {:use, 1}, ref: nil, mask: :all, local: %{axis: %{scale: :x, side: :bottom}}}
iex> Visualize.Chart.Builder.Stack.contribution(use, fn _id -> :error end)
%{frames: %{main: %{axes: [%{scale: :x, side: :bottom}]}}}

copies(entry, fetch, name_of)

@spec copies(
  %{kind: atom(), name: String.t() | nil, fragment: map()},
  (Visualize.Chart.Fragment.id() -> {:ok, map()} | :error),
  (Visualize.Chart.Fragment.id() -> String.t() | nil)
) :: {[{Visualize.Chart.Use.t(), String.t()}], map(), map()}

An entry as the inline sites a copy of it is (spec/14 §18.17): a composite's sites, each holding what its use contributed — the referenced fragment (flattened, when it is a composite) stacked under the use's local, the use's mask, vars and key kept — labelled with the referenced entry's name, or the composite's own for a site it declares in place, with the composite's own vars and signals; any other body as one inline site named for the entry. The sites carry no id: the caller issues one each among the stack they join.

design(uses, vars, fetch)

@spec design([Visualize.Chart.Use.t()], map(), (Visualize.Chart.Fragment.id() ->
                                            {:ok, map()} | :error)) ::
  {:ok, map(), Visualize.Chart.Composite.report()} | {:error, term()}

The design the stack stands for: the enabled uses as a composite over the stack's own vars, flattened through the store, every id at a reference site resolved, so the frame can draw it (spec/14 §19.6).

duplicated(uses, use)

A copy of a site and what it holds, every id fresh among the tree, with the {copy, original} id pairs so labels follow (spec/14 §18.8, #382).

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> inner = [%Use{id: {:use, 2}, ref: nil, local: %{mark: %{}}}]
iex> group = %Use{id: {:use, 1}, ref: nil, local: %{composite: %{uses: inner, vars: %{}}}}
iex> {copy, pairs} = Stack.duplicated([group], group)
iex> {copy.id, Enum.map(Stack.children(copy), & &1.id), pairs}
{{:use, 3}, [{:use, 4}], [{{:use, 3}, {:use, 1}}, {{:use, 4}, {:use, 2}}]}

empty(kind)

@spec empty(atom()) :: map()

The empty, valid fragment of a kind, for new fragment of a kind (spec/14 §19.8).

enabled?(use)

@spec enabled?(Visualize.Chart.Use.t()) :: boolean()

Whether a use contributes to the design: anything but a mask of everything.

fetch(store)

@spec fetch(Visualize.Chart.Builder.Store.store() | nil) ::
  (Visualize.Chart.Fragment.id() ->
     {:ok, map()} | :error)

The fetch a store gives resolution: an id to {:ok, fragment} or :error.

get(uses, id)

The use with an id anywhere in the tree, or nil.

group()

@spec group() :: map()

A group's local: an inline composite with no objects.

group?(use)

@spec group?(Visualize.Chart.Use.t()) :: boolean()

Whether a use is a container: an inline composite — a layer or a group.

grouped(uses, ids, group)

@spec grouped(
  [Visualize.Chart.Use.t()],
  [Visualize.Chart.Use.id()],
  Visualize.Chart.Use.t()
) ::
  {:ok, [Visualize.Chart.Use.t()]} | :error

Sibling sites wrapped in a group at the first one's position, in their tree order (spec/14 §18.8, #382); :error when the ids are not siblings, or none.

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> uses = for n <- 1..3, do: %Use{id: {:use, n}, ref: nil, local: %{mark: %{}}}
iex> group = %{Use.new(nil, uses) | local: Stack.group()}
iex> {:ok, grouped} = Stack.grouped(uses, [{:use, 3}, {:use, 2}], group)
iex> Enum.map(grouped, & &1.id)
[{:use, 1}, {:use, 4}]
iex> grouped |> List.last() |> Stack.children() |> Enum.map(& &1.id)
[{:use, 2}, {:use, 3}]

index_of(uses, id)

@spec index_of([Visualize.Chart.Use.t()], Visualize.Chart.Use.id() | nil) ::
  non_neg_integer() | nil

The flat position of a use in the tree, or nil.

inner_label(use, name_of, own)

@spec inner_label(
  Visualize.Chart.Use.t(),
  (Visualize.Chart.Fragment.id() -> String.t() | nil),
  String.t()
) :: String.t()

The name a site of a composite is shown by (spec/14 §18.8, §18.17): a referenced site by its entry's name — its id when the store has none — and a site the composite declares in place by its kind (#380), or by own, the composite's name, when it declares several. A copy's sites are labelled so, and so are a referenced group's rows.

iex> name_of = fn {:style, 1} -> "Styles::thick"; _id -> nil end
iex> Visualize.Chart.Builder.Stack.inner_label(%Visualize.Chart.Use{id: {:use, 1}, ref: {:style, 1}}, name_of, "pair")
"Styles::thick"
iex> Visualize.Chart.Builder.Stack.inner_label(%Visualize.Chart.Use{id: {:use, 1}, ref: {:frame, 9}}, name_of, "pair")
"frame 9"
iex> Visualize.Chart.Builder.Stack.inner_label(%Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{mark: %{type: :line}}}, name_of, "pair")
"mark"
iex> Visualize.Chart.Builder.Stack.inner_label(%Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{version: 2}}, name_of, "pair")
"pair"

insert(uses, after_id, use)

A use inserted as the sibling after another — at the top, at the end, for nil — or, with {:into, id}, appended inside that group.

insert_at(uses, at, use)

A use inserted before the row at a flat position, in that row's own container — at the end of the top past the last row (spec/14 §18.17, #380).

inside(use, fetch)

@spec inside(Visualize.Chart.Use.t(), (Visualize.Chart.Fragment.id() ->
                                   {:ok, map()} | :error)) ::
  [Visualize.Chart.Use.t()] | nil

The sites a referenced composite's entry holds (spec/14 §18.8, #388) — the library's, read-only in the chart — or nil for a use that is not a reference to a composite or whose entry the fetch cannot find.

kinds()

@spec kinds() :: [atom(), ...]

The kinds new fragment of a kind offers, the composite first and never a design: a design is the deployed tier, and a composite deploys to one (spec/14 §19.6, §19.8).

label(labels, use)

@spec label(labels(), Visualize.Chart.Use.t()) :: String.t()

The name a person sees for a use: its label, or its id when it has none.

lifted(mode, use, fetch, node, taken)

@spec lifted(
  :copy | :override,
  Visualize.Chart.Use.t(),
  (Visualize.Chart.Fragment.id() -> {:ok, map()} | :error),
  map(),
  [atom()]
) ::
  %{
    local: map(),
    key: atom() | nil,
    mask: [atom()] | nil,
    lifted: pos_integer()
  }
  | :error

The site that copies or overrides a node the use contributes (spec/14 §18.16, #379): %{local, key, mask, lifted} — the inline site's local and key, the mask path to add to the owner or nil, and how many members were lifted — or :error when the use does not contribute the node. The node carries its body as the design composes it, since a list member's index in the design is not its index in the owner's list. A :copy takes a fresh id or key, avoiding taken; an :override keeps the node's identity and, for a node with none — a label, a mark with no id — lifts the owner's whole list and masks it at the finest path the owner's contribution has for it.

iex> use = %Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{frames: %{main: %{kind: :cartesian, axes: [%{scale: :x, side: :bottom}, %{scale: :y, side: :left}]}}}}
iex> node = %{kind: :axis, path: [:frames, :main, :axes, 1], label: "frames.main.axes[1]", body: %{scale: :y, side: :left}}
iex> Visualize.Chart.Builder.Stack.lifted(:copy, use, fn _ -> :error end, node, [])
%{local: %{axis: %{scale: :y, side: :left}}, key: nil, mask: nil, lifted: 1}
iex> style = %Visualize.Chart.Use{id: {:use, 2}, ref: nil, key: :series, local: %{style: %{stroke: "#111"}}}
iex> Visualize.Chart.Builder.Stack.lifted(:copy, style, fn _ -> :error end, %{kind: :style, path: [:styles, :series], label: "styles.series"}, [:series, :series_2])
%{local: %{style: %{stroke: "#111"}}, key: :series_3, mask: nil, lifted: 1}
iex> labels = %Visualize.Chart.Use{id: {:use, 3}, ref: nil, local: %{labels: [%{anchor: :title, text: ["a"]}, %{anchor: :caption, text: ["b"]}]}}
iex> Visualize.Chart.Builder.Stack.lifted(:override, labels, fn _ -> :error end, %{kind: :label, path: [:labels, 1], label: "labels[1]", body: %{anchor: :caption, text: ["b"]}}, [])
%{local: %{labels: [%{anchor: :title, text: ["a"]}, %{anchor: :caption, text: ["b"]}]}, key: nil, mask: [:labels], lifted: 2}

linkage(use)

@spec linkage(Visualize.Chart.Use.t()) :: :inline | :linked | :edited | :unlocked

A use's state toward the library (spec/14 §18.8, §19.3, #381): :inline with no ref and no origin, :linked with a ref and no local, :edited with a ref over a local — locked again after edits — and :unlocked with an origin and no ref.

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> Stack.linkage(Use.new({:style, 3}))
:linked
iex> Stack.linkage(%{Use.new({:style, 3}) | local: %{stroke: "#111"}})
:edited
iex> Stack.linkage(%{Use.new(nil) | origin: {:style, 3}})
:unlocked
iex> Stack.linkage(Use.new(nil))
:inline

local_path(use, fetch, path)

@spec local_path(
  Visualize.Chart.Use.t(),
  (Visualize.Chart.Fragment.id() -> {:ok, map()} | :error),
  [
    term()
  ]
) :: [term()]

The path a design path has inside a use: a site of one kind holds its body under the kind's key (spec/14 §19.3), so the design's sources.primary is a source site's [:source], and a design-level site's path is its own (§18.15).

iex> use = %Visualize.Chart.Use{id: {:use, 1}, ref: nil, key: :primary, local: %{source: %{fields: [:t]}}}
iex> Visualize.Chart.Builder.Stack.local_path(use, fn _id -> :error end, [:sources, :primary])
[:source]
iex> Visualize.Chart.Builder.Stack.local_path(use, fn _id -> :error end, [:sources, :primary, :fields])
[:source, :fields]
iex> mark = %Visualize.Chart.Use{id: {:use, 2}, ref: nil, local: %{mark: %{type: :line}}}
iex> Visualize.Chart.Builder.Stack.local_path(mark, fn _id -> :error end, [:marks, 3, :channels])
[:mark, :channels]
iex> house = %Visualize.Chart.Use{id: {:use, 3}, ref: nil, local: %{frames: %{main: %{kind: :cartesian}}}}
iex> Visualize.Chart.Builder.Stack.local_path(house, fn _id -> :error end, [:frames, :main, :margin])
[:frames, :main, :margin]

locked(use)

A use with an origin linked to it again, its local kept over the link (spec/14 §18.8, #381); a use with no origin has nothing to link to and is unchanged.

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> use = %{Use.new(nil) | origin: {:style, 3}, local: %{stroke: "#111"}}
iex> Stack.locked(use) |> Map.take([:ref, :origin, :local])
%{ref: {:style, 3}, origin: nil, local: %{stroke: "#111"}}

locked?(use)

@spec locked?(Visualize.Chart.Use.t()) :: boolean()

Whether a use is the library's word — it references an entry — and so is locked in the chart: read-only, edited by opening the entry (spec/14 §18.8, §18.15).

iex> Visualize.Chart.Builder.Stack.locked?(Visualize.Chart.Use.new({:style, 3}))
true
iex> Visualize.Chart.Builder.Stack.locked?(Visualize.Chart.Use.new(nil))
false

move(uses, from, to)

A move within the tree by flat positions (§19.3, #380): the row at from — a group with everything it holds — taken out, then put before the row that stands at to once it is out, in that row's own container; past the last row, at the end of the top. A row cannot land inside what it holds, since those rows are out with it.

open(map, name_of)

@spec open(
  %{kind: atom(), name: String.t() | nil, fragment: map()},
  (Visualize.Chart.Fragment.id() ->
     String.t() | nil)
) ::
  {[Visualize.Chart.Use.t()], labels(), map(), map()}

An entry as a stack (spec/14 §19.9): a composite's sites, labelled by the store's names, with its vars and its signals (§19.10); any other body as one inline site named for the entry.

parent_of(uses, id)

The id of the group holding a use, or nil at the top.

placed(uses, fetch)

@spec placed([Visualize.Chart.Use.t()], (Visualize.Chart.Fragment.id() ->
                                     {:ok, map()} | :error)) :: [
  {Visualize.Chart.Use.id(), map()}
]

The enabled uses as the layers the design is stacked from — {id, fragment} in stack order, each resolved and placed where the design keeps its kind (spec/14 §18.15, §19.6) — so Visualize.Chart.explain/1 over them says which use gave every value. A name-keyed site with no key has nowhere to be placed and is left out, as design/3 leaves it out.

iex> uses = [
...>   %Visualize.Chart.Use{id: {:use, 1}, ref: nil, local: %{frames: %{main: %{kind: :cartesian}}}},
...>   %Visualize.Chart.Use{id: {:use, 2}, ref: nil, key: :primary, local: %{source: %{fields: [:t]}}},
...>   %Visualize.Chart.Use{id: {:use, 3}, ref: nil, mask: :all, local: %{theme: :dark}}
...> ]
iex> Visualize.Chart.Builder.Stack.placed(uses, fn _id -> :error end)
[{{:use, 1}, %{frames: %{main: %{kind: :cartesian}}}}, {{:use, 2}, %{sources: %{primary: %{fields: [:t]}}}}]

remove(uses, id)

The tree without a use (and what it holds); a missing id changes nothing.

resolve(uses, labels, fetch)

Every use resolved to a layer the panels can read — {label, fragment} — and the refs no store could find (spec/14 §19.2, §19.3). A disabled use resolves as it would enabled: the panels edit what a layer says whether or not the design is drawing it, and its mask of everything has its whole effect in design/2.

reverted(use)

A use as the library has it (spec/14 §18.8, #381): its local dropped, its ref restored from its origin where it was unlocked, mask, vars and key kept — they are the site's.

iex> alias Visualize.Chart.{Builder.Stack, Use}
iex> use = %{Use.new(nil) | origin: {:style, 3}, local: %{stroke: "#111"}, mask: [:fill]}
iex> Stack.reverted(use) |> Map.take([:ref, :origin, :local, :mask])
%{ref: {:style, 3}, origin: nil, local: %{}, mask: [:fill]}

seed(host_layers)

@spec seed([term()]) :: {[Visualize.Chart.Use.t()], labels()}

A host's layers as use sites, with their labels (spec/14 §19.9).

A {name, layer} pair keeps its name; a bare layer is named by its position. A fragment — a map, or a %Visualize.Chart{} as the map it is — becomes an inline-only use; an id {kind, n} becomes a use referring to it, so a host may start the builder over entries of its own library.

sites(uses, labels, fetch)

@spec sites([Visualize.Chart.Use.t()], labels(), (Visualize.Chart.Fragment.id() ->
                                              {:ok, map()} | :error)) :: [
  %{
    index: non_neg_integer(),
    id: Visualize.Chart.Use.id(),
    label: String.t(),
    free: [Visualize.Chart.free_var()],
    vars: map()
  }
]

Each site that carries variables: the free variables of its fragment before the site's bindings — the names the fragment itself uses — and what the site binds them to, for the Variables tab's per-site rows (spec/14 §19.4, §19.10).

structure(uses, vars)

@spec structure([Visualize.Chart.Use.t()], map()) :: map()

The stack as a composite: what saving sends and a composite stores (spec/14 §19.6).

structure(uses, vars, signals)

@spec structure(list(), map(), map()) :: map()

As structure/2, with the chart's signals when it has any (spec/14 §19.10).

toggle(use)

A use with its enabled state flipped: disabling is a mask of everything (§19.3).

ungrouped(uses, id)

An inline group's sites spliced in at its position, the group gone (spec/14 §18.8, #382); any other site leaves the tree unchanged.

unlocked(use, entry, fetch, name_of)

@spec unlocked(
  Visualize.Chart.Use.t(),
  %{kind: atom(), name: String.t() | nil, fragment: map()},
  (Visualize.Chart.Fragment.id() -> {:ok, map()} | :error),
  (Visualize.Chart.Fragment.id() -> String.t() | nil)
) :: {Visualize.Chart.Use.t(), [{Visualize.Chart.Use.t(), String.t()}], map()}

A use unlocked from its entry (spec/14 §18.8, #381): the ref moved to origin and the entry's body copied into local under whatever the site already held there; mask, vars and key stay the site's, so the chart draws as it did. A composite entry unlocks to a group of copies of its sites, as copies/3 reads them, the composite's vars the group's; the inner sites come with nil ids for the caller to issue, and their labels and the entry's signals are returned beside the use.

unmasked(use, fetch)

@spec unmasked(Visualize.Chart.Use.t(), (Visualize.Chart.Fragment.id() ->
                                     {:ok, map()} | :error)) ::
  map()

What a site contributes before its mask — the referenced body, or nothing, under its local — so the context panel can offer every key the mask could remove (spec/14 §19.5).

update(uses, id, fun)

The tree with one use, anywhere, replaced by a function of it; a missing id changes nothing.

write(use, path, key, parsed)

@spec write(Visualize.Chart.Use.t(), [term()], atom(), {:ok, term()} | :error) ::
  Visualize.Chart.Use.t()

A write into a use's local at a path (spec/14 §19.3): a {:ok, value} sets the key, :error removes it. A container the local does not carry yet is an empty node on the way (§18.5).

write_at(fragment, path, key, arg4)

@spec write_at(map(), [term()], atom(), {:ok, term()} | :error) :: map()

A write into any fragment at a path, as write/4 does into a use's local.