@tiptap/extension-link 2.0.0-beta.20 → 2.0.0-beta.24

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.
@@ -37,12 +37,4 @@ declare module '@tiptap/core' {
37
37
  };
38
38
  }
39
39
  }
40
- /**
41
- * A regex that matches any string that contains a link
42
- */
43
- export declare const pasteRegex: RegExp;
44
- /**
45
- * A regex that matches an url
46
- */
47
- export declare const pasteRegexExact: RegExp;
48
- export declare const Link: Mark<LinkOptions>;
40
+ export declare const Link: Mark<LinkOptions, any>;
@@ -4,26 +4,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@tiptap/core');
6
6
  var prosemirrorState = require('prosemirror-state');
7
+ var linkifyjs = require('linkifyjs');
7
8
 
8
- /**
9
- * A regex that matches any string that contains a link
10
- */
11
- const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
12
- /**
13
- * A regex that matches an url
14
- */
15
- const pasteRegexExact = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi;
16
9
  const Link = core.Mark.create({
17
10
  name: 'link',
18
11
  priority: 1000,
19
12
  inclusive: false,
20
- defaultOptions: {
21
- openOnClick: true,
22
- linkOnPaste: true,
23
- HTMLAttributes: {
24
- target: '_blank',
25
- rel: 'noopener noreferrer nofollow',
26
- },
13
+ addOptions() {
14
+ return {
15
+ openOnClick: true,
16
+ linkOnPaste: true,
17
+ HTMLAttributes: {
18
+ target: '_blank',
19
+ rel: 'noopener noreferrer nofollow',
20
+ },
21
+ };
27
22
  },
28
23
  addAttributes() {
29
24
  return {
@@ -58,7 +53,22 @@ const Link = core.Mark.create({
58
53
  },
59
54
  addPasteRules() {
60
55
  return [
61
- core.markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),
56
+ core.markPasteRule({
57
+ find: text => linkifyjs.find(text)
58
+ .filter(link => link.isLink)
59
+ .map(link => ({
60
+ text: link.value,
61
+ index: link.start,
62
+ data: link,
63
+ })),
64
+ type: this.type,
65
+ getAttributes: match => {
66
+ var _a;
67
+ return ({
68
+ href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
69
+ });
70
+ },
71
+ }),
62
72
  ];
63
73
  },
64
74
  addProseMirrorPlugins() {
@@ -95,11 +105,13 @@ const Link = core.Mark.create({
95
105
  slice.content.forEach(node => {
96
106
  textContent += node.textContent;
97
107
  });
98
- if (!textContent || !textContent.match(pasteRegexExact)) {
108
+ const link = linkifyjs.find(textContent)
109
+ .find(item => item.isLink && item.value === textContent);
110
+ if (!textContent || !link) {
99
111
  return false;
100
112
  }
101
113
  this.editor.commands.setMark(this.type, {
102
- href: textContent,
114
+ href: link.href,
103
115
  });
104
116
  return true;
105
117
  },
@@ -111,7 +123,5 @@ const Link = core.Mark.create({
111
123
  });
112
124
 
113
125
  exports.Link = Link;
114
- exports['default'] = Link;
115
- exports.pasteRegex = pasteRegex;
116
- exports.pasteRegexExact = pasteRegexExact;
126
+ exports["default"] = Link;
117
127
  //# sourceMappingURL=tiptap-extension-link.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiptap-extension-link.cjs.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Unset a link mark\n */\n unsetLink: () => ReturnType,\n }\n }\n}\n\n/**\n * A regex that matches any string that contains a link\n */\nexport const pasteRegex = /https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi\n\n/**\n * A regex that matches an url\n */\nexport const pasteRegexExact = /^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi\n\nexport const Link = Mark.create<LinkOptions>({\n name: 'link',\n\n priority: 1000,\n\n inclusive: false,\n\n defaultOptions: {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),\n ]\n },\n\n addProseMirrorPlugins() {\n const plugins = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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 if (!textContent || !textContent.match(pasteRegexExact)) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: textContent,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":["Mark","mergeAttributes","markPasteRule","Plugin","PluginKey"],"mappings":"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"}
1
+ {"version":3,"file":"tiptap-extension-link.cjs.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport { find } from 'linkifyjs'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => 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 inclusive: false,\n\n addOptions() {\n return {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n }\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule({\n find: text => find(text)\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 = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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)\n .find(item => item.isLink && item.value === textContent)\n\n if (!textContent || !link) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: link.href,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":["Mark","mergeAttributes","markPasteRule","find","Plugin","PluginKey"],"mappings":"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"}
@@ -1,25 +1,20 @@
1
1
  import { Mark, mergeAttributes, markPasteRule } from '@tiptap/core';
2
2
  import { Plugin, PluginKey } from 'prosemirror-state';
3
+ import { find } from 'linkifyjs';
3
4
 
4
- /**
5
- * A regex that matches any string that contains a link
6
- */
7
- const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
8
- /**
9
- * A regex that matches an url
10
- */
11
- const pasteRegexExact = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi;
12
5
  const Link = Mark.create({
13
6
  name: 'link',
14
7
  priority: 1000,
15
8
  inclusive: false,
16
- defaultOptions: {
17
- openOnClick: true,
18
- linkOnPaste: true,
19
- HTMLAttributes: {
20
- target: '_blank',
21
- rel: 'noopener noreferrer nofollow',
22
- },
9
+ addOptions() {
10
+ return {
11
+ openOnClick: true,
12
+ linkOnPaste: true,
13
+ HTMLAttributes: {
14
+ target: '_blank',
15
+ rel: 'noopener noreferrer nofollow',
16
+ },
17
+ };
23
18
  },
24
19
  addAttributes() {
25
20
  return {
@@ -54,7 +49,22 @@ const Link = Mark.create({
54
49
  },
55
50
  addPasteRules() {
56
51
  return [
57
- markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),
52
+ markPasteRule({
53
+ find: text => find(text)
54
+ .filter(link => link.isLink)
55
+ .map(link => ({
56
+ text: link.value,
57
+ index: link.start,
58
+ data: link,
59
+ })),
60
+ type: this.type,
61
+ getAttributes: match => {
62
+ var _a;
63
+ return ({
64
+ href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
65
+ });
66
+ },
67
+ }),
58
68
  ];
59
69
  },
60
70
  addProseMirrorPlugins() {
@@ -91,11 +101,13 @@ const Link = Mark.create({
91
101
  slice.content.forEach(node => {
92
102
  textContent += node.textContent;
93
103
  });
94
- if (!textContent || !textContent.match(pasteRegexExact)) {
104
+ const link = find(textContent)
105
+ .find(item => item.isLink && item.value === textContent);
106
+ if (!textContent || !link) {
95
107
  return false;
96
108
  }
97
109
  this.editor.commands.setMark(this.type, {
98
- href: textContent,
110
+ href: link.href,
99
111
  });
100
112
  return true;
101
113
  },
@@ -106,5 +118,5 @@ const Link = Mark.create({
106
118
  },
107
119
  });
108
120
 
109
- export { Link, Link as default, pasteRegex, pasteRegexExact };
121
+ export { Link, Link as default };
110
122
  //# sourceMappingURL=tiptap-extension-link.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiptap-extension-link.esm.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Unset a link mark\n */\n unsetLink: () => ReturnType,\n }\n }\n}\n\n/**\n * A regex that matches any string that contains a link\n */\nexport const pasteRegex = /https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi\n\n/**\n * A regex that matches an url\n */\nexport const pasteRegexExact = /^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi\n\nexport const Link = Mark.create<LinkOptions>({\n name: 'link',\n\n priority: 1000,\n\n inclusive: false,\n\n defaultOptions: {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),\n ]\n },\n\n addProseMirrorPlugins() {\n const plugins = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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 if (!textContent || !textContent.match(pasteRegexExact)) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: textContent,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tiptap-extension-link.esm.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport { find } from 'linkifyjs'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => 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 inclusive: false,\n\n addOptions() {\n return {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n }\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule({\n find: text => find(text)\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 = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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)\n .find(item => item.isLink && item.value === textContent)\n\n if (!textContent || !link) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: link.href,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":[],"mappings":"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"}
@@ -1,28 +1,22 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('prosemirror-state')) :
3
- typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'prosemirror-state'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global['@tiptap/extension-link'] = {}, global.core, global.prosemirrorState));
5
- }(this, (function (exports, core, prosemirrorState) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('prosemirror-state'), require('linkifyjs')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'prosemirror-state', 'linkifyjs'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/extension-link"] = {}, global.core, global.prosemirrorState, global.linkifyjs));
5
+ })(this, (function (exports, core, prosemirrorState, linkifyjs) { 'use strict';
6
6
 
7
- /**
8
- * A regex that matches any string that contains a link
9
- */
10
- const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi;
11
- /**
12
- * A regex that matches an url
13
- */
14
- const pasteRegexExact = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi;
15
7
  const Link = core.Mark.create({
16
8
  name: 'link',
17
9
  priority: 1000,
18
10
  inclusive: false,
19
- defaultOptions: {
20
- openOnClick: true,
21
- linkOnPaste: true,
22
- HTMLAttributes: {
23
- target: '_blank',
24
- rel: 'noopener noreferrer nofollow',
25
- },
11
+ addOptions() {
12
+ return {
13
+ openOnClick: true,
14
+ linkOnPaste: true,
15
+ HTMLAttributes: {
16
+ target: '_blank',
17
+ rel: 'noopener noreferrer nofollow',
18
+ },
19
+ };
26
20
  },
27
21
  addAttributes() {
28
22
  return {
@@ -57,7 +51,22 @@
57
51
  },
58
52
  addPasteRules() {
59
53
  return [
60
- core.markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),
54
+ core.markPasteRule({
55
+ find: text => linkifyjs.find(text)
56
+ .filter(link => link.isLink)
57
+ .map(link => ({
58
+ text: link.value,
59
+ index: link.start,
60
+ data: link,
61
+ })),
62
+ type: this.type,
63
+ getAttributes: match => {
64
+ var _a;
65
+ return ({
66
+ href: (_a = match.data) === null || _a === void 0 ? void 0 : _a.href,
67
+ });
68
+ },
69
+ }),
61
70
  ];
62
71
  },
63
72
  addProseMirrorPlugins() {
@@ -94,11 +103,13 @@
94
103
  slice.content.forEach(node => {
95
104
  textContent += node.textContent;
96
105
  });
97
- if (!textContent || !textContent.match(pasteRegexExact)) {
106
+ const link = linkifyjs.find(textContent)
107
+ .find(item => item.isLink && item.value === textContent);
108
+ if (!textContent || !link) {
98
109
  return false;
99
110
  }
100
111
  this.editor.commands.setMark(this.type, {
101
- href: textContent,
112
+ href: link.href,
102
113
  });
103
114
  return true;
104
115
  },
@@ -110,11 +121,9 @@
110
121
  });
111
122
 
112
123
  exports.Link = Link;
113
- exports['default'] = Link;
114
- exports.pasteRegex = pasteRegex;
115
- exports.pasteRegexExact = pasteRegexExact;
124
+ exports["default"] = Link;
116
125
 
117
126
  Object.defineProperty(exports, '__esModule', { value: true });
118
127
 
119
- })));
128
+ }));
120
129
  //# sourceMappingURL=tiptap-extension-link.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiptap-extension-link.umd.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Unset a link mark\n */\n unsetLink: () => ReturnType,\n }\n }\n}\n\n/**\n * A regex that matches any string that contains a link\n */\nexport const pasteRegex = /https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi\n\n/**\n * A regex that matches an url\n */\nexport const pasteRegexExact = /^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z]{2,}\\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi\n\nexport const Link = Mark.create<LinkOptions>({\n name: 'link',\n\n priority: 1000,\n\n inclusive: false,\n\n defaultOptions: {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),\n ]\n },\n\n addProseMirrorPlugins() {\n const plugins = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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 if (!textContent || !textContent.match(pasteRegexExact)) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: textContent,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":["Mark","mergeAttributes","markPasteRule","Plugin","PluginKey"],"mappings":";;;;;;EAyCA;;;QAGa,UAAU,GAAG,kIAAiI;EAE3J;;;QAGa,eAAe,GAAG,oIAAmI;QAErJ,IAAI,GAAGA,SAAI,CAAC,MAAM,CAAc;MAC3C,IAAI,EAAE,MAAM;MAEZ,QAAQ,EAAE,IAAI;MAEd,SAAS,EAAE,KAAK;MAEhB,cAAc,EAAE;UACd,WAAW,EAAE,IAAI;UACjB,WAAW,EAAE,IAAI;UACjB,cAAc,EAAE;cACd,MAAM,EAAE,QAAQ;cAChB,GAAG,EAAE,8BAA8B;WACpC;OACF;MAED,aAAa;UACX,OAAO;cACL,IAAI,EAAE;kBACJ,OAAO,EAAE,IAAI;eACd;cACD,MAAM,EAAE;kBACN,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM;eAC5C;WACF,CAAA;OACF;MAED,SAAS;UACP,OAAO;cACL,EAAE,GAAG,EAAE,SAAS,EAAE;WACnB,CAAA;OACF;MAED,UAAU,CAAC,EAAE,cAAc,EAAE;UAC3B,OAAO,CAAC,GAAG,EAAEC,oBAAe,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC,CAAA;OAC9E;MAED,WAAW;UACT,OAAO;cACL,OAAO,EAAE,UAAU,IAAI,CAAC,EAAE,QAAQ,EAAE;kBAClC,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;eAC5C;cACD,UAAU,EAAE,UAAU,IAAI,CAAC,EAAE,QAAQ,EAAE;kBACrC,OAAO,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAA;eAC/E;cACD,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE;kBAC5B,OAAO,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAA;eAClE;WACF,CAAA;OACF;MAED,aAAa;UACX,OAAO;cACLC,kBAAa,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;WACpE,CAAA;OACF;MAED,qBAAqB;UACnB,MAAM,OAAO,GAAG,EAAE,CAAA;UAElB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;cAC5B,OAAO,CAAC,IAAI,CACV,IAAIC,uBAAM,CAAC;kBACT,GAAG,EAAE,IAAIC,0BAAS,CAAC,iBAAiB,CAAC;kBACrC,KAAK,EAAE;sBACL,WAAW,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK;;0BAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;0BAC/C,MAAM,IAAI,GAAG,MAAC,KAAK,CAAC,MAAsB,0CAAE,OAAO,CAAC,GAAG,CAAC,CAAA;0BAExD,IAAI,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE;8BACtB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;8BAErC,OAAO,IAAI,CAAA;2BACZ;0BAED,OAAO,KAAK,CAAA;uBACb;mBACF;eACF,CAAC,CACH,CAAA;WACF;UAED,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;cAC5B,OAAO,CAAC,IAAI,CACV,IAAID,uBAAM,CAAC;kBACT,GAAG,EAAE,IAAIC,0BAAS,CAAC,iBAAiB,CAAC;kBACrC,KAAK,EAAE;sBACL,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK;0BAC9B,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;0BACtB,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;0BAC3B,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAA;0BAE3B,IAAI,KAAK,EAAE;8BACT,OAAO,KAAK,CAAA;2BACb;0BAED,IAAI,WAAW,GAAG,EAAE,CAAA;0BAEpB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI;8BACxB,WAAW,IAAI,IAAI,CAAC,WAAW,CAAA;2BAChC,CAAC,CAAA;0BAEF,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE;8BACvD,OAAO,KAAK,CAAA;2BACb;0BAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;8BACtC,IAAI,EAAE,WAAW;2BAClB,CAAC,CAAA;0BAEF,OAAO,IAAI,CAAA;uBACZ;mBACF;eACF,CAAC,CACH,CAAA;WACF;UAED,OAAO,OAAO,CAAA;OACf;GACF;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"tiptap-extension-link.umd.js","sources":["../src/link.ts"],"sourcesContent":["import {\n Mark,\n markPasteRule,\n mergeAttributes,\n} from '@tiptap/core'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport { find } from 'linkifyjs'\n\nexport interface LinkOptions {\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\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n link: {\n /**\n * Set a link mark\n */\n setLink: (attributes: { href: string, target?: string }) => ReturnType,\n /**\n * Toggle a link mark\n */\n toggleLink: (attributes: { href: string, target?: string }) => 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 inclusive: false,\n\n addOptions() {\n return {\n openOnClick: true,\n linkOnPaste: true,\n HTMLAttributes: {\n target: '_blank',\n rel: 'noopener noreferrer nofollow',\n },\n }\n },\n\n addAttributes() {\n return {\n href: {\n default: null,\n },\n target: {\n default: this.options.HTMLAttributes.target,\n },\n }\n },\n\n parseHTML() {\n return [\n { tag: 'a[href]' },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n addCommands() {\n return {\n setLink: attributes => ({ commands }) => {\n return commands.setMark('link', attributes)\n },\n toggleLink: attributes => ({ commands }) => {\n return commands.toggleMark('link', attributes, { extendEmptyMarkRange: true })\n },\n unsetLink: () => ({ commands }) => {\n return commands.unsetMark('link', { extendEmptyMarkRange: true })\n },\n }\n },\n\n addPasteRules() {\n return [\n markPasteRule({\n find: text => find(text)\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 = []\n\n if (this.options.openOnClick) {\n plugins.push(\n new Plugin({\n key: new PluginKey('handleClickLink'),\n props: {\n handleClick: (view, pos, event) => {\n const attrs = this.editor.getAttributes('link')\n const link = (event.target as HTMLElement)?.closest('a')\n\n if (link && attrs.href) {\n window.open(attrs.href, attrs.target)\n\n return true\n }\n\n return false\n },\n },\n }),\n )\n }\n\n if (this.options.linkOnPaste) {\n plugins.push(\n 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)\n .find(item => item.isLink && item.value === textContent)\n\n if (!textContent || !link) {\n return false\n }\n\n this.editor.commands.setMark(this.type, {\n href: link.href,\n })\n\n return true\n },\n },\n }),\n )\n }\n\n return plugins\n },\n})\n"],"names":["Mark","mergeAttributes","markPasteRule","find","Plugin","PluginKey"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/extension-link",
3
3
  "description": "link extension for tiptap",
4
- "version": "2.0.0-beta.20",
4
+ "version": "2.0.0-beta.24",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -24,6 +24,7 @@
24
24
  "@tiptap/core": "^2.0.0-beta.1"
25
25
  },
26
26
  "dependencies": {
27
+ "linkifyjs": "^3.0.3",
27
28
  "prosemirror-state": "^1.3.4"
28
29
  },
29
30
  "repository": {
@@ -31,5 +32,5 @@
31
32
  "url": "https://github.com/ueberdosis/tiptap",
32
33
  "directory": "packages/extension-link"
33
34
  },
34
- "gitHead": "42e8755d87b37506d537152dbae4b0c8d497fd48"
35
+ "gitHead": "642627ec3635a1d8fa851887d75bee5f193ec63b"
35
36
  }
package/src/link.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  mergeAttributes,
5
5
  } from '@tiptap/core'
6
6
  import { Plugin, PluginKey } from 'prosemirror-state'
7
+ import { find } from 'linkifyjs'
7
8
 
8
9
  export interface LinkOptions {
9
10
  /**
@@ -39,16 +40,6 @@ declare module '@tiptap/core' {
39
40
  }
40
41
  }
41
42
 
42
- /**
43
- * A regex that matches any string that contains a link
44
- */
45
- export const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)/gi
46
-
47
- /**
48
- * A regex that matches an url
49
- */
50
- export const pasteRegexExact = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)(?:[-a-zA-Z0-9@:%._+~#=?!&/]*)$/gi
51
-
52
43
  export const Link = Mark.create<LinkOptions>({
53
44
  name: 'link',
54
45
 
@@ -56,13 +47,15 @@ export const Link = Mark.create<LinkOptions>({
56
47
 
57
48
  inclusive: false,
58
49
 
59
- defaultOptions: {
60
- openOnClick: true,
61
- linkOnPaste: true,
62
- HTMLAttributes: {
63
- target: '_blank',
64
- rel: 'noopener noreferrer nofollow',
65
- },
50
+ addOptions() {
51
+ return {
52
+ openOnClick: true,
53
+ linkOnPaste: true,
54
+ HTMLAttributes: {
55
+ target: '_blank',
56
+ rel: 'noopener noreferrer nofollow',
57
+ },
58
+ }
66
59
  },
67
60
 
68
61
  addAttributes() {
@@ -102,7 +95,19 @@ export const Link = Mark.create<LinkOptions>({
102
95
 
103
96
  addPasteRules() {
104
97
  return [
105
- markPasteRule(pasteRegex, this.type, match => ({ href: match[0] })),
98
+ markPasteRule({
99
+ find: text => find(text)
100
+ .filter(link => link.isLink)
101
+ .map(link => ({
102
+ text: link.value,
103
+ index: link.start,
104
+ data: link,
105
+ })),
106
+ type: this.type,
107
+ getAttributes: match => ({
108
+ href: match.data?.href,
109
+ }),
110
+ }),
106
111
  ]
107
112
  },
108
113
 
@@ -151,12 +156,15 @@ export const Link = Mark.create<LinkOptions>({
151
156
  textContent += node.textContent
152
157
  })
153
158
 
154
- if (!textContent || !textContent.match(pasteRegexExact)) {
159
+ const link = find(textContent)
160
+ .find(item => item.isLink && item.value === textContent)
161
+
162
+ if (!textContent || !link) {
155
163
  return false
156
164
  }
157
165
 
158
166
  this.editor.commands.setMark(this.type, {
159
- href: textContent,
167
+ href: link.href,
160
168
  })
161
169
 
162
170
  return true
package/CHANGELOG.md DELETED
@@ -1,292 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- # [2.0.0-beta.20](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.19...@tiptap/extension-link@2.0.0-beta.20) (2021-09-08)
7
-
8
-
9
- ### Features
10
-
11
- * add extendEmptyMarkRange option to mark commands ([#1859](https://github.com/ueberdosis/tiptap/issues/1859)) ([14e458e](https://github.com/ueberdosis/tiptap/commit/14e458ea7d7475a51ac00d6f67509dc769919d0b))
12
-
13
-
14
-
15
-
16
-
17
- # [2.0.0-beta.19](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.18...@tiptap/extension-link@2.0.0-beta.19) (2021-07-26)
18
-
19
- **Note:** Version bump only for package @tiptap/extension-link
20
-
21
-
22
-
23
-
24
-
25
- # [2.0.0-beta.18](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.17...@tiptap/extension-link@2.0.0-beta.18) (2021-06-07)
26
-
27
- **Note:** Version bump only for package @tiptap/extension-link
28
-
29
-
30
-
31
-
32
-
33
- # [2.0.0-beta.17](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.16...@tiptap/extension-link@2.0.0-beta.17) (2021-05-18)
34
-
35
- **Note:** Version bump only for package @tiptap/extension-link
36
-
37
-
38
-
39
-
40
-
41
- # [2.0.0-beta.16](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.15...@tiptap/extension-link@2.0.0-beta.16) (2021-05-13)
42
-
43
- **Note:** Version bump only for package @tiptap/extension-link
44
-
45
-
46
-
47
-
48
-
49
- # [2.0.0-beta.15](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.14...@tiptap/extension-link@2.0.0-beta.15) (2021-05-07)
50
-
51
-
52
- ### Features
53
-
54
- * add editor.getAttributes, deprecate editor.getNodeAttributes and editor.getMarkAttributes ([072905c](https://github.com/ueberdosis/tiptap/commit/072905cb95e022a37f5bc937889999bfbb33ad88))
55
-
56
-
57
-
58
-
59
-
60
- # [2.0.0-beta.14](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.13...@tiptap/extension-link@2.0.0-beta.14) (2021-05-07)
61
-
62
-
63
- ### Bug Fixes
64
-
65
- * fix a bug that messed up pasted link attributes, fix [#1284](https://github.com/ueberdosis/tiptap/issues/1284) ([7da647d](https://github.com/ueberdosis/tiptap/commit/7da647d99f1dcefabc653347a8e2abfae3bb972e))
66
- * revert adding exports ([bc320d0](https://github.com/ueberdosis/tiptap/commit/bc320d0b4b80b0e37a7e47a56e0f6daec6e65d98))
67
-
68
-
69
-
70
-
71
-
72
- # [2.0.0-beta.13](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.12...@tiptap/extension-link@2.0.0-beta.13) (2021-05-06)
73
-
74
-
75
- ### Bug Fixes
76
-
77
- * revert adding type: module ([f8d6475](https://github.com/ueberdosis/tiptap/commit/f8d6475e2151faea6f96baecdd6bd75880d50d2c))
78
-
79
-
80
-
81
-
82
-
83
- # [2.0.0-beta.12](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.11...@tiptap/extension-link@2.0.0-beta.12) (2021-05-06)
84
-
85
-
86
- ### Bug Fixes
87
-
88
- * add exports to package.json ([1277fa4](https://github.com/ueberdosis/tiptap/commit/1277fa47151e9c039508cdb219bdd0ffe647f4ee))
89
-
90
-
91
-
92
-
93
-
94
- # [2.0.0-beta.11](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.10...@tiptap/extension-link@2.0.0-beta.11) (2021-05-06)
95
-
96
- **Note:** Version bump only for package @tiptap/extension-link
97
-
98
-
99
-
100
-
101
-
102
- # [2.0.0-beta.10](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.9...@tiptap/extension-link@2.0.0-beta.10) (2021-05-05)
103
-
104
- **Note:** Version bump only for package @tiptap/extension-link
105
-
106
-
107
-
108
-
109
-
110
- # [2.0.0-beta.9](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.8...@tiptap/extension-link@2.0.0-beta.9) (2021-05-05)
111
-
112
-
113
- ### Features
114
-
115
- * add linkOnPaste option for adding a link to the current selection if the pasted content only contains an url, fix [#1210](https://github.com/ueberdosis/tiptap/issues/1210) ([9e9401e](https://github.com/ueberdosis/tiptap/commit/9e9401e68b79d34070fb79c4f356841fa19fe436))
116
-
117
-
118
-
119
-
120
-
121
- # [2.0.0-beta.8](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.7...@tiptap/extension-link@2.0.0-beta.8) (2021-04-23)
122
-
123
- **Note:** Version bump only for package @tiptap/extension-link
124
-
125
-
126
-
127
-
128
-
129
- # [2.0.0-beta.7](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.6...@tiptap/extension-link@2.0.0-beta.7) (2021-04-22)
130
-
131
- **Note:** Version bump only for package @tiptap/extension-link
132
-
133
-
134
-
135
-
136
-
137
- # [2.0.0-beta.6](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.5...@tiptap/extension-link@2.0.0-beta.6) (2021-04-21)
138
-
139
- **Note:** Version bump only for package @tiptap/extension-link
140
-
141
-
142
-
143
-
144
-
145
- # [2.0.0-beta.5](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.4...@tiptap/extension-link@2.0.0-beta.5) (2021-04-16)
146
-
147
- **Note:** Version bump only for package @tiptap/extension-link
148
-
149
-
150
-
151
-
152
-
153
- # [2.0.0-beta.4](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.3...@tiptap/extension-link@2.0.0-beta.4) (2021-04-15)
154
-
155
- **Note:** Version bump only for package @tiptap/extension-link
156
-
157
-
158
-
159
-
160
-
161
- # [2.0.0-beta.3](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.2...@tiptap/extension-link@2.0.0-beta.3) (2021-04-11)
162
-
163
-
164
- ### Bug Fixes
165
-
166
- * fix a bug in link click handler, where we can’t click on text selections. fix [#263](https://github.com/ueberdosis/tiptap/issues/263) ([33c30c0](https://github.com/ueberdosis/tiptap/commit/33c30c0d6df66190fd1d5073ccc43b1020b517f9))
167
-
168
-
169
-
170
-
171
-
172
- # [2.0.0-beta.2](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-beta.1...@tiptap/extension-link@2.0.0-beta.2) (2021-04-07)
173
-
174
-
175
- ### Features
176
-
177
- * add priority option to extensions ([bb1ae65](https://github.com/ueberdosis/tiptap/commit/bb1ae659a463e97a7ada15af711347b5c004897a))
178
-
179
-
180
-
181
-
182
-
183
- # [2.0.0-beta.1](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.11...@tiptap/extension-link@2.0.0-beta.1) (2021-03-05)
184
-
185
- **Note:** Version bump only for package @tiptap/extension-link
186
-
187
-
188
-
189
-
190
-
191
- # [2.0.0-alpha.11](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.10...@tiptap/extension-link@2.0.0-alpha.11) (2021-02-16)
192
-
193
- **Note:** Version bump only for package @tiptap/extension-link
194
-
195
-
196
-
197
-
198
-
199
- # [2.0.0-alpha.10](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.9...@tiptap/extension-link@2.0.0-alpha.10) (2021-02-07)
200
-
201
- **Note:** Version bump only for package @tiptap/extension-link
202
-
203
-
204
-
205
-
206
-
207
- # [2.0.0-alpha.9](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.8...@tiptap/extension-link@2.0.0-alpha.9) (2021-02-05)
208
-
209
- **Note:** Version bump only for package @tiptap/extension-link
210
-
211
-
212
-
213
-
214
-
215
- # [2.0.0-alpha.8](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.7...@tiptap/extension-link@2.0.0-alpha.8) (2021-01-29)
216
-
217
- **Note:** Version bump only for package @tiptap/extension-link
218
-
219
-
220
-
221
-
222
-
223
- # [2.0.0-alpha.7](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.6...@tiptap/extension-link@2.0.0-alpha.7) (2021-01-29)
224
-
225
- **Note:** Version bump only for package @tiptap/extension-link
226
-
227
-
228
-
229
-
230
-
231
- # [2.0.0-alpha.6](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.5...@tiptap/extension-link@2.0.0-alpha.6) (2021-01-28)
232
-
233
- **Note:** Version bump only for package @tiptap/extension-link
234
-
235
-
236
-
237
-
238
-
239
- # [2.0.0-alpha.5](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.4...@tiptap/extension-link@2.0.0-alpha.5) (2020-12-18)
240
-
241
- **Note:** Version bump only for package @tiptap/extension-link
242
-
243
-
244
-
245
-
246
-
247
- # [2.0.0-alpha.4](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.3...@tiptap/extension-link@2.0.0-alpha.4) (2020-12-02)
248
-
249
- **Note:** Version bump only for package @tiptap/extension-link
250
-
251
-
252
-
253
-
254
-
255
- # [2.0.0-alpha.3](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.2...@tiptap/extension-link@2.0.0-alpha.3) (2020-11-19)
256
-
257
- **Note:** Version bump only for package @tiptap/extension-link
258
-
259
-
260
-
261
-
262
-
263
- # [2.0.0-alpha.2](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@2.0.0-alpha.1...@tiptap/extension-link@2.0.0-alpha.2) (2020-11-19)
264
-
265
- **Note:** Version bump only for package @tiptap/extension-link
266
-
267
-
268
-
269
-
270
-
271
- # [2.0.0-alpha.1](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@1.0.0-alpha.2...@tiptap/extension-link@2.0.0-alpha.1) (2020-11-18)
272
-
273
- **Note:** Version bump only for package @tiptap/extension-link
274
-
275
-
276
-
277
-
278
-
279
- # [1.0.0-alpha.2](https://github.com/ueberdosis/tiptap/compare/@tiptap/extension-link@1.0.0-alpha.1...@tiptap/extension-link@1.0.0-alpha.2) (2020-11-16)
280
-
281
- **Note:** Version bump only for package @tiptap/extension-link
282
-
283
-
284
-
285
-
286
-
287
- # 1.0.0-alpha.1 (2020-11-16)
288
-
289
-
290
- ### Reverts
291
-
292
- * Revert "use global namespace" ([0c9ce26](https://github.com/ueberdosis/tiptap/commit/0c9ce26c02c07d88a757c01b0a9d7f9e2b0b7502))