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:
parent
db0bd56688
commit
b00325942a
6 changed files with 131 additions and 100 deletions
|
@ -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>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -365,6 +366,16 @@ 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');
|
||||||
|
@ -372,10 +383,12 @@ var ZoteroContextPane = new function () {
|
||||||
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();
|
||||||
|
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 readerParentItem = _getCurrentParentItem();
|
||||||
var parentItem = note.parentItem;
|
notesListRef.current.setNotes(notes.map(note => ({
|
||||||
var text = note.note;
|
...note,
|
||||||
text = Zotero.Utilities.unescapeHTML(text);
|
isCurrentChild: readerParentItem && note.parentID == readerParentItem.id
|
||||||
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 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) {
|
|
||||||
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');
|
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
|
||||||
row.appendChild(box);
|
var parentItem = item.parentItem;
|
||||||
if (editable) {
|
if (parentItem) {
|
||||||
var removeButton = document.createElement('label');
|
var container = document.createElementNS(HTML_NS, 'div');
|
||||||
removeButton.setAttribute('value', '-');
|
var img = document.createElementNS(HTML_NS, 'img');
|
||||||
removeButton.setAttribute('class', 'zotero-clicky zotero-clicky-minus');
|
img.src = Zotero.ItemTypes.getImageSrc(parentItem.itemType);
|
||||||
removeButton.addEventListener('click', function () {
|
img.className = 'parent-item-type';
|
||||||
onDelete(id);
|
var title = document.createElementNS(HTML_NS, 'div');
|
||||||
});
|
title.append(parentItem.getDisplayTitle());
|
||||||
row.appendChild(removeButton);
|
title.className = 'parent-title';
|
||||||
|
container.append(img, title);
|
||||||
|
node.querySelector('.zotero-context-pane-editor-parent-line').append(container);
|
||||||
}
|
}
|
||||||
|
_updateAddToNote();
|
||||||
list.appendChild(row);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue