Improve reworked context pane:

- Show child notes at top of notes list, with separate headers for "Item
  Notes" and "All Notes"
- Fix "New Child Note" option
- Add parent item title above note editor when editing child note
- Cleanup

TODO:
- Search on parent item title for child notes
This commit is contained in:
Martynas Bagdonas 2021-02-22 20:02:05 +02:00 committed by Dan Stillman
parent db0bd56688
commit b00325942a
6 changed files with 131 additions and 100 deletions

View file

@ -52,9 +52,18 @@ const NoteRow = ({ title, body, date, onClick, parentItemType, parentTitle }) =>
const NotesList = forwardRef(({ onClick }, ref) => {
const [notes, setNotes] = useState([]);
useImperativeHandle(ref, () => ({ setNotes }));
let currentChildNotes = notes.filter(x => x.isCurrentChild);
let allNotes = notes.filter(x => !x.isCurrentChild);
return (
<div className="notes-list">
{notes.map(note => <NoteRow key={note.id} {...note} onClick={() => onClick(note.id)}/>)}
<section>
{!!currentChildNotes.length && <h2>{Zotero.getString('pane.context.itemNotes')}</h2>}
{currentChildNotes.map(note => <NoteRow key={note.id} {...note} onClick={() => onClick(note.id)}/>)}
</section>
<section>
{!!allNotes && <h2>{Zotero.getString('pane.context.allNotes')}</h2>}
{allNotes.map(note => <NoteRow key={note.id} {...note} onClick={() => onClick(note.id)}/>)}
</section>
</div>
);
});

View file

@ -168,6 +168,11 @@ var ZoteroContextPane = new function () {
await reader._initPromise;
_tabCover.hidden = true;
})();
var attachment = Zotero.Items.get(reader.itemID);
_selectNotesContext(attachment.libraryID);
var notesContext = _getNotesContext(attachment.libraryID);
notesContext.updateFromCache();
}
_contextPaneSplitter.setAttribute('hidden', false);
@ -177,10 +182,6 @@ var ZoteroContextPane = new function () {
_splitButton.classList.remove('hidden');
}
var context = _itemContexts.find(x => x.tabID == ids[0]);
if (context) {
_selectNotesContext(context.libraryID);
}
_selectItemContext(ids[0]);
_update();
}
@ -365,6 +366,16 @@ var ZoteroContextPane = new function () {
_panesDeck.append(_itemPaneDeck, _notesPaneDeck);
}
function _getCurrentParentItem() {
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) {
var attachment = Zotero.Items.get(reader.itemID);
if (attachment) {
return attachment.parentItem;
}
}
}
function _addNotesContext(libraryID) {
var list = document.createElement('vbox');
@ -372,10 +383,12 @@ var ZoteroContextPane = new function () {
list.className = 'zotero-context-notes-list';
var noteContainer = document.createElement('vbox');
var title = document.createElement('vbox');
title.className = 'zotero-context-pane-editor-parent-line';
var editor = document.createElement('zoteronoteeditor');
editor.className = 'zotero-context-pane-pinned-note';
editor.setAttribute('flex', 1);
noteContainer.appendChild(editor);
noteContainer.append(title, editor);
let contextNode = document.createElement('deck');
contextNode.append(list, noteContainer);
@ -404,7 +417,10 @@ var ZoteroContextPane = new function () {
break;
case 'context-pane-new-item-note':
// TODO: Get the parent item
var parentItem = _getCurrentParentItem();
if (!parentItem) {
return;
}
parentID = parentItem.id;
break;
@ -456,53 +472,71 @@ var ZoteroContextPane = new function () {
var notesListRef = React.createRef();
async function _updateNotesList(reset) {
if (reset) {
input.value = '';
contextNode.setAttribute('selectedIndex', 0);
async function _updateNotesList(useCached) {
var notes;
if (useCached && context.cachedNotes.length) {
notes = context.cachedNotes;
}
var query = input.value;
await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note');
if (query) {
let parts = Zotero.SearchConditions.parseSearchString(query);
for (let part of parts) {
s.addCondition('note', 'contains', part.text);
else {
var query = input.value;
await Zotero.Schema.schemaUpdatePromise;
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note');
if (query) {
let parts = Zotero.SearchConditions.parseSearchString(query);
for (let part of parts) {
s.addCondition('note', 'contains', part.text);
}
}
notes = await s.search();
notes = Zotero.Items.get(notes);
notes.sort((a, b) => {
a = a.getField('dateModified');
b = b.getField('dateModified');
return b.localeCompare(a);
});
notes = notes.map(note => {
var parentItem = note.parentItem;
var text = note.note;
text = Zotero.Utilities.unescapeHTML(text);
text = text.trim();
text = text.slice(0, 500);
var parts = text.split('\n').map(x => x.trim()).filter(x => x.length);
var title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
return {
id: note.id,
title: title || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale)),
parentID: note.parentID,
parentItemType: parentItem && parentItem.itemType,
parentTitle: parentItem && parentItem.getDisplayTitle()
};
});
context.cachedNotes = notes;
}
var notes = await s.search();
notes = Zotero.Items.get(notes);
notes.sort((a, b) => {
a = a.getField('dateModified');
b = b.getField('dateModified');
return b.localeCompare(a);
});
notesListRef.current.setNotes(notes.map(note => {
var parentItem = note.parentItem;
var text = note.note;
text = Zotero.Utilities.unescapeHTML(text);
text = text.trim();
text = text.slice(0, 500);
var parts = text.split('\n').map(x => x.trim()).filter(x => x.length);
var title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
return {
id: note.id,
title: title || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale)),
parentItemType: parentItem && parentItem.itemType,
parentTitle: parentItem && parentItem.getDisplayTitle()
};
}));
var readerParentItem = _getCurrentParentItem();
notesListRef.current.setNotes(notes.map(note => ({
...note,
isCurrentChild: readerParentItem && note.parentID == readerParentItem.id
})));
var c = notes.length;
label.value = Zotero.getString('pane.item.notes.count', c, c);
}
var context = {
libraryID,
node: contextNode,
editor,
cachedNotes: [],
update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }),
updateFromCache: () => _updateNotesList(true)
};
ReactDOM.render(
<NotesList
ref={notesListRef}
@ -515,16 +549,7 @@ var ZoteroContextPane = new function () {
_updateNotesList();
}
);
var context = {
libraryID,
node: contextNode,
update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }),
editor
};
_notesContexts.push(context);
return context;
}
@ -560,55 +585,29 @@ var ZoteroContextPane = new function () {
function _setPinnedNote(libraryID, itemID) {
var editable = _isLibraryEditable(libraryID);
var context = _getNotesContext(libraryID);
var item = Zotero.Items.get(itemID);
if (context) {
let { editor, node } = context;
var { editor, node } = context;
node.setAttribute('selectedIndex', 1);
editor.mode = editable ? 'edit' : 'view';
editor.item = Zotero.Items.get(itemID);
editor.item = item;
editor.parentItem = null;
editor.hideLinksContainer = true;
_updateAddToNote();
}
}
function _appendNoteRows(notes, list, editable, onClick, onDelete) {
for (var i = 0; i < notes.length; i++) {
var 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', () => {
onClick(id);
});
box.appendChild(icon);
box.appendChild(label);
var row = document.createElement('row');
row.appendChild(box);
if (editable) {
var removeButton = document.createElement('label');
removeButton.setAttribute('value', '-');
removeButton.setAttribute('class', 'zotero-clicky zotero-clicky-minus');
removeButton.addEventListener('click', function () {
onDelete(id);
});
row.appendChild(removeButton);
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
var parentItem = item.parentItem;
if (parentItem) {
var container = document.createElementNS(HTML_NS, 'div');
var img = document.createElementNS(HTML_NS, 'img');
img.src = Zotero.ItemTypes.getImageSrc(parentItem.itemType);
img.className = 'parent-item-type';
var title = document.createElementNS(HTML_NS, 'div');
title.append(parentItem.getDisplayTitle());
title.className = 'parent-title';
container.append(img, title);
node.querySelector('.zotero-context-pane-editor-parent-line').append(container);
}
list.appendChild(row);
_updateAddToNote();
}
}

