v1.6.0

<bmx-rich-text-editor>

A rich text editor that keeps everything in the page: no cloud service, no licence server, no script loaded from anywhere. Headings, marks, colour, links, bulleted, numbered and check lists, quotes, code blocks, tables, images, dividers; a slash menu for blocks, mentions of people, Markdown typed as you go, find and replace, comments with replies, tracked changes to accept or reject, the HTML source, and full screen. Paste from Word or Google Docs arrives clean, and everything that reaches the document - typed, pasted, dropped, loaded - passes the same sanitiser, so it cannot carry script.

23 properties · 6 events · 21 methods · 9 parts

Example

Release notes

This release brings charts, a rich text editor and a new site. Select any text and press the comment button, or switch on Track changes and edit.

  • Write the release notes
  • Check them with the team

What changed

AreaChange
ChartsTwenty-two kinds of chart
EditorsComments and tracked changes
Everything stays in the page: nothing is sent anywhere.
<bmx-rich-text-editor name="body"></bmx-rich-text-editor>
The editor's value appears here.
Show markup
<bmx-rich-text-editor
  id="ex-rte"
  lang="en-GB"
  label="Release notes"
  placeholder="Write here. Type / for blocks, @ to mention someone."
  author="Sundar"
  style="--bmx-rich-text-editor-min-height: 16rem; --bmx-rich-text-editor-max-height: 28rem"
>
  <h2>Release notes</h2>
  <p>This release brings <strong>charts</strong>, a <em>rich text editor</em> and a <a href="https://binarymission.co.uk">new site</a>. Select any text and press the comment button, or switch on <strong>Track changes</strong> and edit.</p>
  <ul data-type="checklist">
    <li data-checked="true">Write the release notes</li>
    <li data-checked="false">Check them with the team</li>
  </ul>
  <h3>What changed</h3>
  <table>
    <thead><tr><th>Area</th><th>Change</th></tr></thead>
    <tbody>
      <tr><td>Charts</td><td>Twenty-two kinds of chart</td></tr>
      <tr><td>Editors</td><td>Comments and tracked changes</td></tr>
    </tbody>
  </table>
  <blockquote>Everything stays in the page: nothing is sent anywhere.</blockquote>
  <pre data-language="html"><code>&lt;bmx-rich-text-editor name="body"&gt;&lt;/bmx-rich-text-editor&gt;</code></pre>
</bmx-rich-text-editor>

<div class="row" style="margin-block-start: 1rem; gap: 0.5rem; flex-wrap: wrap">
  <button type="button" id="ex-rte-html">Show the HTML</button>
  <button type="button" id="ex-rte-md">Show the Markdown</button>
  <button type="button" id="ex-rte-json">Show the JSON</button>
</div>

<pre id="ex-rte-out" class="note" style="margin-block-start: 0.75rem; max-block-size: 16rem; overflow: auto; white-space: pre-wrap">The editor's value appears here.</pre>

<script type="module">
  await customElements.whenDefined('bmx-rich-text-editor');

  const editor = document.getElementById('ex-rte');
  const out = document.getElementById('ex-rte-out');

  editor.mentions = [
    { id: 'u1', label: 'Ann Lee', description: 'Design' },
    { id: 'u2', label: 'Bo Chen', description: 'Sales' },
    { id: 'u3', label: 'Cara Diaz', description: 'Support' },
  ];
  editor.comments = [];

  document.getElementById('ex-rte-html').addEventListener('click', async () => (out.textContent = await editor.toHTML()));
  document.getElementById('ex-rte-md').addEventListener('click', async () => (out.textContent = await editor.toMarkdown()));
  document.getElementById('ex-rte-json').addEventListener('click', async () => (out.textContent = JSON.stringify(await editor.toDocumentJSON(), null, 2)));
  editor.addEventListener('bmxRteCommentsChange', event => (out.textContent = `Comments: ${JSON.stringify(event.detail.comments, null, 2)}`));
</script>

The value is HTML, posted with its form under name; toMarkdown(), toText() and toDocumentJSON() give it in other shapes, and loadMarkdown() loads Markdown. Images are kept in the document as data unless an imageHandler stores them where the application wants.

The text being edited sits in the element's light DOM, so that the browser's own selection, spelling and input methods behave the same in every engine; its styles are scoped to the editor.

Properties

