forked from github/plane
[WEB-617] fix: link behaviour fixed on formatting (#3855)
* fix: link behaviour fixed on formatting * chore: added harmful script checks for links
This commit is contained in:
parent
87eadc3c5d
commit
126d01bdc5
@ -15,9 +15,15 @@ export function clickHandler(options: ClickHandlerOptions): Plugin {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventTarget = event.target as HTMLElement;
|
let a = event.target as HTMLElement;
|
||||||
|
const els = [];
|
||||||
|
|
||||||
if (eventTarget.nodeName !== "A") {
|
while (a.nodeName !== "DIV") {
|
||||||
|
els.push(a);
|
||||||
|
a = a.parentNode as HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!els.find((value) => value.nodeName === "A")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,9 +34,7 @@ export function clickHandler(options: ClickHandlerOptions): Plugin {
|
|||||||
const target = link?.target ?? attrs.target;
|
const target = link?.target ?? attrs.target;
|
||||||
|
|
||||||
if (link && href) {
|
if (link && href) {
|
||||||
if (view.editable) {
|
window.open(href, target);
|
||||||
window.open(href, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,8 @@ export function pasteHandler(options: PasteHandlerOptions): Plugin {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = event.clipboardData?.getData("text/html");
|
|
||||||
|
|
||||||
const hrefRegex = /href="([^"]*)"/;
|
|
||||||
|
|
||||||
const existingLink = html?.match(hrefRegex);
|
|
||||||
|
|
||||||
const url = existingLink ? existingLink[1] : link.href;
|
|
||||||
|
|
||||||
options.editor.commands.setMark(options.type, {
|
options.editor.commands.setMark(options.type, {
|
||||||
href: url,
|
href: link.href,
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,41 +1,76 @@
|
|||||||
import { Mark, markPasteRule, mergeAttributes } from "@tiptap/core";
|
import { Mark, markPasteRule, mergeAttributes, PasteRuleMatch } from "@tiptap/core";
|
||||||
import { Plugin } from "@tiptap/pm/state";
|
import { Plugin } from "@tiptap/pm/state";
|
||||||
import { find, registerCustomProtocol, reset } from "linkifyjs";
|
import { find, registerCustomProtocol, reset } from "linkifyjs";
|
||||||
|
import { autolink } from "./helpers/autolink";
|
||||||
import { autolink } from "src/ui/extensions/custom-link/helpers/autolink";
|
import { clickHandler } from "./helpers/clickHandler";
|
||||||
import { clickHandler } from "src/ui/extensions/custom-link/helpers/clickHandler";
|
import { pasteHandler } from "./helpers/pasteHandler";
|
||||||
import { pasteHandler } from "src/ui/extensions/custom-link/helpers/pasteHandler";
|
|
||||||
|
|
||||||
export interface LinkProtocolOptions {
|
export interface LinkProtocolOptions {
|
||||||
scheme: string;
|
scheme: string;
|
||||||
optionalSlashes?: boolean;
|
optionalSlashes?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 interface LinkOptions {
|
export interface LinkOptions {
|
||||||
|
/**
|
||||||
|
* If enabled, it adds links as you type.
|
||||||
|
*/
|
||||||
autolink: boolean;
|
autolink: boolean;
|
||||||
inclusive: boolean;
|
/**
|
||||||
|
* An array of custom protocols to be registered with linkifyjs.
|
||||||
|
*/
|
||||||
protocols: Array<LinkProtocolOptions | string>;
|
protocols: Array<LinkProtocolOptions | string>;
|
||||||
|
/**
|
||||||
|
* If enabled, links will be opened on click.
|
||||||
|
*/
|
||||||
openOnClick: boolean;
|
openOnClick: boolean;
|
||||||
|
/**
|
||||||
|
* If enabled, links will be inclusive i.e. if you move your cursor to the
|
||||||
|
* link text, and start typing, it'll be a part of the link itself.
|
||||||
|
*/
|
||||||
|
inclusive: boolean;
|
||||||
|
/**
|
||||||
|
* Adds a link to the current selection if the pasted content only contains an url.
|
||||||
|
*/
|
||||||
linkOnPaste: boolean;
|
linkOnPaste: boolean;
|
||||||
|
/**
|
||||||
|
* A list of HTML attributes to be rendered.
|
||||||
|
*/
|
||||||
HTMLAttributes: Record<string, any>;
|
HTMLAttributes: Record<string, any>;
|
||||||
|
/**
|
||||||
|
* A validation function that modifies link verification for the auto linker.
|
||||||
|
* @param url - The url to be validated.
|
||||||
|
* @returns - True if the url is valid, false otherwise.
|
||||||
|
*/
|
||||||
validate?: (url: string) => boolean;
|
validate?: (url: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "@tiptap/core" {
|
declare module "@tiptap/core" {
|
||||||
interface Commands<ReturnType> {
|
interface Commands<ReturnType> {
|
||||||
link: {
|
link: {
|
||||||
|
/**
|
||||||
|
* Set a link mark
|
||||||
|
*/
|
||||||
setLink: (attributes: {
|
setLink: (attributes: {
|
||||||
href: string;
|
href: string;
|
||||||
target?: string | null;
|
target?: string | null;
|
||||||
rel?: string | null;
|
rel?: string | null;
|
||||||
class?: string | null;
|
class?: string | null;
|
||||||
}) => ReturnType;
|
}) => ReturnType;
|
||||||
|
/**
|
||||||
|
* Toggle a link mark
|
||||||
|
*/
|
||||||
toggleLink: (attributes: {
|
toggleLink: (attributes: {
|
||||||
href: string;
|
href: string;
|
||||||
target?: string | null;
|
target?: string | null;
|
||||||
rel?: string | null;
|
rel?: string | null;
|
||||||
class?: string | null;
|
class?: string | null;
|
||||||
}) => ReturnType;
|
}) => ReturnType;
|
||||||
|
/**
|
||||||
|
* Unset a link mark
|
||||||
|
*/
|
||||||
unsetLink: () => ReturnType;
|
unsetLink: () => ReturnType;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -150,37 +185,31 @@ export const CustomLinkExtension = Mark.create<LinkOptions>({
|
|||||||
addPasteRules() {
|
addPasteRules() {
|
||||||
return [
|
return [
|
||||||
markPasteRule({
|
markPasteRule({
|
||||||
find: (text) =>
|
find: (text) => {
|
||||||
find(text)
|
const foundLinks: PasteRuleMatch[] = [];
|
||||||
.filter((link) => {
|
|
||||||
if (this.options.validate) {
|
|
||||||
return this.options.validate(link.value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.filter((link) => link.isLink)
|
|
||||||
.map((link) => ({
|
|
||||||
text: link.value,
|
|
||||||
index: link.start,
|
|
||||||
data: link,
|
|
||||||
})),
|
|
||||||
type: this.type,
|
|
||||||
getAttributes: (match, pasteEvent) => {
|
|
||||||
const html = pasteEvent?.clipboardData?.getData("text/html");
|
|
||||||
const hrefRegex = /href="([^"]*)"/;
|
|
||||||
|
|
||||||
const existingLink = html?.match(hrefRegex);
|
if (text) {
|
||||||
|
const links = find(text).filter((item) => item.isLink);
|
||||||
|
|
||||||
if (existingLink) {
|
if (links.length) {
|
||||||
return {
|
links.forEach((link) =>
|
||||||
href: existingLink[1],
|
foundLinks.push({
|
||||||
};
|
text: link.value,
|
||||||
|
data: {
|
||||||
|
href: link.href,
|
||||||
|
},
|
||||||
|
index: link.start,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return foundLinks;
|
||||||
href: match.data?.href,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
type: this.type,
|
||||||
|
getAttributes: (match) => ({
|
||||||
|
href: match.data?.href,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
},
|
},
|
Loading…
Reference in New Issue
Block a user