diff --git a/addon/chrome/content/styles/linkCreator/notePicker.css b/addon/chrome/content/styles/linkCreator/notePicker.css index 99c19d7..fa51ac0 100644 --- a/addon/chrome/content/styles/linkCreator/notePicker.css +++ b/addon/chrome/content/styles/linkCreator/notePicker.css @@ -33,4 +33,12 @@ bn-note-picker { min-width: 200px; max-height: 50%; } + + .bn-note-list-container { + width: 100%; + } + + #bn-select-recent-notes-content { + border-left: var(--material-border-quarternary); + } } diff --git a/src/elements/linkCreator/inboundCreator.ts b/src/elements/linkCreator/inboundCreator.ts index 320c56b..d46df2c 100644 --- a/src/elements/linkCreator/inboundCreator.ts +++ b/src/elements/linkCreator/inboundCreator.ts @@ -67,6 +67,7 @@ export class InboundCreator extends PluginCEBase { async accept(io: any) { if (!this.targetNote) return; const content = await this.getContentToInsert(); + this.notePicker.saveRecentNotes(); io.targetNoteID = this.targetNote.id; io.content = content; diff --git a/src/elements/linkCreator/notePicker.ts b/src/elements/linkCreator/notePicker.ts index 3d12439..b010754 100644 --- a/src/elements/linkCreator/notePicker.ts +++ b/src/elements/linkCreator/notePicker.ts @@ -2,6 +2,7 @@ import { config } from "../../../package.json"; import { VirtualizedTableHelper } from "zotero-plugin-toolkit/dist/helpers/virtualizedTable"; import { PluginCEBase } from "../base"; import { + getPref, getPrefJSON, registerPrefObserver, setPref, @@ -19,12 +20,15 @@ export class NotePicker extends PluginCEBase { itemsView!: _ZoteroTypes.ItemTree; collectionsView!: _ZoteroTypes.CollectionTree; openedNotesView!: VirtualizedTableHelper; + recentNotesView!: VirtualizedTableHelper; _collectionsList!: XUL.Box; openedNotes: Zotero.Item[] = []; - activeSelectionType: "library" | "tabs" | "none" = "none"; + recentNotes: Zotero.Item[] = []; + + activeSelectionType: "library" | "tabs" | "recent" | "none" = "none"; uid = Zotero.Utilities.randomString(8); @@ -62,14 +66,20 @@ export class NotePicker extends PluginCEBase { - + - + + + + @@ -118,6 +128,10 @@ export class NotePicker extends PluginCEBase { await this.loadLibraryNotes(); this.loadQuickSearch(); await this.loadOpenedNotes(); + + this.recentNotes = this.getRecentNotes(); + + await this.loadRecentNotes(); } async loadLibraryNotes() { @@ -250,6 +264,78 @@ export class NotePicker extends PluginCEBase { // } } + async loadRecentNotes() { + const renderLock = Zotero.Promise.defer(); + this.recentNotesView = new VirtualizedTableHelper(window) + .setContainerId(`bn-select-recent-notes-tree-${this.uid}`) + .setProp({ + id: `bn-select-recent-notes-table-${this.uid}`, + columns: [ + { + dataKey: "title", + label: "Recent Notes", + flex: 1, + }, + ], + showHeader: true, + multiSelect: false, + staticColumns: true, + disableFontSizeScaling: true, + }) + .setProp("getRowCount", () => this.recentNotes.length || 0) + .setProp("getRowData", (index) => { + const note = this.recentNotes[index]; + return { + title: note.getNoteTitle(), + }; + }) + .setProp("onSelectionChange", (selection) => { + this.onRecentNoteSelected(selection); + }) + // For find-as-you-type + .setProp( + "getRowString", + (index) => this.recentNotes[index].getNoteTitle() || "", + ) + .setProp("renderItem", (index, selection, oldElem, columns) => { + let div; + if (oldElem) { + div = oldElem; + div.innerHTML = ""; + } else { + div = document.createElement("div"); + div.className = "row"; + } + + div.classList.toggle("selected", selection.isSelected(index)); + div.classList.toggle("focused", selection.focused == index); + const rowData = this.recentNotes[index]; + + for (const column of columns) { + const span = document.createElement("span"); + // @ts-ignore + span.className = `cell ${column?.className}`; + span.textContent = rowData.getNoteTitle(); + const icon = getCSSItemTypeIcon("note"); + icon.classList.add("cell-icon"); + span.prepend(icon); + div.append(span); + } + return div; + }) + .render(-1, () => { + renderLock.resolve(); + }); + await renderLock.promise; + + if (this.recentNotes.length > 0) { + setTimeout(() => { + this.recentNotesView.treeInstance.selection.select(0); + this.onRecentNoteSelected(this.recentNotesView.treeInstance.selection); + }, 200); + } + } + onSearch() { if (this.itemsView) { const searchVal = ( @@ -316,6 +402,33 @@ export class NotePicker extends PluginCEBase { this.dispatchSelectionChange(selection); } + onRecentNoteSelected(selection: { selected: Set }) { + this.activeSelectionType = "recent"; + this.dispatchSelectionChange(selection); + } + + getRecentNotes() { + return ((getPref("linkCreator.recentNotes") as string) || "") + .split(",") + .map((id) => Zotero.Items.get(parseInt(id))) + .filter((item) => item && item.isNote()); + } + + saveRecentNotes() { + const selectedNotes = this.getSelectedNotes(); + if (!selectedNotes.length) { + return; + } + const recentNotes: number[] = [...selectedNotes.map((note) => note.id)]; + for (const note of this.recentNotes) { + if (!recentNotes.includes(note.id)) { + recentNotes.push(note.id); + } + } + // Save only 10 recent notes + setPref("linkCreator.recentNotes", recentNotes.slice(0, 10).join(",")); + } + dispatchSelectionChange(selection?: { selected: Set }) { this.dispatchEvent( new CustomEvent("selectionchange", { @@ -331,10 +444,16 @@ export class NotePicker extends PluginCEBase { return []; } else if (this.activeSelectionType == "library") { return this.itemsView.getSelectedItems(); + } else if (this.activeSelectionType == "tabs") { + return Array.from( + (selection || this.openedNotesView.treeInstance.selection).selected, + ).map((index) => this.openedNotes[index]); + } else if (this.activeSelectionType == "recent") { + return Array.from( + (selection || this.recentNotesView.treeInstance.selection).selected, + ).map((index) => this.recentNotes[index]); } - return Array.from( - (selection || this.openedNotesView.treeInstance.selection).selected, - ).map((index) => this.openedNotes[index]); + return []; } _persistState() { diff --git a/src/elements/linkCreator/outboundCreator.ts b/src/elements/linkCreator/outboundCreator.ts index 5d38625..e19b61b 100644 --- a/src/elements/linkCreator/outboundCreator.ts +++ b/src/elements/linkCreator/outboundCreator.ts @@ -70,6 +70,7 @@ export class OutboundCreator extends PluginCEBase { async accept(io: any) { if (!this.targetNote) return; const content = await this.getContentToInsert(); + this.notePicker.saveRecentNotes(); io.targetNoteID = this.currentNote!.id; io.content = content;