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 NotesList = forwardRef(({ onClick }, ref) => {
const [notes, setNotes] = useState([]); const [notes, setNotes] = useState([]);
useImperativeHandle(ref, () => ({ setNotes })); useImperativeHandle(ref, () => ({ setNotes }));
let currentChildNotes = notes.filter(x => x.isCurrentChild);
let allNotes = notes.filter(x => !x.isCurrentChild);
return ( return (
<div className="notes-list"> <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> </div>
); );
}); });

View file

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

View file

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

View file

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

View file

@ -69,3 +69,22 @@
.zotero-context-notes-list { .zotero-context-notes-list {
padding-top: 5px; 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 { &> section > h2 {
font-weight: bold; font-weight: bold;
padding: 7px 8px 3px; margin: 7px 8px 3px;
font-size: 13px; font-size: 13px;
} }
} }