Function components for the Kotoba editor and for rendered content.
import Kotoba.Components
<.form for={@form} phx-change="validate" phx-submit="save">
<label id="post-body-label">Body</label>
<.kotoba field={@form[:body]} id="post-body" label_id="post-body-label" />
</.form>
<.kotoba_content content={@post.body} />The editor needs the Kotoba hook in the LiveSocket and the Kotoba CSS
(see the README). It pushes kotoba:change only with change={true}.
Summary
Functions
Renders the rich-text editor for a form field.
Renders a Kotoba.Content as safe HTML.
Renders an option of an embeds picker (kotoba_embeds/1): a button
with an icon, a title and a line about it. Its phx-click is the app's.
Renders the live content of the embeds of the editor for: the app's
canvases, grids and charts in the document, which people use in place
and take full screen. The editor has embeds
Renders the app's own selection menu: the menu that floats over the
selected text. Put it in the selection slot of kotoba/1
Renders a custom toolbar for an editor.
Returns the names of the toolbar commands, in toolbar order.
Functions
Renders the rich-text editor for a form field.
It renders:
- a hidden input with the field's
nameand, as its value, the JSON of the field's value cast toKotoba.Content(or of an empty document). The editor writes the document to it on every change, so a plain form submit posts it. It is outside the editor element, so a LiveView patch can update it; - the editor element, with
phx-hook="Kotoba"andphx-update="ignore". The hook builds the toolbar and the editable area in it; - the LiveView file input of
uploads, visually hidden, whenuploadsis given. The toolbar's attach button, a drop and a paste of files hand the files to it.
The editor always has an accessible name: aria-labelledby with
label_id (the id of a visible label, the best choice), else
aria-label with label, else the field name in words.
Attributes that are not declared (phx-target, …) go on the editor
element. aria-describedby and aria-invalid go on the editable area
(the textbox), and follow the field: a new value in a later render
reaches it, and a value that goes away is removed. In a LiveComponent, give phx-target={@myself},
so that the editor's events go to the component.
The form posts the document, so a LiveView needs no editor event for a
form. With change, the editor also pushes kotoba:change with
%{"id", "doc", "v"} on each change (debounced), for example to save a
draft; the LiveView must then handle it.
A change of readonly or change in a later render reaches the editor. To change
the document after the first render, use Kotoba.Live.push_content/3:
the editor reads the hidden input only when it mounts.
Attributes
field(Phoenix.HTML.FormField) (required) - the form field, for example@form[:body].id(:string) - the id of the editor element. The default is the field's id with_editor. Defaults tonil.label_id(:string) - the id of the element that labels the editor (aria-labelledby). Without it, the editor getsaria-labelfromlabel. Defaults tonil.label(:string) - the accessible name when there is nolabel_id(aria-label). The default is the field name in words, for example "Body". Defaults tonil.placeholder(:string) - the text of an empty editor. Defaults tonil.issho(:any) - a shared editor, on Issho:[instance: MyApp.Issho, document: "page:1/body"], withIssho.Join's keys as every component of the page (can,people,follow,on_behalf_of,claims) and Kotoba's own:undo(:editor,:pageorfalse),socketandmax_channels; needsuserand a LiveView withon_mount Issho.Live; see the Collaboration guide. Defaults tonil.user(:any) - the person, in a shared editor: your user, as itsIssho.Actorsays them (or a map with an:id, a:nameand a:color), the same as in the page's other components. Defaults tonil.collab(:any) - removed in 0.7:isshoanduser; see the Upgrading guide. Defaults tonil.nodes(:list) - app nodes:{module, js_url}tuples, or JavaScript module URLs. Defaults to[].features(:list) - the built-in features of the editor, seeKotoba.Features; every feature butblocksby default. Defaults tonil.extensions(:list) - the URLs of the app's extension modules, see the Extensions guide; in the editor, after the features. Defaults to[].prompts(:list) - the prompt list, seeKotoba.Prompts: a search callback, or options (label,spaces,items,insert...). Defaults to[].assist(:any) - suggestions that the person asks for: a list of actions for the toolbar's Assist menu ([rewrite: "Rewrite"]), ortruefor no menu; the LiveView then handleskotoba:assistandkotoba:suggestion, see the Suggestions guide. Defaults tonil.code_languages(:list) - the ids of the languages of the code language picker, seeKotoba.CodeLanguages; every language by default. Defaults tonil.uploads(Phoenix.LiveView.UploadConfig) - an upload config, for example@uploads.attachments. Defaults tonil.upload_label(:string) - the label of the file input. Defaults to"Attach files".readonly(:boolean) - whentrue, the editor shows the document and cannot change it; a change in a later render reaches the editor. Defaults tofalse.show_toolbar(:boolean) - whenfalse, the editor shows no toolbar: withreadonly, a page that reads the document (its embeds live in it). Defaults totrue.embeds(:boolean) - whentrue, the editor has embeds: live things of the app's own (a canvas, a grid), rendered by thekotoba_embeds/1of this editor; see the Embeds guide. Defaults tofalse.blank_lines(:atom) -:keepsaves the blank lines a person types (empty paragraphs between blocks), as a word processor does;:collapseleaves them out of the saved document. Defaults to:keep. Must be one of:keep, or:collapse.paste_markdown(:boolean) - whentrue, pasted plain text that reads as Markdown becomes rich text, read on the server byKotoba.MarkdownPaste, which the editor renders with no code of the app;falserenders no LiveComponent, for a template that cannot have one. Defaults totrue.change(:boolean) - whentrue, the editor pusheskotoba:changewith the document on each change, and the LiveView (or thephx-targetcomponent) must handle it. Defaults tofalse.debounce(:integer) - milliseconds betweenkotoba:changepushes; 300 when not given. Defaults tonil.selection_menu(:any) - a menu over the selected text:truefor the default commands (bold, italic, underline, strikethrough, code, highlight, link), or a list of commands, seetoolbar_commands/0. Theselectionslot gives the app's own menu. Defaults tofalse.class(:any) - classes for the wrapper element. Defaults tonil.- Global attributes are accepted. attributes for the editor element.
Slots
selection- the app's own selection menu, for example<.kotoba_selection_menu>with the app's icons.toolbar- a custom toolbar, for example<.kotoba_toolbar>; the default toolbar has the commands of the editor's features, butsubscriptandsuperscript.
Renders a Kotoba.Content as safe HTML.
The cached :html of the content is used when its cache version is the
current one (Kotoba.Content.cache_version/0), the policy is :default
and no extra nodes are given: this is the HTML that Kotoba.Renderer
made when the content was cast. In every other case, the document is
rendered again with Kotoba.Renderer (a document that no longer parses
renders as nothing). nil renders an empty wrapper.
Attributes
content(:any) (required) - aKotoba.Content, ornil.policy(:any) -:defaultor:untrusted, seeKotoba.Sanitizer. Defaults to:default.nodes(:list) - extra node modules,{module, js_url}tuples, or JavaScript module URLs (the same list askotoba/1; a URL has no module to render). Defaults to[].embed(:any) -fn embed, :html -> html | nil end: the HTML of an embed (Kotoba.Nodes.Embed), as an image of a canvas;nilgives the default; see the Embeds guide. Defaults tonil.class(:any) - classes for the wrapper, for example"prose". Defaults tonil.- Global attributes are accepted. attributes for the wrapper.
Renders an option of an embeds picker (kotoba_embeds/1): a button
with an icon, a title and a line about it. Its phx-click is the app's.
<.kotoba_embed_option phx-click="new_canvas" phx-value-kit="schema" title="Database schema" about="Tables and foreign keys">
<:icon><.icon name="hero-circle-stack" /></:icon>
</.kotoba_embed_option>Attributes
title(:string) (required)about(:string) - Defaults tonil.class(:any) - Defaults tonil.- Global attributes are accepted. Supports all globals plus:
["disabled"].
Slots
icon
Renders the live content of the embeds of the editor for: the app's
canvases, grids and charts in the document, which people use in place
and take full screen. The editor has embeds:
<.kotoba field={@form[:body]} id="post-body" embeds />
<.kotoba_embeds for="post-body" label="Canvas" icon="canvas">
<:embed :let={embed}>
<MyAppWeb.Diagrams.embed id={embed.id} ref={embed.ref} full={embed.full} on_full={embed.toggle_full} />
</:embed>
<:picker :let={picker}>
<.kotoba_embed_option :for={d <- @diagrams} phx-click="insert_diagram" phx-value-id={d.id} title={d.title} />
</:picker>
</.kotoba_embeds>The :embed slot renders one embed; :let gets a map with:
id- the id of the embed's slot: unique in the page, for the ids of what it renders (a canvas's"canvas-" <> embed.id).kind,refandtitle- the embed's, as the document has them.editor- the editor's id.full- whether the embed is full screen now.toggle_full- aPhoenix.LiveView.JScommand that takes it full screen and back, for a button.readonly- this component'sreadonly.
With resolve, a link to something your app embeds becomes the embed:
a URL pasted alone on a line, or dropped on the editor (from another
tab), goes to resolve, which returns the embed or nil (a link).
Render it outside the editor's form, once for each editor with embeds.
With a :picker, the toolbar has a button (label, icon) that opens
it; the app's buttons in it insert an embed with
Kotoba.Live.insert_embed/4, which closes it. See the
Embeds guide.
Attributes
for(:string) (required) - the id of the editor.readonly(:boolean) - given to the:embedslot, for read-only content. Defaults tofalse.label(:string) - the words of the toolbar button and its tooltip, as "Canvas". Defaults to"Embed".icon(:string) - the icon of the toolbar button. Defaults to"embed". Must be one of"embed","canvas","grid", or"chart".picker_title(:string) - the title of the picker. Defaults to"Insert".resolve(:any) -fn url -> embed | nil end: what a URL pasted alone on a line, or dropped, is (a map ofkind,refandtitle, or aKotoba.Nodes.Embed), ornilfor a link; for links to the app's own pages. Defaults tonil.class(:any) - Defaults tonil.
Slots
embed(required) - renders an embed (:letgets the embed, see above).picker- the picker's content: the app's buttons that insert an embed (:letgetseditorandclose, a JS command).
Renders a custom toolbar for an editor.
Inside the toolbar slot of kotoba/1, it needs no for. Elsewhere on
the page, give the editor's id as for: the toolbar then has
phx-update="ignore" (and the id <for>-toolbar), so that a LiveView
patch does not undo the state that the editor puts on its buttons.
With no button slots, it renders one text button for each of
commands. With button slots, it renders them, for example with the
app's own icons:
<.kotoba_toolbar for="post-body">
<:button command="bold"><.icon name="hero-bold" /></:button>
<:button command="link" label="Add a link"><.icon name="hero-link" /></:button>
</.kotoba_toolbar>The editor adds role="toolbar", the roving tabindex, aria-pressed
and aria-disabled. The commands are the ones of toolbar_commands/0;
or the command of an extension's control, "<extension>:<command>" (give
it a label); another command raises ArgumentError. The editor hides
the buttons of the features that it does not have (see
Kotoba.Features), and the buttons of the table-* commands (the ones that act on the table at the selection)
when the selection is not in a table, and the buttons of gallery,
image-previous and image-next (the ones that act on the selected
image) when no image is selected. code-language renders a
<select>, the code language picker: the editor fills its options, and
hides it when the selection is not in a code block. highlight opens the
color palette of the editor (aria-haspopup="dialog").
Attributes
for(:string) - the id of the editor, when the toolbar is outside it. Defaults tonil.commands(:list) - the commands, when there are no buttons. Defaults to["bold", "italic", "underline", "strikethrough", "highlight", "code", "subscript", "superscript", "link", "h1", "h2", "h3", "h4", "quote", "bullet", "number", "check", "code-block", "rule", "table", "upload", "embed", "assist", "code-language", "table-row-before", "table-row-after", "table-column-before", "table-column-after", "table-header-row", "table-header-column", "table-delete-row", "table-delete-column", "table-delete", "gallery", "image-previous", "image-next", "undo", "redo"].label(:string) - the accessible name of the toolbar. Defaults to"Formatting".class(:any) - classes for the toolbar element. Defaults tonil.- Global attributes are accepted. attributes for the toolbar element.
Slots
button- Accepts attributes:command(:string) (required)label(:string)class(:any)
@spec toolbar_commands() :: [String.t()]
Returns the names of the toolbar commands, in toolbar order.
Examples
iex> "bold" in Kotoba.Components.toolbar_commands()
true