feat: add linkOnPaste option for adding a link to the current selection if the pasted content only contains an url, fix #1210

This commit is contained in:
Philipp Kühn
2021-05-05 09:33:52 +02:00
parent 196eb8622e
commit 9e9401e68b
2 changed files with 71 additions and 21 deletions

View File

@@ -18,10 +18,11 @@ yarn add @tiptap/extension-link
``` ```
## Settings ## Settings
| Option | Type | Default | Description | | Option | Type | Default | Description |
| -------------- | --------- | ----------------------------------------------------------- | --------------------------------------------------------------------- | | -------------- | --------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------- |
| HTMLAttributes | `Object` | `{ target: '_blank', rel: 'noopener noreferrer nofollow' }` | Custom HTML attributes that should be added to the rendered HTML tag. | | HTMLAttributes | `Object` | `{ target: '_blank', rel: 'noopener noreferrer nofollow' }` | Custom HTML attributes that should be added to the rendered HTML tag. |
| openOnClick | `Boolean` | `true` | If enabled, links will be opened on click. | | openOnClick | `Boolean` | `true` | If enabled, links will be opened on click. |
| linkOnPaste | `Boolean` | `true` | Adds a link to the current selection if the pasted content only contains an url. |
## Commands ## Commands
| Command | Parameters | Description | | Command | Parameters | Description |

View File

@@ -7,7 +7,17 @@ import {
import { Plugin, PluginKey } from 'prosemirror-state' import { Plugin, PluginKey } from 'prosemirror-state'
export interface LinkOptions { export interface LinkOptions {
/**
* If enabled, links will be opened on click.
*/
openOnClick: boolean, openOnClick: boolean,
/**
* Adds a link to the current selection if the pasted content only contains an url.
*/
linkOnPaste: boolean,
/**
* A list of HTML attributes to be rendered.
*/
HTMLAttributes: Record<string, any>, HTMLAttributes: Record<string, any>,
} }
@@ -32,6 +42,7 @@ declare module '@tiptap/core' {
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 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
export const pasteRegexWithBrackets = /(?:\()https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/()]*)(?:\))/gi export const pasteRegexWithBrackets = /(?:\()https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%._+~#=?!&/()]*)(?:\))/gi
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
export const Link = Mark.create<LinkOptions>({ export const Link = Mark.create<LinkOptions>({
name: 'link', name: 'link',
@@ -42,6 +53,7 @@ export const Link = Mark.create<LinkOptions>({
defaultOptions: { defaultOptions: {
openOnClick: true, openOnClick: true,
linkOnPaste: true,
HTMLAttributes: { HTMLAttributes: {
target: '_blank', target: '_blank',
rel: 'noopener noreferrer nofollow', rel: 'noopener noreferrer nofollow',
@@ -91,28 +103,65 @@ export const Link = Mark.create<LinkOptions>({
}, },
addProseMirrorPlugins() { addProseMirrorPlugins() {
if (!this.options.openOnClick) { const plugins = []
return []
if (this.options.openOnClick) {
plugins.push(
new Plugin({
key: new PluginKey('handleClickLink'),
props: {
handleClick: (view, pos, event) => {
const attrs = this.editor.getMarkAttributes('link')
const link = (event.target as HTMLElement)?.closest('a')
if (link && attrs.href) {
window.open(attrs.href, attrs.target)
return true
}
return false
},
},
}),
)
} }
return [ if (this.options.linkOnPaste) {
new Plugin({ plugins.push(
key: new PluginKey('handleClick'), new Plugin({
props: { key: new PluginKey('handlePasteLink'),
handleClick: (view, pos, event) => { props: {
const attrs = this.editor.getMarkAttributes('link') handlePaste: (view, event, slice) => {
const link = (event.target as HTMLElement)?.closest('a') const { state } = view
const { selection } = state
const { empty } = selection
if (link && attrs.href) { if (empty) {
window.open(attrs.href, attrs.target) return false
}
let textContent = ''
slice.content.forEach(node => {
textContent += node.textContent
})
if (!textContent || !textContent.match(pasteRegexExact)) {
return false
}
this.editor.commands.setMark(this.type, {
href: textContent,
})
return true return true
} },
return false
}, },
}, }),
}), )
] }
return plugins
}, },
}) })