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:
parent
73ba5f9ffe
commit
dba841770c
5 changed files with 102 additions and 175 deletions
|
@ -24,15 +24,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { forwardRef, useImperativeHandle, useState } from 'react';
|
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 (
|
return (
|
||||||
<div className="note-row" onClick={onClick}>
|
<div className={cx('note-row', { 'standalone-note-row': !parentItemType })} onClick={onClick}>
|
||||||
<div className="inner">
|
<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 className="title">{title}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="second-line">
|
<div className="body-line">
|
||||||
<div className="date">{date}</div>
|
<div className="date">{date}</div>
|
||||||
<div className="body">{body}</div>
|
<div className="body">{body}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -395,18 +395,40 @@ var ZoteroContextPane = new function () {
|
||||||
var label = document.createElement('label');
|
var label = document.createElement('label');
|
||||||
var button = document.createElement('button');
|
var button = document.createElement('button');
|
||||||
button.setAttribute('label', Zotero.Intl.strings['zotero.toolbar.newNote']);
|
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);
|
contextNode.setAttribute('selectedIndex', 1);
|
||||||
var item = new Zotero.Item('note');
|
var item = new Zotero.Item('note');
|
||||||
item.libraryID = libraryID;
|
item.libraryID = libraryID;
|
||||||
// item.parentKey = parentItem.key;
|
if (parentID) {
|
||||||
|
item.parentID = parentID;
|
||||||
|
}
|
||||||
editor.mode = 'edit';
|
editor.mode = 'edit';
|
||||||
editor.item = item;
|
editor.item = item;
|
||||||
editor.parentItem = null;
|
editor.parentItem = null;
|
||||||
editor.focus();
|
editor.focus();
|
||||||
_updateAddToNote();
|
_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');
|
var vbox1 = document.createElement('vbox');
|
||||||
vbox1.append(label, button);
|
vbox1.append(label, button);
|
||||||
|
@ -445,7 +467,6 @@ var ZoteroContextPane = new function () {
|
||||||
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');
|
||||||
s.addCondition('noChildren', 'true');
|
|
||||||
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) {
|
||||||
|
@ -461,6 +482,7 @@ var ZoteroContextPane = new function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
notesListRef.current.setNotes(notes.map(note => {
|
notesListRef.current.setNotes(notes.map(note => {
|
||||||
|
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();
|
||||||
|
@ -471,7 +493,9 @@ var ZoteroContextPane = new function () {
|
||||||
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,
|
||||||
|
parentTitle: parentItem && parentItem.getDisplayTitle()
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -636,170 +660,17 @@ var ZoteroContextPane = new function () {
|
||||||
}
|
}
|
||||||
var parentItem = Zotero.Items.get(item.parentID);
|
var parentItem = Zotero.Items.get(item.parentID);
|
||||||
|
|
||||||
// tabbox
|
// Info pane
|
||||||
var tabbox = document.createElement('tabbox');
|
var panelInfo = document.createElement('vbox');
|
||||||
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');
|
|
||||||
panelInfo.setAttribute('flex', '1');
|
panelInfo.setAttribute('flex', '1');
|
||||||
panelInfo.className = 'zotero-editpane-item-box';
|
panelInfo.className = 'zotero-editpane-item-box';
|
||||||
var itemBox = document.createElement('zoteroitembox');
|
var itemBox = document.createElement('zoteroitembox');
|
||||||
itemBox.setAttribute('flex', '1');
|
itemBox.setAttribute('flex', '1');
|
||||||
panelInfo.append(itemBox);
|
panelInfo.append(itemBox);
|
||||||
// Notes panel
|
container.append(panelInfo);
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
itemBox.mode = editable ? 'edit' : 'view';
|
itemBox.mode = editable ? 'edit' : 'view';
|
||||||
itemBox.item = parentItem;
|
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();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -624,6 +624,13 @@
|
||||||
<hbox id="zotero-context-pane-inner" flex="1" zotero-persist="height"/>
|
<hbox id="zotero-context-pane-inner" flex="1" zotero-persist="height"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
</box>
|
</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>
|
</hbox>
|
||||||
|
|
||||||
<!-- Barrier to prevent tabbing into Zotero pane when busy -->
|
<!-- Barrier to prevent tabbing into Zotero pane when busy -->
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
margin-bottom: .25em !important;
|
margin-bottom: .25em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zotero-item-pane-content {
|
||||||
|
background: -moz-Field; /* Same as background colour for treeview */
|
||||||
|
}
|
||||||
|
|
||||||
.zotero-view-item
|
.zotero-view-item
|
||||||
{
|
{
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
background: #d2d8e2;
|
||||||
|
border-top: 1px solid lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notes-list {
|
.notes-list {
|
||||||
|
@ -10,17 +12,23 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 0;
|
height: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
padding-top: 2px;
|
||||||
|
|
||||||
|
&> section > h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 7px 8px 3px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-row {
|
.note-row {
|
||||||
padding: 8px 12px;
|
border: 1px solid #bcc4d2;
|
||||||
|
border-radius: 5px;
|
||||||
&:not(:last-child) {
|
margin: 4px 7px;
|
||||||
border-bottom: 1px solid $shade-3;
|
background-color: #fff;
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: #e2e2e2;
|
background: #e4ebf9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inner {
|
.inner {
|
||||||
|
@ -28,8 +36,30 @@
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.first-line {
|
.parent-line {
|
||||||
display: flex;
|
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 {
|
.title {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
@ -41,8 +71,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.second-line {
|
.body-line {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
padding: 0 8px 6px;
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
color: $shade-6;
|
color: $shade-6;
|
||||||
|
@ -60,3 +91,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.standalone-note-row {
|
||||||
|
.title-line {
|
||||||
|
padding-top: 6px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue