diff --git a/app/src/boot/globalEvent/keydown.ts b/app/src/boot/globalEvent/keydown.ts index 56e076532..93993134f 100644 --- a/app/src/boot/globalEvent/keydown.ts +++ b/app/src/boot/globalEvent/keydown.ts @@ -1259,12 +1259,6 @@ export const windowKeyDown = (app: App, event: KeyboardEvent) => { return; } - const avElement = document.querySelector(".av__panel"); - if (avElement) { - avElement.remove(); - return; - } - if (!window.siyuan.menus.menu.element.classList.contains("fn__none")) { if (window.siyuan.dialogs.length > 0 && window.siyuan.menus.menu.element.style.zIndex < (window.siyuan.dialogs[0].element.querySelector(".b3-dialog") as HTMLElement).style.zIndex) { @@ -1280,6 +1274,13 @@ export const windowKeyDown = (app: App, event: KeyboardEvent) => { return; } + // 需放在 menus 后,否则资源列中添加资源会先关闭菜单 + const avElement = document.querySelector(".av__panel"); + if (avElement) { + avElement.remove(); + return; + } + // remove blockpopover const maxEditLevels: { [key: string]: number } = {oid: 0}; window.siyuan.blockPanels.forEach((item) => { diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts index ce6b3a400..069d19d1d 100644 --- a/app/src/menus/protyle.ts +++ b/app/src/menus/protyle.ts @@ -1,4 +1,9 @@ -import {hasClosestBlock, hasClosestByMatchTag} from "../protyle/util/hasClosest"; +import { + hasClosestBlock, + hasClosestByAttribute, + hasClosestByClassName, + hasClosestByMatchTag +} from "../protyle/util/hasClosest"; import {MenuItem} from "./Menu"; import {focusBlock, focusByRange, focusByWbr, getEditorRange, selectAll,} from "../protyle/util/selection"; import { @@ -46,6 +51,142 @@ import {emitOpenMenu} from "../plugin/EventBus"; import {openMobileFileById} from "../mobile/editor"; import {openBacklink, openGraph} from "../layout/dock/util"; import {updateHeader} from "../protyle/render/av/row"; +import {renderAssetsPreview} from "../asset/renderAssets"; +import {upDownHint} from "../util/upDownHint"; +import {hintRenderAssets} from "../protyle/hint/extend"; +import {Menu} from "../plugin/Menu"; + +const renderAssetList = (element: Element, k: string, position: IPosition, exts: string[] = []) => { + fetchPost("/api/search/searchAsset", { + k, + exts + }, (response) => { + let searchHTML = ""; + response.data.forEach((item: { path: string, hName: string }, index: number) => { + searchHTML += `
${item.hName}
`; + }); + + const listElement = element.querySelector(".b3-list"); + const previewElement = element.querySelector("#preview"); + const inputElement = element.querySelector("input"); + listElement.innerHTML = searchHTML || `
  • ${window.siyuan.languages.emptyContent}
  • `; + if (response.data.length > 0) { + previewElement.innerHTML = renderAssetsPreview(response.data[0].path); + } else { + previewElement.innerHTML = window.siyuan.languages.emptyContent; + } + /// #if MOBILE + window.siyuan.menus.menu.fullscreen(); + /// #else + window.siyuan.menus.menu.popup(position); + /// #endif + if (!k) { + inputElement.select(); + } + }); +} + +export const assetMenu = (protyle: IProtyle, position: IPosition, callback?: (url: string) => void, exts?: string[]) => { + window.siyuan.menus.menu.remove(); + const menu = new Menu() + menu.addItem({ + iconHTML: "", + type: "readonly", + label: `
    +
    +
    + + + + + +
    +
    +
    +
    +
    `, + bind(element) { + element.style.maxWidth = "none"; + const listElement = element.querySelector(".b3-list"); + const previewElement = element.querySelector("#preview"); + listElement.addEventListener("mouseover", (event) => { + const target = event.target as HTMLElement; + const hoverItemElement = hasClosestByClassName(target, "b3-list-item"); + if (!hoverItemElement) { + return; + } + previewElement.innerHTML = renderAssetsPreview(hoverItemElement.getAttribute("data-value")); + }); + const inputElement = element.querySelector("input"); + inputElement.addEventListener("keydown", (event: KeyboardEvent) => { + if (event.isComposing) { + return; + } + const isEmpty = element.querySelector(".b3-list--empty"); + if (!isEmpty) { + const currentElement = upDownHint(listElement, event); + if (currentElement) { + previewElement.innerHTML = renderAssetsPreview(currentElement.getAttribute("data-value")); + event.stopPropagation(); + } + } + + if (event.key === "Enter") { + if (!isEmpty) { + const currentURL = element.querySelector(".b3-list-item--focus").getAttribute("data-value"); + if (callback) { + callback(currentURL); + } else { + hintRenderAssets(currentURL, protyle); + window.siyuan.menus.menu.remove(); + } + } else if (!callback) { + window.siyuan.menus.menu.remove(); + focusByRange(protyle.toolbar.range); + } + // 空行处插入 mp3 会多一个空的 mp3 块 + event.preventDefault(); + event.stopPropagation(); + } else if (event.key === "Escape") { + if (!callback) { + focusByRange(protyle.toolbar.range); + } + } + }); + inputElement.addEventListener("input", (event) => { + event.stopPropagation(); + renderAssetList(element, inputElement.value, position, exts); + }); + element.lastElementChild.addEventListener("click", (event) => { + const target = event.target as HTMLElement; + const previousElement = hasClosestByAttribute(target, "data-type", "previous"); + if (previousElement) { + inputElement.dispatchEvent(new KeyboardEvent("keydown", {key: "ArrowUp"})); + event.stopPropagation(); + return; + } + const nextElement = hasClosestByAttribute(target, "data-type", "next"); + if (nextElement) { + inputElement.dispatchEvent(new KeyboardEvent("keydown", {key: "ArrowDown"})); + event.stopPropagation(); + return; + } + const listItemElement = hasClosestByClassName(target, "b3-list-item"); + if (listItemElement) { + event.stopPropagation(); + const currentURL = listItemElement.getAttribute("data-value"); + if (callback) { + callback(currentURL); + } else { + hintRenderAssets(currentURL, protyle); + window.siyuan.menus.menu.remove(); + } + } + }); + renderAssetList(element, "", position, exts); + } + }) +} export const fileAnnotationRefMenu = (protyle: IProtyle, refElement: HTMLElement) => { const nodeElement = hasClosestBlock(refElement); diff --git a/app/src/protyle/header/Background.ts b/app/src/protyle/header/Background.ts index ce468b462..085b3ae41 100644 --- a/app/src/protyle/header/Background.ts +++ b/app/src/protyle/header/Background.ts @@ -13,6 +13,7 @@ import {popSearch} from "../../mobile/menu/search"; import {getEventName} from "../util/compatibility"; import {Dialog} from "../../dialog"; import {Constants} from "../../constants"; +import {assetMenu} from "../../menus/protyle"; const bgs = [ "background:radial-gradient(black 3px, transparent 4px),radial-gradient(black 3px, transparent 4px),linear-gradient(#fff 4px, transparent 0),linear-gradient(45deg, transparent 74px, transparent 75px, #a4a4a4 75px, #a4a4a4 76px, transparent 77px, transparent 109px),linear-gradient(-45deg, transparent 75px, transparent 76px, #a4a4a4 76px, #a4a4a4 77px, transparent 78px, transparent 109px),#fff;background-size: 109px 109px, 109px 109px,100% 6px, 109px 109px, 109px 109px;background-position: 54px 55px, 0px 0px, 0px 0px, 0px 0px, 0px 0px;", @@ -303,10 +304,10 @@ export class Background { break; } else if (type === "asset") { const rect = target.getBoundingClientRect(); - protyle.toolbar.showAssets(protyle, { - x: rect.right - 798, + assetMenu(protyle, { + x: target.parentElement.getBoundingClientRect().right, y: rect.bottom + 8, - isLeft: false, + isLeft: true, }, (url) => { this.ial["title-img"] = `background-image:url("${url}")`; this.render(this.ial, protyle.block.rootID); diff --git a/app/src/protyle/hint/index.ts b/app/src/protyle/hint/index.ts index e8fdc8cc5..d66d4b8aa 100644 --- a/app/src/protyle/hint/index.ts +++ b/app/src/protyle/hint/index.ts @@ -16,7 +16,7 @@ import {getContenteditableElement, hasNextSibling, hasPreviousSibling} from "../ import {transaction, updateTransaction} from "../wysiwyg/transaction"; import {insertHTML} from "../util/insertHTML"; import {highlightRender} from "../render/highlightRender"; -import {imgMenu} from "../../menus/protyle"; +import {assetMenu, imgMenu} from "../../menus/protyle"; import {hideElements} from "../ui/hideElements"; import {fetchPost} from "../../util/fetch"; import {getDisplayName, pathPosix} from "../../util/pathName"; @@ -608,7 +608,7 @@ ${genHintItemHTML(item)} this.fixImageCursor(range); protyle.toolbar.range = range; const rangePosition = getSelectionPosition(nodeElement, range); - protyle.toolbar.showAssets(protyle, {x: rangePosition.left, y: rangePosition.top + 26, w: 0, h: 26}); + assetMenu(protyle, {x: rangePosition.left, y: rangePosition.top + 26, w: 0, h: 26}); updateTransaction(protyle, id, nodeElement.outerHTML, html); return; } else if (value === Constants.ZWSP + 3) { diff --git a/app/src/protyle/render/av/openMenuPanel.ts b/app/src/protyle/render/av/openMenuPanel.ts index 5141306e2..9750b76cb 100644 --- a/app/src/protyle/render/av/openMenuPanel.ts +++ b/app/src/protyle/render/av/openMenuPanel.ts @@ -20,6 +20,7 @@ import {getSearch} from "../../../util/functions"; import {openAsset} from "../../../editor/util"; /// #endif import {previewImage} from "../../preview/image"; +import {assetMenu} from "../../../menus/protyle"; export const openMenuPanel = (options: { protyle: IProtyle, @@ -613,7 +614,7 @@ export const openMenuPanel = (options: { id: options.colId, avID, name, - type: target.dataset.oldType as TAVCol, + type: target.dataset.oldType as TAVCol, }]); } avPanelElement.remove(); @@ -755,7 +756,7 @@ export const openMenuPanel = (options: { break; } else if (type === "addAssetExist") { const rect = target.getBoundingClientRect(); - options.protyle.toolbar.showAssets(options.protyle, { + assetMenu(options.protyle, { x: rect.right, y: rect.bottom, w: target.parentElement.clientWidth + 8, @@ -782,7 +783,7 @@ export const openMenuPanel = (options: { type: "addUpdate", addUpdateValue: [value] }); - hideElements(["util"], options.protyle); + window.siyuan.menus.menu.remove(); }); event.preventDefault(); event.stopPropagation(); diff --git a/app/src/protyle/toolbar/index.ts b/app/src/protyle/toolbar/index.ts index 52cdca5e4..a46e3b940 100644 --- a/app/src/protyle/toolbar/index.ts +++ b/app/src/protyle/toolbar/index.ts @@ -1522,136 +1522,6 @@ export class Toolbar { }); } - private renderAssetList(listElement: Element, previewElement: Element, k: string, position: IPosition, exts: string[] = []) { - fetchPost("/api/search/searchAsset", { - k, - exts - }, (response) => { - let searchHTML = ""; - response.data.forEach((item: { path: string, hName: string }, index: number) => { - searchHTML += `
    ${item.hName}
    `; - }); - listElement.innerHTML = searchHTML || `
  • ${window.siyuan.languages.emptyContent}
  • `; - if (response.data.length > 0) { - previewElement.innerHTML = renderAssetsPreview(response.data[0].path); - } - /// #if !MOBILE - setPosition(this.subElement, position.x, position.y, position.h, position.w); - /// #endif - }); - } - - public showAssets(protyle: IProtyle, position: IPosition, callback?: (url: string) => void, exts: string[] = []) { - hideElements(["hint"], protyle); - window.siyuan.menus.menu.remove(); - this.subElement.style.width = ""; - this.subElement.style.padding = ""; - this.subElement.innerHTML = `
    -
    -
    - - - - - -
    -
    -
    -
    -
    `; - const listElement = this.subElement.querySelector(".b3-list"); - const previewElement = this.subElement.firstElementChild.lastElementChild; - listElement.addEventListener("mouseover", (event) => { - const target = event.target as HTMLElement; - const hoverItemElement = hasClosestByClassName(target, "b3-list-item"); - if (!hoverItemElement) { - return; - } - previewElement.innerHTML = renderAssetsPreview(hoverItemElement.getAttribute("data-value")); - }); - const inputElement = this.subElement.querySelector("input"); - inputElement.addEventListener("keydown", (event: KeyboardEvent) => { - event.stopPropagation(); - if (event.isComposing) { - return; - } - const isEmpty = this.subElement.querySelector(".b3-list--empty"); - if (!isEmpty) { - const currentElement = upDownHint(listElement, event); - if (currentElement) { - previewElement.innerHTML = renderAssetsPreview(currentElement.getAttribute("data-value")); - } - } - - if (event.key === "Enter") { - if (!isEmpty) { - const currentURL = this.subElement.querySelector(".b3-list-item--focus").getAttribute("data-value"); - if (callback) { - callback(currentURL); - } else { - hintRenderAssets(currentURL, protyle); - } - } else if (!callback) { - focusByRange(this.range); - } - this.subElement.classList.add("fn__none"); - // 空行处插入 mp3 会多一个空的 mp3 块 - event.preventDefault(); - } else if (event.key === "Escape") { - this.subElement.classList.add("fn__none"); - if (!callback) { - focusByRange(this.range); - } - } - }); - inputElement.addEventListener("input", (event) => { - event.stopPropagation(); - this.renderAssetList(listElement, previewElement, inputElement.value, position, exts); - }); - this.subElement.lastElementChild.addEventListener("click", (event) => { - const target = event.target as HTMLElement; - const previousElement = hasClosestByAttribute(target, "data-type", "previous"); - if (previousElement) { - inputElement.dispatchEvent(new KeyboardEvent("keydown", {key: "ArrowUp"})); - event.stopPropagation(); - return; - } - const nextElement = hasClosestByAttribute(target, "data-type", "next"); - if (nextElement) { - inputElement.dispatchEvent(new KeyboardEvent("keydown", {key: "ArrowDown"})); - event.stopPropagation(); - return; - } - if (target.classList.contains("b3-list--empty")) { - this.subElement.classList.add("fn__none"); - if (!callback) { - focusByRange(this.range); - } - event.stopPropagation(); - return; - } - const listItemElement = hasClosestByClassName(target, "b3-list-item"); - if (listItemElement) { - event.stopPropagation(); - const currentURL = listItemElement.getAttribute("data-value"); - if (callback) { - callback(currentURL); - } else { - hintRenderAssets(currentURL, protyle); - } - } - }); - this.subElement.style.zIndex = (++window.siyuan.zIndex).toString(); - this.subElement.classList.remove("fn__none"); - this.subElementCloseCB = undefined; - /// #if MOBILE - setPosition(this.subElement, 0, 0); - /// #endif - this.element.classList.add("fn__none"); - inputElement.select(); - this.renderAssetList(listElement, previewElement, "", position, exts); - } - public showContent(protyle: IProtyle, range: Range, nodeElement: Element) { this.range = range; hideElements(["hint"], protyle);