This commit is contained in:
parent
01af1ca85f
commit
8af738ab06
9 changed files with 195 additions and 114 deletions
|
@ -203,7 +203,7 @@
|
|||
transition: var(--b3-transition);
|
||||
}
|
||||
|
||||
span[data-type="a"] {
|
||||
span[data-type~="a"] {
|
||||
color: var(--b3-protyle-inline-link-color);
|
||||
cursor: pointer;
|
||||
transition: var(--b3-transition);
|
||||
|
|
|
@ -25,7 +25,7 @@ export const initBlockPopover = () => {
|
|||
tip += " " + title;
|
||||
}
|
||||
}
|
||||
if (tip && !tip.startsWith("siyuan://blocks")) {
|
||||
if (tip && !tip.startsWith("siyuan://blocks") && !aElement.classList.contains("b3-tooltips")) {
|
||||
showTooltip(tip, aElement);
|
||||
event.stopPropagation();
|
||||
return;
|
||||
|
@ -141,7 +141,7 @@ export const initBlockPopover = () => {
|
|||
});
|
||||
ids = postResponse.data;
|
||||
}
|
||||
} else if (popoverTargetElement.getAttribute("data-type") === "a") {
|
||||
} else if (popoverTargetElement.getAttribute("data-type").split(" ").includes("a")) {
|
||||
// 以思源协议开头的链接
|
||||
ids = [popoverTargetElement.getAttribute("data-href").substr(16, 22)];
|
||||
} else {
|
||||
|
|
|
@ -41,6 +41,7 @@ import {hasNextSibling} from "../protyle/wysiwyg/getBlock";
|
|||
import {electronUndo} from "../protyle/undo";
|
||||
import {pushBack} from "../mobile/util/MobileBackFoward";
|
||||
import {exportAsset} from "./util";
|
||||
import {removeLink} from "../protyle/toolbar/Link";
|
||||
|
||||
export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
|
||||
const nodeElement = hasClosestBlock(element);
|
||||
|
@ -685,7 +686,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) {
|
||||
protyle.toolbar.setInlineMark(protyle, "link", "remove");
|
||||
removeLink(linkElement, protyle.toolbar.range)
|
||||
} else {
|
||||
protyle.toolbar.range.selectNodeContents(linkElement);
|
||||
protyle.toolbar.range.collapse(false);
|
||||
|
@ -728,7 +729,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!inputElement.value) {
|
||||
protyle.toolbar.setInlineMark(protyle, "link", "remove");
|
||||
removeLink(linkElement, protyle.toolbar.range)
|
||||
} else {
|
||||
protyle.toolbar.range.selectNodeContents(linkElement);
|
||||
protyle.toolbar.range.collapse(false);
|
||||
|
@ -768,7 +769,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
|
|||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) {
|
||||
protyle.toolbar.setInlineMark(protyle, "link", "remove");
|
||||
removeLink(linkElement, protyle.toolbar.range)
|
||||
} else {
|
||||
protyle.toolbar.range.selectNodeContents(linkElement);
|
||||
protyle.toolbar.range.collapse(false);
|
||||
|
@ -813,7 +814,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
|
|||
label: `${window.siyuan.languages.turnInto} <b>${window.siyuan.languages.text}</b>`,
|
||||
icon: "iconRefresh",
|
||||
click() {
|
||||
protyle.toolbar.setInlineMark(protyle, "link", "remove");
|
||||
removeLink(linkElement, protyle.toolbar.range);
|
||||
}
|
||||
}).element);
|
||||
if (linkAddress?.startsWith("assets/")) {
|
||||
|
|
|
@ -109,7 +109,7 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
|
|||
value: "emoji",
|
||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconEmoji"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.emoji}</span><span class="b3-menu__accelerator">:</span></div>`,
|
||||
}, {
|
||||
filter: ["链接", "lianjie", "lj", "link"],
|
||||
filter: ["链接", "lianjie", "lj", "link", "a"],
|
||||
value: "a",
|
||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconLink"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.link}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}</span></div>`,
|
||||
}, {
|
||||
|
|
|
@ -528,7 +528,8 @@ ${unicode2Emoji(emoji.unicode, true)}</button>`;
|
|||
range.deleteContents();
|
||||
focusByRange(range);
|
||||
protyle.toolbar.range = range;
|
||||
protyle.toolbar.setInlineMark(protyle, value, "add");
|
||||
// TODO
|
||||
// protyle.toolbar.setInlineMark(protyle, value, "hint");
|
||||
return;
|
||||
} else if (value === "emoji") {
|
||||
range.deleteContents();
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import {ToolbarItem} from "./ToolbarItem";
|
||||
import {linkMenu} from "../../menus/protyle";
|
||||
import {Constants} from "../../constants";
|
||||
import * as dayjs from "dayjs";
|
||||
import {updateTransaction} from "../wysiwyg/transaction";
|
||||
import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
|
||||
import {hasNextSibling, hasPreviousSibling} from "../wysiwyg/getBlock";
|
||||
import {focusByRange, focusByWbr} from "../util/selection";
|
||||
|
||||
export class Link extends ToolbarItem {
|
||||
public element: HTMLElement;
|
||||
|
@ -6,10 +13,82 @@ export class Link extends ToolbarItem {
|
|||
constructor(protyle: IProtyle, menuItem: IMenuItem) {
|
||||
super(protyle, menuItem);
|
||||
// 不能用 getEventName,否则会导致光标位置变动到点击的文档中
|
||||
this.element.addEventListener("click", (event: MouseEvent & { changedTouches: MouseEvent[] }) => {
|
||||
protyle.toolbar.setInlineMark(protyle, "link", "add");
|
||||
this.element.addEventListener("click", async (event: MouseEvent & { changedTouches: MouseEvent[] }) => {
|
||||
protyle.toolbar.element.classList.add("fn__none");
|
||||
event.stopPropagation();
|
||||
|
||||
const range = protyle.toolbar.range
|
||||
const nodeElement = hasClosestBlock(range.startContainer);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
}
|
||||
const aElement = hasClosestByAttribute(range.startContainer, "data-type", "a")
|
||||
if (aElement) {
|
||||
linkMenu(protyle, aElement);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!["DIV", "TD", "TH"].includes(range.startContainer.parentElement.tagName) && range.startOffset === 0 && !hasPreviousSibling(range.startContainer)) {
|
||||
range.setStartBefore(range.startContainer.parentElement);
|
||||
}
|
||||
if (!["DIV", "TD", "TH"].includes(range.endContainer.parentElement.tagName) && range.endOffset === range.endContainer.textContent.length && !hasNextSibling(range.endContainer)) {
|
||||
range.setEndAfter(range.endContainer.parentElement);
|
||||
}
|
||||
const wbrElement = document.createElement("wbr");
|
||||
range.insertNode(wbrElement);
|
||||
const html = nodeElement.outerHTML;
|
||||
|
||||
const newElement = document.createElement("span")
|
||||
newElement.setAttribute("data-type", "a")
|
||||
const rangeString = range.toString()
|
||||
newElement.textContent = rangeString;
|
||||
range.extractContents();
|
||||
range.insertNode(newElement);
|
||||
let needShowLink = true;
|
||||
let focusText = false;
|
||||
try {
|
||||
const clipText = await navigator.clipboard.readText();
|
||||
// 选中链接时需忽略剪切板内容 https://ld246.com/article/1643035329737
|
||||
if (protyle.lute.IsValidLinkDest(rangeString.trim())) {
|
||||
(newElement as HTMLElement).setAttribute("data-href", rangeString.trim());
|
||||
needShowLink = false;
|
||||
} else if (protyle.lute.IsValidLinkDest(clipText)) {
|
||||
(newElement as HTMLElement).setAttribute("data-href", clipText);
|
||||
if (newElement.textContent.replace(Constants.ZWSP, "") !== "") {
|
||||
needShowLink = false;
|
||||
}
|
||||
focusText = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (needShowLink) {
|
||||
linkMenu(protyle, newElement as HTMLElement, focusText);
|
||||
}
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html);
|
||||
wbrElement.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const removeLink = (linkElement: HTMLElement, range: Range) => {
|
||||
const types = linkElement.getAttribute("data-type").split(" ")
|
||||
if (types.length === 1) {
|
||||
const linkParentElement = linkElement.parentElement
|
||||
linkElement.outerHTML = linkElement.innerHTML + "<wbr>";
|
||||
focusByWbr(linkParentElement, range);
|
||||
} else {
|
||||
types.find((itemType, index) => {
|
||||
if ("a" === itemType) {
|
||||
types.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
linkElement.setAttribute("data-type", types.join(" "));
|
||||
linkElement.removeAttribute("data-href")
|
||||
range.selectNodeContents(linkElement);
|
||||
range.collapse(false);
|
||||
focusByRange(range);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export class ToolbarItem {
|
|||
this.element.setAttribute("data-type", menuItem.name);
|
||||
this.element.setAttribute("aria-label", tip + hotkey);
|
||||
this.element.innerHTML = `<svg><use xlink:href="#${menuItem.icon}"></use></svg>`;
|
||||
if (menuItem.name === "text" || menuItem.name === "block-ref" || menuItem.name === "a") {
|
||||
if (["text", "a", "block-ref"].includes(menuItem.name)) {
|
||||
return;
|
||||
}
|
||||
this.element.addEventListener(getEventName(), (event) => {
|
||||
|
|
|
@ -126,7 +126,7 @@ export class Toolbar {
|
|||
});
|
||||
const types = this.getCurrentType();
|
||||
types.forEach(item => {
|
||||
if (item === "block-ref" || item === "text" || item === "file-annotation-ref") {
|
||||
if (["a", "block-ref", "text", "file-annotation-ref"].includes(item)) {
|
||||
return;
|
||||
}
|
||||
this.element.querySelector(`[data-type="${item}"]`).classList.add("protyle-toolbar__item--current");
|
||||
|
@ -253,7 +253,7 @@ export class Toolbar {
|
|||
}
|
||||
}
|
||||
|
||||
public setInlineMark(protyle: IProtyle, type: string, action: "remove" | "add" | "range" | "toolbar", textObj?: ITextOption) {
|
||||
public setInlineMark(protyle: IProtyle, type: string, action: "range" | "toolbar", textObj?: ITextOption) {
|
||||
const nodeElement = hasClosestBlock(this.range.startContainer);
|
||||
if (!nodeElement) {
|
||||
return;
|
||||
|
@ -294,7 +294,7 @@ export class Toolbar {
|
|||
this.mergeNode(contents.childNodes);
|
||||
const actionBtn = action === "toolbar" ? this.element.querySelector(`[data-type="${type}"]`) : undefined;
|
||||
const newNodes: Node[] = [];
|
||||
if (action === "remove" || actionBtn?.classList.contains("protyle-toolbar__item--current") ||
|
||||
if (actionBtn?.classList.contains("protyle-toolbar__item--current") ||
|
||||
(action === "range" && rangeTypes.length > 0 && rangeTypes.includes(type) && (!textObj || textObj.type === "remove"))) {
|
||||
// 移除
|
||||
if (actionBtn) {
|
||||
|
@ -431,13 +431,13 @@ export class Toolbar {
|
|||
return;
|
||||
}
|
||||
const types = this.getCurrentType();
|
||||
if (action === "add" && types.length > 0 && types.includes(type) && !focusAdd) {
|
||||
if (type === "link") {
|
||||
this.element.classList.add("fn__none");
|
||||
linkMenu(protyle, this.range.startContainer.parentElement);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// if (action === "add" && types.length > 0 && types.includes(type) && !focusAdd) {
|
||||
// if (type === "link") {
|
||||
// this.element.classList.add("fn__none");
|
||||
// linkMenu(protyle, this.range.startContainer.parentElement);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// 对已有字体样式的文字再次添加字体样式
|
||||
// if (focusAdd && action === "add" && types.includes("text") && this.range.startContainer.nodeType === 3 &&
|
||||
// this.range.startContainer.parentNode.isSameNode(this.range.endContainer.parentNode)) {
|
||||
|
@ -492,11 +492,11 @@ export class Toolbar {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (types.length > 0 && types.includes("link") && action === "range") {
|
||||
// 链接快捷键不应取消,应该显示链接信息
|
||||
linkMenu(protyle, this.range.startContainer.parentElement);
|
||||
return;
|
||||
}
|
||||
// if (types.length > 0 && types.includes("link") && action === "range") {
|
||||
// // 链接快捷键不应取消,应该显示链接信息
|
||||
// linkMenu(protyle, this.range.startContainer.parentElement);
|
||||
// return;
|
||||
// }
|
||||
const wbrElement = document.createElement("wbr");
|
||||
this.range.insertNode(wbrElement);
|
||||
this.range.setStartAfter(wbrElement);
|
||||
|
@ -540,22 +540,22 @@ export class Toolbar {
|
|||
this.range.insertNode(textNode);
|
||||
this.range.selectNodeContents(textNode);
|
||||
} else {
|
||||
newNodes.forEach((item, index) => {
|
||||
this.range.insertNode(item);
|
||||
if (index !== newNodes.length - 1) {
|
||||
this.range.collapse(false);
|
||||
} else {
|
||||
this.range.setEnd(item, item.textContent.length);
|
||||
}
|
||||
});
|
||||
if (newNodes.length > 0) {
|
||||
this.range.setStart(newNodes[0], 0);
|
||||
}
|
||||
// newNodes.forEach((item, index) => {
|
||||
// this.range.insertNode(item);
|
||||
// if (index !== newNodes.length - 1) {
|
||||
// this.range.collapse(false);
|
||||
// } else {
|
||||
// this.range.setEnd(item, item.textContent.length);
|
||||
// }
|
||||
// });
|
||||
// if (newNodes.length > 0) {
|
||||
// this.range.setStart(newNodes[0], 0);
|
||||
// }
|
||||
}
|
||||
focusByRange(this.range);
|
||||
this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
|
||||
item.classList.remove("protyle-toolbar__item--current");
|
||||
});
|
||||
// focusByRange(this.range);
|
||||
// this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
|
||||
// item.classList.remove("protyle-toolbar__item--current");
|
||||
// });
|
||||
} else {
|
||||
if (newNodes.length === 0) {
|
||||
newNodes.push(document.createTextNode(Constants.ZWSP));
|
||||
|
@ -565,41 +565,41 @@ export class Toolbar {
|
|||
const refText = startText + selectContents.textContent + endText;
|
||||
const refNode = document.createTextNode(refText);
|
||||
switch (type) {
|
||||
case "bold":
|
||||
newElement = document.createElement("strong");
|
||||
break;
|
||||
case "underline":
|
||||
newElement = document.createElement("u");
|
||||
break;
|
||||
case "italic":
|
||||
newElement = document.createElement("em");
|
||||
break;
|
||||
case "strike":
|
||||
newElement = document.createElement("s");
|
||||
break;
|
||||
case "inline-code":
|
||||
newElement = document.createElement("code");
|
||||
break;
|
||||
case "mark":
|
||||
newElement = document.createElement("mark");
|
||||
break;
|
||||
case "sup":
|
||||
newElement = document.createElement("sup");
|
||||
break;
|
||||
case "sub":
|
||||
newElement = document.createElement("sub");
|
||||
break;
|
||||
case "kbd":
|
||||
newElement = document.createElement("kbd");
|
||||
break;
|
||||
case "tag":
|
||||
newElement = document.createElement("span");
|
||||
newElement.setAttribute("data-type", "tag");
|
||||
break;
|
||||
case "link":
|
||||
newElement = document.createElement("span");
|
||||
newElement.setAttribute("data-type", "a");
|
||||
break;
|
||||
// case "bold":
|
||||
// newElement = document.createElement("strong");
|
||||
// break;
|
||||
// case "underline":
|
||||
// newElement = document.createElement("u");
|
||||
// break;
|
||||
// case "italic":
|
||||
// newElement = document.createElement("em");
|
||||
// break;
|
||||
// case "strike":
|
||||
// newElement = document.createElement("s");
|
||||
// break;
|
||||
// case "inline-code":
|
||||
// newElement = document.createElement("code");
|
||||
// break;
|
||||
// case "mark":
|
||||
// newElement = document.createElement("mark");
|
||||
// break;
|
||||
// case "sup":
|
||||
// newElement = document.createElement("sup");
|
||||
// break;
|
||||
// case "sub":
|
||||
// newElement = document.createElement("sub");
|
||||
// break;
|
||||
// case "kbd":
|
||||
// newElement = document.createElement("kbd");
|
||||
// break;
|
||||
// case "tag":
|
||||
// newElement = document.createElement("span");
|
||||
// newElement.setAttribute("data-type", "tag");
|
||||
// break;
|
||||
// case "link":
|
||||
// newElement = document.createElement("span");
|
||||
// newElement.setAttribute("data-type", "a");
|
||||
// break;
|
||||
// case "blockRef":
|
||||
// if (refText === "") {
|
||||
// wbrElement.remove();
|
||||
|
@ -619,9 +619,9 @@ export class Toolbar {
|
|||
mathRender(newElement);
|
||||
break;
|
||||
}
|
||||
if (newElement) {
|
||||
this.range.insertNode(newElement);
|
||||
}
|
||||
// if (newElement) {
|
||||
// this.range.insertNode(newElement);
|
||||
// }
|
||||
if (type === "inline-math") {
|
||||
this.range.setStartAfter(newElement);
|
||||
this.range.collapse(true);
|
||||
|
@ -653,40 +653,40 @@ export class Toolbar {
|
|||
this.range.setEnd(newElement.lastChild, newElement.lastChild.textContent.length);
|
||||
focusByRange(this.range);
|
||||
}
|
||||
if (type === "link") {
|
||||
let needShowLink = true;
|
||||
let focusText = false;
|
||||
try {
|
||||
const clipText = await navigator.clipboard.readText();
|
||||
// 选中链接时需忽略剪切板内容 https://ld246.com/article/1643035329737
|
||||
if (protyle.lute.IsValidLinkDest(this.range.toString().trim())) {
|
||||
(newElement as HTMLElement).setAttribute("data-href", this.range.toString().trim());
|
||||
needShowLink = false;
|
||||
} else if (protyle.lute.IsValidLinkDest(clipText)) {
|
||||
(newElement as HTMLElement).setAttribute("data-href", clipText);
|
||||
if (newElement.textContent.replace(Constants.ZWSP, "") !== "") {
|
||||
needShowLink = false;
|
||||
}
|
||||
focusText = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (needShowLink) {
|
||||
linkMenu(protyle, newElement as HTMLElement, focusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionBtn) {
|
||||
this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
|
||||
item.classList.remove("protyle-toolbar__item--current");
|
||||
});
|
||||
actionBtn.classList.add("protyle-toolbar__item--current");
|
||||
// if (type === "link") {
|
||||
// let needShowLink = true;
|
||||
// let focusText = false;
|
||||
// try {
|
||||
// const clipText = await navigator.clipboard.readText();
|
||||
// // 选中链接时需忽略剪切板内容 https://ld246.com/article/1643035329737
|
||||
// if (protyle.lute.IsValidLinkDest(this.range.toString().trim())) {
|
||||
// (newElement as HTMLElement).setAttribute("data-href", this.range.toString().trim());
|
||||
// needShowLink = false;
|
||||
// } else if (protyle.lute.IsValidLinkDest(clipText)) {
|
||||
// (newElement as HTMLElement).setAttribute("data-href", clipText);
|
||||
// if (newElement.textContent.replace(Constants.ZWSP, "") !== "") {
|
||||
// needShowLink = false;
|
||||
// }
|
||||
// focusText = true;
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
// if (needShowLink) {
|
||||
// linkMenu(protyle, newElement as HTMLElement, focusText);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
// if (actionBtn) {
|
||||
// this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
|
||||
// item.classList.remove("protyle-toolbar__item--current");
|
||||
// });
|
||||
// actionBtn.classList.add("protyle-toolbar__item--current");
|
||||
// }
|
||||
}
|
||||
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html);
|
||||
wbrElement.remove();
|
||||
// nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
|
||||
// updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html);
|
||||
// wbrElement.remove();
|
||||
}
|
||||
|
||||
public showFileAnnotationRef(protyle: IProtyle, refElement: HTMLElement) {
|
||||
|
|
|
@ -1185,8 +1185,8 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
|
|||
}
|
||||
if (matchHotKey(menuItem.hotkey, event)) {
|
||||
protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element);
|
||||
if (menuItem.name === "text" || menuItem.name === "block-ref") {
|
||||
protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent(getEventName()));
|
||||
if (["text", "a", "block-ref"].includes(menuItem.name)) {
|
||||
protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent("block-ref" === menuItem.name ? getEventName() : "click"));
|
||||
} else {
|
||||
protyle.toolbar.setInlineMark(protyle, menuItem.name, "range");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue