ElementorexCms is a set of pieces you place where they fit: the editor in your admin, documents in your schemas and the renderer in your pages.
Where documents live
Any schema can have one or more documents:
field :body, ElementorexCms.Ecto.Document, default: ElementorexCms.Document.new()The type normalizes on cast, so a document that arrives from a form, an import or the editor is valid and sanitized before it is stored.
Saving from the editor
The editor never writes to your database. Save sends
{ElementorexCms.Editor, id, {:save, document}} to the LiveView that hosts it;
persist it however your app does (authorization, versions, audit) and answer
with ElementorexCms.Editor.saved(id) or save_failed(id, message). With
notify_changes you also get {:change, document} on every edit, e.g. for
autosave.
Showing documents inside your own chrome
The canvas can render the real context of the content: pass your stylesheets
and a frame with data-exr-mount where the document goes.
def canvas(event) do
ElementorexCms.Canvas.new(
stylesheets: [~p"/assets/css/app.css"],
frame: modal_frame(%{title: event.title})
)
end
defp modal_frame(assigns) do
~H"""
<div class="grid min-h-screen place-items-center bg-black/60 p-10">
<div class="w-full max-w-lg rounded-box bg-base-100 p-8">
<h2 class="text-2xl font-bold">{@title}</h2>
<div data-exr-mount></div>
</div>
</div>
"""
endThe frame can be a string or anything Phoenix.HTML.Safe, like the rendered
component above. On your site, render the same wrapper and put
<ElementorexCms.Components.document> inside it.
Images
Implement ElementorexCms.Media (store/2 and optionally list/2) and pass
media={MyApp.EditorMedia}: image fields get Upload (LiveView uploads) and
Library. Without it they take a URL.
Fewer blocks for a given place
registry = ElementorexCms.Registry.new(only: ~w(heading text button image alert list))Pass the same registry to the editor and to <ElementorexCms.Components.document>.
Data for blocks
Blocks receive @ctx.data, set with data={%{brand: @brand}} in the editor and
the document component, for blocks that depend on the page (a WhatsApp button
with the number of the current brand, prices of the current product…).
Development with a path dependency
While developing the library next to your app ({:elementorex_cms, path: "../elementorex_cms"}):
- add
reloadable_apps: [:my_app, :elementorex_cms]to your endpoint config indev.exsso the code reloader recompiles it; - point esbuild's
NODE_PATHto the folder that contains the library, and the Tailwind@sourceto../../../elementorex_cms/lib(relative toassets/css). Switch both back todeps/when it comes from Hex.