# `Kotoba.Components`
[🔗](https://github.com/hattoriai/kotoba/blob/v0.8.0/lib/kotoba/components.ex#L1)

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}`.

# `kotoba`

Renders the rich-text editor for a form field.

It renders:

  * a hidden input with the field's `name` and, as its value, the JSON of
    the field's value cast to `Kotoba.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"` and `phx-update="ignore"`.
    The hook builds the toolbar and the editable area in it;
  * the LiveView file input of `uploads`, visually hidden, when
    `uploads` is 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 to `nil`.
* `label_id` (`:string`) - the id of the element that labels the editor (`aria-labelledby`). Without it, the editor gets `aria-label` from `label`. Defaults to `nil`.
* `label` (`:string`) - the accessible name when there is no `label_id` (`aria-label`). The default is the field name in words, for example "Body". Defaults to `nil`.
* `placeholder` (`:string`) - the text of an empty editor. Defaults to `nil`.
* `issho` (`:any`) - a shared editor, on [Issho](https://hex.pm/packages/issho): `[instance: MyApp.Issho, document: "page:1/body"]`, with `Issho.Join`'s keys as every component of the page (`can`, `people`, `follow`, `on_behalf_of`, `claims`) and Kotoba's own: `undo` (`:editor`, `:page` or `false`), `socket` and `max_channels`; needs `user` and a LiveView with `on_mount Issho.Live`; see the Collaboration guide. Defaults to `nil`.
* `user` (`:any`) - the person, in a shared editor: your user, as its `Issho.Actor` says them (or a map with an `:id`, a `:name` and a `:color`), the same as in the page's other components. Defaults to `nil`.
* `collab` (`:any`) - removed in 0.7: `issho` and `user`; see the Upgrading guide. Defaults to `nil`.
* `nodes` (`:list`) - app nodes: `{module, js_url}` tuples, or JavaScript module URLs. Defaults to `[]`.
* `features` (`:list`) - the built-in features of the editor, see `Kotoba.Features`; every feature but `blocks` by default. Defaults to `nil`.
* `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, see `Kotoba.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"]`), or `true` for no menu; the LiveView then handles `kotoba:assist` and `kotoba:suggestion`, see the Suggestions guide. Defaults to `nil`.
* `code_languages` (`:list`) - the ids of the languages of the code language picker, see `Kotoba.CodeLanguages`; every language by default. Defaults to `nil`.
* `uploads` (`Phoenix.LiveView.UploadConfig`) - an upload config, for example `@uploads.attachments`. Defaults to `nil`.
* `upload_label` (`:string`) - the label of the file input. Defaults to `"Attach files"`.
* `readonly` (`:boolean`) - when `true`, the editor shows the document and cannot change it; a change in a later render reaches the editor. Defaults to `false`.
* `show_toolbar` (`:boolean`) - when `false`, the editor shows no toolbar: with `readonly`, a page that reads the document (its embeds live in it). Defaults to `true`.
* `embeds` (`:boolean`) - when `true`, the editor has embeds: live things of the app's own (a canvas, a grid), rendered by the `kotoba_embeds/1` of this editor; see the [Embeds](embeds.md) guide. Defaults to `false`.
* `blank_lines` (`:atom`) - `:keep` saves the blank lines a person types (empty paragraphs between blocks), as a word processor does; `:collapse` leaves them out of the saved document. Defaults to `:keep`. Must be one of `:keep`, or `:collapse`.
* `paste_markdown` (`:boolean`) - when `true`, pasted plain text that reads as Markdown becomes rich text, read on the server by `Kotoba.MarkdownPaste`, which the editor renders with no code of the app; `false` renders no LiveComponent, for a template that cannot have one. Defaults to `true`.
* `change` (`:boolean`) - when `true`, the editor pushes `kotoba:change` with the document on each change, and the LiveView (or the `phx-target` component) must handle it. Defaults to `false`.
* `debounce` (`:integer`) - milliseconds between `kotoba:change` pushes; 300 when not given. Defaults to `nil`.
* `selection_menu` (`:any`) - a menu over the selected text: `true` for the default commands (bold, italic, underline, strikethrough, code, highlight, link), or a list of commands, see `toolbar_commands/0`. The `selection` slot gives the app's own menu. Defaults to `false`.
* `class` (`:any`) - classes for the wrapper element. Defaults to `nil`.
* 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, but `subscript` and `superscript`.

# `kotoba_content`

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) - a `Kotoba.Content`, or `nil`.
* `policy` (`:any`) - `:default` or `:untrusted`, see `Kotoba.Sanitizer`. Defaults to `:default`.
* `nodes` (`:list`) - extra node modules, `{module, js_url}` tuples, or JavaScript module URLs (the same list as `kotoba/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; `nil` gives the default; see the [Embeds](embeds.md) guide. Defaults to `nil`.
* `class` (`:any`) - classes for the wrapper, for example `"prose"`. Defaults to `nil`.
* Global attributes are accepted. attributes for the wrapper.

# `kotoba_embed_option`

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 to `nil`.
* `class` (`:any`) - Defaults to `nil`.
* Global attributes are accepted. Supports all globals plus: `["disabled"]`.
## Slots

* `icon`

# `kotoba_embeds`

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`, `ref` and `title` - the embed's, as the document has them.
  * `editor` - the editor's id.
  * `full` - whether the embed is full screen now.
  * `toggle_full` - a `Phoenix.LiveView.JS` command that takes it full
    screen and back, for a button.
  * `readonly` - this component's `readonly`.

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](embeds.md) guide.

