This commit is contained in:
Vanessa 2022-09-15 11:57:33 +08:00
parent 01af1ca85f
commit 8af738ab06
9 changed files with 195 additions and 114 deletions

View file

@ -203,7 +203,7 @@
transition: var(--b3-transition); transition: var(--b3-transition);
} }
span[data-type="a"] { span[data-type~="a"] {
color: var(--b3-protyle-inline-link-color); color: var(--b3-protyle-inline-link-color);
cursor: pointer; cursor: pointer;
transition: var(--b3-transition); transition: var(--b3-transition);

View file

@ -25,7 +25,7 @@ export const initBlockPopover = () => {
tip += " " + title; tip += " " + title;
} }
} }
if (tip && !tip.startsWith("siyuan://blocks")) { if (tip && !tip.startsWith("siyuan://blocks") && !aElement.classList.contains("b3-tooltips")) {
showTooltip(tip, aElement); showTooltip(tip, aElement);
event.stopPropagation(); event.stopPropagation();
return; return;
@ -141,7 +141,7 @@ export const initBlockPopover = () => {
}); });
ids = postResponse.data; 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)]; ids = [popoverTargetElement.getAttribute("data-href").substr(16, 22)];
} else { } else {

View file

@ -41,6 +41,7 @@ import {hasNextSibling} from "../protyle/wysiwyg/getBlock";
import {electronUndo} from "../protyle/undo"; import {electronUndo} from "../protyle/undo";
import {pushBack} from "../mobile/util/MobileBackFoward"; import {pushBack} from "../mobile/util/MobileBackFoward";
import {exportAsset} from "./util"; import {exportAsset} from "./util";
import {removeLink} from "../protyle/toolbar/Link";
export const refMenu = (protyle: IProtyle, element: HTMLElement) => { export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
const nodeElement = hasClosestBlock(element); const nodeElement = hasClosestBlock(element);
@ -685,7 +686,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) { if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) {
protyle.toolbar.setInlineMark(protyle, "link", "remove"); removeLink(linkElement, protyle.toolbar.range)
} else { } else {
protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.selectNodeContents(linkElement);
protyle.toolbar.range.collapse(false); protyle.toolbar.range.collapse(false);
@ -728,7 +729,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (!inputElement.value) { if (!inputElement.value) {
protyle.toolbar.setInlineMark(protyle, "link", "remove"); removeLink(linkElement, protyle.toolbar.range)
} else { } else {
protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.selectNodeContents(linkElement);
protyle.toolbar.range.collapse(false); protyle.toolbar.range.collapse(false);
@ -768,7 +769,7 @@ export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) { if (linkElement.textContent === "" || linkElement.textContent === Constants.ZWSP) {
protyle.toolbar.setInlineMark(protyle, "link", "remove"); removeLink(linkElement, protyle.toolbar.range)
} else { } else {
protyle.toolbar.range.selectNodeContents(linkElement); protyle.toolbar.range.selectNodeContents(linkElement);
protyle.toolbar.range.collapse(false); 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>`, label: `${window.siyuan.languages.turnInto} <b>${window.siyuan.languages.text}</b>`,
icon: "iconRefresh", icon: "iconRefresh",
click() { click() {
protyle.toolbar.setInlineMark(protyle, "link", "remove"); removeLink(linkElement, protyle.toolbar.range);
} }
}).element); }).element);
if (linkAddress?.startsWith("assets/")) { if (linkAddress?.startsWith("assets/")) {

View file

@ -109,7 +109,7 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
value: "emoji", 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>`, 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", 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>`, 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>`,
}, { }, {

View file

@ -528,7 +528,8 @@ ${unicode2Emoji(emoji.unicode, true)}</button>`;
range.deleteContents(); range.deleteContents();
focusByRange(range); focusByRange(range);
protyle.toolbar.range = range; protyle.toolbar.range = range;
protyle.toolbar.setInlineMark(protyle, value, "add"); // TODO
// protyle.toolbar.setInlineMark(protyle, value, "hint");
return; return;
} else if (value === "emoji") { } else if (value === "emoji") {
range.deleteContents(); range.deleteContents();

View file

@ -1,4 +1,11 @@
import {ToolbarItem} from "./ToolbarItem"; 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 { export class Link extends ToolbarItem {
public element: HTMLElement; public element: HTMLElement;
@ -6,10 +13,82 @@ export class Link extends ToolbarItem {
constructor(protyle: IProtyle, menuItem: IMenuItem) { constructor(protyle: IProtyle, menuItem: IMenuItem) {
super(protyle, menuItem); super(protyle, menuItem);
// 不能用 getEventName否则会导致光标位置变动到点击的文档中 // 不能用 getEventName否则会导致光标位置变动到点击的文档中
this.element.addEventListener("click", (event: MouseEvent & { changedTouches: MouseEvent[] }) => { this.element.addEventListener("click", async (event: MouseEvent & { changedTouches: MouseEvent[] }) => {
protyle.toolbar.setInlineMark(protyle, "link", "add");
protyle.toolbar.element.classList.add("fn__none"); protyle.toolbar.element.classList.add("fn__none");
event.stopPropagation(); 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);
}
}

View file

@ -12,7 +12,7 @@ export class ToolbarItem {
this.element.setAttribute("data-type", menuItem.name); this.element.setAttribute("data-type", menuItem.name);
this.element.setAttribute("aria-label", tip + hotkey); this.element.setAttribute("aria-label", tip + hotkey);
this.element.innerHTML = `<svg><use xlink:href="#${menuItem.icon}"></use></svg>`; 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; return;
} }
this.element.addEventListener(getEventName(), (event) => { this.element.addEventListener(getEventName(), (event) => {

View file

@ -126,7 +126,7 @@ export class Toolbar {
}); });
const types = this.getCurrentType(); const types = this.getCurrentType();
types.forEach(item => { types.forEach(item => {
if (item === "block-ref" || item === "text" || item === "file-annotation-ref") { if (["a", "block-ref", "text", "file-annotation-ref"].includes(item)) {
return; return;
} }
this.element.querySelector(`[data-type="${item}"]`).classList.add("protyle-toolbar__item--current"); 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); const nodeElement = hasClosestBlock(this.range.startContainer);
if (!nodeElement) { if (!nodeElement) {
return; return;
@ -294,7 +294,7 @@ export class Toolbar {
this.mergeNode(contents.childNodes); this.mergeNode(contents.childNodes);
const actionBtn = action === "toolbar" ? this.element.querySelector(`[data-type="${type}"]`) : undefined; const actionBtn = action === "toolbar" ? this.element.querySelector(`[data-type="${type}"]`) : undefined;
const newNodes: Node[] = []; 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"))) { (action === "range" && rangeTypes.length > 0 && rangeTypes.includes(type) && (!textObj || textObj.type === "remove"))) {
// 移除 // 移除
if (actionBtn) { if (actionBtn) {
@ -431,13 +431,13 @@ export class Toolbar {
return; return;
} }
const types = this.getCurrentType(); const types = this.getCurrentType();
if (action === "add" && types.length > 0 && types.includes(type) && !focusAdd) { // if (action === "add" && types.length > 0 && types.includes(type) && !focusAdd) {
if (type === "link") { // if (type === "link") {
this.element.classList.add("fn__none"); // this.element.classList.add("fn__none");
linkMenu(protyle, this.range.startContainer.parentElement); // linkMenu(protyle, this.range.startContainer.parentElement);
} // }
return; // return;
} // }
// 对已有字体样式的文字再次添加字体样式 // 对已有字体样式的文字再次添加字体样式
// if (focusAdd && action === "add" && types.includes("text") && this.range.startContainer.nodeType === 3 && // if (focusAdd && action === "add" && types.includes("text") && this.range.startContainer.nodeType === 3 &&
// this.range.startContainer.parentNode.isSameNode(this.range.endContainer.parentNode)) { // this.range.startContainer.parentNode.isSameNode(this.range.endContainer.parentNode)) {
@ -492,11 +492,11 @@ export class Toolbar {
return; return;
} }
} }
if (types.length > 0 && types.includes("link") && action === "range") { // if (types.length > 0 && types.includes("link") && action === "range") {
// 链接快捷键不应取消,应该显示链接信息 // // 链接快捷键不应取消,应该显示链接信息
linkMenu(protyle, this.range.startContainer.parentElement); // linkMenu(protyle, this.range.startContainer.parentElement);
return; // return;
} // }
const wbrElement = document.createElement("wbr"); const wbrElement = document.createElement("wbr");
this.range.insertNode(wbrElement); this.range.insertNode(wbrElement);
this.range.setStartAfter(wbrElement); this.range.setStartAfter(wbrElement);
@ -540,22 +540,22 @@ export class Toolbar {
this.range.insertNode(textNode); this.range.insertNode(textNode);
this.range.selectNodeContents(textNode); this.range.selectNodeContents(textNode);
} else { } else {
newNodes.forEach((item, index) => { // newNodes.forEach((item, index) => {
this.range.insertNode(item); // this.range.insertNode(item);
if (index !== newNodes.length - 1) { // if (index !== newNodes.length - 1) {
this.range.collapse(false); // this.range.collapse(false);
} else { // } else {
this.range.setEnd(item, item.textContent.length); // this.range.setEnd(item, item.textContent.length);
} // }
}); // });
if (newNodes.length > 0) { // if (newNodes.length > 0) {
this.range.setStart(newNodes[0], 0); // this.range.setStart(newNodes[0], 0);
} // }
} }
focusByRange(this.range); // focusByRange(this.range);
this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => { // this.element.querySelectorAll(".protyle-toolbar__item--current").forEach(item => {
item.classList.remove("protyle-toolbar__item--current"); // item.classList.remove("protyle-toolbar__item--current");
}); // });
} else { } else {
if (newNodes.length === 0) { if (newNodes.length === 0) {
newNodes.push(document.createTextNode(Constants.ZWSP)); newNodes.push(document.createTextNode(Constants.ZWSP));
@ -565,41 +565,41 @@ export class Toolbar {
const refText = startText + selectContents.textContent + endText; const refText = startText + selectContents.textContent + endText;
const refNode = document.createTextNode(refText); const refNode = document.createTextNode(refText);
switch (type) { switch (type) {
case "bold": // case "bold":
newElement = document.createElement("strong"); // newElement = document.createElement("strong");
break; // break;
case "underline": // case "underline":
newElement = document.createElement("u"); // newElement = document.createElement("u");
break; // break;
case "italic": // case "italic":
newElement = document.createElement("em"); // newElement = document.createElement("em");
break; // break;
case "strike": // case "strike":
newElement = document.createElement("s"); // newElement = document.createElement("s");
break; // break;
case "inline-code": // case "inline-code":
newElement = document.createElement("code"); // newElement = document.createElement("code");
break; // break;
case "mark": // case "mark":
newElement = document.createElement("mark"); // newElement = document.createElement("mark");
break; // break;
case "sup": // case "sup":
newElement = document.createElement("sup"); // newElement = document.createElement("sup");
break; // break;
case "sub": // case "sub":
newElement = document.createElement("sub"); // newElement = document.createElement("sub");
break; // break;
case "kbd": // case "kbd":
newElement = document.createElement("kbd"); // newElement = document.createElement("kbd");
break; // break;
case "tag": // case "tag":
newElement = document.createElement("span"); // newElement = document.createElement("span");
newElement.setAttribute("data-type", "tag"); // newElement.setAttribute("data-type", "tag");
break; // break;
case "link": // case "link":
newElement = document.createElement("span"); // newElement = document.createElement("span");
newElement.setAttribute("data-type", "a"); // newElement.setAttribute("data-type", "a");
break; // break;
// case "blockRef": // case "blockRef":
// if (refText === "") { // if (refText === "") {
// wbrElement.remove(); // wbrElement.remove();
@ -619,9 +619,9 @@ export class Toolbar {
mathRender(newElement); mathRender(newElement);
break; break;
} }
if (newElement) { // if (newElement) {
this.range.insertNode(newElement); // this.range.insertNode(newElement);
} // }
if (type === "inline-math") { if (type === "inline-math") {
this.range.setStartAfter(newElement); this.range.setStartAfter(newElement);
this.range.collapse(true); this.range.collapse(true);
@ -653,40 +653,40 @@ export class Toolbar {
this.range.setEnd(newElement.lastChild, newElement.lastChild.textContent.length); this.range.setEnd(newElement.lastChild, newElement.lastChild.textContent.length);
focusByRange(this.range); focusByRange(this.range);
} }
if (type === "link") { // if (type === "link") {
let needShowLink = true; // let needShowLink = true;
let focusText = false; // let focusText = false;
try { // try {
const clipText = await navigator.clipboard.readText(); // const clipText = await navigator.clipboard.readText();
// 选中链接时需忽略剪切板内容 https://ld246.com/article/1643035329737 // // 选中链接时需忽略剪切板内容 https://ld246.com/article/1643035329737
if (protyle.lute.IsValidLinkDest(this.range.toString().trim())) { // if (protyle.lute.IsValidLinkDest(this.range.toString().trim())) {
(newElement as HTMLElement).setAttribute("data-href", this.range.toString().trim()); // (newElement as HTMLElement).setAttribute("data-href", this.range.toString().trim());
needShowLink = false; // needShowLink = false;
} else if (protyle.lute.IsValidLinkDest(clipText)) { // } else if (protyle.lute.IsValidLinkDest(clipText)) {
(newElement as HTMLElement).setAttribute("data-href", clipText); // (newElement as HTMLElement).setAttribute("data-href", clipText);
if (newElement.textContent.replace(Constants.ZWSP, "") !== "") { // if (newElement.textContent.replace(Constants.ZWSP, "") !== "") {
needShowLink = false; // needShowLink = false;
} // }
focusText = true; // focusText = true;
} // }
} catch (e) { // } catch (e) {
console.log(e); // console.log(e);
} // }
if (needShowLink) { // if (needShowLink) {
linkMenu(protyle, newElement as HTMLElement, focusText); // 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 (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")); // nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html); // updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, html);
wbrElement.remove(); // wbrElement.remove();
} }
public showFileAnnotationRef(protyle: IProtyle, refElement: HTMLElement) { public showFileAnnotationRef(protyle: IProtyle, refElement: HTMLElement) {

View file

@ -1185,8 +1185,8 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
} }
if (matchHotKey(menuItem.hotkey, event)) { if (matchHotKey(menuItem.hotkey, event)) {
protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element); protyle.toolbar.range = getEditorRange(protyle.wysiwyg.element);
if (menuItem.name === "text" || menuItem.name === "block-ref") { if (["text", "a", "block-ref"].includes(menuItem.name)) {
protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent(getEventName())); protyle.toolbar.element.querySelector(`[data-type="${menuItem.name}"]`).dispatchEvent(new CustomEvent("block-ref" === menuItem.name ? getEventName() : "click"));
} else { } else {
protyle.toolbar.setInlineMark(protyle, menuItem.name, "range"); protyle.toolbar.setInlineMark(protyle, menuItem.name, "range");
} }