@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.
@@ -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 (empty) {
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 (!textContent || !link) {
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
- options.editor.commands.setMark(options.type, {
37
- href: link.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
- return true
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
@@ -1,5 +1,5 @@
1
- import { Link } from './link'
1
+ import { Link } from './link.js'
2
2
 
3
- export * from './link'
3
+ export * from './link.js'
4
4
 
5
5
  export default Link
package/src/link.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { Mark, markPasteRule, mergeAttributes } from '@tiptap/core'
1
+ import { Mark, mergeAttributes } from '@tiptap/core'
2
2
  import { Plugin } from '@tiptap/pm/state'
3
- import { find, registerCustomProtocol, reset } from 'linkifyjs'
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
- if (this.options.linkOnPaste) {
195
- plugins.push(
196
- pasteHandler({
197
- editor: this.editor,
198
- type: this.type,
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
  },