From 6bc782e3ce319dfb5b02c30d6ca28f0e976d2fc4 Mon Sep 17 00:00:00 2001 From: xiangyu <3170102889@zju.edu.cn> Date: Tue, 3 May 2022 00:55:10 +0800 Subject: [PATCH] add: outline buttons --- addon/chrome/content/workspace.xul | 7 +- .../default/Knowledge4Zotero/workspace.css | 2 +- src/events.ts | 22 +++- src/knowledge.ts | 122 ++++++++++++++---- src/views.ts | 87 ++++++++++++- 5 files changed, 207 insertions(+), 33 deletions(-) diff --git a/addon/chrome/content/workspace.xul b/addon/chrome/content/workspace.xul index c854bfd..05935a6 100644 --- a/addon/chrome/content/workspace.xul +++ b/addon/chrome/content/workspace.xul @@ -37,7 +37,7 @@
- Note Outline + Knowledge Outline
@@ -45,6 +45,11 @@ + +
+
+
+
diff --git a/addon/chrome/skin/default/Knowledge4Zotero/workspace.css b/addon/chrome/skin/default/Knowledge4Zotero/workspace.css index ea48bf5..f31ad85 100644 --- a/addon/chrome/skin/default/Knowledge4Zotero/workspace.css +++ b/addon/chrome/skin/default/Knowledge4Zotero/workspace.css @@ -18,7 +18,7 @@ } .drive-panel { - padding: 20px 15px; + padding: 20px 0px 20px 30px; font-size: 115%; font-weight: bold; border-right: 1px solid rgba(165, 165, 165, 0.4); diff --git a/src/events.ts b/src/events.ts index 54db8ae..e556eb4 100644 --- a/src/events.ts +++ b/src/events.ts @@ -163,7 +163,7 @@ class AddonEvents extends AddonBase { const title = _window.document.getElementById("knowledge-addlink"); this._Addon.views.changeEditorButtonView( title, - "mainTitle", + "mainHeading", "This is a Main Knowledge", "empty" ); @@ -187,6 +187,7 @@ class AddonEvents extends AddonBase { Zotero.debug("Knowledge4Zotero: addToKnowledge"); this._Addon.knowledge.addLinkToNote( undefined, + // -1 for automatically insert to current selected line or end of note -1, message.content.editorInstance._item.id ); @@ -238,7 +239,24 @@ class AddonEvents extends AddonBase { } } } - } else if (message.type === "moveOutlineTitle") { + } else if (message.type === "clickOutlineHeading") { + /* + message.content = { + event: {itemData} + } + */ + let editorInstance = + await this._Addon.knowledge.getWorkspaceEditorInstance(); + // Set node id + this._Addon.knowledge.currentNodeID = parseInt( + (message.content.event as any).itemData.id + ); + this._Addon.views.scrollToLine( + editorInstance, + // Scroll to 6 lines before the inserted line + (message.content.event as any).itemData.lineIndex - 5 + ); + } else if (message.type === "moveOutlineHeading") { /* message.content = { params: { diff --git a/src/knowledge.ts b/src/knowledge.ts index 74d71bb..700ed6a 100644 --- a/src/knowledge.ts +++ b/src/knowledge.ts @@ -4,11 +4,12 @@ const TreeModel = require("./treemodel"); class Knowledge extends AddonBase { currentLine: number; - currentVersion: number; + currentNodeID: number; workspaceWindow: Window; constructor(parent: Knowledge4Zotero) { super(parent); - this.currentLine = 0; + this.currentLine = -1; + this.currentNodeID = -1; } getWorkspaceNote() { @@ -35,7 +36,13 @@ class Knowledge extends AddonBase { ); this.workspaceWindow = win; await this.waitWorkspaceReady(); + // @ts-ignore + win.addEventListener("resize", (e) => { + // @ts-ignore + this._Addon.views.setTreeViewHeight(); + }); this.setWorkspaceNote("main"); + this.currentLine = -1; this._Addon.views.buildOutline(); } } @@ -87,6 +94,9 @@ class Knowledge extends AddonBase { _window.document .getElementById("preview-splitter") .setAttribute("state", "open"); + } else { + // Set line to default + this.currentLine = -1; } await this.waitWorkspaceReady(); let noteEditor: any = await this.getWorkspaceEditor(type); @@ -175,30 +185,50 @@ class Knowledge extends AddonBase { this.setLinesToNote(note, noteLines); } - addSubLineToNote(note: ZoteroItem, text: string, lineIndex: number = -1) { + async addSubLineToNote( + note: ZoteroItem, + text: string, + lineIndex: number = -1 + ) { if (lineIndex < 0) { - lineIndex = this.currentLine; + lineIndex = + this.currentLine >= 0 + ? this.currentLine + 1 + : this.getLinesInNote(note).length + 1; } - let parentNode = this.getLineParentInNote(note, lineIndex); - if (!parentNode) { - this.addLineToNote(note, text, lineIndex); - return; - } - let nodes = this.getNoteTreeAsList(note); - let i = 0; - for (let node of nodes) { - if (node.model.lineIndex === parentNode.model.lineIndex) { - break; - } - i++; - } - // Get next header line index - i++; - if (i >= nodes.length) { - i = nodes.length - 1; - } - // Add line before next header, which is also the end of current parent header - this.addLineToNote(note, text, nodes[i].model.lineIndex); + // let parentNode = this.getLineParentInNote(note, lineIndex); + // if (!parentNode) { + // this.addLineToNote(note, text, lineIndex); + // return; + // } + // let nodes = this.getNoteTreeAsList(note); + // let i = 0; + // for (let node of nodes) { + // if (node.model.lineIndex === parentNode.model.lineIndex) { + // break; + // } + // i++; + // } + // // Get next header line index + // i++; + // if (i >= nodes.length) { + // i = nodes.length - 1; + // } + // Add to next line + this.addLineToNote(note, text, lineIndex); + await Zotero.Promise.delay(500); + let editorInstance = await this.getWorkspaceEditorInstance(); + this._Addon.views.scrollToLine( + editorInstance, + // Scroll to 6 lines before the inserted line + lineIndex - 5 + ); + this._Addon.events.onEditorEvent( + new EditorMessage("enterWorkspace", { + editorInstance: editorInstance, + params: "main", + }) + ); } addLinkToNote( @@ -234,6 +264,50 @@ class Knowledge extends AddonBase { ); } + modifyLineInNote(note: ZoteroItem, text: string, lineIndex: number) { + note = note || this.getWorkspaceNote(); + if (!note) { + return; + } + let noteLines = this.getLinesInNote(note); + if (lineIndex < 0 || lineIndex >= noteLines.length) { + return; + } + noteLines[lineIndex] = text; + this.setLinesToNote(note, noteLines); + } + + changeHeadingLineInNote( + note: ZoteroItem, + rankChange: number, + lineIndex: number + ) { + note = note || this.getWorkspaceNote(); + if (!note) { + return; + } + const noteLines = this.getLinesInNote(note); + if (lineIndex < 0 || lineIndex >= noteLines.length) { + return; + } + const headerStartReg = new RegExp(""); + const headerStopReg = new RegExp(""); + let headerStart = noteLines[lineIndex].search(headerStartReg); + if (headerStart === -1) { + return; + } + let lineRank = parseInt(noteLines[lineIndex][headerStart + 2]) + rankChange; + if (lineRank > 6) { + lineRank = 6; + } else if (lineRank < 1) { + lineRank = 1; + } + noteLines[lineIndex] = noteLines[lineIndex] + .replace(headerStartReg, ``) + .replace(headerStopReg, ``); + this.setLinesToNote(note, noteLines); + } + moveHeaderLineInNote( note: ZoteroItem, currentNode: TreeModel.Node, diff --git a/src/views.ts b/src/views.ts index 3503b58..9f01bff 100644 --- a/src/views.ts +++ b/src/views.ts @@ -13,7 +13,7 @@ class AddonViews extends AddonBase { default: "chrome://Knowledge4Zotero/skin/favicon.png", }; this.editorIcon = { - mainTitle: "Main Knowledge", + mainHeading: "Main Knowledge", addToKnowledge: ``, notMainKnowledge: ``, isMainKnowledge: ``, @@ -114,6 +114,8 @@ class AddonViews extends AddonBase { let editor = this.getEditor(instance._iframeWindow.document); if (lineIndex > editor.children.length) { lineIndex = editor.children.length - 1; + } else if (lineIndex < 0) { + lineIndex = 0; } // @ts-ignore editor.parentNode.scrollTo(0, editor.children[lineIndex].offsetTop); @@ -165,21 +167,22 @@ class AddonViews extends AddonBase { } async buildOutline(note: ZoteroItem) { + this._Addon.knowledge.currentNodeID = -1; let treeList = this._Addon.knowledge.getNoteTreeAsList(note); const treeData = []; treeList.map((node: TreeModel.Node) => { treeData.push({ id: String(node.model.id), name: node.model.name, + rank: node.model.rank, + lineIndex: node.model.lineIndex, + endIndex: node.model.endIndex, isDirectory: node.hasChildren(), expanded: true, parentId: node.model.rank > 1 ? String(node.parent.model.id) : undefined, }); }); - Zotero.debug(treeList); - Zotero.debug(treeData); - this.$(() => { this.createTreeView("#treeview", treeData); this.createSortable("#treeview", "treeData"); @@ -193,8 +196,82 @@ class AddonViews extends AddonBase { expandNodesRecursive: false, dataStructure: "plain", width: 220, + height: this.$("window").height() - 130, displayExpr: "name", + noDataText: "No Heading 1 found", + onItemClick: (e) => { + this._Addon.events.onEditorEvent( + new EditorMessage("clickOutlineHeading", { + event: e, + }) + ); + }, + onItemSelectionChanged: (e) => { + console.log(e); + }, }); + this.$("#outline-addafter").dxButton({ + icon: "plus", + hint: "Add Heading after the selected Heading's section", + onClick: (e) => { + if (this._Addon.knowledge.currentNodeID < 0) { + return; + } + const text = prompt("Enter new Heading:"); + this._Addon.knowledge.openWorkspaceWindow(); + if (text.trim()) { + let node = this._Addon.knowledge.getNoteTreeNodeById( + undefined, + this._Addon.knowledge.currentNodeID + ); + this._Addon.knowledge.addSubLineToNote( + undefined, + `${text}`, + node.model.endIndex + ); + } + }, + }); + this.$("#outline-tab").dxButton({ + icon: "increaseindent", + hint: "Raise the selected Heading level", + onClick: (e) => { + if (this._Addon.knowledge.currentNodeID < 0) { + return; + } + let node = this._Addon.knowledge.getNoteTreeNodeById( + undefined, + this._Addon.knowledge.currentNodeID + ); + this._Addon.knowledge.changeHeadingLineInNote( + undefined, + 1, + node.model.lineIndex + ); + }, + }); + this.$("#outline-untab").dxButton({ + icon: "decreaseindent", + hint: "Decrease the selected Heading level", + onClick: (e) => { + if (this._Addon.knowledge.currentNodeID < 0) { + return; + } + let node = this._Addon.knowledge.getNoteTreeNodeById( + undefined, + this._Addon.knowledge.currentNodeID + ); + this._Addon.knowledge.changeHeadingLineInNote( + undefined, + -1, + node.model.lineIndex + ); + }, + }); + } + + setTreeViewHeight() { + this.$("#treeview").css("height", `${this.$("window").height() - 130}px`); } createSortable(selector, driveName) { @@ -298,7 +375,7 @@ class AddonViews extends AddonBase { : this.findIndex(toItems, toNode.itemData.id); this._Addon.events.onEditorEvent( - new EditorMessage("moveOutlineTitle", { + new EditorMessage("moveOutlineHeading", { params: { fromID: parseInt(fromNode.itemData.id), toID: toNode