PropertyAttributeTypeDefaultDescription
author author string 'You' The name tracked changes and comments are written under.
comments comments readonly BmxRteComment[] | string [] The comments on the document. JSON in markup. Updated as comments are added, answered and resolved.
disabled disabled boolean false Neither editable nor posted with its form.
fullscreen fullscreen boolean false Show the editor over the whole window.
hideChanges hide-changes boolean false Show tracked deletions struck through (default) or hide them, showing the text as it would read.
imageHandler property only (file: File) => Promise<string> — Stores an image the reader adds and returns its URL. Default: the image is kept in the document as data.
images images boolean true Allow images.
label label string — What the editor is for, said by screen readers.
markdownShortcuts markdown-shortcuts boolean true Turn # , - , 1. , > , [] , ``` and **bold** into formatting as they are typed.
maxLength max-length number — At most this many characters of text.
mentionSource property only (query: string) => readonly BmxRteMentionItem[] | Promise<readonly BmxRteMentionItem[]> — Finds people to
mentions mentions readonly BmxRteMentionItem[] | string — People (or anything) to
name name string — The name the value is posted under with its form.
placeholder placeholder string — Shown while the document is empty.
readonly readonly boolean false Shown but not editable.
required required boolean false The form will not submit while the document is empty.
spellcheck spellcheck boolean true Underline misspellings, as the browser does.
statusBar status-bar boolean true Show the word count and the review buttons under the document.
strings strings Partial<BmxRteStrings> | string — Words the editor shows or says: any of BmxRteStrings. JSON in markup.
toolbar toolbar string | readonly string[] 'full' The toolbar: full (default), basic, minimal, or a list of items (bold,italic,|,link).
toolbarMode toolbar-mode 'top' | 'bubble' | 'both' | 'none' 'top' Where the toolbar is: top (default), bubble (over a selection), both or none.
tracking tracking boolean false Record typing and deleting as changes to accept or reject.
value value string '' The document as HTML. Set it to load one; read it (or listen for bmxRteInput) for what was written.

Events

EventDetailDescription
bmxRteChange BmxRteValueDetail The document changed and the editor lost focus, as a field's change does.
bmxRteCommentsChange BmxRteCommentsDetail A comment was added, answered, resolved, reopened or deleted. comments is the whole new list.
bmxRteInput BmxRteValueDetail The document changed (typing, a command, a paste).
bmxRteMention BmxRteMentionDetail Someone was
bmxRteSelectionChange BmxRteFormats The formats at the caret changed.
bmxRteTrackedChange BmxRteChangesDetail The tracked changes changed.

Methods

MethodSignatureDescription
acceptChange acceptChange(id?: string) => Promise<number> Accepts one tracked change by id, or all of them.
addCommentToSelection addCommentToSelection(text: string) => Promise<string | null> Adds a comment to the selected text. Returns its id, or null with nothing selected.
clear clear() => Promise<void> Empties the document.
execCommand execCommand(name: string, value?: string) => Promise<void> Runs a command, as its toolbar button does: bold, italic, underline, strike, code, p, h1-h6, quote, codeBlock, bulletList, orderedList, checklist, alignLeft, alignCenter, alignRight, alignJustify, indent, outdent, textColor and highlight (with a colour), unlink, rule, clear, undo, redo, find, track, source, fullscreen and the table commands.
getChanges getChanges() => Promise<readonly BmxRteChange[]> Every tracked change.
getStats getStats() => Promise<{ words: number; characters: number; }> Words and characters.
insertHTML insertHTML(html: string) => Promise<void> Inserts HTML (sanitised) at the caret, or at the end when the editor has none.
insertTableAt insertTableAt(cols?: number, rows?: number) => Promise<void> Inserts a table of cols by rows (the first row a header).
insertText insertText(text: string) => Promise<void> Inserts text at the caret.
loadHTML loadHTML(html: string, keepHistory?: boolean) => Promise<void> Loads HTML (sanitised). With keepHistory, the change can be undone.
loadMarkdown loadMarkdown(markdown: string, keepHistory?: boolean) => Promise<void> Loads Markdown.
redo redo() => Promise<void> Redoes what was undone.
rejectChange rejectChange(id?: string) => Promise<number> Rejects one tracked change by id, or all of them.
replaceAll replaceAll(query: string, replacement: string, matchCase?: boolean) => Promise<number> Replaces every match of query with replacement. Returns how many.
setFocus setFocus() => Promise<void> Moves focus into the document.
setLink setLink(href: string, newTab?: boolean) => Promise<void> Adds a link to the selection, or changes the one at the caret.
toDocumentJSON toDocumentJSON() => Promise<BmxRteJsonNode> The document as a tree of blocks, marks and text.
toHTML toHTML() => Promise<string> The document as sanitised HTML (the same as value).
toMarkdown toMarkdown() => Promise<string> The document as Markdown (GitHub's dialect).
toText toText() => Promise<string> The text, as a reader sees it, with tracked deletions left out.
undo undo() => Promise<void> Undoes the last change.

Slots

SlotDescription
surface (managed by the editor) the editing surface.

CSS shadow parts

PartDescription
bubble the toolbar over a selection.
button a toolbar button.
comments the comments panel.
find the find and replace bar.
frame the editor's border and background.
popover a menu or form opened from the toolbar.
source the HTML source view.
status the status bar.
toolbar the toolbar.

CSS custom properties

PropertyDescription
--bmx-rich-text-editor-background Behind the document.
--bmx-rich-text-editor-font The document's typeface. Default the library's.
--bmx-rich-text-editor-font-size The document's text size. Default 1rem.
--bmx-rich-text-editor-max-height The document's greatest height before it scrolls. Default none.
--bmx-rich-text-editor-min-height The document's least height. Default 10rem.
--bmx-rich-text-editor-toolbar-background Behind the toolbar.