${customHTML}
@@ -252,8 +252,8 @@ export const openFileAttr = (attrs: IObject, focusName = "bookmark") => {
target.classList.add("item--focus");
dialog.element.querySelectorAll(".custom-attr").forEach((item: HTMLElement) => {
if (item.dataset.type === target.dataset.type) {
- if (item.dataset.type === "av" && item.innerHTML === "") {
- renderAVAttribute(item, attrs.id);
+ if (item.dataset.type === "NodeAttributeView" && item.innerHTML === "") {
+ renderAVAttribute(item, attrs.id, protyle);
}
item.classList.remove("fn__none");
} else {
@@ -308,7 +308,7 @@ export const openFileAttr = (attrs: IObject, focusName = "bookmark") => {
});
const inputElement = addDialog.element.querySelector("input") as HTMLInputElement;
const btnsElement = addDialog.element.querySelectorAll(".b3-button");
- dialog.bindInput(inputElement, () => {
+ addDialog.bindInput(inputElement, () => {
(btnsElement[1] as HTMLButtonElement).click();
});
inputElement.focus();
@@ -349,13 +349,13 @@ export const openFileAttr = (attrs: IObject, focusName = "bookmark") => {
});
};
-export const openAttr = (nodeElement: Element, focusName = "bookmark") => {
+export const openAttr = (nodeElement: Element, focusName = "bookmark", protyle?: IProtyle) => {
if (nodeElement.getAttribute("data-type") === "NodeThematicBreak") {
return;
}
const id = nodeElement.getAttribute("data-node-id");
fetchPost("/api/attr/getBlockAttrs", {id}, (response) => {
- openFileAttr(response.data, focusName);
+ openFileAttr(response.data, focusName, protyle);
});
};
@@ -431,6 +431,7 @@ export const exportMd = (id: string) => {
icon: "iconUpload",
submenu: [{
label: window.siyuan.languages.template,
+ iconClass: "ft__error",
icon: "iconMarkdown",
click: async () => {
const result = await fetchSyncPost("/api/block/getRefText", {id: id});
@@ -473,7 +474,7 @@ export const exportMd = (id: string) => {
fetchPost("/api/template/docSaveAsTemplate", {
id,
- name,
+ name: inputElement.value,
overwrite: false
}, response => {
if (response.code === 1) {
@@ -481,7 +482,7 @@ export const exportMd = (id: string) => {
confirmDialog(window.siyuan.languages.export, window.siyuan.languages.exportTplTip, () => {
fetchPost("/api/template/docSaveAsTemplate", {
id,
- name,
+ name: inputElement.value,
overwrite: true
}, resp => {
if (resp.code === 0) {
@@ -493,7 +494,6 @@ export const exportMd = (id: string) => {
}
showMessage(window.siyuan.languages.exportTplSucc);
});
-
dialog.destroy();
});
}
@@ -537,6 +537,7 @@ export const exportMd = (id: string) => {
}
}, {
label: "HTML (SiYuan)",
+ iconClass: "ft__error",
icon: "iconHTML5",
click: () => {
saveExport({type: "html", id});
diff --git a/app/src/menus/navigation.ts b/app/src/menus/navigation.ts
index 329599ab014..98217be73dd 100644
--- a/app/src/menus/navigation.ts
+++ b/app/src/menus/navigation.ts
@@ -26,8 +26,11 @@ import {viewCards} from "../card/viewCards";
import {App} from "../index";
import {openDocHistory} from "../history/doc";
import {openEditorTab} from "./util";
+import {makeCard} from "../card/makeCard";
+import {transaction} from "../protyle/wysiwyg/transaction";
+import {emitOpenMenu} from "../plugin/EventBus";
-const initMultiMenu = (selectItemElements: NodeListOf) => {
+const initMultiMenu = (selectItemElements: NodeListOf, app: App) => {
const fileItemElement = Array.from(selectItemElements).find(item => {
if (item.getAttribute("data-type") === "navigation-file") {
return true;
@@ -47,6 +50,73 @@ const initMultiMenu = (selectItemElements: NodeListOf) => {
deleteFiles(Array.from(selectItemElements));
}
}).element);
+
+ const blockIDs: string[] = [];
+ selectItemElements.forEach(item => {
+ const id = item.getAttribute("data-node-id");
+ if (id) {
+ blockIDs.push(id);
+ }
+ });
+ if (blockIDs.length === 0) {
+ return window.siyuan.menus.menu;
+ }
+ window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
+ const riffCardMenu = [{
+ iconHTML: Constants.ZWSP,
+ accelerator: window.siyuan.config.keymap.editor.general.quickMakeCard.custom,
+ label: window.siyuan.languages.quickMakeCard,
+ click: () => {
+ transaction(undefined, [{
+ action: "addFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs,
+ }], [{
+ action: "removeFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs,
+ }]);
+ }
+ }, {
+ iconHTML: Constants.ZWSP,
+ label: `${window.siyuan.languages.cancel} ${window.siyuan.languages.quickMakeCard}`,
+ click: () => {
+ transaction(undefined, [{
+ action: "removeFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs,
+ }], [{
+ action: "addFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs,
+ }]);
+ }
+ }];
+ if (window.siyuan.config.flashcard.deck) {
+ riffCardMenu.push({
+ iconHTML: Constants.ZWSP,
+ label: window.siyuan.languages.addToDeck,
+ click: () => {
+ makeCard(app, blockIDs);
+ }
+ });
+ }
+ window.siyuan.menus.menu.append(new MenuItem({
+ label: window.siyuan.languages.riffCard,
+ icon: "iconRiffCard",
+ submenu: riffCardMenu,
+ }).element);
+ if (app.plugins) {
+ emitOpenMenu({
+ plugins: app.plugins,
+ type: "open-menu-doctree",
+ detail: {
+ elements: selectItemElements,
+ type: "docs"
+ },
+ separatorPosition: "top",
+ });
+ }
return window.siyuan.menus.menu;
};
@@ -66,7 +136,7 @@ export const initNavigationMenu = (app: App, liElement: HTMLElement) => {
}
const selectItemElements = fileElement.querySelectorAll(".b3-list-item--focus");
if (selectItemElements.length > 1) {
- return initMultiMenu(selectItemElements);
+ return initMultiMenu(selectItemElements, app);
}
const notebookId = liElement.parentElement.getAttribute("data-url");
const name = getNotebookName(notebookId);
@@ -267,6 +337,17 @@ export const initNavigationMenu = (app: App, liElement: HTMLElement) => {
}
}]
}).element);
+ if (app.plugins) {
+ emitOpenMenu({
+ plugins: app.plugins,
+ type: "open-menu-doctree",
+ detail: {
+ elements: selectItemElements,
+ type: "notebook"
+ },
+ separatorPosition: "top",
+ });
+ }
return window.siyuan.menus.menu;
};
@@ -286,7 +367,7 @@ export const initFileMenu = (app: App, notebookId: string, pathString: string, l
}
const selectItemElements = fileElement.querySelectorAll(".b3-list-item--focus");
if (selectItemElements.length > 1) {
- return initMultiMenu(selectItemElements);
+ return initMultiMenu(selectItemElements, app);
}
const id = liElement.getAttribute("data-node-id");
let name = liElement.getAttribute("data-name");
@@ -306,7 +387,13 @@ export const initFileMenu = (app: App, notebookId: string, pathString: string, l
paths.push(item.getAttribute("data-path"));
}
});
- newFile(app, notebookId, pathPosix().dirname(pathString), paths);
+ newFile({
+ app,
+ notebookId,
+ currentPath: pathPosix().dirname(pathString),
+ paths,
+ useSavePath: false
+ });
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
@@ -322,7 +409,13 @@ export const initFileMenu = (app: App, notebookId: string, pathString: string, l
}
}
});
- newFile(app, notebookId, pathPosix().dirname(pathString), paths);
+ newFile({
+ app,
+ notebookId,
+ currentPath: pathPosix().dirname(pathString),
+ paths,
+ useSavePath: false
+ });
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
@@ -370,36 +463,75 @@ export const initFileMenu = (app: App, notebookId: string, pathString: string, l
});
}
}).element);
+ const riffCardMenu = [{
+ iconHTML: Constants.ZWSP,
+ label: window.siyuan.languages.spaceRepetition,
+ accelerator: window.siyuan.config.keymap.editor.general.spaceRepetition.custom,
+ click: () => {
+ fetchPost("/api/riff/getTreeRiffDueCards", {rootID: id}, (response) => {
+ openCardByData(app, response.data, "doc", id, name);
+ });
+ /// #if MOBILE
+ closePanel();
+ /// #endif
+ }
+ }, {
+ iconHTML: Constants.ZWSP,
+ label: window.siyuan.languages.manage,
+ click: () => {
+ fetchPost("/api/filetree/getHPathByID", {
+ id
+ }, (response) => {
+ viewCards(app, id, pathPosix().join(getNotebookName(notebookId), response.data), "Tree");
+ });
+ /// #if MOBILE
+ closePanel();
+ /// #endif
+ }
+ }, {
+ iconHTML: Constants.ZWSP,
+ accelerator: window.siyuan.config.keymap.editor.general.quickMakeCard.custom,
+ label: window.siyuan.languages.quickMakeCard,
+ click: () => {
+ transaction(undefined, [{
+ action: "addFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs: [id]
+ }], [{
+ action: "removeFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs: [id]
+ }]);
+ }
+ }, {
+ iconHTML: Constants.ZWSP,
+ label: `${window.siyuan.languages.cancel} ${window.siyuan.languages.quickMakeCard}`,
+ click: () => {
+ transaction(undefined, [{
+ action: "removeFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs: [id]
+ }], [{
+ action: "addFlashcards",
+ deckID: Constants.QUICK_DECK_ID,
+ blockIDs: [id]
+ }]);
+ }
+ }];
+ if (window.siyuan.config.flashcard.deck) {
+ riffCardMenu.push({
+ iconHTML: Constants.ZWSP,
+ label: window.siyuan.languages.addToDeck,
+ click: () => {
+ makeCard(app, [id]);
+ }
+ });
+ }
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.riffCard,
type: "submenu",
icon: "iconRiffCard",
- submenu: [{
- iconHTML: Constants.ZWSP,
- label: window.siyuan.languages.spaceRepetition,
- accelerator: window.siyuan.config.keymap.editor.general.spaceRepetition.custom,
- click: () => {
- fetchPost("/api/riff/getTreeRiffDueCards", {rootID: id}, (response) => {
- openCardByData(app, response.data, "doc", id, name);
- });
- /// #if MOBILE
- closePanel();
- /// #endif
- }
- }, {
- iconHTML: Constants.ZWSP,
- label: window.siyuan.languages.manage,
- click: () => {
- fetchPost("/api/filetree/getHPathByID", {
- id
- }, (response) => {
- viewCards(app, id, pathPosix().join(getNotebookName(notebookId), response.data), "Tree");
- });
- /// #if MOBILE
- closePanel();
- /// #endif
- }
- }],
+ submenu: riffCardMenu,
}).element);
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.search,
@@ -485,10 +617,21 @@ export const initFileMenu = (app: App, notebookId: string, pathString: string, l
}
genImportMenu(notebookId, pathString);
window.siyuan.menus.menu.append(exportMd(id));
+ if (app.plugins) {
+ emitOpenMenu({
+ plugins: app.plugins,
+ type: "open-menu-doctree",
+ detail: {
+ elements: selectItemElements,
+ type: "doc"
+ },
+ separatorPosition: "top",
+ });
+ }
return window.siyuan.menus.menu;
};
-const genImportMenu = (notebookId: string, pathString: string) => {
+export const genImportMenu = (notebookId: string, pathString: string) => {
if (!window.siyuan.config.readonly) {
/// #if !BROWSER
const importstdmd = (label: string, isDoc?: boolean) => {
diff --git a/app/src/menus/protyle.ts b/app/src/menus/protyle.ts
index c0b7731e33d..bbd82b0a93b 100644
--- a/app/src/menus/protyle.ts
+++ b/app/src/menus/protyle.ts
@@ -193,7 +193,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
openFileById({
app: protyle.app,
id: refBlockId,
- action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
zoomIn: foldResponse.data
});
});
@@ -207,7 +207,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
openFileById({
app: protyle.app,
id: refBlockId,
- action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT],
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
keepCursor: true,
zoomIn: foldResponse.data
});
@@ -224,7 +224,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
app: protyle.app,
id: refBlockId,
position: "right",
- action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
zoomIn: foldResponse.data
});
});
@@ -240,7 +240,7 @@ export const refMenu = (protyle: IProtyle, element: HTMLElement) => {
app: protyle.app,
id: refBlockId,
position: "bottom",
- action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
zoomIn: foldResponse.data
});
});
diff --git a/app/src/menus/util.ts b/app/src/menus/util.ts
index df681c60a3a..7338a6633a3 100644
--- a/app/src/menus/util.ts
+++ b/app/src/menus/util.ts
@@ -1,11 +1,10 @@
/// #if !BROWSER
import {dialog} from "@electron/remote";
import {SaveDialogReturnValue} from "electron";
-import {shell} from "electron";
import * as path from "path";
/// #endif
import {fetchPost} from "../util/fetch";
-import {getAssetName, pathPosix} from "../util/pathName";
+import {getAssetName, pathPosix, showFileInFolder} from "../util/pathName";
import {openFileById} from "../editor/util";
import {Constants} from "../constants";
import {openNewWindowById} from "../window/openNewWindow";
@@ -40,14 +39,38 @@ export const openEditorTab = (app: App, id: string, notebookId?: string, pathStr
label: window.siyuan.languages.insertRight,
accelerator: `${updateHotkeyTip(window.siyuan.config.keymap.editor.general.insertRight.custom)}/${updateHotkeyTip("⌥Click")}`,
click: () => {
- openFileById({app, id, position: "right", action: [Constants.CB_GET_FOCUS]});
+ if (notebookId) {
+ openFileById({app, id, position: "right", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL]});
+ } else {
+ fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
+ openFileById({
+ app,
+ id,
+ position: "right",
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
+ zoomIn: foldResponse.data
+ });
+ });
+ }
}
}, {
icon: "iconLayoutBottom",
label: window.siyuan.languages.insertBottom,
accelerator: "⇧Click",
click: () => {
- openFileById({app, id, position: "bottom", action: [Constants.CB_GET_FOCUS]});
+ if (notebookId) {
+ openFileById({app, id, position: "bottom", action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL]});
+ } else {
+ fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
+ openFileById({
+ app,
+ id,
+ position: "bottom",
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
+ zoomIn: foldResponse.data
+ });
+ });
+ }
}
}];
if (window.siyuan.config.fileTree.openFilesUseCurrentTab) {
@@ -55,11 +78,19 @@ export const openEditorTab = (app: App, id: string, notebookId?: string, pathStr
label: window.siyuan.languages.openInNewTab,
accelerator: "⌥⌘Click",
click: () => {
- openFileById({
- app,
- id, action: [Constants.CB_GET_FOCUS],
- removeCurrentTab: false
- });
+ if (notebookId) {
+ openFileById({app, id, action: [Constants.CB_GET_FOCUS, Constants.CB_GET_SCROLL], removeCurrentTab: false});
+ } else {
+ fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
+ openFileById({
+ app,
+ id,
+ action: foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
+ zoomIn: foldResponse.data,
+ removeCurrentTab: false
+ });
+ });
+ }
}
});
}
@@ -82,20 +113,18 @@ export const openEditorTab = (app: App, id: string, notebookId?: string, pathStr
});
/// #if !BROWSER
openSubmenus.push({type: "separator"});
- if (!window.siyuan.config.readonly) {
- openSubmenus.push({
- label: window.siyuan.languages.showInFolder,
- click: () => {
- if (notebookId) {
- shell.showItemInFolder(path.join(window.siyuan.config.system.dataDir, notebookId, pathString));
- } else {
- fetchPost("/api/block/getBlockInfo", {id}, (response) => {
- shell.showItemInFolder(path.join(window.siyuan.config.system.dataDir, response.data.box, response.data.path));
- });
- }
+ openSubmenus.push({
+ label: window.siyuan.languages.showInFolder,
+ click: () => {
+ if (notebookId) {
+ showFileInFolder(path.join(window.siyuan.config.system.dataDir, notebookId, pathString));
+ } else {
+ fetchPost("/api/block/getBlockInfo", {id}, (response) => {
+ showFileInFolder(path.join(window.siyuan.config.system.dataDir, response.data.box, response.data.path));
+ });
}
- });
- }
+ }
+ });
/// #endif
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.openBy,
diff --git a/app/src/menus/workspace.ts b/app/src/menus/workspace.ts
index e088fc66bc7..41ee51035b7 100644
--- a/app/src/menus/workspace.ts
+++ b/app/src/menus/workspace.ts
@@ -1,10 +1,10 @@
import {MenuItem} from "./Menu";
/// #if !BROWSER
import {dialog, getCurrentWindow} from "@electron/remote";
-import {ipcRenderer, shell} from "electron";
+import {ipcRenderer} from "electron";
/// #endif
import {openHistory} from "../history/history";
-import {getOpenNotebookCount, originalPath, pathPosix} from "../util/pathName";
+import {getOpenNotebookCount, originalPath, pathPosix, showFileInFolder} from "../util/pathName";
import {mountHelp, newDailyNote} from "../util/mount";
import {fetchPost} from "../util/fetch";
import {Constants} from "../constants";
@@ -449,7 +449,7 @@ const workspaceItem = (item: IWorkspace) => {
iconHTML: Constants.ZWSP,
label: window.siyuan.languages.showInFolder,
click() {
- shell.showItemInFolder(item.path);
+ showFileInFolder(item.path);
}
}, {
iconHTML: Constants.ZWSP,
diff --git a/app/src/mobile/dock/MobileBookmarks.ts b/app/src/mobile/dock/MobileBookmarks.ts
index 71d0830fc45..f95eb0356f2 100644
--- a/app/src/mobile/dock/MobileBookmarks.ts
+++ b/app/src/mobile/dock/MobileBookmarks.ts
@@ -39,7 +39,7 @@ export class MobileBookmarks {
}
}
fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
- openMobileFileById(app, id, foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL, Constants.CB_GET_HTML] : [Constants.CB_GET_FOCUS, Constants.CB_GET_SETID, Constants.CB_GET_CONTEXT, Constants.CB_GET_HTML]);
+ openMobileFileById(app, id, foldResponse.data ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
});
},
blockExtHTML: '',
diff --git a/app/src/mobile/dock/MobileFiles.ts b/app/src/mobile/dock/MobileFiles.ts
index 4599907bc3b..444a5dfda30 100644
--- a/app/src/mobile/dock/MobileFiles.ts
+++ b/app/src/mobile/dock/MobileFiles.ts
@@ -40,8 +40,22 @@ export class MobileFiles extends Model {
this.onMount(data);
break;
case "createnotebook":
- setNoteBook();
- this.element.insertAdjacentHTML("beforeend", this.genNotebook(data.data.box));
+ setNoteBook((notebooks) => {
+ let previousId: string;
+ notebooks.find(item => {
+ if (!item.closed) {
+ if (item.id === data.data.box.id) {
+ if (previousId) {
+ this.element.querySelector(`.b3-list[data-url="${previousId}"]`).insertAdjacentHTML("afterend", this.genNotebook(data.data.box));
+ } else {
+ this.element.insertAdjacentHTML("afterbegin", this.genNotebook(data.data.box));
+ }
+ return true;
+ }
+ previousId = item.id;
+ }
+ });
+ });
break;
case "unmount":
case "removeDoc":
@@ -179,7 +193,12 @@ export class MobileFiles extends Model {
const notebookId = ulElement.getAttribute("data-url");
if (!window.siyuan.config.readonly) {
if (type === "new") {
- newFile(app, notebookId, pathString);
+ newFile({
+ app,
+ notebookId,
+ currentPath:pathString,
+ useSavePath: false
+ });
} else if (type === "more-root") {
initNavigationMenu(app, target.parentElement);
window.siyuan.menus.menu.fullscreen("bottom");
diff --git a/app/src/mobile/index.ts b/app/src/mobile/index.ts
index 63380459f61..0b37fe52368 100644
--- a/app/src/mobile/index.ts
+++ b/app/src/mobile/index.ts
@@ -2,7 +2,7 @@ import {addScript, addScriptSync} from "../protyle/util/addScript";
import {Constants} from "../constants";
import {onMessage} from "./util/onMessage";
import {genUUID} from "../util/genID";
-import {hasClosestByAttribute, hasTopClosestByClassName} from "../protyle/util/hasClosest";
+import {hasClosestBlock, hasClosestByAttribute, hasTopClosestByClassName} from "../protyle/util/hasClosest";
import {Model} from "../layout/Model";
import "../assets/scss/mobile.scss";
import {Menus} from "../menus";
@@ -16,13 +16,15 @@ import {initMessage, showMessage} from "../dialog/message";
import {goBack} from "./util/MobileBackFoward";
import {hideKeyboardToolbar, showKeyboardToolbar} from "./util/keyboardToolbar";
import {getLocalStorage, writeText} from "../protyle/util/compatibility";
-import {openMobileFileById} from "./editor";
+import {getCurrentEditor, openMobileFileById} from "./editor";
import {getSearch} from "../util/functions";
import {initRightMenu} from "./menu";
import {openChangelog} from "../boot/openChangelog";
import {registerServiceWorker} from "../util/serviceWorker";
import {afterLoadPlugin, loadPlugins} from "../plugin/loader";
import {saveScroll} from "../protyle/scroll/saveScroll";
+import {removeBlock} from "../protyle/wysiwyg/remove";
+import {isNotEditBlock} from "../protyle/wysiwyg/getBlock";
class App {
public plugins: import("../plugin").Plugin[] = [];
@@ -68,7 +70,7 @@ class App {
}
});
window.addEventListener("beforeunload", () => {
- saveScroll(window.siyuan.mobile.editor.protyle);
+ saveScroll(window.siyuan.mobile.editor.protyle);
}, false);
window.addEventListener("pagehide", () => {
saveScroll(window.siyuan.mobile.editor.protyle);
@@ -108,6 +110,25 @@ class App {
document.addEventListener("touchend", (event) => {
handleTouchEnd(event, siyuanApp);
}, false);
+ // 移动端删除键 https://github.com/siyuan-note/siyuan/issues/9259
+ window.addEventListener("keydown", (event) => {
+ if (getSelection().rangeCount > 0) {
+ const range = getSelection().getRangeAt(0);
+ const editor = getCurrentEditor();
+ if (range.toString() === "" &&
+ editor && editor.protyle.wysiwyg.element.contains(range.startContainer) &&
+ !event.altKey && (event.key === "Backspace" || event.key === "Delete")) {
+ const nodeElement = hasClosestBlock(range.startContainer);
+ if (nodeElement && isNotEditBlock(nodeElement)) {
+ nodeElement.classList.add("protyle-wysiwyg--select");
+ removeBlock(editor.protyle, nodeElement, range);
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ }
+ }
+ });
});
}
}
@@ -130,7 +151,7 @@ window.hideKeyboardToolbar = hideKeyboardToolbar;
window.openFileByURL = (openURL) => {
if (openURL && isSYProtocol(openURL)) {
openMobileFileById(siyuanApp, getIdFromSYProtocol(openURL),
- getSearch("focus", openURL) === "1" ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT]);
+ getSearch("focus", openURL) === "1" ? [Constants.CB_GET_ALL, Constants.CB_GET_FOCUS] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
return true;
}
return false;
diff --git a/app/src/mobile/menu/search.ts b/app/src/mobile/menu/search.ts
index 809d337ce1f..637b6692267 100644
--- a/app/src/mobile/menu/search.ts
+++ b/app/src/mobile/menu/search.ts
@@ -18,7 +18,8 @@ import {App} from "../../index";
import {
assetFilterMenu,
assetInputEvent,
- assetMethodMenu, assetMoreMenu,
+ assetMethodMenu,
+ assetMoreMenu,
renderNextAssetMark,
renderPreview,
} from "../../search/assets";
@@ -482,6 +483,7 @@ const initSearchEvent = (app: App, element: Element, config: ISearchOption) => {
superBlock: window.siyuan.config.search.superBlock,
paragraph: window.siyuan.config.search.paragraph,
embedBlock: window.siyuan.config.search.embedBlock,
+ databaseBlock: window.siyuan.config.search.databaseBlock,
}
}, config);
});
@@ -560,7 +562,7 @@ const initSearchEvent = (app: App, element: Element, config: ISearchOption) => {
preventScroll(window.siyuan.mobile.editor.protyle);
}
fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => {
- openMobileFileById(app, id, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]);
+ openMobileFileById(app, id, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
});
closePanel();
} else {
diff --git a/app/src/mobile/settings/about.ts b/app/src/mobile/settings/about.ts
index 47268097fc7..5f05eeac0e2 100644
--- a/app/src/mobile/settings/about.ts
+++ b/app/src/mobile/settings/about.ts
@@ -9,6 +9,7 @@ import {exitSiYuan, processSync} from "../../dialog/processSystem";
import {pathPosix} from "../../util/pathName";
import {openModel} from "../menu/model";
import {setKey} from "../../sync/syncGuide";
+import {isBrowser} from "../../util/functions";
export const initAbout = () => {
if (!window.siyuan.config.localIPs || window.siyuan.config.localIPs.length === 0 ||
@@ -29,14 +30,17 @@ export const initAbout = () => {
- ${window.siyuan.languages.about2}
-
-
-
${window.siyuan.languages.about3.replace("${port}", location.port)}
-
-
${window.siyuan.config.localIPs.join("
")}
+ ${window.siyuan.languages.about2}
+
+
+
${window.siyuan.languages.about3.replace("${port}", location.port)}
+
+
${window.siyuan.config.localIPs.filter(ip => !(ip.startsWith("[") && ip.endsWith("]"))).join("
")}
+
${window.siyuan.config.localIPs.filter(ip => (ip.startsWith("[") && ip.endsWith("]"))).join("
")}
+
+
${window.siyuan.languages.about18}
-
+
${window.siyuan.languages.about5}
`;
};
export const renderTextMenu = (protyle: IProtyle, toolbarElement: Element) => {
@@ -49,6 +49,15 @@ export const renderTextMenu = (protyle: IProtyle, toolbarElement: Element) => {
`;
});
+ const nodeElements = getFontNodeElements(protyle);
+ let disableFont = false;
+ nodeElements?.find((item: HTMLElement) => {
+ if (item.classList.contains("list") || item.classList.contains("li")) {
+ disableFont = true;
+ return true;
+ }
+ });
+
let lastColorHTML = "";
const lastFonts = window.siyuan.storage[Constants.LOCAL_FONTSTYLES];
if (lastFonts.length > 0) {
@@ -82,9 +91,11 @@ export const renderTextMenu = (protyle: IProtyle, toolbarElement: Element) => {
`;
break;
case "fontSize":
- lastColorHTML += `
+ if (!disableFont) {
+ lastColorHTML += `
${lastFontStatus[1]}
`;
+ }
break;
case "style1":
lastColorHTML += `
@@ -103,7 +114,6 @@ export const renderTextMenu = (protyle: IProtyle, toolbarElement: Element) => {
}
let textElement: HTMLElement;
let fontSize = "16px";
- const nodeElements = getFontNodeElements(protyle);
if (nodeElements && nodeElements.length > 0) {
textElement = nodeElements[0] as HTMLElement;
} else {
@@ -157,8 +167,8 @@ export const renderTextMenu = (protyle: IProtyle, toolbarElement: Element) => {
${window.siyuan.languages.clearFontStyle}
-
${window.siyuan.languages.fontSize}
-
+
${window.siyuan.languages.fontSize}
+
`;
toolbarElement.addEventListener("click", (event) => {
+ const protyle = getCurrentEditor()?.protyle;
const target = event.target as HTMLElement;
const slashBtnElement = hasClosestByClassName(event.target as HTMLElement, "keyboard__slash-item");
- const protyle = getCurrentEditor()?.protyle;
if (slashBtnElement && !slashBtnElement.getAttribute("data-type")) {
const dataValue = decodeURIComponent(slashBtnElement.getAttribute("data-value"));
protyle.hint.fill(dataValue, protyle, false); // 点击后 range 会改变
@@ -625,7 +635,8 @@ export const initKeyboardToolbar = () => {
} else if (type === "more") {
protyle.breadcrumb.showMenu(protyle, {
x: 0,
- y: 0
+ y: 0,
+ isLeft: true
});
activeBlur();
hideKeyboardToolbar();
diff --git a/app/src/mobile/util/setEmpty.ts b/app/src/mobile/util/setEmpty.ts
index f4f1c63b30c..de535073ca2 100644
--- a/app/src/mobile/util/setEmpty.ts
+++ b/app/src/mobile/util/setEmpty.ts
@@ -52,11 +52,21 @@ export const setEmpty = (app: App) => {
break;
} else if (target.id === "emptyNewFile") {
if (window.siyuan.mobile.editor) {
- newFile(app, window.siyuan.mobile.editor.protyle.notebookId, window.siyuan.mobile.editor.protyle.path, undefined, true);
+ newFile({
+ app,
+ notebookId: window.siyuan.mobile.editor.protyle.notebookId,
+ currentPath: window.siyuan.mobile.editor.protyle.path,
+ useSavePath: true
+ });
} else {
window.siyuan.notebooks.find(item => {
if (!item.closed) {
- newFile(app, item.id, "/", undefined, true);
+ newFile({
+ app,
+ notebookId: item.id,
+ currentPath: "/",
+ useSavePath: true
+ });
return true;
}
});
diff --git a/app/src/plugin/API.ts b/app/src/plugin/API.ts
index 0331a9fe9fa..0c493e9982a 100644
--- a/app/src/plugin/API.ts
+++ b/app/src/plugin/API.ts
@@ -27,10 +27,7 @@ openWindow = () => {
};
/// #else
openWindow = (options: {
- position?: {
- x: number,
- y: number,
- },
+ position?: IPosition,
height?: number,
width?: number,
tab?: Tab,
diff --git a/app/src/plugin/Menu.ts b/app/src/plugin/Menu.ts
index 7b97781e4ed..e6744ad6948 100644
--- a/app/src/plugin/Menu.ts
+++ b/app/src/plugin/Menu.ts
@@ -3,10 +3,13 @@ import {Menu as SiyuanMenu} from "../menus/Menu";
export class Menu {
private menu: SiyuanMenu;
public isOpen: boolean;
+ public element: HTMLElement;
constructor(id?: string, closeCB?: () => void) {
this.menu = window.siyuan.menus.menu;
this.isOpen = false;
+ this.element = this.menu.element;
+
if (id) {
const dataName = this.menu.element.getAttribute("data-name");
if (dataName && dataName === id) {
@@ -38,11 +41,11 @@ export class Menu {
this.menu.addSeparator(index);
}
- open(options: { x: number, y: number, h?: number, w?: number, isLeft?: boolean }) {
+ open(options:IPosition) {
if (this.isOpen) {
return;
}
- this.menu.popup(options, options.isLeft);
+ this.menu.popup(options);
}
fullscreen(position: "bottom" | "all" = "all") {
diff --git a/app/src/protyle/breadcrumb/index.ts b/app/src/protyle/breadcrumb/index.ts
index ec6331a6acc..64f1a435354 100644
--- a/app/src/protyle/breadcrumb/index.ts
+++ b/app/src/protyle/breadcrumb/index.ts
@@ -3,7 +3,7 @@ import {fetchPost} from "../../util/fetch";
import {Constants} from "../../constants";
import {MenuItem} from "../../menus/Menu";
import {fullscreen, netImg2LocalAssets} from "./action";
-import {exportMd, openFileAttr} from "../../menus/commonMenuItem";
+import {openFileAttr} from "../../menus/commonMenuItem";
import {setEditMode} from "../util/setEditMode";
import {RecordMedia} from "../util/RecordMedia";
import {hideMessage, showMessage} from "../../dialog/message";
@@ -24,7 +24,6 @@ import {onGet} from "../util/onGet";
import {hideElements} from "../ui/hideElements";
import {confirmDialog} from "../../dialog/confirmDialog";
import {reloadProtyle} from "../util/reload";
-import {deleteFile} from "../../editor/deleteFile";
import {Menu} from "../../plugin/Menu";
import {getNoContainerElement} from "../wysiwyg/getBlock";
import {openTitleMenu} from "../header/openTitleMenu";
@@ -88,7 +87,7 @@ export class Breadcrumb {
fetchPost("/api/block/getDocInfo", {
id: protyle.block.rootID
}, (response) => {
- openFileAttr(response.data.ial);
+ openFileAttr(response.data.ial, "bookmark", protyle);
});
} else {
const targetRect = target.getBoundingClientRect();
@@ -102,6 +101,7 @@ export class Breadcrumb {
this.showMenu(protyle, {
x: targetRect.right,
y: targetRect.bottom,
+ isLeft: true,
});
event.stopPropagation();
event.preventDefault();
@@ -241,7 +241,7 @@ export class Breadcrumb {
}
}
- public showMenu(protyle: IProtyle, position: { x: number, y: number }) {
+ public showMenu(protyle: IProtyle, position:IPosition) {
if (!window.siyuan.menus.menu.element.classList.contains("fn__none") &&
window.siyuan.menus.menu.element.getAttribute("data-name") === "breadcrumbMore") {
window.siyuan.menus.menu.remove();
@@ -415,6 +415,13 @@ export class Breadcrumb {
accelerator: window.siyuan.config.keymap.editor.general.wysiwyg.custom,
click: () => {
setEditMode(protyle, "wysiwyg");
+ protyle.scroll.lastScrollTop = 0;
+ fetchPost("/api/filetree/getDoc", {
+ id: protyle.block.parentID,
+ size: window.siyuan.config.editor.dynamicLoadBlocks,
+ }, getResponse => {
+ onGet({data: getResponse, protyle});
+ });
}
}];
editSubmenu.push({
@@ -511,14 +518,6 @@ export class Breadcrumb {
}).element);
}
/// #endif
- window.siyuan.menus.menu.append(exportMd(protyle.block.showAll ? protyle.block.id : protyle.block.rootID));
- window.siyuan.menus.menu.append(new MenuItem({
- icon: "iconTrashcan",
- label: window.siyuan.languages.delete,
- click: () => {
- deleteFile(protyle.notebookId, protyle.path);
- }
- }).element);
if (protyle?.app?.plugins) {
emitOpenMenu({
plugins: protyle.app.plugins,
@@ -530,7 +529,6 @@ export class Breadcrumb {
separatorPosition: "top",
});
}
-
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
window.siyuan.menus.menu.append(new MenuItem({
iconHTML: Constants.ZWSP,
@@ -544,7 +542,7 @@ export class Breadcrumb {
/// #if MOBILE
window.siyuan.menus.menu.fullscreen();
/// #else
- window.siyuan.menus.menu.popup(position, true);
+ window.siyuan.menus.menu.popup(position);
/// #endif
});
}
diff --git a/app/src/protyle/export/util.ts b/app/src/protyle/export/util.ts
index 4a9c84fa61f..fc49471f86b 100644
--- a/app/src/protyle/export/util.ts
+++ b/app/src/protyle/export/util.ts
@@ -1,6 +1,5 @@
/// #if !BROWSER
import {escapeHtml} from "../../util/escape";
-import {shell} from "electron";
import * as path from "path";
/// #endif
import {hideMessage, showMessage} from "../../dialog/message";
@@ -12,6 +11,7 @@ import {Constants} from "../../constants";
import {highlightRender} from "../render/highlightRender";
import {processRender} from "../util/processCode";
import {openByMobile, setStorageVal} from "../util/compatibility";
+import {showFileInFolder} from "../../util/pathName";
export const afterExport = (exportPath: string, msgId: string) => {
/// #if !BROWSER
@@ -19,7 +19,7 @@ export const afterExport = (exportPath: string, msgId: string) => {
${window.siyuan.languages.showInFolder}`, 6000, "info", msgId);
document.querySelector(`#message [data-id="${msgId}"] button`).addEventListener("click", () => {
- shell.showItemInFolder(path.join(exportPath));
+ showFileInFolder(path.join(exportPath));
hideMessage(msgId);
});
/// #endif
diff --git a/app/src/protyle/gutter/index.ts b/app/src/protyle/gutter/index.ts
index 63a82ebfd67..286d1d35938 100644
--- a/app/src/protyle/gutter/index.ts
+++ b/app/src/protyle/gutter/index.ts
@@ -211,7 +211,7 @@ export class Gutter {
}
foldElement.classList.remove("protyle-wysiwyg--hl");
} else if (window.siyuan.shiftIsPressed && !protyle.disabled) {
- openAttr(protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`));
+ openAttr(protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`), "bookmark", protyle);
} else {
this.renderMenu(protyle, buttonElement);
// https://ld246.com/article/1648433751993
@@ -221,7 +221,7 @@ export class Gutter {
if (isMobile()) {
window.siyuan.menus.menu.fullscreen();
} else {
- window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true);
+ window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16, isLeft: true});
focusByRange(protyle.toolbar.range);
}
}
@@ -233,7 +233,7 @@ export class Gutter {
}
if (!window.siyuan.ctrlIsPressed && !window.siyuan.altIsPressed && !window.siyuan.shiftIsPressed) {
this.renderMenu(protyle, buttonElement);
- window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16}, true);
+ window.siyuan.menus.menu.popup({x: event.clientX - 16, y: event.clientY - 16, isLeft: true});
}
event.preventDefault();
event.stopPropagation();
@@ -698,6 +698,7 @@ export class Gutter {
protyle.toolbar.subElement.style.width = "";
protyle.toolbar.subElement.style.padding = "";
protyle.toolbar.subElement.append(appearanceMenu(protyle, selectsElement));
+ protyle.toolbar.subElement.style.zIndex = (++window.siyuan.zIndex).toString();
protyle.toolbar.subElement.classList.remove("fn__none");
protyle.toolbar.subElementCloseCB = undefined;
const position = selectsElement[0].getBoundingClientRect();
@@ -1035,7 +1036,7 @@ export class Gutter {
submenu: turnIntoSubmenu
}).element);
}
- if (!protyle.disabled) {
+ if (!protyle.disabled && !nodeElement.classList.contains("hr")) {
AIActions([nodeElement], protyle);
}
const copyMenu = (copySubMenu(id, true, nodeElement) as IMenu[]).concat([{
@@ -1218,7 +1219,7 @@ export class Gutter {
updateTransaction(protyle, id, nodeElement.outerHTML, html);
html = nodeElement.outerHTML;
event.stopPropagation();
- const chartInstance = echarts.getInstanceById(nodeElement.firstElementChild.nextElementSibling.getAttribute("_echarts_instance_"));
+ const chartInstance = window.echarts.getInstanceById(nodeElement.firstElementChild.nextElementSibling.getAttribute("_echarts_instance_"));
if (chartInstance) {
chartInstance.resize();
}
@@ -1478,7 +1479,7 @@ export class Gutter {
icon: "iconAttr",
accelerator: window.siyuan.config.keymap.editor.general.attr.custom + "/" + updateHotkeyTip("⇧Click"),
click() {
- openAttr(nodeElement);
+ openAttr(nodeElement, "bookmark", protyle);
}
}).element);
}
@@ -1497,6 +1498,7 @@ export class Gutter {
protyle.toolbar.subElement.style.width = "";
protyle.toolbar.subElement.style.padding = "";
protyle.toolbar.subElement.append(appearanceMenu(protyle, [nodeElement]));
+ protyle.toolbar.subElement.style.zIndex = (++window.siyuan.zIndex).toString();
protyle.toolbar.subElement.classList.remove("fn__none");
protyle.toolbar.subElementCloseCB = undefined;
const position = nodeElement.getBoundingClientRect();
@@ -1579,13 +1581,17 @@ export class Gutter {
id,
level
}, (response) => {
- response.data.doOperations.forEach((operation: IOperation) => {
+ response.data.doOperations.forEach((operation: IOperation, index: number) => {
protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.id}"]`).forEach((itemElement: HTMLElement) => {
itemElement.outerHTML = operation.data;
});
+ // 使用 outer 后元素需要重新查询
protyle.wysiwyg.element.querySelectorAll(`[data-node-id="${operation.id}"]`).forEach((itemElement: HTMLElement) => {
mathRender(itemElement);
});
+ if (index === 0) {
+ focusBlock(protyle.wysiwyg.element.querySelector(`[data-node-id="${operation.id}"]`), protyle.wysiwyg.element, true);
+ }
});
transaction(protyle, response.data.doOperations, response.data.undoOperations);
});
@@ -1876,7 +1882,16 @@ export class Gutter {
(rect.height > Math.floor(window.siyuan.config.editor.fontSize * 1.625) + 8 && rect.height < Math.floor(window.siyuan.config.editor.fontSize * 1.625) * 2 + 8)) {
marginHeight = (rect.height - this.element.clientHeight) / 2;
}
- this.element.style.top = `${Math.max(rect.top, wysiwyg.parentElement.getBoundingClientRect().top) + marginHeight}px`;
+ if (nodeElement.getAttribute("data-type") === "NodeAttributeView") {
+ const iconElement = nodeElement.querySelector(".item__graphic");
+ if (iconElement) {
+ this.element.style.top = `${iconElement.getBoundingClientRect().top - (window.siyuan.config.editor.fontSize * 1.625 - 14) / 2}px`;
+ } else {
+ this.element.style.top = `${Math.max(rect.top, wysiwyg.parentElement.getBoundingClientRect().top) + 8}px`;
+ }
+ } else {
+ this.element.style.top = `${Math.max(rect.top, wysiwyg.parentElement.getBoundingClientRect().top) + marginHeight}px`;
+ }
let left = rect.left - this.element.clientWidth - space;
if (nodeElement.getAttribute("data-type") === "NodeBlockQueryEmbed" && this.element.childElementCount === 1) {
// 嵌入块为列表时
diff --git a/app/src/protyle/header/Background.ts b/app/src/protyle/header/Background.ts
index e01a823f10b..cf9f0cefb98 100644
--- a/app/src/protyle/header/Background.ts
+++ b/app/src/protyle/header/Background.ts
@@ -177,7 +177,13 @@ export class Background {
event.stopPropagation();
break;
} else if (type === "open-emoji") {
- openEmojiPanel(protyle.block.rootID, this.iconElement);
+ const rect = this.iconElement.getBoundingClientRect();
+ openEmojiPanel(protyle.block.rootID, "doc", {
+ x: rect.left,
+ y: rect.bottom,
+ h: rect.height,
+ w: rect.width
+ });
event.preventDefault();
event.stopPropagation();
break;
diff --git a/app/src/protyle/header/Title.ts b/app/src/protyle/header/Title.ts
index d1e629eff1f..de901758d06 100644
--- a/app/src/protyle/header/Title.ts
+++ b/app/src/protyle/header/Title.ts
@@ -25,7 +25,6 @@ import {code160to32} from "../util/code160to32";
import {genEmptyElement} from "../../block/util";
import {transaction} from "../wysiwyg/transaction";
import {hideTooltip} from "../../dialog/tooltip";
-import {quickMakeCard} from "../../card/makeCard";
import {commonClick} from "../wysiwyg/commonClick";
import {openTitleMenu} from "./openTitleMenu";
@@ -140,15 +139,10 @@ export class Title {
fetchPost("/api/block/getDocInfo", {
id: protyle.block.rootID
}, (response) => {
- openFileAttr(response.data.ial);
+ openFileAttr(response.data.ial, "bookmark", protyle);
});
event.preventDefault();
event.stopPropagation();
- } else if (matchHotKey(window.siyuan.config.keymap.editor.general.quickMakeCard.custom, event)) {
- quickMakeCard(protyle, [this.element]);
- event.preventDefault();
- event.stopPropagation();
- return true;
} else if (matchHotKey("⌘A", event)) {
getEditorRange(this.editElement).selectNodeContents(this.editElement);
event.preventDefault();
@@ -179,7 +173,7 @@ export class Title {
fetchPost("/api/block/getDocInfo", {
id: protyle.block.rootID
}, (response) => {
- openFileAttr(response.data.ial);
+ openFileAttr(response.data.ial, "bookmark", protyle);
});
} else {
const iconRect = iconElement.getBoundingClientRect();
@@ -287,7 +281,6 @@ export class Title {
}, Constants.TIMEOUT_INPUT);
}
-
public setTitle(title: string) {
if (code160to32(title) !== code160to32(this.editElement.textContent)) {
this.editElement.textContent = title === "Untitled" ? "" : title;
diff --git a/app/src/protyle/header/openTitleMenu.ts b/app/src/protyle/header/openTitleMenu.ts
index 3f00396383c..395fd123f72 100644
--- a/app/src/protyle/header/openTitleMenu.ts
+++ b/app/src/protyle/header/openTitleMenu.ts
@@ -1,26 +1,34 @@
-import {fetchPost} from "../../util/fetch";
+import {fetchPost, fetchSyncPost} from "../../util/fetch";
import {MenuItem} from "../../menus/Menu";
-import {copySubMenu, movePathToMenu, openFileAttr, openFileWechatNotify} from "../../menus/commonMenuItem";
+import {
+ copySubMenu,
+ exportMd,
+ movePathToMenu,
+ openFileAttr,
+ openFileWechatNotify,
+} from "../../menus/commonMenuItem";
import {deleteFile} from "../../editor/deleteFile";
-import {transferBlockRef} from "../../menus/block";
import {updateHotkeyTip} from "../util/compatibility";
/// #if !MOBILE
import {openBacklink, openGraph, openOutline} from "../../layout/dock/util";
+import * as path from "path";
/// #endif
import {Constants} from "../../constants";
import {openCardByData} from "../../card/openCard";
import {viewCards} from "../../card/viewCards";
-import {getNotebookName, pathPosix} from "../../util/pathName";
+import {getDisplayName, getNotebookName, pathPosix, showFileInFolder} from "../../util/pathName";
import {makeCard, quickMakeCard} from "../../card/makeCard";
import {emitOpenMenu} from "../../plugin/EventBus";
import * as dayjs from "dayjs";
import {hideTooltip} from "../../dialog/tooltip";
+import {popSearch} from "../../mobile/menu/search";
+import {openSearch} from "../../search/spread";
+import {openDocHistory} from "../../history/doc";
+import {openNewWindowById} from "../../window/openNewWindow";
+import {genImportMenu} from "../../menus/navigation";
+import {transferBlockRef} from "../../menus/block";
-export const openTitleMenu = (protyle: IProtyle, position: {
- x: number
- y: number
- isLeft?: boolean
-}) => {
+export const openTitleMenu = (protyle: IProtyle, position: IPosition) => {
hideTooltip();
if (!window.siyuan.menus.menu.element.classList.contains("fn__none") &&
window.siyuan.menus.menu.element.getAttribute("data-name") === "titleMenu") {
@@ -47,47 +55,45 @@ export const openTitleMenu = (protyle: IProtyle, position: {
deleteFile(protyle.notebookId, protyle.path);
}
}).element);
+ }
+ /// #if !MOBILE
+ if (protyle.model) {
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
- if (response.data.refCount && response.data.refCount > 0) {
- transferBlockRef(protyle.block.rootID);
- }
window.siyuan.menus.menu.append(new MenuItem({
- label: window.siyuan.languages.attr,
- icon: "iconAttr",
- accelerator: window.siyuan.config.keymap.editor.general.attr.custom + "/" + updateHotkeyTip("⇧Click"),
- click() {
- openFileAttr(response.data.ial);
+ icon: "iconAlignCenter",
+ label: window.siyuan.languages.outline,
+ accelerator: window.siyuan.config.keymap.editor.general.outline.custom,
+ click: () => {
+ openOutline(protyle);
+ }
+ }).element);
+ window.siyuan.menus.menu.append(new MenuItem({
+ icon: "iconLink",
+ label: window.siyuan.languages.backlinks,
+ accelerator: window.siyuan.config.keymap.editor.general.backlinks.custom,
+ click: () => {
+ openBacklink(protyle);
+ }
+ }).element);
+ window.siyuan.menus.menu.append(new MenuItem({
+ icon: "iconGraph",
+ label: window.siyuan.languages.graphView,
+ accelerator: window.siyuan.config.keymap.editor.general.graphView.custom,
+ click: () => {
+ openGraph(protyle);
}
}).element);
}
- /// #if !MOBILE
+ /// #endif
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
window.siyuan.menus.menu.append(new MenuItem({
- icon: "iconAlignCenter",
- label: window.siyuan.languages.outline,
- accelerator: window.siyuan.config.keymap.editor.general.outline.custom,
- click: () => {
- openOutline(protyle);
- }
- }).element);
- window.siyuan.menus.menu.append(new MenuItem({
- icon: "iconLink",
- label: window.siyuan.languages.backlinks,
- accelerator: window.siyuan.config.keymap.editor.general.backlinks.custom,
- click: () => {
- openBacklink(protyle);
- }
- }).element);
- window.siyuan.menus.menu.append(new MenuItem({
- icon: "iconGraph",
- label: window.siyuan.languages.graphView,
- accelerator: window.siyuan.config.keymap.editor.general.graphView.custom,
- click: () => {
- openGraph(protyle);
+ label: window.siyuan.languages.attr,
+ icon: "iconAttr",
+ accelerator: window.siyuan.config.keymap.editor.general.attr.custom + "/" + updateHotkeyTip("⇧Click"),
+ click() {
+ openFileAttr(response.data.ial, "bookmark", protyle);
}
}).element);
- /// #endif
- window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.wechatReminder,
icon: "iconMp",
@@ -144,6 +150,72 @@ export const openTitleMenu = (protyle: IProtyle, position: {
submenu: riffCardMenu,
}).element);
+ window.siyuan.menus.menu.append(new MenuItem({
+ label: window.siyuan.languages.search,
+ icon: "iconSearch",
+ accelerator: window.siyuan.config.keymap.general.search.custom,
+ async click() {
+ const searchPath = getDisplayName(protyle.path, false, true);
+ /// #if MOBILE
+ const pathResponse = await fetchSyncPost("/api/filetree/getHPathByPath", {
+ notebook: protyle.notebookId,
+ path: searchPath + ".sy"
+ });
+ const localData = window.siyuan.storage[Constants.LOCAL_SEARCHDATA];
+ popSearch(protyle.app, {
+ removed: localData.removed,
+ sort: localData.sort,
+ group: localData.group,
+ hasReplace: false,
+ method: localData.method,
+ hPath: pathPosix().join(getNotebookName(protyle.notebookId), pathResponse.data),
+ idPath: [pathPosix().join(protyle.notebookId, searchPath)],
+ k: localData.k,
+ r: localData.r,
+ page: 1,
+ types: Object.assign({}, localData.types)
+ });
+ /// #else
+ openSearch({
+ app: protyle.app,
+ hotkey: window.siyuan.config.keymap.general.search.custom,
+ notebookId: protyle.notebookId,
+ searchPath
+ });
+ /// #endif
+ }
+ }).element);
+ if (!protyle.disabled) {
+ transferBlockRef(protyle.block.rootID);
+ }
+ window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
+ /// #if !BROWSER
+ window.siyuan.menus.menu.append(new MenuItem({
+ label: window.siyuan.languages.openByNewWindow,
+ icon: "iconOpenWindow",
+ click() {
+ openNewWindowById(protyle.block.rootID);
+ }
+ }).element);
+ window.siyuan.menus.menu.append(new MenuItem({
+ label: window.siyuan.languages.showInFolder,
+ click: () => {
+ showFileInFolder(path.join(window.siyuan.config.system.dataDir, protyle.notebookId, protyle.path));
+ }
+ }).element);
+ /// #endif
+ if (!protyle.disabled) {
+ window.siyuan.menus.menu.append(new MenuItem({
+ label: window.siyuan.languages.fileHistory,
+ icon: "iconHistory",
+ click() {
+ openDocHistory({app: protyle.app, id: protyle.block.rootID, notebookId: protyle.notebookId, pathString: response.data.name});
+ }
+ }).element);
+ }
+ genImportMenu(protyle.notebookId, protyle.path);
+ window.siyuan.menus.menu.append(exportMd(protyle.block.showAll ? protyle.block.id : protyle.block.rootID));
+
if (protyle?.app?.plugins) {
emitOpenMenu({
plugins: protyle.app.plugins,
@@ -155,7 +227,6 @@ export const openTitleMenu = (protyle: IProtyle, position: {
separatorPosition: "top",
});
}
-
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
window.siyuan.menus.menu.append(new MenuItem({
iconHTML: Constants.ZWSP,
@@ -163,6 +234,10 @@ export const openTitleMenu = (protyle: IProtyle, position: {
label: `${window.siyuan.languages.modifiedAt} ${dayjs(response.data.ial.updated).format("YYYY-MM-DD HH:mm:ss")}
${window.siyuan.languages.createdAt} ${dayjs(response.data.ial.id.substr(0, 14)).format("YYYY-MM-DD HH:mm:ss")}`
}).element);
- window.siyuan.menus.menu.popup(position, position.isLeft);
+ /// #if MOBILE
+ window.siyuan.menus.menu.fullscreen();
+ /// #else
+ window.siyuan.menus.menu.popup(position);
+ /// #endif
});
};
diff --git a/app/src/protyle/hint/extend.ts b/app/src/protyle/hint/extend.ts
index 7ff8c936815..b5306de266a 100644
--- a/app/src/protyle/hint/extend.ts
+++ b/app/src/protyle/hint/extend.ts
@@ -20,6 +20,7 @@ import {hideElements} from "../ui/hideElements";
import {genAssetHTML} from "../../asset/renderAssets";
import {unicode2Emoji} from "../../emoji";
import {avRender} from "../render/av/render";
+import {isPaidUser} from "../../util/needSubscribe";
export const hintSlash = (key: string, protyle: IProtyle) => {
const allList: IHintData[] = [{
@@ -46,11 +47,15 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
filter: ["ai chat"],
value: Constants.ZWSP + 5,
html: '
AI Chat
',
- },/* {
- filter: ["数据库", "属性视图", "shujuku", "shuxingshitu", "sjk", "sxst", "database", "attribute view"],
- value: '
',
- html: `
${window.siyuan.languages.database}
`,
- }, */{
+ }];
+ if (isPaidUser()) {
+ allList.push({
+ filter: ["数据库", "属性视图", "shujuku", "shuxingshitu", "sjk", "sxst", "database", "attribute view"],
+ value: '
',
+ html: `
${window.siyuan.languages.database}
`,
+ });
+ }
+ [{
filter: ["文档", "子文档", "wendang", "wd", "ziwendang", "zwd", "xjwd"],
value: Constants.ZWSP + 4,
html: `
${window.siyuan.languages.newFile}
`,
@@ -246,7 +251,9 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
filter: ["移除样式", "yichuyangshi", "ycys", "remove style"],
value: `style${Constants.ZWSP}`,
html: `
A
${window.siyuan.languages.clearFontStyle}`,
- }];
+ }].forEach(item => {
+ allList.push(item);
+ });
allList.push({
value: "",
html: "separator",
diff --git a/app/src/protyle/hint/index.ts b/app/src/protyle/hint/index.ts
index 3c51766e0bd..ed9193b608d 100644
--- a/app/src/protyle/hint/index.ts
+++ b/app/src/protyle/hint/index.ts
@@ -276,10 +276,10 @@ ${unicode2Emoji(emoji.unicode)}`;
}
this.element.style.width = Math.max(protyle.element.clientWidth / 2, 320) + "px";
if (this.source === "av") {
- const blockElement = hasClosestBlock(protyle.toolbar.range.startContainer);
- if (blockElement) {
- const rowAddRect = blockElement.querySelector(".av__row--add").getBoundingClientRect();
- setPosition(this.element, rowAddRect.left, rowAddRect.bottom, rowAddRect.height);
+ const cellElement = hasClosestByClassName(protyle.toolbar.range.startContainer, "av__cell");
+ if (cellElement) {
+ const cellRect = cellElement.getBoundingClientRect();
+ setPosition(this.element, cellRect.left, cellRect.bottom, cellRect.height);
}
} else {
const textareaPosition = getSelectionPosition(protyle.wysiwyg.element);
@@ -368,7 +368,7 @@ ${genHintItemHTML(item)}
}
lazyLoadEmojiImg(panelElement);
} else {
- this.element.innerHTML = `
+ this.element.innerHTML = `
${filterEmoji(value, 256)}
${unicode2Emoji("2b50")}
@@ -410,9 +410,12 @@ ${genHintItemHTML(item)}
return;
}
if (this.source === "av") {
+ const cellElement = hasClosestByClassName(protyle.toolbar.range.startContainer, "av__cell");
+ if (!cellElement) {
+ return;
+ }
+ const previousID = cellElement.dataset.blockId;
const avID = nodeElement.getAttribute("data-av-id");
- const rowsElement = nodeElement.querySelectorAll(".av__row");
- const previousID = rowsElement[rowsElement.length - 1].getAttribute("data-id");
let tempElement = document.createElement("div");
tempElement.innerHTML = value.replace(/
/g, "").replace(/<\/mark>/g, "");
tempElement = tempElement.firstElementChild as HTMLDivElement;
@@ -421,35 +424,40 @@ ${genHintItemHTML(item)}
const realFileName = fileNames.length === 1 ? fileNames[0] : fileNames[1];
getSavePath(protyle.path, protyle.notebookId, (pathString) => {
fetchPost("/api/filetree/createDocWithMd", {
- hidden: false,
notebook: protyle.notebookId,
path: pathPosix().join(pathString, realFileName),
parentID: protyle.block.rootID,
markdown: ""
}, response => {
transaction(protyle, [{
- action: "insertAttrViewBlock",
+ action: "replaceAttrViewBlock",
avID,
previousID,
- srcIDs: [response.data],
+ nextID: response.data,
+ isDetached: false,
}], [{
- action: "removeAttrViewBlock",
- srcIDs: [response.data],
+ action: "replaceAttrViewBlock",
avID,
+ previousID: response.data,
+ nextID: previousID,
+ isDetached: true,
}]);
});
});
} else {
const sourceId = tempElement.getAttribute("data-id");
transaction(protyle, [{
- action: "insertAttrViewBlock",
+ action: "replaceAttrViewBlock",
avID,
previousID,
- srcIDs: [sourceId],
+ nextID: sourceId,
+ isDetached: false,
}], [{
- action: "removeAttrViewBlock",
- srcIDs: [sourceId],
+ action: "replaceAttrViewBlock",
avID,
+ previousID: sourceId,
+ nextID: previousID,
+ isDetached: true,
}]);
}
return;
@@ -549,7 +557,7 @@ ${genHintItemHTML(item)}
emoji = unicode2Emoji(value) + " ";
}
insertHTML(protyle.lute.SpinBlockDOM(emoji), protyle);
- } else if (["「「", "{{"].includes(this.splitChar) || this.splitChar === "#" || this.splitChar === ":") {
+ } else if (["「「", "「『", "『「", "『『", "{{"].includes(this.splitChar) || this.splitChar === "#" || this.splitChar === ":") {
if (value === "") {
const editElement = getContenteditableElement(nodeElement);
if (editElement.textContent === "") {
@@ -592,7 +600,9 @@ ${genHintItemHTML(item)}
} else if (value === Constants.ZWSP + 2) {
range.deleteContents();
this.fixImageCursor(range);
- protyle.toolbar.showAssets(protyle, nodeElement, range);
+ protyle.toolbar.range = range;
+ const rangePosition = getSelectionPosition(nodeElement, range);
+ protyle.toolbar.showAssets(protyle, {x: rangePosition.left, y: rangePosition.top + 26, w: 0, h: 26});
updateTransaction(protyle, id, nodeElement.outerHTML, html);
return;
} else if (value === Constants.ZWSP + 3) {
@@ -757,8 +767,9 @@ ${genHintItemHTML(item)}
const rect = nodeElement.getBoundingClientRect();
window.siyuan.menus.menu.popup({
x: rect.left,
- y: rect.top
- }, true);
+ y: rect.top,
+ isLeft: true
+ });
const itemElement = window.siyuan.menus.menu.element.querySelector('[data-id="assetSubMenu"]');
itemElement.classList.add("b3-menu__item--show");
window.siyuan.menus.menu.showSubMenu(itemElement.querySelector(".b3-menu__submenu"));
diff --git a/app/src/protyle/index.ts b/app/src/protyle/index.ts
index c2c0c674849..35477bec432 100644
--- a/app/src/protyle/index.ts
+++ b/app/src/protyle/index.ts
@@ -14,7 +14,7 @@ import {WYSIWYG} from "./wysiwyg";
import {Toolbar} from "./toolbar";
import {Gutter} from "./gutter";
import {Breadcrumb} from "./breadcrumb";
-import {onTransaction} from "./wysiwyg/transaction";
+import {onTransaction, transaction} from "./wysiwyg/transaction";
import {fetchPost} from "../util/fetch";
/// #if !MOBILE
import {Title} from "./header/Title";
@@ -113,7 +113,11 @@ export class Protyle {
break;
case "transactions":
data.data[0].doOperations.forEach((item: IOperation) => {
- onTransaction(this.protyle, item, false);
+ if (!this.protyle.preview.element.classList.contains("fn__none")) {
+ this.protyle.preview.render(this.protyle);
+ } else {
+ onTransaction(this.protyle, item, false);
+ }
});
break;
case "readonly":
@@ -216,55 +220,54 @@ export class Protyle {
removeLoading(this.protyle);
return;
}
- if (options.scrollAttr ||
- mergedOptions.action.includes(Constants.CB_GET_CONTEXT) ||
- (mergedOptions.action.includes(Constants.CB_GET_SCROLL) && this.protyle.options.mode !== "preview")) {
- if (!options.scrollAttr) {
- fetchPost("/api/block/getDocInfo", {
- id: options.blockId
- }, (response) => {
- if (response.data.rootID !== options.blockId && mergedOptions.action.includes(Constants.CB_GET_CONTEXT)) {
- // 搜索打开文档等情况需保持上一次历史 https://github.com/siyuan-note/siyuan/issues/9082
- this.getDoc(mergedOptions);
- return;
+ if (options.scrollAttr) {
+ getDocByScroll({
+ protyle: this.protyle,
+ scrollAttr: options.scrollAttr,
+ mergedOptions,
+ cb: () => {
+ this.afterOnGet(mergedOptions);
+ }
+ });
+ } else if (this.protyle.options.mode !== "preview" &&
+ (mergedOptions.action.includes(Constants.CB_GET_SCROLL) || mergedOptions.action.includes(Constants.CB_GET_ROOTSCROLL))) {
+ fetchPost("/api/block/getDocInfo", {
+ id: options.blockId
+ }, (response) => {
+ if (!mergedOptions.action.includes(Constants.CB_GET_SCROLL) &&
+ response.data.rootID !== options.blockId && mergedOptions.action.includes(Constants.CB_GET_ROOTSCROLL)) {
+ // 打开根文档保持上一次历史,否则按照原有 action 执行 https://github.com/siyuan-note/siyuan/issues/9082
+ this.getDoc(mergedOptions);
+ return;
+ }
+ let scrollObj;
+ if (response.data.ial.scroll) {
+ try {
+ scrollObj = JSON.parse(response.data.ial.scroll.replace(/"/g, '"'));
+ } catch (e) {
+ scrollObj = undefined;
}
- let scrollObj;
- if (response.data.ial.scroll) {
- try {
- scrollObj = JSON.parse(response.data.ial.scroll.replace(/"/g, '"'));
- } catch (e) {
- scrollObj = undefined;
+ }
+ if (scrollObj) {
+ scrollObj.rootId = response.data.rootID;
+ getDocByScroll({
+ protyle: this.protyle,
+ scrollAttr: scrollObj,
+ mergedOptions,
+ cb: () => {
+ this.afterOnGet(mergedOptions);
}
- }
- if (scrollObj) {
- scrollObj.rootId = response.data.rootID;
- getDocByScroll({
- protyle: this.protyle,
- scrollAttr: scrollObj,
- mergedOptions,
- cb: () => {
- this.afterOnGet(mergedOptions);
- }
- });
- } else {
- this.getDoc(mergedOptions);
- }
- });
- } else {
- getDocByScroll({
- protyle: this.protyle,
- scrollAttr: options.scrollAttr,
- mergedOptions,
- cb: () => {
- this.afterOnGet(mergedOptions);
- }
- });
- }
+ });
+ } else {
+ this.getDoc(mergedOptions);
+ }
+ });
} else {
this.getDoc(mergedOptions);
}
+ } else {
+ this.protyle.contentElement.classList.add("protyle-content--transition");
}
- this.protyle.contentElement.classList.add("protyle-content--transition");
}
private getDoc(mergedOptions: IOptions) {
@@ -337,6 +340,7 @@ export class Protyle {
if (mergedOptions.after) {
mergedOptions.after(this);
}
+ this.protyle.contentElement.classList.add("protyle-content--transition");
}
private init() {
@@ -385,4 +389,8 @@ export class Protyle {
public insert(html: string, isBlock = false, useProtyleRange = false) {
insertHTML(html, this.protyle, isBlock, useProtyleRange);
}
+
+ public transaction( doOperations: IOperation[], undoOperations?: IOperation[]) {
+ transaction(this.protyle, doOperations, undoOperations);
+ }
}
diff --git a/app/src/protyle/preview/image.ts b/app/src/protyle/preview/image.ts
index e27ab4bbb64..66447a3cf8b 100644
--- a/app/src/protyle/preview/image.ts
+++ b/app/src/protyle/preview/image.ts
@@ -2,7 +2,46 @@ import {Constants} from "../../constants";
import {addScript} from "../util/addScript";
import {fetchPost} from "../../util/fetch";
-export const previewImage = (src: string, id: string) => {
+export const previewImage = (src: string) => {
+ addScript(`${Constants.PROTYLE_CDN}/js/viewerjs/viewer.js?v=1.10.4`, "protyleViewerScript").then(() => {
+ const imagesElement = document.createElement("ul");
+ imagesElement.innerHTML = ``;
+ window.siyuan.viewer = new Viewer(imagesElement, {
+ title: [1, (image: HTMLImageElement, imageData: IObject) => {
+ let name = image.alt;
+ if (!name) {
+ name = image.src.substring(image.src.lastIndexOf("/") + 1);
+ }
+ name = name.substring(0, name.lastIndexOf(".")).replace(/-\d{14}-\w{7}$/, "");
+ return `${name} [${imageData.naturalWidth} × ${imageData.naturalHeight}]`;
+ }],
+ button: false,
+ transition: false,
+ hidden: function () {
+ window.siyuan.viewer.destroy();
+ },
+ toolbar: {
+ zoomIn: true,
+ zoomOut: true,
+ oneToOne: true,
+ reset: true,
+ prev: true,
+ play: true,
+ next: true,
+ rotateLeft: true,
+ rotateRight: true,
+ flipHorizontal: true,
+ flipVertical: true,
+ close: function () {
+ window.siyuan.viewer.destroy();
+ },
+ },
+ });
+ window.siyuan.viewer.show();
+ });
+};
+
+export const previewDocImage = (src: string, id: string) => {
addScript(`${Constants.PROTYLE_CDN}/js/viewerjs/viewer.js?v=1.10.4`, "protyleViewerScript").then(() => {
fetchPost("/api/asset/getDocImageAssets", {id}, (response) => {
const imagesElement = document.createElement("ul");
@@ -17,7 +56,6 @@ export const previewImage = (src: string, id: string) => {
}
});
imagesElement.innerHTML = html;
- // @ts-ignore
window.siyuan.viewer = new Viewer(imagesElement, {
title: [1, (image: HTMLImageElement, imageData: IObject) => {
let name = image.alt;
diff --git a/app/src/protyle/preview/index.ts b/app/src/protyle/preview/index.ts
index 9bffe70671d..7fda735f616 100644
--- a/app/src/protyle/preview/index.ts
+++ b/app/src/protyle/preview/index.ts
@@ -2,7 +2,7 @@ import {openByMobile, writeText} from "../util/compatibility";
import {focusByRange} from "../util/selection";
import {showMessage} from "../../dialog/message";
import {isLocalPath, pathPosix} from "../../util/pathName";
-import {previewImage} from "./image";
+import {previewDocImage} from "./image";
import {needSubscribe} from "../../util/needSubscribe";
import {Constants} from "../../constants";
import {getSearch, isMobile} from "../../util/functions";
@@ -117,7 +117,7 @@ export class Preview {
}
break;
} else if (target.tagName === "IMG") {
- previewImage((event.target as HTMLImageElement).src, protyle.block.rootID);
+ previewDocImage((event.target as HTMLImageElement).src, protyle.block.rootID);
event.stopPropagation();
event.preventDefault();
break;
@@ -155,7 +155,13 @@ export class Preview {
if (this.element.style.display === "none") {
return;
}
-
+ let loadingElement = this.element.querySelector(".fn__loading");
+ if (!loadingElement) {
+ this.element.insertAdjacentHTML("beforeend", `
+
+
`);
+ loadingElement = this.element.querySelector(".fn__loading");
+ }
this.mdTimeoutId = window.setTimeout(() => {
fetchPost("/api/export/preview", {
id: protyle.block.parentID || protyle.options.blockId,
@@ -183,6 +189,7 @@ export class Preview {
}
});
/// #endif
+ loadingElement.remove();
});
}, protyle.options.preview.delay);
}
diff --git a/app/src/protyle/render/abcRender.ts b/app/src/protyle/render/abcRender.ts
index 89f114b9ebe..de566eae104 100644
--- a/app/src/protyle/render/abcRender.ts
+++ b/app/src/protyle/render/abcRender.ts
@@ -2,10 +2,6 @@ import {addScript} from "../util/addScript";
import {Constants} from "../../constants";
import {genIconHTML} from "./util";
-declare const ABCJS: {
- renderAbc(element: Element, text: string, options: { responsive: string }): void;
-};
-
export const abcRender = (element: Element, cdn = Constants.PROTYLE_CDN) => {
let abcElements: Element[] = [];
if (element.getAttribute("data-subtype") === "abc") {
@@ -30,7 +26,7 @@ export const abcRender = (element: Element, cdn = Constants.PROTYLE_CDN) => {
e.lastElementChild.insertAdjacentHTML("beforebegin", `${Constants.ZWSP}`);
}
const renderElement = e.firstElementChild.nextElementSibling as HTMLElement;
- ABCJS.renderAbc(renderElement, Lute.UnEscapeHTMLStr(e.getAttribute("data-content")), {
+ window.ABCJS.renderAbc(renderElement, Lute.UnEscapeHTMLStr(e.getAttribute("data-content")), {
responsive: "resize"
});
renderElement.setAttribute("contenteditable", "false");
diff --git a/app/src/protyle/render/av/action.ts b/app/src/protyle/render/av/action.ts
index 433a55fbe18..72662e23ca5 100644
--- a/app/src/protyle/render/av/action.ts
+++ b/app/src/protyle/render/av/action.ts
@@ -9,10 +9,10 @@ import {emitOpenMenu} from "../../../plugin/EventBus";
import {addCol} from "./addCol";
import {openMenuPanel} from "./openMenuPanel";
import {hintRef} from "../../hint/extend";
-import {hideElements} from "../../ui/hideElements";
import {focusByRange} from "../../util/selection";
import {writeText} from "../../util/compatibility";
import {showMessage} from "../../../dialog/message";
+import {previewImage} from "../../preview/image";
export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLElement }) => {
const blockElement = hasClosestBlock(event.target);
@@ -30,6 +30,7 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
if (protyle.disabled) {
return false;
}
+
const addElement = hasClosestByAttribute(event.target, "data-type", "av-header-add");
if (addElement) {
const addMenu = addCol(protyle, blockElement);
@@ -118,13 +119,23 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
const linkElement = hasClosestByClassName(event.target, "av__celltext--url");
if (linkElement) {
- let prefix = "";
+ let linkAddress = linkElement.textContent.trim();
if (linkElement.dataset.type === "phone") {
- prefix = "tel:";
+ linkAddress = "tel:" + linkAddress;
} else if (linkElement.dataset.type === "email") {
- prefix = "mailto:";
+ linkAddress = "mailto:" + linkAddress;
+ } else if (linkElement.classList.contains("b3-chip")) {
+ linkAddress = linkElement.dataset.url;
}
- window.open(prefix + linkElement.textContent.trim());
+ window.open(linkAddress);
+ event.preventDefault();
+ event.stopPropagation();
+ return true;
+ }
+
+ const imgElement = hasClosestByClassName(event.target, "av__cellassetimg") as HTMLImageElement;
+ if (imgElement) {
+ previewImage(imgElement.src);
event.preventDefault();
event.stopPropagation();
return true;
@@ -138,6 +149,17 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
return true;
}
+ const blockMoreElement = hasClosestByAttribute(event.target, "data-type", "block-more");
+ if (blockMoreElement) {
+ protyle.toolbar.range = document.createRange();
+ protyle.toolbar.range.selectNodeContents(blockMoreElement);
+ focusByRange(protyle.toolbar.range);
+ hintRef(blockMoreElement.previousElementSibling.textContent.trim(), protyle, "av");
+ event.preventDefault();
+ event.stopPropagation();
+ return true;
+ }
+
const cellElement = hasClosestByClassName(event.target, "av__cell");
if (cellElement && !cellElement.parentElement.classList.contains("av__row--header")) {
cellElement.parentElement.parentElement.querySelectorAll(".av__row--select").forEach(item => {
@@ -160,18 +182,27 @@ export const avClick = (protyle: IProtyle, event: MouseEvent & { target: HTMLEle
const addRowElement = hasClosestByClassName(event.target, "av__row--add");
if (addRowElement) {
- if (protyle.hint.element.classList.contains("fn__none")) {
- protyle.toolbar.range = document.createRange();
- protyle.toolbar.range.selectNodeContents(blockElement.querySelector(".av__title"));
- focusByRange(protyle.toolbar.range);
- hintRef("", protyle, "av");
- } else {
- hideElements(["hint"], protyle);
- }
+ const avID = blockElement.getAttribute("data-av-id");
+ const srcIDs = [Lute.NewNodeID()];
+ const previousID = addRowElement.previousElementSibling.getAttribute("data-id") || "";
+ transaction(protyle, [{
+ action: "insertAttrViewBlock",
+ avID,
+ previousID,
+ srcIDs,
+ isDetached: true,
+ }], [{
+ action: "removeAttrViewBlock",
+ srcIDs,
+ avID,
+ }]);
+ insertAttrViewBlockAnimation(blockElement, 1, previousID, avID);
+ popTextCell(protyle, [addRowElement.previousElementSibling.querySelector('[data-detached="true"]')], "block");
event.preventDefault();
event.stopPropagation();
return true;
}
+
return false;
};
@@ -296,3 +327,31 @@ export const updateAVName = (protyle: IProtyle, blockElement: Element) => {
}]);
nameElement.dataset.title = nameElement.textContent.trim();
};
+
+export const updateAttrViewCellAnimation = (cellElement: HTMLElement) => {
+ cellElement.style.opacity = "0.38";
+ cellElement.style.backgroundColor = "var(--b3-theme-surface-light)";
+};
+
+export const removeAttrViewColAnimation = (blockElement: Element, id: string) => {
+ blockElement.querySelectorAll(`.av__cell[data-col-id="${id}"]`).forEach(item => {
+ item.remove();
+ });
+};
+
+export const insertAttrViewBlockAnimation = (blockElement: Element, size: number, previousId: string, avId?: string) => {
+ const previousElement = blockElement.querySelector(`.av__row[data-id="${previousId}"]`) || blockElement.querySelector(".av__row--header");
+ let colHTML = "";
+ previousElement.querySelectorAll(".av__cell").forEach((item: HTMLElement) => {
+ colHTML += `
`;
+ });
+
+ let html = "";
+ new Array(size).fill(1).forEach(() => {
+ html += ``;
+ });
+ previousElement.insertAdjacentHTML("afterend", html);
+};
diff --git a/app/src/protyle/render/av/addCol.ts b/app/src/protyle/render/av/addCol.ts
index 9ca20954573..3202914dd08 100644
--- a/app/src/protyle/render/av/addCol.ts
+++ b/app/src/protyle/render/av/addCol.ts
@@ -1,7 +1,8 @@
import {Menu} from "../../../plugin/Menu";
import {transaction} from "../../wysiwyg/transaction";
+import {addAttrViewColAnimation} from "./col";
-export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
+export const addCol = (protyle: IProtyle, blockElement: Element) => {
const menu = new Menu("av-header-add");
const avID = blockElement.getAttribute("data-av-id");
menu.addItem({
@@ -11,7 +12,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Text",
+ name: window.siyuan.languages.text,
avID,
type: "text",
id
@@ -20,6 +21,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "text",
+ name: window.siyuan.languages.text,
+ id
+ });
}
});
menu.addItem({
@@ -29,7 +37,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Number",
+ name: window.siyuan.languages.number,
avID,
type: "number",
id
@@ -38,6 +46,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "number",
+ name: window.siyuan.languages.number,
+ id
+ });
}
});
menu.addItem({
@@ -47,7 +62,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Select",
+ name: window.siyuan.languages.select,
avID,
type: "select",
id
@@ -56,6 +71,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "select",
+ name: window.siyuan.languages.select,
+ id
+ });
}
});
menu.addItem({
@@ -65,7 +87,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Multi-select",
+ name: window.siyuan.languages.multiSelect,
avID,
type: "mSelect",
id
@@ -74,6 +96,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "mSelect",
+ name: window.siyuan.languages.multiSelect,
+ id
+ });
}
});
menu.addItem({
@@ -83,7 +112,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Date",
+ name: window.siyuan.languages.date,
avID,
type: "date",
id
@@ -92,6 +121,38 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "date",
+ name: window.siyuan.languages.date,
+ id
+ });
+ }
+ });
+ menu.addItem({
+ icon: "iconImage",
+ label: window.siyuan.languages.assets,
+ click() {
+ const id = Lute.NewNodeID();
+ transaction(protyle, [{
+ action: "addAttrViewCol",
+ name: window.siyuan.languages.assets,
+ avID,
+ type: "mAsset",
+ id
+ }], [{
+ action: "removeAttrViewCol",
+ id,
+ avID,
+ }]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "mAsset",
+ name: window.siyuan.languages.assets,
+ id
+ });
}
});
menu.addItem({
@@ -101,7 +162,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "URL",
+ name: window.siyuan.languages.link,
avID,
type: "url",
id
@@ -110,6 +171,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "url",
+ name: window.siyuan.languages.link,
+ id
+ });
}
});
menu.addItem({
@@ -119,7 +187,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Email",
+ name: window.siyuan.languages.email,
avID,
type: "email",
id
@@ -128,6 +196,13 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "email",
+ name: window.siyuan.languages.email,
+ id
+ });
}
});
menu.addItem({
@@ -137,7 +212,7 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
const id = Lute.NewNodeID();
transaction(protyle, [{
action: "addAttrViewCol",
- name: "Phone",
+ name: window.siyuan.languages.phone,
avID,
type: "phone",
id
@@ -146,6 +221,38 @@ export const addCol = (protyle: IProtyle, blockElement: HTMLElement) => {
id,
avID,
}]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "phone",
+ name: window.siyuan.languages.phone,
+ id
+ });
+ }
+ });
+ menu.addItem({
+ icon: "iconMath",
+ label: window.siyuan.languages.template,
+ click() {
+ const id = Lute.NewNodeID();
+ transaction(protyle, [{
+ action: "addAttrViewCol",
+ name: window.siyuan.languages.template,
+ avID,
+ type: "template",
+ id
+ }], [{
+ action: "removeAttrViewCol",
+ id,
+ avID,
+ }]);
+ addAttrViewColAnimation({
+ blockElement: blockElement,
+ protyle: protyle,
+ type: "template",
+ name: window.siyuan.languages.template,
+ id
+ });
}
});
return menu;
diff --git a/app/src/protyle/render/av/asset.ts b/app/src/protyle/render/av/asset.ts
new file mode 100644
index 00000000000..a3ebbeeeefc
--- /dev/null
+++ b/app/src/protyle/render/av/asset.ts
@@ -0,0 +1,325 @@
+import {Menu} from "../../../plugin/Menu";
+import {transaction} from "../../wysiwyg/transaction";
+import {updateAttrViewCellAnimation} from "./action";
+import {isMobile} from "../../../util/functions";
+import {Constants} from "../../../constants";
+import {uploadFiles} from "../../upload";
+import {pathPosix} from "../../../util/pathName";
+import {openMenu} from "../../../menus/commonMenuItem";
+import {MenuItem} from "../../../menus/Menu";
+import {exportAsset} from "../../../menus/util";
+import {setPosition} from "../../../util/setPosition";
+import {previewImage} from "../../preview/image";
+import {genAVValueHTML} from "./blockAttr";
+
+export const bindAssetEvent = (options: {
+ protyle: IProtyle,
+ data: IAV,
+ menuElement: HTMLElement,
+ cellElements: HTMLElement[]
+}) => {
+ options.menuElement.querySelector("input").addEventListener("change", (event: InputEvent & {
+ target: HTMLInputElement
+ }) => {
+ if (event.target.files.length === 0) {
+ return;
+ }
+ uploadFiles(options.protyle, event.target.files, event.target, (res) => {
+ const resData = JSON.parse(res);
+ const value: IAVCellAssetValue[] = [];
+ Object.keys(resData.data.succMap).forEach((key) => {
+ value.push({
+ name: key,
+ content: resData.data.succMap[key],
+ type: Constants.SIYUAN_ASSETS_IMAGE.includes(pathPosix().extname(resData.data.succMap[key]).toLowerCase()) ? "image" : "file"
+ });
+ });
+ updateAssetCell({
+ protyle: options.protyle,
+ data: options.data,
+ cellElements: options.cellElements,
+ type: "addUpdate",
+ addUpdateValue: value
+ });
+ });
+ });
+};
+
+export const getAssetHTML = (data: IAVTable, cellElements: HTMLElement[]) => {
+ const cellId = cellElements[0].dataset.id;
+ const rowId = cellElements[0].parentElement.dataset.id;
+ let cellData: IAVCell;
+ data.rows.find(row => {
+ if (row.id === rowId) {
+ row.cells.find(cell => {
+ if (cell.id === cellId) {
+ cellData = cell;
+ return true;
+ }
+ });
+ return true;
+ }
+ });
+ let html = "";
+ if (cellData?.value?.mAsset) {
+ cellData.value.mAsset.forEach(item => {
+ if (!item.content) {
+ return;
+ }
+ let contentHTML;
+ if (item.type === "image") {
+ contentHTML = `
+
+`;
+ } else {
+ contentHTML = ``;
+ }
+
+ html += ``;
+ });
+ }
+ return ``;
+};
+
+export const updateAssetCell = (options: {
+ protyle: IProtyle,
+ data: IAV,
+ cellElements: HTMLElement[],
+ type: "replace" | "addUpdate" | "remove",
+ replaceValue?: IAVCellAssetValue[],
+ addUpdateValue?: IAVCellAssetValue[],
+ removeContent?: string
+}) => {
+ let cellIndex: number;
+ Array.from(options.cellElements[0].parentElement.querySelectorAll(".av__cell")).find((item: HTMLElement, index) => {
+ if (item.dataset.id === options.cellElements[0].dataset.id) {
+ cellIndex = index;
+ return true;
+ }
+ });
+ const colId = options.cellElements[0].dataset.colId;
+ const cellDoOperations: IOperation[] = [];
+ const cellUndoOperations: IOperation[] = [];
+ let newValue: IAVCellAssetValue[] = [];
+ options.cellElements.forEach((item, elementIndex) => {
+ let cellData: IAVCell;
+ const rowID = item.parentElement.dataset.id;
+ options.data.view.rows.find(row => {
+ if (row.id === rowID) {
+ if (typeof cellIndex === "number") {
+ cellData = row.cells[cellIndex];
+ // 为空时 cellId 每次请求都不一致
+ cellData.id = item.dataset.id;
+ if (!cellData.value || !cellData.value.mAsset) {
+ cellData.value = {mAsset: []} as IAVCellValue;
+ }
+ } else {
+ cellData = row.cells.find(cellItem => {
+ if (cellItem.id === item.dataset.id) {
+ return true;
+ }
+ });
+ }
+ return true;
+ }
+ });
+ const oldValue = Object.assign([], cellData.value.mAsset);
+ if (options.type === "remove") {
+ if (elementIndex === 0) {
+ cellData.value.mAsset.find((oldItem, index) => {
+ if (oldItem.content === options.removeContent) {
+ cellData.value.mAsset.splice(index, 1);
+ return true;
+ }
+ });
+ newValue = cellData.value.mAsset;
+ } else {
+ cellData.value.mAsset = newValue;
+ }
+ } else if (options.type === "addUpdate") {
+ if (elementIndex === 0) {
+ options.addUpdateValue.forEach(newitem => {
+ if (!newitem.content) {
+ return;
+ }
+ const hasMatch = cellData.value.mAsset.find(oldItem => {
+ if (oldItem.content === newitem.content) {
+ oldItem.name = newitem.name;
+ oldItem.type = newitem.type;
+ return true;
+ }
+ });
+ if (!hasMatch) {
+ if (newitem.type === "file" && !newitem.name) {
+ newitem.name = newitem.content;
+ }
+ cellData.value.mAsset.push(newitem);
+ }
+ });
+ newValue = cellData.value.mAsset;
+ } else {
+ cellData.value.mAsset = newValue;
+ }
+ } else {
+ cellData.value.mAsset = options.replaceValue;
+ }
+ cellDoOperations.push({
+ action: "updateAttrViewCell",
+ id: cellData.id,
+ keyID: colId,
+ rowID,
+ avID: options.data.id,
+ data: cellData.value
+ });
+ cellUndoOperations.push({
+ action: "updateAttrViewCell",
+ id: cellData.id,
+ keyID: colId,
+ rowID,
+ avID: options.data.id,
+ data: {
+ mAsset: oldValue
+ }
+ });
+ if (item.classList.contains("custom-attr__avvalue")) {
+ item.innerHTML = genAVValueHTML(cellData.value);
+ } else {
+ updateAttrViewCellAnimation(item);
+ }
+ });
+ transaction(options.protyle, cellDoOperations, cellUndoOperations);
+ const menuElement = document.querySelector(".av__panel > .b3-menu") as HTMLElement;
+ if (menuElement) {
+ menuElement.innerHTML = getAssetHTML(options.data.view, options.cellElements);
+ bindAssetEvent({protyle: options.protyle, data: options.data, menuElement, cellElements: options.cellElements});
+ const cellRect = (options.cellElements[0].classList.contains("custom-attr__avvalue") ? options.cellElements[0] : options.protyle.wysiwyg.element.querySelector(`.av__cell[data-id="${options.cellElements[0].dataset.id}"]`)).getBoundingClientRect();
+ setTimeout(() => {
+ setPosition(menuElement, cellRect.left, cellRect.bottom, cellRect.height);
+ }, Constants.TIMEOUT_LOAD); // 等待图片加载
+ }
+};
+
+export const editAssetItem = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], target: HTMLElement) => {
+ const linkAddress = target.dataset.content;
+ const type = target.dataset.type as "image" | "file";
+ const menu = new Menu("av-asset-edit", () => {
+ if (!textElement || !textElement.value || textElement.value === target.dataset.name) {
+ return;
+ }
+ updateAssetCell({
+ protyle,
+ data,
+ cellElements,
+ type: "addUpdate",
+ addUpdateValue: [{
+ content: linkAddress,
+ name: textElement.value,
+ type
+ }]
+ });
+ });
+ if (menu.isOpen) {
+ return;
+ }
+ if (type === "file") {
+ menu.addItem({
+ iconHTML: "",
+ label: ``,
+ });
+ } else {
+ menu.addItem({
+ icon: "iconPreview",
+ label: window.siyuan.languages.cardPreview,
+ click() {
+ previewImage(linkAddress);
+ }
+ });
+ }
+ menu.addItem({
+ icon: "iconTrashcan",
+ label: window.siyuan.languages.delete,
+ click() {
+ updateAssetCell({
+ protyle,
+ data,
+ cellElements,
+ type: "remove",
+ removeContent: linkAddress
+ });
+ }
+ });
+ openMenu(protyle ? protyle.app : window.siyuan.ws.app, linkAddress, false, true);
+ /// #if !BROWSER
+ if (linkAddress?.startsWith("assets/")) {
+ window.siyuan.menus.menu.append(new MenuItem(exportAsset(linkAddress)).element);
+ }
+ /// #endif
+ const textElement = menu.element.querySelector("textarea");
+ if (textElement) {
+ textElement.value = target.dataset.name;
+ }
+ const rect = target.getBoundingClientRect();
+ menu.open({
+ x: rect.right,
+ y: rect.top,
+ w: rect.width,
+ h: rect.height,
+ });
+};
+
+export const addAssetLink = (protyle: IProtyle, data: IAV, cellElements: HTMLElement[], target: HTMLElement) => {
+ const menu = new Menu("av-asset-link", () => {
+ const textElements = menu.element.querySelectorAll("textarea");
+ if (!textElements[0].value) {
+ return;
+ }
+ updateAssetCell({
+ protyle,
+ data,
+ cellElements,
+ type: "addUpdate",
+ addUpdateValue: [{
+ type: "file",
+ name: textElements[1].value,
+ content: textElements[0].value,
+ }]
+ });
+ });
+ if (menu.isOpen) {
+ return;
+ }
+ menu.addItem({
+ iconHTML: "",
+ label: ``,
+ });
+ menu.addItem({
+ iconHTML: "",
+ label: ``,
+ });
+ const rect = target.getBoundingClientRect();
+ menu.open({
+ x: rect.right,
+ y: rect.bottom,
+ w: target.parentElement.clientWidth + 8,
+ h: rect.height,
+ });
+};
diff --git a/app/src/protyle/render/av/blockAttr.ts b/app/src/protyle/render/av/blockAttr.ts
new file mode 100644
index 00000000000..fdf7de616f5
--- /dev/null
+++ b/app/src/protyle/render/av/blockAttr.ts
@@ -0,0 +1,146 @@
+import {fetchPost} from "../../../util/fetch";
+import {getColIconByType} from "./col";
+import {escapeAttr} from "../../../util/escape";
+import {hasClosestByAttribute} from "../../util/hasClosest";
+import * as dayjs from "dayjs";
+import {popTextCell} from "./cell";
+
+export const genAVValueHTML = (value: IAVCellValue) => {
+ let html = "";
+ switch (value.type) {
+ case "text":
+ html = ``;
+ break;
+ case "number":
+ html = ``;
+ break;
+ case "mSelect":
+ case "select":
+ value.mSelect?.forEach(item => {
+ html += `${item.content}`;
+ });
+ break;
+ case "mAsset":
+ value.mAsset?.forEach(item => {
+ if (item.type === "image") {
+ html += ``;
+ } else {
+ html += `${item.name}`;
+ }
+ });
+ break;
+ case "date":
+ if (value.date.isNotEmpty) {
+ html = `${dayjs(value.date.content).format("YYYY-MM-DD HH:mm")}`;
+ }
+ if (value.date.hasEndDate && value.date.isNotEmpty2 && value.date.isNotEmpty) {
+ html += `${dayjs(value.date.content2).format("YYYY-MM-DD HH:mm")}`;
+ }
+ break;
+ case "url":
+ html = `
+
+`;
+ break;
+ case "phone":
+ html = `
+
+`;
+ break;
+ case "template":
+ html = `${value.template.content}
`;
+ break;
+ case "email":
+ html = `
+
+`;
+ break;
+ }
+ return html;
+};
+
+export const renderAVAttribute = (element: HTMLElement, id: string, protyle?: IProtyle) => {
+ fetchPost("/api/av/getAttributeViewKeys", {id}, (response) => {
+ let html = "";
+ response.data.forEach((table: {
+ keyValues: {
+ key: {
+ type: TAVCol,
+ name: string,
+ options?: { name: string, color: string }[]
+ },
+ values: { keyID: string, id: string, blockID: string, type?: TAVCol & IAVCellValue } []
+ }[],
+ avID: string
+ avName: string
+ }) => {
+ html += ``;
+ table.keyValues?.forEach(item => {
+ html += `
+
+
+ ${item.key.name}
+
+
+ ${genAVValueHTML(item.values[0])}
+
+
`;
+ });
+ });
+ element.innerHTML = html;
+ element.addEventListener("click", (event) => {
+ const target = event.target as HTMLElement;
+ const dateElement = hasClosestByAttribute(target, "data-type", "date");
+ if (dateElement) {
+ popTextCell(protyle, [dateElement], "date");
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ const mSelectElement = hasClosestByAttribute(target, "data-type", "select") || hasClosestByAttribute(target, "data-type", "mSelect");
+ if (mSelectElement) {
+ popTextCell(protyle, [mSelectElement], mSelectElement.getAttribute("data-type") as TAVCol);
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ const mAssetElement = hasClosestByAttribute(target, "data-type", "mAsset");
+ if (mAssetElement) {
+ popTextCell(protyle, [mAssetElement], "mAsset");
+ event.stopPropagation();
+ event.preventDefault();
+ return;
+ }
+ });
+ element.querySelectorAll(".b3-text-field--text").forEach((item: HTMLInputElement) => {
+ item.addEventListener("change", () => {
+ let value;
+ if (["url", "text", "email", "phone"].includes(item.parentElement.dataset.type)) {
+ value = {
+ [item.parentElement.dataset.type]: {
+ content: item.value
+ }
+ };
+ } else if (item.parentElement.dataset.type === "number") {
+ value = {
+ number: {
+ content: parseFloat(item.value)
+ }
+ };
+ }
+ fetchPost("/api/av/setAttributeViewBlockAttr", {
+ avID: item.parentElement.dataset.avId,
+ keyID: item.parentElement.dataset.colId,
+ rowID: item.parentElement.dataset.blockId,
+ cellID: item.parentElement.dataset.id,
+ value
+ });
+ });
+ });
+ });
+};
diff --git a/app/src/protyle/render/av/cell.ts b/app/src/protyle/render/av/cell.ts
index 8ed2b6a85f2..f17d32ba121 100644
--- a/app/src/protyle/render/av/cell.ts
+++ b/app/src/protyle/render/av/cell.ts
@@ -2,6 +2,9 @@ import {transaction} from "../../wysiwyg/transaction";
import {hasClosestBlock, hasClosestByClassName} from "../../util/hasClosest";
import {openMenuPanel} from "./openMenuPanel";
import {Menu} from "../../../plugin/Menu";
+import {updateAttrViewCellAnimation} from "./action";
+import {isCtrl} from "../../util/compatibility";
+import {objEquals} from "../../../util/functions";
export const getCalcValue = (column: IAVColumn) => {
if (!column.calc || !column.calc.result) {
@@ -63,10 +66,7 @@ export const getCalcValue = (column: IAVColumn) => {
return value;
};
-export const genCellValue = (colType: TAVCol, value: string | {
- content: string,
- color: string
-}[] | IAVCellDateValue) => {
+export const genCellValue = (colType: TAVCol, value: string | any) => {
let cellValue: IAVCellValue;
if (typeof value === "string") {
if (colType === "number") {
@@ -117,16 +117,18 @@ export const genCellValue = (colType: TAVCol, value: string | {
if (colType === "mSelect" || colType === "select") {
cellValue = {
type: colType,
- mSelect: value as {
- content: string,
- color: string
- }[]
+ mSelect: value as IAVCellSelectValue[]
};
} else if (colType === "date") {
cellValue = {
type: colType,
date: value as IAVCellDateValue
};
+ } else if (colType === "mAsset") {
+ cellValue = {
+ type: colType,
+ mAsset: value as IAVCellAssetValue[]
+ };
}
}
return cellValue;
@@ -163,6 +165,7 @@ const calcItem = (options: {
}
});
};
+
export const openCalcMenu = (protyle: IProtyle, calcElement: HTMLElement) => {
const blockElement = hasClosestBlock(calcElement);
if (!blockElement) {
@@ -339,22 +342,30 @@ export const openCalcMenu = (protyle: IProtyle, calcElement: HTMLElement) => {
menu.open({x: calcRect.left, y: calcRect.bottom, h: calcRect.height});
};
-export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
- const type = cellElements[0].parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElements[0].getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
- if (type === "block") {
+export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[], type?: TAVCol) => {
+ if (!type) {
+ type = cellElements[0].parentElement.parentElement.firstElementChild.querySelector(`[data-col-id="${cellElements[0].getAttribute("data-col-id")}"]`).getAttribute("data-dtype") as TAVCol;
+ }
+ if (type === "template") {
+ return;
+ }
+ if (type === "block" && (cellElements.length > 1 || !cellElements[0].getAttribute("data-detached"))) {
return;
}
const cellRect = cellElements[0].getBoundingClientRect();
let html = "";
const style = `style="position:absolute;left: ${cellRect.left}px;top: ${cellRect.top}px;width:${Math.max(cellRect.width, 200)}px;height: ${cellRect.height}px"`;
const blockElement = hasClosestBlock(cellElements[0]);
- if (["text", "url", "email", "phone"].includes(type)) {
+ if (["text", "url", "email", "phone", "block"].includes(type)) {
html = ``;
} else if (type === "number") {
html = ``;
} else if (["select", "mSelect"].includes(type) && blockElement) {
openMenuPanel({protyle, blockElement, type: "select", cellElements});
return;
+ } else if (type === "mAsset" && blockElement) {
+ openMenuPanel({protyle, blockElement, type: "asset", cellElements});
+ return;
} else if (type === "date" && blockElement) {
openMenuPanel({protyle, blockElement, type: "date", cellElements});
return;
@@ -375,7 +386,8 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
if (event.isComposing) {
return;
}
- if (event.key === "Escape" || event.key === "Enter") {
+ if (event.key === "Escape" ||
+ (event.key === "Enter" && !event.shiftKey && !isCtrl(event))) {
updateCellValue(protyle, type, cellElements);
event.preventDefault();
event.stopPropagation();
@@ -390,6 +402,14 @@ export const popTextCell = (protyle: IProtyle, cellElements: HTMLElement[]) => {
};
const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElement[]) => {
+ if (!document.contains(cellElements[0]) && cellElements.length === 1 && cellElements[0].dataset.detached === "true") {
+ // 新增行后弹出的输入框进行修改后,原始 cell 已被更新
+ const avid = cellElements[0].parentElement.dataset.avid;
+ cellElements[0] = protyle.wysiwyg.element.querySelector(`[data-av-id="${avid}"] .av__row--add`).previousElementSibling.querySelector('[data-detached="true"]');
+ }
+ if (cellElements.length === 1 && cellElements[0].dataset.detached === "true" && !cellElements[0].parentElement.dataset.id) {
+ return;
+ }
const blockElement = hasClosestBlock(cellElements[0]);
if (!blockElement) {
return;
@@ -411,7 +431,7 @@ const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElem
content: (avMaskElement.querySelector(".b3-text-field") as HTMLInputElement).value
};
const oldValue: { content: string | number, isNotEmpty?: boolean } = {
- content: item.textContent.trim()
+ content: type === "block" ? item.firstElementChild.textContent.trim() : item.textContent.trim()
};
if (type === "number") {
oldValue.content = parseFloat(oldValue.content as string);
@@ -419,6 +439,9 @@ const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElem
inputValue.content = parseFloat(inputValue.content as string);
inputValue.isNotEmpty = !!inputValue.content;
}
+ if (objEquals(inputValue, oldValue)) {
+ return;
+ }
doOperations.push({
action: "updateAttrViewCell",
id: cellId,
@@ -439,8 +462,11 @@ const updateCellValue = (protyle: IProtyle, type: TAVCol, cellElements: HTMLElem
[type]: oldValue
}
});
+ updateAttrViewCellAnimation(item);
});
- transaction(protyle, doOperations, undoOperations);
+ if (doOperations.length > 0) {
+ transaction(protyle, doOperations, undoOperations);
+ }
setTimeout(() => {
avMaskElement.remove();
});
diff --git a/app/src/protyle/render/av/col.ts b/app/src/protyle/render/av/col.ts
index a38131cb757..ea0367d2224 100644
--- a/app/src/protyle/render/av/col.ts
+++ b/app/src/protyle/render/av/col.ts
@@ -6,14 +6,16 @@ import {getDefaultOperatorByType, setFilter} from "./filter";
import {genCellValue} from "./cell";
import {openMenuPanel} from "./openMenuPanel";
import {getLabelByNumberFormat} from "./number";
+import {removeAttrViewColAnimation, updateAttrViewCellAnimation} from "./action";
+import {openEmojiPanel, unicode2Emoji} from "../../../emoji";
export const duplicateCol = (options: {
protyle: IProtyle,
type: TAVCol,
avID: string,
- nodeID: string,
colId: string,
- newValue: string
+ newValue: string,
+ icon: string
}) => {
const id = Lute.NewNodeID();
const nameMatch = options.newValue.match(/^(.*) \((\d+)\)$/);
@@ -23,7 +25,7 @@ export const duplicateCol = (options: {
options.newValue = `${options.newValue} (1)`;
}
if (["select", "mSelect"].includes(options.type)) {
- fetchPost("/api/av/renderAttributeView", {id: options.avID, nodeID: options.nodeID}, (response) => {
+ fetchPost("/api/av/renderAttributeView", {id: options.avID}, (response) => {
const data = response.data as IAV;
let colOptions;
data.view.columns.find((item) => {
@@ -37,6 +39,7 @@ export const duplicateCol = (options: {
name: options.newValue,
avID: options.avID,
type: options.type,
+ data: options.icon,
id
}, {
action: "sortAttrViewCol",
@@ -60,6 +63,7 @@ export const duplicateCol = (options: {
name: options.newValue,
avID: options.avID,
type: options.type,
+ data: options.icon,
id
}, {
action: "sortAttrViewCol",
@@ -72,6 +76,15 @@ export const duplicateCol = (options: {
avID: options.avID,
}]);
}
+ addAttrViewColAnimation({
+ blockElement: options.protyle.wysiwyg.element.querySelector(`[data-av-id="${options.avID}"]`),
+ protyle: options.protyle,
+ type: options.type,
+ name: options.newValue,
+ icon: options.icon,
+ previousId: options.colId,
+ id
+ });
};
export const getEditHTML = (options: {
@@ -91,18 +104,17 @@ export const getEditHTML = (options: {
-
-
-
+
-
+
-
+
-
+
-
+