import { Command, Mark, markPasteRule, } from '@tiptap/core' import { Plugin, PluginKey } from 'prosemirror-state' export interface LinkOptions { openOnClick: boolean, target: string, rel: string, } export type LinkCommand = (options: {href?: string, target?: string}) => Command declare module '@tiptap/core/src/Editor' { interface Commands { link: LinkCommand, } } export const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%_+.~#?&//=]*)/gi export default new Mark() .name('link') .defaults({ openOnClick: true, target: '_blank', rel: 'noopener noreferrer nofollow', }) .schema(({ options }) => ({ attrs: { href: { default: null, }, target: { default: null, }, }, inclusive: false, parseDOM: [ { tag: 'a[href]', getAttrs: node => ({ href: (node as HTMLElement).getAttribute('href'), target: (node as HTMLElement).getAttribute('target'), }), }, ], toDOM: node => ['a', { ...node.attrs, rel: options.rel, target: node.attrs.target ? node.attrs.target : options.target, }, 0], })) .commands(({ name }) => ({ link: attributes => ({ commands }) => { if (!attributes.href) { return commands.removeMark(name) } return commands.updateMark(name, attributes) }, })) .pasteRules(({ type }) => [ markPasteRule(pasteRegex, type, (url: string) => ({ href: url })), ]) .plugins(({ editor, options, name }) => { if (!options.openOnClick) { return [] } return [ new Plugin({ key: new PluginKey('handleClick'), props: { handleClick: (view, pos, event) => { const attrs = editor.getMarkAttrs(name) if (attrs.href && event.target instanceof HTMLAnchorElement) { window.open(attrs.href, attrs.target) return false } return true }, }, }), ] }) .create()