@tiptap/extension-link
Advanced tools
Comparing version 2.0.0-beta.30 to 2.0.0-beta.31
import { Mark } from '@tiptap/core'; | ||
export interface LinkOptions { | ||
/** | ||
* If enabled, it adds links as you type. | ||
*/ | ||
autolink: boolean; | ||
/** | ||
* If enabled, links will be opened on click. | ||
@@ -5,0 +9,0 @@ */ |
@@ -6,9 +6,127 @@ 'use strict'; | ||
var core = require('@tiptap/core'); | ||
var linkifyjs = require('linkifyjs'); | ||
var prosemirrorState = require('prosemirror-state'); | ||
var linkifyjs = require('linkifyjs'); | ||
function autolink(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('autolink'), | ||
appendTransaction: (transactions, oldState, newState) => { | ||
const docChanges = transactions.some(transaction => transaction.docChanged) | ||
&& !oldState.doc.eq(newState.doc); | ||
if (!docChanges) { | ||
return; | ||
} | ||
const { tr } = newState; | ||
const transform = core.combineTransactionSteps(oldState.doc, transactions); | ||
const { mapping } = transform; | ||
const changes = core.getChangedRanges(transform); | ||
changes.forEach(({ oldRange, newRange }) => { | ||
// at first we check if we have to remove links | ||
core.getMarksBetween(oldRange.from, oldRange.to, oldState.doc) | ||
.filter(item => item.mark.type === options.type) | ||
.forEach(oldMark => { | ||
const newFrom = mapping.map(oldMark.from); | ||
const newTo = mapping.map(oldMark.to); | ||
const newMarks = core.getMarksBetween(newFrom, newTo, newState.doc) | ||
.filter(item => item.mark.type === options.type); | ||
if (!newMarks.length) { | ||
return; | ||
} | ||
const newMark = newMarks[0]; | ||
const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to); | ||
const newLinkText = newState.doc.textBetween(newMark.from, newMark.to); | ||
const wasLink = linkifyjs.test(oldLinkText); | ||
const isLink = linkifyjs.test(newLinkText); | ||
// remove only the link, if it was a link before too | ||
// because we don’t want to remove links that were set manually | ||
if (wasLink && !isLink) { | ||
tr.removeMark(newMark.from, newMark.to, options.type); | ||
} | ||
}); | ||
// now let’s see if we can add new links | ||
core.findChildrenInRange(newState.doc, newRange, node => node.isTextblock) | ||
.forEach(textBlock => { | ||
linkifyjs.find(textBlock.node.textContent) | ||
.filter(link => link.isLink) | ||
// calculate link position | ||
.map(link => ({ | ||
...link, | ||
from: textBlock.pos + link.start + 1, | ||
to: textBlock.pos + link.end + 1, | ||
})) | ||
// check if link is within the changed range | ||
.filter(link => { | ||
const fromIsInRange = newRange.from >= link.from && newRange.from <= link.to; | ||
const toIsInRange = newRange.to >= link.from && newRange.to <= link.to; | ||
return fromIsInRange || toIsInRange; | ||
}) | ||
// add link mark | ||
.forEach(link => { | ||
tr.addMark(link.from, link.to, options.type.create({ | ||
href: link.href, | ||
})); | ||
}); | ||
}); | ||
}); | ||
if (!tr.steps.length) { | ||
return; | ||
} | ||
return tr; | ||
}, | ||
}); | ||
} | ||
function clickHandler(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = core.getAttributes(view.state, options.type.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
}); | ||
} | ||
function pasteHandler(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = linkifyjs.find(textContent).find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
options.editor.commands.setMark(options.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
}); | ||
} | ||
const Link = core.Mark.create({ | ||
name: 'link', | ||
priority: 1000, | ||
inclusive: false, | ||
keepOnSplit: false, | ||
inclusive() { | ||
return this.options.autolink; | ||
}, | ||
addOptions() { | ||
@@ -18,2 +136,3 @@ return { | ||
linkOnPaste: true, | ||
autolink: true, | ||
HTMLAttributes: { | ||
@@ -41,3 +160,7 @@ target: '_blank', | ||
renderHTML({ HTMLAttributes }) { | ||
return ['a', core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; | ||
return [ | ||
'a', | ||
core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), | ||
0, | ||
]; | ||
}, | ||
@@ -79,45 +202,16 @@ addCommands() { | ||
const plugins = []; | ||
if (this.options.autolink) { | ||
plugins.push(autolink({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.openOnClick) { | ||
plugins.push(new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = this.editor.getAttributes(this.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
plugins.push(clickHandler({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push(new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = linkifyjs.find(textContent) | ||
.find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
this.editor.commands.setMark(this.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
plugins.push(pasteHandler({ | ||
editor: this.editor, | ||
type: this.type, | ||
})); | ||
@@ -124,0 +218,0 @@ } |
@@ -1,9 +0,127 @@ | ||
import { Mark, mergeAttributes, markPasteRule } from '@tiptap/core'; | ||
import { combineTransactionSteps, getChangedRanges, getMarksBetween, findChildrenInRange, getAttributes, Mark, mergeAttributes, markPasteRule } from '@tiptap/core'; | ||
import { test, find } from 'linkifyjs'; | ||
import { Plugin, PluginKey } from 'prosemirror-state'; | ||
import { find } from 'linkifyjs'; | ||
function autolink(options) { | ||
return new Plugin({ | ||
key: new PluginKey('autolink'), | ||
appendTransaction: (transactions, oldState, newState) => { | ||
const docChanges = transactions.some(transaction => transaction.docChanged) | ||
&& !oldState.doc.eq(newState.doc); | ||
if (!docChanges) { | ||
return; | ||
} | ||
const { tr } = newState; | ||
const transform = combineTransactionSteps(oldState.doc, transactions); | ||
const { mapping } = transform; | ||
const changes = getChangedRanges(transform); | ||
changes.forEach(({ oldRange, newRange }) => { | ||
// at first we check if we have to remove links | ||
getMarksBetween(oldRange.from, oldRange.to, oldState.doc) | ||
.filter(item => item.mark.type === options.type) | ||
.forEach(oldMark => { | ||
const newFrom = mapping.map(oldMark.from); | ||
const newTo = mapping.map(oldMark.to); | ||
const newMarks = getMarksBetween(newFrom, newTo, newState.doc) | ||
.filter(item => item.mark.type === options.type); | ||
if (!newMarks.length) { | ||
return; | ||
} | ||
const newMark = newMarks[0]; | ||
const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to); | ||
const newLinkText = newState.doc.textBetween(newMark.from, newMark.to); | ||
const wasLink = test(oldLinkText); | ||
const isLink = test(newLinkText); | ||
// remove only the link, if it was a link before too | ||
// because we don’t want to remove links that were set manually | ||
if (wasLink && !isLink) { | ||
tr.removeMark(newMark.from, newMark.to, options.type); | ||
} | ||
}); | ||
// now let’s see if we can add new links | ||
findChildrenInRange(newState.doc, newRange, node => node.isTextblock) | ||
.forEach(textBlock => { | ||
find(textBlock.node.textContent) | ||
.filter(link => link.isLink) | ||
// calculate link position | ||
.map(link => ({ | ||
...link, | ||
from: textBlock.pos + link.start + 1, | ||
to: textBlock.pos + link.end + 1, | ||
})) | ||
// check if link is within the changed range | ||
.filter(link => { | ||
const fromIsInRange = newRange.from >= link.from && newRange.from <= link.to; | ||
const toIsInRange = newRange.to >= link.from && newRange.to <= link.to; | ||
return fromIsInRange || toIsInRange; | ||
}) | ||
// add link mark | ||
.forEach(link => { | ||
tr.addMark(link.from, link.to, options.type.create({ | ||
href: link.href, | ||
})); | ||
}); | ||
}); | ||
}); | ||
if (!tr.steps.length) { | ||
return; | ||
} | ||
return tr; | ||
}, | ||
}); | ||
} | ||
function clickHandler(options) { | ||
return new Plugin({ | ||
key: new PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = getAttributes(view.state, options.type.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
}); | ||
} | ||
function pasteHandler(options) { | ||
return new Plugin({ | ||
key: new PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = find(textContent).find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
options.editor.commands.setMark(options.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
}); | ||
} | ||
const Link = Mark.create({ | ||
name: 'link', | ||
priority: 1000, | ||
inclusive: false, | ||
keepOnSplit: false, | ||
inclusive() { | ||
return this.options.autolink; | ||
}, | ||
addOptions() { | ||
@@ -13,2 +131,3 @@ return { | ||
linkOnPaste: true, | ||
autolink: true, | ||
HTMLAttributes: { | ||
@@ -36,3 +155,7 @@ target: '_blank', | ||
renderHTML({ HTMLAttributes }) { | ||
return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; | ||
return [ | ||
'a', | ||
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), | ||
0, | ||
]; | ||
}, | ||
@@ -74,45 +197,16 @@ addCommands() { | ||
const plugins = []; | ||
if (this.options.autolink) { | ||
plugins.push(autolink({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.openOnClick) { | ||
plugins.push(new Plugin({ | ||
key: new PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = this.editor.getAttributes(this.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
plugins.push(clickHandler({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push(new Plugin({ | ||
key: new PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = find(textContent) | ||
.find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
this.editor.commands.setMark(this.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
plugins.push(pasteHandler({ | ||
editor: this.editor, | ||
type: this.type, | ||
})); | ||
@@ -119,0 +213,0 @@ } |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('prosemirror-state'), require('linkifyjs')) : | ||
typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'prosemirror-state', 'linkifyjs'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/extension-link"] = {}, global.core, global.prosemirrorState, global.linkifyjs)); | ||
})(this, (function (exports, core, prosemirrorState, linkifyjs) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/core'), require('linkifyjs'), require('prosemirror-state')) : | ||
typeof define === 'function' && define.amd ? define(['exports', '@tiptap/core', 'linkifyjs', 'prosemirror-state'], factory) : | ||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/extension-link"] = {}, global.core, global.linkifyjs, global.prosemirrorState)); | ||
})(this, (function (exports, core, linkifyjs, prosemirrorState) { 'use strict'; | ||
function autolink(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('autolink'), | ||
appendTransaction: (transactions, oldState, newState) => { | ||
const docChanges = transactions.some(transaction => transaction.docChanged) | ||
&& !oldState.doc.eq(newState.doc); | ||
if (!docChanges) { | ||
return; | ||
} | ||
const { tr } = newState; | ||
const transform = core.combineTransactionSteps(oldState.doc, transactions); | ||
const { mapping } = transform; | ||
const changes = core.getChangedRanges(transform); | ||
changes.forEach(({ oldRange, newRange }) => { | ||
// at first we check if we have to remove links | ||
core.getMarksBetween(oldRange.from, oldRange.to, oldState.doc) | ||
.filter(item => item.mark.type === options.type) | ||
.forEach(oldMark => { | ||
const newFrom = mapping.map(oldMark.from); | ||
const newTo = mapping.map(oldMark.to); | ||
const newMarks = core.getMarksBetween(newFrom, newTo, newState.doc) | ||
.filter(item => item.mark.type === options.type); | ||
if (!newMarks.length) { | ||
return; | ||
} | ||
const newMark = newMarks[0]; | ||
const oldLinkText = oldState.doc.textBetween(oldMark.from, oldMark.to); | ||
const newLinkText = newState.doc.textBetween(newMark.from, newMark.to); | ||
const wasLink = linkifyjs.test(oldLinkText); | ||
const isLink = linkifyjs.test(newLinkText); | ||
// remove only the link, if it was a link before too | ||
// because we don’t want to remove links that were set manually | ||
if (wasLink && !isLink) { | ||
tr.removeMark(newMark.from, newMark.to, options.type); | ||
} | ||
}); | ||
// now let’s see if we can add new links | ||
core.findChildrenInRange(newState.doc, newRange, node => node.isTextblock) | ||
.forEach(textBlock => { | ||
linkifyjs.find(textBlock.node.textContent) | ||
.filter(link => link.isLink) | ||
// calculate link position | ||
.map(link => ({ | ||
...link, | ||
from: textBlock.pos + link.start + 1, | ||
to: textBlock.pos + link.end + 1, | ||
})) | ||
// check if link is within the changed range | ||
.filter(link => { | ||
const fromIsInRange = newRange.from >= link.from && newRange.from <= link.to; | ||
const toIsInRange = newRange.to >= link.from && newRange.to <= link.to; | ||
return fromIsInRange || toIsInRange; | ||
}) | ||
// add link mark | ||
.forEach(link => { | ||
tr.addMark(link.from, link.to, options.type.create({ | ||
href: link.href, | ||
})); | ||
}); | ||
}); | ||
}); | ||
if (!tr.steps.length) { | ||
return; | ||
} | ||
return tr; | ||
}, | ||
}); | ||
} | ||
function clickHandler(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = core.getAttributes(view.state, options.type.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
}); | ||
} | ||
function pasteHandler(options) { | ||
return new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = linkifyjs.find(textContent).find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
options.editor.commands.setMark(options.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
}); | ||
} | ||
const Link = core.Mark.create({ | ||
name: 'link', | ||
priority: 1000, | ||
inclusive: false, | ||
keepOnSplit: false, | ||
inclusive() { | ||
return this.options.autolink; | ||
}, | ||
addOptions() { | ||
@@ -15,2 +133,3 @@ return { | ||
linkOnPaste: true, | ||
autolink: true, | ||
HTMLAttributes: { | ||
@@ -38,3 +157,7 @@ target: '_blank', | ||
renderHTML({ HTMLAttributes }) { | ||
return ['a', core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; | ||
return [ | ||
'a', | ||
core.mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), | ||
0, | ||
]; | ||
}, | ||
@@ -76,45 +199,16 @@ addCommands() { | ||
const plugins = []; | ||
if (this.options.autolink) { | ||
plugins.push(autolink({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.openOnClick) { | ||
plugins.push(new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
var _a; | ||
const attrs = this.editor.getAttributes(this.name); | ||
const link = (_a = event.target) === null || _a === void 0 ? void 0 : _a.closest('a'); | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
}, | ||
plugins.push(clickHandler({ | ||
type: this.type, | ||
})); | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push(new prosemirrorState.Plugin({ | ||
key: new prosemirrorState.PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view; | ||
const { selection } = state; | ||
const { empty } = selection; | ||
if (empty) { | ||
return false; | ||
} | ||
let textContent = ''; | ||
slice.content.forEach(node => { | ||
textContent += node.textContent; | ||
}); | ||
const link = linkifyjs.find(textContent) | ||
.find(item => item.isLink && item.value === textContent); | ||
if (!textContent || !link) { | ||
return false; | ||
} | ||
this.editor.commands.setMark(this.type, { | ||
href: link.href, | ||
}); | ||
return true; | ||
}, | ||
}, | ||
plugins.push(pasteHandler({ | ||
editor: this.editor, | ||
type: this.type, | ||
})); | ||
@@ -121,0 +215,0 @@ } |
{ | ||
"name": "@tiptap/extension-link", | ||
"description": "link extension for tiptap", | ||
"version": "2.0.0-beta.30", | ||
"version": "2.0.0-beta.31", | ||
"homepage": "https://tiptap.dev", | ||
@@ -28,2 +28,3 @@ "keywords": [ | ||
"linkifyjs": "^3.0.4", | ||
"prosemirror-model": "^1.15.0", | ||
"prosemirror-state": "^1.3.4" | ||
@@ -36,3 +37,3 @@ }, | ||
}, | ||
"gitHead": "6360278660d9c1e256975699d911a55dfa943d5d" | ||
"gitHead": "4e1a50250bc5d3ef916326e5bed30c93448284b3" | ||
} |
@@ -1,11 +0,13 @@ | ||
import { | ||
Mark, | ||
markPasteRule, | ||
mergeAttributes, | ||
} from '@tiptap/core' | ||
import { Plugin, PluginKey } from 'prosemirror-state' | ||
import { Mark, markPasteRule, mergeAttributes } from '@tiptap/core' | ||
import { find } from 'linkifyjs' | ||
import autolink from './helpers/autolink' | ||
import clickHandler from './helpers/clickHandler' | ||
import pasteHandler from './helpers/pasteHandler' | ||
export interface LinkOptions { | ||
/** | ||
* If enabled, it adds links as you type. | ||
*/ | ||
autolink: boolean, | ||
/** | ||
* If enabled, links will be opened on click. | ||
@@ -48,4 +50,8 @@ */ | ||
inclusive: false, | ||
keepOnSplit: false, | ||
inclusive() { | ||
return this.options.autolink | ||
}, | ||
addOptions() { | ||
@@ -55,2 +61,3 @@ return { | ||
linkOnPaste: true, | ||
autolink: true, | ||
HTMLAttributes: { | ||
@@ -81,3 +88,7 @@ target: '_blank', | ||
renderHTML({ HTMLAttributes }) { | ||
return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0] | ||
return [ | ||
'a', | ||
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), | ||
0, | ||
] | ||
}, | ||
@@ -90,5 +101,7 @@ | ||
}, | ||
toggleLink: attributes => ({ commands }) => { | ||
return commands.toggleMark(this.name, attributes, { extendEmptyMarkRange: true }) | ||
}, | ||
unsetLink: () => ({ commands }) => { | ||
@@ -121,60 +134,19 @@ return commands.unsetMark(this.name, { extendEmptyMarkRange: true }) | ||
if (this.options.autolink) { | ||
plugins.push(autolink({ | ||
type: this.type, | ||
})) | ||
} | ||
if (this.options.openOnClick) { | ||
plugins.push( | ||
new Plugin({ | ||
key: new PluginKey('handleClickLink'), | ||
props: { | ||
handleClick: (view, pos, event) => { | ||
const attrs = this.editor.getAttributes(this.name) | ||
const link = (event.target as HTMLElement)?.closest('a') | ||
if (link && attrs.href) { | ||
window.open(attrs.href, attrs.target) | ||
return true | ||
} | ||
return false | ||
}, | ||
}, | ||
}), | ||
) | ||
plugins.push(clickHandler({ | ||
type: this.type, | ||
})) | ||
} | ||
if (this.options.linkOnPaste) { | ||
plugins.push( | ||
new Plugin({ | ||
key: new PluginKey('handlePasteLink'), | ||
props: { | ||
handlePaste: (view, event, slice) => { | ||
const { state } = view | ||
const { selection } = state | ||
const { empty } = selection | ||
if (empty) { | ||
return false | ||
} | ||
let textContent = '' | ||
slice.content.forEach(node => { | ||
textContent += node.textContent | ||
}) | ||
const link = find(textContent) | ||
.find(item => item.isLink && item.value === textContent) | ||
if (!textContent || !link) { | ||
return false | ||
} | ||
this.editor.commands.setMark(this.type, { | ||
href: link.href, | ||
}) | ||
return true | ||
}, | ||
}, | ||
}), | ||
) | ||
plugins.push(pasteHandler({ | ||
editor: this.editor, | ||
type: this.type, | ||
})) | ||
} | ||
@@ -181,0 +153,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
81857
19
983
4
1
+ Addedprosemirror-model@^1.15.0