Éditeur : constructeur, méthodes, événements

Deux objets, et la frontière entre eux est tout le modèle :Editor est le document et la seule façon de le changer, sans une ligne de DOM ; EditorView est une projectionde cet objet dans une page. Supprimez la vue, le document est toujours là.

Editor — le cœur, sans DOM

Rien ici n'importe le navigateur, et c'est ce qui laisse le même éditeur tourner dans un onglet, dans un test Node, dans un plugin Obsidian et dans une fenêtre Tauri.

Propriétés

NomTypeDéfautDescription
doc*Doc

The live document. Read it freely; write only through Editor.dispatch.

plugins*PluginRegistry

The block plugins this document is edited under.

On the *model*, not only on the view, because a plugin's document invariants have to hold with no view mounted at all — a CLI import, a server-side migration, a collaborative peer applying a remote change. It is the same reason ProseMirror keeps plugins in the state rather than in the view.

schema*Schema

Which block types exist, and what they may contain.

selection*Selection

Where the caret or the block selection is, or `null` when nowhere.

validation*ValidationMode```ts 'warn' ```

How invalid blocks are surfaced.

`warn` by default rather than `throw`: a validation bug that takes down a user's editor mid-sentence is worse than the state it was guarding against, since the document is still in memory and still savable. Tests and CI set `throw`.

redoDepth*number

undoDepth*number

dispatch*void

on*function

onSelection*function

redo*boolean

setSelection*void

undo*boolean

use*this

Constructeur et méthodes

dispatch

dispatch(build: function, opts: DispatchOptions): void

Apply a transaction: the one way the document changes.

Paramètres

  • build: function — Fills the transaction.
  • opts: DispatchOptions — `origin` labels the change for listeners; `selection` sets the selection atomically with the edit; `addToHistory: false` keeps it out of undo; `coalesce` merges consecutive dispatches sharing a key within 500 ms into one undo step, which is how typing undoes by word rather than by keystroke.

Retourne

  • void
editor.dispatch(
  (tx) => tx.op({ type: 'delete_block', id }),
  { origin: 'ui' },
)

on

on(listener: function): function

Every change, after it has been applied.

Paramètres

  • listener: function

Retourne

  • function
const off = editor.on((change) => {
  if (change.origin !== 'history') save(docToJSON(editor.doc))
})

onSelection

onSelection(listener: function): function

Caret and block-selection moves, which are not edits and so never reach Editor.on.

Paramètres

  • listener: function

Retourne

  • function

redo

redo(): boolean

Redo one step.

Retourne

  • boolean

setSelection

setSelection(sel: Selection, origin: string): void

Move the selection without a transaction, because a caret move is not an edit: it is not undoable and it does not mark the document dirty.

Paramètres

  • sel: Selection
  • origin: string

Retourne

  • void

undo

undo(): boolean

Undo one step, restoring the selection it was made from.

Retourne

  • boolean

use

use(plugin: BlockPlugin): this

Register a block plugin: its schema becomes part of this editor's, and its normalization runs on every transaction.

Paramètres

  • plugin: BlockPlugin

Retourne

  • this
import { tableBlocks } from '@nbe/blocks-table'
const editor = new Editor({ plugins: tableBlocks })

Écouter

Trois canaux, séparés parce qu'ils ne signifient pas la même chose. Chacun renvoie sa fonction de désabonnement : un écouteur qui survit à son hôte est une fuite.

// une transaction, une notification — un collage de quarante blocs
// n'appelle ce listener qu'une fois
const offChange = editor.on((change) => {
  change.origin;      // 'ui' | 'paste' | 'history' | ce que l'appelant a passé
  change.ops;         // les opérations appliquées
  change.dirty;       // les ids des blocs touchés
  change.selectionSet;
});

// déplacer le caret n'est pas une édition : c'est un autre canal
const offSelection = editor.onSelection((selection, origin) => {
  render(selection);
});

// la vue publie la fin d'un geste pointeur, une fois qu'il est retombé
const offGesture = view.onGestureEnd((name, committed) => {});

offChange(); offSelection(); offGesture();

Écrire

dispatch est le seul chemin. Il applique la transaction, enregistre son inverse pour l'annulation, valide uniquement les blocs touchés, puis notifie une fois. Une mutation qui le contourne n'est ni annulable, ni validée, ni observée.

