feat: Add extension storage (#2069)

This commit is contained in:
Philipp Kühn
2021-10-22 08:52:54 +02:00
committed by GitHub
parent 6987505fda
commit 7ffabf251c
26 changed files with 555 additions and 105 deletions

View File

@@ -50,6 +50,8 @@ export class Editor extends EventEmitter<EditorEvents> {
public isFocused = false
public extensionStorage: Record<string, any> = {}
public options: EditorOptions = {
element: document.createElement('div'),
content: '',
@@ -100,6 +102,13 @@ export class Editor extends EventEmitter<EditorEvents> {
}, 0)
}
/**
* Returns the editor storage.
*/
public get storage(): Record<string, any> {
return this.extensionStorage
}
/**
* An object of all registered commands.
*/

View File

@@ -5,7 +5,10 @@ import { Editor } from './Editor'
import { Node } from './Node'
import { Mark } from './Mark'
import mergeDeep from './utilities/mergeDeep'
import callOrReturn from './utilities/callOrReturn'
import getExtensionField from './helpers/getExtensionField'
import {
AnyConfig,
Extensions,
GlobalAttributes,
RawCommands,
@@ -15,7 +18,7 @@ import {
import { ExtensionConfig } from '.'
declare module '@tiptap/core' {
interface ExtensionConfig<Options = any> {
interface ExtensionConfig<Options = any, Storage = any> {
[key: string]: any;
/**
@@ -33,13 +36,23 @@ declare module '@tiptap/core' {
*/
defaultOptions?: Options,
/**
* Default Storage
*/
addStorage?: (this: {
name: string,
options: Options,
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
}) => Storage,
/**
* Global attributes
*/
addGlobalAttributes?: (this: {
name: string,
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['addGlobalAttributes'],
storage: Storage,
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@@ -48,8 +61,9 @@ declare module '@tiptap/core' {
addCommands?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addCommands'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands'],
}) => Partial<RawCommands>,
/**
@@ -58,8 +72,9 @@ declare module '@tiptap/core' {
addKeyboardShortcuts?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addKeyboardShortcuts'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts'],
}) => {
[key: string]: KeyboardShortcutCommand,
},
@@ -70,8 +85,9 @@ declare module '@tiptap/core' {
addInputRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addInputRules'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules'],
}) => InputRule[],
/**
@@ -80,8 +96,9 @@ declare module '@tiptap/core' {
addPasteRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addPasteRules'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules'],
}) => PasteRule[],
/**
@@ -90,8 +107,9 @@ declare module '@tiptap/core' {
addProseMirrorPlugins?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['addProseMirrorPlugins'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@@ -100,7 +118,8 @@ declare module '@tiptap/core' {
addExtensions?: (this: {
name: string,
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['addExtensions'],
storage: Storage,
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions'],
}) => Extensions,
/**
@@ -110,7 +129,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['extendNodeSchema'],
storage: Storage,
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema'],
},
extension: Node,
) => Record<string, any>) | null,
@@ -122,7 +142,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<ExtensionConfig<Options>>['extendMarkSchema'],
storage: Storage,
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema'],
},
extension: Mark,
) => Record<string, any>) | null,
@@ -133,8 +154,9 @@ declare module '@tiptap/core' {
onBeforeCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onBeforeCreate'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate'],
}) => void) | null,
/**
@@ -143,8 +165,9 @@ declare module '@tiptap/core' {
onCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onCreate'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate'],
}) => void) | null,
/**
@@ -153,8 +176,9 @@ declare module '@tiptap/core' {
onUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onUpdate'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate'],
}) => void) | null,
/**
@@ -163,8 +187,9 @@ declare module '@tiptap/core' {
onSelectionUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onSelectionUpdate'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate'],
}) => void) | null,
/**
@@ -174,8 +199,9 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onTransaction'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction'],
},
props: {
transaction: Transaction,
@@ -189,8 +215,9 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onFocus'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus'],
},
props: {
event: FocusEvent,
@@ -204,8 +231,9 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onBlur'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur'],
},
props: {
event: FocusEvent,
@@ -218,13 +246,14 @@ declare module '@tiptap/core' {
onDestroy?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
parent: ParentConfig<ExtensionConfig<Options>>['onDestroy'],
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy'],
}) => void) | null,
}
}
export class Extension<Options = any> {
export class Extension<Options = any, Storage = any> {
type = 'extension'
name = 'extension'
@@ -235,12 +264,14 @@ export class Extension<Options = any> {
options: Options
storage: Storage
config: ExtensionConfig = {
name: this.name,
defaultOptions: {},
}
constructor(config: Partial<ExtensionConfig<Options>> = {}) {
constructor(config: Partial<ExtensionConfig<Options, Storage>> = {}) {
this.config = {
...this.config,
...config,
@@ -248,10 +279,18 @@ export class Extension<Options = any> {
this.name = this.config.name
this.options = this.config.defaultOptions
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
this,
'addStorage',
{
name: this.name,
options: this.options,
},
))
}
static create<O>(config: Partial<ExtensionConfig<O>> = {}) {
return new Extension<O>(config)
static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
return new Extension<O, S>(config)
}
configure(options: Partial<Options> = {}) {
@@ -264,8 +303,8 @@ export class Extension<Options = any> {
return extension
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>> = {}) {
const extension = new Extension<ExtendedOptions>(extendedConfig)
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions, ExtendedStorage>> = {}) {
const extension = new Extension<ExtendedOptions, ExtendedStorage>(extendedConfig)
extension.parent = this
@@ -279,6 +318,15 @@ export class Extension<Options = any> {
? extendedConfig.defaultOptions
: extension.parent.options
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
extension,
'addStorage',
{
name: extension.name,
options: extension.options,
},
))
return extension
}
}

View File

@@ -14,6 +14,7 @@ import splitExtensions from './helpers/splitExtensions'
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
import getRenderedAttributes from './helpers/getRenderedAttributes'
import callOrReturn from './utilities/callOrReturn'
import findDuplicates from './utilities/findDuplicates'
import { NodeConfig } from '.'
export default class ExtensionManager {
@@ -32,9 +33,13 @@ export default class ExtensionManager {
this.schema = getSchemaByResolvedExtensions(this.extensions)
this.extensions.forEach(extension => {
// store extension storage in editor
this.editor.extensionStorage[extension.name] = extension.storage
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
editor: this.editor,
type: getSchemaTypeByName(extension.name, this.schema),
}
@@ -130,7 +135,14 @@ export default class ExtensionManager {
}
static resolve(extensions: Extensions): Extensions {
return ExtensionManager.sort(ExtensionManager.flatten(extensions))
const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions))
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
if (duplicatedNames.length) {
console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames.map(item => `'${item}'`).join(', ')}]. This can lead to issues.`)
}
return resolvedExtensions
}
static flatten(extensions: Extensions): Extensions {
@@ -139,6 +151,7 @@ export default class ExtensionManager {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
@@ -184,6 +197,7 @@ export default class ExtensionManager {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
editor: this.editor,
type: getSchemaTypeByName(extension.name, this.schema),
}
@@ -223,6 +237,7 @@ export default class ExtensionManager {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
editor,
type: getSchemaTypeByName(extension.name, this.schema),
}
@@ -313,6 +328,7 @@ export default class ExtensionManager {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
editor,
type: getNodeType(extension.name, this.schema),
}

View File

@@ -8,7 +8,10 @@ import { Plugin, Transaction } from 'prosemirror-state'
import { InputRule } from './InputRule'
import { PasteRule } from './PasteRule'
import mergeDeep from './utilities/mergeDeep'
import callOrReturn from './utilities/callOrReturn'
import getExtensionField from './helpers/getExtensionField'
import {
AnyConfig,
Extensions,
Attributes,
RawCommands,
@@ -21,7 +24,7 @@ import { MarkConfig } from '.'
import { Editor } from './Editor'
declare module '@tiptap/core' {
export interface MarkConfig<Options = any> {
export interface MarkConfig<Options = any, Storage = any> {
[key: string]: any;
/**
@@ -39,13 +42,23 @@ declare module '@tiptap/core' {
*/
defaultOptions?: Options,
/**
* Default Storage
*/
addStorage?: (this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
}) => Storage,
/**
* Global attributes
*/
addGlobalAttributes?: (this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['addGlobalAttributes'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@@ -54,9 +67,10 @@ declare module '@tiptap/core' {
addCommands?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addCommands'],
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands'],
}) => Partial<RawCommands>,
/**
@@ -65,9 +79,10 @@ declare module '@tiptap/core' {
addKeyboardShortcuts?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addKeyboardShortcuts'],
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts'],
}) => {
[key: string]: KeyboardShortcutCommand,
},
@@ -78,9 +93,10 @@ declare module '@tiptap/core' {
addInputRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addInputRules'],
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules'],
}) => InputRule[],
/**
@@ -89,9 +105,10 @@ declare module '@tiptap/core' {
addPasteRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addPasteRules'],
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules'],
}) => PasteRule[],
/**
@@ -100,9 +117,10 @@ declare module '@tiptap/core' {
addProseMirrorPlugins?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['addProseMirrorPlugins'],
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@@ -111,7 +129,8 @@ declare module '@tiptap/core' {
addExtensions?: (this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['addExtensions'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions'],
}) => Extensions,
/**
@@ -121,7 +140,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['extendNodeSchema'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema'],
},
extension: Node,
) => Record<string, any>) | null,
@@ -133,7 +153,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['extendMarkSchema'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema'],
},
extension: Mark,
) => Record<string, any>) | null,
@@ -144,9 +165,10 @@ declare module '@tiptap/core' {
onBeforeCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onBeforeCreate'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate'],
}) => void) | null,
/**
@@ -155,9 +177,10 @@ declare module '@tiptap/core' {
onCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onCreate'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate'],
}) => void) | null,
/**
@@ -166,9 +189,10 @@ declare module '@tiptap/core' {
onUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onUpdate'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate'],
}) => void) | null,
/**
@@ -177,9 +201,10 @@ declare module '@tiptap/core' {
onSelectionUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onSelectionUpdate'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate'],
}) => void) | null,
/**
@@ -189,9 +214,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onTransaction'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction'],
},
props: {
transaction: Transaction,
@@ -205,9 +231,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onFocus'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus'],
},
props: {
event: FocusEvent,
@@ -221,9 +248,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onBlur'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur'],
},
props: {
event: FocusEvent,
@@ -236,9 +264,10 @@ declare module '@tiptap/core' {
onDestroy?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: MarkType,
parent: ParentConfig<MarkConfig<Options>>['onDestroy'],
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy'],
}) => void) | null,
/**
@@ -252,7 +281,8 @@ declare module '@tiptap/core' {
inclusive?: MarkSpec['inclusive'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['inclusive'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive'],
}) => MarkSpec['inclusive']),
/**
@@ -261,7 +291,8 @@ declare module '@tiptap/core' {
excludes?: MarkSpec['excludes'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['excludes'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'],
}) => MarkSpec['excludes']),
/**
@@ -270,7 +301,8 @@ declare module '@tiptap/core' {
group?: MarkSpec['group'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['group'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['group'],
}) => MarkSpec['group']),
/**
@@ -279,7 +311,8 @@ declare module '@tiptap/core' {
spanning?: MarkSpec['spanning'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['spanning'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning'],
}) => MarkSpec['spanning']),
/**
@@ -288,7 +321,8 @@ declare module '@tiptap/core' {
code?: boolean | ((this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['code'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['code'],
}) => boolean),
/**
@@ -298,7 +332,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['parseHTML'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML'],
},
) => MarkSpec['parseDOM'],
@@ -309,7 +344,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['renderHTML'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML'],
},
props: {
mark: ProseMirrorMark,
@@ -324,13 +360,14 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<MarkConfig<Options>>['addAttributes'],
storage: Storage,
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes'],
},
) => Attributes | {},
}
}
export class Mark<Options = any> {
export class Mark<Options = any, Storage = any> {
type = 'mark'
name = 'mark'
@@ -341,12 +378,14 @@ export class Mark<Options = any> {
options: Options
storage: Storage
config: MarkConfig = {
name: this.name,
defaultOptions: {},
}
constructor(config: Partial<MarkConfig<Options>> = {}) {
constructor(config: Partial<MarkConfig<Options, Storage>> = {}) {
this.config = {
...this.config,
...config,
@@ -354,10 +393,18 @@ export class Mark<Options = any> {
this.name = this.config.name
this.options = this.config.defaultOptions
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
this,
'addStorage',
{
name: this.name,
options: this.options,
},
))
}
static create<O>(config: Partial<MarkConfig<O>> = {}) {
return new Mark<O>(config)
static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> = {}) {
return new Mark<O, S>(config)
}
configure(options: Partial<Options> = {}) {
@@ -370,8 +417,8 @@ export class Mark<Options = any> {
return extension
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>> = {}) {
const extension = new Mark<ExtendedOptions>(extendedConfig)
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<MarkConfig<ExtendedOptions, ExtendedStorage>> = {}) {
const extension = new Mark<ExtendedOptions, ExtendedStorage>(extendedConfig)
extension.parent = this
@@ -385,6 +432,15 @@ export class Mark<Options = any> {
? extendedConfig.defaultOptions
: extension.parent.options
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
extension,
'addStorage',
{
name: extension.name,
options: extension.options,
},
))
return extension
}
}

View File

@@ -8,7 +8,10 @@ import { Plugin, Transaction } from 'prosemirror-state'
import { InputRule } from './InputRule'
import { PasteRule } from './PasteRule'
import mergeDeep from './utilities/mergeDeep'
import callOrReturn from './utilities/callOrReturn'
import getExtensionField from './helpers/getExtensionField'
import {
AnyConfig,
Extensions,
Attributes,
NodeViewRenderer,
@@ -21,7 +24,7 @@ import { NodeConfig } from '.'
import { Editor } from './Editor'
declare module '@tiptap/core' {
interface NodeConfig<Options = any> {
interface NodeConfig<Options = any, Storage = any> {
[key: string]: any;
/**
@@ -39,13 +42,23 @@ declare module '@tiptap/core' {
*/
defaultOptions?: Options,
/**
* Default Storage
*/
addStorage?: (this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
}) => Storage,
/**
* Global attributes
*/
addGlobalAttributes?: (this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['addGlobalAttributes'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
}) => GlobalAttributes | {},
/**
@@ -54,9 +67,10 @@ declare module '@tiptap/core' {
addCommands?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addCommands'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands'],
}) => Partial<RawCommands>,
/**
@@ -65,9 +79,10 @@ declare module '@tiptap/core' {
addKeyboardShortcuts?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addKeyboardShortcuts'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts'],
}) => {
[key: string]: KeyboardShortcutCommand,
},
@@ -78,9 +93,10 @@ declare module '@tiptap/core' {
addInputRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addInputRules'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules'],
}) => InputRule[],
/**
@@ -89,9 +105,10 @@ declare module '@tiptap/core' {
addPasteRules?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addPasteRules'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules'],
}) => PasteRule[],
/**
@@ -100,9 +117,10 @@ declare module '@tiptap/core' {
addProseMirrorPlugins?: (this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addProseMirrorPlugins'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins'],
}) => Plugin[],
/**
@@ -111,7 +129,8 @@ declare module '@tiptap/core' {
addExtensions?: (this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['addExtensions'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions'],
}) => Extensions,
/**
@@ -121,7 +140,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['extendNodeSchema'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema'],
},
extension: Node,
) => Record<string, any>) | null,
@@ -133,7 +153,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['extendMarkSchema'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema'],
},
extension: Node,
) => Record<string, any>) | null,
@@ -144,9 +165,10 @@ declare module '@tiptap/core' {
onBeforeCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onBeforeCreate'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate'],
}) => void) | null,
/**
@@ -155,9 +177,10 @@ declare module '@tiptap/core' {
onCreate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onCreate'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate'],
}) => void) | null,
/**
@@ -166,9 +189,10 @@ declare module '@tiptap/core' {
onUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onUpdate'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate'],
}) => void) | null,
/**
@@ -177,9 +201,10 @@ declare module '@tiptap/core' {
onSelectionUpdate?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onSelectionUpdate'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate'],
}) => void) | null,
/**
@@ -189,9 +214,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onTransaction'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction'],
},
props: {
transaction: Transaction,
@@ -205,9 +231,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onFocus'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus'],
},
props: {
event: FocusEvent,
@@ -221,9 +248,10 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onBlur'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur'],
},
props: {
event: FocusEvent,
@@ -236,9 +264,10 @@ declare module '@tiptap/core' {
onDestroy?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['onDestroy'],
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy'],
}) => void) | null,
/**
@@ -247,9 +276,10 @@ declare module '@tiptap/core' {
addNodeView?: ((this: {
name: string,
options: Options,
storage: Storage,
editor: Editor,
type: NodeType,
parent: ParentConfig<NodeConfig<Options>>['addNodeView'],
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView'],
}) => NodeViewRenderer) | null,
/**
@@ -263,7 +293,8 @@ declare module '@tiptap/core' {
content?: NodeSpec['content'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['content'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['content'],
}) => NodeSpec['content']),
/**
@@ -272,7 +303,8 @@ declare module '@tiptap/core' {
marks?: NodeSpec['marks'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['marks'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['marks'],
}) => NodeSpec['marks']),
/**
@@ -281,7 +313,8 @@ declare module '@tiptap/core' {
group?: NodeSpec['group'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['group'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['group'],
}) => NodeSpec['group']),
/**
@@ -290,7 +323,8 @@ declare module '@tiptap/core' {
inline?: NodeSpec['inline'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['inline'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['inline'],
}) => NodeSpec['inline']),
/**
@@ -299,7 +333,8 @@ declare module '@tiptap/core' {
atom?: NodeSpec['atom'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['atom'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['atom'],
}) => NodeSpec['atom']),
/**
@@ -308,7 +343,8 @@ declare module '@tiptap/core' {
selectable?: NodeSpec['selectable'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['selectable'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable'],
}) => NodeSpec['selectable']),
/**
@@ -317,7 +353,8 @@ declare module '@tiptap/core' {
draggable?: NodeSpec['draggable'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['draggable'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable'],
}) => NodeSpec['draggable']),
/**
@@ -326,7 +363,8 @@ declare module '@tiptap/core' {
code?: NodeSpec['code'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['code'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['code'],
}) => NodeSpec['code']),
/**
@@ -335,7 +373,8 @@ declare module '@tiptap/core' {
defining?: NodeSpec['defining'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['defining'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['defining'],
}) => NodeSpec['defining']),
/**
@@ -344,7 +383,8 @@ declare module '@tiptap/core' {
isolating?: NodeSpec['isolating'] | ((this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['isolating'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating'],
}) => NodeSpec['isolating']),
/**
@@ -354,7 +394,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['parseHTML'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML'],
},
) => NodeSpec['parseDOM'],
@@ -365,7 +406,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['renderHTML'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML'],
},
props: {
node: ProseMirrorNode,
@@ -380,7 +422,8 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['renderText'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText'],
},
props: {
node: ProseMirrorNode,
@@ -397,13 +440,14 @@ declare module '@tiptap/core' {
this: {
name: string,
options: Options,
parent: ParentConfig<NodeConfig<Options>>['addAttributes'],
storage: Storage,
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes'],
},
) => Attributes | {},
}
}
export class Node<Options = any> {
export class Node<Options = any, Storage = any> {
type = 'node'
name = 'node'
@@ -414,12 +458,14 @@ export class Node<Options = any> {
options: Options
storage: Storage
config: NodeConfig = {
name: this.name,
defaultOptions: {},
}
constructor(config: Partial<NodeConfig<Options>> = {}) {
constructor(config: Partial<NodeConfig<Options, Storage>> = {}) {
this.config = {
...this.config,
...config,
@@ -427,10 +473,18 @@ export class Node<Options = any> {
this.name = this.config.name
this.options = this.config.defaultOptions
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
this,
'addStorage',
{
name: this.name,
options: this.options,
},
))
}
static create<O>(config: Partial<NodeConfig<O>> = {}) {
return new Node<O>(config)
static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> = {}) {
return new Node<O, S>(config)
}
configure(options: Partial<Options> = {}) {
@@ -443,8 +497,8 @@ export class Node<Options = any> {
return extension
}
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>> = {}) {
const extension = new Node<ExtendedOptions>(extendedConfig)
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<NodeConfig<ExtendedOptions, ExtendedStorage>> = {}) {
const extension = new Node<ExtendedOptions, ExtendedStorage>(extendedConfig)
extension.parent = this
@@ -458,6 +512,15 @@ export class Node<Options = any> {
? extendedConfig.defaultOptions
: extension.parent.options
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
extension,
'addStorage',
{
name: extension.name,
options: extension.options,
},
))
return extension
}
}

View File

@@ -30,6 +30,7 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(
@@ -67,6 +68,7 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(

View File

@@ -1,9 +1,9 @@
import { AnyExtension, RemoveThis } from '../types'
import { AnyExtension, RemoveThis, MaybeThisParameterType } from '../types'
export default function getExtensionField<T = any>(
extension: AnyExtension,
field: string,
context: Record<string, any> = {},
context?: Omit<MaybeThisParameterType<T>, 'parent'>,
): RemoveThis<T> {
if (extension.config[field] === undefined && extension.parent) {

View File

@@ -29,6 +29,7 @@ export default function getSchemaByResolvedExtensions(extensions: Extensions): S
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const extraNodeFields = extensions.reduce((fields, e) => {
@@ -91,6 +92,7 @@ export default function getSchemaByResolvedExtensions(extensions: Extensions): S
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const extraMarkFields = extensions.reduce((fields, e) => {

View File

@@ -15,6 +15,7 @@ export default function isList(name: string, extensions: Extensions): boolean {
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))

View File

@@ -56,10 +56,10 @@ export { default as posToDOMRect } from './helpers/posToDOMRect'
export interface Commands<ReturnType = any> {}
// eslint-disable-next-line
export interface ExtensionConfig<Options = any> {}
export interface ExtensionConfig<Options = any, Storage = any> {}
// eslint-disable-next-line
export interface NodeConfig<Options = any> {}
export interface NodeConfig<Options = any, Storage = any> {}
// eslint-disable-next-line
export interface MarkConfig<Options = any> {}
export interface MarkConfig<Options = any, Storage = any> {}

View File

@@ -31,6 +31,15 @@ export type ParentConfig<T> = Partial<{
: T[P]
}>
export type Primitive =
| null
| undefined
| string
| number
| boolean
| symbol
| bigint
export type RemoveThis<T> = T extends (...args: any) => any
? (...args: Parameters<T>) => ReturnType<T>
: T
@@ -39,6 +48,10 @@ export type MaybeReturnType<T> = T extends (...args: any) => any
? ReturnType<T>
: T
export type MaybeThisParameterType<T> = Exclude<T, Primitive> extends (...args: any) => any
? ThisParameterType<Exclude<T, Primitive>>
: any
export interface EditorEvents {
beforeCreate: { editor: Editor },
create: { editor: Editor },

View File

@@ -0,0 +1,5 @@
export default function findDuplicates(items: any[]): any[] {
const filtered = items.filter((el, index) => items.indexOf(el) !== index)
return [...new Set(filtered)]
}

View File

@@ -7,7 +7,7 @@ import {
import { gapCursor } from 'prosemirror-gapcursor'
declare module '@tiptap/core' {
interface NodeConfig<Options> {
interface NodeConfig<Options, Storage> {
/**
* Allow gap cursor
*/
@@ -17,6 +17,7 @@ declare module '@tiptap/core' {
| ((this: {
name: string,
options: Options,
storage: Storage,
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'],
}) => boolean | null),
}
@@ -35,6 +36,7 @@ export const Gapcursor = Extension.create({
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
return {

View File

@@ -66,13 +66,14 @@ declare module '@tiptap/core' {
}
}
interface NodeConfig<Options> {
interface NodeConfig<Options, Storage> {
/**
* Table Role
*/
tableRole?: string | ((this: {
name: string,
options: Options,
storage: Storage,
parent: ParentConfig<NodeConfig<Options>>['tableRole'],
}) => string),
}
@@ -245,6 +246,7 @@ export const Table = Node.create<TableOptions>({
const context = {
name: extension.name,
options: extension.options,
storage: extension.storage,
}
return {

View File

@@ -17,7 +17,13 @@ export const useEditor = (options: Partial<EditorOptions> = {}, deps: Dependency
setEditor(instance)
instance.on('transaction', forceUpdate)
instance.on('transaction', () => {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
forceUpdate()
})
})
})
return () => {
instance.destroy()

View File

@@ -39,6 +39,8 @@ export type ContentComponent = ComponentInternalInstance & {
export class Editor extends CoreEditor {
private reactiveState: Ref<EditorState>
private reactiveExtensionStorage: Ref<Record<string, any>>
public vueRenderers = reactive<Map<string, VueRenderer>>(new Map())
public contentComponent: ContentComponent | null = null
@@ -47,9 +49,11 @@ export class Editor extends CoreEditor {
super(options)
this.reactiveState = useDebouncedRef(this.view.state)
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage)
this.on('transaction', () => {
this.reactiveState.value = this.view.state
this.reactiveExtensionStorage.value = this.extensionStorage
})
return markRaw(this)
@@ -61,6 +65,12 @@ export class Editor extends CoreEditor {
: this.view.state
}
get storage() {
return this.reactiveExtensionStorage
? this.reactiveExtensionStorage.value
: super.storage
}
/**
* Register a ProseMirror plugin.
*/