diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index 5d4fc3888..ab72bee96 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -48,7 +48,7 @@ import {pushBack} from "../mobile/util/MobileBackFoward"; import {copyPNGByLink, exportAsset, writeAssetToClipboard} from "./util"; import {removeInlineType} from "../protyle/toolbar/util"; import {alignImgCenter, alignImgLeft} from "../protyle/wysiwyg/commonHotkey"; -import {checkFold, renameTag} from "../util/noRelyPCFunction"; +import {checkFold, genTagList, renameTag} from "../util/noRelyPCFunction"; import {hideElements} from "../protyle/ui/hideElements"; import {emitOpenMenu} from "../plugin/EventBus"; import {openMobileFileById} from "../mobile/editor"; @@ -65,6 +65,7 @@ import {hideTooltip} from "../dialog/tooltip"; import {clearSelect} from "../protyle/util/clear"; import {scrollCenter} from "../util/highlightById"; import {base64ToURL} from "../util/image"; +import {setPosition} from "../util/setPosition"; const renderAssetList = (element: Element, k: string, position: IPosition, exts: string[] = []) => { fetchPost("/api/search/searchAsset", { @@ -1764,55 +1765,84 @@ style="margin:4px 0;width: ${isMobile() ? "100%" : "360px"}" class="b3-text-fiel export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => { window.siyuan.menus.menu.remove(); - window.siyuan.menus.menu.element.setAttribute("data-name", Constants.MENU_INLINE_TAG); const nodeElement = hasClosestBlock(tagElement); if (!nodeElement) { return; } hideElements(["util", "toolbar", "hint"], protyle); const id = nodeElement.getAttribute("data-node-id"); - let html = nodeElement.outerHTML; + let inputElement: HTMLInputElement; + const oldHTML = nodeElement.outerHTML; + window.siyuan.menus.menu.removeCB = () => { + tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || ""); + if (!inputElement.value) { + tagElement.insertAdjacentHTML("afterend", ""); + tagElement.remove(); + focusByWbr(nodeElement, protyle.toolbar.range); + } else { + protyle.toolbar.range.selectNodeContents(tagElement); + protyle.toolbar.range.collapse(false); + focusByRange(protyle.toolbar.range); + } + if (nodeElement.outerHTML !== oldHTML) { + nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); + updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML); + } + }; + window.siyuan.menus.menu.element.setAttribute("data-name", Constants.MENU_INLINE_TAG); window.siyuan.menus.menu.append(new MenuItem({ id: "tag", iconHTML: "", type: "readonly", - label: ``, + label: ` +
`, bind(element) { - const inputElement = element.querySelector("input"); + const listElement = element.querySelector(".b3-list") as HTMLElement; + inputElement = element.querySelector("input"); inputElement.value = tagElement.textContent.replace(Constants.ZWSP, ""); - inputElement.addEventListener("change", () => { - updateTransaction(protyle, id, nodeElement.outerHTML, html); - html = nodeElement.outerHTML; - }); inputElement.addEventListener("compositionend", () => { - tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || ""); + genTagList(listElement, inputElement.value.trim()); + setPosition(listElement, inputElementRect.right + 8, inputElementRect[isMobile() ? "bottom" : "top"], inputElementRect.height); }); inputElement.addEventListener("input", (event: KeyboardEvent) => { if (!event.isComposing) { - // https://github.com/siyuan-note/siyuan/issues/4511 - tagElement.innerHTML = Constants.ZWSP + Lute.EscapeHTMLStr(inputElement.value || ""); + listElement.classList.remove("fn__none"); + genTagList(listElement, inputElement.value.trim()); + setPosition(listElement, inputElementRect.right + 8, inputElementRect[isMobile() ? "bottom" : "top"], inputElementRect.height); } }); inputElement.addEventListener("keydown", (event) => { - if ((event.key === "Enter" || event.key === "Escape") && !event.isComposing) { - event.preventDefault(); - event.stopPropagation(); - if (!inputElement.value) { - const oldHTML = nodeElement.outerHTML; - tagElement.insertAdjacentHTML("afterend", ""); - tagElement.remove(); - nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); - updateTransaction(protyle, id, nodeElement.outerHTML, oldHTML); - focusByWbr(nodeElement, protyle.toolbar.range); - } else { - protyle.toolbar.range.selectNodeContents(tagElement); - protyle.toolbar.range.collapse(false); - focusByRange(protyle.toolbar.range); - } - window.siyuan.menus.menu.remove(); - } else if (electronUndo(event)) { + event.stopPropagation(); + if (event.isComposing) { return; } + if (event.key === "Enter" || event.key === "Escape") { + if (!listElement.classList.contains("fn__none")) { + listElement.classList.add("fn__none"); + if (event.key === "Enter") { + const currentElement = listElement.querySelector(".b3-list-item--focus") as HTMLElement; + inputElement.value = currentElement.dataset.type === "new" ? currentElement.querySelector("mark").textContent.trim() : currentElement.textContent.trim(); + } + return; + } + if (event.key === "Escape") { + window.siyuan.menus.menu.removeCB = null; + } + window.siyuan.menus.menu.remove(); + event.preventDefault(); + } else { + electronUndo(event); + upDownHint(listElement, event); + } + }); + listElement.addEventListener("click", (event) => { + const target = event.target as HTMLElement; + const listItemElement = hasClosestByClassName(target, "b3-list-item"); + if (!listItemElement) { + return; + } + inputElement.value = listItemElement.dataset.type === "new" ? listItemElement.querySelector("mark").textContent.trim() : listItemElement.textContent.trim(); + listElement.classList.add("fn__none"); }); } }).element); @@ -1885,7 +1915,6 @@ export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => { icon: "iconTrashcan", label: window.siyuan.languages.remove, click() { - const oldHTML = nodeElement.outerHTML; tagElement.insertAdjacentHTML("afterend", ""); tagElement.remove(); nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss")); @@ -1918,7 +1947,8 @@ export const tagMenu = (protyle: IProtyle, tagElement: HTMLElement) => { /// #endif const popoverElement = hasTopClosestByClassName(protyle.element, "block__popover", true); window.siyuan.menus.menu.element.setAttribute("data-from", popoverElement ? popoverElement.dataset.level + "popover" : "app"); - window.siyuan.menus.menu.element.querySelector("input").select(); + inputElement.select(); + const inputElementRect = inputElement.getBoundingClientRect(); }; export const inlineMathMenu = (protyle: IProtyle, element: Element) => { diff --git a/app/src/util/noRelyPCFunction.ts b/app/src/util/noRelyPCFunction.ts index c904e521b..27a278c9e 100644 --- a/app/src/util/noRelyPCFunction.ts +++ b/app/src/util/noRelyPCFunction.ts @@ -12,6 +12,29 @@ import {upDownHint} from "./upDownHint"; import {escapeHtml} from "./escape"; import {hasClosestByClassName} from "../protyle/util/hasClosest"; import {isNotCtrl} from "../protyle/util/compatibility"; +import {electronUndo} from "../protyle/undo"; + +export const genTagList = (listElement: Element, k: string) => { + listElement.classList.remove("fn__none"); + fetchPost("/api/search/searchTag", { + k, + }, (response) => { + let searchHTML = ""; + let hasKey = false; + response.data.tags.forEach((item: string, index: number) => { + searchHTML += `
+
${item}
+
`; + if (item === `${response.data.k}`) { + hasKey = true; + } + }); + if (!hasKey && response.data.k) { + searchHTML = `
${window.siyuan.languages.new} ${escapeHtml(response.data.k)}
` + searchHTML; + } + listElement.innerHTML = searchHTML; + }); +}; // 需独立出来,否则移动端引用的时候会引入 pc 端大量无用代码 export const renameTag = (labelName: string) => { @@ -72,29 +95,18 @@ export const renameTag = (labelName: string) => { listElement.classList.add("fn__none"); } event.preventDefault(); + } else { + electronUndo(event); } }); - inputElement.addEventListener("input", (event) => { + inputElement.addEventListener("input", (event: KeyboardEvent) => { event.stopPropagation(); - listElement.classList.remove("fn__none"); - fetchPost("/api/search/searchTag", { - k: inputElement.value.trim(), - }, (response) => { - let searchHTML = ""; - let hasKey = false; - response.data.tags.forEach((item: string, index: number) => { - searchHTML += `
-
${item}
-
`; - if (item === `${response.data.k}`) { - hasKey = true; - } - }); - if (!hasKey && response.data.k) { - searchHTML = `
${window.siyuan.languages.new} ${escapeHtml(response.data.k)}
` + searchHTML; - } - listElement.innerHTML = searchHTML; - }); + if (!event.isComposing) { + genTagList(listElement, inputElement.value.trim()); + } + }); + inputElement.addEventListener("compositionend", () => { + genTagList(listElement, inputElement.value.trim()); }); listElement.addEventListener("click", (event) => { const target = event.target as HTMLElement;