@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 CHANGED
@@ -19,8 +19,9 @@ function autolink(options) {
19
19
  const transform = core.combineTransactionSteps(oldState.doc, [...transactions]);
20
20
  const { mapping } = transform;
21
21
  const changes = core.getChangedRanges(transform);
22
+ let needsAutolink = true;
22
23
  changes.forEach(({ oldRange, newRange }) => {
23
- // at first we check if we have to remove links
24
+ // At first we check if we have to remove links.
24
25
  core.getMarksBetween(oldRange.from, oldRange.to, oldState.doc)
25
26
  .filter(item => item.mark.type === options.type)
26
27
  .forEach(oldMark => {
@@ -35,23 +36,26 @@ function autolink(options) {
35
36
  const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, ' ');
36
37
  const wasLink = linkifyjs.test(oldLinkText);
37
38
  const isLink = linkifyjs.test(newLinkText);
38
- // remove only the link, if it was a link before too
39
- // because we don’t want to remove links that were set manually
39
+ if (wasLink) {
40
+ needsAutolink = false;
41
+ }
42
+ // Remove only the link, if it was a link before too.
43
+ // Because we don’t want to remove links that were set manually.
40
44
  if (wasLink && !isLink) {
41
- tr.removeMark(newMark.from, newMark.to, options.type);
45
+ tr.removeMark(needsAutolink ? newMark.from : newMark.to - 1, newMark.to, options.type);
42
46
  }
43
47
  });
44
- // now let’s see if we can add new links
48
+ // Now let’s see if we can add new links.
45
49
  const nodesInChangedRanges = core.findChildrenInRange(newState.doc, newRange, node => node.isTextblock);
46
50
  let textBlock;
47
51
  let textBeforeWhitespace;
48
52
  if (nodesInChangedRanges.length > 1) {
49
- // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)
53
+ // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter).
50
54
  textBlock = nodesInChangedRanges[0];
51
55
  textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, ' ');
52
56
  }
53
57
  else if (nodesInChangedRanges.length
54
- // We want to make sure to include the block seperator argument to treat hard breaks like spaces
58
+ // We want to make sure to include the block seperator argument to treat hard breaks like spaces.
55
59
  && newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')) {
56
60
  textBlock = nodesInChangedRanges[0];
57
61
  textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, ' ');
@@ -68,20 +72,28 @@ function autolink(options) {
68
72
  }
69
73
  linkifyjs.find(lastWordBeforeSpace)
70
74
  .filter(link => link.isLink)
75
+ // Calculate link position.
76
+ .map(link => ({
77
+ ...link,
78
+ from: lastWordAndBlockOffset + link.start + 1,
79
+ to: lastWordAndBlockOffset + link.end + 1,
80
+ }))
81
+ // ignore link inside code mark
82
+ .filter(link => {
83
+ return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code);
84
+ })
85
+ // validate link
71
86
  .filter(link => {
72
87
  if (options.validate) {
73
88
  return options.validate(link.value);
74
89
  }
75
90
  return true;
76
91
  })
77
- // calculate link position
78
- .map(link => ({
79
- ...link,
80
- from: lastWordAndBlockOffset + link.start + 1,
81
- to: lastWordAndBlockOffset + link.end + 1,
82
- }))
83
- // add link mark
92
+ // Add link mark.
84
93
  .forEach(link => {
94
+ if (core.getMarksBetween(link.from, link.to, newState.doc).some(item => item.mark.type === options.type)) {
95
+ return;
96
+ }
85
97
  tr.addMark(link.from, link.to, options.type.create({
86
98
  href: link.href,
87
99
  }));
@@ -101,16 +113,18 @@ function clickHandler(options) {
101
113
  key: new state.PluginKey('handleClickLink'),
102
114
  props: {
103
115
  handleClick: (view, pos, event) => {
104
- var _a, _b, _c;
116
+ var _a, _b;
105
117
  if (event.button !== 0) {
106
118
  return false;
107
119
  }
108
120
  const attrs = core.getAttributes(view.state, options.type.name);
109
- const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a');
110
- const href = (_b = link === null || link === void 0 ? void 0 : link.href) !== null && _b !== void 0 ? _b : attrs.href;
111
- const target = (_c = link === null || link === void 0 ? void 0 : link.target) !== null && _c !== void 0 ? _c : attrs.target;
121
+ const link = event.target;
122
+ const href = (_a = link === null || link === void 0 ? void 0 : link.href) !== null && _a !== void 0 ? _a : attrs.href;
123
+ const target = (_b = link === null || link === void 0 ? void 0 : link.target) !== null && _b !== void 0 ? _b : attrs.target;
112
124
  if (link && href) {
113
- window.open(href, target);
125
+ if (view.editable) {
126
+ window.open(href, target);
127
+ }
114
128
  return true;
115
129
  }
116
130
  return false;
@@ -124,24 +138,71 @@ function pasteHandler(options) {
124
138
  key: new state.PluginKey('handlePasteLink'),
125
139
  props: {
126
140
  handlePaste: (view, event, slice) => {
141
+ var _a, _b;
127
142
  const { state } = view;
128
143
  const { selection } = state;
129
- const { empty } = selection;
130
- if (empty) {
144
+ // Do not proceed if in code block.
145
+ if (state.doc.resolve(selection.from).parent.type.spec.code) {
131
146
  return false;
132
147
  }
148
+ const pastedLinkMarks = [];
133
149
  let textContent = '';
134
150
  slice.content.forEach(node => {
135
151
  textContent += node.textContent;
152
+ node.marks.forEach(mark => {
153
+ if (mark.type.name === options.type.name) {
154
+ pastedLinkMarks.push(mark);
155
+ }
156
+ });
136
157
  });
158
+ const hasPastedLink = pastedLinkMarks.length > 0;
137
159
  const link = linkifyjs.find(textContent).find(item => item.isLink && item.value === textContent);
138
- if (!textContent || !link) {
160
+ if (!selection.empty && options.linkOnPaste) {
161
+ const pastedLink = hasPastedLink ? pastedLinkMarks[0].attrs.href : (link === null || link === void 0 ? void 0 : link.href) || null;
162
+ if (pastedLink) {
163
+ options.editor.commands.setMark(options.type, { href: pastedLink });
164
+ return true;
165
+ }
166
+ }
167
+ const firstChildIsText = ((_a = slice.content.firstChild) === null || _a === void 0 ? void 0 : _a.type.name) === 'text';
168
+ const firstChildContainsLinkMark = (_b = slice.content.firstChild) === null || _b === void 0 ? void 0 : _b.marks.some(mark => mark.type.name === options.type.name);
169
+ if (firstChildIsText && firstChildContainsLinkMark) {
139
170
  return false;
140
171
  }
141
- options.editor.commands.setMark(options.type, {
142
- href: link.href,
172
+ if (link && selection.empty) {
173
+ options.editor.commands.insertContent(`<a href="${link.href}">${link.href}</a>`);
174
+ return true;
175
+ }
176
+ const { tr } = state;
177
+ let deleteOnly = false;
178
+ if (!selection.empty) {
179
+ deleteOnly = true;
180
+ tr.delete(selection.from, selection.to);
181
+ }
182
+ let currentPos = selection.from;
183
+ let fragmentLinks = [];
184
+ slice.content.forEach(node => {
185
+ fragmentLinks = linkifyjs.find(node.textContent);
186
+ tr.insert(currentPos - 1, node);
187
+ if (fragmentLinks.length > 0) {
188
+ deleteOnly = false;
189
+ fragmentLinks.forEach(fragmentLink => {
190
+ const linkStart = currentPos + fragmentLink.start;
191
+ const linkEnd = currentPos + fragmentLink.end;
192
+ const hasMark = tr.doc.rangeHasMark(linkStart, linkEnd, options.type);
193
+ if (!hasMark) {
194
+ tr.addMark(linkStart, linkEnd, options.type.create({ href: fragmentLink.href }));
195
+ }
196
+ });
197
+ }
198
+ currentPos += node.nodeSize;
143
199
  });
144
- return true;
200
+ const hasFragmentLinks = fragmentLinks.length > 0;
201
+ if (tr.docChanged && !deleteOnly && hasFragmentLinks) {
202
+ options.editor.view.dispatch(tr);
203
+ return true;
204
+ }
205
+ return false;
145
206
  },
146
207
  },
147
208
  });
@@ -188,6 +249,9 @@ const Link = core.Mark.create({
188
249
  target: {
189
250
  default: this.options.HTMLAttributes.target,
190
251
  },
252
+ rel: {
253
+ default: this.options.HTMLAttributes.rel,
254
+ },
191
255
  class: {
192
256
  default: this.options.HTMLAttributes.class,
193
257
  },
@@ -218,32 +282,6 @@ const Link = core.Mark.create({
218
282
  },
219
283
  };
220
284
  },
221
- addPasteRules() {
222
- return [
223
- core.markPasteRule({
224
- find: text => linkifyjs.find(text)
225
- .filter(link => {
226
- if (this.options.validate) {
227
- return this.options.validate(link.value);
228
- }
229
- return true;
230
- })
231
- .filter(link => link.isLink)
232
- .map(link => ({
233
- text: link.value,
234
- index: link.start,
235
- data: link,
236
- })),
237
- type: this.type,
238
- getAttributes: match => {
239
- var _a;
240
- return ({
241
- href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
242
- });
243
- },
244
- }),
245
- ];
246
- },
247
285
  addProseMirrorPlugins() {
248
286
  const plugins = [];
249
287
  if (this.options.autolink) {
@@ -257,12 +295,11 @@ const Link = core.Mark.create({
257
295
  type: this.type,
258
296
  }));
259
297
  }
260
- if (this.options.linkOnPaste) {
261
- plugins.push(pasteHandler({
262
- editor: this.editor,
263
- type: this.type,
264
- }));
265
- }
298
+ plugins.push(pasteHandler({
299
+ editor: this.editor,
300
+ type: this.type,
301
+ linkOnPaste: this.options.linkOnPaste,
302
+ }));
266
303
  return plugins;
267
304
  },
268
305
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/helpers/autolink.ts","../src/helpers/clickHandler.ts","../src/helpers/pasteHandler.ts","../src/link.ts"],"sourcesContent":["import {\n combineTransactionSteps,\n findChildrenInRange,\n getChangedRanges,\n getMarksBetween,\n NodeWithPos,\n} from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { find, test } from 'linkifyjs'\n\ntype AutolinkOptions = {\n type: MarkType\n validate?: (url: string) => boolean\n}\n\nexport function autolink(options: AutolinkOptions): Plugin {\n return new Plugin({\n key: new PluginKey('autolink'),\n appendTransaction: (transactions, oldState, newState) => {\n const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc)\n const preventAutolink = transactions.some(transaction => transaction.getMeta('preventAutolink'))\n\n if (!docChanges || preventAutolink) {\n return\n }\n\n const { tr } = newState\n const transform = combineTransactionSteps(oldState.doc, [...transactions])\n const { mapping } = transform\n const changes = getChangedRanges(transform)\n\n changes.forEach(({ oldRange, newRange }) => {\n // at first we check if we have to remove links\n getMarksBetween(oldRange.from, oldRange.to, oldState.doc)\n .filter(item => item.mark.type === options.type)\n .forEach(oldMark => {\n const newFrom = mapping.map(oldMark.from)\n const newTo = mapping.map(oldMark.to)\n const newMarks = getMarksBetween(newFrom, newTo, newState.doc).filter(\n item => item.mark.type === options.type,\n )\n\n if (!newMarks.length) {\n return\n }\n\n const newMark = newMarks[0]\n const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to, undefined, ' ')\n const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, ' ')\n const wasLink = test(oldLinkText)\n const isLink = test(newLinkText)\n\n // remove only the link, if it was a link before too\n // because we don’t want to remove links that were set manually\n if (wasLink && !isLink) {\n tr.removeMark(newMark.from, newMark.to, options.type)\n }\n })\n\n // now let’s see if we can add new links\n const nodesInChangedRanges = findChildrenInRange(\n newState.doc,\n newRange,\n node => node.isTextblock,\n )\n\n let textBlock: NodeWithPos | undefined\n let textBeforeWhitespace: string | undefined\n\n if (nodesInChangedRanges.length > 1) {\n // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)\n textBlock = nodesInChangedRanges[0]\n textBeforeWhitespace = newState.doc.textBetween(\n textBlock.pos,\n textBlock.pos + textBlock.node.nodeSize,\n undefined,\n ' ',\n )\n } else if (\n nodesInChangedRanges.length\n // We want to make sure to include the block seperator argument to treat hard breaks like spaces\n && newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')\n ) {\n textBlock = nodesInChangedRanges[0]\n textBeforeWhitespace = newState.doc.textBetween(\n textBlock.pos,\n newRange.to,\n undefined,\n ' ',\n )\n }\n\n if (textBlock && textBeforeWhitespace) {\n const wordsBeforeWhitespace = textBeforeWhitespace.split(' ').filter(s => s !== '')\n\n if (wordsBeforeWhitespace.length <= 0) {\n return false\n }\n\n const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1]\n const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace)\n\n if (!lastWordBeforeSpace) {\n return false\n }\n\n find(lastWordBeforeSpace)\n .filter(link => link.isLink)\n .filter(link => {\n if (options.validate) {\n return options.validate(link.value)\n }\n return true\n })\n // calculate link position\n .map(link => ({\n ...link,\n from: lastWordAndBlockOffset + link.start + 1,\n to: lastWordAndBlockOffset + link.end + 1,\n }))\n // add link mark\n .forEach(link => {\n tr.addMark(\n link.from,\n link.to,\n options.type.create({\n href: link.href,\n }),\n )\n })\n }\n })\n\n if (!tr.steps.length) {\n return\n }\n\n return tr\n },\n })\n}\n","import { getAttributes } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\ntype ClickHandlerOptions = {\n type: MarkType\n}\n\nexport function clickHandler(options: ClickHandlerOptions): Plugin {\n return new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n if (event.button !== 0) {\n return false\n }\n\n const attrs = getAttributes(view.state, options.type.name)\n const link = (event.target as HTMLElement)?.closest('a')\n\n const href = link?.href ?? attrs.href\n const target = link?.target ?? attrs.target\n\n if (link && href) {\n window.open(href, target)\n\n return true\n }\n\n return false\n },\n },\n })\n}\n","import { Editor } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { find } from 'linkifyjs'\n\ntype PasteHandlerOptions = {\n editor: Editor\n type: MarkType\n}\n\nexport function pasteHandler(options: PasteHandlerOptions): Plugin {\n return new Plugin({\n key: new PluginKey('handlePasteLink'),\n props: {\n handlePaste: (view, event, slice) => {\n const { state } = view\n const { selection } = state\n const { empty } = selection\n\n if (empty) {\n return false\n }\n\n let textContent = ''\n\n slice.content.forEach(node => {\n textContent += node.textContent\n })\n\n const link = find(textContent).find(item => item.isLink && item.value === textContent)\n\n if (!textContent || !link) {\n return false\n }\n\n options.editor.commands.setMark(options.type, {\n href: link.href,\n })\n\n return true\n },\n },\n })\n}\n","import { Mark, markPasteRule, mergeAttributes } from '@tiptap/core'\nimport { Plugin } from '@tiptap/pm/state'\nimport { find, registerCustomProtocol, reset } from 'linkifyjs'\n\nimport { autolink } from './helpers/autolink'\nimport { clickHandler } from './helpers/clickHandler'\nimport { pasteHandler } from './helpers/pasteHandler'\n\nexport interface LinkProtocolOptions {\n scheme: string;\n optionalSlashes?: boolean;\n}\n\nexport interface LinkOptions {\n /**\n * If enabled, it adds links as you type.\n */\n autolink: boolean\n /**\n * An array of custom protocols to be registered with linkifyjs.\n */\n protocols: Array<LinkProtocolOptions | string>\n /**\n * If enabled, links will be opened on click.\n */\n openOnClick: boolean\n /**\n * Adds a link to the current selection if the pasted content only contains an url.\n */\n linkOnPaste: boolean\n /**\n * A list of HTML attributes to be rendered.\n */\n HTMLAttributes: Record<string, any>\n /**\n * A validation function that modifies link verification for the auto linker.\n * @param url - The url to be validated.\n * @returns - True if the url is valid, false otherwise.\n */\n validate?: (url: string) => boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string; target?: string | null }) => ReturnType\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string; target?: string | null }) => ReturnType\n /**\n * Unset a link mark\n */\n unsetLink: () => ReturnType\n }\n }\n}\n\nexport const Link = Mark.create<LinkOptions>({\n name: 'link',\n\n priority: 1000,\n\n keepOnSplit: false,\n\n onCreate() {\n this.options.protocols.forEach(protocol => {\n if (typeof protocol === 'string') {\n registerCustomProtocol(protocol)\n return\n }\n registerCustomProtocol(protocol.scheme, protocol.optionalSlashes)\n })\n },\n\n onDestroy() {\n reset()\n },\n\n inclusive() {\n return this.options.autolink\n },\n\n addOptions() {\n return {\n openOnClick: true,\n linkOnPaste: true,\n autolink: true,\n protocols: [],\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n class: null,\n },\n validate: undefined,\n }\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n class: {\n default: this.options.HTMLAttributes.class,\n },\n }\n },\n\n parseHTML() {\n return [{ tag: 'a[href]:not([href *= \"javascript:\" i])' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink:\n attributes => ({ chain }) => {\n return chain().setMark(this.name, attributes).setMeta('preventAutolink', true).run()\n },\n\n toggleLink:\n attributes => ({ chain }) => {\n return chain()\n .toggleMark(this.name, attributes, { extendEmptyMarkRange: true })\n .setMeta('preventAutolink', true)\n .run()\n },\n\n unsetLink:\n () => ({ chain }) => {\n return chain()\n .unsetMark(this.name, { extendEmptyMarkRange: true })\n .setMeta('preventAutolink', true)\n .run()\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule({\n find: text => find(text)\n .filter(link => {\n if (this.options.validate) {\n return this.options.validate(link.value)\n }\n\n return true\n })\n .filter(link => link.isLink)\n .map(link => ({\n text: link.value,\n index: link.start,\n data: link,\n })),\n type: this.type,\n getAttributes: match => ({\n href: match.data?.href,\n }),\n }),\n ]\n },\n\n addProseMirrorPlugins() {\n const plugins: Plugin[] = []\n\n if (this.options.autolink) {\n plugins.push(\n autolink({\n type: this.type,\n validate: this.options.validate,\n }),\n )\n }\n\n if (this.options.openOnClick) {\n plugins.push(\n clickHandler({\n type: this.type,\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n pasteHandler({\n editor: this.editor,\n type: this.type,\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":["Plugin","PluginKey","combineTransactionSteps","getChangedRanges","getMarksBetween","test","findChildrenInRange","find","getAttributes","Mark","registerCustomProtocol","reset","mergeAttributes","markPasteRule"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/helpers/autolink.ts","../src/helpers/clickHandler.ts","../src/helpers/pasteHandler.ts","../src/link.ts"],"sourcesContent":["import {\n combineTransactionSteps,\n findChildrenInRange,\n getChangedRanges,\n getMarksBetween,\n NodeWithPos,\n} from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { find, test } from 'linkifyjs'\n\ntype AutolinkOptions = {\n type: MarkType\n validate?: (url: string) => boolean\n}\n\nexport function autolink(options: AutolinkOptions): Plugin {\n return new Plugin({\n key: new PluginKey('autolink'),\n appendTransaction: (transactions, oldState, newState) => {\n const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc)\n const preventAutolink = transactions.some(transaction => transaction.getMeta('preventAutolink'))\n\n if (!docChanges || preventAutolink) {\n return\n }\n\n const { tr } = newState\n const transform = combineTransactionSteps(oldState.doc, [...transactions])\n const { mapping } = transform\n const changes = getChangedRanges(transform)\n let needsAutolink = true\n\n changes.forEach(({ oldRange, newRange }) => {\n // At first we check if we have to remove links.\n getMarksBetween(oldRange.from, oldRange.to, oldState.doc)\n .filter(item => item.mark.type === options.type)\n .forEach(oldMark => {\n const newFrom = mapping.map(oldMark.from)\n const newTo = mapping.map(oldMark.to)\n const newMarks = getMarksBetween(newFrom, newTo, newState.doc).filter(\n item => item.mark.type === options.type,\n )\n\n if (!newMarks.length) {\n return\n }\n\n const newMark = newMarks[0]\n const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to, undefined, ' ')\n const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, ' ')\n const wasLink = test(oldLinkText)\n const isLink = test(newLinkText)\n\n if (wasLink) {\n needsAutolink = false\n }\n\n // Remove only the link, if it was a link before too.\n // Because we don’t want to remove links that were set manually.\n if (wasLink && !isLink) {\n tr.removeMark(needsAutolink ? newMark.from : newMark.to - 1, newMark.to, options.type)\n }\n })\n\n // Now let’s see if we can add new links.\n const nodesInChangedRanges = findChildrenInRange(\n newState.doc,\n newRange,\n node => node.isTextblock,\n )\n\n let textBlock: NodeWithPos | undefined\n let textBeforeWhitespace: string | undefined\n\n if (nodesInChangedRanges.length > 1) {\n // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter).\n textBlock = nodesInChangedRanges[0]\n textBeforeWhitespace = newState.doc.textBetween(\n textBlock.pos,\n textBlock.pos + textBlock.node.nodeSize,\n undefined,\n ' ',\n )\n } else if (\n nodesInChangedRanges.length\n // We want to make sure to include the block seperator argument to treat hard breaks like spaces.\n && newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')\n ) {\n textBlock = nodesInChangedRanges[0]\n textBeforeWhitespace = newState.doc.textBetween(\n textBlock.pos,\n newRange.to,\n undefined,\n ' ',\n )\n }\n\n if (textBlock && textBeforeWhitespace) {\n const wordsBeforeWhitespace = textBeforeWhitespace.split(' ').filter(s => s !== '')\n\n if (wordsBeforeWhitespace.length <= 0) {\n return false\n }\n\n const lastWordBeforeSpace = wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1]\n const lastWordAndBlockOffset = textBlock.pos + textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace)\n\n if (!lastWordBeforeSpace) {\n return false\n }\n\n find(lastWordBeforeSpace)\n .filter(link => link.isLink)\n // Calculate link position.\n .map(link => ({\n ...link,\n from: lastWordAndBlockOffset + link.start + 1,\n to: lastWordAndBlockOffset + link.end + 1,\n }))\n // ignore link inside code mark\n .filter(link => {\n return !newState.doc.rangeHasMark(\n link.from,\n link.to,\n newState.schema.marks.code,\n )\n })\n // validate link\n .filter(link => {\n if (options.validate) {\n return options.validate(link.value)\n }\n return true\n })\n // Add link mark.\n .forEach(link => {\n if (getMarksBetween(link.from, link.to, newState.doc).some(item => item.mark.type === options.type)) {\n return\n }\n\n tr.addMark(\n link.from,\n link.to,\n options.type.create({\n href: link.href,\n }),\n )\n })\n }\n })\n\n if (!tr.steps.length) {\n return\n }\n\n return tr\n },\n })\n}\n","import { getAttributes } from '@tiptap/core'\nimport { MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\ntype ClickHandlerOptions = {\n type: MarkType\n}\n\nexport function clickHandler(options: ClickHandlerOptions): Plugin {\n return new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n if (event.button !== 0) {\n return false\n }\n\n const attrs = getAttributes(view.state, options.type.name)\n const link = (event.target as HTMLLinkElement)\n\n const href = link?.href ?? attrs.href\n const target = link?.target ?? attrs.target\n\n if (link && href) {\n if (view.editable) {\n window.open(href, target)\n }\n\n return true\n }\n\n return false\n },\n },\n })\n}\n","import { Editor } from '@tiptap/core'\nimport { Mark, MarkType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { find } from 'linkifyjs'\n\ntype PasteHandlerOptions = {\n editor: Editor\n type: MarkType\n linkOnPaste?: boolean\n}\n\nexport function pasteHandler(options: PasteHandlerOptions): Plugin {\n return new Plugin({\n key: new PluginKey('handlePasteLink'),\n props: {\n handlePaste: (view, event, slice) => {\n const { state } = view\n const { selection } = state\n\n // Do not proceed if in code block.\n if (state.doc.resolve(selection.from).parent.type.spec.code) {\n return false\n }\n\n const pastedLinkMarks: Mark[] = []\n let textContent = ''\n\n slice.content.forEach(node => {\n textContent += node.textContent\n\n node.marks.forEach(mark => {\n if (mark.type.name === options.type.name) {\n pastedLinkMarks.push(mark)\n }\n })\n })\n\n const hasPastedLink = pastedLinkMarks.length > 0\n const link = find(textContent).find(item => item.isLink && item.value === textContent)\n\n if (!selection.empty && options.linkOnPaste) {\n const pastedLink = hasPastedLink ? pastedLinkMarks[0].attrs.href : link?.href || null\n\n if (pastedLink) {\n options.editor.commands.setMark(options.type, { href: pastedLink })\n\n return true\n }\n }\n\n const firstChildIsText = slice.content.firstChild?.type.name === 'text'\n const firstChildContainsLinkMark = slice.content.firstChild?.marks.some(mark => mark.type.name === options.type.name)\n\n if (firstChildIsText && firstChildContainsLinkMark) {\n return false\n }\n\n if (link && selection.empty) {\n options.editor.commands.insertContent(`<a href=\"${link.href}\">${link.href}</a>`)\n\n return true\n }\n\n const { tr } = state\n let deleteOnly = false\n\n if (!selection.empty) {\n deleteOnly = true\n\n tr.delete(selection.from, selection.to)\n }\n\n let currentPos = selection.from\n let fragmentLinks = []\n\n slice.content.forEach(node => {\n fragmentLinks = find(node.textContent)\n\n tr.insert(currentPos - 1, node)\n\n if (fragmentLinks.length > 0) {\n deleteOnly = false\n\n fragmentLinks.forEach(fragmentLink => {\n const linkStart = currentPos + fragmentLink.start\n const linkEnd = currentPos + fragmentLink.end\n const hasMark = tr.doc.rangeHasMark(linkStart, linkEnd, options.type)\n\n if (!hasMark) {\n tr.addMark(linkStart, linkEnd, options.type.create({ href: fragmentLink.href }))\n }\n })\n\n }\n currentPos += node.nodeSize\n })\n\n const hasFragmentLinks = fragmentLinks.length > 0\n\n if (tr.docChanged && !deleteOnly && hasFragmentLinks) {\n options.editor.view.dispatch(tr)\n\n return true\n }\n\n return false\n },\n },\n })\n}\n","import { Mark, mergeAttributes } from '@tiptap/core'\nimport { Plugin } from '@tiptap/pm/state'\nimport { registerCustomProtocol, reset } from 'linkifyjs'\n\nimport { autolink } from './helpers/autolink.js'\nimport { clickHandler } from './helpers/clickHandler.js'\nimport { pasteHandler } from './helpers/pasteHandler.js'\n\nexport interface LinkProtocolOptions {\n scheme: string;\n optionalSlashes?: boolean;\n}\n\nexport interface LinkOptions {\n /**\n * If enabled, it adds links as you type.\n */\n autolink: boolean\n /**\n * An array of custom protocols to be registered with linkifyjs.\n */\n protocols: Array<LinkProtocolOptions | string>\n /**\n * If enabled, links will be opened on click.\n */\n openOnClick: boolean\n /**\n * Adds a link to the current selection if the pasted content only contains an url.\n */\n linkOnPaste: boolean\n /**\n * A list of HTML attributes to be rendered.\n */\n HTMLAttributes: Record<string, any>\n /**\n * A validation function that modifies link verification for the auto linker.\n * @param url - The url to be validated.\n * @returns - True if the url is valid, false otherwise.\n */\n validate?: (url: string) => boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string; target?: string | null }) => ReturnType\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string; target?: string | null }) => ReturnType\n /**\n * Unset a link mark\n */\n unsetLink: () => ReturnType\n }\n }\n}\n\nexport const Link = Mark.create<LinkOptions>({\n name: 'link',\n\n priority: 1000,\n\n keepOnSplit: false,\n\n onCreate() {\n this.options.protocols.forEach(protocol => {\n if (typeof protocol === 'string') {\n registerCustomProtocol(protocol)\n return\n }\n registerCustomProtocol(protocol.scheme, protocol.optionalSlashes)\n })\n },\n\n onDestroy() {\n reset()\n },\n\n inclusive() {\n return this.options.autolink\n },\n\n addOptions() {\n return {\n openOnClick: true,\n linkOnPaste: true,\n autolink: true,\n protocols: [],\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n class: null,\n },\n validate: undefined,\n }\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n rel: {\n default: this.options.HTMLAttributes.rel,\n },\n class: {\n default: this.options.HTMLAttributes.class,\n },\n }\n },\n\n parseHTML() {\n return [{ tag: 'a[href]:not([href *= \"javascript:\" i])' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink:\n attributes => ({ chain }) => {\n return chain().setMark(this.name, attributes).setMeta('preventAutolink', true).run()\n },\n\n toggleLink:\n attributes => ({ chain }) => {\n return chain()\n .toggleMark(this.name, attributes, { extendEmptyMarkRange: true })\n .setMeta('preventAutolink', true)\n .run()\n },\n\n unsetLink:\n () => ({ chain }) => {\n return chain()\n .unsetMark(this.name, { extendEmptyMarkRange: true })\n .setMeta('preventAutolink', true)\n .run()\n },\n }\n },\n\n addProseMirrorPlugins() {\n const plugins: Plugin[] = []\n\n if (this.options.autolink) {\n plugins.push(\n autolink({\n type: this.type,\n validate: this.options.validate,\n }),\n )\n }\n\n if (this.options.openOnClick) {\n plugins.push(\n clickHandler({\n type: this.type,\n }),\n )\n }\n\n plugins.push(\n pasteHandler({\n editor: this.editor,\n type: this.type,\n linkOnPaste: this.options.linkOnPaste,\n }),\n )\n\n return plugins\n },\n})\n"],"names":["Plugin","PluginKey","combineTransactionSteps","getChangedRanges","getMarksBetween","test","findChildrenInRange","find","getAttributes","Mark","registerCustomProtocol","reset","mergeAttributes"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { combineTransactionSteps, getChangedRanges, getMarksBetween, findChildrenInRange, getAttributes, Mark, mergeAttributes, markPasteRule } from '@tiptap/core';
1
+ import { combineTransactionSteps, getChangedRanges, getMarksBetween, findChildrenInRange, getAttributes, Mark, mergeAttributes } from '@tiptap/core';
2
2
  import { test, find, registerCustomProtocol, reset } from 'linkifyjs';
3
3
  import { Plugin, PluginKey } from '@tiptap/pm/state';
4
4
 
@@ -15,8 +15,9 @@ function autolink(options) {
15
15
  const transform = combineTransactionSteps(oldState.doc, [...transactions]);
16
16
  const { mapping } = transform;
17
17
  const changes = getChangedRanges(transform);
18
+ let needsAutolink = true;
18
19
  changes.forEach(({ oldRange, newRange }) => {
19
- // at first we check if we have to remove links
20
+ // At first we check if we have to remove links.
20
21
  getMarksBetween(oldRange.from, oldRange.to, oldState.doc)
21
22
  .filter(item => item.mark.type === options.type)
22
23
  .forEach(oldMark => {
@@ -31,23 +32,26 @@ function autolink(options) {
31
32
  const newLinkText = newState.doc.textBetween(newMark.from, newMark.to, undefined, ' ');
32
33
  const wasLink = test(oldLinkText);
33
34
  const isLink = test(newLinkText);
34
- // remove only the link, if it was a link before too
35
- // because we don’t want to remove links that were set manually
35
+ if (wasLink) {
36
+ needsAutolink = false;
37
+ }
38
+ // Remove only the link, if it was a link before too.
39
+ // Because we don’t want to remove links that were set manually.
36
40
  if (wasLink && !isLink) {
37
- tr.removeMark(newMark.from, newMark.to, options.type);
41
+ tr.removeMark(needsAutolink ? newMark.from : newMark.to - 1, newMark.to, options.type);
38
42
  }
39
43
  });
40
- // now let’s see if we can add new links
44
+ // Now let’s see if we can add new links.
41
45
  const nodesInChangedRanges = findChildrenInRange(newState.doc, newRange, node => node.isTextblock);
42
46
  let textBlock;
43
47
  let textBeforeWhitespace;
44
48
  if (nodesInChangedRanges.length > 1) {
45
- // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter)
49
+ // Grab the first node within the changed ranges (ex. the first of two paragraphs when hitting enter).
46
50
  textBlock = nodesInChangedRanges[0];
47
51
  textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, textBlock.pos + textBlock.node.nodeSize, undefined, ' ');
48
52
  }
49
53
  else if (nodesInChangedRanges.length
50
- // We want to make sure to include the block seperator argument to treat hard breaks like spaces
54
+ // We want to make sure to include the block seperator argument to treat hard breaks like spaces.
51
55
  && newState.doc.textBetween(newRange.from, newRange.to, ' ', ' ').endsWith(' ')) {
52
56
  textBlock = nodesInChangedRanges[0];
53
57
  textBeforeWhitespace = newState.doc.textBetween(textBlock.pos, newRange.to, undefined, ' ');
@@ -64,20 +68,28 @@ function autolink(options) {
64
68
  }
65
69
  find(lastWordBeforeSpace)
66
70
  .filter(link => link.isLink)
71
+ // Calculate link position.
72
+ .map(link => ({
73
+ ...link,
74
+ from: lastWordAndBlockOffset + link.start + 1,
75
+ to: lastWordAndBlockOffset + link.end + 1,
76
+ }))
77
+ // ignore link inside code mark
78
+ .filter(link => {
79
+ return !newState.doc.rangeHasMark(link.from, link.to, newState.schema.marks.code);
80
+ })
81
+ // validate link
67
82
  .filter(link => {
68
83
  if (options.validate) {
69
84
  return options.validate(link.value);
70
85
  }
71
86
  return true;
72
87
  })
73
- // calculate link position
74
- .map(link => ({
75
- ...link,
76
- from: lastWordAndBlockOffset + link.start + 1,
77
- to: lastWordAndBlockOffset + link.end + 1,
78
- }))
79
- // add link mark
88
+ // Add link mark.
80
89
  .forEach(link => {
90
+ if (getMarksBetween(link.from, link.to, newState.doc).some(item => item.mark.type === options.type)) {
91
+ return;
92
+ }
81
93
  tr.addMark(link.from, link.to, options.type.create({
82
94
  href: link.href,
83
95
  }));
@@ -97,16 +109,18 @@ function clickHandler(options) {
97
109
  key: new PluginKey('handleClickLink'),
98
110
  props: {
99
111
  handleClick: (view, pos, event) => {
100
- var _a, _b, _c;
112
+ var _a, _b;
101
113
  if (event.button !== 0) {
102
114
  return false;
103
115
  }
104
116
  const attrs = getAttributes(view.state, options.type.name);
105
- const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a');
106
- const href = (_b = link === null || link === void 0 ? void 0 : link.href) !== null && _b !== void 0 ? _b : attrs.href;
107
- const target = (_c = link === null || link === void 0 ? void 0 : link.target) !== null && _c !== void 0 ? _c : attrs.target;
117
+ const link = event.target;
118
+ const href = (_a = link === null || link === void 0 ? void 0 : link.href) !== null && _a !== void 0 ? _a : attrs.href;
119
+ const target = (_b = link === null || link === void 0 ? void 0 : link.target) !== null && _b !== void 0 ? _b : attrs.target;
108
120
  if (link && href) {
109
- window.open(href, target);
121
+ if (view.editable) {
122
+ window.open(href, target);
123
+ }
110
124
  return true;
111
125
  }
112
126
  return false;
@@ -120,24 +134,71 @@ function pasteHandler(options) {
120
134
  key: new PluginKey('handlePasteLink'),
121
135
  props: {
122
136
  handlePaste: (view, event, slice) => {
137
+ var _a, _b;
123
138
  const { state } = view;
124
139
  const { selection } = state;
125
- const { empty } = selection;
126
- if (empty) {
140
+ // Do not proceed if in code block.
141
+ if (state.doc.resolve(selection.from).parent.type.spec.code) {
127
142
  return false;
128
143
  }
144
+ const pastedLinkMarks = [];
129
145
  let textContent = '';
130
146
  slice.content.forEach(node => {
131
147
  textContent += node.textContent;
148
+ node.marks.forEach(mark => {
149
+ if (mark.type.name === options.type.name) {
150
+ pastedLinkMarks.push(mark);
151
+ }
152
+ });
132
153
  });
154
+ const hasPastedLink = pastedLinkMarks.length > 0;
133
155
  const link = find(textContent).find(item => item.isLink && item.value === textContent);
134
- if (!textContent || !link) {
156
+ if (!selection.empty && options.linkOnPaste) {
157
+ const pastedLink = hasPastedLink ? pastedLinkMarks[0].attrs.href : (link === null || link === void 0 ? void 0 : link.href) || null;
158
+ if (pastedLink) {
159
+ options.editor.commands.setMark(options.type, { href: pastedLink });
160
+ return true;
161
+ }
162
+ }
163
+ const firstChildIsText = ((_a = slice.content.firstChild) === null || _a === void 0 ? void 0 : _a.type.name) === 'text';
164
+ const firstChildContainsLinkMark = (_b = slice.content.firstChild) === null || _b === void 0 ? void 0 : _b.marks.some(mark => mark.type.name === options.type.name);
165
+ if (firstChildIsText && firstChildContainsLinkMark) {
135
166
  return false;
136
167
  }
137
- options.editor.commands.setMark(options.type, {
138
- href: link.href,
168
+ if (link && selection.empty) {
169
+ options.editor.commands.insertContent(`<a href="${link.href}">${link.href}</a>`);
170
+ return true;
171
+ }
172
+ const { tr } = state;
173
+ let deleteOnly = false;
174
+ if (!selection.empty) {
175
+ deleteOnly = true;
176
+ tr.delete(selection.from, selection.to);
177
+ }
178
+ let currentPos = selection.from;
179
+ let fragmentLinks = [];
180
+ slice.content.forEach(node => {
181
+ fragmentLinks = find(node.textContent);
182
+ tr.insert(currentPos - 1, node);
183
+ if (fragmentLinks.length > 0) {
184
+ deleteOnly = false;
185
+ fragmentLinks.forEach(fragmentLink => {
186
+ const linkStart = currentPos + fragmentLink.start;
187
+ const linkEnd = currentPos + fragmentLink.end;
188
+ const hasMark = tr.doc.rangeHasMark(linkStart, linkEnd, options.type);
189
+ if (!hasMark) {
190
+ tr.addMark(linkStart, linkEnd, options.type.create({ href: fragmentLink.href }));
191
+ }
192
+ });
193
+ }
194
+ currentPos += node.nodeSize;
139
195
  });
140
- return true;
196
+ const hasFragmentLinks = fragmentLinks.length > 0;
197
+ if (tr.docChanged && !deleteOnly && hasFragmentLinks) {
198
+ options.editor.view.dispatch(tr);
199
+ return true;
200
+ }
201
+ return false;
141
202
  },
142
203
  },
143
204
  });
@@ -184,6 +245,9 @@ const Link = Mark.create({
184
245
  target: {
185
246
  default: this.options.HTMLAttributes.target,
186
247
  },
248
+ rel: {
249
+ default: this.options.HTMLAttributes.rel,
250
+ },
187
251
  class: {
188
252
  default: this.options.HTMLAttributes.class,
189
253
  },
@@ -214,32 +278,6 @@ const Link = Mark.create({
214
278
  },
215
279
  };
216
280
  },
217
- addPasteRules() {
218
- return [
219
- markPasteRule({
220
- find: text => find(text)
221
- .filter(link => {
222
- if (this.options.validate) {
223
- return this.options.validate(link.value);
224
- }
225
- return true;
226
- })
227
- .filter(link => link.isLink)
228
- .map(link => ({
229
- text: link.value,
230
- index: link.start,
231
- data: link,
232
- })),
233
- type: this.type,
234
- getAttributes: match => {
235
- var _a;
236
- return ({
237
- href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
238
- });
239
- },
240
- }),
241
- ];
242
- },
243
281
  addProseMirrorPlugins() {
244
282
  const plugins = [];
245
283
  if (this.options.autolink) {
@@ -253,12 +291,11 @@ const Link = Mark.create({
253
291
  type: this.type,
254
292
  }));
255
293
  }
256
- if (this.options.linkOnPaste) {
257
- plugins.push(pasteHandler({
258
- editor: this.editor,
259
- type: this.type,
260
- }));
261
- }
294
+ plugins.push(pasteHandler({
295
+ editor: this.editor,
296
+ type: this.type,
297
+ linkOnPaste: this.options.linkOnPaste,
298
+ }));
262
299
  return plugins;
263
300
  },
264
301
  });