diff --git a/app/src/mobile/menu/search.ts b/app/src/mobile/menu/search.ts index 7598113cb..65bb58fbc 100644 --- a/app/src/mobile/menu/search.ts +++ b/app/src/mobile/menu/search.ts @@ -5,10 +5,93 @@ import {fetchPost} from "../../util/fetch"; import {getIconByType} from "../../editor/getIcon"; import {preventScroll} from "../../protyle/scroll/preventScroll"; import {openModel} from "./model"; -import {getNotebookName, movePathTo, pathPosix} from "../../util/pathName"; -import {filterMenu, initCriteriaMenu, moreMenu, queryMenu} from "../../search/menu"; +import {getDisplayName, getNotebookIcon, getNotebookName, movePathTo, pathPosix} from "../../util/pathName"; +import {filterMenu, getKeyByLiElement, initCriteriaMenu, moreMenu, queryMenu} from "../../search/menu"; import {setStorageVal} from "../../protyle/util/compatibility"; -import {escapeHtml} from "../../util/escape"; +import {escapeGreat, escapeHtml} from "../../util/escape"; +import {unicode2Emoji} from "../../emoji"; +import {newFileByName} from "../../util/newFile"; +import {showMessage} from "../../dialog/message"; +import {reloadProtyle} from "../../protyle/util/reload"; + +const replace = (element: Element, config: ISearchOption, isAll: boolean) => { + if (config.method === 1 || config.method === 2) { + showMessage(window.siyuan.languages._kernel[132]); + return; + } + const searchListElement = element.querySelector("#searchList"); + const replaceInputElement = element.querySelector("#toolbarReplace") as HTMLInputElement; + + const loadElement = replaceInputElement.nextElementSibling; + if (!loadElement.classList.contains("fn__none")) { + return; + } + let currentLiElement: HTMLElement = searchListElement.querySelector(".b3-list-item--focus"); + if (!currentLiElement) { + return; + } + loadElement.classList.remove("fn__none"); + loadElement.nextElementSibling.classList.add("fn__none"); + + let ids: string[] = []; + if (isAll) { + searchListElement.querySelectorAll('.b3-list-item[data-type="search-item"]').forEach(item => { + ids.push(item.getAttribute("data-node-id")); + }); + } else { + ids = [currentLiElement.getAttribute("data-node-id")]; + } + fetchPost("/api/search/findReplace", { + k: config.method === 0 ? getKeyByLiElement(currentLiElement) : (document.querySelector("#toolbarSearch") as HTMLInputElement).value, + r: replaceInputElement.value, + ids, + types: config.types, + method: config.method, + }, (response) => { + loadElement.classList.add("fn__none"); + loadElement.nextElementSibling.classList.remove("fn__none"); + + if (response.code === 1) { + showMessage(response.msg); + return; + } + if (ids.length > 1) { + return; + } + reloadProtyle(window.siyuan.mobile.editor.protyle); + + if (currentLiElement.nextElementSibling) { + currentLiElement.nextElementSibling.classList.add("b3-list-item--focus"); + } else if (currentLiElement.previousElementSibling) { + currentLiElement.previousElementSibling.classList.add("b3-list-item--focus"); + } + if (config.group === 1) { + if (currentLiElement.nextElementSibling || currentLiElement.previousElementSibling) { + currentLiElement.remove(); + } else { + const nextDocElement = currentLiElement.parentElement.nextElementSibling || currentLiElement.parentElement.previousElementSibling.previousElementSibling?.previousElementSibling; + if (nextDocElement) { + nextDocElement.nextElementSibling.firstElementChild.classList.add("b3-list-item--focus"); + nextDocElement.nextElementSibling.classList.remove("fn__none"); + nextDocElement.firstElementChild.firstElementChild.classList.add("b3-list-item__arrow--open"); + } + currentLiElement.parentElement.previousElementSibling.remove(); + currentLiElement.parentElement.remove(); + } + } else { + currentLiElement.remove(); + } + currentLiElement = searchListElement.querySelector(".b3-list-item--focus"); + if (!currentLiElement) { + searchListElement.innerHTML = `
${window.siyuan.languages.emptyContent}
`; + return; + } + if (searchListElement.scrollTop < currentLiElement.offsetTop - searchListElement.clientHeight + 30 || + searchListElement.scrollTop > currentLiElement.offsetTop) { + searchListElement.scrollTop = currentLiElement.offsetTop - searchListElement.clientHeight + 30; + } + }); +}; const updateConfig = (element: Element, newConfig: ISearchOption, config: ISearchOption) => { newConfig.hPath = config.hPath; @@ -64,43 +147,84 @@ const updateConfig = (element: Element, newConfig: ISearchOption, config: ISearc Object.assign(config, newConfig); window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config); setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]); - updateSearchResult(config); + updateSearchResult(config, element); window.siyuan.menus.menu.remove(); }; const onRecentBlocks = (data: IBlock[], matchedRootCount?: number, matchedBlockCount?: number) => { + const listElement = document.querySelector("#searchList") let resultHTML = ""; - if (matchedBlockCount) { - resultHTML = '
' + window.siyuan.languages.findInDoc.replace("${x}", matchedRootCount).replace("${y}", matchedBlockCount) + "
"; - } - data.forEach((item: IBlock) => { - resultHTML += `
+ data.forEach((item: IBlock, index: number) => { + const title = getNotebookName(item.box) + getDisplayName(item.hPath, false); + if (item.children) { + resultHTML += `
+ + + +${unicode2Emoji(getNotebookIcon(item.box) || Constants.SIYUAN_IMAGE_NOTE, false, "b3-list-item__graphic", true)} +${escapeGreat(title)} +
`; + item.children.forEach((childItem, childIndex) => { + resultHTML += `
+ +${unicode2Emoji(childItem.ial.icon, false, "b3-list-item__graphic", true)} +${childItem.content} +
`; + }); + resultHTML += "
"; + } else { + resultHTML += `
+ ${unicode2Emoji(item.ial.icon, false, "b3-list-item__graphic", true)} ${item.content}
-
${Lute.EscapeHTMLStr(item.hPath)}
+${escapeGreat(title)}
`; + } }); - document.querySelector("#searchList").innerHTML = resultHTML; + listElement.innerHTML = resultHTML || + `
+ + + ${window.siyuan.languages.newFile} ${(document.querySelector("#toolbarSearch") as HTMLInputElement).value} + +
`; + listElement.scrollTop = 0; + let countHTML = "" + if (matchedBlockCount) { + countHTML = `
${window.siyuan.languages.findInDoc.replace("${x}", matchedRootCount).replace("${y}", matchedBlockCount)}
`; + } + listElement.previousElementSibling.innerHTML = countHTML; + }; let toolbarSearchTimeout = 0; -export const updateSearchResult = (config: ISearchOption) => { +export const updateSearchResult = (config: ISearchOption, element: Element) => { clearTimeout(toolbarSearchTimeout); toolbarSearchTimeout = window.setTimeout(() => { + const loadingElement = element.querySelector(".fn__loading--top"); + loadingElement.classList.remove("fn__none"); const inputElement = document.getElementById("toolbarSearch") as HTMLInputElement; - if (inputElement.value === "") { + if (inputElement.value === "" && (!config.idPath || config.idPath.length === 0)) { fetchPost("/api/block/getRecentUpdatedBlocks", {}, (response) => { onRecentBlocks(response.data); + loadingElement.classList.add("fn__none"); }); } else { - fetchPost("/api/search/fullTextSearchBlock", {query: inputElement.value,}, (response) => { + fetchPost("/api/search/fullTextSearchBlock", { + query: inputElement.value, + method: config.method, + types: config.types, + paths: config.idPath || [], + groupBy: config.group, + orderBy: config.sort, + }, (response) => { onRecentBlocks(response.data.blocks, response.data.matchedRootCount, response.data.matchedBlockCount); + loadingElement.classList.add("fn__none"); }); } }, Constants.TIMEOUT_SEARCH); - return toolbarSearchTimeout }; const initSearchEvent = (element: Element, config: ISearchOption) => { @@ -110,13 +234,13 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { if (event && event.isComposing) { return; } - updateSearchResult(config); + updateSearchResult(config, element); }); searchInputElement.addEventListener("input", (event: InputEvent) => { if (event && event.isComposing) { return; } - updateSearchResult(config); + updateSearchResult(config, element); }); const replaceInputElement = element.querySelector(".toolbar .b3-text-field") as HTMLInputElement replaceInputElement.value = config.r || "" @@ -124,6 +248,7 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { const criteriaData: ISearchOption[] = []; initCriteriaMenu(element.querySelector("#criteria"), criteriaData); + const searchListElement = element.querySelector("#searchList") as HTMLElement; element.addEventListener("click", (event: MouseEvent) => { let target = event.target as HTMLElement; while (target && !target.isSameNode(element)) { @@ -161,30 +286,30 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { config.idPath = []; config.hPath = ""; element.querySelector("#searchPath").classList.add("fn__none") - toolbarSearchTimeout = updateSearchResult(config); + updateSearchResult(config, element); const includeElement = element.querySelector('[data-type="include"]'); includeElement.classList.remove("toolbar__icon--active"); includeElement.setAttribute("disabled", "disabled"); event.stopPropagation(); event.preventDefault(); break; - } else if (target.id === "searchExpand") { - // Array.from(searchPanelElement.children).forEach(item => { - // if (item.classList.contains("b3-list-item")) { - // item.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); - // item.nextElementSibling.classList.remove("fn__none"); - // } - // }); + } else if (type === "expand") { + Array.from(searchListElement.children).forEach(item => { + if (item.classList.contains("b3-list-item")) { + item.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); + item.nextElementSibling.classList.remove("fn__none"); + } + }); event.stopPropagation(); event.preventDefault(); break; - } else if (target.id === "searchCollapse") { - // Array.from(searchPanelElement.children).forEach(item => { - // if (item.classList.contains("b3-list-item")) { - // item.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open"); - // item.nextElementSibling.classList.add("fn__none"); - // } - // }); + } else if (type === "contract") { + Array.from(searchListElement.children).forEach(item => { + if (item.classList.contains("b3-list-item")) { + item.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open"); + item.nextElementSibling.classList.add("fn__none"); + } + }); event.stopPropagation(); event.preventDefault(); break; @@ -207,9 +332,11 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { hPathList.push(...response.data); } config.hPath = hPathList.join(" "); + const searchPathElement = element.querySelector("#searchPath") searchPathElement.classList.remove("fn__none"); element.querySelector("#searchPath").innerHTML = `
${escapeHtml(config.hPath)}
`; + const includeElement = element.querySelector('[data-type="include"]'); includeElement.classList.add("toolbar__icon--active"); if (enableIncludeChild) { @@ -217,28 +344,28 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { } else { includeElement.setAttribute("disabled", "disabled"); } - toolbarSearchTimeout = updateSearchResult(config); + updateSearchResult(config, element); }); }, [], undefined, window.siyuan.languages.specifyPath); event.stopPropagation(); event.preventDefault(); break; - } else if (target.id === "searchInclude") { - target.classList.toggle("b3-button--cancel"); - if (target.classList.contains("b3-button--cancel")) { - config.idPath.forEach((item, index) => { - if (!item.endsWith(".sy") && item.split("/").length > 1) { - config.idPath[index] = item + ".sy"; - } - }); - } else { + } else if (type === "include") { + target.classList.toggle("toolbar__icon--active"); + if (target.classList.contains("toolbar__icon--active")) { config.idPath.forEach((item, index) => { if (item.endsWith(".sy")) { config.idPath[index] = item.replace(".sy", ""); } }); + } else { + config.idPath.forEach((item, index) => { + if (!item.endsWith(".sy") && item.split("/").length > 1) { + config.idPath[index] = item + ".sy"; + } + }); } - // inputTimeout = inputEvent(element, config, inputTimeout, edit); + updateSearchResult(config, element); event.stopPropagation(); event.preventDefault(); break; @@ -251,7 +378,7 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { break; } else if (type === "more") { moreMenu(config, criteriaData, element, () => { - updateSearchResult(config); + updateSearchResult(config, element); }, () => { updateConfig(element, { removed: true, @@ -286,27 +413,27 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { break; } else if (type === "filter") { filterMenu(config, () => { - updateSearchResult(config) + updateSearchResult(config, element) }); event.stopPropagation(); event.preventDefault(); break; } else if (type === "query") { queryMenu(config, () => { - updateSearchResult(config) + updateSearchResult(config, element) }); window.siyuan.menus.menu.element.style.zIndex = "220"; window.siyuan.menus.menu.fullscreen(); event.stopPropagation(); event.preventDefault(); break; - } else if (target.id === "replaceAllBtn") { - // replace(element, config, edit, true); + } else if (type === "replace-all") { + replace(element, config, true); event.stopPropagation(); event.preventDefault(); break; - } else if (target.id === "replaceBtn") { - // replace(element, config, edit, false); + } else if (type === "replace") { + replace(element, config, false); event.stopPropagation(); event.preventDefault(); break; @@ -317,15 +444,10 @@ const initSearchEvent = (element: Element, config: ISearchOption) => { event.preventDefault(); break; } else if (target.classList.contains("b3-list-item")) { - if (target.parentElement.id === "searchHistoryList") { - searchInputElement.value = target.textContent; - // inputTimeout = inputEvent(element, config, inputTimeout, edit); - } else if (target.parentElement.id === "replaceHistoryList") { - replaceInputElement.value = target.textContent; - } else if (target.getAttribute("data-type") === "search-new") { - // newEmptyFileByInput(searchInputElement.value); + if (target.getAttribute("data-type") === "search-new") { + newFileByName(searchInputElement.value); } else if (target.getAttribute("data-type") === "search-item") { - const id = target.getAttribute("data-id"); + const id = target.getAttribute("data-node-id"); if (window.siyuan.mobile.editor.protyle) { preventScroll(window.siyuan.mobile.editor.protyle); } @@ -365,14 +487,16 @@ export const popSearch = (config = window.siyuan.storage[Constants.LOCAL_SEARCHD
+
- +
- +
-
+
+
${escapeHtml(config.hPath)} @@ -391,10 +515,11 @@ export const popSearch = (config = window.siyuan.storage[Constants.LOCAL_SEARCHD
+
`, bindEvent(element) { initSearchEvent(element.firstElementChild, config); - toolbarSearchTimeout = updateSearchResult(config); + updateSearchResult(config, element); } }); }; diff --git a/app/src/search/menu.ts b/app/src/search/menu.ts index 7af5fef26..b0689eaa9 100644 --- a/app/src/search/menu.ts +++ b/app/src/search/menu.ts @@ -371,3 +371,11 @@ export const initCriteriaMenu = (element: HTMLElement, data: ISearchOption[]) => } }); }; + +export const getKeyByLiElement = (element: HTMLElement) => { + const keys: string[] = []; + element.querySelectorAll("mark").forEach(item => { + keys.push(item.textContent); + }); + return [...new Set(keys)].join(" "); +}; diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 993f51a37..6e8b78d21 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -16,14 +16,11 @@ import {disabledProtyle, onGet} from "../protyle/util/onGet"; import {addLoading, setPadding} from "../protyle/ui/initUI"; import {getIconByType} from "../editor/getIcon"; import {unicode2Emoji} from "../emoji"; -import {Dialog} from "../dialog"; import {hasClosestByClassName} from "../protyle/util/hasClosest"; import {setStorageVal, updateHotkeyTip} from "../protyle/util/compatibility"; -import {replaceFileName} from "../editor/rename"; -import {hideElements} from "../protyle/ui/hideElements"; -import {getNewFilePath} from "../util/newFile"; +import {newFileByName} from "../util/newFile"; import {matchHotKey} from "../protyle/util/hotKey"; -import {filterMenu, initCriteriaMenu, moreMenu, queryMenu} from "./menu"; +import {filterMenu, getKeyByLiElement, initCriteriaMenu, moreMenu, queryMenu} from "./menu"; const saveKeyList = (type: "keys" | "replaceKeys", value: string) => { let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type]; @@ -82,23 +79,6 @@ export const openGlobalSearch = (text: string, replace: boolean) => { setPanelFocus(tab.panelElement); }; -const newEmptyFileByInput = (value: string) => { - const newData = getNewFilePath(true); - fetchPost("/api/filetree/getHPathByPath", { - notebook: newData.notebookId, - path: newData.currentPath, - }, (responsePath) => { - fetchPost("/api/filetree/createDocWithMd", { - notebook: newData.notebookId, - path: pathPosix().join(responsePath.data, replaceFileName(value.trim()) || "Untitled"), - markdown: "" - }, response => { - hideElements(["dialog"]); - openFileById({id: response.data, action: [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]}); - }); - }); -}; - // closeCB 不存在为页签搜索 export const genSearch = (config: ISearchOption, element: Element, closeCB?: () => void) => { let methodText = window.siyuan.languages.keyword; @@ -621,7 +601,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () replaceInputElement.value = target.textContent; replaceHistoryElement.classList.add("fn__none"); } else if (target.getAttribute("data-type") === "search-new") { - newEmptyFileByInput(searchInputElement.value); + newFileByName(searchInputElement.value); } else if (target.getAttribute("data-type") === "search-item") { if (event.detail === 1) { clickTimeout = window.setTimeout(() => { @@ -644,7 +624,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () getArticle({ edit, id: target.getAttribute("data-node-id"), - k: getKey(target) + k: getKeyByLiElement(target) }); searchInputElement.focus(); } else if (target.classList.contains("b3-list-item--focus")) { @@ -706,14 +686,14 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } const focusIsNew = currentList.getAttribute("data-type") === "search-new"; if (focusIsNew && matchHotKey(window.siyuan.config.keymap.general.newFile.custom, event)) { - newEmptyFileByInput(searchInputElement.value); + newFileByName(searchInputElement.value); event.preventDefault(); event.stopPropagation(); return; } if (event.key === "Enter") { if (focusIsNew) { - newEmptyFileByInput(searchInputElement.value); + newFileByName(searchInputElement.value); } else { const id = currentList.getAttribute("data-node-id"); fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => { @@ -755,7 +735,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } getArticle({ id: currentList.getAttribute("data-node-id"), - k: getKey(currentList), + k: getKeyByLiElement(currentList), edit }); event.preventDefault(); @@ -781,7 +761,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } getArticle({ id: currentList.getAttribute("data-node-id"), - k: getKey(currentList), + k: getKeyByLiElement(currentList), edit }); event.preventDefault(); @@ -877,14 +857,6 @@ const updateConfig = (element: Element, item: ISearchOption, config: ISearchOpti window.siyuan.menus.menu.remove(); }; -const getKey = (element: HTMLElement) => { - const keys: string[] = []; - element.querySelectorAll("mark").forEach(item => { - keys.push(item.textContent); - }); - return [...new Set(keys)].join(" "); -}; - const renderNextSearchMark = (options: { id: string, edit: Protyle, @@ -973,7 +945,7 @@ const replace = (element: Element, config: ISearchOption, edit: Protyle, isAll: rootIds = [currentList.getAttribute("data-root-id")]; } fetchPost("/api/search/findReplace", { - k: config.method === 0 ? getKey(currentList) : (element.querySelector("#searchInput") as HTMLInputElement).value, + k: config.method === 0 ? getKeyByLiElement(currentList) : (element.querySelector("#searchInput") as HTMLInputElement).value, r: replaceInputElement.value, ids, types: config.types, @@ -1027,7 +999,7 @@ const replace = (element: Element, config: ISearchOption, edit: Protyle, isAll: getArticle({ edit, id: currentList.getAttribute("data-node-id"), - k: getKey(currentList) + k: getKeyByLiElement(currentList) }); }); }; @@ -1106,14 +1078,14 @@ ${unicode2Emoji(item.ial.icon, false, "b3-list-item__graphic", true)} getArticle({ edit, id: data[0].children[0].id, - k: getKey(contentElement), + k: getKeyByLiElement(contentElement), }); } else { contentElement.innerHTML = data[0].content; getArticle({ edit, id: data[0].id, - k: getKey(contentElement), + k: getKeyByLiElement(contentElement), }); } } else { diff --git a/app/src/util/newFile.ts b/app/src/util/newFile.ts index 2aa15c67a..77f7044ec 100644 --- a/app/src/util/newFile.ts +++ b/app/src/util/newFile.ts @@ -9,7 +9,10 @@ import {openFileById} from "../editor/util"; import {fetchPost} from "./fetch"; import {getDisplayName, getOpenNotebookCount, pathPosix} from "./pathName"; import {Constants} from "../constants"; -import {validateName} from "../editor/rename"; +import {replaceFileName, validateName} from "../editor/rename"; +import {hideElements} from "../protyle/ui/hideElements"; +import {isMobile} from "./functions"; +import {openMobileFileById} from "../mobile/editor"; export const getNewFilePath = (useSavePath: boolean) => { let notebookId = ""; @@ -149,3 +152,24 @@ export const getSavePath = (pathString: string, notebookId: string, cb: (p: stri } }); }; + +export const newFileByName = (value: string) => { + const newData = getNewFilePath(true); + fetchPost("/api/filetree/getHPathByPath", { + notebook: newData.notebookId, + path: newData.currentPath, + }, (responsePath) => { + fetchPost("/api/filetree/createDocWithMd", { + notebook: newData.notebookId, + path: pathPosix().join(responsePath.data, replaceFileName(value.trim()) || "Untitled"), + markdown: "" + }, response => { + hideElements(["dialog"]); + if (isMobile()) { + openMobileFileById(response.data, [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]); + } else { + openFileById({id: response.data, action: [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]}); + } + }); + }); +};