move history plugin to extension package

This commit is contained in:
Philipp Kühn
2018-08-25 21:33:06 +02:00
parent bf1a091c94
commit deb8cb33b0
21 changed files with 108 additions and 42 deletions

View File

@@ -46,7 +46,7 @@ export default {
## Editor Properties ## Editor Properties
| **Property** | **Type** | **Default** | **Description** | | **Property** | **Type** | **Default** | **Description** |
| --- | :---: | :---: | --- | | --- | :---: | :---: | --- |
| `editable` | `Boolean` | `true` | When set to `false` the editor is read-only. | | `editable` | `Boolean` | `true` | When set to `false` the editor is read-only. |
| `doc` | `Object` | `null` | The editor state object used by Prosemirror. You can also pass HTML to the `content` slot. When used both, the `content` slot will be ignored. | | `doc` | `Object` | `null` | The editor state object used by Prosemirror. You can also pass HTML to the `content` slot. When used both, the `content` slot will be ignored. |
| `extensions` | `Array` | `[]` | A list of extensions used, by the editor. This can be `Nodes`, `Marks` or `Plugins`. | | `extensions` | `Array` | `[]` | A list of extensions used, by the editor. This can be `Nodes`, `Marks` or `Plugins`. |
@@ -56,7 +56,7 @@ export default {
By default the editor will only support some boring paragraphs. Other nodes and marks are available as **extensions**. There is a package called `tiptap-extensions` with the most basic nodes, marks and plugins. By default the editor will only support some boring paragraphs. Other nodes and marks are available as **extensions**. There is a package called `tiptap-extensions` with the most basic nodes, marks and plugins.
### Available Extensions ### Available Extensions
```vue ```vue
<template> <template>
@@ -84,6 +84,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -106,6 +107,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },
@@ -113,12 +115,12 @@ export default {
</script> </script>
``` ```
### Create Custom Extensions ### Create Custom Extensions
The most powerful feature of tiptap is that you can create you own extensions. There are 3 basic types of extensions. The most powerful feature of tiptap is that you can create you own extensions. There are 3 basic types of extensions.
| **Type** | **Description** | | **Type** | **Description** |
| --- | --- | | --- | --- |
| `Extension` | The most basic type. It's useful to register some [Prosemirror plugins](https://prosemirror.net/docs/guide/) or some input rules. | | `Extension` | The most basic type. It's useful to register some [Prosemirror plugins](https://prosemirror.net/docs/guide/) or some input rules. |
| `Node` | Add a custom node. Nodes are basically block elements like a headline or a paragraph. | | `Node` | Add a custom node. Nodes are basically block elements like a headline or a paragraph. |
| `Mark` | Add a custom mark. Marks are used to add extra styling or other information to inline content like a strong tag or links. | | `Mark` | Add a custom mark. Marks are used to add extra styling or other information to inline content like a strong tag or links. |
@@ -126,16 +128,17 @@ The most powerful feature of tiptap is that you can create you own extensions. T
### Extension Class ### Extension Class
| **Method** | **Type** | **Default** | **Description** | | **Method** | **Type** | **Default** | **Description** |
| --- | :---: | :---: | --- | | --- | :---: | :---: | --- |
| `get name()` | `String` | `null` | Define a name for your extension. | | `get name()` | `String` | `null` | Define a name for your extension. |
| `get defaultOptions()` | `Object` | `{}` | Define some default options. The options are available as `this.$options`. | | `get defaultOptions()` | `Object` | `{}` | Define some default options. The options are available as `this.$options`. |
| `get plugins()` | `Array` | `[]` | Define a list of [Prosemirror plugins](https://prosemirror.net/docs/guide/). | | `get plugins()` | `Array` | `[]` | Define a list of [Prosemirror plugins](https://prosemirror.net/docs/guide/). |
| `get inputRules()` | `Array` | `[]` | Define a list of input rules. | | `keys({ schema })` | `Object` | `null` | Define some keybindings. |
| `inputRules({ schema })` | `Array` | `[]` | Define a list of input rules. |
### Node|Mark Class ### Node|Mark Class
| **Method** | **Type** | **Default** | **Description** | | **Method** | **Type** | **Default** | **Description** |
| --- | :---: | :---: | --- | | --- | :---: | :---: | --- |
| `get name()` | `String` | `null` | Define a name for your node or mark. | | `get name()` | `String` | `null` | Define a name for your node or mark. |
| `get defaultOptions()` | `Object` | `{}` | Define some default options. The options are available as `this.$options`. | | `get defaultOptions()` | `Object` | `{}` | Define some default options. The options are available as `this.$options`. |
| `get schema()` | `Object` | `null` | Define a [schema](https://prosemirror.net/docs/guide/#schema). | | `get schema()` | `Object` | `null` | Define a [schema](https://prosemirror.net/docs/guide/#schema). |
@@ -154,12 +157,12 @@ import { Node } from 'tiptap'
import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands' import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands'
export default class BlockquoteNode extends Node { export default class BlockquoteNode extends Node {
// choose a unique name // choose a unique name
get name() { get name() {
return 'blockquote' return 'blockquote'
} }
// the prosemirror schema object // the prosemirror schema object
// take a look at https://prosemirror.net/docs/guide/#schema for a detailed explanation // take a look at https://prosemirror.net/docs/guide/#schema for a detailed explanation
get schema() { get schema() {
@@ -179,14 +182,14 @@ export default class BlockquoteNode extends Node {
toDOM: () => ['blockquote', { class: 'awesome-blockquote' }, 0], toDOM: () => ['blockquote', { class: 'awesome-blockquote' }, 0],
} }
} }
// this command will be called from menus to add a blockquote // this command will be called from menus to add a blockquote
// `type` is the prosemirror schema object for this blockquote // `type` is the prosemirror schema object for this blockquote
// `schema` is a collection of all registered nodes and marks // `schema` is a collection of all registered nodes and marks
command({ type, schema }) { command({ type, schema }) {
return wrapIn(type) return wrapIn(type)
} }
// here you can register some shortcuts // here you can register some shortcuts
// in this case you can create a blockquote with `ctrl` + `>` // in this case you can create a blockquote with `ctrl` + `>`
keys({ type }) { keys({ type }) {
@@ -194,7 +197,7 @@ export default class BlockquoteNode extends Node {
'Ctrl->': wrapIn(type), 'Ctrl->': wrapIn(type),
} }
} }
// a blockquote will be created when you are on a new line and type `>` followed by a space // a blockquote will be created when you are on a new line and type `>` followed by a space
inputRules({ type }) { inputRules({ type }) {
return [ return [
@@ -242,7 +245,7 @@ export default class IframeNode extends Node {
}], }],
} }
} }
// return a vue component // return a vue component
// this can be an object or an imported component // this can be an object or an imported component
get view() { get view() {
@@ -261,7 +264,7 @@ export default class IframeNode extends Node {
methods: { methods: {
onChange(event) { onChange(event) {
this.url = event.target.value this.url = event.target.value
// update the iframe url // update the iframe url
this.updateAttrs({ this.updateAttrs({
src: this.url, src: this.url,

View File

@@ -132,6 +132,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -155,6 +156,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -33,6 +33,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
import Iframe from './Iframe.js' import Iframe from './Iframe.js'
@@ -57,6 +58,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
// custom extension // custom extension
new Iframe(), new Iframe(),
], ],

View File

@@ -118,6 +118,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -141,6 +142,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -53,6 +53,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -76,6 +77,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
linkUrl: null, linkUrl: null,
linkMenuIsActive: false, linkMenuIsActive: false,

View File

@@ -35,6 +35,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -58,6 +59,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -62,6 +62,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -85,6 +86,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -32,6 +32,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -55,6 +56,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -84,6 +84,7 @@ import {
Code, Code,
Italic, Italic,
Link, Link,
History,
} from 'tiptap-extensions' } from 'tiptap-extensions'
export default { export default {
@@ -107,6 +108,7 @@ export default {
new Code(), new Code(),
new Italic(), new Italic(),
new Link(), new Link(),
new History(),
], ],
} }
}, },

View File

@@ -20,6 +20,7 @@
"url": "https://github.com/heyscrumpy/tiptap/issues" "url": "https://github.com/heyscrumpy/tiptap/issues"
}, },
"dependencies": { "dependencies": {
"prosemirror-history": "^1.0.2",
"tiptap": "^0.5.1", "tiptap": "^0.5.1",
"tiptap-commands": "^0.2.0" "tiptap-commands": "^0.2.0"
} }

View File

@@ -0,0 +1,30 @@
import { Extension } from 'tiptap'
import { history, undo, redo } from 'prosemirror-history'
export default class HistoryExtension extends Extension {
get name() {
return 'history'
}
keys() {
const isMac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false
const keymap = {
'Mod-z': undo,
'Shift-Mod-z': redo,
}
if (!isMac) {
keymap['Mod-y'] = redo
}
return keymap
}
get plugins() {
return [
history(),
]
}
}

View File

@@ -12,3 +12,5 @@ export { default as Bold } from './marks/Bold'
export { default as Code } from './marks/Code' export { default as Code } from './marks/Code'
export { default as Italic } from './marks/Italic' export { default as Italic } from './marks/Italic'
export { default as Link } from './marks/Link' export { default as Link } from './marks/Link'
export { default as History } from './extensions/History'

View File

@@ -26,7 +26,6 @@
"dependencies": { "dependencies": {
"prosemirror-commands": "^1.0.7", "prosemirror-commands": "^1.0.7",
"prosemirror-gapcursor": "^1.0.2", "prosemirror-gapcursor": "^1.0.2",
"prosemirror-history": "^1.0.2",
"prosemirror-inputrules": "^1.0.1", "prosemirror-inputrules": "^1.0.1",
"prosemirror-keymap": "^1.0.1", "prosemirror-keymap": "^1.0.1",
"prosemirror-model": "^1.5.0", "prosemirror-model": "^1.5.0",

View File

@@ -2,7 +2,6 @@ import { EditorState, Plugin } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view' import { EditorView } from 'prosemirror-view'
import { Schema, DOMParser } from 'prosemirror-model' import { Schema, DOMParser } from 'prosemirror-model'
import { gapCursor } from 'prosemirror-gapcursor' import { gapCursor } from 'prosemirror-gapcursor'
import { history } from 'prosemirror-history'
import { keymap } from 'prosemirror-keymap' import { keymap } from 'prosemirror-keymap'
import { baseKeymap } from 'prosemirror-commands' import { baseKeymap } from 'prosemirror-commands'
import { inputRules } from 'prosemirror-inputrules' import { inputRules } from 'prosemirror-inputrules'
@@ -165,7 +164,6 @@ export default {
keymap(builtInKeymap), keymap(builtInKeymap),
keymap(baseKeymap), keymap(baseKeymap),
gapCursor(), gapCursor(),
history(),
new Plugin({ new Plugin({
props: { props: {
editable: () => this.editable, editable: () => this.editable,

View File

@@ -35,6 +35,7 @@ export default class ExtensionManager {
get views() { get views() {
return this.extensions return this.extensions
.filter(extension => ['node', 'mark'].includes(extension.type))
.filter(extension => extension.view) .filter(extension => extension.view)
.reduce((views, { name, view }) => ({ .reduce((views, { name, view }) => ({
...views, ...views,
@@ -43,30 +44,60 @@ export default class ExtensionManager {
} }
keymaps({ schema }) { keymaps({ schema }) {
return this.extensions const extensionKeymaps = this.extensions
.filter(extension => ['extension'].includes(extension.type))
.filter(extension => extension.keys)
.map(extension => extension.keys({ schema }))
const nodeMarkKeymaps = this.extensions
.filter(extension => ['node', 'mark'].includes(extension.type))
.filter(extension => extension.keys) .filter(extension => extension.keys)
.map(extension => extension.keys({ .map(extension => extension.keys({
type: schema[`${extension.type}s`][extension.name], type: schema[`${extension.type}s`][extension.name],
schema, schema,
})) }))
.map(keys => keymap(keys))
return [
...extensionKeymaps,
...nodeMarkKeymaps,
].map(keys => keymap(keys))
// return this.extensions
// .filter(extension => ['node', 'mark'].includes(extension.type))
// .filter(extension => extension.keys)
// .map(extension => extension.keys({
// type: schema[`${extension.type}s`][extension.name],
// schema,
// }))
// .map(keys => keymap(keys))
} }
inputRules({ schema }) { inputRules({ schema }) {
return this.extensions const extensionInputRules = this.extensions
.filter(extension => ['extension'].includes(extension.type))
.filter(extension => extension.inputRules)
.map(extension => extension.inputRules({ schema }))
const nodeMarkInputRules = this.extensions
.filter(extension => ['node', 'mark'].includes(extension.type))
.filter(extension => extension.inputRules) .filter(extension => extension.inputRules)
.map(extension => extension.inputRules({ .map(extension => extension.inputRules({
type: schema[`${extension.type}s`][extension.name], type: schema[`${extension.type}s`][extension.name],
schema, schema,
})) }))
.reduce((allInputRules, inputRules) => ([
...allInputRules, return [
...inputRules, ...extensionInputRules,
]), []) ...nodeMarkInputRules,
].reduce((allInputRules, inputRules) => ([
...allInputRules,
...inputRules,
]), [])
} }
commands({ schema, view }) { commands({ schema, view }) {
return this.extensions return this.extensions
.filter(extension => ['node', 'mark'].includes(extension.type))
.filter(extension => extension.command) .filter(extension => extension.command)
.reduce((commands, { name, type, command }) => ({ .reduce((commands, { name, type, command }) => ({
...commands, ...commands,

View File

@@ -1,18 +1,10 @@
import { lift, selectParentNode } from 'prosemirror-commands' import { lift, selectParentNode } from 'prosemirror-commands'
import { undo, redo } from 'prosemirror-history'
import { undoInputRule } from 'prosemirror-inputrules' import { undoInputRule } from 'prosemirror-inputrules'
import isMac from './isMac'
const keymap = { const keymap = {
'Mod-z': undo,
'Shift-Mod-z': undo,
'Mod-BracketLeft': lift, 'Mod-BracketLeft': lift,
Backspace: undoInputRule, Backspace: undoInputRule,
Escape: selectParentNode, Escape: selectParentNode,
} }
if (!isMac) {
keymap['Mod-y'] = redo
}
export default keymap export default keymap

View File

@@ -27,4 +27,8 @@ export default class Extension {
return [] return []
} }
keys() {
return {}
}
} }

View File

@@ -2,6 +2,5 @@ export { default as buildMenuActions } from './buildMenuActions'
export { default as builtInKeymap } from './builtInKeymap' export { default as builtInKeymap } from './builtInKeymap'
export { default as ComponentView } from './ComponentView' export { default as ComponentView } from './ComponentView'
export { default as initNodeViews } from './initNodeViews' export { default as initNodeViews } from './initNodeViews'
export { default as isMac } from './isMac'
export { default as menuBubble } from './menuBubble' export { default as menuBubble } from './menuBubble'
export { default as ExtensionManager } from './ExtensionManager' export { default as ExtensionManager } from './ExtensionManager'

View File

@@ -1 +0,0 @@
export default typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false

View File

@@ -22,8 +22,4 @@ export default class Mark extends Extension {
return () => {} return () => {}
} }
keys() {
return {}
}
} }

View File

@@ -22,8 +22,4 @@ export default class Node extends Extension {
return () => {} return () => {}
} }
keys() {
return {}
}
} }