## Attributes

* `for` (`:string`) (required) - the id of the editor.
* `readonly` (`:boolean`) - given to the `:embed` slot, for read-only content. Defaults to `false`.
* `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 of `kind`, `ref` and `title`, or a `Kotoba.Nodes.Embed`), or `nil` for a link; for links to the app's own pages. Defaults to `nil`.
* `class` (`:any`) - Defaults to `nil`.
## Slots

* `embed` (required) - renders an embed (`:let` gets the embed, see above).
* `picker` - the picker's content: the app's buttons that insert an embed (`:let` gets `editor` and `close`, a JS command).

# `kotoba_selection_menu`

Renders the app's own selection menu: the menu that floats over the
selected text. Put it in the `selection` slot of `kotoba/1`:

    <.kotoba field={@form[:body]} id="post-body">
      <:selection>
        <.kotoba_selection_menu class="my-menu">
          <:button command="bold"><.icon name="hero-bold" /></:button>
          <:button command="italic"><.icon name="hero-italic" /></:button>
          <:button command="link" label="Add a link"><.icon name="hero-link" /></:button>
        </.kotoba_selection_menu>
      </:selection>
    </.kotoba>

With no `button` slots, it renders one text button for each of
`commands`. The commands, and what the editor does with the buttons, are
the ones of `kotoba_toolbar/1`. The editor shows the menu over a
selection of text, gives it the `kotoba-selection-menu` class and
`data-placement` (`"top"` or `"bottom"`), and moves the focus to it with
Alt+F10.

## Attributes

* `commands` (`:list`) - the commands, when there are no buttons. Defaults to `["bold", "italic", "underline", "strikethrough", "code", "highlight", "link"]`.
* `label` (`:string`) - the accessible name of the menu. Defaults to `"Selection formatting"`.
* `class` (`:any`) - classes for the menu element. Defaults to `nil`.
* Global attributes are accepted. attributes for the menu element.
## Slots

* `button` - Accepts attributes:

  * `command` (`:string`) (required)
  * `label` (`:string`)
  * `class` (`:any`)

# `kotoba_toolbar`

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 to `nil`.
* `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 to `nil`.
* Global attributes are accepted. attributes for the toolbar element.
## Slots

* `button` - Accepts attributes:

  * `command` (`:string`) (required)
  * `label` (`:string`)
  * `class` (`:any`)

# `toolbar_commands`

```elixir
@spec toolbar_commands() :: [String.t()]
```

Returns the names of the toolbar commands, in toolbar order.

## Examples

    iex> "bold" in Kotoba.Components.toolbar_commands()
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
