DevactionUI.Editor (devaction_ui v0.2.3)

Copy Markdown View Source

A rich text editor for delivery notes, scope and long comments: a toolbar over a TipTap (ProseMirror) surface, the way the canvas draws it (.editor, .editor-toolbar, .editor-btn, .editor-content).

The text lives in the browser (DevactionUI.Editor hook) and goes out as HTML and as Markdown in two hidden inputs, <name>[html] and <name>[markdown], which fire input on every change so a form with phx-change hears it. The initial content is value, as HTML. The root is ignored by LiveView patches, so the text is never lost to a re-render; to replace it, give the editor a new id.

The server must treat what comes back as untrusted: sanitize the HTML before it is stored or shown.

Typing / at the start of a line opens a menu of blocks (slash, each %{key, label} with a key of paragraph, h2, h3, bulletList, orderedList, taskList, blockquote, codeBlock or divider), and @ opens the members (mentions, each %{id, label}); a chosen member stays in the text as <span data-type="mention" data-id="...">, which is what the server reads to know who was mentioned. Each slash and mentions entry may also carry an icon (a glyph) and a hint (what is written at the end of the row, such as #). compact is the comment form: a short surface, usually without a toolbar, and with send_on_enter the command (or control) and Enter submit the form the editor is in; its footer slot is for the buttons under the surface. With toolbar_position={:bottom} the toolbar goes under the surface, in one bar with the footer (tools on the left, the send button on the right), as the canvas draws a comment box.

The hook is tied to the host by names the caller can change: the image is sent with the CSRF token of the <meta> called csrf_meta in the header csrf_header, and the ai tool sends the event improve_event and listens for replace_event and failed_event (the defaults are Phoenix's csrf-token and x-csrf-token, and improve, editor:replace and editor:failed).

The field has a visible label (show_label), a hint slot beside the word count, an error and a disabled state. The label and the error sit outside the ignored root and the flags are data-* attributes, so they follow the server; the toolbar, the hint and the footer are drawn once.

TipTap is loaded by the hook only when an editor is mounted (a dynamic import()), so an app that never renders one does not need its npm packages: see assets/package.json for the list and the versions.

Buttons come from the :tool slot, each with the command it runs (bold, italic, strike, code, bulletList, orderedList, taskList, blockquote, indent, outdent, alignLeft, alignCenter, alignRight, undo, redo, link, image (which sends a file to upload_url) or ai (which sends the Markdown as the event improve to the component the editor is in, and takes back the text it answers with in editor:replace)), its label and its icon as the body. A tool with separator is preceded by a divider. The library holds no icons and no language: the caller gives both.

suggest/1 is the popup of those two menus as a component of its own, for a page that draws it itself (a mock, a server-rendered mention list, the docs): a listbox with a group label (PEOPLE, COMMANDS), rows of a 26px mark (the initials of a person, or an icon), a title and a hint, the active row highlighted and carrying the Enter key cap.

Summary

Functions

The popup of the @ and / menus: a listbox with a group label and rows of a mark, a title and a hint.

Functions

editor(assigns)

Attributes

  • id (:string) (required)
  • name (:string) (required) - prefix of the two inputs: name[html] and name[markdown].
  • value (:string) - the initial content, as HTML. Defaults to "".
  • label (:string) (required) - the name of the text area, shown when show_label.
  • toolbar_label (:string) (required) - the name of the toolbar, distinct from the text area's (e.g. Formatting).
  • show_label (:boolean) - draw label above the editor. Defaults to false.
  • error (:string) - what is wrong; marks the field invalid. Defaults to nil.
  • disabled (:boolean) - the text cannot be edited and the tools are off. Defaults to false.
  • placeholder (:string) - Defaults to "".
  • styles (:list) - [{label, value}] with values paragraph, h2, h3; empty hides the select. Defaults to [].
  • style_label (:string) (required)
  • words_label (:string) (required)
  • slash (:list) - [%{key, label}] for the / menu; empty turns it off. Defaults to [].
  • mentions (:list) - [%{id, label}] for the @ menu; empty turns it off. Defaults to [].
  • upload_url (:string) - where the image tool sends a file (the image is the body of the request; it answers {url}); nil turns the tool off. Defaults to nil.
  • upload_max (:integer) - the largest image, in bytes. Defaults to 5242880.
  • upload_errors (:map) - what to say when an upload fails: unsupported, too_large, failed. Defaults to %{}.
  • ai_labels (:map) - texts of the ai tool: working, done and failed; the tool sends improve with the Markdown to the component the editor is in. Defaults to %{}.
  • compact (:boolean) - the short comment form. Defaults to false.
  • toolbar_position (:atom) - :bottom puts the toolbar under the surface, in one bar with the footer. Defaults to :top. Must be one of :top, or :bottom.
  • csrf_meta (:string) - the name of the <meta> that holds the CSRF token sent with an image. Defaults to "csrf-token".
  • csrf_header (:string) - the header that carries the token with an image. Defaults to "x-csrf-token".
  • improve_event (:string) - the event the ai tool sends, with the Markdown. Defaults to "improve".
  • replace_event (:string) - the event the server answers improve_event with: %{id, markdown}. Defaults to "editor:replace".
  • failed_event (:string) - the event the server answers with when it could not improve the text: %{id}. Defaults to "editor:failed".
  • send_on_enter (:boolean) - ⌘↵ or Ctrl↵ submits the form. Defaults to false.
  • link_label (:string) (required)
  • class (:any) - Defaults to nil.
  • Global attributes are accepted. phx-target: where the ai tool sends improve. Supports all globals plus: ["phx-target"].

Slots

  • hint - text on the left of the word count, e.g. what Markdown is understood.
  • footer - a bar under the surface, e.g. the send button of a comment.
  • tool - Accepts attributes:
    • name (:string) (required)
    • label (:string) (required)
    • separator (:boolean)

suggest(assigns)

@spec suggest(map()) :: Phoenix.LiveView.Rendered.t()

The popup of the @ and / menus: a listbox with a group label and rows of a mark, a title and a hint.

Attributes

  • id (:string) (required)
  • label (:string) (required) - the name of the list, for assistive technology.
  • heading (:string) - the label of the group: PEOPLE, COMMANDS. Defaults to nil.
  • active (:integer) - the index of the highlighted row; -1 for none. Defaults to 0.
  • enter (:string) - the key cap at the end of the active row. Defaults to "↵".
  • class (:any) - Defaults to nil.
  • target (:any) - a phx-target (a @myself) when used inside a LiveComponent. Defaults to nil.
  • Global attributes are accepted.

Slots

  • option (required) - a row; its body is the mark before the title: initials or an icon. Accepts attributes:
    • title (:string) (required)
    • hint (:string) - the second line, such as the role of a person or what a command does.
    • tone (:atom) - :ink (the default) or a DevactionUI.Tone: the colour of the mark.
    • event (:string) - sent when the row is chosen, with its value.
    • value (:string) - sent as choice.