ProseMirror's view component that manages DOM structure and user interactions for rich text editing
npx @tessl/cli install tessl/npm-prosemirror-view@1.40.0ProseMirror's view component provides the DOM rendering and user interaction layer for rich text editors. It manages the DOM representation of documents, handles user input and events, and offers extensible APIs for custom node and mark rendering. The view bridges ProseMirror's abstract document model with the browser's contentEditable interface.
npm install prosemirror-viewimport { EditorView } from "prosemirror-view";For decorations:
import { Decoration, DecorationSet } from "prosemirror-view";For custom views:
import { NodeView, MarkView } from "prosemirror-view";import { EditorView } from "prosemirror-view";
import { EditorState } from "prosemirror-state";
import { Schema, DOMParser } from "prosemirror-model";
import { exampleSetup } from "prosemirror-example-setup";
// Create editor state
const schema = new Schema({
nodes: {
doc: { content: "paragraph+" },
paragraph: { content: "text*", toDOM: () => ["p", 0] },
text: {}
}
});
const state = EditorState.create({
schema,
plugins: exampleSetup({ schema })
});
// Create editor view
const view = new EditorView(document.querySelector("#editor"), {
state,
dispatchTransaction(tr) {
view.updateState(view.state.apply(tr));
}
});
// Focus the editor
view.focus();
// Get position from coordinates
const pos = view.posAtCoords({ left: 100, top: 50 });
if (pos) {
console.log("Document position:", pos.pos);
}ProseMirror View is built around several key components:
The main EditorView class that manages the DOM structure representing an editable document and handles user interactions.
class EditorView {
constructor(
place: null | DOMNode | ((editor: HTMLElement) => void) | {mount: HTMLElement},
props: DirectEditorProps
);
readonly state: EditorState;
readonly dom: HTMLElement;
readonly editable: boolean;
readonly dragging: null | {slice: Slice, move: boolean};
readonly composing: boolean;
readonly props: DirectEditorProps;
readonly root: Document | ShadowRoot;
update(props: DirectEditorProps): void;
setProps(props: Partial<DirectEditorProps>): void;
updateState(state: EditorState): void;
focus(): void;
hasFocus(): boolean;
destroy(): void;
dispatch(tr: Transaction): void;
}Visual decorations that can be applied to document content for styling, annotations, and interactive elements.
class Decoration {
readonly from: number;
readonly to: number;
readonly spec: any;
static widget(pos: number, toDOM: WidgetConstructor, spec?: object): Decoration;
static inline(from: number, to: number, attrs: DecorationAttrs, spec?: object): Decoration;
static node(from: number, to: number, attrs: DecorationAttrs, spec?: object): Decoration;
}
class DecorationSet {
static empty: DecorationSet;
static create(doc: Node, decorations: readonly Decoration[]): DecorationSet;
find(start?: number, end?: number, predicate?: (spec: any) => boolean): Decoration[];
map(mapping: Mappable, doc: Node, options?: {onRemove?: (decorationSpec: any) => void}): DecorationSet;
add(doc: Node, decorations: readonly Decoration[]): DecorationSet;
remove(decorations: readonly Decoration[]): DecorationSet;
}Extensible interfaces for custom rendering of document nodes and marks.
interface NodeView {
dom: DOMNode;
contentDOM?: HTMLElement | null;
update?(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
selectNode?(): void;
deselectNode?(): void;
setSelection?(anchor: number, head: number, root: Document | ShadowRoot): void;
stopEvent?(event: Event): boolean;
ignoreMutation?(mutation: ViewMutationRecord): boolean;
destroy?(): void;
}
interface MarkView {
dom: DOMNode;
contentDOM?: HTMLElement | null;
ignoreMutation?(mutation: ViewMutationRecord): boolean;
destroy?(): void;
}Utilities for converting between document positions and DOM coordinates.
class EditorView {
posAtCoords(coords: {left: number, top: number}): {pos: number, inside: number} | null;
coordsAtPos(pos: number, side?: number): {left: number, right: number, top: number, bottom: number};
domAtPos(pos: number, side?: number): {node: DOMNode, offset: number};
nodeDOM(pos: number): DOMNode | null;
posAtDOM(node: DOMNode, offset: number, bias?: number): number;
endOfTextblock(dir: "up" | "down" | "left" | "right" | "forward" | "backward", state?: EditorState): boolean;
}Comprehensive configuration system for customizing editor behavior and handling events.
interface EditorProps<P = any> {
handleDOMEvents?: {[event in keyof DOMEventMap]?: (this: P, view: EditorView, event: DOMEventMap[event]) => boolean | void};
handleKeyDown?(this: P, view: EditorView, event: KeyboardEvent): boolean | void;
handleTextInput?(this: P, view: EditorView, from: number, to: number, text: string, deflt: () => Transaction): boolean | void;
handleClick?(this: P, view: EditorView, pos: number, event: MouseEvent): boolean | void;
handlePaste?(this: P, view: EditorView, event: ClipboardEvent, slice: Slice): boolean | void;
handleDrop?(this: P, view: EditorView, event: DragEvent, slice: Slice, moved: boolean): boolean | void;
nodeViews?: {[node: string]: NodeViewConstructor};
markViews?: {[mark: string]: MarkViewConstructor};
decorations?(this: P, state: EditorState): DecorationSource | null | undefined;
editable?(this: P, state: EditorState): boolean;
attributes?: {[name: string]: string} | ((state: EditorState) => {[name: string]: string});
}
interface DirectEditorProps extends EditorProps {
state: EditorState;
plugins?: readonly Plugin[];
dispatchTransaction?(tr: Transaction): void;
}Comprehensive input processing including keyboard events, mouse interactions, clipboard operations, and composition input.
class EditorView {
pasteHTML(html: string, event?: ClipboardEvent): boolean;
pasteText(text: string, event?: ClipboardEvent): boolean;
serializeForClipboard(slice: Slice): {dom: HTMLElement, text: string, slice: Slice};
dispatchEvent(event: Event): boolean;
}type NodeViewConstructor = (
node: Node,
view: EditorView,
getPos: () => number | undefined,
decorations: readonly Decoration[],
innerDecorations: DecorationSource
) => NodeView;
type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => MarkView;
type WidgetConstructor = ((view: EditorView, getPos: () => number | undefined) => DOMNode) | DOMNode;
type DecorationAttrs = {
nodeName?: string;
class?: string;
style?: string;
[attribute: string]: string | undefined;
};
type ViewMutationRecord = MutationRecord | { type: "selection", target: DOMNode };
interface DOMEventMap extends HTMLElementEventMap {
[event: string]: any;
}
interface DecorationSource {
map(mapping: Mapping, node: Node): DecorationSource;
forChild(offset: number, child: Node): DecorationSource;
locals(node: Node): readonly Decoration[];
eq(other: DecorationSource): boolean;
forEachSet(f: (set: DecorationSet) => void): void;
}