diff --git a/app/src/assets/scss/_search.scss b/app/src/assets/scss/_search.scss index 16b440209..17ceef469 100644 --- a/app/src/assets/scss/_search.scss +++ b/app/src/assets/scss/_search.scss @@ -28,7 +28,7 @@ max-width: 70%; max-height: 50vh; overflow: auto; - top: 48px; + top: 42px; position: absolute; left: 8px; } diff --git a/app/src/config/editor.ts b/app/src/config/editor.ts index 854457ad1..9dea7a13d 100644 --- a/app/src/config/editor.ts +++ b/app/src/config/editor.ts @@ -42,9 +42,9 @@ export const editor = { }); allModels.search.forEach(search => { if (readOnly) { - disabledProtyle(search.protyle.protyle); + disabledProtyle(search.edit.protyle); } else { - enableProtyle(search.protyle.protyle); + enableProtyle(search.edit.protyle); } }); window.siyuan.blockPanels.forEach(item => { diff --git a/app/src/constants.ts b/app/src/constants.ts index 166d4071c..003c1c48d 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -58,7 +58,6 @@ export abstract class Constants { // localstorage public static readonly LOCAL_SEARCHEDATA = "local-searchedata"; - public static readonly LOCAL_SEARCHETABDATA = "local-searchetabdata"; public static readonly LOCAL_DOCINFO = "local-docinfo"; // only mobile public static readonly LOCAL_DAILYNOTEID = "local-dailynoteid"; public static readonly LOCAL_HISTORYNOTEID = "local-historynoteid"; diff --git a/app/src/layout/Wnd.ts b/app/src/layout/Wnd.ts index dd799aba0..9d9d536be 100644 --- a/app/src/layout/Wnd.ts +++ b/app/src/layout/Wnd.ts @@ -563,8 +563,8 @@ export class Wnd { return; } if (model instanceof Search) { - if (model.protyle) { - model.protyle.destroy(); + if (model.edit) { + model.edit.destroy(); } return; } diff --git a/app/src/layout/util.ts b/app/src/layout/util.ts index 0b2959061..5da8ffa6e 100644 --- a/app/src/layout/util.ts +++ b/app/src/layout/util.ts @@ -268,7 +268,7 @@ const JSONToCenter = (json: any, layout?: Layout | Wnd | Tab | Model) => { } else if (json.instance === "Search") { (layout as Tab).addModel(new Search({ tab: (layout as Tab), - text: json.text + config: json.config })); } if (json.children) { @@ -377,7 +377,7 @@ export const layoutToJSON = (layout: Layout | Wnd | Tab | Model, json: any) => { json.instance = "Tag"; } else if (layout instanceof Search) { json.instance = "Search"; - json.text = layout.text; + json.config = layout.config; } if (layout instanceof Layout || layout instanceof Wnd) { @@ -524,7 +524,7 @@ export const copyTab = (tab: Tab) => { } else if (tab.model instanceof Search) { model = new Search({ tab: newTab, - text: tab.model.text + config: tab.model.config }); } else if (!tab.model && tab.headElement) { const initData = JSON.parse(tab.headElement.getAttribute("data-initdata") || "{}"); diff --git a/app/src/search/index.ts b/app/src/search/index.ts index 202cf7609..9e687a11f 100644 --- a/app/src/search/index.ts +++ b/app/src/search/index.ts @@ -1,242 +1,20 @@ import {Model} from "../layout/Model"; import {Tab} from "../layout/Tab"; import {Protyle} from "../protyle"; -import {Constants} from "../constants"; -import {getIconByType} from "../editor/getIcon"; -import {getDisplayName, getNotebookName} from "../util/pathName"; -import {escapeHtml} from "../util/escape"; -import {fetchPost} from "../util/fetch"; -import {disabledProtyle, onGet} from "../protyle/util/onGet"; -import {addLoading} from "../protyle/ui/initUI"; -import {unicode2Emoji} from "../emoji"; import {genSearch} from "./util"; export class Search extends Model { - public text: string; private element: HTMLElement; - public protyle: Protyle; - private inputTimeout: number; - private config: ISearchOption; + public config: ISearchOption; + public edit: Protyle; - constructor(options: { tab: Tab, text: string }) { + constructor(options: { tab: Tab, config: ISearchOption }) { super({ id: options.tab.id, }); this.element = options.tab.panelElement as HTMLElement; - options.tab.updateTitle(options.text || ""); - genSearch({ - k: options.text, - r: "", - hasReplace: false, - querySyntax: false, - hPath: "", - notebookId: "", - idPath: "", - types: { - mathBlock: true, - table: true, - blockquote: true, - superBlock: true, - paragraph: true, - document: true, - heading: true, - list: true, - listItem: true, - codeBlock: true, - htmlBlock: true, - } - }, this.element) - - // const historyElement = this.element.querySelector("#searchHistoryList") as HTMLInputElement; - // const lineHeight = 30; - // const searchPanelElement = this.element.querySelector("#globalSearchList"); - // inputElement.addEventListener("keydown", (event) => { - // 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; - // } - // this.getArticle(currentList.getAttribute("data-node-id"), inputElement.value); - // 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; - // } - // this.getArticle(currentList.getAttribute("data-node-id"), inputElement.value); - // event.preventDefault(); - // } - // }); - // inputElement.select(); - // this.inputEvent(inputElement); - // let clickTimeout: number; - // this.element.addEventListener("click", (event: MouseEvent) => { - // setPanelFocus(this.element.parentElement.parentElement); - // let target = event.target as HTMLElement; - // let hideList = true; - // while (target && !target.isEqualNode(this.element)) { - // if (target.id === "globalSearchReload") { - // this.inputEvent(inputElement); - // } else if (target.classList.contains("b3-list-item")) { - // if (target.getAttribute("data-node-id")) { - // 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", - // }); - // }); - // } else { - // this.element.querySelectorAll(".b3-list-item--focus").forEach((item) => { - // item.classList.remove("b3-list-item--focus"); - // }); - // target.classList.add("b3-list-item--focus"); - // this.getArticle(target.getAttribute("data-node-id"), inputElement.value); - // } - // }, 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 - // }); - // }); - // } - // window.siyuan.menus.menu.remove(); - // } else { - // this.text = target.textContent; - // this.parent.updateTitle(this.text); - // inputElement.value = this.text; - // inputElement.select(); - // this.inputEvent(inputElement); - // } - // event.preventDefault(); - // event.stopPropagation(); - // break; - // } else if (target.id === "searchHistoryBtn") { - // hideList = false; - // let html = ""; - // const data = JSON.parse(localStorage.getItem(Constants.LOCAL_SEARCHETABDATA) || "[]"); - // data.forEach((s: string) => { - // if (s !== inputElement.value) { - // html += `
${escapeHtml(s)}
`; - // } - // }); - // historyElement.classList.remove("fn__none"); - // historyElement.innerHTML = html; - // event.preventDefault(); - // event.stopPropagation(); - // break; - // } - // target = target.parentElement; - // } - // if (hideList) { - // historyElement.classList.add("fn__none"); - // } - // }, false); - } - - private getArticle(id: string, value: string) { - fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => { - if (this.protyle) { - this.protyle.protyle.element.classList.remove("fn__none"); - this.protyle.protyle.scroll.lastScrollTop = 0; - addLoading(this.protyle.protyle); - - fetchPost("/api/filetree/getDoc", { - id, - k: value, - mode: foldResponse.data ? 0 : 3, - size: foldResponse.data ? Constants.SIZE_GET_MAX : window.siyuan.config.editor.dynamicLoadBlocks, - }, getResponse => { - onGet(getResponse, this.protyle.protyle, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_HL, Constants.CB_GET_HTML]); - const matchElement = this.protyle.protyle.wysiwyg.element.querySelector(`div[data-node-id="${id}"] span[data-type="search-mark"]`); - if (matchElement) { - matchElement.scrollIntoView(); - } - }); - } else { - this.protyle = new Protyle(this.element.querySelector("#searchPreview") as HTMLElement, { - blockId: 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: value, - render: { - gutter: true, - breadcrumbDocName: true, - }, - after: (editor) => { - if (window.siyuan.config.readonly || window.siyuan.config.editor.readOnly) { - disabledProtyle(editor.protyle); - } - setTimeout(() => { - const matchElement = this.protyle.protyle.wysiwyg.element.querySelector(`div[data-node-id="${id}"] span[data-type="search-mark"]`); - if (matchElement) { - matchElement.scrollIntoView(); - } - }, Constants.TIMEOUT_SEARCH); - } - }); - } - }); - } - - private setLocalStorage(value: string) { - if (!value) { - return; - } - let searches: string[] = JSON.parse(localStorage.getItem(Constants.LOCAL_SEARCHETABDATA) || "[]"); - searches.splice(0, 0, 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); - } - localStorage.setItem(Constants.LOCAL_SEARCHETABDATA, JSON.stringify(searches)); - } - - private inputEvent(inputElement: HTMLInputElement, event?: InputEvent) { - if (event && event.isComposing) { - return; - } - clearTimeout(this.inputTimeout); - const loadElement = this.element.querySelector(".fn__loading--top"); - this.inputTimeout = window.setTimeout(() => { - this.text = inputElement.value; - this.parent.updateTitle(this.text); - loadElement.classList.remove("fn__none"); - fetchPost("/api/search/fullTextSearchBlock", {query: this.text}, (response) => { - this.onSearch(response.data.blocks); - this.element.querySelector("#globalSearchResult").innerHTML = window.siyuan.languages.findInDoc.replace("${x}", response.data.matchedRootCount).replace("${y}", response.data.matchedBlockCount); - loadElement.classList.add("fn__none"); - }); - }, Constants.TIMEOUT_SEARCH); + this.config = options.config; + this.edit = genSearch(this.config, this.element) } public updateSearch(text: string, replace: boolean) { @@ -256,31 +34,8 @@ export class Search extends Model { text = oldText + " " + text; } } - this.text = text; - this.parent.updateTitle(this.text); - inputElement.value = this.text; + inputElement.value = text; inputElement.select(); - this.inputEvent(inputElement); - this.setLocalStorage(text); - } - - private onSearch(data: IBlock[]) { - let resultHTML = ""; - data.forEach((item, index) => { - const title = escapeHtml(getNotebookName(item.box)) + getDisplayName(item.hPath, false); - resultHTML += `
- - ${unicode2Emoji(item.ial.icon)}${item.ial.icon ? " " : ""}${item.content} - ${title} -
`; - }); - this.element.querySelector("#globalSearchList").innerHTML = resultHTML || `
${window.siyuan.languages.emptyContent}
`; - if (data.length === 0) { - if (this.protyle) { - this.protyle.protyle.element.classList.add("fn__none"); - } - return; - } - this.getArticle(data[0].id, (this.element.querySelector(".b3-text-field") as HTMLInputElement).value); + inputElement.dispatchEvent(new CustomEvent("input")); } } diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 9d92d60aa..ce8a4adfb 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -38,9 +38,34 @@ export const openGlobalSearch = (text: string, replace: boolean) => { icon: "iconSearch", title: text, callback(tab) { + const localData = JSON.parse(localStorage.getItem(Constants.LOCAL_SEARCHEDATA) || "{}"); + if (!localData.types) { + localData.types = { + document: window.siyuan.config.search.document, + heading: window.siyuan.config.search.heading, + list: window.siyuan.config.search.list, + listItem: window.siyuan.config.search.listItem, + codeBlock: window.siyuan.config.search.codeBlock, + htmlBlock: window.siyuan.config.search.htmlBlock, + mathBlock: window.siyuan.config.search.mathBlock, + table: window.siyuan.config.search.table, + blockquote: window.siyuan.config.search.blockquote, + superBlock: window.siyuan.config.search.superBlock, + paragraph: window.siyuan.config.search.paragraph, + }; + } const asset = new Search({ tab, - text + config: { + k: text, + r: "", + hasReplace: false, + querySyntax: localData.querySyntax || false, + hPath: "", + notebookId: "", + idPath: "", + types: localData.types + } }); tab.addModel(asset); resizeTabs(); @@ -151,17 +176,17 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () break; } else if (target.id === "searchFilter") { window.siyuan.menus.menu.remove(); - addConfigMenu(config, window.siyuan.languages.math, "mathBlock", edit, element); - addConfigMenu(config, window.siyuan.languages.table, "table", edit, element); - addConfigMenu(config, window.siyuan.languages.quote, "blockquote", edit, element); - addConfigMenu(config, window.siyuan.languages.superBlock, "superBlock", edit, element); - addConfigMenu(config, window.siyuan.languages.paragraph, "paragraph", edit, element); - addConfigMenu(config, window.siyuan.languages.doc, "document", edit, element); - addConfigMenu(config, window.siyuan.languages.headings, "heading", edit, element); - addConfigMenu(config, window.siyuan.languages.list1, "list", edit, element); - addConfigMenu(config, window.siyuan.languages.listItem, "listItem", edit, element); - addConfigMenu(config, window.siyuan.languages.code, "codeBlock", edit, element); - addConfigMenu(config, "HTML", "htmlBlock", edit, element); + addConfigMenu(config, window.siyuan.languages.math, "mathBlock", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.table, "table", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.quote, "blockquote", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.superBlock, "superBlock", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.paragraph, "paragraph", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.doc, "document", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.headings, "heading", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.list1, "list", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.listItem, "listItem", edit, element, closeCB); + addConfigMenu(config, window.siyuan.languages.code, "codeBlock", edit, element, closeCB); + addConfigMenu(config, "HTML", "htmlBlock", edit, element, closeCB); window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true); event.stopPropagation(); event.preventDefault(); @@ -184,7 +209,9 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } } if (reload) { - localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } inputTimeout = inputEvent(element, config, inputTimeout, edit); } event.stopPropagation(); @@ -194,7 +221,9 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () target.classList.toggle("b3-button--cancel"); config.querySyntax = !target.classList.contains("b3-button--cancel"); inputTimeout = inputEvent(element, config, inputTimeout, edit); - localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } event.stopPropagation(); event.preventDefault(); break; @@ -226,11 +255,17 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () return; } else if (target.id === "replaceAllBtn") { replace(element, config, edit, true); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } event.stopPropagation(); event.preventDefault(); break; } else if (target.id === "replaceBtn") { replace(element, config, edit, false); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } event.stopPropagation(); event.preventDefault(); break; @@ -242,6 +277,14 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } else if (target.parentElement.id === "replaceHistoryList") { replaceInputElement.value = target.textContent; replaceHistoryElement.classList.add("fn__none"); + } else if (target.parentElement.id === "searchList" && !target.classList.contains("b3-list-item--focus")) { + target.parentElement.querySelector(".b3-list-item--focus")?.classList.remove("b3-list-item--focus") + target.classList.add("b3-list-item--focus") + getArticle({ + id: target.getAttribute("data-node-id"), + k: getKey(target), + edit + }); } event.stopPropagation(); event.preventDefault(); @@ -313,7 +356,9 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () } config.list = searches; config.k = searchInputElement.value; - localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } }); searchInputElement.addEventListener("focus", () => { historyElement.classList.add("fn__none"); @@ -385,13 +430,17 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () return; } replace(element, config, edit, false); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } event.preventDefault(); }); inputTimeout = inputEvent(element, config, inputTimeout, edit); + return edit; } const addConfigMenu = (config: ISearchOption, lang: string, key: "mathBlock" | "table" | "blockquote" | "superBlock" | "paragraph" | "document" | "heading" | "list" | "listItem" | "codeBlock" | "htmlBlock", - edit: Protyle, element: Element) => { + edit: Protyle, element: Element, closeCB?: () => void) => { window.siyuan.menus.menu.append(new MenuItem({ label: `
${lang}
`, @@ -403,7 +452,9 @@ const addConfigMenu = (config: ISearchOption, lang: string, key: "mathBlock" | " } config.types[key] = inputElement.checked; inputEvent(element, config, undefined, edit); - localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + if (closeCB) { + localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); + } window.siyuan.menus.menu.remove(); }); } @@ -458,7 +509,6 @@ const replace = (element: Element, config: ISearchOption, edit: Protyle, isAll: } config.replaceList = searches; config.r = replaceInputElement.value; - localStorage.setItem(Constants.LOCAL_SEARCHEDATA, JSON.stringify(config)); let currentList: HTMLElement = searchPanelElement.querySelector(".b3-list-item--focus"); if (!currentList) {