import { insertText, toggleMarkRange, moveBlocks } from '@nbe/core';

// une commande : du sucre au-dessus de dispatch
insertText(editor, 'bonjour');

// ou la transaction à la main
editor.dispatch(
  (tx) => {
    tx.op({ type: 'delete_block', id });
    tx.op({ type: 'insert_block', block, index: 0 });
  },
  { origin: 'ui', selection: textCaret(block.id, 0) },
);

editor.undo();  // → false s'il n'y avait rien à annuler
editor.redo();

EditorView — la projection

La vue crée son propre élément dans le conteneur que vous lui donnez, y rend le document, attache les fonctionnalités demandées, et détache tout àdestroy(). Le contenu est non contrôlé : le document initial est lu une seule fois: le remonter à chaque rendu détruirait le caret, la sélection et l'historique.

Propriétés

NomTypeDescription
content*HTMLElement

The `.nbe-editor` element this view created and owns.

editor*Editor

The model this view projects.

gesture*ActiveGesture | null

What pointer gesture is running, published by the gesture router. This is the state that replaced three wall-clock windows: modules ask what is happening instead of guessing from how long ago something happened.

gestureEndListeners*Set<function>

Notified once a pointer gesture has fully settled. See `onGestureEnd`.

labels*EditorLabels

Resolved on-screen strings: the defaults with `options.labels` merged in.

lastTextCaret*Point | null

Where the caret last was in text. Opening a menu or selecting a block replaces the live selection, so anything that needs to act "where the user was typing" — table row/column actions, for one — reads this instead.

options*EditorViewOptions

The options it was mounted with, verbatim.

plugins*PluginRegistry

Per-editor, never module-global: two editors may have different sets.

readOnly*boolean

True when the view was mounted read-only.

recognizers*GestureRecognizer[]

Pointer gestures in precedence order. Features contribute to this list — `unshift` to outrank the defaults — and the router reads it at press time, so who wins a contested press is data rather than attach order.

topology*EditableTopology

Where the editable boundary sits: one host per block, or one at the root.

composing*boolean

announce*void

blockEl*HTMLElement | null

destroy*void

focusBlock*void

leafEl*HTMLElement | null

onGestureEnd*function

onRender*function

renderAll*void

slot*HTMLElement

syncDomSelection*void

whenComplete*Promise<void>

Constructeur et méthodes

announce

announce(message: string): void

Say something in the live region, for screen readers.

Paramètres

  • message: string

Retourne

  • void

blockEl

blockEl(id: string): HTMLElement | null

The element rendering a block, or `null` if it is not on screen.

Paramètres

  • id: string

Retourne

  • HTMLElement | null

destroy

destroy(): void

Detach every feature, stop listening to the editor, and remove the element.

Retourne

  • void

focusBlock

focusBlock(id: string, offset: number): void

Put the caret in a block, at a character offset, and focus it.

Paramètres

  • id: string
  • offset: number

Retourne

  • void

leafEl

leafEl(id: string): HTMLElement | null

The editable leaf inside a block, where its text lives.

Paramètres

  • id: string

Retourne

  • HTMLElement | null

onGestureEnd

onGestureEnd(cb: function): function

Called once a pointer gesture has fully settled.

Paramètres

  • cb: function

Retourne

  • function

onRender

onRender(cb: function): function

Called after the view has written to the DOM.

Paramètres

  • cb: function — Receives the blocks whose elements were replaced, or `null` when the whole surface was rebuilt.

Retourne

  • function

renderAll

renderAll(): void

Rebuild the whole surface from the document.

Retourne

  • void

slot

slot(where: SlotName): HTMLElement

A place for chrome that is not a block.

Paramètres

  • where: SlotName

Retourne

  • HTMLElement
const feature: EditorFeature = {
  name: 'word-count',
  attach(view) {
    const el = view.slot('bottom')
    …
  },
}

syncDomSelection

syncDomSelection(scrollIntoView: boolean): void

Push the model's selection into the browser, after changing it in code.

Paramètres

  • scrollIntoView: boolean

Retourne

  • void

whenComplete

whenComplete(): Promise<void>

Resolves once a streamed opening render has finished.

Retourne

  • Promise<void>

Les options de montage ont leur propre page :Configuration.