Skip to content

Commit

Permalink
feat: Add extension storage (#2069)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippkuehn authored Oct 22, 2021
1 parent 6987505 commit 7ffabf2
Show file tree
Hide file tree
Showing 26 changed files with 555 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
19 changes: 19 additions & 0 deletions demos/src/Experiments/ExtensionStorage/React/CustomExtension.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions demos/src/Experiments/ExtensionStorage/React/index.html
Original file line number Diff line number Diff line change
@@ -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 changes: 33 additions & 0 deletions demos/src/Experiments/ExtensionStorage/React/index.jsx
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions demos/src/Experiments/ExtensionStorage/React/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* Basic editor styles */
.ProseMirror {
> * + * {
margin-top: 0.75em;
}
}
19 changes: 19 additions & 0 deletions demos/src/Experiments/ExtensionStorage/Vue/CustomExtension.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions demos/src/Experiments/ExtensionStorage/Vue/index.html
Original file line number Diff line number Diff line change
@@ -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 changes: 56 additions & 0 deletions demos/src/Experiments/ExtensionStorage/Vue/index.vue
Original file line number Diff line number Diff line change
@@ -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>
33 changes: 33 additions & 0 deletions docs/guide/custom-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
19 changes: 19 additions & 0 deletions docs/guide/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
Expand Down Expand Up @@ -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.
*/
Expand Down
Loading

0 comments on commit 7ffabf2

Please sign in to comment.