diff --git a/chrome/content/zotero/elements/notesBox.js b/chrome/content/zotero/elements/notesBox.js
new file mode 100644
index 0000000000..11a0055e6d
--- /dev/null
+++ b/chrome/content/zotero/elements/notesBox.js
@@ -0,0 +1,180 @@
+/*
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright © 2021 Corporation for Digital Scholarship
+ Vienna, Virginia, USA
+ https://www.zotero.org
+
+ This file is part of Zotero.
+
+ Zotero is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Zotero is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Zotero. If not, see .
+
+ ***** END LICENSE BLOCK *****
+*/
+
+"use strict";
+
+{
+ class NotesBox extends XULElement {
+ constructor() {
+ super();
+
+ this._mode = 'view';
+ this._item = null;
+ this._destroyed = false;
+ this._noteIDs = [];
+
+ this.content = MozXULElement.parseXULToFragment(`
+
+
+
+ `, ['chrome://zotero/locale/zotero.dtd']);
+ }
+
+ connectedCallback() {
+ this._destroyed = false;
+ window.addEventListener("unload", this.destroy);
+
+ let shadow = this.attachShadow({ mode: "open" });
+
+ let s1 = document.createElement("link");
+ s1.rel = "stylesheet";
+ s1.href = "chrome://zotero-platform/content/notesBox.css";
+ shadow.append(s1);
+
+ let content = document.importNode(this.content, true);
+ shadow.append(content);
+
+ this._id('add').addEventListener('click', this._handleAdd);
+
+ this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'notesBox');
+ }
+
+ destroy() {
+ if (this._destroyed) {
+ return;
+ }
+ window.removeEventListener("unload", this.destroy);
+ this._destroyed = true;
+
+ Zotero.Notifier.unregisterObserver(this._notifierID);
+ }
+
+ disconnectedCallback() {
+ this.replaceChildren();
+ this.destroy();
+ }
+
+ get mode() {
+ return this._mode;
+ }
+
+ set mode(val) {
+ switch (val) {
+ case 'view':
+ case 'merge':
+ case 'mergeedit':
+ case 'edit':
+ break;
+
+ default:
+ throw new Error(`Invalid mode '${val}'`);
+ }
+
+ this._mode = val;
+ }
+
+ get item() {
+ return this._item;
+ }
+
+ set item(val) {
+ this._item = val;
+ this._refresh();
+ }
+
+ notify(event, type, ids, extraData) {
+ if (['modify', 'delete'].includes(event) && ids.some(id => this._noteIDs.includes(id))) {
+ this._refresh();
+ }
+ }
+
+ _refresh() {
+ if (!this._item) {
+ return;
+ }
+
+ this._noteIDs = this._item.getNotes();
+ this._id('add').hidden = this._mode != 'edit';
+
+ let grid = this._id('grid');
+ grid.replaceChildren();
+
+ let notes = Zotero.Items.get(this._item.getNotes());
+ for (let item of notes) {
+ let id = item.id;
+ let icon = document.createElement("img");
+ icon.src = item.getImageSrc();
+
+ let label = document.createElement("label");
+ label.append(item.getDisplayTitle());
+
+ let box = document.createElement('div');
+ box.addEventListener('click', () => this._handleShowItem(id));
+ box.className = 'box zotero-clicky';
+ box.appendChild(icon);
+ box.appendChild(label);
+
+ grid.append(box);
+
+ if (this._mode == 'edit') {
+ let remove = document.createElement("label");
+ remove.addEventListener('click', () => this._handleRemove(id));
+ remove.className = 'zotero-clicky zotero-clicky-minus';
+ remove.append('-');
+ grid.append(remove);
+ }
+ }
+
+ let num = this._noteIDs.length;
+ this._id('num').replaceChildren(Zotero.getString('pane.item.notes.count', num, num));
+ }
+
+ _handleAdd = (event) => {
+ ZoteroPane_Local.newNote(event.shiftKey, this._item.key);
+ };
+
+ _handleRemove(id) {
+ var ps = Services.prompt;
+ if (ps.confirm(null, '', Zotero.getString('pane.item.notes.delete.confirm'))) {
+ Zotero.Items.trashTx(id);
+ }
+ }
+
+ _handleShowItem(id) {
+ ZoteroPane_Local.selectItem(id);
+ }
+
+ _id(id) {
+ return this.shadowRoot.querySelector(`[id=${id}]`);
+ }
+ }
+ customElements.define("notes-box", NotesBox);
+}
diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js
index 504ca23e36..295e488ed4 100644
--- a/chrome/content/zotero/itemPane.js
+++ b/chrome/content/zotero/itemPane.js
@@ -28,11 +28,10 @@ import ReactDOM from 'react-dom';
import TagsBoxContainer from 'containers/tagsBoxContainer';
var ZoteroItemPane = new function() {
- var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox;
+ var _lastItem, _itemBox, _tagsBox, _notesBox, _relatedBox;
var _selectedNoteID;
var _translationTarget;
- var _noteIDs;
-
+
this.onLoad = function () {
if (!Zotero) {
return;
@@ -44,16 +43,14 @@ var ZoteroItemPane = new function() {
if (!document.getElementById('zotero-view-tabbox')) {
return;
}
-
- _notesLabel = document.getElementById('zotero-editpane-notes-label');
- _notesButton = document.getElementById('zotero-editpane-notes-add');
- _notesList = document.getElementById('zotero-editpane-dynamic-notes');
+
// Fake a ref
_tagsBox = {
current: null
};
+ _notesBox = document.getElementById('zotero-editpane-notes');
_relatedBox = document.getElementById('zotero-editpane-related');
-
+
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item'], 'itemPane');
}
@@ -84,6 +81,11 @@ var ZoteroItemPane = new function() {
break;
}
+ case 1:
+ var box = _notesBox;
+ box.parentItem = item;
+ break;
+
case 3:
var box = _relatedBox;
break;
@@ -134,60 +136,6 @@ var ZoteroItemPane = new function() {
this.setTranslateButton();
}
}
- else if (index == 1) {
- var editable = ZoteroPane_Local.canEdit();
- _notesButton.hidden = !editable;
-
- while(_notesList.hasChildNodes()) {
- _notesList.removeChild(_notesList.firstChild);
- }
-
- _noteIDs = new Set();
- let notes = yield Zotero.Items.getAsync(item.getNotes());
- if (notes.length) {
- for (var i = 0; i < notes.length; i++) {
- let note = notes[i];
- let id = notes[i].id;
-
- var icon = document.createElement('image');
- icon.className = "zotero-box-icon";
- icon.setAttribute('src', `chrome://zotero/skin/treeitem-note${Zotero.hiDPISuffix}.png`);
-
- var label = document.createElement('label');
- label.className = "zotero-box-label";
- var title = note.getNoteTitle();
- title = title ? title : Zotero.getString('pane.item.notes.untitled');
- label.setAttribute('value', title);
- label.setAttribute('flex','1'); //so that the long names will flex smaller
- label.setAttribute('crop','end');
-
- var box = document.createElement('box');
- box.setAttribute('class','zotero-clicky');
- box.addEventListener('click', function () { ZoteroPane_Local.selectItem(id); });
- box.appendChild(icon);
- box.appendChild(label);
-
- if (editable) {
- var removeButton = document.createElement('label');
- removeButton.setAttribute("value","-");
- removeButton.setAttribute("class","zotero-clicky zotero-clicky-minus");
- removeButton.addEventListener('click', function () { ZoteroItemPane.removeNote(id); });
- }
-
- var row = document.createElement('row');
- row.appendChild(box);
- if (editable) {
- row.appendChild(removeButton);
- }
-
- _notesList.appendChild(row);
- _noteIDs.add(id);
- }
- }
-
- _updateNoteCount();
- return;
- }
else if (index == 2) {
ReactDOM.render(
_noteIDs.has(id))) {
- refresh = true;
- }
- if (refresh) {
- yield this.viewItem(_lastItem, null, 1);
- }
- }
if (viewBox.selectedIndex == 0 && action == 'refresh' && _lastItem) {
yield this.viewItem(_lastItem, null, 0);
}
@@ -290,20 +228,6 @@ var ZoteroItemPane = new function() {
};
- this.addNote = function (popup) {
- ZoteroPane_Local.newNote(popup, _lastItem.key);
- }
-
-
- this.removeNote = function (id) {
- var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Components.interfaces.nsIPromptService);
- if (ps.confirm(null, '', Zotero.getString('pane.item.notes.delete.confirm'))) {
- Zotero.Items.trashTx(id);
- }
- }
-
-
this.onTagsContextPopupShowing = function () {
if (!_lastItem.isEditable()) {
return false;
@@ -414,12 +338,6 @@ var ZoteroItemPane = new function() {
var tooltip = label + (Zotero.rtl ? ' \u202B' : ' ') + '(' + key + ')'
elem.setAttribute('tooltiptext', tooltip);
};
-
-
- function _updateNoteCount() {
- var c = _notesList.childNodes.length;
- _notesLabel.value = Zotero.getString('pane.item.notes.count', c, c);
- }
}
addEventListener("load", function(e) { ZoteroItemPane.onLoad(e); }, false);
diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml
index 0b15243c81..c8aa25258b 100644
--- a/chrome/content/zotero/zoteroPane.xhtml
+++ b/chrome/content/zotero/zoteroPane.xhtml
@@ -79,6 +79,7 @@
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/menulistItemTypes.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/itemBox.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/noteEditor.js", this);
+ Services.scriptloader.loadSubScript("chrome://zotero/content/elements/notesBox.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/relatedBox.js", this);
Services.scriptloader.loadSubScript("chrome://zotero/content/tabs.js", this);
@@ -1117,20 +1118,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/scss/_notesBox.scss b/scss/_notesBox.scss
new file mode 100644
index 0000000000..261fbca2ba
--- /dev/null
+++ b/scss/_notesBox.scss
@@ -0,0 +1,2 @@
+@import "components/notesBox";
+@import "components/clicky";
diff --git a/scss/components/_notesBox.scss b/scss/components/_notesBox.scss
new file mode 100644
index 0000000000..d6982aa3fa
--- /dev/null
+++ b/scss/components/_notesBox.scss
@@ -0,0 +1,42 @@
+.header {
+ display: flex;
+ padding-left: 10px;
+ align-items: center;
+
+ label {
+ margin-right: 5px;
+ }
+
+ button {
+ min-width: 79px;
+ margin: 5px 6px 3px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ color: ButtonText;
+ text-shadow: none;
+ font-size: inherit;
+ }
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr auto;
+
+ .box {
+ overflow: hidden;
+ display: flex;
+ margin-left: 5px;
+
+ img {
+ width: 16px;
+ height: 16px;
+ }
+
+ label {
+ margin-left: 3px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+}
diff --git a/scss/notesBox-mac.scss b/scss/notesBox-mac.scss
new file mode 100644
index 0000000000..92fd0e92c7
--- /dev/null
+++ b/scss/notesBox-mac.scss
@@ -0,0 +1 @@
+@import "notesBox";
diff --git a/scss/notesBox-unix.scss b/scss/notesBox-unix.scss
new file mode 100644
index 0000000000..92fd0e92c7
--- /dev/null
+++ b/scss/notesBox-unix.scss
@@ -0,0 +1 @@
+@import "notesBox";
diff --git a/scss/notesBox-win.scss b/scss/notesBox-win.scss
new file mode 100644
index 0000000000..92fd0e92c7
--- /dev/null
+++ b/scss/notesBox-win.scss
@@ -0,0 +1 @@
+@import "notesBox";