MishkaCmsCoreWeb.Live.Admin.Components.ContentComponentPicker (Mishka CMS Core v0.0.1)

Copy Markdown View Source

The server half of the content editor MishkaCmsCoreWeb.UIAdapters.Admin.textarea/1 draws for a :textarea field with ui do extra %{content_editor: true} end: the palette an author places from, the settings of a placed component, and the answers the editor's hook asks for. See MishkaCmsCore.Runtime.ContentComponents for the marker and how a page draws it.

The adapter mounts it inside the editor, so a resource adds nothing else to its form.

The palette

Two tabs: Blocks — whole ready-made pieces, MishkaCmsCore.Runtime.ContentBlocks.available/3 — and Components, every component the site may place (ContentComponents.palette/2) under its heading, text first (ContentComponents.category/2). One search box searches the tab that is open; the headings filter the components. It is read in the background as the signed-in user the first time it is opened. A master's record is on the site its Site field names, else the site of the record being edited; a site user's is on their own (site/1).

The settings

A component being placed or edited opens its settings in a drawer at the side of the page — its attributes, the text and components inside it, and its slot entries, each of those with its own, put in another order with their arrows or dragged there by their grips — through a portal, so the drawer's form is a form of its own and not part of the record's. Every change is written straight back into the component in the content, as the author types, and the drawer is closed with Done, a click outside it, or Escape. A picture or a video is chosen from the site's media library, or uploaded to it, in MishkaCmsCoreWeb.Live.Admin.Components.ContentMediaChooser, which opens over the settings and leaves them as they were — scrolled to the field it was opened for, with the same parts open.

What the hook asks

The editor's surface carries this component's DOM id as data-components, and the hook pushes these to it:

  • "segments", %{"html" => html} — answered %{segments: [...], styles: href | nil}: html as ContentComponents.segments/1 splits it, %{type: "html", html: html} and %{type: "component", name: name, marker: marker}.

  • "build", %{"name" => name, "attrs" => %{}, "inner" => html} — answered %{name: name, marker: marker} for a component the palette lists, with only the attributes it declares and an empty entry for each slot it requires, and the palette closes; or %{error: message}, and the palette stays open and says why.
  • "block", %{"id" => id} — a ready-made block's marker, answered as "build" is.
  • "nest", %{"marker" => marker, "name" => name} or %{"marker" => marker, "block" => id} — the component marker names with the component name, or the ready-made block id, put inside it: beside its part "beside" ("r-2"), after it when "after" is true, or at the end of what it holds. Answered as "build" is; %{error: message} when marker holds nothing or either is not listed.
  • "reorder", %{"marker" => marker, "path" => path, "beside" => path, "after" => boolean} — the component marker names with its part at path moved before, or after, its sibling at beside, answered as "build" is.
  • "edit", %{"marker" => marker, "serial" => n} — opens the settings of the component marker names, filled in from it. Answered %{ok: true}, or %{error: message} when the palette does not list it. n comes back on what those settings write.
  • "close" — closes the palette, and "settings_done" the settings.

"nest" and "reorder" carry the "serial" of the settings open on that component, if they are: the settings then take the change, answer %{ok: true} and write it back as applied.

And this pushes "mishka:content_editor" to the page, %{id: editor_id, ...} with:

  • styles: href — the stylesheet previews are drawn with, once it is built (MishkaCmsCore.Runtime.ContentEditorStyles); "segments" answers it too, when known.
  • holders: [name] — the components the palette lists that take others inside them, once the palette is read.
  • previews: [%{marker: marker, preview: html}] — a component's preview, drawn in the background after a marker is answered or written, each in a task of its own; it runs the component's own code. A component that raises as it is drawn is pushed as a notice saying so — the page would fail on it too. The parts it holds are marked in it (marked/1).
  • applied: %{name: name, marker: marker}, serial: n — the component being edited, as the settings now write it.
  • done: true, serial: n — the settings closed.

Every name is checked as a string: against the names the palette lists before a marker is written, and against the components the site may draw (ContentComponents.draw/2) before a preview is. No name a request carries is made into an atom.

Summary

Functions

The site the record a Gervaz form state edits is on: for a master, the one its Site field names, else the record's own; for a site user, their own. nil is the global components alone.

Functions

site(state)

@spec site(map() | nil) :: String.t() | nil

The site the record a Gervaz form state edits is on: for a master, the one its Site field names, else the record's own; for a site user, their own. nil is the global components alone.