Leaf (Leaf v0.2.23)

Copy Markdown View Source

Dual-mode content editor LiveComponent with visual (WYSIWYG) and markdown modes.

Visual mode uses a contenteditable div with vanilla JS (no npm dependencies). Markdown mode uses a plain textarea with toolbar support. Content syncs between modes using Earmark (markdown→HTML) and client-side HTML→markdown conversion.

Usage

import Leaf, only: [leaf_editor: 1]

<.leaf_editor
  id="my-editor"
  content={@content}
  mode={:visual}
  preset={:advanced}
  toolbar={[:image, :video]}
  placeholder="Write something..."
  readonly={false}
  height="480px"
  debounce={400}
/>

Presets

  • :advanced (default) — Full toolbar with all formatting options
  • :simple — Compact toolbar for comments/lightweight editing: undo/redo, bold, italic, strikethrough, inline code, lists, link, emoji, clear formatting

Messages Sent to Parent

  • {:leaf_changed, %{editor_id, markdown, html}} — Content updated
  • {:leaf_insert_request, %{editor_id, type: :image | :video}} — Insert requested

  • {:leaf_mode_changed, %{editor_id, mode: :visual | :markdown}} — Mode switched

Commands from Parent

Use send_update/2:

send_update(Leaf, id: "my-editor", action: :insert_image, url: "https://...", alt: "description")
send_update(Leaf, id: "my-editor", action: :set_content, content: "# Hello")
send_update(Leaf, id: "my-editor", action: :set_mode, mode: :visual)

JS Setup

Add to your app.js:

import "../../../deps/leaf/priv/static/assets/leaf.js"

let Hooks = {
  Leaf: window.LeafHooks.Leaf,
  // ... your other hooks
}

Gettext (optional)

To enable translations, configure a gettext backend:

config :leaf, :gettext_backend, MyApp.Gettext

Otherwise, English strings are used as-is.

Summary

Functions

Renders a Leaf editor as a function component.

Functions

leaf_editor(assigns)

Renders a Leaf editor as a function component.

This is a convenience wrapper around the Leaf LiveComponent. Import it in your view helpers:

import Leaf, only: [leaf_editor: 1]

Then use it in your templates:

<.leaf_editor id="my-editor" content={@content} />

All attributes are passed through to the underlying LiveComponent.

Attributes

  • id (:string) (required)
  • content (:string) - Defaults to "".
  • mode (:atom) - Defaults to :hybrid. Must be one of :visual, :hybrid, :markdown, or :html.
  • preset (:atom) - Defaults to :advanced. Must be one of :advanced, or :simple.
  • toolbar (:list) - Defaults to [].
  • placeholder (:string) - Defaults to "Write something...".
  • readonly (:boolean) - Defaults to false.
  • height (:string) - Defaults to "480px".
  • min_height (:string) - Defaults to nil.
  • max_height (:string) - Defaults to nil.
  • debounce (:integer) - Defaults to 400.
  • flush_on_blur (:boolean) - Defaults to true.
  • emit_events (:boolean) - Defaults to false.
  • toolbar_extra (:list) - Defaults to [].
  • toolbar_layout (:atom) - Defaults to :fixed. Must be one of :fixed, :floating, or :both.
  • preserve_tags (:list) - Defaults to [].
  • maxlength (:integer) - Defaults to nil.
  • spellcheck (:boolean) - Defaults to true.
  • dir (:string) - Defaults to "ltr". Must be one of "ltr", "rtl", or "auto".
  • smart_typography (:boolean) - Defaults to false.
  • export (:boolean) - Defaults to false.
  • protect_navigation (:boolean) - Defaults to false.
  • save_status (:atom) - Defaults to nil.Must be one of nil, :saved, :saving, or :unsaved.
  • gettext_backend (:any) - Defaults to nil.
  • upload_handler (:any) - Defaults to nil.
  • sync_input_name (:string) - Defaults to nil.
  • class (:string) - Defaults to nil.
  • script_nonce (:string) - Defaults to "".
  • loading_preset (:atom) - Defaults to :random. Must be one of :default, :random, :unpuzzling, :brewing, :polishing, :composing, :crafting, or :tidying.
  • loading_text (:string) - Defaults to nil.
  • Global attributes are accepted.