feat: Add extension storage (#2069)
This commit is contained in:
@@ -31,7 +31,7 @@ module.exports = {
|
|||||||
extends: [
|
extends: [
|
||||||
'plugin:@typescript-eslint/eslint-recommended',
|
'plugin:@typescript-eslint/eslint-recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:vue/strongly-recommended',
|
'plugin:vue/vue3-strongly-recommended',
|
||||||
'airbnb-base',
|
'airbnb-base',
|
||||||
],
|
],
|
||||||
rules: {
|
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
|
### 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.
|
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
|
### 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:
|
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 isFocused = false
|
||||||
|
|
||||||
|
public extensionStorage: Record<string, any> = {}
|
||||||
|
|
||||||
public options: EditorOptions = {
|
public options: EditorOptions = {
|
||||||
element: document.createElement('div'),
|
element: document.createElement('div'),
|
||||||
content: '',
|
content: '',
|
||||||
@@ -100,6 +102,13 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the editor storage.
|
||||||
|
*/
|
||||||
|
public get storage(): Record<string, any> {
|
||||||
|
return this.extensionStorage
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object of all registered commands.
|
* An object of all registered commands.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import { Editor } from './Editor'
|
|||||||
import { Node } from './Node'
|
import { Node } from './Node'
|
||||||
import { Mark } from './Mark'
|
import { Mark } from './Mark'
|
||||||
import mergeDeep from './utilities/mergeDeep'
|
import mergeDeep from './utilities/mergeDeep'
|
||||||
|
import callOrReturn from './utilities/callOrReturn'
|
||||||
|
import getExtensionField from './helpers/getExtensionField'
|
||||||
import {
|
import {
|
||||||
|
AnyConfig,
|
||||||
Extensions,
|
Extensions,
|
||||||
GlobalAttributes,
|
GlobalAttributes,
|
||||||
RawCommands,
|
RawCommands,
|
||||||
@@ -15,7 +18,7 @@ import {
|
|||||||
import { ExtensionConfig } from '.'
|
import { ExtensionConfig } from '.'
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface ExtensionConfig<Options = any> {
|
interface ExtensionConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,13 +36,23 @@ declare module '@tiptap/core' {
|
|||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Storage
|
||||||
|
*/
|
||||||
|
addStorage?: (this: {
|
||||||
|
name: string,
|
||||||
|
options: Options,
|
||||||
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
|
}) => Storage,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addGlobalAttributes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,8 +61,9 @@ declare module '@tiptap/core' {
|
|||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addCommands'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands'],
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,8 +72,9 @@ declare module '@tiptap/core' {
|
|||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addKeyboardShortcuts'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand,
|
||||||
},
|
},
|
||||||
@@ -70,8 +85,9 @@ declare module '@tiptap/core' {
|
|||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addInputRules'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules'],
|
||||||
}) => InputRule[],
|
}) => InputRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,8 +96,9 @@ declare module '@tiptap/core' {
|
|||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addPasteRules'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules'],
|
||||||
}) => PasteRule[],
|
}) => PasteRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,8 +107,9 @@ declare module '@tiptap/core' {
|
|||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addProseMirrorPlugins'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||||
}) => Plugin[],
|
}) => Plugin[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +118,8 @@ declare module '@tiptap/core' {
|
|||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['addExtensions'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions'],
|
||||||
}) => Extensions,
|
}) => Extensions,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,7 +129,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['extendNodeSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema'],
|
||||||
},
|
},
|
||||||
extension: Node,
|
extension: Node,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -122,7 +142,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['extendMarkSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema'],
|
||||||
},
|
},
|
||||||
extension: Mark,
|
extension: Mark,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -133,8 +154,9 @@ declare module '@tiptap/core' {
|
|||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onBeforeCreate'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,8 +165,9 @@ declare module '@tiptap/core' {
|
|||||||
onCreate?: ((this: {
|
onCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onCreate'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,8 +176,9 @@ declare module '@tiptap/core' {
|
|||||||
onUpdate?: ((this: {
|
onUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onUpdate'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,8 +187,9 @@ declare module '@tiptap/core' {
|
|||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onSelectionUpdate'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,8 +199,9 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onTransaction'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
@@ -189,8 +215,9 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onFocus'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -204,8 +231,9 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onBlur'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -218,13 +246,14 @@ declare module '@tiptap/core' {
|
|||||||
onDestroy?: ((this: {
|
onDestroy?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
parent: ParentConfig<ExtensionConfig<Options>>['onDestroy'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Extension<Options = any> {
|
export class Extension<Options = any, Storage = any> {
|
||||||
type = 'extension'
|
type = 'extension'
|
||||||
|
|
||||||
name = 'extension'
|
name = 'extension'
|
||||||
@@ -235,12 +264,14 @@ export class Extension<Options = any> {
|
|||||||
|
|
||||||
options: Options
|
options: Options
|
||||||
|
|
||||||
|
storage: Storage
|
||||||
|
|
||||||
config: ExtensionConfig = {
|
config: ExtensionConfig = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
defaultOptions: {},
|
defaultOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(config: Partial<ExtensionConfig<Options>> = {}) {
|
constructor(config: Partial<ExtensionConfig<Options, Storage>> = {}) {
|
||||||
this.config = {
|
this.config = {
|
||||||
...this.config,
|
...this.config,
|
||||||
...config,
|
...config,
|
||||||
@@ -248,10 +279,18 @@ export class Extension<Options = any> {
|
|||||||
|
|
||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
this.options = this.config.defaultOptions
|
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>> = {}) {
|
static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
|
||||||
return new Extension<O>(config)
|
return new Extension<O, S>(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(options: Partial<Options> = {}) {
|
configure(options: Partial<Options> = {}) {
|
||||||
@@ -264,8 +303,8 @@ export class Extension<Options = any> {
|
|||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
|
||||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions>> = {}) {
|
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||||
const extension = new Extension<ExtendedOptions>(extendedConfig)
|
const extension = new Extension<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
@@ -279,6 +318,15 @@ export class Extension<Options = any> {
|
|||||||
? extendedConfig.defaultOptions
|
? extendedConfig.defaultOptions
|
||||||
: extension.parent.options
|
: extension.parent.options
|
||||||
|
|
||||||
|
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||||
|
extension,
|
||||||
|
'addStorage',
|
||||||
|
{
|
||||||
|
name: extension.name,
|
||||||
|
options: extension.options,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import splitExtensions from './helpers/splitExtensions'
|
|||||||
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
|
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
|
||||||
import getRenderedAttributes from './helpers/getRenderedAttributes'
|
import getRenderedAttributes from './helpers/getRenderedAttributes'
|
||||||
import callOrReturn from './utilities/callOrReturn'
|
import callOrReturn from './utilities/callOrReturn'
|
||||||
|
import findDuplicates from './utilities/findDuplicates'
|
||||||
import { NodeConfig } from '.'
|
import { NodeConfig } from '.'
|
||||||
|
|
||||||
export default class ExtensionManager {
|
export default class ExtensionManager {
|
||||||
@@ -32,9 +33,13 @@ export default class ExtensionManager {
|
|||||||
this.schema = getSchemaByResolvedExtensions(this.extensions)
|
this.schema = getSchemaByResolvedExtensions(this.extensions)
|
||||||
|
|
||||||
this.extensions.forEach(extension => {
|
this.extensions.forEach(extension => {
|
||||||
|
// store extension storage in editor
|
||||||
|
this.editor.extensionStorage[extension.name] = extension.storage
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
editor: this.editor,
|
editor: this.editor,
|
||||||
type: getSchemaTypeByName(extension.name, this.schema),
|
type: getSchemaTypeByName(extension.name, this.schema),
|
||||||
}
|
}
|
||||||
@@ -130,7 +135,14 @@ export default class ExtensionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static resolve(extensions: Extensions): Extensions {
|
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 {
|
static flatten(extensions: Extensions): Extensions {
|
||||||
@@ -139,6 +151,7 @@ export default class ExtensionManager {
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
||||||
@@ -184,6 +197,7 @@ export default class ExtensionManager {
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
editor: this.editor,
|
editor: this.editor,
|
||||||
type: getSchemaTypeByName(extension.name, this.schema),
|
type: getSchemaTypeByName(extension.name, this.schema),
|
||||||
}
|
}
|
||||||
@@ -223,6 +237,7 @@ export default class ExtensionManager {
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
editor,
|
editor,
|
||||||
type: getSchemaTypeByName(extension.name, this.schema),
|
type: getSchemaTypeByName(extension.name, this.schema),
|
||||||
}
|
}
|
||||||
@@ -313,6 +328,7 @@ export default class ExtensionManager {
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
editor,
|
editor,
|
||||||
type: getNodeType(extension.name, this.schema),
|
type: getNodeType(extension.name, this.schema),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import { Plugin, Transaction } from 'prosemirror-state'
|
|||||||
import { InputRule } from './InputRule'
|
import { InputRule } from './InputRule'
|
||||||
import { PasteRule } from './PasteRule'
|
import { PasteRule } from './PasteRule'
|
||||||
import mergeDeep from './utilities/mergeDeep'
|
import mergeDeep from './utilities/mergeDeep'
|
||||||
|
import callOrReturn from './utilities/callOrReturn'
|
||||||
|
import getExtensionField from './helpers/getExtensionField'
|
||||||
import {
|
import {
|
||||||
|
AnyConfig,
|
||||||
Extensions,
|
Extensions,
|
||||||
Attributes,
|
Attributes,
|
||||||
RawCommands,
|
RawCommands,
|
||||||
@@ -21,7 +24,7 @@ import { MarkConfig } from '.'
|
|||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
export interface MarkConfig<Options = any> {
|
export interface MarkConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,13 +42,23 @@ declare module '@tiptap/core' {
|
|||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Storage
|
||||||
|
*/
|
||||||
|
addStorage?: (this: {
|
||||||
|
name: string,
|
||||||
|
options: Options,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
|
}) => Storage,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addGlobalAttributes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,9 +67,10 @@ declare module '@tiptap/core' {
|
|||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addCommands'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands'],
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,9 +79,10 @@ declare module '@tiptap/core' {
|
|||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addKeyboardShortcuts'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand,
|
||||||
},
|
},
|
||||||
@@ -78,9 +93,10 @@ declare module '@tiptap/core' {
|
|||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addInputRules'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules'],
|
||||||
}) => InputRule[],
|
}) => InputRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,9 +105,10 @@ declare module '@tiptap/core' {
|
|||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addPasteRules'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules'],
|
||||||
}) => PasteRule[],
|
}) => PasteRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,9 +117,10 @@ declare module '@tiptap/core' {
|
|||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addProseMirrorPlugins'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||||
}) => Plugin[],
|
}) => Plugin[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +129,8 @@ declare module '@tiptap/core' {
|
|||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addExtensions'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions'],
|
||||||
}) => Extensions,
|
}) => Extensions,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +140,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['extendNodeSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema'],
|
||||||
},
|
},
|
||||||
extension: Node,
|
extension: Node,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -133,7 +153,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['extendMarkSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema'],
|
||||||
},
|
},
|
||||||
extension: Mark,
|
extension: Mark,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -144,9 +165,10 @@ declare module '@tiptap/core' {
|
|||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onBeforeCreate'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,9 +177,10 @@ declare module '@tiptap/core' {
|
|||||||
onCreate?: ((this: {
|
onCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onCreate'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,9 +189,10 @@ declare module '@tiptap/core' {
|
|||||||
onUpdate?: ((this: {
|
onUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onUpdate'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,9 +201,10 @@ declare module '@tiptap/core' {
|
|||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onSelectionUpdate'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,9 +214,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onTransaction'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
@@ -205,9 +231,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onFocus'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -221,9 +248,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onBlur'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -236,9 +264,10 @@ declare module '@tiptap/core' {
|
|||||||
onDestroy?: ((this: {
|
onDestroy?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: MarkType,
|
type: MarkType,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['onDestroy'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,7 +281,8 @@ declare module '@tiptap/core' {
|
|||||||
inclusive?: MarkSpec['inclusive'] | ((this: {
|
inclusive?: MarkSpec['inclusive'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['inclusive'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive'],
|
||||||
}) => MarkSpec['inclusive']),
|
}) => MarkSpec['inclusive']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,7 +291,8 @@ declare module '@tiptap/core' {
|
|||||||
excludes?: MarkSpec['excludes'] | ((this: {
|
excludes?: MarkSpec['excludes'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['excludes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'],
|
||||||
}) => MarkSpec['excludes']),
|
}) => MarkSpec['excludes']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,7 +301,8 @@ declare module '@tiptap/core' {
|
|||||||
group?: MarkSpec['group'] | ((this: {
|
group?: MarkSpec['group'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['group'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['group'],
|
||||||
}) => MarkSpec['group']),
|
}) => MarkSpec['group']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,7 +311,8 @@ declare module '@tiptap/core' {
|
|||||||
spanning?: MarkSpec['spanning'] | ((this: {
|
spanning?: MarkSpec['spanning'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['spanning'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning'],
|
||||||
}) => MarkSpec['spanning']),
|
}) => MarkSpec['spanning']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -288,7 +321,8 @@ declare module '@tiptap/core' {
|
|||||||
code?: boolean | ((this: {
|
code?: boolean | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['code'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['code'],
|
||||||
}) => boolean),
|
}) => boolean),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -298,7 +332,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['parseHTML'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML'],
|
||||||
},
|
},
|
||||||
) => MarkSpec['parseDOM'],
|
) => MarkSpec['parseDOM'],
|
||||||
|
|
||||||
@@ -309,7 +344,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['renderHTML'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
mark: ProseMirrorMark,
|
mark: ProseMirrorMark,
|
||||||
@@ -324,13 +360,14 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<MarkConfig<Options>>['addAttributes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes'],
|
||||||
},
|
},
|
||||||
) => Attributes | {},
|
) => Attributes | {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Mark<Options = any> {
|
export class Mark<Options = any, Storage = any> {
|
||||||
type = 'mark'
|
type = 'mark'
|
||||||
|
|
||||||
name = 'mark'
|
name = 'mark'
|
||||||
@@ -341,12 +378,14 @@ export class Mark<Options = any> {
|
|||||||
|
|
||||||
options: Options
|
options: Options
|
||||||
|
|
||||||
|
storage: Storage
|
||||||
|
|
||||||
config: MarkConfig = {
|
config: MarkConfig = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
defaultOptions: {},
|
defaultOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(config: Partial<MarkConfig<Options>> = {}) {
|
constructor(config: Partial<MarkConfig<Options, Storage>> = {}) {
|
||||||
this.config = {
|
this.config = {
|
||||||
...this.config,
|
...this.config,
|
||||||
...config,
|
...config,
|
||||||
@@ -354,10 +393,18 @@ export class Mark<Options = any> {
|
|||||||
|
|
||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
this.options = this.config.defaultOptions
|
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>> = {}) {
|
static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> = {}) {
|
||||||
return new Mark<O>(config)
|
return new Mark<O, S>(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(options: Partial<Options> = {}) {
|
configure(options: Partial<Options> = {}) {
|
||||||
@@ -370,8 +417,8 @@ export class Mark<Options = any> {
|
|||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
|
||||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<MarkConfig<ExtendedOptions>> = {}) {
|
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<MarkConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||||
const extension = new Mark<ExtendedOptions>(extendedConfig)
|
const extension = new Mark<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
@@ -385,6 +432,15 @@ export class Mark<Options = any> {
|
|||||||
? extendedConfig.defaultOptions
|
? extendedConfig.defaultOptions
|
||||||
: extension.parent.options
|
: extension.parent.options
|
||||||
|
|
||||||
|
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||||
|
extension,
|
||||||
|
'addStorage',
|
||||||
|
{
|
||||||
|
name: extension.name,
|
||||||
|
options: extension.options,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import { Plugin, Transaction } from 'prosemirror-state'
|
|||||||
import { InputRule } from './InputRule'
|
import { InputRule } from './InputRule'
|
||||||
import { PasteRule } from './PasteRule'
|
import { PasteRule } from './PasteRule'
|
||||||
import mergeDeep from './utilities/mergeDeep'
|
import mergeDeep from './utilities/mergeDeep'
|
||||||
|
import callOrReturn from './utilities/callOrReturn'
|
||||||
|
import getExtensionField from './helpers/getExtensionField'
|
||||||
import {
|
import {
|
||||||
|
AnyConfig,
|
||||||
Extensions,
|
Extensions,
|
||||||
Attributes,
|
Attributes,
|
||||||
NodeViewRenderer,
|
NodeViewRenderer,
|
||||||
@@ -21,7 +24,7 @@ import { NodeConfig } from '.'
|
|||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface NodeConfig<Options = any> {
|
interface NodeConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,13 +42,23 @@ declare module '@tiptap/core' {
|
|||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Storage
|
||||||
|
*/
|
||||||
|
addStorage?: (this: {
|
||||||
|
name: string,
|
||||||
|
options: Options,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
|
}) => Storage,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addGlobalAttributes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,9 +67,10 @@ declare module '@tiptap/core' {
|
|||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addCommands'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands'],
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,9 +79,10 @@ declare module '@tiptap/core' {
|
|||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addKeyboardShortcuts'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand,
|
||||||
},
|
},
|
||||||
@@ -78,9 +93,10 @@ declare module '@tiptap/core' {
|
|||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addInputRules'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules'],
|
||||||
}) => InputRule[],
|
}) => InputRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,9 +105,10 @@ declare module '@tiptap/core' {
|
|||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addPasteRules'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules'],
|
||||||
}) => PasteRule[],
|
}) => PasteRule[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,9 +117,10 @@ declare module '@tiptap/core' {
|
|||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addProseMirrorPlugins'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||||
}) => Plugin[],
|
}) => Plugin[],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +129,8 @@ declare module '@tiptap/core' {
|
|||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addExtensions'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions'],
|
||||||
}) => Extensions,
|
}) => Extensions,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +140,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['extendNodeSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema'],
|
||||||
},
|
},
|
||||||
extension: Node,
|
extension: Node,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -133,7 +153,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['extendMarkSchema'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema'],
|
||||||
},
|
},
|
||||||
extension: Node,
|
extension: Node,
|
||||||
) => Record<string, any>) | null,
|
) => Record<string, any>) | null,
|
||||||
@@ -144,9 +165,10 @@ declare module '@tiptap/core' {
|
|||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onBeforeCreate'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,9 +177,10 @@ declare module '@tiptap/core' {
|
|||||||
onCreate?: ((this: {
|
onCreate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onCreate'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,9 +189,10 @@ declare module '@tiptap/core' {
|
|||||||
onUpdate?: ((this: {
|
onUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onUpdate'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,9 +201,10 @@ declare module '@tiptap/core' {
|
|||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onSelectionUpdate'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,9 +214,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onTransaction'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
transaction: Transaction,
|
transaction: Transaction,
|
||||||
@@ -205,9 +231,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onFocus'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -221,9 +248,10 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onBlur'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
event: FocusEvent,
|
event: FocusEvent,
|
||||||
@@ -236,9 +264,10 @@ declare module '@tiptap/core' {
|
|||||||
onDestroy?: ((this: {
|
onDestroy?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['onDestroy'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy'],
|
||||||
}) => void) | null,
|
}) => void) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,9 +276,10 @@ declare module '@tiptap/core' {
|
|||||||
addNodeView?: ((this: {
|
addNodeView?: ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
type: NodeType,
|
type: NodeType,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addNodeView'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView'],
|
||||||
}) => NodeViewRenderer) | null,
|
}) => NodeViewRenderer) | null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,7 +293,8 @@ declare module '@tiptap/core' {
|
|||||||
content?: NodeSpec['content'] | ((this: {
|
content?: NodeSpec['content'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['content'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['content'],
|
||||||
}) => NodeSpec['content']),
|
}) => NodeSpec['content']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,7 +303,8 @@ declare module '@tiptap/core' {
|
|||||||
marks?: NodeSpec['marks'] | ((this: {
|
marks?: NodeSpec['marks'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['marks'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['marks'],
|
||||||
}) => NodeSpec['marks']),
|
}) => NodeSpec['marks']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,7 +313,8 @@ declare module '@tiptap/core' {
|
|||||||
group?: NodeSpec['group'] | ((this: {
|
group?: NodeSpec['group'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['group'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['group'],
|
||||||
}) => NodeSpec['group']),
|
}) => NodeSpec['group']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +323,8 @@ declare module '@tiptap/core' {
|
|||||||
inline?: NodeSpec['inline'] | ((this: {
|
inline?: NodeSpec['inline'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['inline'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['inline'],
|
||||||
}) => NodeSpec['inline']),
|
}) => NodeSpec['inline']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,7 +333,8 @@ declare module '@tiptap/core' {
|
|||||||
atom?: NodeSpec['atom'] | ((this: {
|
atom?: NodeSpec['atom'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['atom'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['atom'],
|
||||||
}) => NodeSpec['atom']),
|
}) => NodeSpec['atom']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,7 +343,8 @@ declare module '@tiptap/core' {
|
|||||||
selectable?: NodeSpec['selectable'] | ((this: {
|
selectable?: NodeSpec['selectable'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['selectable'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable'],
|
||||||
}) => NodeSpec['selectable']),
|
}) => NodeSpec['selectable']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,7 +353,8 @@ declare module '@tiptap/core' {
|
|||||||
draggable?: NodeSpec['draggable'] | ((this: {
|
draggable?: NodeSpec['draggable'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['draggable'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable'],
|
||||||
}) => NodeSpec['draggable']),
|
}) => NodeSpec['draggable']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,7 +363,8 @@ declare module '@tiptap/core' {
|
|||||||
code?: NodeSpec['code'] | ((this: {
|
code?: NodeSpec['code'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['code'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['code'],
|
||||||
}) => NodeSpec['code']),
|
}) => NodeSpec['code']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -335,7 +373,8 @@ declare module '@tiptap/core' {
|
|||||||
defining?: NodeSpec['defining'] | ((this: {
|
defining?: NodeSpec['defining'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['defining'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['defining'],
|
||||||
}) => NodeSpec['defining']),
|
}) => NodeSpec['defining']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -344,7 +383,8 @@ declare module '@tiptap/core' {
|
|||||||
isolating?: NodeSpec['isolating'] | ((this: {
|
isolating?: NodeSpec['isolating'] | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['isolating'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating'],
|
||||||
}) => NodeSpec['isolating']),
|
}) => NodeSpec['isolating']),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,7 +394,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['parseHTML'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML'],
|
||||||
},
|
},
|
||||||
) => NodeSpec['parseDOM'],
|
) => NodeSpec['parseDOM'],
|
||||||
|
|
||||||
@@ -365,7 +406,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['renderHTML'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
node: ProseMirrorNode,
|
node: ProseMirrorNode,
|
||||||
@@ -380,7 +422,8 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['renderText'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText'],
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
node: ProseMirrorNode,
|
node: ProseMirrorNode,
|
||||||
@@ -397,13 +440,14 @@ declare module '@tiptap/core' {
|
|||||||
this: {
|
this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['addAttributes'],
|
storage: Storage,
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes'],
|
||||||
},
|
},
|
||||||
) => Attributes | {},
|
) => Attributes | {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Node<Options = any> {
|
export class Node<Options = any, Storage = any> {
|
||||||
type = 'node'
|
type = 'node'
|
||||||
|
|
||||||
name = 'node'
|
name = 'node'
|
||||||
@@ -414,12 +458,14 @@ export class Node<Options = any> {
|
|||||||
|
|
||||||
options: Options
|
options: Options
|
||||||
|
|
||||||
|
storage: Storage
|
||||||
|
|
||||||
config: NodeConfig = {
|
config: NodeConfig = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
defaultOptions: {},
|
defaultOptions: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(config: Partial<NodeConfig<Options>> = {}) {
|
constructor(config: Partial<NodeConfig<Options, Storage>> = {}) {
|
||||||
this.config = {
|
this.config = {
|
||||||
...this.config,
|
...this.config,
|
||||||
...config,
|
...config,
|
||||||
@@ -427,10 +473,18 @@ export class Node<Options = any> {
|
|||||||
|
|
||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
this.options = this.config.defaultOptions
|
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>> = {}) {
|
static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> = {}) {
|
||||||
return new Node<O>(config)
|
return new Node<O, S>(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(options: Partial<Options> = {}) {
|
configure(options: Partial<Options> = {}) {
|
||||||
@@ -443,8 +497,8 @@ export class Node<Options = any> {
|
|||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
|
||||||
extend<ExtendedOptions = Options>(extendedConfig: Partial<NodeConfig<ExtendedOptions>> = {}) {
|
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<NodeConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||||
const extension = new Node<ExtendedOptions>(extendedConfig)
|
const extension = new Node<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
@@ -458,6 +512,15 @@ export class Node<Options = any> {
|
|||||||
? extendedConfig.defaultOptions
|
? extendedConfig.defaultOptions
|
||||||
: extension.parent.options
|
: extension.parent.options
|
||||||
|
|
||||||
|
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||||
|
extension,
|
||||||
|
'addStorage',
|
||||||
|
{
|
||||||
|
name: extension.name,
|
||||||
|
options: extension.options,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(
|
const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(
|
||||||
@@ -67,6 +68,7 @@ export default function getAttributesFromExtensions(extensions: Extensions): Ext
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(
|
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>(
|
export default function getExtensionField<T = any>(
|
||||||
extension: AnyExtension,
|
extension: AnyExtension,
|
||||||
field: string,
|
field: string,
|
||||||
context: Record<string, any> = {},
|
context?: Omit<MaybeThisParameterType<T>, 'parent'>,
|
||||||
): RemoveThis<T> {
|
): RemoveThis<T> {
|
||||||
|
|
||||||
if (extension.config[field] === undefined && extension.parent) {
|
if (extension.config[field] === undefined && extension.parent) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export default function getSchemaByResolvedExtensions(extensions: Extensions): S
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const extraNodeFields = extensions.reduce((fields, e) => {
|
const extraNodeFields = extensions.reduce((fields, e) => {
|
||||||
@@ -91,6 +92,7 @@ export default function getSchemaByResolvedExtensions(extensions: Extensions): S
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
const extraMarkFields = extensions.reduce((fields, e) => {
|
const extraMarkFields = extensions.reduce((fields, e) => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export default function isList(name: string, extensions: Extensions): boolean {
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))
|
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> {}
|
export interface Commands<ReturnType = any> {}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
export interface ExtensionConfig<Options = any> {}
|
export interface ExtensionConfig<Options = any, Storage = any> {}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
export interface NodeConfig<Options = any> {}
|
export interface NodeConfig<Options = any, Storage = any> {}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// 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]
|
: T[P]
|
||||||
}>
|
}>
|
||||||
|
|
||||||
|
export type Primitive =
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| symbol
|
||||||
|
| bigint
|
||||||
|
|
||||||
export type RemoveThis<T> = T extends (...args: any) => any
|
export type RemoveThis<T> = T extends (...args: any) => any
|
||||||
? (...args: Parameters<T>) => ReturnType<T>
|
? (...args: Parameters<T>) => ReturnType<T>
|
||||||
: T
|
: T
|
||||||
@@ -39,6 +48,10 @@ export type MaybeReturnType<T> = T extends (...args: any) => any
|
|||||||
? ReturnType<T>
|
? ReturnType<T>
|
||||||
: T
|
: T
|
||||||
|
|
||||||
|
export type MaybeThisParameterType<T> = Exclude<T, Primitive> extends (...args: any) => any
|
||||||
|
? ThisParameterType<Exclude<T, Primitive>>
|
||||||
|
: any
|
||||||
|
|
||||||
export interface EditorEvents {
|
export interface EditorEvents {
|
||||||
beforeCreate: { editor: Editor },
|
beforeCreate: { editor: Editor },
|
||||||
create: { 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'
|
import { gapCursor } from 'prosemirror-gapcursor'
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface NodeConfig<Options> {
|
interface NodeConfig<Options, Storage> {
|
||||||
/**
|
/**
|
||||||
* Allow gap cursor
|
* Allow gap cursor
|
||||||
*/
|
*/
|
||||||
@@ -17,6 +17,7 @@ declare module '@tiptap/core' {
|
|||||||
| ((this: {
|
| ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'],
|
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'],
|
||||||
}) => boolean | null),
|
}) => boolean | null),
|
||||||
}
|
}
|
||||||
@@ -35,6 +36,7 @@ export const Gapcursor = Extension.create({
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -66,13 +66,14 @@ declare module '@tiptap/core' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NodeConfig<Options> {
|
interface NodeConfig<Options, Storage> {
|
||||||
/**
|
/**
|
||||||
* Table Role
|
* Table Role
|
||||||
*/
|
*/
|
||||||
tableRole?: string | ((this: {
|
tableRole?: string | ((this: {
|
||||||
name: string,
|
name: string,
|
||||||
options: Options,
|
options: Options,
|
||||||
|
storage: Storage,
|
||||||
parent: ParentConfig<NodeConfig<Options>>['tableRole'],
|
parent: ParentConfig<NodeConfig<Options>>['tableRole'],
|
||||||
}) => string),
|
}) => string),
|
||||||
}
|
}
|
||||||
@@ -245,6 +246,7 @@ export const Table = Node.create<TableOptions>({
|
|||||||
const context = {
|
const context = {
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
|
storage: extension.storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -17,7 +17,13 @@ export const useEditor = (options: Partial<EditorOptions> = {}, deps: Dependency
|
|||||||
|
|
||||||
setEditor(instance)
|
setEditor(instance)
|
||||||
|
|
||||||
instance.on('transaction', forceUpdate)
|
instance.on('transaction', () => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
forceUpdate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
instance.destroy()
|
instance.destroy()
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export type ContentComponent = ComponentInternalInstance & {
|
|||||||
export class Editor extends CoreEditor {
|
export class Editor extends CoreEditor {
|
||||||
private reactiveState: Ref<EditorState>
|
private reactiveState: Ref<EditorState>
|
||||||
|
|
||||||
|
private reactiveExtensionStorage: Ref<Record<string, any>>
|
||||||
|
|
||||||
public vueRenderers = reactive<Map<string, VueRenderer>>(new Map())
|
public vueRenderers = reactive<Map<string, VueRenderer>>(new Map())
|
||||||
|
|
||||||
public contentComponent: ContentComponent | null = null
|
public contentComponent: ContentComponent | null = null
|
||||||
@@ -47,9 +49,11 @@ export class Editor extends CoreEditor {
|
|||||||
super(options)
|
super(options)
|
||||||
|
|
||||||
this.reactiveState = useDebouncedRef(this.view.state)
|
this.reactiveState = useDebouncedRef(this.view.state)
|
||||||
|
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage)
|
||||||
|
|
||||||
this.on('transaction', () => {
|
this.on('transaction', () => {
|
||||||
this.reactiveState.value = this.view.state
|
this.reactiveState.value = this.view.state
|
||||||
|
this.reactiveExtensionStorage.value = this.extensionStorage
|
||||||
})
|
})
|
||||||
|
|
||||||
return markRaw(this)
|
return markRaw(this)
|
||||||
@@ -61,6 +65,12 @@ export class Editor extends CoreEditor {
|
|||||||
: this.view.state
|
: this.view.state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get storage() {
|
||||||
|
return this.reactiveExtensionStorage
|
||||||
|
? this.reactiveExtensionStorage.value
|
||||||
|
: super.storage
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a ProseMirror plugin.
|
* Register a ProseMirror plugin.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user