add paste rules

This commit is contained in:
Philipp Kühn
2018-11-26 11:11:39 +01:00
parent df402a66ad
commit d27b0deb6a
6 changed files with 97 additions and 1 deletions

View File

@@ -23,6 +23,7 @@
"prosemirror-commands": "^1.0.7", "prosemirror-commands": "^1.0.7",
"prosemirror-inputrules": "^1.0.1", "prosemirror-inputrules": "^1.0.1",
"prosemirror-schema-list": "^1.0.1", "prosemirror-schema-list": "^1.0.1",
"prosemirror-state": "^1.2.2",
"tiptap-utils": "^1.0.1" "tiptap-utils": "^1.0.1"
} }
} }

View File

@@ -0,0 +1,52 @@
import { Plugin } from 'prosemirror-state'
import { Slice, Fragment } from 'prosemirror-model'
export default function (regexp, type, getAttrs) {
const handler = fragment => {
const nodes = []
fragment.forEach(child => {
if (child.isText) {
const { text } = child
let pos = 0
let match
do {
match = regexp.exec(text)
if (match) {
const start = match.index
const end = start + match[0].length
const attrs = getAttrs instanceof Function ? getAttrs(match[0]) : getAttrs
if (start > 0) {
nodes.push(child.cut(pos, start))
}
nodes.push(child
.cut(start, end)
.mark(type.create(attrs)
.addToSet(child.marks)))
pos = end
}
} while (match)
if (pos < text.length) {
nodes.push(child.cut(pos))
}
} else {
nodes.push(child.copy(handler(child.content)))
}
})
return Fragment.fromArray(nodes)
}
return new Plugin({
props: {
transformPasted: slice => new Slice(handler(slice.content), slice.openStart, slice.openEnd),
},
})
}

View File

@@ -48,6 +48,7 @@ import toggleBlockType from './commands/toggleBlockType'
import toggleList from './commands/toggleList' import toggleList from './commands/toggleList'
import toggleWrap from './commands/toggleWrap' import toggleWrap from './commands/toggleWrap'
import updateMark from './commands/updateMark' import updateMark from './commands/updateMark'
import wrappingPasteRule from './commands/wrappingPasteRule'
export { export {
// prosemirror-commands // prosemirror-commands
@@ -98,4 +99,5 @@ export {
toggleList, toggleList,
toggleWrap, toggleWrap,
updateMark, updateMark,
wrappingPasteRule,
} }

View File

@@ -1,5 +1,5 @@
import { Mark, Plugin, TextSelection } from 'tiptap' import { Mark, Plugin, TextSelection } from 'tiptap'
import { updateMark, removeMark } from 'tiptap-commands' import { updateMark, removeMark, wrappingPasteRule } from 'tiptap-commands'
import { getMarkRange } from 'tiptap-utils' import { getMarkRange } from 'tiptap-utils'
export default class Link extends Mark { export default class Link extends Mark {
@@ -41,6 +41,16 @@ export default class Link extends Mark {
} }
} }
pasteRules({ type }) {
return [
wrappingPasteRule(
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/g,
type,
url => ({ href: url }),
),
]
}
get plugins() { get plugins() {
return [ return [
new Plugin({ new Plugin({

View File

@@ -25,6 +25,7 @@ export default class Editor {
this.plugins = this.createPlugins() this.plugins = this.createPlugins()
this.keymaps = this.createKeymaps() this.keymaps = this.createKeymaps()
this.inputRules = this.createInputRules() this.inputRules = this.createInputRules()
this.pasteRules = this.createPasteRules()
this.state = this.createState() this.state = this.createState()
this.view = this.createView() this.view = this.createView()
this.commands = this.createCommands() this.commands = this.createCommands()
@@ -94,6 +95,12 @@ export default class Editor {
}) })
} }
createPasteRules() {
return this.extensions.pasteRules({
schema: this.schema,
})
}
createCommands() { createCommands() {
return this.extensions.commands({ return this.extensions.commands({
schema: this.schema, schema: this.schema,
@@ -126,6 +133,7 @@ export default class Editor {
inputRules({ inputRules({
rules: this.inputRules, rules: this.inputRules,
}), }),
...this.pasteRules,
...this.keymaps, ...this.keymaps,
keymap({ keymap({
Backspace: undoInputRule, Backspace: undoInputRule,

View File

@@ -76,6 +76,29 @@ export default class ExtensionManager {
]), []) ]), [])
} }
pasteRules({ schema }) {
const extensionPasteRules = this.extensions
.filter(extension => ['extension'].includes(extension.type))
.filter(extension => extension.pasteRules)
.map(extension => extension.pasteRules({ schema }))
const nodeMarkPasteRules = this.extensions
.filter(extension => ['node', 'mark'].includes(extension.type))
.filter(extension => extension.pasteRules)
.map(extension => extension.pasteRules({
type: schema[`${extension.type}s`][extension.name],
schema,
}))
return [
...extensionPasteRules,
...nodeMarkPasteRules,
].reduce((allPasteRules, pasteRules) => ([
...allPasteRules,
...pasteRules,
]), [])
}
commands({ schema, view, editable }) { commands({ schema, view, editable }) {
return this.extensions return this.extensions
.filter(extension => extension.commands) .filter(extension => extension.commands)