The builder's library: the store's fragments as a tree (spec/14 §18.16).
A Visualize.Chart.Builder.Store is a flat map of name to fragment (§18.3), and a
library of any size is unusable as a dropdown. The tree groups by what the name
carries:
<category>:<sub-category>:<fragment name>Frame:axes:dense ticks is dense ticks, under axes, under Frame. Nothing is
classified anywhere else — no metadata beside the fragment, nothing derived from the keys
it happens to set — so a category exists because some item names it, and inventing one is
typing it (D-97).
The parse is total and forgiving, because a store populated before this existed must keep
working: two parts is a category and a name, one part is a name under Uncategorised, and
an empty sub-category and the literal none are the same absent thing.
This module compiles only when Phoenix.Component is loaded (spec/10 §1.1, D-45, D-85).
Summary
Functions
Entries as the kind tree (spec/14 §19.8): one group per kind, the kinds a library item is usually one of always present, empty or not, and any other kind when an entry has it. Items within a kind sort by name; each carries its group as a second line.
The categories a set of stored names names, in name order.
A category, sub-category and name as the name to store it under (spec/14 §18.16).
Renders the library (spec/10 §15.3).
A stored name as {category, sub_category, name} (spec/14 §18.16).
The sub-categories one category has, in name order, without the unnamed group.
Stored names as the tree the panel draws (spec/14 §18.16).
Types
Functions
Entries as the kind tree (spec/14 §19.8): one group per kind, the kinds a library item is usually one of always present, empty or not, and any other kind when an entry has it. Items within a kind sort by name; each carries its group as a second line.
iex> entries = [
...> %{id: {:style, 1}, name: "Styles::thick", kind: :style},
...> %{id: {:size, 1}, name: "Frame:size:wide", kind: :size}
...> ]
iex> tree = Visualize.Chart.Builder.Library.by_kind(entries)
iex> Enum.map(tree, &{&1.kind, length(&1.items)})
[composite: 0, theme: 0, style: 1, frame: 0, scale: 0, axis: 0, source: 0, var: 0, mark: 0, label: 0, size: 1]
The categories a set of stored names names, in name order.
iex> Visualize.Chart.Builder.Library.categories(["Theme::dark", "Frame::wide"])
["Frame", "Theme"]
A category, sub-category and name as the name to store it under (spec/14 §18.16).
The inverse of parse/1 for every name this builds; nil, "" and "none" are all
written as the empty sub-category, so one absent sub-category has one spelling on disk.
iex> Visualize.Chart.Builder.Library.name("Frame", "axes", "dense ticks")
"Frame:axes:dense ticks"
iex> Visualize.Chart.Builder.Library.name("Styles", nil, "thick line")
"Styles::thick line"
iex> Visualize.Chart.Builder.Library.name("Styles", "none", "thick line")
"Styles::thick line"
Renders the library (spec/10 §15.3).
An <aside class="vis-builder-library-pane"> holding one section per category, each with
one list per sub-category, each item draggable by its stored name. Every top-level
header is a button that folds its section (#355): folded, the header stays with the
count of what it hides and the items are not rendered.
Attributes
entries(:list) (required) - the store's entries, asStore.list/1returns them.view(:atom) -:group, the default, or:kind(spec/14 §19.8). Defaults to:group.open(:any) - the id last acted on, marked in the tree. Defaults tonil.folded(:any) - the top-level headers folded, by name (spec/14 §18.16, #355). Defaults toMapSet.new([]).target(:any) - thephx-targetof the library's events. Defaults tonil.id(:string) (required) - the hook element's id.class(:string) - appended to the library's class. Defaults tonil.
A stored name as {category, sub_category, name} (spec/14 §18.16).
The split takes the first two separators, so a fragment's own name may contain one.
An absent sub-category is nil, which both an empty part and the literal none mean.
iex> Visualize.Chart.Builder.Library.parse("Frame:axes:dense ticks")
{"Frame", "axes", "dense ticks"}
iex> Visualize.Chart.Builder.Library.parse("Styles::thick line")
{"Styles", nil, "thick line"}
iex> Visualize.Chart.Builder.Library.parse("Theme:none:dark")
{"Theme", nil, "dark"}
iex> Visualize.Chart.Builder.Library.parse("Frame:dense ticks")
{"Frame", nil, "dense ticks"}
iex> Visualize.Chart.Builder.Library.parse("dark")
{"Uncategorised", nil, "dark"}
iex> Visualize.Chart.Builder.Library.parse("Frame:aspect:2:1 aspect")
{"Frame", "aspect", "2:1 aspect"}
The sub-categories one category has, in name order, without the unnamed group.
iex> names = ["Frame:axes:dense", "Frame::wide", "Theme:mood:dark"]
iex> Visualize.Chart.Builder.Library.sub_categories(names, "Frame")
["axes"]
Stored names as the tree the panel draws (spec/14 §18.16).
One entry per category in name order; within it one group per sub-category, the unnamed group first and the rest in name order, because a fragment sitting directly under its category is the general case and the sub-categories refine it.
iex> entries = [
...> %{id: {:theme, 32}, name: "Theme::dark", kind: :theme},
...> %{id: {:axis, 2}, name: "Frame:axes:dense", kind: :axis},
...> %{id: {:size, 1}, name: "Frame::wide", kind: :size}
...> ]
iex> Visualize.Chart.Builder.Library.tree(entries)
[
%{category: "Frame", groups: [
%{sub_category: nil, items: [%{id: {:size, 1}, stored: "Frame::wide", name: "wide", kind: :size}]},
%{sub_category: "axes", items: [%{id: {:axis, 2}, stored: "Frame:axes:dense", name: "dense", kind: :axis}]}
]},
%{category: "Theme", groups: [
%{sub_category: nil, items: [%{id: {:theme, 32}, stored: "Theme::dark", name: "dark", kind: :theme}]}
]}
]