Bläddra i källkod

:art: fix https://github.com/siyuan-note/siyuan/issues/4998

Vanessa 3 år sedan
förälder
incheckning
c5294371c9

+ 128 - 0
app/src/menus/protyle.ts

@@ -32,6 +32,7 @@ import {removeFoldHeading} from "../protyle/util/heading";
 import {lineNumberRender} from "../protyle/markdown/highlightRender";
 import * as dayjs from "dayjs";
 import {blockRender} from "../protyle/markdown/blockRender";
+import {isLocalPath} from "../util/pathName";
 
 export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
     const nodeElement = hasClosestBlock(element);
@@ -535,6 +536,133 @@ export const imgMenu = (protyle: IProtyle, range: Range, assetElement: HTMLEleme
     window.siyuan.menus.menu.element.querySelector("input").focus();
 };
 
+export const linkMenu = (protyle: IProtyle, linkElement: HTMLElement, focusText = false) => {
+    window.siyuan.menus.menu.remove();
+    protyle.toolbar.isNewEmptyInline = false;
+    const nodeElement = hasClosestBlock(linkElement);
+    if (!nodeElement) {
+        return;
+    }
+    const id = nodeElement.getAttribute("data-node-id");
+    let html = nodeElement.outerHTML;
+    const linkAddress = linkElement.getAttribute("data-href");
+    window.siyuan.menus.menu.append(new MenuItem({
+        label: `<div class="fn__hr--small"></div><input class="b3-text-field fn__size200" placeholder="${window.siyuan.languages.link}"><div class="fn__hr--small"></div>`,
+        bind(element) {
+            const inputElement = element.querySelector("input");
+            inputElement.value = linkAddress || "";
+            inputElement.addEventListener("change", () => {
+                linkElement.setAttribute("data-href", inputElement.value);
+                updateTransaction(protyle, id, nodeElement.outerHTML, html);
+                html = nodeElement.outerHTML;
+            });
+            inputElement.addEventListener("keydown", (event) => {
+                if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) {
+                    event.preventDefault();
+                    event.stopPropagation();
+                    protyle.toolbar.range.selectNodeContents(linkElement);
+                    protyle.toolbar.range.collapse(false);
+                    focusByRange(protyle.toolbar.range);
+                    window.siyuan.menus.menu.remove();
+                }
+            });
+        }
+    }).element);
+    window.siyuan.menus.menu.append(new MenuItem({
+        label: `<div class="fn__hr--small"></div><input class="b3-text-field fn__size200" placeholder="${window.siyuan.languages.anchor}"><div class="fn__hr--small"></div>`,
+        bind(element) {
+            const inputElement = element.querySelector("input");
+            inputElement.value = linkElement.textContent.replace(Constants.ZWSP, "");
+            inputElement.addEventListener("change", () => {
+                if (!inputElement.value) {
+                    protyle.toolbar.setInlineMark(protyle, "link", "remove");
+                    window.siyuan.menus.menu.remove();
+                }
+                updateTransaction(protyle, id, nodeElement.outerHTML, html);
+                html = nodeElement.outerHTML;
+            });
+            inputElement.addEventListener("compositionend", () => {
+                linkElement.innerHTML = Lute.EscapeHTMLStr(inputElement.value) || "";
+            });
+            inputElement.addEventListener("input", (event: KeyboardEvent) => {
+                if (!event.isComposing) {
+                    linkElement.innerHTML = Lute.EscapeHTMLStr(inputElement.value) || "";
+                }
+            });
+            inputElement.addEventListener("keydown", (event) => {
+                if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) {
+                    event.preventDefault();
+                    event.stopPropagation();
+                    protyle.toolbar.range.selectNodeContents(linkElement);
+                    protyle.toolbar.range.collapse(false);
+                    focusByRange(protyle.toolbar.range);
+                    window.siyuan.menus.menu.remove();
+                }
+            });
+        }
+    }).element);
+    window.siyuan.menus.menu.append(new MenuItem({
+        label: `<div class="fn__hr--small"></div><input class="b3-text-field fn__size200" placeholder="${window.siyuan.languages.title}"><div class="fn__hr--small"></div>`,
+        bind(element) {
+            const inputElement = element.querySelector("input");
+            inputElement.value = Lute.UnEscapeHTMLStr(linkElement.getAttribute("data-title") || "");
+            inputElement.addEventListener("change", () => {
+                if (inputElement.value) {
+                    linkElement.setAttribute("data-title", Lute.EscapeHTMLStr(inputElement.value));
+                } else {
+                    linkElement.removeAttribute("data-title");
+                }
+                updateTransaction(protyle, id, nodeElement.outerHTML, html);
+                html = nodeElement.outerHTML;
+            });
+            inputElement.addEventListener("keydown", (event) => {
+                if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) {
+                    event.preventDefault();
+                    event.stopPropagation();
+                    protyle.toolbar.range.selectNodeContents(linkElement);
+                    protyle.toolbar.range.collapse(false);
+                    focusByRange(protyle.toolbar.range);
+                    window.siyuan.menus.menu.remove();
+                }
+            });
+        }
+    }).element);
+    window.siyuan.menus.menu.append(new MenuItem({
+        icon: "iconTrashcan",
+        label: window.siyuan.languages.remove,
+        click() {
+            protyle.toolbar.setInlineMark(protyle, "link", "remove");
+        }
+    }).element);
+    if (linkAddress?.startsWith("siyuan://blocks/")) {
+        window.siyuan.menus.menu.append(new MenuItem({
+            icon: "iconRefresh",
+            label: window.siyuan.languages.turnInto + " " + window.siyuan.languages.blockRef,
+            click() {
+                linkElement.setAttribute("data-subtype", "s");
+                linkElement.setAttribute("data-type", "block-ref");
+                linkElement.setAttribute("data-id", linkAddress?.replace("siyuan://blocks/", ""));
+                linkElement.removeAttribute("data-href");
+                linkElement.removeAttribute("data-title");
+                nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
+                updateTransaction(protyle, id, nodeElement.outerHTML, html);
+                protyle.toolbar.range.selectNode(linkElement);
+                protyle.toolbar.range.collapse(false);
+                focusByRange(protyle.toolbar.range);
+            }
+        }).element);
+    }
+    if (isLocalPath(linkAddress)) {
+        openMenu(linkAddress);
+    }
+    window.siyuan.menus.menu.element.classList.remove("fn__none");
+    if (focusText || protyle.lute.IsValidLinkDest(linkAddress)) {
+        window.siyuan.menus.menu.element.querySelectorAll("input")[1].select();
+    } else {
+        window.siyuan.menus.menu.element.querySelector("input").select();
+    }
+};
+
 const genImageWidthMenu = (label: string, assetElement: HTMLElement, imgElement: HTMLElement, protyle: IProtyle, id: string, nodeElement: HTMLElement, html: string) => {
     return {
         label,

+ 11 - 158
app/src/protyle/toolbar/index.ts

@@ -39,6 +39,7 @@ import {matchHotKey} from "../util/hotKey";
 import {unicode2Emoji} from "../../emoji";
 import {escapeHtml} from "../../util/escape";
 import {hideElements} from "../ui/hideElements";
+import {linkMenu} from "../../menus/protyle";
 
 export class Toolbar {
     public element: HTMLElement;
@@ -252,7 +253,10 @@ export class Toolbar {
         const types = this.getCurrentType();
         if (action === "add" && types.length > 0 && types.includes(type) && !focusAdd) {
             if (type === "link") {
-                this.showLink(protyle, this.range.startContainer.parentElement);
+                this.element.classList.add("fn__none");
+                linkMenu(protyle, this.range.startContainer.parentElement);
+                const rect = this.range.startContainer.parentElement.getBoundingClientRect();
+                setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
             }
             return;
         }
@@ -312,7 +316,9 @@ export class Toolbar {
         }
         if (types.length > 0 && types.includes("link") && action === "range") {
             // 链接快捷键不应取消,应该显示链接信息
-            this.showLink(protyle, this.range.startContainer.parentElement);
+            linkMenu(protyle, this.range.startContainer.parentElement);
+            const rect = this.range.startContainer.parentElement.getBoundingClientRect();
+            setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
             return;
         }
         const wbrElement = document.createElement("wbr");
@@ -491,7 +497,9 @@ export class Toolbar {
                         console.log(e);
                     }
                     if (needShowLink) {
-                        this.showLink(protyle, newElement as HTMLElement, focusText);
+                        linkMenu(protyle, newElement as HTMLElement, focusText);
+                        const rect = newElement.getBoundingClientRect();
+                        setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
                     }
                 }
             }
@@ -507,161 +515,6 @@ export class Toolbar {
         wbrElement.remove();
     }
 
-    public showLink(protyle: IProtyle, linkElement: HTMLElement, focusText = false) {
-        const nodeElement = hasClosestBlock(linkElement);
-        if (!nodeElement) {
-            return;
-        }
-        const id = nodeElement.getAttribute("data-node-id");
-        this.subElement.style.width = isMobile() ? "80vw" : Math.min(480, window.innerWidth) + "px";
-        this.subElement.style.padding = "";
-        this.subElement.innerHTML = `<div class="b3-form__space--small">
-<label class="fn__flex">
-    <span class="ft__on-surface fn__flex-center" style="width: 72px">${window.siyuan.languages.link}</span>
-    <div class="fn__space"></div>
-    <input class="b3-text-field fn__block" placeholder="${window.siyuan.languages.link}" />
-</label>
-<div class="fn__hr"></div>
-<label class="fn__flex">
-    <span class="ft__on-surface fn__flex-center" style="width: 72px">${window.siyuan.languages.anchor}</span>
-    <div class="fn__space"></div>
-    <input class="b3-text-field fn__block" placeholder="${window.siyuan.languages.anchor}" />
-</label>
-<div class="fn__hr"></div>
-<label class="fn__flex">
-    <span class="ft__on-surface fn__flex-center" style="width: 72px">${window.siyuan.languages.title}</span>
-    <div class="fn__space"></div>
-    <input class="b3-text-field fn__block" placeholder="${window.siyuan.languages.title}" />
-</label>
-<div class="fn__hr"></div>
-<div class="fn__hr"></div>
-<div class="fn__flex"><span class="fn__flex-1"></span>
-    <button class="b3-button b3-button--outline${linkElement.getAttribute("data-href")?.startsWith("siyuan://blocks/") ? "" : " fn__none"}">${window.siyuan.languages.turnInto} ${window.siyuan.languages.blockRef}</button>
-    <span class="fn__space"></span>
-    <button class="b3-button b3-button--cancel">${window.siyuan.languages.remove}</button>
-</div></div>`;
-        let preventChange = false;
-        let oldHTML = nodeElement.outerHTML;
-        this.subElement.querySelector(".b3-button--outline").addEventListener(getEventName(), (event) => {
-            preventChange = true;
-            linkElement.setAttribute("data-subtype", "s");
-            linkElement.setAttribute("data-type", "block-ref");
-            linkElement.setAttribute("data-id", linkElement.getAttribute("data-href")?.replace("siyuan://blocks/", ""));
-            linkElement.removeAttribute("data-href");
-            linkElement.removeAttribute("data-title");
-            nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
-            updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML);
-            protyle.toolbar.range.selectNode(linkElement);
-            protyle.toolbar.range.collapse(false);
-            focusByRange(protyle.toolbar.range);
-            this.subElement.classList.add("fn__none");
-            event.stopPropagation();
-        });
-        this.subElement.querySelector(".b3-button--cancel").addEventListener(getEventName(), (event) => {
-            preventChange = true;
-            this.setInlineMark(protyle, "link", "remove");
-            this.subElement.classList.add("fn__none");
-            event.stopPropagation();
-        });
-        const titleElements = this.subElement.querySelectorAll(".b3-text-field") as NodeListOf<HTMLInputElement>;
-        titleElements[0].value = linkElement.getAttribute("data-href") || "";
-        titleElements[1].value = linkElement.textContent.replace(Constants.ZWSP, "");
-        titleElements[2].value = Lute.UnEscapeHTMLStr(linkElement.getAttribute("data-title") || "");
-        const updateChange = (event: KeyboardEvent) => {
-            this.isNewEmptyInline = false;
-            event.stopPropagation();
-            if (event.isComposing) {
-                return;
-            }
-            window.setTimeout(() => {
-                if (preventChange) {
-                    return;
-                }
-                if (titleElements[1].value === "" || titleElements[0].value === "") {
-                    if (this.subElement.classList.contains("fn__none")) {
-                        this.setInlineMark(protyle, "link", "remove");
-                    }
-                    return;
-                }
-                linkElement.innerHTML = Lute.EscapeHTMLStr(titleElements[1].value);
-                if (titleElements[2].value) {
-                    linkElement.setAttribute("data-title", Lute.EscapeHTMLStr(titleElements[2].value));
-                } else {
-                    linkElement.removeAttribute("data-title");
-                }
-                linkElement.setAttribute("data-href", titleElements[0].value);
-                if (nodeElement.outerHTML !== oldHTML) {
-                    nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
-                    updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML);
-                    oldHTML = nodeElement.outerHTML;
-                }
-                if (this.subElement.classList.contains("fn__none")) {
-                    this.range.setEnd(linkElement.lastChild, linkElement.lastChild.textContent.length);
-                    this.range.collapse(false);
-                    focusByRange(this.range);
-                }
-            }, Constants.TIMEOUT_SEARCH);
-        };
-        const hideSubElement = (event: KeyboardEvent) => {
-            if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) {
-                event.stopPropagation();
-                event.preventDefault();
-                this.subElement.classList.add("fn__none");
-            }
-        };
-        titleElements[0].addEventListener("keydown", (event: KeyboardEvent) => {
-            hideSubElement(event);
-        });
-        titleElements[1].addEventListener("keydown", (event: KeyboardEvent) => {
-            hideSubElement(event);
-        });
-        titleElements[2].addEventListener("keydown", (event: KeyboardEvent) => {
-            hideSubElement(event);
-        });
-        titleElements[0].addEventListener("blur", (event: KeyboardEvent) => {
-            updateChange(event);
-        });
-        titleElements[1].addEventListener("blur", (event: KeyboardEvent) => {
-            updateChange(event);
-        });
-        titleElements[1].addEventListener("compositionend", () => {
-            linkElement.innerHTML = Lute.EscapeHTMLStr(titleElements[1].value) || "";
-        });
-        titleElements[1].addEventListener("input", (event: KeyboardEvent) => {
-            if (!event.isComposing) {
-                linkElement.innerHTML = Lute.EscapeHTMLStr(titleElements[1].value) || "";
-            }
-        });
-        titleElements[2].addEventListener("blur", (event: KeyboardEvent) => {
-            updateChange(event);
-        });
-        titleElements[2].addEventListener("compositionend", () => {
-            if (titleElements[2].value) {
-                linkElement.setAttribute("data-title", Lute.EscapeHTMLStr(titleElements[2].value) || "");
-            } else {
-                linkElement.removeAttribute("data-title");
-            }
-        });
-        titleElements[2].addEventListener("input", (event: KeyboardEvent) => {
-            if (!event.isComposing) {
-                if (titleElements[2].value) {
-                    linkElement.setAttribute("data-title", Lute.EscapeHTMLStr(titleElements[2].value) || "");
-                } else {
-                    linkElement.removeAttribute("data-title");
-                }
-            }
-        });
-        this.subElement.classList.remove("fn__none");
-        const nodeRect = linkElement.getBoundingClientRect();
-        setPosition(this.subElement, nodeRect.left, nodeRect.bottom, nodeRect.height + 4);
-        this.element.classList.add("fn__none");
-        if (focusText || protyle.lute.IsValidLinkDest(titleElements[0].value)) {
-            titleElements[1].select();
-        } else {
-            titleElements[0].select();
-        }
-    }
-
     public showFileAnnotationRef(protyle: IProtyle, refElement: HTMLElement) {
         const nodeElement = hasClosestBlock(refElement);
         if (!nodeElement) {

+ 7 - 4
app/src/protyle/wysiwyg/index.ts

@@ -20,7 +20,7 @@ import {isLocalPath, pathPosix} from "../../util/pathName";
 import {genEmptyElement} from "../../block/util";
 import {previewImage} from "../preview/image";
 import {openGlobalSearch} from "../../search/util";
-import {contentMenu, imgMenu, refMenu, setFold, zoomOut} from "../../menus/protyle";
+import {contentMenu, imgMenu, linkMenu, refMenu, setFold, zoomOut} from "../../menus/protyle";
 import * as dayjs from "dayjs";
 import {dropEvent} from "../util/editorCommonEvent";
 import {input} from "./input";
@@ -428,7 +428,8 @@ export class WYSIWYG {
             const type = target.getAttribute("data-type");
             if (type === "block-ref") {
                 refMenu(protyle, target);
-                setPosition(window.siyuan.menus.menu.element, event.clientX, event.clientY + 13, 26);
+                const rect =target.getBoundingClientRect();
+                setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
                 // 阻止 popover
                 target.removeAttribute("data-type");
                 setTimeout(() => {
@@ -441,8 +442,10 @@ export class WYSIWYG {
                 return false;
             }
             if (type === "a") {
-                protyle.toolbar.showLink(protyle, target);
-                if (target.getAttribute("data-href").startsWith("siyuan://blocks")) {
+                linkMenu(protyle, target);
+                const rect =target.getBoundingClientRect();
+                setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
+                if (target.getAttribute("data-href")?.startsWith("siyuan://blocks")) {
                     // 阻止 popover
                     target.setAttribute("prevent-popover", "true");
                     setTimeout(() => {

+ 4 - 2
app/src/protyle/wysiwyg/keydown.ts

@@ -34,7 +34,7 @@ import {isLocalPath} from "../../util/pathName";
 import {clipboard} from "electron";
 import {getCurrentWindow} from "@electron/remote";
 /// #endif
-import {refMenu, setFold, zoomOut} from "../../menus/protyle";
+import {linkMenu, refMenu, setFold, zoomOut} from "../../menus/protyle";
 import {setPosition} from "../../util/setPosition";
 import {removeEmbed} from "./removeEmbed";
 import {openAttr} from "../../menus/commonMenuItem";
@@ -477,7 +477,9 @@ export const keydown = (protyle: IProtyle, editorElement: HTMLElement) => {
                     protyle.toolbar.showFileAnnotationRef(protyle, inlineElement);
                     return;
                 } else if (type === "a") {
-                    protyle.toolbar.showLink(protyle, inlineElement);
+                    linkMenu(protyle, inlineElement);
+                    const rect = inlineElement.getBoundingClientRect();
+                    setPosition(window.siyuan.menus.menu.element, rect.left, rect.top + 13, 26);
                     return;
                 }
             }

+ 4 - 1
app/src/util/pathName.ts

@@ -29,6 +29,9 @@ export const getDisplayName = (filePath: string, basename = true, removeSY = fal
 };
 
 export const isLocalPath = (link: string) => {
+    if (!link) {
+        return false;
+    }
     return link.startsWith("assets/") || link.startsWith("file://");
 };
 
@@ -93,7 +96,7 @@ export const movePathTo = async (notebookId: string, path: string, focus = true)
             k: inputElement.value
         }, (data) => {
             let fileHTML = "";
-            data.data.forEach((item: { boxIcon:string, box: string, hPath: string, path: string }) => {
+            data.data.forEach((item: { boxIcon: string, box: string, hPath: string, path: string }) => {
                 if (item.path === pathPosix().dirname(path) + "/" || item.path === path) {
                     return;
                 }