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.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]});
+ }
+ });
+ });
+};