The plain HTML an author reaches for constantly — a section, a column grid, a heading, a list.
Why this exists
The editor had two ways to put something on a page and neither of them was "an empty box". The
palette lists a UI kit's components; MishkaCmsCore.Runtime.PageBuilder.Blocks lists whole
finished sections. To get a <section> of your own you had to drop a Hero and delete everything
inside it, or open the Code window and type. Every other builder — Elementor's Layout group,
Gantry's structural particles — puts the primitives first, because they are what a page is
actually made of; the finished sections are the shortcut, not the foundation.
What an element is
The same %{id, title, category, description, template} shape a block has, so one panel lists
both and one search covers both. The difference is intent: a block arrives finished and is edited
down, an element arrives empty and is built up.
Plain HTML and Tailwind utilities only, for the same reason blocks are: an element that reached
for a kit component would fail on an install without that kit, and every node here has to be
something the Inspector already knows how to style. <.form> and <.input> are inside that rule
rather than outside it: they are Phoenix.Component.form/1 and
MishkaCmsCore.CoreComponents.input/1, imported into every compiled page and resolved by the
editing canvas, so they are there on every install — the standing <.link> already has.
What the Forms elements are
A form is a real Phoenix form: for={assigns[:form_form]} names the page assign that holds what
the visitor typed, as={:form} scopes the params, and the two phx-* names are the handlers
MishkaCmsCore.Runtime.Compilers.LiveViewCompiler generates from the markup. All five names come
from one seed, minted and renumbered by
MishkaCmsCore.Runtime.PageBuilder.PageForms.Naming, so a second form on the page cannot share the
first one's assign. el-form therefore ships with a field and a button in it rather than empty:
the five attributes are the point of it, and an empty one teaches none of them.
The bare controls — el-input, el-textarea, el-select, el-checkbox, el-radio-group — keep
a literal name, because a palette card and a hover preview render them on their own, outside any
form. MishkaCmsCore.Runtime.PageBuilder.PageForms.FormPlacement rewrites that name into a binding
on the way in, and wraps the control in a form first when it is dropped where there is none.
Two rules that decide whether these are usable at all
Containers ship empty, and the canvas draws them anyway. An empty <section> is zero pixels
tall, so without help it cannot be clicked, cannot be dropped into, and looks like the drop
failed. MishkaCmsCoreWeb.Live.Admin.Builder.CanvasLive gives any childless container a dashed
outline and a minimum height while editing — never in preview, and never for a void tag like
<img>, which is childless on purpose.
Text elements ship with words in them. An empty <p> has the same problem and no good answer:
a paragraph is its text. They arrive saying what they are, and the first thing an author does is
type over it — which is exactly what the inline text editor is for.
Summary
Functions
Every shipped element, in the order the panel lists them.
The categories the section's elements fall into, in order.
Every element the given section can use, in the order the panel lists them.
One element by id, or nil.
Elements matching a search string, nil or "" meaning all of them.
Types
Functions
@spec all() :: [element()]
Every shipped element, in the order the panel lists them.
The whole catalogue, whatever section it belongs to — which is what a check over "every element"
has to mean. Use for_section/1 to ask what a particular document may place.
The categories the section's elements fall into, in order.
Every element the given section can use, in the order the panel lists them.
MOST ELEMENTS BELONG EVERYWHERE and say nothing; a <section> is a <section> in all four
documents. The ones that declare sections are the exception, and they are exceptions for a
reason a reader can check: {@inner_content} names an assign only a LAYOUT is handed, so offering
it on a page would be offering a document that will not compile.
A section has to be NAMED. It used to be an optional argument defaulting to nil, and nil meant
"only the ones that belong everywhere" — so the catalogue-wide checks, which pass no section,
quietly stopped covering the three elements that declare one.
One element by id, or nil.
Unfiltered on purpose: this answers "what is el-inner-content", which a drop that has already
happened needs regardless of which panel offered it.
Elements matching a search string, nil or "" meaning all of them.
Title and description are both searched, so "column" finds the grids and "sidebar" finds aside
— an author looking for a thing rarely knows what the tag is called.