Pure view of a mishka.ui_kit.bundle.v3 for the installer's component picker: groups the
components by type (one group per category however a kit spelled it, see file_categories/2),
keeps only the light fields the picker needs (name / label / deps — never
the heavy body/template/examples), and computes the transitive COMPONENT-dependency closure of a
selection so required components can be locked.
The bundle JSON has no "required" flag and UiKitHandler does not resolve dependencies itself, so
the picker must derive "required" = the closure of the user's picks. Picking chelekom-card pulls
in chelekom-icon (depended on by ~80 components), which the user then cannot unmark. *.js hook
deps are ignored here — js_hooks and the stylesheet install in full regardless of the component
selection — and the chelekom-scroll_area (underscore) vs chelekom-scroll-area (hyphen) naming
mismatch is normalized.
Per-component narrowing is offered along the axes variant/color/size/padding/space/rounded/border
(in that display order). An axis is shown for a component only when the kit enumerates string values
for it AND declares a string default — components whose axis (e.g. size) is numeric simply skip it.
The axis name is used verbatim as the :selection per-component key, which UiKitHandler accepts;
the picker pre-checks and locks the kit-declared default. Components install minimal by default
(their own default value), and config_extras widens chosen axes to [default | extras].
See MishkaCmsCore.Runtime.Handlers.UiKitHandler and MishkaCmsWeb.InstallLive.
Summary
Functions
The order the installer shows configurable axes in, most-recognised first.
Builds the picker catalog (groups + name index + dependency graph) from a parsed v3 bundle.
One category's spelling with case, spaces, hyphens and underscores folded: Data-Display,
data_display and data display are all data display. A component with no category is
general, as the installer has always filed it.
Files each category under one spelling, decided over all of them at once: a map from every category as a kit wrote it to the key it is filed under.
Builds the :selection map for UiKitHandler.install_chelekom!/1 from the picked components and the
per-component variant config.
The same selection, split per kit — what UiKitHandler.install_kits!/2 takes.
The full set of components to install: the user's picks plus their required closure.
A kit's own name, as a person would read it: chelekom_headless -> "Chelekom Headless".
Strips the kit prefix for display (chelekom-card -> card).
The same, told which kit shipped the component.
The components the kit declares the CMS cannot run without, whatever the operator picked.
One catalogue over several bundles, for an installer that offers more than one kit.
The selected components that force name into the required set (for a 'required by …' hint).
The transitive component-dependency closure of selected that the user did NOT explicitly pick —
i.e. the components that must be installed and locked in the UI. install_set/2 adds the picks back.
Sorts axes by axis_order/0, with anything unlisted after, then alphabetically.
Types
Functions
@spec axis_order() :: [String.t()]
The order the installer shows configurable axes in, most-recognised first.
Kept as a SORT KEY rather than a filter — see config_features/1. An axis this list has never
heard of sorts after the ones it has, which is the only thing the order was ever for. Public so
the installer reads it here instead of holding a second copy.
Builds the picker catalog (groups + name index + dependency graph) from a parsed v3 bundle.
One category's spelling with case, spaces, hyphens and underscores folded: Data-Display,
data_display and data display are all data display. A component with no category is
general, as the installer has always filed it.
Files each category under one spelling, decided over all of them at once: a map from every category as a kit wrote it to the key it is filed under.
KITS DO NOT AGREE ON THE WORDS. Chelekom's headless kit wrote data display on eight components
and data-display on four, and navigation where its styled kit writes navigations, so a
picker or a palette that grouped by the word as written drew each category twice, each heading
holding half of it.
Case, spaces, hyphens and underscores are one spelling (category_key/1), and a category that
sits beside its own plural is filed under the plural, the form most kits use. known are
spellings that count as present whether or not a kit wrote them — the installer passes its own
group order, so a kit that only ever says navigation still lands in the picker's navigations.
iex> MishkaCmsCore.Install.UiKitCatalog.file_categories(["data-display", "Data display"])
%{"data-display" => "data display", "Data display" => "data display"}
iex> MishkaCmsCore.Install.UiKitCatalog.file_categories(["navigation", "navigations"])
%{"navigation" => "navigations", "navigations" => "navigations"}
Builds the :selection map for UiKitHandler.install_chelekom!/1 from the picked components and the
per-component variant config.
Every component installs minimal by default — narrowed to its OWN default variant/color (the
kit's defaults are not uniform: color defaults to natural for ~half the components, base for
others — so a global "base only" would render those bare). config_extras (%{component_name => %{axis => [extra_values]}}) widens individual components: e.g. adding danger to button's color
installs that component with [natural, danger]. Axes without a usable default, or with no extras
on components that have no enumerable values, simply install full.
The same selection, split per kit — what UiKitHandler.install_kits!/2 takes.
With one kit installed this is the single-kit map under one key, and behaves exactly as before.
With two, each kit gets only its own components: the labels a selection is keyed by (alert,
tree) are unique per kit but not across kits, so a flat map would install one kit's narrowing
onto another kit's component of the same name.
The full set of components to install: the user's picks plus their required closure.
A kit's own name, as a person would read it: chelekom_headless -> "Chelekom Headless".
Used by the installer's kit chips and the badge beside a component, both of which only appear once a second kit is on disk.
Strips the kit prefix for display (chelekom-card -> card).
The same, told which kit shipped the component.
The kit's own name is the prefix, and Component SLUGIFIES what it stores — a bundle named
chelekom_headless installs chelekom-headless-tree, so stripping at the first hyphen would
leave "headless-tree". Given the kit, the whole prefix comes off and the label is "tree".
The components the kit declares the CMS cannot run without, whatever the operator picked.
deps answers "this component needs that one"; required answers "this install needs it at all".
An icon is drawn by half of Chelekom's other components and nothing in the markup says so, so
deselecting it left the rest unable to render — and the picker had no way to know.
Seeded into the dependency closure rather than checked separately, so it is locked in the UI and installed by the same path a dependency is.
One catalogue over several bundles, for an installer that offers more than one kit.
Entries keep their FULL component name, which is unique across kits, so the picker's selection set and dependency closure keep working unchanged; only the labels and the install selection need to know which kit an entry came from.
The selected components that force name into the required set (for a 'required by …' hint).
The transitive component-dependency closure of selected that the user did NOT explicitly pick —
i.e. the components that must be installed and locked in the UI. install_set/2 adds the picks back.
Sorts axes by axis_order/0, with anything unlisted after, then alphabetically.