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:
@@ -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 |
|
||||||
|
|||||||
@@ -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
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user