flotilla/src/lib/tiptap/LinkExtension.ts

91 lines
2 KiB
TypeScript
Raw Normal View History

2024-08-26 21:43:43 +00:00
import {Node, nodePasteRule, type PasteRuleMatch} from "@tiptap/core"
import type {Node as ProsemirrorNode} from "@tiptap/pm/model"
import type {MarkdownSerializerState} from "prosemirror-markdown"
2024-08-19 21:22:16 +00:00
2024-08-26 21:43:43 +00:00
export const LINK_REGEX =
/^([a-z\+:]{2,30}:\/\/)?[^<>\(\)\s]+\.[a-z]{2,6}[^\s]*[^<>"'\.!?,:\s\)\(]*/gi
2024-08-19 21:22:16 +00:00
export const createPasteRuleMatch = <T extends Record<string, unknown>>(
match: RegExpMatchArray,
data: T,
2024-08-26 21:43:43 +00:00
): PasteRuleMatch => ({index: match.index!, replaceWith: match[2], text: match[0], match, data})
2024-08-19 21:22:16 +00:00
export interface LinkAttributes {
url: string
}
2024-08-26 21:43:43 +00:00
declare module "@tiptap/core" {
2024-08-19 21:22:16 +00:00
interface Commands<ReturnType> {
2024-08-29 16:23:54 +00:00
inlineLink: {
2024-08-26 21:43:43 +00:00
insertLink: (options: {url: string}) => ReturnType
2024-08-19 21:22:16 +00:00
}
}
}
export const LinkExtension = Node.create({
2024-08-22 23:16:35 +00:00
atom: true,
2024-08-29 16:23:54 +00:00
name: "inlineLink",
2024-08-26 21:43:43 +00:00
group: "inline",
2024-08-19 21:22:16 +00:00
inline: true,
2024-08-20 20:20:45 +00:00
selectable: true,
draggable: true,
2024-08-19 21:22:16 +00:00
priority: 1000,
addAttributes() {
return {
2024-08-26 21:43:43 +00:00
url: {default: null},
2024-08-19 21:22:16 +00:00
}
},
renderHTML(props) {
2024-08-26 21:43:43 +00:00
return ["div", {"data-url": props.node.attrs.url}]
2024-08-19 21:22:16 +00:00
},
renderText(props) {
return props.node.attrs.url
},
addStorage() {
return {
markdown: {
serialize(state: MarkdownSerializerState, node: ProsemirrorNode) {
state.write(node.attrs.url)
},
parse: {},
},
}
},
addCommands() {
return {
insertLink:
2024-08-26 21:43:43 +00:00
({url}) =>
({commands}) => {
2024-08-19 21:22:16 +00:00
return commands.insertContent(
2024-08-26 21:43:43 +00:00
{type: this.name, attrs: {url}},
2024-08-19 21:22:16 +00:00
{
updateSelection: false,
},
)
},
}
},
addPasteRules() {
return [
nodePasteRule({
type: this.type,
2024-08-26 21:43:43 +00:00
getAttributes: match => match.data,
find: text => {
2024-08-19 21:22:16 +00:00
const matches = []
for (const match of text.matchAll(LINK_REGEX)) {
2024-08-29 16:23:54 +00:00
console.log(text, match)
2024-08-19 21:22:16 +00:00
try {
2024-08-26 21:43:43 +00:00
matches.push(createPasteRuleMatch(match, {url: match[0]}))
2024-08-19 21:22:16 +00:00
} catch (e) {
continue
}
}
return matches
},
}),
]
},
})