@tiptap/extension-link 2.1.0-rc.0 → 2.1.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +94 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +95 -58
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +94 -57
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/extension-link/src/helpers/pasteHandler.d.ts +1 -0
- package/dist/packages/extension-link/src/index.d.ts +2 -2
- package/package.json +3 -3
- package/src/helpers/autolink.ts +32 -14
- package/src/helpers/clickHandler.ts +4 -2
- package/src/helpers/pasteHandler.ts +73 -7
- package/src/index.ts +2 -2
- package/src/link.ts +15 -38
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Editor } from '@tiptap/core'
|
|
2
|
-
import { MarkType } from '@tiptap/pm/model'
|
|
2
|
+
import { Mark, MarkType } from '@tiptap/pm/model'
|
|
3
3
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
|
4
4
|
import { find } from 'linkifyjs'
|
|
5
5
|
|
|
6
6
|
type PasteHandlerOptions = {
|
|
7
7
|
editor: Editor
|
|
8
8
|
type: MarkType
|
|
9
|
+
linkOnPaste?: boolean
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export function pasteHandler(options: PasteHandlerOptions): Plugin {
|
|
@@ -15,29 +16,94 @@ export function pasteHandler(options: PasteHandlerOptions): Plugin {
|
|
|
15
16
|
handlePaste: (view, event, slice) => {
|
|
16
17
|
const { state } = view
|
|
17
18
|
const { selection } = state
|
|
18
|
-
const { empty } = selection
|
|
19
19
|
|
|
20
|
-
if
|
|
20
|
+
// Do not proceed if in code block.
|
|
21
|
+
if (state.doc.resolve(selection.from).parent.type.spec.code) {
|
|
21
22
|
return false
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
const pastedLinkMarks: Mark[] = []
|
|
24
26
|
let textContent = ''
|
|
25
27
|
|
|
26
28
|
slice.content.forEach(node => {
|
|
27
29
|
textContent += node.textContent
|
|
30
|
+
|
|
31
|
+
node.marks.forEach(mark => {
|
|
32
|
+
if (mark.type.name === options.type.name) {
|
|
33
|
+
pastedLinkMarks.push(mark)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
28
36
|
})
|
|
29
37
|
|
|
38
|
+
const hasPastedLink = pastedLinkMarks.length > 0
|
|
30
39
|
const link = find(textContent).find(item => item.isLink && item.value === textContent)
|
|
31
40
|
|
|
32
|
-
if (!
|
|
41
|
+
if (!selection.empty && options.linkOnPaste) {
|
|
42
|
+
const pastedLink = hasPastedLink ? pastedLinkMarks[0].attrs.href : link?.href || null
|
|
43
|
+
|
|
44
|
+
if (pastedLink) {
|
|
45
|
+
options.editor.commands.setMark(options.type, { href: pastedLink })
|
|
46
|
+
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const firstChildIsText = slice.content.firstChild?.type.name === 'text'
|
|
52
|
+
const firstChildContainsLinkMark = slice.content.firstChild?.marks.some(mark => mark.type.name === options.type.name)
|
|
53
|
+
|
|
54
|
+
if (firstChildIsText && firstChildContainsLinkMark) {
|
|
33
55
|
return false
|
|
34
56
|
}
|
|
35
57
|
|
|
36
|
-
|
|
37
|
-
href
|
|
58
|
+
if (link && selection.empty) {
|
|
59
|
+
options.editor.commands.insertContent(`<a href="${link.href}">${link.href}</a>`)
|
|
60
|
+
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { tr } = state
|
|
65
|
+
let deleteOnly = false
|
|
66
|
+
|
|
67
|
+
if (!selection.empty) {
|
|
68
|
+
deleteOnly = true
|
|
69
|
+
|
|
70
|
+
tr.delete(selection.from, selection.to)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let currentPos = selection.from
|
|
74
|
+
let fragmentLinks = []
|
|
75
|
+
|
|
76
|
+
slice.content.forEach(node => {
|
|
77
|
+
fragmentLinks = find(node.textContent)
|
|
78
|
+
|
|
79
|
+
tr.insert(currentPos - 1, node)
|
|
80
|
+
|
|
81
|
+
if (fragmentLinks.length > 0) {
|
|
82
|
+
deleteOnly = false
|
|
83
|
+
|
|
84
|
+
fragmentLinks.forEach(fragmentLink => {
|
|
85
|
+
const linkStart = currentPos + fragmentLink.start
|
|
86
|
+
const linkEnd = currentPos + fragmentLink.end
|
|
87
|
+
const hasMark = tr.doc.rangeHasMark(linkStart, linkEnd, options.type)
|
|
88
|
+
|
|
89
|
+
if (!hasMark) {
|
|
90
|
+
tr.addMark(linkStart, linkEnd, options.type.create({ href: fragmentLink.href }))
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
currentPos += node.nodeSize
|
|
38
96
|
})
|
|
39
97
|
|
|
40
|
-
|
|
98
|
+
const hasFragmentLinks = fragmentLinks.length > 0
|
|
99
|
+
|
|
100
|
+
if (tr.docChanged && !deleteOnly && hasFragmentLinks) {
|
|
101
|
+
options.editor.view.dispatch(tr)
|
|
102
|
+
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false
|
|
41
107
|
},
|
|
42
108
|
},
|
|
43
109
|
})
|
package/src/index.ts
CHANGED
package/src/link.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Mark,
|
|
1
|
+
import { Mark, mergeAttributes } from '@tiptap/core'
|
|
2
2
|
import { Plugin } from '@tiptap/pm/state'
|
|
3
|
-
import {
|
|
3
|
+
import { registerCustomProtocol, reset } from 'linkifyjs'
|
|
4
4
|
|
|
5
|
-
import { autolink } from './helpers/autolink'
|
|
6
|
-
import { clickHandler } from './helpers/clickHandler'
|
|
7
|
-
import { pasteHandler } from './helpers/pasteHandler'
|
|
5
|
+
import { autolink } from './helpers/autolink.js'
|
|
6
|
+
import { clickHandler } from './helpers/clickHandler.js'
|
|
7
|
+
import { pasteHandler } from './helpers/pasteHandler.js'
|
|
8
8
|
|
|
9
9
|
export interface LinkProtocolOptions {
|
|
10
10
|
scheme: string;
|
|
@@ -107,6 +107,9 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
107
107
|
target: {
|
|
108
108
|
default: this.options.HTMLAttributes.target,
|
|
109
109
|
},
|
|
110
|
+
rel: {
|
|
111
|
+
default: this.options.HTMLAttributes.rel,
|
|
112
|
+
},
|
|
110
113
|
class: {
|
|
111
114
|
default: this.options.HTMLAttributes.class,
|
|
112
115
|
},
|
|
@@ -146,31 +149,6 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
146
149
|
}
|
|
147
150
|
},
|
|
148
151
|
|
|
149
|
-
addPasteRules() {
|
|
150
|
-
return [
|
|
151
|
-
markPasteRule({
|
|
152
|
-
find: text => find(text)
|
|
153
|
-
.filter(link => {
|
|
154
|
-
if (this.options.validate) {
|
|
155
|
-
return this.options.validate(link.value)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return true
|
|
159
|
-
})
|
|
160
|
-
.filter(link => link.isLink)
|
|
161
|
-
.map(link => ({
|
|
162
|
-
text: link.value,
|
|
163
|
-
index: link.start,
|
|
164
|
-
data: link,
|
|
165
|
-
})),
|
|
166
|
-
type: this.type,
|
|
167
|
-
getAttributes: match => ({
|
|
168
|
-
href: match.data?.href,
|
|
169
|
-
}),
|
|
170
|
-
}),
|
|
171
|
-
]
|
|
172
|
-
},
|
|
173
|
-
|
|
174
152
|
addProseMirrorPlugins() {
|
|
175
153
|
const plugins: Plugin[] = []
|
|
176
154
|
|
|
@@ -191,14 +169,13 @@ export const Link = Mark.create<LinkOptions>({
|
|
|
191
169
|
)
|
|
192
170
|
}
|
|
193
171
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
172
|
+
plugins.push(
|
|
173
|
+
pasteHandler({
|
|
174
|
+
editor: this.editor,
|
|
175
|
+
type: this.type,
|
|
176
|
+
linkOnPaste: this.options.linkOnPaste,
|
|
177
|
+
}),
|
|
178
|
+
)
|
|
202
179
|
|
|
203
180
|
return plugins
|
|
204
181
|
},
|