mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-06 00:38:49 +01:00
This commit is contained in:
parent
62e111ad15
commit
633f3504bb
1 changed files with 37 additions and 641 deletions
|
|
@ -4,17 +4,10 @@ import {getDisplayName, getNotebookName, pathPosix} from "../util/pathName";
|
|||
import {Constants} from "../constants";
|
||||
import {Protyle} from "../protyle";
|
||||
import {Dialog} from "../dialog";
|
||||
import {fetchPost, fetchSyncPost} from "../util/fetch";
|
||||
import {disabledProtyle, onGet} from "../protyle/util/onGet";
|
||||
import {openFileById} from "../editor/util";
|
||||
import {addLoading} from "../protyle/ui/initUI";
|
||||
import {getAllModels} from "../layout/getAll";
|
||||
import {showMessage} from "../dialog/message";
|
||||
import {fetchSyncPost} from "../util/fetch";
|
||||
import {focusByRange} from "../protyle/util/selection";
|
||||
import {reloadProtyle} from "../protyle/util/reload";
|
||||
import {unicode2Emoji} from "../emoji";
|
||||
import {genSearch} from "./util";
|
||||
|
||||
let protyle: Protyle;
|
||||
export const openSearch = async (hotkey: string, key?: string, notebookId?: string, searchPath?: string) => {
|
||||
const exitDialog = window.siyuan.dialogs.find((item) => {
|
||||
if (item.element.querySelector("#searchList")) {
|
||||
|
|
@ -25,12 +18,11 @@ export const openSearch = async (hotkey: string, key?: string, notebookId?: stri
|
|||
item.element.setAttribute("data-key", hotkey);
|
||||
return true;
|
||||
}
|
||||
const searchPathElement = item.element.querySelector("#searchPathCheck");
|
||||
const searchPathElement = item.element.querySelector("#searchPathInput");
|
||||
if (lastKey !== hotkey && hotkey === window.siyuan.config.keymap.general.globalSearch.custom) {
|
||||
if (!searchPathElement.classList.contains("b3-button--cancel")) {
|
||||
searchPathElement.dispatchEvent(new CustomEvent("click"));
|
||||
item.element.setAttribute("data-key", hotkey);
|
||||
return true;
|
||||
if (searchPathElement.textContent !== "") {
|
||||
item.destroy();
|
||||
return false;
|
||||
} else if (!replaceHeaderElement.classList.contains("fn__none")) {
|
||||
replaceHeaderElement.classList.add("fn__none");
|
||||
item.element.setAttribute("data-key", hotkey);
|
||||
|
|
@ -38,16 +30,16 @@ export const openSearch = async (hotkey: string, key?: string, notebookId?: stri
|
|||
}
|
||||
}
|
||||
if (lastKey !== hotkey && hotkey === window.siyuan.config.keymap.general.search.custom) {
|
||||
if (searchPathElement.classList.contains("b3-button--cancel")) {
|
||||
searchPathElement.dispatchEvent(new CustomEvent("click"));
|
||||
item.element.setAttribute("data-key", hotkey);
|
||||
return true;
|
||||
if (searchPathElement.textContent === "") {
|
||||
item.destroy();
|
||||
return false;
|
||||
} else if (!replaceHeaderElement.classList.contains("fn__none")) {
|
||||
replaceHeaderElement.classList.add("fn__none");
|
||||
item.element.setAttribute("data-key", hotkey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 切换关闭
|
||||
item.destroy();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -55,17 +47,7 @@ export const openSearch = async (hotkey: string, key?: string, notebookId?: stri
|
|||
if (exitDialog) {
|
||||
return;
|
||||
}
|
||||
protyle = undefined;
|
||||
const localData = JSON.parse(localStorage.getItem(Constants.LOCAL_SEARCHEDATA) || "{}");
|
||||
if (!localData.idPath) {
|
||||
localData.idPath = "";
|
||||
}
|
||||
if (!localData.hPath) {
|
||||
localData.hPath = "";
|
||||
}
|
||||
if (key) {
|
||||
localData.k = key;
|
||||
}
|
||||
if (!localData.types) {
|
||||
localData.types = {
|
||||
document: window.siyuan.config.search.document,
|
||||
|
|
@ -84,634 +66,48 @@ export const openSearch = async (hotkey: string, key?: string, notebookId?: stri
|
|||
if (notebookId) {
|
||||
localData.hPath = escapeHtml(getNotebookName(notebookId));
|
||||
localData.idPath = notebookId;
|
||||
}
|
||||
if (searchPath && searchPath !== "/") {
|
||||
const response = await fetchSyncPost("/api/filetree/getHPathByPath", {
|
||||
notebook: notebookId,
|
||||
path: searchPath.endsWith(".sy") ? searchPath : searchPath + ".sy"
|
||||
});
|
||||
localData.hPath = pathPosix().join(localData.hPath, escapeHtml(response.data));
|
||||
localData.idPath = pathPosix().join(localData.idPath, searchPath);
|
||||
}
|
||||
if (notebookId) {
|
||||
if (searchPath && searchPath !== "/") {
|
||||
const response = await fetchSyncPost("/api/filetree/getHPathByPath", {
|
||||
notebook: notebookId,
|
||||
path: searchPath.endsWith(".sy") ? searchPath : searchPath + ".sy"
|
||||
});
|
||||
localData.hPath = pathPosix().join(localData.hPath, escapeHtml(response.data));
|
||||
localData.idPath = pathPosix().join(localData.idPath, searchPath);
|
||||
}
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
}
|
||||
|
||||
let range: Range;
|
||||
if (getSelection().rangeCount > 0) {
|
||||
range = getSelection().getRangeAt(0);
|
||||
}
|
||||
const dialog = new Dialog({
|
||||
content: `<div class="fn__flex">
|
||||
<div class="fn__flex-column" style="height:70vh;width:80vw;border-radius: 4px;overflow: hidden;">
|
||||
<div class="b3-form__icon search__header">
|
||||
<span class="fn__a" id="searchHistoryBtn">
|
||||
<svg data-menu="true" class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
|
||||
<svg class="search__arrowdown"><use xlink:href="#iconDown"></use></svg>
|
||||
</span>
|
||||
<input id="searchInput" class="b3-text-field b3-text-field--text fn__block b3-form__icon-input">
|
||||
<div id="searchHistoryList" data-close="false" class="fn__none b3-menu b3-list b3-list--background"></div>
|
||||
</div>
|
||||
<div class="b3-form__icon search__header${hotkey === window.siyuan.config.keymap.general.replace.custom ? "" : " fn__none"}">
|
||||
<span class="fn__a" id="replaceHistoryBtn">
|
||||
<svg data-menu="true" class="b3-form__icon-icon"><use xlink:href="#iconSearch"></use></svg>
|
||||
<svg class="search__arrowdown"><use xlink:href="#iconDown"></use></svg>
|
||||
</span>
|
||||
<input id="replaceInput" class="b3-text-field b3-text-field--text fn__block b3-form__icon-input">
|
||||
<svg class="fn__rotate fn__none svg" style="padding: 0 8px;align-self: center;"><use xlink:href="#iconRefresh"></use></svg>
|
||||
<button id="replaceAllBtn" class="b3-button b3-button--outline fn__flex-center">${window.siyuan.languages.replaceAll}</button>
|
||||
<div class="fn__space"></div>
|
||||
<button id="replaceBtn" class="b3-button b3-button--outline fn__flex-center">${window.siyuan.languages.replace}</button>
|
||||
<div class="fn__space"></div>
|
||||
<div id="replaceHistoryList" data-close="false" class="fn__none b3-menu b3-list b3-list--background"></div>
|
||||
</div>
|
||||
<div class="fn__flex b3-form__space--small">
|
||||
<span id="searchResult" style="white-space: nowrap;"></span>
|
||||
<span class="fn__space"></span>
|
||||
<span class="fn__flex-1"></span>
|
||||
<span id="searchPathInput" class="ft__on-surface fn__flex-center ft__smaller fn__ellipsis" style="white-space: nowrap;" title="${localData.hPath}">${localData.hPath}</span>
|
||||
<span class="fn__space"></span>
|
||||
<button id="searchPathCheck" class="b3-button b3-button--small${notebookId ? "" : " b3-button--cancel"}">${window.siyuan.languages.specifyPath}</button>
|
||||
<span class="fn__space"></span>
|
||||
<button id="includeChildCheck" class="b3-button b3-button--small${(notebookId && localData.idPath && !localData.idPath.endsWith(".sy")) ? "" : " b3-button--cancel"}">${window.siyuan.languages.includeChildDoc}</button>
|
||||
<span class="fn__space"></span>
|
||||
<button id="searchSyntaxCheck" class="b3-button b3-button--small${localData.querySyntax ? "" : " b3-button--cancel"}">${window.siyuan.languages.querySyntax}</button>
|
||||
<span class="fn__space"></span>
|
||||
<span aria-label="${window.siyuan.languages.type}" class="b3-tooltips b3-tooltips__nw">
|
||||
<svg class="svg ft__on-surface" id="searchFilter" style="height: 19px;float: left"><use xlink:href="#iconSettings"></use></svg>
|
||||
</span>
|
||||
</div>
|
||||
<div id="searchList" style="position:relative;height:calc(50% - 69px);overflow: auto;padding-bottom: 8px" class="b3-list b3-list--background search__list"></div>
|
||||
<div id="searchPreview" class="fn__flex-1 search__preview"></div></div>
|
||||
<div id="searchFilterPanel" class="fn__none spread-search__filter">
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.math}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="mathBlock" type="checkbox"${localData.types.mathBlock ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.table}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="table" type="checkbox"${localData.types.table ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.quote}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="blockquote" type="checkbox"${localData.types.blockquote ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.superBlock}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="superBlock" type="checkbox"${localData.types.superBlock ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.paragraph}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="paragraph" type="checkbox"${localData.types.paragraph ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.doc}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="document" type="checkbox"${localData.types.document ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.headings}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="heading" type="checkbox"${localData.types.heading ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.list1}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="list" type="checkbox"${localData.types.list ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.listItem}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="listItem" type="checkbox"${localData.types.listItem ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
${window.siyuan.languages.code}
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="codeBlock" type="checkbox"${localData.types.codeBlock ? " checked" : ""}/>
|
||||
</label>
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1 b3-label__text">
|
||||
HTML
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="htmlBlock" type="checkbox"${localData.types.htmlBlock ? " checked" : ""}/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="fn__loading fn__loading--top"><img width="120px" src="/stage/loading-pure.svg"></div>
|
||||
</div>`,
|
||||
content: "",
|
||||
width: "80vw",
|
||||
height: "80vh",
|
||||
destroyCallback: () => {
|
||||
if (range) {
|
||||
focusByRange(range);
|
||||
}
|
||||
if (protyle) {
|
||||
protyle.destroy();
|
||||
if (edit) {
|
||||
edit.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
dialog.element.setAttribute("data-key", hotkey);
|
||||
const edit = genSearch({
|
||||
k: key || "",
|
||||
r: localData.r || "",
|
||||
hasReplace: hotkey === window.siyuan.config.keymap.general.replace.custom,
|
||||
querySyntax: localData.querySyntax || false,
|
||||
hPath: localData.hPath || "",
|
||||
idPath: localData.idPath || "",
|
||||
list: localData.list || [],
|
||||
replaceList: localData.replaceList || [],
|
||||
group: localData.group || 0,
|
||||
types: localData.types
|
||||
}, dialog.element.querySelector(".b3-dialog__container").lastElementChild, () => {
|
||||
dialog.destroy();
|
||||
})
|
||||
dialog.element.firstElementChild.setAttribute("style", "z-index:199"); // https://github.com/siyuan-note/siyuan/issues/3515
|
||||
const searchFilterElement = dialog.element.querySelector("#searchFilter") as HTMLInputElement;
|
||||
const searchFilterPanelElement = dialog.element.querySelector("#searchFilterPanel") as HTMLInputElement;
|
||||
searchFilterElement.addEventListener("click", () => {
|
||||
if (searchFilterPanelElement.classList.contains("fn__none")) {
|
||||
searchFilterElement.classList.add("ft__primary");
|
||||
searchFilterPanelElement.classList.remove("fn__none");
|
||||
} else {
|
||||
searchFilterElement.classList.remove("ft__primary");
|
||||
searchFilterPanelElement.classList.add("fn__none");
|
||||
}
|
||||
});
|
||||
searchFilterPanelElement.querySelectorAll("input").forEach(item => {
|
||||
item.addEventListener("change", () => {
|
||||
inputEvent();
|
||||
localData.types = {
|
||||
document: (dialog.element.querySelector("#document") as HTMLInputElement).checked,
|
||||
heading: (dialog.element.querySelector("#heading") as HTMLInputElement).checked,
|
||||
list: (dialog.element.querySelector("#list") as HTMLInputElement).checked,
|
||||
listItem: (dialog.element.querySelector("#listItem") as HTMLInputElement).checked,
|
||||
codeBlock: (dialog.element.querySelector("#codeBlock") as HTMLInputElement).checked,
|
||||
htmlBlock: (dialog.element.querySelector("#htmlBlock") as HTMLInputElement).checked,
|
||||
mathBlock: (dialog.element.querySelector("#mathBlock") as HTMLInputElement).checked,
|
||||
table: (dialog.element.querySelector("#table") as HTMLInputElement).checked,
|
||||
blockquote: (dialog.element.querySelector("#blockquote") as HTMLInputElement).checked,
|
||||
superBlock: (dialog.element.querySelector("#superBlock") as HTMLInputElement).checked,
|
||||
paragraph: (dialog.element.querySelector("#paragraph") as HTMLInputElement).checked,
|
||||
};
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
});
|
||||
});
|
||||
const includeChildElement = dialog.element.querySelector("#includeChildCheck");
|
||||
const searchPathElement = dialog.element.querySelector("#searchPathCheck");
|
||||
searchPathElement.addEventListener("click", () => {
|
||||
searchPathElement.classList.toggle("b3-button--cancel");
|
||||
if (searchPathElement.classList.contains("b3-button--cancel") && !includeChildElement.classList.contains("b3-button--cancel")) {
|
||||
includeChildElement.classList.add("b3-button--cancel");
|
||||
}
|
||||
inputEvent();
|
||||
});
|
||||
includeChildElement.addEventListener("click", () => {
|
||||
includeChildElement.classList.toggle("b3-button--cancel");
|
||||
let reload = false;
|
||||
if (includeChildElement.classList.contains("b3-button--cancel")) {
|
||||
if (!localData.idPath.endsWith(".sy")) {
|
||||
localData.idPath = localData.idPath + ".sy";
|
||||
reload = true;
|
||||
}
|
||||
} else {
|
||||
if (searchPathElement.classList.contains("b3-button--cancel")) {
|
||||
searchPathElement.classList.remove("b3-button--cancel");
|
||||
reload = true;
|
||||
}
|
||||
if (localData.idPath.endsWith(".sy")) {
|
||||
localData.idPath = localData.idPath.replace(".sy", "");
|
||||
reload = true;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
inputEvent();
|
||||
}
|
||||
});
|
||||
const searchSyntaxElement = dialog.element.querySelector("#searchSyntaxCheck");
|
||||
searchSyntaxElement.addEventListener("click", () => {
|
||||
searchSyntaxElement.classList.toggle("b3-button--cancel");
|
||||
localData.querySyntax = !searchSyntaxElement.classList.contains("b3-button--cancel");
|
||||
inputEvent();
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
});
|
||||
const searchPanelElement = dialog.element.querySelector("#searchList");
|
||||
const searchInputElement = dialog.element.querySelector("#searchInput") as HTMLInputElement;
|
||||
const replaceInputElement = dialog.element.querySelector("#replaceInput") as HTMLInputElement;
|
||||
searchInputElement.value = localData.k || "";
|
||||
replaceInputElement.value = localData.r || "";
|
||||
searchInputElement.select();
|
||||
const historyElement = dialog.element.querySelector("#searchHistoryList");
|
||||
historyElement.addEventListener("click", (event: Event & { target: HTMLElement }) => {
|
||||
if (event.target.classList.contains("b3-list-item")) {
|
||||
searchInputElement.value = event.target.textContent;
|
||||
searchInputElement.dispatchEvent(new CustomEvent("blur"));
|
||||
historyElement.classList.add("fn__none");
|
||||
inputEvent();
|
||||
}
|
||||
});
|
||||
dialog.element.querySelector("#searchHistoryBtn").addEventListener("click", () => {
|
||||
let html = "";
|
||||
(localData.list || []).forEach((s: string) => {
|
||||
if (s !== searchInputElement.value) {
|
||||
html += `<div class="b3-list-item">${escapeHtml(s)}</div>`;
|
||||
}
|
||||
});
|
||||
historyElement.classList.remove("fn__none");
|
||||
historyElement.innerHTML = html;
|
||||
replaceHistoryElement.classList.add("fn__none");
|
||||
});
|
||||
const replaceHistoryElement = dialog.element.querySelector("#replaceHistoryList");
|
||||
replaceHistoryElement.addEventListener("click", (event: Event & { target: HTMLElement }) => {
|
||||
if (event.target.classList.contains("b3-list-item")) {
|
||||
replaceInputElement.value = event.target.textContent;
|
||||
replaceHistoryElement.classList.add("fn__none");
|
||||
}
|
||||
});
|
||||
dialog.element.querySelector("#replaceHistoryBtn").addEventListener("click", () => {
|
||||
let html = "";
|
||||
(localData.replaceList || []).forEach((s: string) => {
|
||||
if (s !== replaceInputElement.value) {
|
||||
html += `<div class="b3-list-item">${escapeHtml(s)}</div>`;
|
||||
}
|
||||
});
|
||||
replaceHistoryElement.classList.remove("fn__none");
|
||||
replaceHistoryElement.innerHTML = html;
|
||||
historyElement.classList.add("fn__none");
|
||||
});
|
||||
let inputTimeout = 0;
|
||||
const loadingElement = dialog.element.querySelector(".fn__loading--top");
|
||||
const inputEvent = (event?: InputEvent) => {
|
||||
if (event && event.isComposing) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(inputTimeout);
|
||||
inputTimeout = window.setTimeout(() => {
|
||||
loadingElement.classList.remove("fn__none");
|
||||
const inputValue = searchInputElement.value;
|
||||
if (inputValue === "") {
|
||||
fetchPost("/api/block/getRecentUpdatedBlocks", {}, (response) => {
|
||||
onSearch(response.data, dialog);
|
||||
loadingElement.classList.add("fn__none");
|
||||
dialog.element.querySelector("#searchResult").innerHTML = "";
|
||||
});
|
||||
} else {
|
||||
fetchPost("/api/search/fullTextSearchBlock", {
|
||||
query: inputValue,
|
||||
querySyntax: localData.querySyntax,
|
||||
types: {
|
||||
document: (dialog.element.querySelector("#document") as HTMLInputElement).checked,
|
||||
heading: (dialog.element.querySelector("#heading") as HTMLInputElement).checked,
|
||||
list: (dialog.element.querySelector("#list") as HTMLInputElement).checked,
|
||||
listItem: (dialog.element.querySelector("#listItem") as HTMLInputElement).checked,
|
||||
codeBlock: (dialog.element.querySelector("#codeBlock") as HTMLInputElement).checked,
|
||||
htmlBlock: (dialog.element.querySelector("#htmlBlock") as HTMLInputElement).checked,
|
||||
mathBlock: (dialog.element.querySelector("#mathBlock") as HTMLInputElement).checked,
|
||||
table: (dialog.element.querySelector("#table") as HTMLInputElement).checked,
|
||||
blockquote: (dialog.element.querySelector("#blockquote") as HTMLInputElement).checked,
|
||||
superBlock: (dialog.element.querySelector("#superBlock") as HTMLInputElement).checked,
|
||||
paragraph: (dialog.element.querySelector("#paragraph") as HTMLInputElement).checked,
|
||||
},
|
||||
path: !searchPathElement.classList.contains("b3-button--cancel") ? localData.idPath : ""
|
||||
}, (response) => {
|
||||
onSearch(response.data.blocks, dialog);
|
||||
dialog.element.querySelector("#searchResult").innerHTML = window.siyuan.languages.findInDoc.replace("${x}", response.data.matchedRootCount).replace("${y}", response.data.matchedBlockCount);
|
||||
loadingElement.classList.add("fn__none");
|
||||
});
|
||||
}
|
||||
}, Constants.TIMEOUT_SEARCH);
|
||||
};
|
||||
searchInputElement.addEventListener("compositionend", (event: InputEvent) => {
|
||||
inputEvent(event);
|
||||
});
|
||||
searchInputElement.addEventListener("input", (event: InputEvent) => {
|
||||
inputEvent(event);
|
||||
});
|
||||
searchInputElement.addEventListener("blur", () => {
|
||||
let searches: string[] = localData.list || [];
|
||||
searches.splice(0, 0, searchInputElement.value);
|
||||
searches = Array.from(new Set(searches));
|
||||
if (searches.length > window.siyuan.config.search.limit) {
|
||||
searches.splice(window.siyuan.config.search.limit, searches.length - window.siyuan.config.search.limit);
|
||||
}
|
||||
localData.list = searches;
|
||||
localData.k = searchInputElement.value;
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
});
|
||||
searchInputElement.addEventListener("focus", () => {
|
||||
historyElement.classList.add("fn__none");
|
||||
replaceHistoryElement.classList.add("fn__none");
|
||||
});
|
||||
const lineHeight = 28;
|
||||
searchInputElement.addEventListener("keydown", (event: KeyboardEvent) => {
|
||||
let currentList: HTMLElement = searchPanelElement.querySelector(".b3-list-item--focus");
|
||||
if (!currentList || event.isComposing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === "ArrowDown") {
|
||||
currentList.classList.remove("b3-list-item--focus");
|
||||
if (!currentList.nextElementSibling) {
|
||||
searchPanelElement.children[0].classList.add("b3-list-item--focus");
|
||||
} else {
|
||||
currentList.nextElementSibling.classList.add("b3-list-item--focus");
|
||||
}
|
||||
currentList = searchPanelElement.querySelector(".b3-list-item--focus");
|
||||
if (searchPanelElement.scrollTop < currentList.offsetTop - searchPanelElement.clientHeight + lineHeight ||
|
||||
searchPanelElement.scrollTop > currentList.offsetTop) {
|
||||
searchPanelElement.scrollTop = currentList.offsetTop - searchPanelElement.clientHeight + lineHeight;
|
||||
}
|
||||
getArticle({
|
||||
dialog,
|
||||
id: currentList.getAttribute("data-node-id"),
|
||||
k: getKey(currentList),
|
||||
});
|
||||
event.preventDefault();
|
||||
} else if (event.key === "ArrowUp") {
|
||||
currentList.classList.remove("b3-list-item--focus");
|
||||
if (!currentList.previousElementSibling) {
|
||||
const length = searchPanelElement.children.length;
|
||||
searchPanelElement.children[length - 1].classList.add("b3-list-item--focus");
|
||||
} else {
|
||||
currentList.previousElementSibling.classList.add("b3-list-item--focus");
|
||||
}
|
||||
currentList = searchPanelElement.querySelector(".b3-list-item--focus");
|
||||
if (searchPanelElement.scrollTop < currentList.offsetTop - searchPanelElement.clientHeight + lineHeight ||
|
||||
searchPanelElement.scrollTop > currentList.offsetTop - lineHeight * 2) {
|
||||
searchPanelElement.scrollTop = currentList.offsetTop - lineHeight * 2;
|
||||
}
|
||||
getArticle({
|
||||
dialog,
|
||||
id: currentList.getAttribute("data-node-id"),
|
||||
k: getKey(currentList)
|
||||
});
|
||||
event.preventDefault();
|
||||
} else if (event.key === "Enter") {
|
||||
const id = currentList.getAttribute("data-node-id");
|
||||
fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
|
||||
openFileById({
|
||||
id,
|
||||
action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
|
||||
zoomIn: foldResponse.data
|
||||
});
|
||||
dialog.destroy();
|
||||
});
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
const replace = (isAll: boolean) => {
|
||||
const loadElement = replaceInputElement.nextElementSibling;
|
||||
if (!loadElement.classList.contains("fn__none")) {
|
||||
return;
|
||||
}
|
||||
let searches: string[] = localData.replaceList || [];
|
||||
searches.splice(0, 0, replaceInputElement.value);
|
||||
searches = Array.from(new Set(searches));
|
||||
if (searches.length > window.siyuan.config.search.limit) {
|
||||
searches.splice(window.siyuan.config.search.limit, searches.length - window.siyuan.config.search.limit);
|
||||
}
|
||||
localData.replaceList = searches;
|
||||
localData.r = replaceInputElement.value;
|
||||
localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(localData));
|
||||
|
||||
let currentList: HTMLElement = searchPanelElement.querySelector(".b3-list-item--focus");
|
||||
if (!currentList) {
|
||||
return;
|
||||
}
|
||||
loadElement.classList.remove("fn__none");
|
||||
let ids: string[] = [];
|
||||
let rootIds: string[] = [];
|
||||
if (isAll) {
|
||||
searchPanelElement.querySelectorAll(".b3-list-item").forEach(item => {
|
||||
ids.push(item.getAttribute("data-node-id"));
|
||||
rootIds.push(item.getAttribute("data-root-id"));
|
||||
});
|
||||
} else {
|
||||
ids = [currentList.getAttribute("data-node-id")];
|
||||
rootIds = [currentList.getAttribute("data-root-id")];
|
||||
}
|
||||
fetchPost("/api/search/findReplace", {
|
||||
k: getKey(currentList),
|
||||
r: replaceInputElement.value,
|
||||
ids,
|
||||
types: {
|
||||
document: (dialog.element.querySelector("#document") as HTMLInputElement).checked,
|
||||
heading: (dialog.element.querySelector("#heading") as HTMLInputElement).checked,
|
||||
list: (dialog.element.querySelector("#list") as HTMLInputElement).checked,
|
||||
listItem: (dialog.element.querySelector("#listItem") as HTMLInputElement).checked,
|
||||
codeBlock: (dialog.element.querySelector("#codeBlock") as HTMLInputElement).checked,
|
||||
htmlBlock: (dialog.element.querySelector("#htmlBlock") as HTMLInputElement).checked,
|
||||
mathBlock: (dialog.element.querySelector("#mathBlock") as HTMLInputElement).checked,
|
||||
table: (dialog.element.querySelector("#table") as HTMLInputElement).checked,
|
||||
blockquote: (dialog.element.querySelector("#blockquote") as HTMLInputElement).checked,
|
||||
superBlock: (dialog.element.querySelector("#superBlock") as HTMLInputElement).checked,
|
||||
paragraph: (dialog.element.querySelector("#paragraph") as HTMLInputElement).checked,
|
||||
},
|
||||
}, (response) => {
|
||||
loadElement.classList.add("fn__none");
|
||||
if (response.code === 1) {
|
||||
showMessage(response.msg);
|
||||
return;
|
||||
}
|
||||
if (ids.length > 1) {
|
||||
return;
|
||||
}
|
||||
getAllModels().editor.forEach(item => {
|
||||
if (rootIds[0] === item.editor.protyle.block.rootID) {
|
||||
reloadProtyle(item.editor.protyle);
|
||||
}
|
||||
});
|
||||
if (!currentList.nextElementSibling && searchPanelElement.children[0]) {
|
||||
searchPanelElement.children[0].classList.add("b3-list-item--focus");
|
||||
} else {
|
||||
currentList.nextElementSibling.classList.add("b3-list-item--focus");
|
||||
}
|
||||
currentList.remove();
|
||||
if (searchPanelElement.childElementCount === 0) {
|
||||
searchPanelElement.innerHTML = `<div class="b3-list--empty">${window.siyuan.languages.emptyContent}</div>`;
|
||||
protyle.protyle.element.classList.add("fn__none");
|
||||
return;
|
||||
}
|
||||
currentList = searchPanelElement.querySelector(".b3-list-item--focus");
|
||||
if (searchPanelElement.scrollTop < currentList.offsetTop - searchPanelElement.clientHeight + lineHeight ||
|
||||
searchPanelElement.scrollTop > currentList.offsetTop) {
|
||||
searchPanelElement.scrollTop = currentList.offsetTop - searchPanelElement.clientHeight + lineHeight;
|
||||
}
|
||||
getArticle({
|
||||
dialog,
|
||||
id: currentList.getAttribute("data-node-id"),
|
||||
k: getKey(currentList)
|
||||
});
|
||||
});
|
||||
};
|
||||
replaceInputElement.addEventListener("focus", () => {
|
||||
historyElement.classList.add("fn__none");
|
||||
replaceHistoryElement.classList.add("fn__none");
|
||||
});
|
||||
replaceInputElement.addEventListener("keydown", (event: KeyboardEvent) => {
|
||||
if (event.isComposing || event.key !== "Enter") {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
replace(false);
|
||||
});
|
||||
dialog.element.querySelector("#replaceAllBtn").addEventListener("click", () => {
|
||||
replace(true);
|
||||
});
|
||||
dialog.element.querySelector("#replaceBtn").addEventListener("click", () => {
|
||||
replace(false);
|
||||
});
|
||||
let clickTimeout: number;
|
||||
searchPanelElement.addEventListener("click", (event: MouseEvent) => {
|
||||
let target = event.target as HTMLElement;
|
||||
while (target && !target.isEqualNode(dialog.element)) {
|
||||
if (target.getAttribute("data-type") === "search-item") {
|
||||
if (event.detail === 1) {
|
||||
clickTimeout = window.setTimeout(() => {
|
||||
if (window.siyuan.altIsPressed) {
|
||||
const id = target.getAttribute("data-node-id");
|
||||
fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
|
||||
openFileById({
|
||||
id,
|
||||
action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
|
||||
zoomIn: foldResponse.data,
|
||||
position: "right"
|
||||
});
|
||||
dialog.destroy();
|
||||
});
|
||||
} else {
|
||||
searchPanelElement.querySelector(".b3-list-item--focus").classList.remove("b3-list-item--focus");
|
||||
target.classList.add("b3-list-item--focus");
|
||||
getArticle({
|
||||
dialog,
|
||||
id: target.getAttribute("data-node-id"),
|
||||
k: getKey(target)
|
||||
});
|
||||
searchInputElement.focus();
|
||||
}
|
||||
}, Constants.TIMEOUT_DBLCLICK);
|
||||
} else if (event.detail === 2) {
|
||||
clearTimeout(clickTimeout);
|
||||
const id = target.getAttribute("data-node-id");
|
||||
fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
|
||||
openFileById({
|
||||
id,
|
||||
action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
|
||||
zoomIn: foldResponse.data
|
||||
});
|
||||
dialog.destroy();
|
||||
});
|
||||
}
|
||||
window.siyuan.menus.menu.remove();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
break;
|
||||
}
|
||||
target = target.parentElement;
|
||||
}
|
||||
historyElement.classList.add("fn__none");
|
||||
replaceHistoryElement.classList.add("fn__none");
|
||||
}, false);
|
||||
|
||||
inputEvent();
|
||||
};
|
||||
|
||||
const getKey = (element: HTMLElement) => {
|
||||
const keys: string[] = [];
|
||||
element.querySelectorAll("mark").forEach(item => {
|
||||
keys.push(item.textContent);
|
||||
});
|
||||
return [...new Set(keys)].join(" ");
|
||||
};
|
||||
|
||||
const getArticle = (options: {
|
||||
id: string,
|
||||
k: string,
|
||||
dialog: Dialog
|
||||
}) => {
|
||||
fetchPost("/api/block/checkBlockFold", {id: options.id}, (foldResponse) => {
|
||||
if (!protyle) {
|
||||
protyle = new Protyle(options.dialog.element.querySelector("#searchPreview") as HTMLElement, {
|
||||
blockId: options.id,
|
||||
action: foldResponse.data ? [Constants.CB_GET_HL, Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_HTML],
|
||||
key: options.k,
|
||||
render: {
|
||||
gutter: true,
|
||||
breadcrumbDocName: true
|
||||
},
|
||||
after: (editor) => {
|
||||
if (window.siyuan.config.readonly || window.siyuan.config.editor.readOnly) {
|
||||
disabledProtyle(editor.protyle);
|
||||
}
|
||||
setTimeout(() => {
|
||||
const matchElement = protyle.protyle.wysiwyg.element.querySelector(`div[data-node-id="${options.id}"] span[data-type="search-mark"]`);
|
||||
if (matchElement) {
|
||||
matchElement.scrollIntoView();
|
||||
}
|
||||
}, Constants.TIMEOUT_SEARCH);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
protyle.protyle.scroll.lastScrollTop = 0;
|
||||
addLoading(protyle.protyle);
|
||||
fetchPost("/api/filetree/getDoc", {
|
||||
id: options.id,
|
||||
k: options.k,
|
||||
mode: foldResponse.data ? 0 : 3,
|
||||
size: foldResponse.data ? Constants.SIZE_GET_MAX : window.siyuan.config.editor.dynamicLoadBlocks,
|
||||
}, getResponse => {
|
||||
onGet(getResponse, protyle.protyle, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_HL, Constants.CB_GET_HTML]);
|
||||
const matchElement = protyle.protyle.wysiwyg.element.querySelector(`div[data-node-id="${options.id}"] span[data-type="search-mark"]`);
|
||||
if (matchElement) {
|
||||
matchElement.scrollIntoView();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onSearch = (data: IBlock[], dialog: Dialog) => {
|
||||
let resultHTML = "";
|
||||
data.forEach((item, index) => {
|
||||
const title = escapeHtml(getNotebookName(item.box)) + getDisplayName(item.hPath, false);
|
||||
resultHTML += `<div data-type="search-item" class="b3-list-item${index === 0 ? " b3-list-item--focus" : ""}" data-node-id="${item.id}" data-root-id="${item.rootID}">
|
||||
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(item.type)}"></use></svg>
|
||||
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(item.type)}"></use></svg>
|
||||
${unicode2Emoji(item.ial.icon, false, "b3-list-item__graphic", true)}
|
||||
<span class="b3-list-item__text">${item.content}</span>
|
||||
<span class="b3-list-item__meta b3-list-item__meta--ellipsis" title="${title}">${title}</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
if (data[0]) {
|
||||
if (protyle) {
|
||||
protyle.protyle.element.classList.remove("fn__none");
|
||||
} else {
|
||||
dialog.element.querySelector("#searchPreview").classList.remove("fn__none");
|
||||
}
|
||||
const contentElement = document.createElement("div");
|
||||
contentElement.innerHTML = data[0].content;
|
||||
getArticle({
|
||||
dialog,
|
||||
id: data[0].id,
|
||||
k: getKey(contentElement),
|
||||
});
|
||||
} else {
|
||||
if (protyle) {
|
||||
protyle.protyle.element.classList.add("fn__none");
|
||||
} else {
|
||||
dialog.element.querySelector("#searchPreview").classList.add("fn__none");
|
||||
}
|
||||
}
|
||||
dialog.element.querySelector("#searchList").innerHTML = resultHTML || `<div class="b3-list--empty">${window.siyuan.languages.emptyContent}</div>`;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue