add basic inputrules and pasterules
This commit is contained in:
@@ -4,4 +4,7 @@ export default Editor
|
|||||||
export { Editor }
|
export { Editor }
|
||||||
export { default as Extension } from './src/Extension'
|
export { default as Extension } from './src/Extension'
|
||||||
export { default as Node } from './src/Node'
|
export { default as Node } from './src/Node'
|
||||||
export { default as Mark } from './src/Mark'
|
export { default as Mark } from './src/Mark'
|
||||||
|
|
||||||
|
export { default as markInputRule } from './src/inputRules/markInputRule'
|
||||||
|
export { default as markPasteRule } from './src/pasteRules/markPasteRule'
|
||||||
@@ -122,6 +122,8 @@ export class Editor extends EventEmitter {
|
|||||||
return [
|
return [
|
||||||
...this.extensionManager.plugins,
|
...this.extensionManager.plugins,
|
||||||
...this.extensionManager.keymaps,
|
...this.extensionManager.keymaps,
|
||||||
|
...this.extensionManager.pasteRules,
|
||||||
|
inputRules({ rules: this.extensionManager.inputRules }),
|
||||||
keymap({ Backspace: undoInputRule }),
|
keymap({ Backspace: undoInputRule }),
|
||||||
keymap(baseKeymap),
|
keymap(baseKeymap),
|
||||||
dropCursor(),
|
dropCursor(),
|
||||||
|
|||||||
@@ -48,6 +48,18 @@ export default class ExtensionManager {
|
|||||||
.toArray()
|
.toArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get inputRules(): any {
|
||||||
|
return collect(this.extensions)
|
||||||
|
.flatMap(extension => extension.inputRules())
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
get pasteRules(): any {
|
||||||
|
return collect(this.extensions)
|
||||||
|
.flatMap(extension => extension.pasteRules())
|
||||||
|
.toArray()
|
||||||
|
}
|
||||||
|
|
||||||
get keymaps() {
|
get keymaps() {
|
||||||
return collect(this.extensions)
|
return collect(this.extensions)
|
||||||
.map(extension => extension.keys())
|
.map(extension => extension.keys())
|
||||||
|
|||||||
60
packages/core/src/inputRules/markInputRule.ts
Normal file
60
packages/core/src/inputRules/markInputRule.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { InputRule } from 'prosemirror-inputrules'
|
||||||
|
import { EditorState } from 'prosemirror-state'
|
||||||
|
import { MarkType } from 'prosemirror-model'
|
||||||
|
|
||||||
|
function getMarksBetween(start: number, end: number, state: EditorState) {
|
||||||
|
let marks: any[] = []
|
||||||
|
|
||||||
|
state.doc.nodesBetween(start, end, (node, pos) => {
|
||||||
|
marks = [...marks, ...node.marks.map(mark => ({
|
||||||
|
start: pos,
|
||||||
|
end: pos + node.nodeSize,
|
||||||
|
mark,
|
||||||
|
}))]
|
||||||
|
})
|
||||||
|
|
||||||
|
return marks
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (regexp: RegExp, markType: MarkType, getAttrs?: Function) {
|
||||||
|
return new InputRule(regexp, (state, match, start, end) => {
|
||||||
|
const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
|
||||||
|
const { tr } = state
|
||||||
|
const m = match.length - 1
|
||||||
|
let markEnd = end
|
||||||
|
let markStart = start
|
||||||
|
|
||||||
|
if (match[m]) {
|
||||||
|
const matchStart = start + match[0].indexOf(match[m - 1])
|
||||||
|
const matchEnd = matchStart + match[m - 1].length - 1
|
||||||
|
const textStart = matchStart + match[m - 1].lastIndexOf(match[m])
|
||||||
|
const textEnd = textStart + match[m].length
|
||||||
|
|
||||||
|
const excludedMarks = getMarksBetween(start, end, state)
|
||||||
|
.filter(item => {
|
||||||
|
const { excluded } = item.mark.type
|
||||||
|
return excluded.find((type: MarkType) => type.name === markType.name)
|
||||||
|
})
|
||||||
|
.filter(item => item.end > matchStart)
|
||||||
|
|
||||||
|
if (excludedMarks.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textEnd < matchEnd) {
|
||||||
|
tr.delete(textEnd, matchEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textStart > matchStart) {
|
||||||
|
tr.delete(matchStart, textStart)
|
||||||
|
}
|
||||||
|
|
||||||
|
markStart = matchStart
|
||||||
|
markEnd = markStart + match[m].length
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.addMark(markStart, markEnd, markType.create(attrs))
|
||||||
|
tr.removeStoredMark(markType)
|
||||||
|
return tr
|
||||||
|
})
|
||||||
|
}
|
||||||
60
packages/core/src/pasteRules/markPasteRule.ts
Normal file
60
packages/core/src/pasteRules/markPasteRule.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Plugin } from 'prosemirror-state'
|
||||||
|
import { Slice, Fragment, MarkType } from 'prosemirror-model'
|
||||||
|
|
||||||
|
export default function (regexp: RegExp, type: MarkType, getAttrs?: Function) {
|
||||||
|
|
||||||
|
const handler = (fragment: Fragment, parent?: any) => {
|
||||||
|
const nodes: any[] = []
|
||||||
|
|
||||||
|
fragment.forEach(child => {
|
||||||
|
if (child.isText && child.text) {
|
||||||
|
const { text } = child
|
||||||
|
let pos = 0
|
||||||
|
let match
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
while ((match = regexp.exec(text)) !== null) {
|
||||||
|
if (parent.type.allowsMarkType(type) && match[1]) {
|
||||||
|
const start = match.index
|
||||||
|
const end = start + match[0].length
|
||||||
|
const textStart = start + match[0].indexOf(match[1])
|
||||||
|
const textEnd = textStart + match[1].length
|
||||||
|
const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
|
||||||
|
|
||||||
|
// adding text before markdown to nodes
|
||||||
|
if (start > 0) {
|
||||||
|
nodes.push(child.cut(pos, start))
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding the markdown part to nodes
|
||||||
|
nodes.push(child
|
||||||
|
.cut(textStart, textEnd)
|
||||||
|
// @ts-ignore
|
||||||
|
.mark(type.create(attrs).addToSet(child.marks)))
|
||||||
|
|
||||||
|
pos = end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding rest of text to nodes
|
||||||
|
if (pos < text.length) {
|
||||||
|
nodes.push(child.cut(pos))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodes.push(child.copy(handler(child.content, child)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return Fragment.fromArray(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Plugin({
|
||||||
|
props: {
|
||||||
|
transformPasted: slice => {
|
||||||
|
console.log({slice})
|
||||||
|
return new Slice(handler(slice.content), slice.openStart, slice.openEnd)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Mark } from '@tiptap/core'
|
import { Mark, markInputRule, markPasteRule } from '@tiptap/core'
|
||||||
import { toggleMark } from 'prosemirror-commands'
|
import { toggleMark } from 'prosemirror-commands'
|
||||||
import { MarkSpec } from 'prosemirror-model'
|
import { MarkSpec } from 'prosemirror-model'
|
||||||
|
|
||||||
@@ -44,4 +44,16 @@ export default class Bold extends Mark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputRules() {
|
||||||
|
return [
|
||||||
|
markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/, this.schemaType),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteRules() {
|
||||||
|
return [
|
||||||
|
markPasteRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)/g, this.schemaType),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
23
packages/extension-codeblock/index.ts
Normal file
23
packages/extension-codeblock/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Node } from '@tiptap/core'
|
||||||
|
import { NodeSpec } from 'prosemirror-model'
|
||||||
|
|
||||||
|
export default class CodeBlock extends Node {
|
||||||
|
|
||||||
|
name = 'code_block'
|
||||||
|
|
||||||
|
schema(): NodeSpec {
|
||||||
|
return {
|
||||||
|
content: 'text*',
|
||||||
|
marks: 'italic',
|
||||||
|
group: 'block',
|
||||||
|
code: true,
|
||||||
|
defining: true,
|
||||||
|
draggable: false,
|
||||||
|
parseDOM: [
|
||||||
|
{ tag: 'pre', preserveWhitespace: 'full' },
|
||||||
|
],
|
||||||
|
toDOM: () => ['pre', ['code', 0]],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
packages/extension-codeblock/package.json
Normal file
17
packages/extension-codeblock/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "@tiptap/extension-codeblock",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"source": "index.ts",
|
||||||
|
"main": "dist/tiptap-extension-codeblock.js",
|
||||||
|
"umd:main": "dist/tiptap-extension-codeblock.umd.js",
|
||||||
|
"module": "dist/tiptap-extension-codeblock.mjs",
|
||||||
|
"unpkg": "dist/tiptap-extension-codeblock.js",
|
||||||
|
"jsdelivr": "dist/tiptap-extension-codeblock.js",
|
||||||
|
"files": [
|
||||||
|
"src",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tiptap/core": "2.x"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Mark } from '@tiptap/core'
|
import { Mark, markInputRule, markPasteRule } from '@tiptap/core'
|
||||||
import { toggleMark } from 'prosemirror-commands'
|
import { toggleMark } from 'prosemirror-commands'
|
||||||
import { MarkSpec } from 'prosemirror-model'
|
import { MarkSpec } from 'prosemirror-model'
|
||||||
|
|
||||||
@@ -36,4 +36,18 @@ export default class Italic extends Mark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputRules() {
|
||||||
|
return [
|
||||||
|
markInputRule(/(?:^|[^_])(_([^_]+)_)$/, this.schemaType),
|
||||||
|
// markInputRule(/(?:^|[^*])(\*([^*]+)\*)$/, this.schemaType),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pasteRules() {
|
||||||
|
return [
|
||||||
|
markPasteRule(/_([^_]+)_/g, this.schemaType),
|
||||||
|
// markPasteRule(/\*([^*]+)\*/g, this.schemaType),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,7 @@ import Text from '@tiptap/extension-text'
|
|||||||
import History from '@tiptap/extension-history'
|
import History from '@tiptap/extension-history'
|
||||||
import Bold from '@tiptap/extension-bold'
|
import Bold from '@tiptap/extension-bold'
|
||||||
import Italic from '@tiptap/extension-italic'
|
import Italic from '@tiptap/extension-italic'
|
||||||
|
import CodeBlock from '@tiptap/extension-codeblock'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -41,11 +42,12 @@ export default {
|
|||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
content: '<p>foo</p>',
|
content: '<p>foo</p><pre>code **bold** _italic_</pre>',
|
||||||
extensions: [
|
extensions: [
|
||||||
new Document(),
|
new Document(),
|
||||||
new Paragraph(),
|
new Paragraph(),
|
||||||
new Text(),
|
new Text(),
|
||||||
|
new CodeBlock(),
|
||||||
new History(),
|
new History(),
|
||||||
new Bold(),
|
new Bold(),
|
||||||
new Italic(),
|
new Italic(),
|
||||||
|
|||||||
Reference in New Issue
Block a user