View file

@ -41,9 +41,6 @@ class ReaderInstance {
}
async open({ itemID, state, location }) {
if (itemID === this._itemID) {
return false;
}
let item = await Zotero.Items.getAsync(itemID);
if (!item) {
return false;
@ -72,6 +69,10 @@ class ReaderInstance {
return true;
}
get itemID() {
return this._itemID;
}
updateTitle() {
let item = Zotero.Items.get(this._itemID);
let title = item.getField('title');
@ -464,6 +465,7 @@ class ReaderInstance {
class ReaderTab extends ReaderInstance {
constructor({ itemID, sidebarWidth, sidebarOpen, bottomPlaceholderHeight }) {
super();
this._itemID = itemID;
this._sidebarWidth = sidebarWidth;
this._sidebarOpen = sidebarOpen;
this._bottomPlaceholderHeight = bottomPlaceholderHeight;

View file

@ -400,6 +400,8 @@ pane.item.related.count.plural = %S related:
pane.item.parentItem = Parent Item:
pane.context.noParent = No parent item
pane.context.itemNotes = Item Notes
pane.context.allNotes = All Notes
noteEditor.editNote = Edit Note

View file

@ -69,3 +69,22 @@
.zotero-context-notes-list {
padding-top: 5px;
}
.zotero-context-pane-editor-parent-line > div {
display: flex;
align-items: center;
padding: 5px 5px;
}
.zotero-context-pane-editor-parent-line .parent-item-type {
margin-right: 3px;
width: 16px;
}
.zotero-context-pane-editor-parent-line .parent-title {
flex-grow: 1;
width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

View file

@ -16,7 +16,7 @@
&> section > h2 {
font-weight: bold;
padding: 7px 8px 3px;
margin: 7px 8px 3px;
font-size: 13px;
}
}