diff --git a/app/src/assets/scss/component/_chip.scss b/app/src/assets/scss/component/_chip.scss index 0e917a738..4f37755fc 100644 --- a/app/src/assets/scss/component/_chip.scss +++ b/app/src/assets/scss/component/_chip.scss @@ -89,6 +89,7 @@ a.b3-chip:hover { transition: var(--b3-transition); cursor: pointer; opacity: .68; + flex-shrink: 0; &:hover { // 不明原因 https://github.com/siyuan-note/siyuan/issues/7756#issuecomment-1484057954 diff --git a/app/src/constants.ts b/app/src/constants.ts index 5fce363d6..cd2fc43b6 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -67,7 +67,6 @@ export abstract class Constants { public static readonly LOCAL_ZOOM = "local-zoom"; public static readonly LOCAL_SEARCHDATA = "local-searchdata"; public static readonly LOCAL_SEARCHKEYS = "local-searchkeys"; - public static readonly LOCAL_SEARCHKEY = "local-searchkey"; // only mobile public static readonly LOCAL_DOCINFO = "local-docinfo"; // only mobile public static readonly LOCAL_DAILYNOTEID = "local-dailynoteid"; // string public static readonly LOCAL_HISTORYNOTEID = "local-historynoteid"; // string diff --git a/app/src/mobile/menu/search.ts b/app/src/mobile/menu/search.ts index 396b6f785..7598113cb 100644 --- a/app/src/mobile/menu/search.ts +++ b/app/src/mobile/menu/search.ts @@ -4,10 +4,71 @@ import {Constants} from "../../constants"; import {fetchPost} from "../../util/fetch"; import {getIconByType} from "../../editor/getIcon"; import {preventScroll} from "../../protyle/scroll/preventScroll"; -import {setStorageVal} from "../../protyle/util/compatibility"; import {openModel} from "./model"; +import {getNotebookName, movePathTo, pathPosix} from "../../util/pathName"; +import {filterMenu, initCriteriaMenu, moreMenu, queryMenu} from "../../search/menu"; +import {setStorageVal} from "../../protyle/util/compatibility"; +import {escapeHtml} from "../../util/escape"; -const onRecentBlocks = (data: IBlock[], matchedRootCount?:number, matchedBlockCount?:number) => { +const updateConfig = (element: Element, newConfig: ISearchOption, config: ISearchOption) => { + newConfig.hPath = config.hPath; + newConfig.idPath = config.idPath.join(",").split(","); + if (config.hasReplace !== newConfig.hasReplace) { + if (newConfig.hasReplace) { + element.querySelector('[data-type="toggle-replace"]').classList.add("toolbar__icon--active"); + element.querySelector(".toolbar").classList.remove("fn__none"); + } else { + element.querySelector('[data-type="toggle-replace"]').classList.remove("toolbar__icon--active"); + element.querySelector(".toolbar").classList.add("fn__none"); + } + } + const searchPathElement = element.querySelector("#searchPath"); + if (newConfig.hPath) { + searchPathElement.classList.remove("fn__none") + searchPathElement.innerHTML = `
${escapeHtml(newConfig.hPath)}
`; + } else { + searchPathElement.classList.add("fn__none") + } + if (config.group !== newConfig.group) { + if (newConfig.group === 0) { + element.querySelector('[data-type="expand"]').classList.add("fn__none"); + element.querySelector('[data-type="contract"]').classList.add("fn__none"); + } else { + element.querySelector('[data-type="expand"]').classList.remove("fn__none"); + element.querySelector('[data-type="contract"]').classList.remove("fn__none"); + } + } + let includeChild = true; + let enableIncludeChild = false; + newConfig.idPath.forEach(newConfig => { + if (newConfig.endsWith(".sy")) { + includeChild = false; + } + if (newConfig.split("/").length > 1) { + enableIncludeChild = true; + } + }); + const searchIncludeElement = element.querySelector('[data-type="include"]'); + if (includeChild) { + searchIncludeElement.classList.add("toolbar__icon--active"); + } else { + searchIncludeElement.classList.remove("toolbar__icon--active"); + } + if (enableIncludeChild) { + searchIncludeElement.removeAttribute("disabled"); + } else { + searchIncludeElement.setAttribute("disabled", "disabled"); + } + (document.querySelector("#toolbarSearch") as HTMLInputElement).value = newConfig.k; + (element.querySelector("#toolbarReplace") as HTMLInputElement).value = newConfig.r; + 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); + window.siyuan.menus.menu.remove(); +}; + +const onRecentBlocks = (data: IBlock[], matchedRootCount?: number, matchedBlockCount?: number) => { let resultHTML = ""; if (matchedBlockCount) { resultHTML = '
' + window.siyuan.languages.findInDoc.replace("${x}", matchedRootCount).replace("${y}", matchedBlockCount) + "
"; @@ -21,12 +82,11 @@ const onRecentBlocks = (data: IBlock[], matchedRootCount?:number, matchedBlockCo
${Lute.EscapeHTMLStr(item.hPath)}
`; }); - document.querySelector("#searchPanel").innerHTML = resultHTML; + document.querySelector("#searchList").innerHTML = resultHTML; }; - let toolbarSearchTimeout = 0; -export const toolbarSearchEvent = () => { +export const updateSearchResult = (config: ISearchOption) => { clearTimeout(toolbarSearchTimeout); toolbarSearchTimeout = window.setTimeout(() => { const inputElement = document.getElementById("toolbarSearch") as HTMLInputElement; @@ -36,75 +96,305 @@ export const toolbarSearchEvent = () => { }); } else { fetchPost("/api/search/fullTextSearchBlock", {query: inputElement.value,}, (response) => { - onRecentBlocks(response.data.blocks, response.data.matchedRootCount,response.data.matchedBlockCount); + onRecentBlocks(response.data.blocks, response.data.matchedRootCount, response.data.matchedBlockCount); }); } - window.siyuan.storage[Constants.LOCAL_SEARCHKEY] = inputElement.value; - setStorageVal(Constants.LOCAL_SEARCHKEY, window.siyuan.storage[Constants.LOCAL_SEARCHKEY]); }, Constants.TIMEOUT_SEARCH); + return toolbarSearchTimeout }; -const initToolbarSearch = () => { - const inputElement = document.getElementById("toolbarSearch") as HTMLInputElement; - inputElement.focus(); - inputElement.value = window.siyuan.storage[Constants.LOCAL_SEARCHKEY] || ""; - inputElement.addEventListener("compositionend", (event: InputEvent) => { +const initSearchEvent = (element: Element, config: ISearchOption) => { + const searchInputElement = document.getElementById("toolbarSearch") as HTMLInputElement; + searchInputElement.value = config.k || ""; + searchInputElement.addEventListener("compositionend", (event: InputEvent) => { if (event && event.isComposing) { return; } - toolbarSearchEvent(); + updateSearchResult(config); }); - inputElement.addEventListener("input", (event: InputEvent) => { + searchInputElement.addEventListener("input", (event: InputEvent) => { if (event && event.isComposing) { return; } - toolbarSearchEvent(); + updateSearchResult(config); }); + const replaceInputElement = element.querySelector(".toolbar .b3-text-field") as HTMLInputElement + replaceInputElement.value = config.r || "" + + const criteriaData: ISearchOption[] = []; + initCriteriaMenu(element.querySelector("#criteria"), criteriaData); + + element.addEventListener("click", (event: MouseEvent) => { + let target = event.target as HTMLElement; + while (target && !target.isSameNode(element)) { + const type = target.getAttribute("data-type"); + if (type === "set-criteria") { + config.removed = false; + criteriaData.find(item => { + if (item.name === target.innerText.trim()) { + updateConfig(element, item, config); + return true; + } + }); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "remove-criteria") { + const name = target.parentElement.innerText.trim(); + fetchPost("/api/storage/removeCriterion", {name}); + criteriaData.find((item, index) => { + if (item.name === name) { + criteriaData.splice(index, 1); + return true; + } + }); + if (target.parentElement.parentElement.childElementCount === 1) { + target.parentElement.parentElement.classList.add("fn__none"); + target.parentElement.remove(); + } else { + target.parentElement.remove(); + } + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "remove-path") { + config.idPath = []; + config.hPath = ""; + element.querySelector("#searchPath").classList.add("fn__none") + toolbarSearchTimeout = updateSearchResult(config); + 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"); + // } + // }); + 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"); + // } + // }); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "path") { + movePathTo((toPath, toNotebook) => { + fetchPost("/api/filetree/getHPathsByPaths", {paths: toPath}, (response) => { + config.idPath = []; + const hPathList: string[] = []; + let enableIncludeChild = false; + toPath.forEach((item, index) => { + if (item === "/") { + config.idPath.push(toNotebook[index]); + hPathList.push(getNotebookName(toNotebook[index])); + } else { + enableIncludeChild = true; + config.idPath.push(pathPosix().join(toNotebook[index], item.replace(".sy", ""))); + } + }); + if (response.data) { + 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) { + includeElement.removeAttribute("disabled"); + } else { + includeElement.setAttribute("disabled", "disabled"); + } + toolbarSearchTimeout = updateSearchResult(config); + }); + }, [], 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 { + config.idPath.forEach((item, index) => { + if (item.endsWith(".sy")) { + config.idPath[index] = item.replace(".sy", ""); + } + }); + } + // inputTimeout = inputEvent(element, config, inputTimeout, edit); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "toggle-replace") { + config.hasReplace = !config.hasReplace; + replaceInputElement.parentElement.classList.toggle("fn__none"); + target.classList.toggle("toolbar__icon--active"); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "more") { + moreMenu(config, criteriaData, element, () => { + updateSearchResult(config); + }, () => { + updateConfig(element, { + removed: true, + sort: 0, + group: 0, + hasReplace: false, + method: 0, + hPath: "", + idPath: [], + k: "", + r: "", + 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, + embedBlock: window.siyuan.config.search.embedBlock, + } + }, config); + }) + window.siyuan.menus.menu.element.style.zIndex = "220"; + window.siyuan.menus.menu.fullscreen(); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "filter") { + filterMenu(config, () => { + updateSearchResult(config) + }); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (type === "query") { + queryMenu(config, () => { + updateSearchResult(config) + }); + 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); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (target.id === "replaceBtn") { + // replace(element, config, edit, false); + event.stopPropagation(); + event.preventDefault(); + break; + } else if (target.classList.contains("b3-list-item__toggle")) { + target.parentElement.nextElementSibling.classList.toggle("fn__none"); + target.firstElementChild.classList.toggle("b3-list-item__arrow--open"); + event.stopPropagation(); + 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); + } else if (target.getAttribute("data-type") === "search-item") { + const id = target.getAttribute("data-id"); + if (window.siyuan.mobile.editor.protyle) { + preventScroll(window.siyuan.mobile.editor.protyle); + } + fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => { + openMobileFileById(id, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]); + }); + closePanel(); + } else if (target.querySelector(".b3-list-item__toggle")) { + target.nextElementSibling.classList.toggle("fn__none"); + target.firstElementChild.firstElementChild.classList.toggle("b3-list-item__arrow--open"); + } + event.stopPropagation(); + event.preventDefault(); + break; + } + target = target.parentElement; + } + }, false); }; -export const popSearch = () => { +export const popSearch = (config = window.siyuan.storage[Constants.LOCAL_SEARCHDATA] as ISearchOption) => { + let includeChild = true; + let enableIncludeChild = false; + config.idPath.forEach(item => { + if (item.endsWith(".sy")) { + includeChild = false; + } + if (item.split("/").length > 1) { + enableIncludeChild = true; + } + }); + openModel({ - title: ``, - icon:"iconSearch", + title: ``, + icon: "iconSearch", html: `
-
+
+ + +
+ +
+ +
+
+
+
+
+
+ ${escapeHtml(config.hPath)} + +
+
- - - - - - - - + + + + + + + +
`, - bindEvent() { - initToolbarSearch(); - const searchElement = document.getElementById("searchPanel"); - // 不能使用 getEventName() https://ld246.com/article/1638887457149 - searchElement.addEventListener("click", (event) => { - let target = event.target as HTMLElement; - while (target && !target.isEqualNode(searchElement)) { - if (target.classList.contains("b3-list-item")) { - const id = target.getAttribute("data-id"); - if (window.siyuan.mobile.editor.protyle) { - preventScroll(window.siyuan.mobile.editor.protyle); - } - fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => { - openMobileFileById(id,foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]); - }); - closePanel(); - event.preventDefault(); - event.stopPropagation(); - break; - } - target = target.parentElement; - } - }, false); - toolbarSearchEvent(); + bindEvent(element) { + initSearchEvent(element.firstElementChild, config); + toolbarSearchTimeout = updateSearchResult(config); } }); }; diff --git a/app/src/mobile/util/MobileTags.ts b/app/src/mobile/util/MobileTags.ts index 3201e91dd..f9fc29f53 100644 --- a/app/src/mobile/util/MobileTags.ts +++ b/app/src/mobile/util/MobileTags.ts @@ -5,7 +5,8 @@ import {MenuItem} from "../../menus/Menu"; import {Dialog} from "../../dialog"; import {confirmDialog} from "../../dialog/confirmDialog"; import {escapeHtml} from "../../util/escape"; -import {popSearch, toolbarSearchEvent} from "../menu/search"; +import {popSearch} from "../menu/search"; +import {Constants} from "../../constants"; export class MobileTags { public element: HTMLElement; @@ -65,9 +66,9 @@ export class MobileTags { }); } } else { - popSearch(); - (document.getElementById("toolbarSearch") as HTMLInputElement).value = `#${element.getAttribute("data-label")}#`; - toolbarSearchEvent(); + const searchOption = Object.assign({}, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]); + searchOption.k = `#${element.getAttribute("data-label")}#` + popSearch(searchOption); } }, topExtHTML: window.siyuan.config.readonly ? undefined : '' diff --git a/app/src/protyle/util/compatibility.ts b/app/src/protyle/util/compatibility.ts index 746fe8ad9..584289946 100644 --- a/app/src/protyle/util/compatibility.ts +++ b/app/src/protyle/util/compatibility.ts @@ -154,7 +154,11 @@ export const getLocalStorage = (cb: () => void) => { rowTab: "", layoutTab: 0 }; - defaultStorage[Constants.LOCAL_PDFTHEME] = {light: "light", dark: "dark", annoColor: "var(--b3-pdf-background1)"}; + defaultStorage[Constants.LOCAL_PDFTHEME] = { + light: "light", + dark: "dark", + annoColor: "var(--b3-pdf-background1)" + }; defaultStorage[Constants.LOCAL_LAYOUTS] = []; // {name: "", layout:{}} defaultStorage[Constants.LOCAL_AI] = []; // {name: "", memo: ""} defaultStorage[Constants.LOCAL_BAZAAR] = { @@ -206,16 +210,15 @@ export const getLocalStorage = (cb: () => void) => { } }; defaultStorage[Constants.LOCAL_ZOOM] = 1; - defaultStorage[Constants.LOCAL_SEARCHKEY] = ""; [Constants.LOCAL_EXPORTIMG, Constants.LOCAL_SEARCHKEYS, Constants.LOCAL_PDFTHEME, Constants.LOCAL_BAZAAR, Constants.LOCAL_EXPORTWORD, Constants.LOCAL_EXPORTPDF, Constants.LOCAL_DOCINFO, Constants.LOCAL_FONTSTYLES, Constants.LOCAL_SEARCHDATA, - Constants.LOCAL_ZOOM, Constants.LOCAL_SEARCHKEY, Constants.LOCAL_LAYOUTS, Constants.LOCAL_AI].forEach((key) => { + Constants.LOCAL_ZOOM, Constants.LOCAL_LAYOUTS, Constants.LOCAL_AI].forEach((key) => { if (typeof response.data[key] === "string") { try { window.siyuan.storage[key] = Object.assign(defaultStorage[key], JSON.parse(response.data[key])); } catch (e) { - window.siyuan.storage[key] = key === Constants.LOCAL_SEARCHKEY ? (response.data[key] || "") : defaultStorage[key]; + window.siyuan.storage[key] = defaultStorage[key]; } } else if (typeof response.data[key] === "undefined") { window.siyuan.storage[key] = defaultStorage[key]; @@ -226,7 +229,7 @@ export const getLocalStorage = (cb: () => void) => { // 数据兼容,移除历史数据,3.8.4 移除 fetchPost("/api/storage/removeLocalStorageVals", { app: Constants.SIYUAN_APPID, - keys:["leftColumn", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"] + keys: ["leftColumn", "local-searchkey", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"] }); }); }; diff --git a/app/src/search/menu.ts b/app/src/search/menu.ts new file mode 100644 index 000000000..7af5fef26 --- /dev/null +++ b/app/src/search/menu.ts @@ -0,0 +1,373 @@ +import {Dialog} from "../dialog"; +import {isMobile} from "../util/functions"; +import {MenuItem} from "../menus/Menu"; +import {Constants} from "../constants"; +import {showMessage} from "../dialog/message"; +import {fetchPost} from "../util/fetch"; +import {escapeHtml} from "../util/escape"; + +export const filterMenu = (config: ISearchOption, cb: () => void) => { + const filterDialog = new Dialog({ + title: window.siyuan.languages.type, + content: `
+ + + + + + + + + + + + +
+
+
+ +
`, + width: isMobile() ? "90vw" : "520px", + }); + const btnsElement = filterDialog.element.querySelectorAll(".b3-button"); + btnsElement[0].addEventListener("click", () => { + filterDialog.destroy(); + }); + btnsElement[1].addEventListener("click", () => { + filterDialog.element.querySelectorAll(".b3-switch").forEach((item: HTMLInputElement) => { + config.types[item.getAttribute("data-type") as TSearchFilter] = item.checked; + }); + cb(); + filterDialog.destroy(); + }); +}; + +export const queryMenu = (config: ISearchOption, cb: () => void) => { + window.siyuan.menus.menu.remove(); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.keyword, + current: config.method === 0, + click() { + config.method = 0; + cb(); + } + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.querySyntax, + current: config.method === 1, + click() { + config.method = 1; + cb(); + } + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: "SQL", + current: config.method === 2, + click() { + config.method = 2; + cb(); + } + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.regex, + current: config.method === 3, + click() { + config.method = 3; + cb(); + } + }).element); +}; + +export const moreMenu = async (config: ISearchOption, + criteriaData: ISearchOption[], + element: Element, + cb: () => void, + removeCriterion: () => void, + layoutMenu?: () => void) => { + window.siyuan.menus.menu.remove(); + const sortMenu = [{ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.type, + current: config.sort === 0, + click() { + config.sort = 0; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.createdASC, + current: config.sort === 1, + click() { + config.sort = 1; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.createdDESC, + current: config.sort === 2, + click() { + config.sort = 2; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.modifiedASC, + current: config.sort === 3, + click() { + config.sort = 3; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.modifiedDESC, + current: config.sort === 4, + click() { + config.sort = 4; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.sortByRankAsc, + current: config.sort === 6, + click() { + config.sort = 6; + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.sortByRankDesc, + current: config.sort === 7, + click() { + config.sort = 7; + cb(); + } + }]; + if (config.group === 1) { + sortMenu.push({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.sortByContent, + current: config.sort === 5, + click() { + config.sort = 5; + cb(); + } + }); + } + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.sort, + type: "submenu", + submenu: sortMenu, + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.group, + type: "submenu", + submenu: [{ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.noGroupBy, + current: config.group === 0, + click() { + if (isMobile()) { + element.querySelector('[data-type="expand"]').classList.add("fn__none"); + element.querySelector('[data-type="contract"]').classList.add("fn__none"); + } else { + element.querySelector("#searchCollapse").parentElement.classList.add("fn__none"); + } + config.group = 0; + if (config.sort === 5) { + config.sort = 0; + } + cb(); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.groupByDoc, + current: config.group === 1, + click() { + if (isMobile()) { + element.querySelector('[data-type="expand"]').classList.remove("fn__none"); + element.querySelector('[data-type="contract"]').classList.remove("fn__none"); + } else { + element.querySelector("#searchCollapse").parentElement.classList.remove("fn__none"); + } + config.group = 1; + cb(); + } + }] + }).element); + if (layoutMenu) { + layoutMenu(); + } + window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); + window.siyuan.menus.menu.append(new MenuItem({ + label: window.siyuan.languages.saveCriterion, + iconHTML: Constants.ZWSP, + click() { + const saveDialog = new Dialog({ + title: window.siyuan.languages.saveCriterion, + content: `
+ +
+
+
+ +
`, + width: isMobile() ? "80vw" : "520px", + }); + const btnsElement = saveDialog.element.querySelectorAll(".b3-button"); + saveDialog.bindInput(saveDialog.element.querySelector("input"), () => { + btnsElement[1].dispatchEvent(new CustomEvent("click")); + }); + btnsElement[0].addEventListener("click", () => { + saveDialog.destroy(); + }); + btnsElement[1].addEventListener("click", () => { + const value = saveDialog.element.querySelector("input").value; + if (!value) { + showMessage(window.siyuan.languages["_kernel"]["142"]); + return; + } + if (isMobile()) { + config.k = (document.querySelector("#toolbarSearch") as HTMLInputElement).value; + config.r = (element.querySelector("#toolbarReplace") as HTMLInputElement).value; + } else { + config.k = (element.querySelector("#searchInput") as HTMLInputElement).value; + config.r = (element.querySelector("#replaceInput") as HTMLInputElement).value; + } + const criterion = config; + criterion.name = value; + criteriaData.push(Object.assign({}, criterion)); + fetchPost("/api/storage/setCriterion", {criterion}, () => { + saveDialog.destroy(); + const criteriaElement = element.querySelector("#criteria"); + criteriaElement.classList.remove("fn__none"); + criteriaElement.insertAdjacentHTML("beforeend", `
${criterion.name}
`); + }); + }); + } + }).element); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.removeCriterion, + click() { + removeCriterion(); + } + }).element); +}; + +export const initCriteriaMenu = (element: HTMLElement, data: ISearchOption[]) => { + fetchPost("/api/storage/getCriteria", {}, (response) => { + let html = ""; + response.data.forEach((item: ISearchOption, index: number) => { + data.push(item); + html += `
${escapeHtml(item.name)}
`; + }); + element.innerHTML = html; + if (html === "") { + element.classList.add("fn__none"); + } else { + element.classList.remove("fn__none"); + } + }); +}; diff --git a/app/src/search/util.ts b/app/src/search/util.ts index 1fda62a1b..993f51a37 100644 --- a/app/src/search/util.ts +++ b/app/src/search/util.ts @@ -23,22 +23,7 @@ import {replaceFileName} from "../editor/rename"; import {hideElements} from "../protyle/ui/hideElements"; import {getNewFilePath} from "../util/newFile"; import {matchHotKey} from "../protyle/util/hotKey"; - -const appendCriteria = (element: HTMLElement, data: ISearchOption[]) => { - fetchPost("/api/storage/getCriteria", {}, (response) => { - let html = ""; - response.data.forEach((item: ISearchOption, index: number) => { - data.push(item); - html += `
${escapeHtml(item.name)}
`; - }); - element.innerHTML = html; - if (html === "") { - element.classList.add("fn__none"); - } else { - element.classList.remove("fn__none"); - } - }); -}; +import {filterMenu, initCriteriaMenu, moreMenu, queryMenu} from "./menu"; const saveKeyList = (type: "keys" | "replaceKeys", value: string) => { let list: string[] = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS][type]; @@ -223,7 +208,7 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: ()
`; const criteriaData: ISearchOption[] = []; - appendCriteria(element.querySelector("#criteria"), criteriaData); + initCriteriaMenu(element.querySelector("#criteria"), criteriaData); const searchPanelElement = element.querySelector("#searchList"); const searchInputElement = element.querySelector("#searchInput") as HTMLInputElement; const replaceInputElement = element.querySelector("#replaceInput") as HTMLInputElement; @@ -472,18 +457,102 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () event.preventDefault(); break; } else if (target.id === "searchMore") { - addConfigMoreMenu(config, edit, element, event, criteriaData); + moreMenu(config, criteriaData, element, () => { + inputEvent(element, config, undefined, edit); + }, ()=> { + updateConfig(element, { + removed: true, + sort: 0, + group: 0, + hasReplace: false, + method: 0, + hPath: "", + idPath: [], + k: "", + r: "", + 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, + embedBlock: window.siyuan.config.search.embedBlock, + } + }, config, edit); + }, () => { + const localData = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]; + const isPopover = hasClosestByClassName(element, "b3-dialog__container"); + window.siyuan.menus.menu.append(new MenuItem({ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.layout, + type: "submenu", + submenu: [{ + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.topBottomLayout, + current: isPopover ? localData.layout === 0 : localData.layoutTab === 0, + click() { + element.querySelector(".search__layout").classList.remove("search__layout--row"); + edit.protyle.element.style.width = ""; + if ((isPopover && localData.row) || (!isPopover && localData.rowTab)) { + edit.protyle.element.style.height = isPopover ? localData.row : localData.rowTab; + edit.protyle.element.classList.remove("fn__flex-1"); + } else { + edit.protyle.element.classList.add("fn__flex-1"); + } + setPadding(edit.protyle); + if (isPopover) { + localData.layout = 0; + } else { + localData.layoutTab = 0; + } + setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]); + } + }, { + iconHTML: Constants.ZWSP, + label: window.siyuan.languages.leftRightLayout, + current: isPopover ? localData.layout === 1 : localData.layoutTab === 1, + click() { + element.querySelector(".search__layout").classList.add("search__layout--row"); + edit.protyle.element.style.height = ""; + if ((isPopover && localData.col) || (!isPopover && localData.colTab)) { + edit.protyle.element.style.width = isPopover ? localData.col : localData.colTab; + edit.protyle.element.classList.remove("fn__flex-1"); + } else { + edit.protyle.element.classList.add("fn__flex-1"); + } + setPadding(edit.protyle); + if (isPopover) { + localData.layout = 1; + } else { + localData.layoutTab = 1; + } + setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]); + } + }] + }).element); + }); + window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true); event.stopPropagation(); event.preventDefault(); break; } else if (target.id === "searchFilter") { - addConfigFilterMenu(config, edit, element); + filterMenu(config, () => { + inputEvent(element, config, undefined, edit); + }); event.stopPropagation(); event.preventDefault(); break; } else if (target.id === "searchSyntaxCheck") { - window.siyuan.menus.menu.remove(); - addQueryMenu(config, edit, element); + queryMenu(config, () => { + element.querySelector("#searchSyntaxCheck").setAttribute("aria-label", getQueryTip(config.method)); + inputEvent(element, config, undefined, edit); + }); window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true); event.stopPropagation(); event.preventDefault(); @@ -729,235 +798,24 @@ export const genSearch = (config: ISearchOption, element: Element, closeCB?: () return edit; }; -const addConfigMoreMenu = async (config: ISearchOption, edit: Protyle, element: Element, event: MouseEvent, criteriaData: ISearchOption[]) => { - window.siyuan.menus.menu.remove(); - const sortMenu = [{ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.type, - current: config.sort === 0, - click() { - config.sort = 0; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.createdASC, - current: config.sort === 1, - click() { - config.sort = 1; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.createdDESC, - current: config.sort === 2, - click() { - config.sort = 2; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.modifiedASC, - current: config.sort === 3, - click() { - config.sort = 3; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.modifiedDESC, - current: config.sort === 4, - click() { - config.sort = 4; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.sortByRankAsc, - current: config.sort === 6, - click() { - config.sort = 6; - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.sortByRankDesc, - current: config.sort === 7, - click() { - config.sort = 7; - inputEvent(element, config, undefined, edit); - } - }]; - if (config.group === 1) { - sortMenu.push({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.sortByContent, - current: config.sort === 5, - click() { - config.sort = 5; - inputEvent(element, config, undefined, edit); - } - }); +const getQueryTip = (method: number) => { + let methodTip = window.siyuan.languages.searchMethod + " "; + switch (method) { + case 0: + methodTip += window.siyuan.languages.keyword; + break; + case 1: + methodTip += window.siyuan.languages.querySyntax; + break; + case 2: + methodTip += "SQL"; + break; + case 3: + methodTip += window.siyuan.languages.regex; + break; } - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.sort, - type: "submenu", - submenu: sortMenu, - }).element); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.group, - type: "submenu", - submenu: [{ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.noGroupBy, - current: config.group === 0, - click() { - element.querySelector("#searchCollapse").parentElement.classList.add("fn__none"); - config.group = 0; - if (config.sort === 5) { - config.sort = 0; - } - inputEvent(element, config, undefined, edit); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.groupByDoc, - current: config.group === 1, - click() { - element.querySelector("#searchCollapse").parentElement.classList.remove("fn__none"); - config.group = 1; - inputEvent(element, config, undefined, edit); - } - }] - }).element); - const localData = window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]; - const isPopover = hasClosestByClassName(element, "b3-dialog__container"); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.layout, - type: "submenu", - submenu: [{ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.topBottomLayout, - current: isPopover ? localData.layout === 0 : localData.layoutTab === 0, - click() { - element.querySelector(".search__layout").classList.remove("search__layout--row"); - edit.protyle.element.style.width = ""; - if ((isPopover && localData.row) || (!isPopover && localData.rowTab)) { - edit.protyle.element.style.height = isPopover ? localData.row : localData.rowTab; - edit.protyle.element.classList.remove("fn__flex-1"); - } else { - edit.protyle.element.classList.add("fn__flex-1"); - } - setPadding(edit.protyle); - if (isPopover) { - localData.layout = 0; - } else { - localData.layoutTab = 0; - } - setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]); - } - }, { - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.leftRightLayout, - current: isPopover ? localData.layout === 1 : localData.layoutTab === 1, - click() { - element.querySelector(".search__layout").classList.add("search__layout--row"); - edit.protyle.element.style.height = ""; - if ((isPopover && localData.col) || (!isPopover && localData.colTab)) { - edit.protyle.element.style.width = isPopover ? localData.col : localData.colTab; - edit.protyle.element.classList.remove("fn__flex-1"); - } else { - edit.protyle.element.classList.add("fn__flex-1"); - } - setPadding(edit.protyle); - if (isPopover) { - localData.layout = 1; - } else { - localData.layoutTab = 1; - } - setStorageVal(Constants.LOCAL_SEARCHKEYS, window.siyuan.storage[Constants.LOCAL_SEARCHKEYS]); - } - }] - }).element); - window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element); - window.siyuan.menus.menu.append(new MenuItem({ - label: window.siyuan.languages.saveCriterion, - iconHTML: Constants.ZWSP, - click() { - const saveDialog = new Dialog({ - title: window.siyuan.languages.saveCriterion, - content: `
- -
-
-
- -
`, - width: "520px", - }); - const btnsElement = saveDialog.element.querySelectorAll(".b3-button"); - saveDialog.bindInput(saveDialog.element.querySelector("input"), () => { - btnsElement[1].dispatchEvent(new CustomEvent("click")); - }); - btnsElement[0].addEventListener("click", () => { - saveDialog.destroy(); - }); - btnsElement[1].addEventListener("click", () => { - const value = saveDialog.element.querySelector("input").value; - if (!value) { - showMessage(window.siyuan.languages["_kernel"]["142"]); - return; - } - config.k = (element.querySelector("#searchInput") as HTMLInputElement).value; - config.r = (element.querySelector("#replaceInput") as HTMLInputElement).value; - const criterion = config; - criterion.name = value; - criteriaData.push(Object.assign({}, criterion)); - fetchPost("/api/storage/setCriterion", {criterion}, () => { - saveDialog.destroy(); - const criteriaElement = element.querySelector("#criteria"); - criteriaElement.classList.remove("fn__none"); - criteriaElement.insertAdjacentHTML("beforeend", `
${criterion.name}
`); - }); - }); - } - }).element); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.removeCriterion, - click() { - updateConfig(element, { - removed: true, - sort: 0, - group: 0, - hasReplace: false, - method: 0, - hPath: "", - idPath: [], - k: "", - r: "", - 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, - embedBlock: window.siyuan.config.search.embedBlock, - } - }, config, edit); - } - }).element); - window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true); -}; + return methodTip +} const updateConfig = (element: Element, item: ISearchOption, config: ISearchOption, edit: Protyle) => { const dialogElement = hasClosestByClassName(element, "b3-dialog--open"); @@ -1011,22 +869,7 @@ const updateConfig = (element: Element, item: ISearchOption, config: ISearchOpti } (element.querySelector("#searchInput") as HTMLInputElement).value = item.k; (element.querySelector("#replaceInput") as HTMLInputElement).value = item.r; - let methodTip = window.siyuan.languages.searchMethod + " "; - switch (item.method) { - case 0: - methodTip += window.siyuan.languages.keyword; - break; - case 1: - methodTip += window.siyuan.languages.querySyntax; - break; - case 2: - methodTip += "SQL"; - break; - case 3: - methodTip += window.siyuan.languages.regex; - break; - } - element.querySelector("#searchSyntaxCheck").setAttribute("aria-label", methodTip); + element.querySelector("#searchSyntaxCheck").setAttribute("aria-label", getQueryTip(item.method)); Object.assign(config, item); window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config); setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]); @@ -1034,182 +877,6 @@ const updateConfig = (element: Element, item: ISearchOption, config: ISearchOpti window.siyuan.menus.menu.remove(); }; -const addConfigFilterMenu = (config: ISearchOption, edit: Protyle, element: Element) => { - const filterDialog = new Dialog({ - title: window.siyuan.languages.type, - content: `
- - - - - - - - - - - - -
-
-
- -
`, - width: "520px", - }); - const btnsElement = filterDialog.element.querySelectorAll(".b3-button"); - btnsElement[0].addEventListener("click", () => { - filterDialog.destroy(); - }); - btnsElement[1].addEventListener("click", () => { - filterDialog.element.querySelectorAll(".b3-switch").forEach((item: HTMLInputElement) => { - config.types[item.getAttribute("data-type") as TSearchFilter] = item.checked; - }); - inputEvent(element, config, undefined, edit); - filterDialog.destroy(); - }); -}; - -const addQueryMenu = (config: ISearchOption, edit: Protyle, element: Element) => { - const searchSyntaxCheckElement = element.querySelector("#searchSyntaxCheck"); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.keyword, - current: config.method === 0, - click() { - config.method = 0; - searchSyntaxCheckElement.setAttribute("aria-label", `${window.siyuan.languages.searchMethod} ${window.siyuan.languages.keyword}`); - inputEvent(element, config, undefined, edit); - } - }).element); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.querySyntax, - current: config.method === 1, - click() { - config.method = 1; - searchSyntaxCheckElement.setAttribute("aria-label", `${window.siyuan.languages.searchMethod} ${window.siyuan.languages.querySyntax}`); - inputEvent(element, config, undefined, edit); - } - }).element); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: "SQL", - current: config.method === 2, - click() { - config.method = 2; - searchSyntaxCheckElement.setAttribute("aria-label", `${window.siyuan.languages.searchMethod} SQL`); - inputEvent(element, config, undefined, edit); - } - }).element); - window.siyuan.menus.menu.append(new MenuItem({ - iconHTML: Constants.ZWSP, - label: window.siyuan.languages.regex, - current: config.method === 3, - click() { - config.method = 3; - searchSyntaxCheckElement.setAttribute("aria-label", `${window.siyuan.languages.searchMethod} ${window.siyuan.languages.regex}`); - inputEvent(element, config, undefined, edit); - } - }).element); -}; - const getKey = (element: HTMLElement) => { const keys: string[] = []; element.querySelectorAll("mark").forEach(item => {