Rework context pane in PDF reader

- Remove item pane tabs
- Show all notes in notes pane, showing the parent title where
  appropriate
- Show drop-down menu for "New Note" button in notes pane that allows
  creating standalone or child notes
- Add some temporary styling for notes in the notes pane

TODO:

- 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
- Search on parent item title for child notes
This commit is contained in:
Dan Stillman 2021-02-21 20:10:42 -05:00
parent 73ba5f9ffe
commit dba841770c
5 changed files with 102 additions and 175 deletions

View file

@ -24,15 +24,23 @@
*/
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import cx from 'classnames';
const NoteRow = ({ title, body, date, onClick }) => {
const NoteRow = ({ title, body, date, onClick, parentItemType, parentTitle }) => {
return (
<div className="note-row" onClick={onClick}>
<div className={cx('note-row', { 'standalone-note-row': !parentItemType })} onClick={onClick}>
<div className="inner">
<div className="first-line">
{ parentItemType
? <div className="parent-line">
<img className="parent-item-type" src={Zotero.ItemTypes.getImageSrc(parentItemType)} />
<span className="parent-title">{parentTitle}</span>
</div>
: null
}
<div className="title-line">
<div className="title">{title}</div>
</div>
<div className="second-line">
<div className="body-line">
<div className="date">{date}</div>
<div className="body">{body}</div>
</div>

View file

@ -395,19 +395,41 @@ var ZoteroContextPane = new function () {
var label = document.createElement('label');
var button = document.createElement('button');
button.setAttribute('label', Zotero.Intl.strings['zotero.toolbar.newNote']);
button.addEventListener('click', () => {
// Create standalone or child note on menuitem click
document.getElementById('context-pane-new-note-button-popup').onclick = function (event) {
var parentID = null;
switch (event.originalTarget.id) {
case 'context-pane-new-standalone-note':
break;
case 'context-pane-new-item-note':
// TODO: Get the parent item
parentID = parentItem.id;
break;
default:
return;
}
contextNode.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note');
item.libraryID = libraryID;
// item.parentKey = parentItem.key;
if (parentID) {
item.parentID = parentID;
}
editor.mode = 'edit';
editor.item = item;
editor.parentItem = null;
editor.focus();
_updateAddToNote();
};
button.addEventListener('mousedown', (event) => {
var popup = document.getElementById('context-pane-new-note-button-popup');
popup.openPopup(event.target, 'after_end');
});
var vbox1 = document.createElement('vbox');
vbox1.append(label, button);
@ -445,7 +467,6 @@ var ZoteroContextPane = new function () {
var s = new Zotero.Search();
s.addCondition('libraryID', 'is', libraryID);
s.addCondition('itemType', 'is', 'note');
s.addCondition('noChildren', 'true');
if (query) {
let parts = Zotero.SearchConditions.parseSearchString(query);
for (let part of parts) {
@ -461,6 +482,7 @@ var ZoteroContextPane = new function () {
});
notesListRef.current.setNotes(notes.map(note => {
var parentItem = note.parentItem;
var text = note.note;
text = Zotero.Utilities.unescapeHTML(text);
text = text.trim();
@ -471,7 +493,9 @@ var ZoteroContextPane = new function () {
id: note.id,
title: title || Zotero.getString('pane.item.notes.untitled'),
body: parts[1] || '',
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale))
date: (new Date(note.dateModified).toLocaleDateString(Zotero.locale)),
parentItemType: parentItem && parentItem.itemType,
parentTitle: parentItem && parentItem.getDisplayTitle()
};
}));
@ -636,170 +660,17 @@ var ZoteroContextPane = new function () {
}
var parentItem = Zotero.Items.get(item.parentID);
// tabbox
var tabbox = document.createElement('tabbox');
tabbox.setAttribute('flex', '1');
tabbox.className = 'zotero-view-tabbox';
container.append(tabbox);
// tabs
var tabs = document.createElement('tabs');
tabs.className = 'zotero-editpane-tabs';
// tabpanels
var tabpanels = document.createElement('tabpanels');
tabpanels.setAttribute('flex', '1');
tabpanels.className = 'zotero-view-item';
tabpanels.addEventListener('select', () => {
_updateAddToNote();
});
tabbox.append(tabs, tabpanels);
// Info tab
var tabInfo = document.createElement('tab');
tabInfo.setAttribute('label', Zotero.Intl.strings['zotero.tabs.info.label']);
// Notes tab
var tabNotes = document.createElement('tab');
tabNotes.setAttribute('label', Zotero.Intl.strings['zotero.tabs.notes.label']);
// Tags tab
var tabTags = document.createElement('tab');
tabTags.setAttribute('label', Zotero.Intl.strings['zotero.tabs.tags.label']);
// Related tab
var tabRelated = document.createElement('tab');
tabRelated.setAttribute('label', Zotero.Intl.strings['zotero.tabs.related.label']);
tabs.append(tabInfo, tabNotes, tabTags, tabRelated);
// Info panel
var panelInfo = document.createElement('tabpanel');
// Info pane
var panelInfo = document.createElement('vbox');
panelInfo.setAttribute('flex', '1');
panelInfo.className = 'zotero-editpane-item-box';
var itemBox = document.createElement('zoteroitembox');
itemBox.setAttribute('flex', '1');
panelInfo.append(itemBox);
// Notes panel
var panelNotes = document.createElement('tabpanel');
panelNotes.setAttribute('flex', '1');
panelNotes.setAttribute('orient', 'vertical');
var deck = document.createElement('deck');
deck.className = 'notes-deck';
deck.setAttribute('flex', '1');
panelNotes.append(deck);
var vbox2 = document.createElement('vbox');
var note = document.createElement('zoteronoteeditor');
note.setAttribute('flex', 1);
vbox2.append(note);
var vbox = document.createElement('vbox');
vbox.setAttribute('flex', '1');
vbox.setAttribute('class', 'zotero-box');
vbox.style.overflowY = 'auto';
panelNotes.append(vbox);
var hbox = document.createElement('hbox');
hbox.setAttribute('align', 'center');
var label = document.createElement('label');
var button = document.createElement('button');
button.hidden = !editable;
button.setAttribute('label', Zotero.Intl.strings['zotero.item.add']);
button.addEventListener('click', () => {
deck.setAttribute('selectedIndex', 1);
var item = new Zotero.Item('note');
item.libraryID = parentItem.libraryID;
item.parentID = parentItem.id;
note.returnHandler = () => {
deck.setAttribute('selectedIndex', 0);
_updateAddToNote();
};
note.mode = editable ? 'edit' : 'view';
note.item = item;
note.focus();
_updateAddToNote();
});
hbox.append(label, button);
var grid = document.createElement('grid');
grid.setAttribute('flex', 1);
var columns = document.createElement('columns');
var column = document.createElement('column');
column.setAttribute('flex', 1);
columns.append(column);
var column = document.createElement('column');
columns.append(column);
grid.append(columns);
var rows = document.createElement('rows');
rows.setAttribute('flex', 1);
grid.append(rows);
vbox.append(hbox, grid);
deck.append(vbox, vbox2);
deck.setAttribute('selectedIndex', 0);
// Tags panel
var panelTags = document.createElement('tabpanel');
panelTags.setAttribute('orient', 'vertical');
panelTags.setAttribute('context', 'tags-context-menu');
panelTags.className = 'tags-pane';
panelTags.style.display = 'flex';
var div = document.createElementNS(HTML_NS, 'div');
div.className = 'tags-box-container';
div.style.display = 'flex';
div.style.flexGrow = '1';
panelTags.append(div);
var tagsBoxRef = React.createRef();
ReactDOM.render(
<TagsBoxContainer
key={'tagsBox-' + parentItem.id}
item={parentItem}
editable={editable}
ref={tagsBoxRef}
/>,
div
);
// Related panel
var panelRelated = document.createElement('tabpanel');
var relatedBox = document.createElement('relatedbox');
relatedBox.setAttribute('flex', '1');
relatedBox.className = 'zotero-editpane-related';
panelRelated.addEventListener('click', (event) => {
if (event.originalTarget.closest('.zotero-clicky')) {
Zotero_Tabs.select('zotero-pane');
}
});
panelRelated.append(relatedBox);
tabpanels.append(panelInfo, panelNotes, panelTags, panelRelated);
tabbox.selectedIndex = 0;
container.append(panelInfo);
itemBox.mode = editable ? 'edit' : 'view';
itemBox.item = parentItem;
relatedBox.mode = editable ? 'edit' : 'view';
relatedBox.item = parentItem;
function _renderNotesPanel() {
rows.innerHTML = '';
var parentItem = Zotero.Items.get(parentID);
if (!parentItem) {
return;
}
var parentNotes = Zotero.Items.get(parentItem.getNotes());
_appendNoteRows(parentNotes, rows, editable, (id) => {
deck.setAttribute('selectedIndex', 1);
note.returnHandler = () => {
deck.setAttribute('selectedIndex', 0);
_updateAddToNote();
};
note.mode = editable ? 'edit' : 'view';
note.item = Zotero.Items.get(id);
note.parentItem = null;
_updateAddToNote();
}, (id) => {
_removeNote(id);
});
var c = parentNotes.length;
label.value = Zotero.getString('pane.item.notes.count', c, c);
}
context.update = Zotero.Utilities.throttle(_renderNotesPanel, 500);
_renderNotesPanel();
}
};

View file

@ -624,6 +624,13 @@
<hbox id="zotero-context-pane-inner" flex="1" zotero-persist="height"/>
</vbox>
</box>
<popupset>
<menupopup id="context-pane-new-note-button-popup">
<menuitem id="context-pane-new-standalone-note" label="&zotero.toolbar.note.standalone;"/>
<menuitem id="context-pane-new-item-note" label="&zotero.toolbar.note.child;"/>
</menupopup>
</popupset>
</hbox>
<!-- Barrier to prevent tabbing into Zotero pane when busy -->

View file

@ -20,6 +20,10 @@
margin-bottom: .25em !important;
}
.zotero-item-pane-content {
background: -moz-Field; /* Same as background colour for treeview */
}
.zotero-view-item
{
padding: 0;

View file

@ -3,6 +3,8 @@
width: 100%;
overflow-y: auto;
flex-grow: 1;
background: #d2d8e2;
border-top: 1px solid lightgray;
}
.notes-list {
@ -10,17 +12,23 @@
flex-direction: column;
height: 0;
flex-grow: 1;
padding-top: 2px;
&> section > h2 {
font-weight: bold;
padding: 7px 8px 3px;
font-size: 13px;
}
}
.note-row {
padding: 8px 12px;
&:not(:last-child) {
border-bottom: 1px solid $shade-3;
}
border: 1px solid #bcc4d2;
border-radius: 5px;
margin: 4px 7px;
background-color: #fff;
&:active {
background: #e2e2e2;
background: #e4ebf9;
}
.inner {
@ -28,9 +36,31 @@
margin-top: 3px;
}
.first-line {
.parent-line {
display: flex;
width: calc(100% - 16px);
border-bottom: 1px solid #d7dad7;
align-items: center;
padding: 5px 8px 4px;
margin-bottom: 5px;
}
.parent-item-type {
margin-right: 3px;
}
.parent-title {
flex-grow: 1;
width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.title-line {
display: flex;
padding: 0 8px 0;
.title {
flex-grow: 1;
width: 0;
@ -40,9 +70,10 @@
font-weight: bold;
}
}
.second-line {
.body-line {
display: flex;
padding: 0 8px 6px;
.date {
color: $shade-6;
@ -60,3 +91,9 @@
}
}
}
.standalone-note-row {
.title-line {
padding-top: 6px !important;
}
}