mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-01-10 18:58:50 +01:00
876 lines
44 KiB
TypeScript
876 lines
44 KiB
TypeScript
import {closePanel} from "../util/closePanel";
|
|
import {getCurrentEditor, openMobileFileById} from "../editor";
|
|
import {Constants} from "../../constants";
|
|
import {fetchPost} from "../../util/fetch";
|
|
import {getIconByType} from "../../editor/getIcon";
|
|
import {preventScroll} from "../../protyle/scroll/preventScroll";
|
|
import {openModel} from "./model";
|
|
import {getDisplayName, getNotebookIcon, getNotebookName, movePathTo, pathPosix} from "../../util/pathName";
|
|
import {getKeyByLiElement, initCriteriaMenu, moreMenu} from "../../search/menu";
|
|
import {setStorageVal} from "../../protyle/util/compatibility";
|
|
import {escapeGreat, escapeHtml} from "../../util/escape";
|
|
import {unicode2Emoji} from "../../emoji";
|
|
import {newFileByName} from "../../util/newFile";
|
|
import {showMessage} from "../../dialog/message";
|
|
import {reloadProtyle} from "../../protyle/util/reload";
|
|
import {activeBlur, hideKeyboardToolbar} from "../util/keyboardToolbar";
|
|
import {App} from "../../index";
|
|
import {
|
|
assetFilterMenu,
|
|
assetInputEvent,
|
|
assetMethodMenu,
|
|
assetMoreMenu,
|
|
renderNextAssetMark,
|
|
renderPreview,
|
|
} from "../../search/assets";
|
|
import {addClearButton} from "../../util/addClearButton";
|
|
import {checkFold} from "../../util/noRelyPCFunction";
|
|
import {getDefaultType} from "../../search/getDefault";
|
|
import {
|
|
saveAssetKeyList,
|
|
saveKeyList,
|
|
toggleAssetHistory,
|
|
toggleReplaceHistory,
|
|
toggleSearchHistory
|
|
} from "../../search/toggleHistory";
|
|
|
|
const replace = (element: Element, config: Config.IUILayoutTabSearchConfig, isAll: boolean) => {
|
|
if (config.method === 1 || config.method === 2) {
|
|
showMessage(window.siyuan.languages._kernel[132]);
|
|
return;
|
|
}
|
|
const searchListElement = element.querySelector("#searchList");
|
|
const replaceInputElement = element.querySelector("#toolbarReplace") as HTMLInputElement;
|
|
|
|
const loadElement = replaceInputElement.parentElement.querySelector(".fn__rotate");
|
|
if (!loadElement.classList.contains("fn__none")) {
|
|
return;
|
|
}
|
|
saveKeyList("replaceKeys", replaceInputElement.value);
|
|
let currentLiElement: HTMLElement = searchListElement.querySelector(".b3-list-item--focus");
|
|
if (!currentLiElement) {
|
|
return;
|
|
}
|
|
loadElement.classList.remove("fn__none");
|
|
loadElement.nextElementSibling.classList.add("fn__none");
|
|
let ids: string[] = [];
|
|
if (isAll) {
|
|
searchListElement.querySelectorAll('.b3-list-item[data-type="search-item"]').forEach(item => {
|
|
ids.push(item.getAttribute("data-node-id"));
|
|
});
|
|
} else {
|
|
ids = [currentLiElement.getAttribute("data-node-id")];
|
|
}
|
|
fetchPost("/api/search/findReplace", {
|
|
k: config.method === 0 ? getKeyByLiElement(currentLiElement) : (document.querySelector("#toolbarSearch") as HTMLInputElement).value,
|
|
r: replaceInputElement.value,
|
|
ids,
|
|
types: config.types,
|
|
method: config.method,
|
|
replaceTypes: config.replaceTypes
|
|
}, (response) => {
|
|
loadElement.classList.add("fn__none");
|
|
loadElement.nextElementSibling.classList.remove("fn__none");
|
|
|
|
if (response.code === 1) {
|
|
showMessage(response.msg);
|
|
return;
|
|
}
|
|
if (ids.length > 1) {
|
|
return;
|
|
}
|
|
reloadProtyle(window.siyuan.mobile.editor.protyle, false);
|
|
|
|
if (currentLiElement.nextElementSibling) {
|
|
currentLiElement.nextElementSibling.classList.add("b3-list-item--focus");
|
|
} else if (currentLiElement.previousElementSibling) {
|
|
currentLiElement.previousElementSibling.classList.add("b3-list-item--focus");
|
|
}
|
|
if (config.group === 1) {
|
|
if (currentLiElement.nextElementSibling || currentLiElement.previousElementSibling) {
|
|
currentLiElement.remove();
|
|
} else {
|
|
const nextDocElement = currentLiElement.parentElement.nextElementSibling || currentLiElement.parentElement.previousElementSibling.previousElementSibling?.previousElementSibling;
|
|
if (nextDocElement) {
|
|
nextDocElement.nextElementSibling.firstElementChild.classList.add("b3-list-item--focus");
|
|
nextDocElement.nextElementSibling.classList.remove("fn__none");
|
|
nextDocElement.firstElementChild.firstElementChild.classList.add("b3-list-item__arrow--open");
|
|
}
|
|
currentLiElement.parentElement.previousElementSibling.remove();
|
|
currentLiElement.parentElement.remove();
|
|
}
|
|
} else {
|
|
currentLiElement.remove();
|
|
}
|
|
currentLiElement = searchListElement.querySelector(".b3-list-item--focus");
|
|
if (!currentLiElement) {
|
|
searchListElement.innerHTML = `<div class="b3-list--empty">${window.siyuan.languages.emptyContent}</div>`;
|
|
return;
|
|
}
|
|
if (searchListElement.scrollTop < currentLiElement.offsetTop - searchListElement.clientHeight + 30 ||
|
|
searchListElement.scrollTop > currentLiElement.offsetTop) {
|
|
searchListElement.scrollTop = currentLiElement.offsetTop - searchListElement.clientHeight + 30;
|
|
}
|
|
});
|
|
};
|
|
|
|
const updateConfig = (element: Element, newConfig: Config.IUILayoutTabSearchConfig, config: Config.IUILayoutTabSearchConfig) => {
|
|
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 = `<div class="b3-chip b3-chip--middle">${escapeHtml(newConfig.hPath)}<svg data-type="remove-path" class="b3-chip__close"><use xlink:href="#iconCloseRound"></use></svg></div>`;
|
|
} 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, element);
|
|
window.siyuan.menus.menu.remove();
|
|
};
|
|
|
|
const onRecentBlocks = (data: IBlock[], config: Config.IUILayoutTabSearchConfig, response?: IWebSocketData) => {
|
|
const listElement = document.querySelector("#searchList");
|
|
let resultHTML = "";
|
|
data.forEach((item: IBlock, index: number) => {
|
|
const title = getNotebookName(item.box) + getDisplayName(item.hPath, false);
|
|
if (item.children) {
|
|
resultHTML += `<div class="b3-list-item">
|
|
<span class="b3-list-item__toggle b3-list-item__toggle--hl">
|
|
<svg class="b3-list-item__arrow b3-list-item__arrow--open"><use xlink:href="#iconRight"></use></svg>
|
|
</span>
|
|
${unicode2Emoji(getNotebookIcon(item.box) || window.siyuan.storage[Constants.LOCAL_IMAGES].note, "b3-list-item__graphic", true)}
|
|
<span class="b3-list-item__text" style="color: var(--b3-theme-on-surface)">${escapeGreat(title)}</span>
|
|
</div><div>`;
|
|
item.children.forEach((childItem, childIndex) => {
|
|
resultHTML += `<div style="padding-left: 36px" data-type="search-item" class="b3-list-item${childIndex === 0 && index === 0 ? " b3-list-item--focus" : ""}" data-node-id="${childItem.id}">
|
|
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(childItem.type)}"></use></svg>
|
|
${unicode2Emoji(childItem.ial.icon, "b3-list-item__graphic", true)}
|
|
<span class="b3-list-item__text">${childItem.content}</span>
|
|
${childItem.tag ? `<span class="b3-list-item__meta b3-list-item__meta--ellipsis">${childItem.tag.split("# #").map(tag => `${tag.replace("#", "")}`).join(" ").replace("#", "")}</span>` : ""}
|
|
</div>`;
|
|
});
|
|
resultHTML += "</div>";
|
|
} else {
|
|
resultHTML += `<div class="b3-list-item b3-list-item--two${index === 0 ? " b3-list-item--focus" : ""}" data-type="search-item" data-node-id="${item.id}">
|
|
<div class="b3-list-item__first">
|
|
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(item.type)}"></use></svg>
|
|
${unicode2Emoji(item.ial.icon, "b3-list-item__graphic", true)}
|
|
<span class="b3-list-item__text">${item.content}</span>
|
|
</div>
|
|
<div class="fn__flex">
|
|
${item.tag ? `<span class="b3-list-item__meta b3-list-item__meta--ellipsis">${item.tag.split("# #").map(tag => `${tag.replace("#", "")}`).join(" ").replace("#", "")}</span><span class="fn__space"></span>` : ""}
|
|
<span class="b3-list-item__text b3-list-item__meta">${escapeGreat(title)}</span>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
});
|
|
listElement.innerHTML = resultHTML ||
|
|
`<div class="b3-list-item b3-list-item--focus" data-type="search-new">
|
|
<svg class="b3-list-item__graphic"><use xlink:href="#iconFile"></use></svg>
|
|
<span class="b3-list-item__text">
|
|
${window.siyuan.languages.newFile} <mark>${(document.querySelector("#toolbarSearch") as HTMLInputElement).value}</mark>
|
|
</span>
|
|
</div>`;
|
|
listElement.scrollTop = 0;
|
|
let countHTML = "";
|
|
if (response) {
|
|
let text = window.siyuan.languages.findInDoc.replace("${x}", response.data.matchedRootCount).replace("${y}", response.data.matchedBlockCount);
|
|
if (response.data.docMode) {
|
|
text = window.siyuan.languages.matchDoc.replace("${x}", response.data.matchedRootCount);
|
|
}
|
|
countHTML = `<span class="fn__flex-center">${text}</span>
|
|
<span class="fn__flex-1"></span>
|
|
<span class="fn__flex-center">${config.page}/${response.data.pageCount || 1}</span>`;
|
|
}
|
|
listElement.previousElementSibling.querySelector('[data-type="result"]').innerHTML = countHTML;
|
|
};
|
|
|
|
let toolbarSearchTimeout = 0;
|
|
export const updateSearchResult = (config: Config.IUILayoutTabSearchConfig, element: Element, rmCurrentCriteria = false) => {
|
|
clearTimeout(toolbarSearchTimeout);
|
|
toolbarSearchTimeout = window.setTimeout(() => {
|
|
if (rmCurrentCriteria) {
|
|
element.querySelector("#criteria .b3-chip--current")?.classList.remove("b3-chip--current");
|
|
}
|
|
const loadingElement = element.querySelector(".fn__loading--top");
|
|
loadingElement.classList.remove("fn__none");
|
|
const previousElement = element.querySelector('[data-type="previous"]');
|
|
const nextElement = element.querySelector('[data-type="next"]');
|
|
const inputElement = document.getElementById("toolbarSearch") as HTMLInputElement;
|
|
if (inputElement.value === "" && (!config.idPath || config.idPath.length === 0)) {
|
|
fetchPost("/api/block/getRecentUpdatedBlocks", {}, (response) => {
|
|
onRecentBlocks(response.data, config);
|
|
loadingElement.classList.add("fn__none");
|
|
previousElement.setAttribute("disabled", "true");
|
|
nextElement.setAttribute("disabled", "true");
|
|
});
|
|
} else {
|
|
if (!config.page) {
|
|
config.page = 1;
|
|
}
|
|
if (config.page > 1) {
|
|
previousElement.removeAttribute("disabled");
|
|
} else {
|
|
previousElement.setAttribute("disabled", "disabled");
|
|
}
|
|
fetchPost("/api/search/fullTextSearchBlock", {
|
|
query: inputElement.value,
|
|
method: config.method,
|
|
types: config.types,
|
|
paths: config.idPath || [],
|
|
groupBy: config.group,
|
|
orderBy: config.sort,
|
|
page: config.page,
|
|
}, (response) => {
|
|
onRecentBlocks(response.data.blocks, config, response);
|
|
loadingElement.classList.add("fn__none");
|
|
if (config.page < response.data.pageCount) {
|
|
nextElement.removeAttribute("disabled");
|
|
} else {
|
|
nextElement.setAttribute("disabled", "disabled");
|
|
}
|
|
});
|
|
}
|
|
}, Constants.TIMEOUT_INPUT);
|
|
};
|
|
|
|
const initSearchEvent = (app: App, element: Element, config: Config.IUILayoutTabSearchConfig) => {
|
|
const searchInputElement = document.getElementById("toolbarSearch") as HTMLInputElement;
|
|
searchInputElement.value = config.k || "";
|
|
searchInputElement.addEventListener("compositionend", (event: InputEvent) => {
|
|
if (event && event.isComposing) {
|
|
return;
|
|
}
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
});
|
|
searchInputElement.addEventListener("input", (event: InputEvent) => {
|
|
if (event && event.isComposing) {
|
|
return;
|
|
}
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
});
|
|
searchInputElement.addEventListener("blur", () => {
|
|
if (config.removed) {
|
|
config.k = searchInputElement.value;
|
|
window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config);
|
|
setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]);
|
|
}
|
|
saveKeyList("keys", searchInputElement.value);
|
|
});
|
|
addClearButton({
|
|
inputElement: searchInputElement,
|
|
className: "toolbar__icon",
|
|
clearCB() {
|
|
config.page = 1;
|
|
updateSearchResult(config, element);
|
|
}
|
|
});
|
|
const replaceInputElement = element.querySelector(".toolbar .toolbar__title") as HTMLInputElement;
|
|
replaceInputElement.value = config.r || "";
|
|
addClearButton({
|
|
inputElement: replaceInputElement,
|
|
className: "toolbar__icon",
|
|
});
|
|
const criteriaData: Config.IUILayoutTabSearchConfig[] = [];
|
|
initCriteriaMenu(element.querySelector("#criteria"), criteriaData, config);
|
|
|
|
const assetsElement = document.querySelector("#searchAssetsPanel");
|
|
const unRefElement = document.querySelector("#searchUnRefPanel");
|
|
const searchListElement = element.querySelector("#searchList") as HTMLElement;
|
|
const localSearch = window.siyuan.storage[Constants.LOCAL_SEARCHASSET] as ISearchAssetOption;
|
|
element.addEventListener("click", (event: MouseEvent) => {
|
|
let target = event.target as HTMLElement;
|
|
while (target && !target.isSameNode(element)) {
|
|
const type = target.getAttribute("data-type");
|
|
if (type === "replaceHistory") {
|
|
toggleReplaceHistory(target.nextElementSibling as HTMLInputElement);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "assetHistory") {
|
|
toggleAssetHistory(assetsElement);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "previous") {
|
|
if (!target.getAttribute("disabled")) {
|
|
config.page--;
|
|
updateSearchResult(config, element);
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "next") {
|
|
if (!target.getAttribute("disabled")) {
|
|
config.page++;
|
|
updateSearchResult(config, element);
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "set-criteria") {
|
|
config.removed = false;
|
|
target.parentElement.querySelector(".b3-chip--current")?.classList.remove("b3-chip--current");
|
|
target.classList.add("b3-chip--current");
|
|
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.classList.contains("b3-chip--current")) {
|
|
updateConfig(element, {
|
|
removed: true,
|
|
sort: 0,
|
|
group: 0,
|
|
hasReplace: false,
|
|
method: 0,
|
|
hPath: "",
|
|
idPath: [],
|
|
k: "",
|
|
r: "",
|
|
page: 1,
|
|
types: getDefaultType(),
|
|
replaceTypes: Object.assign({}, Constants.SIYUAN_DEFAULT_REPLACETYPES),
|
|
}, config);
|
|
}
|
|
if (target.parentElement.parentElement.childElementCount === 1) {
|
|
target.parentElement.parentElement.classList.add("fn__none");
|
|
}
|
|
target.parentElement.remove();
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "remove-path") {
|
|
config.idPath = [];
|
|
config.hPath = "";
|
|
element.querySelector("#searchPath").classList.add("fn__none");
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
const includeElement = element.querySelector('[data-type="include"]');
|
|
includeElement.classList.remove("toolbar__icon--active");
|
|
includeElement.setAttribute("disabled", "disabled");
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "expand") {
|
|
Array.from(searchListElement.children).forEach(item => {
|
|
if (item.classList.contains("b3-list-item")) {
|
|
item.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open");
|
|
item.nextElementSibling.classList.remove("fn__none");
|
|
}
|
|
});
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "contract") {
|
|
Array.from(searchListElement.children).forEach(item => {
|
|
if (item.classList.contains("b3-list-item")) {
|
|
item.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
|
|
item.nextElementSibling.classList.add("fn__none");
|
|
}
|
|
});
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "currentPath" && !target.hasAttribute("disabled")) {
|
|
const editProtyle = getCurrentEditor().protyle;
|
|
fetchPost("/api/filetree/getHPathsByPaths", {paths: [editProtyle.path]}, (response) => {
|
|
config.idPath = [pathPosix().join(editProtyle.notebookId, editProtyle.path)];
|
|
config.hPath = response.data[0];
|
|
const searchPathElement = element.querySelector("#searchPath");
|
|
searchPathElement.classList.remove("fn__none");
|
|
searchPathElement.innerHTML = `<div class="b3-chip b3-chip--middle">${escapeHtml(config.hPath)}<svg data-type="remove-path" class="b3-chip__close"><use xlink:href="#iconCloseRound"></use></svg></div>`;
|
|
|
|
const includeElement = element.querySelector('[data-type="include"]');
|
|
includeElement.classList.remove("toolbar__icon--active");
|
|
includeElement.removeAttribute("disabled");
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
});
|
|
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");
|
|
searchPathElement.innerHTML = `<div class="b3-chip b3-chip--middle">${escapeHtml(config.hPath)}<svg data-type="remove-path" class="b3-chip__close"><use xlink:href="#iconCloseRound"></use></svg></div>`;
|
|
|
|
const includeElement = element.querySelector('[data-type="include"]');
|
|
includeElement.classList.add("toolbar__icon--active");
|
|
if (enableIncludeChild) {
|
|
includeElement.removeAttribute("disabled");
|
|
} else {
|
|
includeElement.setAttribute("disabled", "disabled");
|
|
}
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
});
|
|
}, [], undefined, window.siyuan.languages.specifyPath);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "include" && !target.hasAttribute("disabled")) {
|
|
target.classList.toggle("toolbar__icon--active");
|
|
if (target.classList.contains("toolbar__icon--active")) {
|
|
config.idPath.forEach((item, index) => {
|
|
if (item.endsWith(".sy")) {
|
|
config.idPath[index] = item.replace(".sy", "");
|
|
}
|
|
});
|
|
} else {
|
|
config.idPath.forEach((item, index) => {
|
|
if (!item.endsWith(".sy") && item.split("/").length > 1) {
|
|
config.idPath[index] = item + ".sy";
|
|
}
|
|
});
|
|
}
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
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, () => {
|
|
config.page = 1;
|
|
updateSearchResult(config, element, true);
|
|
}, () => {
|
|
updateConfig(element, {
|
|
removed: true,
|
|
sort: 0,
|
|
group: 0,
|
|
hasReplace: false,
|
|
method: 0,
|
|
hPath: "",
|
|
idPath: [],
|
|
k: "",
|
|
r: "",
|
|
page: 1,
|
|
types: getDefaultType(),
|
|
replaceTypes: Object.assign({}, Constants.SIYUAN_DEFAULT_REPLACETYPES),
|
|
}, config);
|
|
});
|
|
window.siyuan.menus.menu.fullscreen();
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "replace-all") {
|
|
replace(element, config, true);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "refreshUnRef") {
|
|
getUnRefListMobile(unRefElement);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "unRefPrevious") {
|
|
if (!target.getAttribute("disabled")) {
|
|
let currentPage = parseInt(unRefElement.querySelector("#searchUnRefResult").lastElementChild.textContent);
|
|
if (currentPage > 1) {
|
|
currentPage--;
|
|
getUnRefListMobile(unRefElement, currentPage);
|
|
}
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "unRefNext") {
|
|
const unRefRageElement = unRefElement.querySelector("#searchUnRefResult").lastElementChild;
|
|
let currentPage = parseInt(unRefRageElement.textContent);
|
|
if (currentPage < parseInt(unRefRageElement.textContent.split("/")[1])) {
|
|
currentPage++;
|
|
getUnRefListMobile(unRefElement, currentPage);
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "queryAsset") {
|
|
assetMethodMenu(target, () => {
|
|
assetInputEvent(assetsElement, localSearch);
|
|
setStorageVal(Constants.LOCAL_SEARCHASSET, localSearch);
|
|
});
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "filterAsset") {
|
|
assetFilterMenu(assetsElement);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "moreAsset") {
|
|
assetMoreMenu(target, assetsElement, () => {
|
|
assetInputEvent(assetsElement);
|
|
setStorageVal(Constants.LOCAL_SEARCHASSET, localSearch);
|
|
});
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "goAsset") {
|
|
goAsset();
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "goSearch") {
|
|
assetsElement.classList.add("fn__none");
|
|
unRefElement.classList.add("fn__none");
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "assetPrevious") {
|
|
if (!target.getAttribute("disabled")) {
|
|
assetInputEvent(assetsElement, localSearch, parseInt(assetsElement.querySelector("#searchAssetResult .fn__flex-center").textContent.split("/")[1]) - 1);
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "assetNext") {
|
|
if (!target.getAttribute("disabled")) {
|
|
assetInputEvent(assetsElement, localSearch, parseInt(assetsElement.querySelector("#searchAssetResult .fn__flex-center").textContent.split("/")[1]) + 1);
|
|
}
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
break;
|
|
} else if (type === "replace") {
|
|
replace(element, config, 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.getAttribute("data-type") === "search-new") {
|
|
newFileByName(app, searchInputElement.value);
|
|
} else if (target.getAttribute("data-type") === "search-item") {
|
|
const id = target.getAttribute("data-node-id");
|
|
if (id) {
|
|
if (window.siyuan.mobile.editor?.protyle) {
|
|
preventScroll(window.siyuan.mobile.editor.protyle);
|
|
}
|
|
checkFold(id, (zoomIn) => {
|
|
openMobileFileById(app, id, zoomIn ? [Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
|
|
});
|
|
closePanel();
|
|
} else {
|
|
if (!target.classList.contains("b3-list-item--focus")) {
|
|
element.querySelector("#searchAssetList .b3-list-item--focus").classList.remove("b3-list-item--focus");
|
|
target.classList.add("b3-list-item--focus");
|
|
renderPreview(element.querySelector("#searchAssetPreview"), target.dataset.id, (element.querySelector("#searchAssetInput") as HTMLInputElement).value, window.siyuan.storage[Constants.LOCAL_SEARCHASSET].method);
|
|
} else if (target.classList.contains("b3-list-item--focus")) {
|
|
renderNextAssetMark(element.querySelector("#searchAssetPreview"));
|
|
}
|
|
}
|
|
} 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 = (app: App, searchConfig?: any) => {
|
|
const config: Config.IUILayoutTabSearchConfig = JSON.parse(JSON.stringify(window.siyuan.storage[Constants.LOCAL_SEARCHDATA]));
|
|
const rangeText = (getCurrentEditor()?.protyle.toolbar.range || (getSelection().rangeCount > 0 ? getSelection().getRangeAt(0) : document.createRange())).toString();
|
|
if (rangeText) {
|
|
config.k = rangeText;
|
|
}
|
|
if (searchConfig) {
|
|
Object.keys(searchConfig).forEach((key: "r") => {
|
|
config[key] = searchConfig[key];
|
|
});
|
|
}
|
|
|
|
activeBlur();
|
|
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: `<div class="fn__flex">
|
|
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="history">
|
|
<svg class="svg--mid"><use xlink:href="#iconSearch"></use></svg>
|
|
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
|
|
</span>
|
|
<input id="toolbarSearch" placeholder="${window.siyuan.languages.showRecentUpdatedBlocks}" class="toolbar__title fn__block">
|
|
<svg id="toolbarSearchNew" class="toolbar__icon"><use xlink:href="#iconFile"></use></svg>
|
|
</div>`,
|
|
html: `<div class="fn__flex-column" style="height: 100%">
|
|
<div class="toolbar toolbar--border${config.hasReplace ? "" : " fn__none"}">
|
|
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="replaceHistory">
|
|
<svg class="svg--mid"><use xlink:href="#iconReplace"></use></svg>
|
|
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
|
|
</span>
|
|
<input id="toolbarReplace" class="toolbar__title">
|
|
<svg class="fn__rotate fn__none toolbar__icon"><use xlink:href="#iconRefresh"></use></svg>
|
|
<div class="fn__space"></div>
|
|
<button data-type="replace-all" class="b3-button b3-button--outline fn__flex-center">${window.siyuan.languages.replaceAll}</button>
|
|
<div class="fn__space"></div>
|
|
<button data-type="replace" class="b3-button b3-button--outline fn__flex-center">${window.siyuan.languages.replace}</button>
|
|
<div class="fn__space"></div>
|
|
</div>
|
|
<div id="criteria" style="background-color: var(--b3-theme-background);"></div>
|
|
<div class="toolbar">
|
|
<span class="fn__space"></span>
|
|
<span data-type="result" class="fn__flex-1 fn__flex"></span>
|
|
<span class="fn__space"></span>
|
|
<svg data-type="previous" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconLeft"></use></svg>
|
|
<svg data-type="next" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconRight"></use></svg>
|
|
</div>
|
|
<div id="searchList" style="overflow:auto;" class="fn__flex-1 b3-list b3-list--background"></div>
|
|
<div id="searchPath" class="b3-chips${config.hPath ? "" : " fn__none"}" style="background-color: var(--b3-theme-background);">
|
|
<div class="b3-chip b3-chip--middle">
|
|
${escapeHtml(config.hPath)}
|
|
<svg data-type="remove-path" class="b3-chip__close"><use xlink:href="#iconCloseRound"></use></svg>
|
|
</div>
|
|
</div>
|
|
<div class="toolbar">
|
|
<span class="fn__flex-1"></span>
|
|
<svg data-type="toggle-replace" class="toolbar__icon${config.hasReplace ? " toolbar__icon--active" : ""}"><use xlink:href="#iconReplace"></use></svg>
|
|
<svg ${enableIncludeChild ? "" : "disabled"} data-type="include" class="toolbar__icon${includeChild ? " toolbar__icon--active" : ""}"><use xlink:href="#iconCopy"></use></svg>
|
|
<svg data-type="path" class="toolbar__icon"><use xlink:href="#iconFolder"></use></svg>
|
|
<svg ${document.querySelector("#empty").classList.contains("fn__none") ? "" : "disabled"} data-type="currentPath" class="toolbar__icon"><use xlink:href="#iconFocus"></use></svg>
|
|
<svg data-type="expand" class="toolbar__icon${config.group === 0 ? " fn__none" : ""}"><use xlink:href="#iconExpand"></use></svg>
|
|
<svg data-type="contract" class="toolbar__icon${config.group === 0 ? " fn__none" : ""}"><use xlink:href="#iconContract"></use></svg>
|
|
<svg data-type="more" class="toolbar__icon"><use xlink:href="#iconMore"></use></svg>
|
|
<svg data-type="goAsset" class="toolbar__icon"><use xlink:href="#iconExact"></use></svg>
|
|
<span class="fn__flex-1"></span>
|
|
</div>
|
|
<div class="fn__none fn__flex-column" style="position: fixed;top: 0;width: 100%;background: var(--b3-theme-surface);height: 100%;" id="searchAssetsPanel">
|
|
<div class="toolbar toolbar--border">
|
|
<span data-menu="true" class="toolbar__icon toolbar__icon--history" data-type="assetHistory">
|
|
<svg class="svg--mid"><use xlink:href="#iconSearch"></use></svg>
|
|
<svg class="svg--smaller"><use xlink:href="#iconDown"></use></svg>
|
|
</span>
|
|
<input id="searchAssetInput" placeholder="${window.siyuan.languages.keyword}" class="toolbar__title fn__block">
|
|
</div>
|
|
<div class="toolbar">
|
|
<span class="fn__space"></span>
|
|
<span id="searchAssetResult" class="fn__flex-1 fn__flex"><span class="fn__flex-1"></span></span>
|
|
<span class="fn__space"></span>
|
|
<svg data-type="assetPrevious" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconLeft"></use></svg>
|
|
<svg data-type="assetNext" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconRight"></use></svg>
|
|
</div>
|
|
<div id="searchAssetList" style="overflow:auto;" class="fn__flex-1 b3-list b3-list--background"></div>
|
|
<div id="searchAssetPreview" class="fn__flex-1 search__preview b3-typography" style="padding: 8px;border-bottom: 1px solid var(--b3-border-color);"></div>
|
|
<div class="toolbar">
|
|
<span class="fn__flex-1"></span>
|
|
<svg data-type="queryAsset" class="toolbar__icon"><use xlink:href="#iconRegex"></use></svg>
|
|
<svg data-type="filterAsset" class="toolbar__icon"><use xlink:href="#iconFilter"></use></svg>
|
|
<svg data-type="moreAsset" class="toolbar__icon"><use xlink:href="#iconMore"></use></svg>
|
|
<svg data-type="goSearch" class="toolbar__icon"><use xlink:href="#iconBack"></use></svg>
|
|
<span class="fn__flex-1"></span>
|
|
</div>
|
|
</div>
|
|
<div class="fn__none fn__flex-column" style="position: fixed;top: 0;width: 100%;background: var(--b3-theme-surface);height: 100%;" id="searchUnRefPanel">
|
|
<div class="toolbar">
|
|
<span class="fn__space"></span>
|
|
<span id="searchUnRefResult" class="fn__flex-1 fn__flex"></span>
|
|
<span class="fn__space"></span>
|
|
<svg data-type="unRefPrevious" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconLeft"></use></svg>
|
|
<svg data-type="unRefNext" disabled="disabled" class="toolbar__icon"><use xlink:href="#iconRight"></use></svg>
|
|
</div>
|
|
<div id="searchUnRefList" style="overflow:auto;" class="fn__flex-1 b3-list b3-list--background"></div>
|
|
<div class="toolbar">
|
|
<span class="fn__flex-1"></span>
|
|
<svg data-type="refreshUnRef" class="toolbar__icon"><use xlink:href="#iconRefresh"></use></svg>
|
|
<svg data-type="goSearch" class="toolbar__icon"><use xlink:href="#iconBack"></use></svg>
|
|
<span class="fn__flex-1"></span>
|
|
</div>
|
|
</div>
|
|
<div class="fn__loading fn__loading--top"><img width="120px" src="/stage/loading-pure.svg"></div>
|
|
</div>`,
|
|
bindEvent(element) {
|
|
document.querySelector("#toolbarSearchNew").addEventListener("click", () => {
|
|
newFileByName(app, (document.querySelector("#toolbarSearch") as HTMLInputElement).value);
|
|
});
|
|
const historyElement = document.querySelector('.toolbar [data-type="history"]');
|
|
historyElement.addEventListener("click", () => {
|
|
toggleSearchHistory(document.querySelector("#model"), config, undefined);
|
|
});
|
|
initSearchEvent(app, element.firstElementChild, config);
|
|
updateSearchResult(config, element);
|
|
}
|
|
});
|
|
};
|
|
|
|
const goAsset = () => {
|
|
const assetsElement = document.querySelector("#searchAssetsPanel");
|
|
assetsElement.classList.remove("fn__none");
|
|
const listElement = assetsElement.querySelector("#searchAssetList");
|
|
if (listElement.innerHTML) {
|
|
return;
|
|
}
|
|
const localSearch = window.siyuan.storage[Constants.LOCAL_SEARCHASSET] as ISearchAssetOption;
|
|
const inputElement = assetsElement.querySelector("input");
|
|
inputElement.value = localSearch.k;
|
|
inputElement.addEventListener("compositionend", (event: InputEvent) => {
|
|
if (event.isComposing) {
|
|
return;
|
|
}
|
|
assetInputEvent(assetsElement, localSearch);
|
|
});
|
|
inputElement.addEventListener("input", (event: InputEvent) => {
|
|
if (event.isComposing) {
|
|
return;
|
|
}
|
|
assetInputEvent(assetsElement, localSearch);
|
|
});
|
|
inputElement.addEventListener("blur", () => {
|
|
saveAssetKeyList(inputElement);
|
|
});
|
|
assetInputEvent(assetsElement, localSearch);
|
|
addClearButton({
|
|
inputElement,
|
|
className: "toolbar__icon",
|
|
clearCB() {
|
|
assetInputEvent(assetsElement, localSearch);
|
|
}
|
|
});
|
|
};
|
|
|
|
export const goUnRef = () => {
|
|
window.siyuan.menus.menu.remove();
|
|
const unRefElement = document.querySelector("#searchUnRefPanel");
|
|
unRefElement.classList.remove("fn__none");
|
|
const listElement = unRefElement.querySelector("#searchUnRefList");
|
|
if (listElement.innerHTML) {
|
|
return;
|
|
}
|
|
getUnRefListMobile(unRefElement);
|
|
};
|
|
|
|
const getUnRefListMobile = (element: Element, page = 1) => {
|
|
const previousElement = element.querySelector('[data-type="unRefPrevious"]');
|
|
if (page > 1) {
|
|
previousElement.removeAttribute("disabled");
|
|
} else {
|
|
previousElement.setAttribute("disabled", "disabled");
|
|
}
|
|
fetchPost("/api/search/listInvalidBlockRefs", {
|
|
page,
|
|
}, (response) => {
|
|
element.parentElement.querySelector(".fn__loading--top").classList.add("fn__none");
|
|
const nextElement = element.querySelector('[data-type="unRefNext"]');
|
|
if (page < response.data.pageCount) {
|
|
nextElement.removeAttribute("disabled");
|
|
} else {
|
|
nextElement.setAttribute("disabled", "disabled");
|
|
}
|
|
let resultHTML = "";
|
|
response.data.blocks.forEach((item: IBlock, index: number) => {
|
|
const title = getNotebookName(item.box) + getDisplayName(item.hPath, false);
|
|
resultHTML += `<div class="b3-list-item b3-list-item--two${index === 0 ? " b3-list-item--focus" : ""}" data-type="search-item" data-node-id="${item.id}">
|
|
<div class="b3-list-item__first">
|
|
<svg class="b3-list-item__graphic"><use xlink:href="#${getIconByType(item.type)}"></use></svg>
|
|
${unicode2Emoji(item.ial.icon, "b3-list-item__graphic", true)}
|
|
<span class="b3-list-item__text">${item.content}</span>
|
|
</div>
|
|
<span class="b3-list-item__text b3-list-item__meta">${escapeGreat(title)}</span>
|
|
</div>`;
|
|
});
|
|
element.querySelector("#searchUnRefResult").innerHTML = `<span class="fn__flex-center">${window.siyuan.languages.findInDoc.replace("${x}", response.data.matchedRootCount).replace("${y}", response.data.matchedBlockCount)}</span>
|
|
<span class="fn__flex-1"></span>
|
|
<span class="fn__flex-center">${page}/${response.data.pageCount || 1}</span>`;
|
|
element.querySelector("#searchUnRefList").innerHTML = resultHTML || `<div class="search__empty">
|
|
${window.siyuan.languages.emptyContent}
|
|
</div>`;
|
|
});
|
|
};
|