UNPKG

@tiptap/core

Version:

headless rich text editor

1,506 lines (1,497 loc) 164 kB
import { Transaction, EditorState, Plugin, Selection, NodeSelection, TextSelection, PluginKey } from '@tiptap/pm/state'; import { Node as Node$1, MarkType as MarkType$1, MarkSpec, Mark as Mark$1, DOMOutputSpec, NodeType as NodeType$1, NodeSpec, Slice, ParseOptions, Fragment as Fragment$1, Schema, ContentMatch, ResolvedPos, ParseRule } from '@tiptap/pm/model'; import { EditorProps, EditorView, Decoration, DecorationAttrs, ViewMutationRecord, NodeViewConstructor, NodeView as NodeView$1, MarkViewConstructor, MarkView as MarkView$1, DecorationSource } from '@tiptap/pm/view'; import { Transform, Mappable } from '@tiptap/pm/transform'; type StringKeyOf<T> = Extract<keyof T, string>; type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]]; type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any; declare class EventEmitter<T extends Record<string, any>> { private callbacks; on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this; emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): this; off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): this; once<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this; removeAllListeners(): void; } /** * Returns a new `Transform` based on all steps of the passed transactions. * @param oldDoc The Prosemirror node to start from * @param transactions The transactions to combine * @returns A new `Transform` with all steps of the passed transactions */ declare function combineTransactionSteps(oldDoc: Node$1, transactions: Transaction[]): Transform; /** * Takes a Transaction & Editor State and turns it into a chainable state object * @param config The transaction and state to create the chainable state from * @returns A chainable Editor state object */ declare function createChainableState(config: { transaction: Transaction; state: EditorState; }): EditorState; type InputRuleMatch = { index: number; text: string; replaceWith?: string; match?: RegExpMatchArray; data?: Record<string, any>; }; type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null); declare class InputRule { find: InputRuleFinder; handler: (props: { state: EditorState; range: Range; match: ExtendedRegExpMatchArray; commands: SingleCommands; chain: () => ChainedCommands; can: () => CanCommands; }) => void | null; undoable: boolean; constructor(config: { find: InputRuleFinder; handler: (props: { state: EditorState; range: Range; match: ExtendedRegExpMatchArray; commands: SingleCommands; chain: () => ChainedCommands; can: () => CanCommands; }) => void | null; undoable?: boolean; }); } /** * Create an input rules plugin. When enabled, it will cause text * input that matches any of the given rules to trigger the rule’s * action. */ declare function inputRulesPlugin(props: { editor: Editor; rules: InputRule[]; }): Plugin; interface MarkConfig<Options = any, Storage = any> extends ExtendableConfig<Options, Storage, MarkConfig<Options, Storage>, MarkType$1> { /** * Mark View */ addMarkView?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: MarkType$1; parent: ParentConfig<MarkConfig<Options, Storage>>['addMarkView']; }) => MarkViewRenderer) | null; /** * Keep mark after split node */ keepOnSplit?: boolean | (() => boolean); /** * Inclusive */ inclusive?: MarkSpec['inclusive'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive']; editor?: Editor; }) => MarkSpec['inclusive']); /** * Excludes */ excludes?: MarkSpec['excludes'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['excludes']; editor?: Editor; }) => MarkSpec['excludes']); /** * Marks this Mark as exitable */ exitable?: boolean | (() => boolean); /** * Group */ group?: MarkSpec['group'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['group']; editor?: Editor; }) => MarkSpec['group']); /** * Spanning */ spanning?: MarkSpec['spanning'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['spanning']; editor?: Editor; }) => MarkSpec['spanning']); /** * Code */ code?: boolean | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['code']; editor?: Editor; }) => boolean); /** * Parse HTML */ parseHTML?: (this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML']; editor?: Editor; }) => MarkSpec['parseDOM']; /** * Render HTML */ renderHTML?: ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML']; editor?: Editor; }, props: { mark: Mark$1; HTMLAttributes: Record<string, any>; }) => DOMOutputSpec) | null; /** * Attributes */ addAttributes?: (this: { name: string; options: Options; storage: Storage; parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes']; editor?: Editor; }) => Attributes$1 | {}; } /** * The Mark class is used to create custom mark extensions. * @see https://tiptap.dev/api/extensions#create-a-new-extension */ declare class Mark<Options = any, Storage = any> extends Extendable<Options, Storage, MarkConfig<Options, Storage>> { type: string; /** * Create a new Mark instance * @param config - Mark configuration object or a function that returns a configuration object */ static create<O = any, S = any>(config?: Partial<MarkConfig<O, S>> | (() => Partial<MarkConfig<O, S>>)): Mark<O, S>; static handleExit({ editor, mark }: { editor: Editor; mark: Mark; }): boolean; configure(options?: Partial<Options>): Mark<Options, Storage>; extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = MarkConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: (() => Partial<ExtendedConfig>) | (Partial<ExtendedConfig> & ThisType<{ name: string; options: ExtendedOptions; storage: ExtendedStorage; editor: Editor; type: MarkType$1; }>)): Mark<ExtendedOptions, ExtendedStorage>; } interface NodeConfig<Options = any, Storage = any> extends ExtendableConfig<Options, Storage, NodeConfig<Options, Storage>, NodeType$1> { /** * Node View */ addNodeView?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: NodeType$1; parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']; }) => NodeViewRenderer | null) | null; /** * Defines if this node should be a top level node (doc) * @default false * @example true */ topNode?: boolean; /** * The content expression for this node, as described in the [schema * guide](/docs/guide/#schema.content_expressions). When not given, * the node does not allow any content. * * You can read more about it on the Prosemirror documentation here * @see https://prosemirror.net/docs/guide/#schema.content_expressions * @default undefined * @example content: 'block+' * @example content: 'headline paragraph block*' */ content?: NodeSpec['content'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['content']; editor?: Editor; }) => NodeSpec['content']); /** * The marks that are allowed inside of this node. May be a * space-separated string referring to mark names or groups, `"_"` * to explicitly allow all marks, or `""` to disallow marks. When * not given, nodes with inline content default to allowing all * marks, other nodes default to not allowing marks. * * @example marks: 'strong em' */ marks?: NodeSpec['marks'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['marks']; editor?: Editor; }) => NodeSpec['marks']); /** * The group or space-separated groups to which this node belongs, * which can be referred to in the content expressions for the * schema. * * By default Tiptap uses the groups 'block' and 'inline' for nodes. You * can also use custom groups if you want to group specific nodes together * and handle them in your schema. * @example group: 'block' * @example group: 'inline' * @example group: 'customBlock' // this uses a custom group */ group?: NodeSpec['group'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['group']; editor?: Editor; }) => NodeSpec['group']); /** * Should be set to true for inline nodes. (Implied for text nodes.) */ inline?: NodeSpec['inline'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['inline']; editor?: Editor; }) => NodeSpec['inline']); /** * Can be set to true to indicate that, though this isn't a [leaf * node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable * content and should be treated as a single unit in the view. * * @example atom: true */ atom?: NodeSpec['atom'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['atom']; editor?: Editor; }) => NodeSpec['atom']); /** * Controls whether nodes of this type can be selected as a [node * selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text * nodes. * * @default true * @example selectable: false */ selectable?: NodeSpec['selectable'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']; editor?: Editor; }) => NodeSpec['selectable']); /** * Determines whether nodes of this type can be dragged without * being selected. Defaults to false. * * @default: false * @example: draggable: true */ draggable?: NodeSpec['draggable'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']; editor?: Editor; }) => NodeSpec['draggable']); /** * Can be used to indicate that this node contains code, which * causes some commands to behave differently. */ code?: NodeSpec['code'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['code']; editor?: Editor; }) => NodeSpec['code']); /** * Controls way whitespace in this a node is parsed. The default is * `"normal"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to * collapse whitespace in normal mode, and normalize it (replacing * newlines and such with spaces) otherwise. `"pre"` causes the * parser to preserve spaces inside the node. When this option isn't * given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace` * will default to `"pre"`. Note that this option doesn't influence * the way the node is rendered—that should be handled by `toDOM` * and/or styling. */ whitespace?: NodeSpec['whitespace'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']; editor?: Editor; }) => NodeSpec['whitespace']); /** * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak). * When converting between block types that have whitespace set to "pre" * and don't support the linebreak node (e.g. codeBlock) and other block types * that do support the linebreak node (e.g. paragraphs) - this node will be used * as the linebreak instead of stripping the newline. * * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement). */ linebreakReplacement?: NodeSpec['linebreakReplacement'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']; editor?: Editor; }) => NodeSpec['linebreakReplacement']); /** * When enabled, enables both * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and * [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent). * * @default false * @example isolating: true */ defining?: NodeSpec['defining'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['defining']; editor?: Editor; }) => NodeSpec['defining']); /** * When enabled (default is false), the sides of nodes of this type * count as boundaries that regular editing operations, like * backspacing or lifting, won't cross. An example of a node that * should probably have this enabled is a table cell. */ isolating?: NodeSpec['isolating'] | ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']; editor?: Editor; }) => NodeSpec['isolating']); /** * Associates DOM parser information with this node, which can be * used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to * automatically derive a parser. The `node` field in the rules is * implied (the name of this node will be filled in automatically). * If you supply your own parser, you do not need to also specify * parsing rules in your schema. * * @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }] */ parseHTML?: (this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']; editor?: Editor; }) => NodeSpec['parseDOM']; /** * A description of a DOM structure. Can be either a string, which is * interpreted as a text node, a DOM node, which is interpreted as * itself, a `{dom, contentDOM}` object, or an array. * * An array describes a DOM element. The first value in the array * should be a string—the name of the DOM element, optionally prefixed * by a namespace URL and a space. If the second element is plain * object, it is interpreted as a set of attributes for the element. * Any elements after that (including the 2nd if it's not an attribute * object) are interpreted as children of the DOM elements, and must * either be valid `DOMOutputSpec` values, or the number zero. * * The number zero (pronounced “hole”) is used to indicate the place * where a node's child nodes should be inserted. If it occurs in an * output spec, it should be the only child element in its parent * node. * * @example toDOM: ['div[data-id="my-block"]', { class: 'my-block' }, 0] */ renderHTML?: ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']; editor?: Editor; }, props: { node: Node$1; HTMLAttributes: Record<string, any>; }) => DOMOutputSpec) | null; /** * renders the node as text * @example renderText: () => 'foo */ renderText?: ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']; editor?: Editor; }, props: { node: Node$1; pos: number; parent: Node$1; index: number; }) => string) | null; /** * Add attributes to the node * @example addAttributes: () => ({ class: 'foo' }) */ addAttributes?: (this: { name: string; options: Options; storage: Storage; parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']; editor?: Editor; }) => Attributes$1 | {}; } /** * The Node class is used to create custom node extensions. * @see https://tiptap.dev/api/extensions#create-a-new-extension */ declare class Node<Options = any, Storage = any> extends Extendable<Options, Storage, NodeConfig<Options, Storage>> { type: string; /** * Create a new Node instance * @param config - Node configuration object or a function that returns a configuration object */ static create<O = any, S = any>(config?: Partial<NodeConfig<O, S>> | (() => Partial<NodeConfig<O, S>>)): Node<O, S>; configure(options?: Partial<Options>): Node<Options, Storage>; extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = NodeConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: (() => Partial<ExtendedConfig>) | (Partial<ExtendedConfig> & ThisType<{ name: string; options: ExtendedOptions; storage: ExtendedStorage; editor: Editor; type: NodeType$1; }>)): Node<ExtendedOptions, ExtendedStorage>; } type PasteRuleMatch = { index: number; text: string; replaceWith?: string; match?: RegExpMatchArray; data?: Record<string, any>; }; type PasteRuleFinder = RegExp | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined); /** * Paste rules are used to react to pasted content. * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules */ declare class PasteRule { find: PasteRuleFinder; handler: (props: { state: EditorState; range: Range; match: ExtendedRegExpMatchArray; commands: SingleCommands; chain: () => ChainedCommands; can: () => CanCommands; pasteEvent: ClipboardEvent | null; dropEvent: DragEvent | null; }) => void | null; constructor(config: { find: PasteRuleFinder; handler: (props: { can: () => CanCommands; chain: () => ChainedCommands; commands: SingleCommands; dropEvent: DragEvent | null; match: ExtendedRegExpMatchArray; pasteEvent: ClipboardEvent | null; range: Range; state: EditorState; }) => void | null; }); } /** * Create an paste rules plugin. When enabled, it will cause pasted * text that matches any of the given rules to trigger the rule’s * action. */ declare function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[]; }): Plugin[]; interface ExtendableConfig<Options = any, Storage = any, Config extends ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage> | ExtendableConfig<Options, Storage> = ExtendableConfig<Options, Storage, any, any>, PMType = any> { /** * The extension name - this must be unique. * It will be used to identify the extension. * * @example 'myExtension' */ name: string; /** * The priority of your extension. The higher, the earlier it will be called * and will take precedence over other extensions with a lower priority. * @default 100 * @example 101 */ priority?: number; /** * This method will add options to this extension * @see https://tiptap.dev/docs/editor/guide/custom-extensions#settings * @example * addOptions() { * return { * myOption: 'foo', * myOtherOption: 10, * } */ addOptions?: (this: { name: string; parent: ParentConfig<Config>['addOptions']; }) => Options; /** * The default storage this extension can save data to. * @see https://tiptap.dev/docs/editor/guide/custom-extensions#storage * @example * defaultStorage: { * prefetchedUsers: [], * loading: false, * } */ addStorage?: (this: { name: string; options: Options; parent: ParentConfig<Config>['addStorage']; }) => Storage; /** * This function adds globalAttributes to specific nodes. * @see https://tiptap.dev/docs/editor/guide/custom-extensions#global-attributes * @example * addGlobalAttributes() { * return [ * { // Extend the following extensions * types: [ * 'heading', * 'paragraph', * ], * // … with those attributes * attributes: { * textAlign: { * default: 'left', * renderHTML: attributes => ({ * style: `text-align: ${attributes.textAlign}`, * }), * parseHTML: element => element.style.textAlign || 'left', * }, * }, * }, * ] * } */ addGlobalAttributes?: (this: { name: string; options: Options; storage: Storage; extensions: (Node | Mark)[]; parent: ParentConfig<Config>['addGlobalAttributes']; }) => GlobalAttributes; /** * This function adds commands to the editor * @see https://tiptap.dev/docs/editor/guide/custom-extensions#commands * @example * addCommands() { * return { * myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(), * } * } */ addCommands?: (this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['addCommands']; }) => Partial<RawCommands>; /** * This function registers keyboard shortcuts. * @see https://tiptap.dev/docs/editor/guide/custom-extensions#keyboard-shortcuts * @example * addKeyboardShortcuts() { * return { * 'Mod-l': () => this.editor.commands.toggleBulletList(), * } * }, */ addKeyboardShortcuts?: (this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['addKeyboardShortcuts']; }) => { [key: string]: KeyboardShortcutCommand; }; /** * This function adds input rules to the editor. * @see https://tiptap.dev/docs/editor/guide/custom-extensions#input-rules * @example * addInputRules() { * return [ * markInputRule({ * find: inputRegex, * type: this.type, * }), * ] * }, */ addInputRules?: (this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['addInputRules']; }) => InputRule[]; /** * This function adds paste rules to the editor. * @see https://tiptap.dev/docs/editor/guide/custom-extensions#paste-rules * @example * addPasteRules() { * return [ * markPasteRule({ * find: pasteRegex, * type: this.type, * }), * ] * }, */ addPasteRules?: (this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['addPasteRules']; }) => PasteRule[]; /** * This function adds Prosemirror plugins to the editor * @see https://tiptap.dev/docs/editor/guide/custom-extensions#prosemirror-plugins * @example * addProseMirrorPlugins() { * return [ * customPlugin(), * ] * } */ addProseMirrorPlugins?: (this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['addProseMirrorPlugins']; }) => Plugin[]; /** * This function adds additional extensions to the editor. This is useful for * building extension kits. * @example * addExtensions() { * return [ * BulletList, * OrderedList, * ListItem * ] * } */ addExtensions?: (this: { name: string; options: Options; storage: Storage; parent: ParentConfig<Config>['addExtensions']; }) => Extensions; /** * The markdown token name * * This is the name of the token that this extension uses to parse and render markdown and comes from the Marked Lexer. * * @see https://github.com/markedjs/marked/blob/master/src/Tokens.ts * */ markdownTokenName?: string; /** * The parse function used by the markdown parser to convert markdown tokens to ProseMirror nodes. */ parseMarkdown?: (token: MarkdownToken, helpers: MarkdownParseHelpers) => MarkdownParseResult; /** * The serializer function used by the markdown serializer to convert ProseMirror nodes to markdown tokens. */ renderMarkdown?: (node: JSONContent, helpers: MarkdownRendererHelpers, ctx: RenderContext) => string; /** * The markdown tokenizer responsible for turning a markdown string into tokens * * Custom tokenizers are only needed when you want to parse non-standard markdown token. */ markdownTokenizer?: MarkdownTokenizer; /** * Optional markdown options for indentation */ markdownOptions?: { /** * Defines if this markdown element should indent it's child elements */ indentsContent?: boolean; }; /** * This function extends the schema of the node. * @example * extendNodeSchema() { * return { * group: 'inline', * selectable: false, * } * } */ extendNodeSchema?: ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<Config>['extendNodeSchema']; }, extension: Node) => Record<string, any>) | null; /** * This function extends the schema of the mark. * @example * extendMarkSchema() { * return { * group: 'inline', * selectable: false, * } * } */ extendMarkSchema?: ((this: { name: string; options: Options; storage: Storage; parent: ParentConfig<Config>['extendMarkSchema']; }, extension: Mark) => Record<string, any>) | null; /** * The editor is not ready yet. */ onBeforeCreate?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onBeforeCreate']; }, event: EditorEvents['beforeCreate']) => void) | null; /** * The editor is ready. */ onCreate?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onCreate']; }, event: EditorEvents['create']) => void) | null; /** * The content has changed. */ onUpdate?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onUpdate']; }, event: EditorEvents['update']) => void) | null; /** * The selection has changed. */ onSelectionUpdate?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onSelectionUpdate']; }, event: EditorEvents['selectionUpdate']) => void) | null; /** * The editor state has changed. */ onTransaction?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onTransaction']; }, event: EditorEvents['transaction']) => void) | null; /** * The editor is focused. */ onFocus?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onFocus']; }, event: EditorEvents['focus']) => void) | null; /** * The editor isn’t focused anymore. */ onBlur?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onBlur']; }, event: EditorEvents['blur']) => void) | null; /** * The editor is destroyed. */ onDestroy?: ((this: { name: string; options: Options; storage: Storage; editor: Editor; type: PMType; parent: ParentConfig<Config>['onDestroy']; }, event: EditorEvents['destroy']) => void) | null; } declare class Extendable<Options = any, Storage = any, Config = ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>> { type: string; parent: Extendable | null; child: Extendable | null; name: string; config: Config; constructor(config?: Partial<Config>); get options(): Options; get storage(): Readonly<Storage>; configure(options?: Partial<Options>): Extendable<Options, Storage, ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>>; extend<ExtendedOptions = Options, ExtendedStorage = Storage, ExtendedConfig = ExtensionConfig<ExtendedOptions, ExtendedStorage> | NodeConfig<ExtendedOptions, ExtendedStorage> | MarkConfig<ExtendedOptions, ExtendedStorage>>(extendedConfig?: Partial<ExtendedConfig>): Extendable<ExtendedOptions, ExtendedStorage>; } type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig; type AnyExtension = Extendable; type Extensions = AnyExtension[]; type ParentConfig<T> = Partial<{ [P in keyof T]: Required<T>[P] extends (...args: any) => any ? (...args: Parameters<Required<T>[P]>) => ReturnType<Required<T>[P]> : T[P]; }>; type Primitive = null | undefined | string | number | boolean | symbol | bigint; type RemoveThis<T> = T extends (...args: any) => any ? (...args: Parameters<T>) => ReturnType<T> : T; type MaybeReturnType<T> = T extends (...args: any) => any ? ReturnType<T> : T; type MaybeThisParameterType<T> = Exclude<T, Primitive> extends (...args: any) => any ? ThisParameterType<Exclude<T, Primitive>> : any; interface EditorEvents { mount: { /** * The editor instance */ editor: Editor; }; unmount: { /** * The editor instance */ editor: Editor; }; beforeCreate: { /** * The editor instance */ editor: Editor; }; create: { /** * The editor instance */ editor: Editor; }; contentError: { /** * The editor instance */ editor: Editor; /** * The error that occurred while parsing the content */ error: Error; /** * If called, will re-initialize the editor with the collaboration extension removed. * This will prevent syncing back deletions of content not present in the current schema. */ disableCollaboration: () => void; }; update: { /** * The editor instance */ editor: Editor; /** * The transaction that caused the update */ transaction: Transaction; /** * Appended transactions that were added to the initial transaction by plugins */ appendedTransactions: Transaction[]; }; selectionUpdate: { /** * The editor instance */ editor: Editor; /** * The transaction that caused the selection update */ transaction: Transaction; }; beforeTransaction: { /** * The editor instance */ editor: Editor; /** * The transaction that will be applied */ transaction: Transaction; /** * The next state of the editor after the transaction is applied */ nextState: EditorState; }; transaction: { /** * The editor instance */ editor: Editor; /** * The initial transaction */ transaction: Transaction; /** * Appended transactions that were added to the initial transaction by plugins */ appendedTransactions: Transaction[]; }; focus: { /** * The editor instance */ editor: Editor; /** * The focus event */ event: FocusEvent; /** * The transaction that caused the focus */ transaction: Transaction; }; blur: { /** * The editor instance */ editor: Editor; /** * The focus event */ event: FocusEvent; /** * The transaction that caused the blur */ transaction: Transaction; }; destroy: void; paste: { /** * The editor instance */ editor: Editor; /** * The clipboard event */ event: ClipboardEvent; /** * The slice that was pasted */ slice: Slice; }; drop: { /** * The editor instance */ editor: Editor; /** * The drag event */ event: DragEvent; /** * The slice that was dropped */ slice: Slice; /** * Whether the content was moved (true) or copied (false) */ moved: boolean; }; delete: { /** * The editor instance */ editor: Editor; /** * The range of the deleted content (before the deletion) */ deletedRange: Range; /** * The new range of positions of where the deleted content was in the new document (after the deletion) */ newRange: Range; /** * The transaction that caused the deletion */ transaction: Transaction; /** * The combined transform (including all appended transactions) that caused the deletion */ combinedTransform: Transform; /** * Whether the deletion was partial (only a part of this content was deleted) */ partial: boolean; /** * This is the start position of the mark in the document (before the deletion) */ from: number; /** * This is the end position of the mark in the document (before the deletion) */ to: number; } & ({ /** * The content that was deleted */ type: 'node'; /** * The node which the deletion occurred in * @note This can be a parent node of the deleted content */ node: Node$1; /** * The new start position of the node in the document (after the deletion) */ newFrom: number; /** * The new end position of the node in the document (after the deletion) */ newTo: number; } | { /** * The content that was deleted */ type: 'mark'; /** * The mark that was deleted */ mark: Mark$1; }); } type EnableRules = (AnyExtension | string)[] | boolean; interface EditorOptions { /** * The element to bind the editor to: * - If an `Element` is passed, the editor will be mounted appended to that element * - If `null` is passed, the editor will not be mounted automatically * - If an object with a `mount` property is passed, the editor will be mounted to that element * - If a function is passed, it will be called with the editor's element, which should place the editor within the document */ element: Element | { mount: HTMLElement; } | ((editor: HTMLElement) => void) | null; /** * The content of the editor (HTML, JSON, or a JSON array) */ content: Content; /** * The extensions to use */ extensions: Extensions; /** * Whether to inject base CSS styles */ injectCSS: boolean; /** * A nonce to use for CSP while injecting styles */ injectNonce: string | undefined; /** * The editor's initial focus position */ autofocus: FocusPosition; /** * Whether the editor is editable */ editable: boolean; /** * The editor's props */ editorProps: EditorProps; /** * The editor's content parser options */ parseOptions: ParseOptions; /** * The editor's core extension options */ coreExtensionOptions?: { clipboardTextSerializer?: { blockSeparator?: string; }; delete?: { /** * Whether the `delete` extension should be called asynchronously to avoid blocking the editor while processing deletions * @default true deletion events are called asynchronously */ async?: boolean; /** * Allows filtering the transactions that are processed by the `delete` extension. * If the function returns `true`, the transaction will be ignored. */ filterTransaction?: (transaction: Transaction) => boolean; }; }; /** * Whether to enable input rules behavior */ enableInputRules: EnableRules; /** * Whether to enable paste rules behavior */ enablePasteRules: EnableRules; /** * Determines whether core extensions are enabled. * * If set to `false`, all core extensions will be disabled. * To disable specific core extensions, provide an object where the keys are the extension names and the values are `false`. * Extensions not listed in the object will remain enabled. * * @example * // Disable all core extensions * enabledCoreExtensions: false * * @example * // Disable only the keymap core extension * enabledCoreExtensions: { keymap: false } * * @default true */ enableCoreExtensions?: boolean | Partial<Record<'editable' | 'clipboardTextSerializer' | 'commands' | 'focusEvents' | 'keymap' | 'tabindex' | 'drop' | 'paste' | 'delete', false>>; /** * If `true`, the editor will check the content for errors on initialization. * Emitting the `contentError` event if the content is invalid. * Which can be used to show a warning or error message to the user. * @default false */ enableContentCheck: boolean; /** * If `true`, the editor will emit the `contentError` event if invalid content is * encountered but `enableContentCheck` is `false`. This lets you preserve the * invalid editor content while still showing a warning or error message to * the user. * * @default false */ emitContentError: boolean; /** * Called before the editor is constructed. */ onBeforeCreate: (props: EditorEvents['beforeCreate']) => void; /** * Called after the editor is constructed. */ onCreate: (props: EditorEvents['create']) => void; /** * Called when the editor is mounted. */ onMount: (props: EditorEvents['mount']) => void; /** * Called when the editor is unmounted. */ onUnmount: (props: EditorEvents['unmount']) => void; /** * Called when the editor encounters an error while parsing the content. * Only enabled if `enableContentCheck` is `true`. */ onContentError: (props: EditorEvents['contentError']) => void; /** * Called when the editor's content is updated. */ onUpdate: (props: EditorEvents['update']) => void; /** * Called when the editor's selection is updated. */ onSelectionUpdate: (props: EditorEvents['selectionUpdate']) => void; /** * Called after a transaction is applied to the editor. */ onTransaction: (props: EditorEvents['transaction']) => void; /** * Called on focus events. */ onFocus: (props: EditorEvents['focus']) => void; /** * Called on blur events. */ onBlur: (props: EditorEvents['blur']) => void; /** * Called when the editor is destroyed. */ onDestroy: (props: EditorEvents['destroy']) => void; /** * Called when content is pasted into the editor. */ onPaste: (e: ClipboardEvent, slice: Slice) => void; /** * Called when content is dropped into the editor. */ onDrop: (e: DragEvent, slice: Slice, moved: boolean) => void; /** * Called when content is deleted from the editor. */ onDelete: (props: EditorEvents['delete']) => void; } /** * The editor's content as HTML */ type HTMLContent = string; /** * Loosely describes a JSON representation of a Prosemirror document or node */ type JSONContent = { type?: string; attrs?: Record<string, any> | undefined; content?: JSONContent[]; marks?: { type: string; attrs?: Record<string, any>; [key: string]: any; }[]; text?: string; [key: string]: any; }; /** * A mark type is either a JSON representation of a mark or a Prosemirror mark instance */ type MarkType<Type extends string | { name: string; } = any, TAttributes extends undefined | Record<string, any> = any> = { type: Type; attrs: TAttributes; }; /** * A node type is either a JSON representation of a node or a Prosemirror node instance */ type NodeType<Type extends string | { name: string; } = any, TAttributes extends undefined | Record<string, any> = any, NodeMarkType extends MarkType = any, TContent extends (NodeType | TextType)[] = any> = { type: Type; attrs: TAttributes; content?: TContent; marks?: NodeMarkType[]; }; /** * A node type is either a JSON representation of a doc node or a Prosemirror doc node instance */ type DocumentType<TDocAttributes extends Record<string, any> | undefined = Record<string, any>, TContentType extends NodeType[] = NodeType[]> = Omit<NodeType<'doc', TDocAttributes, never, TContentType>, 'marks' | 'content'> & { content: TContentType; }; /** * A node type is either a JSON representation of a text node or a Prosemirror text node instance */ type TextType<TMarkType extends MarkType = MarkType> = { type: 'text'; text: string; marks: TMarkType[]; }; /** * Describes the output of a `renderHTML` function in prosemirror * @see https://prosemirror.net/docs/ref/#model.DOMOutputSpec */ type DOMOutputSpecArray$1 = [string] | [string, Record<string, any>] | [string, 0] | [string, Record<string, any>, 0] | [string, Record<string, any>, DOMOutputSpecArray$1 | 0] | [string, DOMOutputSpecArray$1]; type Content = HTMLContent | JSONContent | JSONContent[] | null; type CommandProps = { editor: Editor; tr: Transaction; commands: SingleCommands; can: () => CanCommands; chain: () => ChainedCommands; state: EditorState; view: EditorView; dispatch: ((args?: any) => any) | undefined; }; type Command = (props: CommandProps) => boolean; type CommandSpec = (...args: any[]) => Command; type KeyboardShortcutCommand = (props: { editor: Editor; }) => boolean; type Attribute = { default?: any; validate?: string | ((value: any) => void); rendered?: boolean; renderHTML?: ((attributes: Record<string, any>) => Record<string, any> | null) | null; parseHTML?: ((element: HTMLElement) => any | null) | null; keepOnSplit?: boolean; isRequired?: boolean; }; type Attributes$1 = { [key: string]: Attribute; }; type ExtensionAttribute = { type: string; name: string; attribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'>; }; type GlobalAttributes = { /** * The node & mark types this attribute should be applied to. */ types: string[]; /** * The attributes to add to the node or mark types. */ attributes: Record<string, Attribute | undefined>; }[]; type PickValue<T, K extends keyof T> = T[K]; type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never; type Diff<T extends keyof any, U extends keyof any> = ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]; type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U; type ValuesOf<T> = T[keyof T]; type KeysWithTypeOf<T, Type> = { [P in keyof T]: T[P] extends Type ? P : never; }[keyof T]; type DOMNode = InstanceType<typeof window.Node>; /** * prosemirror-view does not export the `type` property of `Decoration`. * So, this defines the `DecorationType` interface to include the `type` property. */ interface DecorationType { spec: any; map(mapping: Mappable, span: Decoration, offset: number, oldOffset: number): Decoration | null; valid(node: Node, span: Decoration): boolean; eq(other: DecorationType): boolean; destroy(dom: DOMNode): void; readonly attrs: DecorationAttrs; } /** * prosemirror-view does not export the `type` property of `Decoration`. * This adds the `type` property to the `Decoration` type. */ type DecorationWithType = Decoration & { type: DecorationType; }; interface NodeViewProps extends NodeViewRendererProps { decorations: readonly DecorationWithType[]; selected: boolean; updateAttributes: (attributes: Record<string, any>) => void; deleteNode: () => void; } interface NodeViewRendererOptions { stopEvent: ((props: { event: Event; }) => boolean) | null; ignoreMutation: ((props: { mutation: ViewMutationRecord; }) => boolean) | null; contentDOMElementTag: string; } interface NodeViewRendererProps { /** * The node that is being rendered. */ node: Parameters<NodeViewConstructor>[0]; /** * The editor's view. */ view: Parameters<NodeViewConstructor>[1]; /** * A function that can be called to get the node's current position in the document. */ getPos: Parameters<NodeViewConstructor>[2]; /** * is an array of node or inline decorations that are active around the node. * They are automatically drawn in the normal way, and you will usually just want to ignore this, but they can also be used as a way to provide context information to the node view without adding it to the document itself. */ decorations: Parameters<NodeViewConstructor>[3]; /** * holds the decorations for the node's content. You can safely ignore this if your view has no content or a contentDOM property, since the editor will draw the decorations on the content. * But if you, for example, want to create a nested editor with the content, it may make sense to provide it with the inner decorations. */ innerDecorations: Parameters<NodeViewConstructor>[4]; /** * The editor instance. */ editor: Editor; /** * The extension that is responsible for the node. */ extension: Node; /** * The HTML attributes that should be added to the node's DOM element. */ HTMLAttributes: Record<string, any>; } type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView$1; interface MarkViewProps extends MarkViewRendererProps { } interface MarkViewRendererProps { /** * The node that is being rendered. */ mark: Parameters<MarkViewConstructor>[0]; /** * The editor's view. */ view: Parameters<MarkViewConstructor>[1]; /** * indicates whether the mark's content is inline */ inline: Parameters<MarkViewConstructor>[2]; /** * The editor instance. */ editor: Editor; /** * The extension that is responsible for the mark. */ extension: Mark; /** * The HTML attribut