Vanessa 2022-11-27 00:50:53 +08:00
parent 1480afe42f
commit 0c63c49909
7 changed files with 83 additions and 279 deletions

View file

@ -28,7 +28,7 @@
max-width: 70%;
max-height: 50vh;
overflow: auto;
top: 48px;
top: 42px;
position: absolute;
left: 8px;
}

View file

@ -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 => {

View file

@ -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";

View file

@ -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;
}

View file

@ -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") || "{}");

View file

@ -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 += `<div class="b3-list-item">${escapeHtml(s)}</div>`;
// }
// });
// 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 += `<div data-type="search-item" class="b3-list-item${index === 0 ? " b3-list-item--focus" : ""}" data-url="${item.box}" data-path="${item.path}" data-node-id="${item.id}">
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(item.type)}"></use></svg>
<span class="b3-list-item__text">${unicode2Emoji(item.ial.icon)}${item.ial.icon ? "&nbsp;" : ""}${item.content}</span>
<span class="b3-list-item__meta b3-list-item__meta--ellipsis" title="${title}">${title}</span>
</div>`;
});
this.element.querySelector("#globalSearchList").innerHTML = resultHTML || `<div class="b3-list--empty">${window.siyuan.languages.emptyContent}</div>`;
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"));
}
}

View file

@ -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) {
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);
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;
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: `<div class="fn__flex" style="margin-bottom: 4px"><span>${lang}</span><span class="fn__space fn__flex-1"></span>
<input type="checkbox" class="b3-switch fn__flex-center"${config.types[key] ? " checked" : ""}></div>`,
@ -403,7 +452,9 @@ const addConfigMenu = (config: ISearchOption, lang: string, key: "mathBlock" | "
}
config.types[key] = inputElement.checked;
inputEvent(element, config, undefined, edit);
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) {