import { DOMOutputSpec, NodeSpec, Node as ProseMirrorNode, NodeType, } from 'prosemirror-model' import { Command as ProseMirrorCommand } from 'prosemirror-commands' import { Plugin, Transaction } from 'prosemirror-state' import { InputRule } from 'prosemirror-inputrules' import mergeDeep from './utilities/mergeDeep' import { Attributes, NodeViewRenderer, GlobalAttributes, RawCommands, ParentConfig, } from './types' import { NodeConfig } from '.' import { Editor } from './Editor' declare module '@tiptap/core' { interface NodeConfig { [key: string]: any; /** * Name */ name: string, /** * Priority */ priority?: number, /** * Default options */ defaultOptions?: Options, /** * Global attributes */ addGlobalAttributes?: (this: { options: Options, parentConfig: ParentConfig>, }) => GlobalAttributes | {}, /** * Raw */ addCommands?: (this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => Partial, /** * Keyboard shortcuts */ addKeyboardShortcuts?: (this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => { [key: string]: ProseMirrorCommand, }, /** * Input rules */ addInputRules?: (this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => InputRule[], /** * Paste rules */ addPasteRules?: (this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => Plugin[], /** * ProseMirror plugins */ addProseMirrorPlugins?: (this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => Plugin[], /** * Extend Node Schema */ extendNodeSchema?: (( this: { options: Options, parentConfig: ParentConfig>, }, extension: Node, ) => { [key: string]: any, }) | null, /** * Extend Mark Schema */ extendMarkSchema?: (( this: { options: Options, parentConfig: ParentConfig>, }, extension: Node, ) => { [key: string]: any, }) | null, /** * The editor is not ready yet. */ onBeforeCreate?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => void) | null, /** * The editor is ready. */ onCreate?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => void) | null, /** * The content has changed. */ onUpdate?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => void) | null, /** * The selection has changed. */ onSelectionUpdate?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => void) | null, /** * The editor state has changed. */ onTransaction?: (( this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }, props: { transaction: Transaction, }, ) => void) | null, /** * The editor is focused. */ onFocus?: (( this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }, props: { event: FocusEvent, }, ) => void) | null, /** * The editor isn’t focused anymore. */ onBlur?: (( this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }, props: { event: FocusEvent, }, ) => void) | null, /** * The editor is destroyed. */ onDestroy?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => void) | null, /** * Node View */ addNodeView?: ((this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }) => NodeViewRenderer) | null, /** * TopNode */ topNode?: boolean, /** * Content */ content?: NodeSpec['content'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['content']), /** * Marks */ marks?: NodeSpec['marks'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['marks']), /** * Group */ group?: NodeSpec['group'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['group']), /** * Inline */ inline?: NodeSpec['inline'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['inline']), /** * Atom */ atom?: NodeSpec['atom'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['atom']), /** * Selectable */ selectable?: NodeSpec['selectable'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['selectable']), /** * Draggable */ draggable?: NodeSpec['draggable'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['draggable']), /** * Code */ code?: NodeSpec['code'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['code']), /** * Defining */ defining?: NodeSpec['defining'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['defining']), /** * Isolating */ isolating?: NodeSpec['isolating'] | ((this: { options: Options, parentConfig: ParentConfig>, }) => NodeSpec['isolating']), /** * Parse HTML */ parseHTML?: ( this: { options: Options, parentConfig: ParentConfig>, }, ) => NodeSpec['parseDOM'], /** * Render HTML */ renderHTML?: (( this: { options: Options, parentConfig: ParentConfig>, }, props: { node: ProseMirrorNode, HTMLAttributes: { [key: string]: any }, } ) => DOMOutputSpec) | null, /** * Render Text */ renderText?: (( this: { options: Options, editor: Editor, type: NodeType, parentConfig: ParentConfig>, }, props: { node: ProseMirrorNode, } ) => string) | null, /** * Add Attributes */ addAttributes?: ( this: { options: Options, parentConfig: ParentConfig>, }, ) => Attributes | {}, } } export class Node { type = 'node' config: NodeConfig = { name: 'node', priority: 100, defaultOptions: {}, } // parentConfig: Partial = {} parent: any options!: Options constructor(config: NodeConfig) { this.config = { ...this.config, ...config, } this.options = this.config.defaultOptions } static create(config: NodeConfig) { return new Node(config) } configure(options: Partial = {}) { return Node .create(this.config as NodeConfig) .#configure(options) } #configure = (options: Partial) => { this.options = mergeDeep(this.config.defaultOptions, options) as Options return this } extend(extendedConfig: Partial>) { const extension = new Node({ ...this.config, ...extendedConfig, } as NodeConfig) extension.parent = this // extension.parentConfig = this.config return extension } }