fix: add way to cancel inputrules and pasterules (#2368)
Co-authored-by: Philipp Kühn <philippkuehn@MacBook-Pro-von-Philipp.local>
This commit is contained in:
@@ -306,7 +306,7 @@ export class ExtensionManager {
|
|||||||
editor,
|
editor,
|
||||||
rules: inputRules,
|
rules: inputRules,
|
||||||
}),
|
}),
|
||||||
pasteRulesPlugin({
|
...pasteRulesPlugin({
|
||||||
editor,
|
editor,
|
||||||
rules: pasteRules,
|
rules: pasteRules,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { EditorState, Plugin, TextSelection } from 'prosemirror-state'
|
import {
|
||||||
|
EditorState,
|
||||||
|
Plugin,
|
||||||
|
TextSelection,
|
||||||
|
Transaction,
|
||||||
|
} from 'prosemirror-state'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
import { CommandManager } from './CommandManager'
|
import { CommandManager } from './CommandManager'
|
||||||
import { createChainableState } from './helpers/createChainableState'
|
import { createChainableState } from './helpers/createChainableState'
|
||||||
@@ -33,7 +38,7 @@ export class InputRule {
|
|||||||
commands: SingleCommands,
|
commands: SingleCommands,
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands,
|
||||||
can: () => CanCommands,
|
can: () => CanCommands,
|
||||||
}) => void
|
}) => Transaction | null
|
||||||
|
|
||||||
constructor(config: {
|
constructor(config: {
|
||||||
find: InputRuleFinder,
|
find: InputRuleFinder,
|
||||||
@@ -44,7 +49,7 @@ export class InputRule {
|
|||||||
commands: SingleCommands,
|
commands: SingleCommands,
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands,
|
||||||
can: () => CanCommands,
|
can: () => CanCommands,
|
||||||
}) => void,
|
}) => Transaction | null,
|
||||||
}) {
|
}) {
|
||||||
this.find = config.find
|
this.find = config.find
|
||||||
this.handler = config.handler
|
this.handler = config.handler
|
||||||
@@ -87,7 +92,7 @@ function run(config: {
|
|||||||
text: string,
|
text: string,
|
||||||
rules: InputRule[],
|
rules: InputRule[],
|
||||||
plugin: Plugin,
|
plugin: Plugin,
|
||||||
}): any {
|
}): boolean {
|
||||||
const {
|
const {
|
||||||
editor,
|
editor,
|
||||||
from,
|
from,
|
||||||
@@ -148,7 +153,7 @@ function run(config: {
|
|||||||
state,
|
state,
|
||||||
})
|
})
|
||||||
|
|
||||||
rule.handler({
|
const handler = rule.handler({
|
||||||
state,
|
state,
|
||||||
range,
|
range,
|
||||||
match,
|
match,
|
||||||
@@ -158,7 +163,7 @@ function run(config: {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// stop if there are no changes
|
// stop if there are no changes
|
||||||
if (!tr.steps.length) {
|
if (!handler || !tr.steps.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EditorState, Plugin } from 'prosemirror-state'
|
import { EditorState, Plugin, Transaction } from 'prosemirror-state'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
import { CommandManager } from './CommandManager'
|
import { CommandManager } from './CommandManager'
|
||||||
import { createChainableState } from './helpers/createChainableState'
|
import { createChainableState } from './helpers/createChainableState'
|
||||||
@@ -34,7 +34,7 @@ export class PasteRule {
|
|||||||
commands: SingleCommands,
|
commands: SingleCommands,
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands,
|
||||||
can: () => CanCommands,
|
can: () => CanCommands,
|
||||||
}) => void
|
}) => Transaction | null
|
||||||
|
|
||||||
constructor(config: {
|
constructor(config: {
|
||||||
find: PasteRuleFinder,
|
find: PasteRuleFinder,
|
||||||
@@ -45,7 +45,7 @@ export class PasteRule {
|
|||||||
commands: SingleCommands,
|
commands: SingleCommands,
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands,
|
||||||
can: () => CanCommands,
|
can: () => CanCommands,
|
||||||
}) => void,
|
}) => Transaction | null,
|
||||||
}) {
|
}) {
|
||||||
this.find = config.find
|
this.find = config.find
|
||||||
this.handler = config.handler
|
this.handler = config.handler
|
||||||
@@ -88,15 +88,14 @@ function run(config: {
|
|||||||
state: EditorState,
|
state: EditorState,
|
||||||
from: number,
|
from: number,
|
||||||
to: number,
|
to: number,
|
||||||
rules: PasteRule[],
|
rule: PasteRule,
|
||||||
plugin: Plugin,
|
}): boolean {
|
||||||
}): any {
|
|
||||||
const {
|
const {
|
||||||
editor,
|
editor,
|
||||||
state,
|
state,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
rules,
|
rule,
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
const { commands, chain, can } = new CommandManager({
|
const { commands, chain, can } = new CommandManager({
|
||||||
@@ -104,6 +103,8 @@ function run(config: {
|
|||||||
state,
|
state,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handlers: (Transaction | null)[] = []
|
||||||
|
|
||||||
state.doc.nodesBetween(from, to, (node, pos) => {
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
||||||
if (!node.isTextblock || node.type.spec.code) {
|
if (!node.isTextblock || node.type.spec.code) {
|
||||||
return
|
return
|
||||||
@@ -118,7 +119,6 @@ function run(config: {
|
|||||||
'\ufffc',
|
'\ufffc',
|
||||||
)
|
)
|
||||||
|
|
||||||
rules.forEach(rule => {
|
|
||||||
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
|
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
|
||||||
|
|
||||||
matches.forEach(match => {
|
matches.forEach(match => {
|
||||||
@@ -133,7 +133,7 @@ function run(config: {
|
|||||||
to: state.tr.mapping.map(end),
|
to: state.tr.mapping.map(end),
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.handler({
|
const handler = rule.handler({
|
||||||
state,
|
state,
|
||||||
range,
|
range,
|
||||||
match,
|
match,
|
||||||
@@ -141,9 +141,14 @@ function run(config: {
|
|||||||
chain,
|
chain,
|
||||||
can,
|
can,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
handlers.push(handler)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
const success = handlers.every(handler => handler !== null)
|
||||||
|
|
||||||
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,11 +156,12 @@ function run(config: {
|
|||||||
* text that matches any of the given rules to trigger the rule’s
|
* text that matches any of the given rules to trigger the rule’s
|
||||||
* action.
|
* action.
|
||||||
*/
|
*/
|
||||||
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin {
|
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin[] {
|
||||||
const { editor, rules } = props
|
const { editor, rules } = props
|
||||||
let isProseMirrorHTML = false
|
let isProseMirrorHTML = false
|
||||||
|
|
||||||
const plugin = new Plugin({
|
const plugins = rules.map(rule => {
|
||||||
|
return new Plugin({
|
||||||
props: {
|
props: {
|
||||||
handlePaste: (view, event) => {
|
handlePaste: (view, event) => {
|
||||||
const html = event.clipboardData?.getData('text/html')
|
const html = event.clipboardData?.getData('text/html')
|
||||||
@@ -174,9 +180,8 @@ export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }):
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stop if there is no changed range
|
// stop if there is no changed range
|
||||||
const { doc, before } = transaction
|
const from = oldState.doc.content.findDiffStart(state.doc.content)
|
||||||
const from = before.content.findDiffStart(doc.content)
|
const to = oldState.doc.content.findDiffEnd(state.doc.content)
|
||||||
const to = before.content.findDiffEnd(doc.content)
|
|
||||||
|
|
||||||
if (!isNumber(from) || !to || from === to.b) {
|
if (!isNumber(from) || !to || from === to.b) {
|
||||||
return
|
return
|
||||||
@@ -190,26 +195,23 @@ export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }):
|
|||||||
transaction: tr,
|
transaction: tr,
|
||||||
})
|
})
|
||||||
|
|
||||||
run({
|
const handler = run({
|
||||||
editor,
|
editor,
|
||||||
state: chainableState,
|
state: chainableState,
|
||||||
from: Math.max(from - 1, 0),
|
from: Math.max(from - 1, 0),
|
||||||
to: to.b,
|
to: to.b,
|
||||||
rules,
|
rule,
|
||||||
plugin,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// stop if there are no changes
|
// stop if there are no changes
|
||||||
if (!tr.steps.length) {
|
if (!handler || !tr.steps.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr
|
return tr
|
||||||
},
|
},
|
||||||
|
})
|
||||||
// @ts-ignore
|
|
||||||
isPasteRules: true,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return plugin
|
return plugins
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function markInputRule(config: {
|
|||||||
const attributes = callOrReturn(config.getAttributes, undefined, match)
|
const attributes = callOrReturn(config.getAttributes, undefined, match)
|
||||||
|
|
||||||
if (attributes === false || attributes === null) {
|
if (attributes === false || attributes === null) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tr } = state
|
const { tr } = state
|
||||||
@@ -64,6 +64,8 @@ export function markInputRule(config: {
|
|||||||
|
|
||||||
tr.removeStoredMark(config.type)
|
tr.removeStoredMark(config.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export function nodeInputRule(config: {
|
|||||||
} else if (match[0]) {
|
} else if (match[0]) {
|
||||||
tr.replaceWith(start, end, config.type.create(attributes))
|
tr.replaceWith(start, end, config.type.create(attributes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ export function textInputRule(config: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.tr.insertText(insert, start, end)
|
const { tr } = state
|
||||||
|
|
||||||
|
tr.insertText(insert, start, end)
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,9 +29,12 @@ export function textblockTypeInputRule(config: {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
state.tr
|
const { tr } = state
|
||||||
.delete(range.from, range.to)
|
|
||||||
|
tr.delete(range.from, range.to)
|
||||||
.setBlockType(range.from, range.from, config.type, attributes)
|
.setBlockType(range.from, range.from, config.type, attributes)
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ export function wrappingInputRule(config: {
|
|||||||
) {
|
) {
|
||||||
tr.join(range.from - 1)
|
tr.join(range.from - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function markPasteRule(config: {
|
|||||||
const attributes = callOrReturn(config.getAttributes, undefined, match)
|
const attributes = callOrReturn(config.getAttributes, undefined, match)
|
||||||
|
|
||||||
if (attributes === false || attributes === null) {
|
if (attributes === false || attributes === null) {
|
||||||
return
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tr } = state
|
const { tr } = state
|
||||||
@@ -64,6 +64,8 @@ export function markPasteRule(config: {
|
|||||||
|
|
||||||
tr.removeStoredMark(config.type)
|
tr.removeStoredMark(config.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ export function textPasteRule(config: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.tr.insertText(insert, start, end)
|
const { tr } = state
|
||||||
|
|
||||||
|
tr.insertText(insert, start, end)
|
||||||
|
|
||||||
|
return tr
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user