mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-27 14:59:27 +08:00
feat: Add extension storage (#2069)
This commit is contained in:
parent
6987505fda
commit
7ffabf251c
@ -31,7 +31,7 @@ module.exports = {
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:vue/strongly-recommended',
|
||||
'plugin:vue/vue3-strongly-recommended',
|
||||
'airbnb-base',
|
||||
],
|
||||
rules: {
|
||||
|
@ -0,0 +1,19 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
|
||||
type CustomStorage = {
|
||||
foo: number,
|
||||
}
|
||||
|
||||
export const CustomExtension = Extension.create<{}, CustomStorage>({
|
||||
name: 'custom',
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
foo: 123,
|
||||
}
|
||||
},
|
||||
|
||||
onUpdate() {
|
||||
this.storage.foo += 1
|
||||
},
|
||||
})
|
15
demos/src/Experiments/ExtensionStorage/React/index.html
Normal file
15
demos/src/Experiments/ExtensionStorage/React/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import setup from '../../../../setup/react.ts'
|
||||
import source from '@source'
|
||||
setup('Experiments/ExtensionStorage', source)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
33
demos/src/Experiments/ExtensionStorage/React/index.jsx
Normal file
33
demos/src/Experiments/ExtensionStorage/React/index.jsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import { useEditor, EditorContent } from '@tiptap/react'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { CustomExtension } from './CustomExtension'
|
||||
import './styles.scss'
|
||||
|
||||
export default () => {
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CustomExtension,
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
This is a radically reduced version of tiptap. It has support for a document, with paragraphs and text. That’s it. It’s probably too much for real minimalists though.
|
||||
</p>
|
||||
<p>
|
||||
The paragraph extension is not really required, but you need at least one node. Sure, that node can be something different.
|
||||
</p>
|
||||
`,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
reactive storage: {editor?.storage.custom.foo}
|
||||
<EditorContent editor={editor} />
|
||||
</>
|
||||
)
|
||||
}
|
6
demos/src/Experiments/ExtensionStorage/React/styles.scss
Normal file
6
demos/src/Experiments/ExtensionStorage/React/styles.scss
Normal file
@ -0,0 +1,6 @@
|
||||
/* Basic editor styles */
|
||||
.ProseMirror {
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
|
||||
type CustomStorage = {
|
||||
foo: number,
|
||||
}
|
||||
|
||||
export const CustomExtension = Extension.create<{}, CustomStorage>({
|
||||
name: 'custom',
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
foo: 123,
|
||||
}
|
||||
},
|
||||
|
||||
onUpdate() {
|
||||
this.storage.foo += 1
|
||||
},
|
||||
})
|
15
demos/src/Experiments/ExtensionStorage/Vue/index.html
Normal file
15
demos/src/Experiments/ExtensionStorage/Vue/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import setup from '../../../../setup/vue.ts'
|
||||
import source from '@source'
|
||||
setup('Experiments/ExtensionStorage', source)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
56
demos/src/Experiments/ExtensionStorage/Vue/index.vue
Normal file
56
demos/src/Experiments/ExtensionStorage/Vue/index.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
reactive storage: {{ editor?.storage.custom.foo }}
|
||||
<editor-content :editor="editor" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent } from '@tiptap/vue-3'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { CustomExtension } from './CustomExtension'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
Document,
|
||||
Paragraph,
|
||||
Text,
|
||||
CustomExtension,
|
||||
],
|
||||
content: `
|
||||
<p>
|
||||
This is a radically reduced version of tiptap. It has support for a document, with paragraphs and text. That’s it. It’s probably too much for real minimalists though.
|
||||
</p>
|
||||
<p>
|
||||
The paragraph extension is not really required, but you need at least one node. Sure, that node can be something different.
|
||||
</p>
|
||||
`,
|
||||
})
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* Basic editor styles */
|
||||
.ProseMirror {
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -78,6 +78,39 @@ const CustomHeading = Heading.extend({
|
||||
})
|
||||
```
|
||||
|
||||
### Storage
|
||||
At some point you probably want to save some data within your extension instance. This data is mutable. You can access it within the extension under `this.storage`.
|
||||
|
||||
```js
|
||||
import { Extension } from '@tiptap/core'
|
||||
|
||||
const CustomExtension = Extension.create({
|
||||
name: 'customExtension',
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
awesomeness: 100,
|
||||
}
|
||||
},
|
||||
|
||||
onUpdate() {
|
||||
this.storage.awesomeness += 1
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Outside the extension you have access via `editor.storage`. Make sure that each extension has a unique name.
|
||||
|
||||
```js
|
||||
const editor = new Editor({
|
||||
extensions: [
|
||||
CustomExtension,
|
||||
],
|
||||
})
|
||||
|
||||
const awesomeness = editor.storage.customExtension.awesomeness
|
||||
```
|
||||
|
||||
### Schema
|
||||
tiptap works with a strict schema, which configures how the content can be structured, nested, how it behaves and many more things. You [can change all aspects of the schema](/api/schema) for existing extensions. Let’s walk through a few common use cases.
|
||||
|
||||
|
@ -32,6 +32,25 @@ const CustomExtension = Extension.create<CustomExtensionOptions>({
|
||||
})
|
||||
```
|
||||
|
||||
### Storage types
|
||||
To add types for your extension storage, you’ll have to pass that as a second type parameter.
|
||||
|
||||
```ts
|
||||
import { Extension } from '@tiptap/core'
|
||||
|
||||
export interface CustomExtensionStorage {
|
||||
awesomeness: number,
|
||||
}
|
||||
|
||||
const CustomExtension = Extension.create<{}, CustomExtensionStorage>({
|
||||
addStorage() {
|
||||
return {
|
||||
awesomeness: 100,
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Command type
|
||||
The core package also exports a `Command` type, which needs to be added to all commands that you specify in your code. Here is an example:
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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']>(
|
||||
|
@ -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) {
|
||||
|
@ -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) => {
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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> {}
|
||||
|
@ -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 },
|
||||
|
5
packages/core/src/utilities/findDuplicates.ts
Normal file
5
packages/core/src/utilities/findDuplicates.ts
Normal 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)]
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user