mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-28 07:40:13 +08:00
fix copying mentions as plain text
This commit is contained in:
parent
80ec657053
commit
563f37d74b
@ -1,5 +1,5 @@
|
||||
import { Node, mergeAttributes } from '@tiptap/core'
|
||||
import { VueRenderer } from '@tiptap/vue'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue'
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
@ -30,6 +30,6 @@ export default Node.create({
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return VueRenderer(Component)
|
||||
return VueNodeViewRenderer(Component)
|
||||
},
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Node, mergeAttributes } from '@tiptap/core'
|
||||
import { VueRenderer } from '@tiptap/vue'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue'
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
@ -24,6 +24,6 @@ export default Node.create({
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return VueRenderer(Component)
|
||||
return VueNodeViewRenderer(Component)
|
||||
},
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Node, mergeAttributes } from '@tiptap/core'
|
||||
import { VueRenderer } from '@tiptap/vue'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue'
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
@ -22,6 +22,6 @@ export default Node.create({
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return VueRenderer(Component)
|
||||
return VueNodeViewRenderer(Component)
|
||||
},
|
||||
})
|
||||
|
@ -7,7 +7,7 @@ Node views are the best thing since sliced bread, at least if you’re a fan of
|
||||
|
||||
<!-- ```js
|
||||
import { Node } from '@tiptap/core'
|
||||
import { VueRenderer } from '@tiptap/vue'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue'
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
@ -83,12 +83,12 @@ https://github.com/ueberdosis/tiptap-next/blob/main/packages/extension-task-item
|
||||
|
||||
```js
|
||||
import { Node } from '@tiptap/core'
|
||||
import { VueRenderer } from '@tiptap/vue'
|
||||
import { VueNodeViewRenderer } from '@tiptap/vue'
|
||||
import Component from './Component.vue'
|
||||
|
||||
export default Node.create({
|
||||
addNodeView() {
|
||||
return VueRenderer(Component)
|
||||
return VueNodeViewRenderer(Component)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
@ -29,7 +29,7 @@ export class Editor extends EventEmitter {
|
||||
|
||||
private commandManager!: CommandManager
|
||||
|
||||
private extensionManager!: ExtensionManager
|
||||
public extensionManager!: ExtensionManager
|
||||
|
||||
private css!: HTMLStyleElement
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { Editor } from './Editor'
|
||||
import { Extensions, NodeViewRenderer } from './types'
|
||||
import getSchema from './helpers/getSchema'
|
||||
import getSchemaTypeByName from './helpers/getSchemaTypeByName'
|
||||
import getNodeType from './helpers/getNodeType'
|
||||
import splitExtensions from './helpers/splitExtensions'
|
||||
import getAttributesFromExtensions from './helpers/getAttributesFromExtensions'
|
||||
import getRenderedAttributes from './helpers/getRenderedAttributes'
|
||||
@ -145,11 +146,9 @@ export default class ExtensionManager {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor,
|
||||
type: getSchemaTypeByName(extension.config.name, this.schema),
|
||||
type: getNodeType(extension.config.name, this.schema),
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const renderer = extension.config.addNodeView?.bind(context)?.() as NodeViewRenderer
|
||||
const renderer = extension.config.addNodeView?.call(context) as NodeViewRenderer
|
||||
|
||||
const nodeview = (
|
||||
node: ProsemirrorNode,
|
||||
@ -173,4 +172,23 @@ export default class ExtensionManager {
|
||||
}))
|
||||
}
|
||||
|
||||
get textSerializers() {
|
||||
const { editor } = this
|
||||
const { nodeExtensions } = splitExtensions(this.extensions)
|
||||
|
||||
return Object.fromEntries(nodeExtensions
|
||||
.filter(extension => !!extension.config.renderText)
|
||||
.map(extension => {
|
||||
const context = {
|
||||
options: extension.options,
|
||||
editor,
|
||||
type: getNodeType(extension.config.name, this.schema),
|
||||
}
|
||||
|
||||
const textSerializer = (props: { node: ProsemirrorNode }) => extension.config.renderText?.call(context, props)
|
||||
|
||||
return [extension.config.name, textSerializer]
|
||||
}))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -88,6 +88,20 @@ export interface NodeConfig<Options = any, Commands = {}> extends Overwrite<Exte
|
||||
}
|
||||
) => DOMOutputSpec) | null,
|
||||
|
||||
/**
|
||||
* Render Text
|
||||
*/
|
||||
renderText?: ((
|
||||
this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode,
|
||||
}
|
||||
) => string) | null,
|
||||
|
||||
/**
|
||||
* Add Attributes
|
||||
*/
|
||||
@ -257,6 +271,7 @@ export class Node<Options = any, Commands = {}> {
|
||||
isolating: null,
|
||||
parseHTML: () => null,
|
||||
renderHTML: null,
|
||||
renderText: null,
|
||||
addAttributes: () => ({}),
|
||||
addNodeView: null,
|
||||
onCreate: null,
|
||||
|
60
packages/core/src/extensions/clipboardTextSerializer.ts
Normal file
60
packages/core/src/extensions/clipboardTextSerializer.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Extension } from '../Extension'
|
||||
|
||||
const textBetween = (
|
||||
editor: Editor,
|
||||
from: number,
|
||||
to: number,
|
||||
blockSeparator?: string,
|
||||
leafText?: string,
|
||||
): string => {
|
||||
let text = ''
|
||||
let separated = true
|
||||
|
||||
editor.state.doc.nodesBetween(from, to, (node, pos) => {
|
||||
const textSerializer = editor.extensionManager.textSerializers[node.type.name]
|
||||
|
||||
if (textSerializer) {
|
||||
text += textSerializer({ node })
|
||||
separated = !blockSeparator
|
||||
} else if (node.isText) {
|
||||
text += node?.text?.slice(Math.max(from, pos) - pos, to - pos)
|
||||
separated = !blockSeparator
|
||||
} else if (node.isLeaf && leafText) {
|
||||
text += leafText
|
||||
separated = !blockSeparator
|
||||
} else if (!separated && node.isBlock) {
|
||||
text += blockSeparator
|
||||
separated = true
|
||||
}
|
||||
}, 0)
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
export const ClipboardTextSerializer = Extension.create({
|
||||
name: 'editable',
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('clipboardTextSerializer'),
|
||||
props: {
|
||||
clipboardTextSerializer: () => {
|
||||
const { editor } = this
|
||||
const { from, to } = editor.state.selection
|
||||
|
||||
return textBetween(editor, from, to, '\n')
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface AllExtensions {
|
||||
ClipboardTextSerializer: typeof ClipboardTextSerializer,
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export { ClipboardTextSerializer } from './clipboardTextSerializer'
|
||||
export { Commands } from './commands'
|
||||
export { Editable } from './editable'
|
||||
export { FocusEvents } from './focusEvents'
|
||||
|
@ -60,6 +60,10 @@ export const Mention = Node.create({
|
||||
return ['span', HTMLAttributes, `@${node.attrs.id}`]
|
||||
},
|
||||
|
||||
renderText({ node }) {
|
||||
return `@${node.attrs.id}`
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
Suggestion({
|
||||
|
@ -16,7 +16,10 @@ export type SuggestionMatch = {
|
||||
|
||||
export function findSuggestionMatch(config: Trigger): SuggestionMatch {
|
||||
const {
|
||||
char, allowSpaces, startOfLine, $position,
|
||||
char,
|
||||
allowSpaces,
|
||||
startOfLine,
|
||||
$position,
|
||||
} = config
|
||||
|
||||
// cancel if top level node
|
||||
|
Loading…
Reference in New Issue
Block a user