Add sorting options and column picker to View menu (#2543)
This commit is contained in:
parent
a7b5648733
commit
e516fc8354
4 changed files with 231 additions and 143 deletions
|
@ -2463,6 +2463,182 @@ var ItemTree = class ItemTree extends LibraryTree {
|
|||
}
|
||||
};
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Menu utilities for ZoteroPane
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
buildColumnPickerMenu(menupopup) {
|
||||
const prefix = 'zotero-column-picker-';
|
||||
// Filter out ignored columns
|
||||
const columns = this._getColumns();
|
||||
let columnMenuitemElements = {};
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
if (column.showInColumnPicker === false) continue;
|
||||
let label = formatColumnName(column);
|
||||
let menuitem = document.createXULElement('menuitem');
|
||||
menuitem.setAttribute('type', 'checkbox');
|
||||
menuitem.setAttribute('label', label);
|
||||
menuitem.setAttribute('colindex', i);
|
||||
menuitem.addEventListener('command', () => this.tree._columns.toggleHidden(i));
|
||||
if (!column.hidden) {
|
||||
menuitem.setAttribute('checked', true);
|
||||
}
|
||||
if (column.disabledIn && column.disabledIn.includes(this.collectionTreeRow.visibilityGroup)) {
|
||||
menuitem.setAttribute('disabled', true);
|
||||
}
|
||||
columnMenuitemElements[column.dataKey] = menuitem;
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
|
||||
try {
|
||||
// More Columns menu
|
||||
let id = prefix + 'more-menu';
|
||||
|
||||
let moreMenu = document.createXULElement('menu');
|
||||
moreMenu.setAttribute('label', Zotero.getString('pane.items.columnChooser.moreColumns'));
|
||||
moreMenu.setAttribute('anonid', id);
|
||||
|
||||
let moreMenuPopup = document.createXULElement('menupopup');
|
||||
moreMenuPopup.setAttribute('anonid', id + '-popup');
|
||||
|
||||
let moreItems = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
if (column.columnPickerSubMenu) {
|
||||
moreItems.push(columnMenuitemElements[column.dataKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort fields and move to submenu
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
moreItems.sort(function (a, b) {
|
||||
return collation.compareString(1, a.getAttribute('label'), b.getAttribute('label'));
|
||||
});
|
||||
moreItems.forEach(function (elem) {
|
||||
moreMenuPopup.appendChild(menupopup.removeChild(elem));
|
||||
});
|
||||
|
||||
let sep = document.createXULElement('menuseparator');
|
||||
menupopup.appendChild(sep);
|
||||
moreMenu.appendChild(moreMenuPopup);
|
||||
menupopup.appendChild(moreMenu);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Secondary Sort menu
|
||||
//
|
||||
if (!this.collectionTreeRow.isFeedsOrFeed()) {
|
||||
try {
|
||||
const id = prefix + 'sort-menu';
|
||||
const primaryField = this.getSortField();
|
||||
const sortFields = this.getSortFields();
|
||||
let secondaryField = false;
|
||||
if (sortFields[1]) {
|
||||
secondaryField = sortFields[1];
|
||||
}
|
||||
|
||||
const primaryFieldLabel = formatColumnName(columns.find(c => c.dataKey == primaryField));
|
||||
|
||||
const sortMenu = document.createXULElement('menu');
|
||||
sortMenu.setAttribute('label',
|
||||
Zotero.getString('pane.items.columnChooser.secondarySort', primaryFieldLabel));
|
||||
sortMenu.setAttribute('anonid', id);
|
||||
|
||||
const sortMenuPopup = document.createXULElement('menupopup');
|
||||
sortMenuPopup.setAttribute('anonid', id + '-popup');
|
||||
|
||||
// Generate menuitems
|
||||
const sortOptions = [
|
||||
'title',
|
||||
'firstCreator',
|
||||
'itemType',
|
||||
'date',
|
||||
'year',
|
||||
'publisher',
|
||||
'publicationTitle',
|
||||
'dateAdded',
|
||||
'dateModified'
|
||||
];
|
||||
for (let field of sortOptions) {
|
||||
// Hide current primary field, and don't show Year for Date, since it would be a no-op
|
||||
if (field == primaryField || (primaryField == 'date' && field == 'year')) {
|
||||
continue;
|
||||
}
|
||||
let column = columns.find(c => c.dataKey == field);
|
||||
let label = formatColumnName(column);
|
||||
|
||||
let sortMenuItem = document.createXULElement('menuitem');
|
||||
sortMenuItem.setAttribute('fieldName', field);
|
||||
sortMenuItem.setAttribute('label', label);
|
||||
sortMenuItem.setAttribute('type', 'checkbox');
|
||||
if (field == secondaryField) {
|
||||
sortMenuItem.setAttribute('checked', 'true');
|
||||
}
|
||||
sortMenuItem.addEventListener('command', async () => {
|
||||
if (this._setSecondarySortField(field)) {
|
||||
await this.sort();
|
||||
}
|
||||
})
|
||||
sortMenuPopup.appendChild(sortMenuItem);
|
||||
}
|
||||
|
||||
sortMenu.appendChild(sortMenuPopup);
|
||||
menupopup.appendChild(sortMenu);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let sep = document.createXULElement('menuseparator');
|
||||
// sep.setAttribute('anonid', prefix + 'sep');
|
||||
menupopup.appendChild(sep);
|
||||
|
||||
//
|
||||
// Restore Default Column Order
|
||||
//
|
||||
let menuitem = document.createXULElement('menuitem');
|
||||
menuitem.setAttribute('label', Zotero.getString('zotero.items.restoreColumnOrder.label'));
|
||||
menuitem.setAttribute('anonid', prefix + 'restore-order');
|
||||
menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
|
||||
buildSortMenu(menupopup) {
|
||||
this._getColumns()
|
||||
.filter(column => !column.hidden)
|
||||
.forEach((column, i) => {
|
||||
let menuItem = document.createXULElement('menuitem');
|
||||
menuItem.setAttribute('type', 'checkbox');
|
||||
menuItem.setAttribute('checked', this.getSortField() == column.dataKey);
|
||||
menuItem.setAttribute('label', formatColumnName(column));
|
||||
menuItem.addEventListener('command', () => {
|
||||
this.toggleSort(i, true);
|
||||
});
|
||||
menupopup.append(menuItem);
|
||||
});
|
||||
}
|
||||
|
||||
toggleSort(sortIndex, countVisible = false) {
|
||||
if (countVisible) {
|
||||
let cols = this._getColumns();
|
||||
sortIndex = cols.indexOf(cols.filter(col => !col.hidden)[sortIndex]);
|
||||
if (sortIndex == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.tree._columns.toggleSort(sortIndex);
|
||||
}
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Private methods
|
||||
|
@ -3532,16 +3708,14 @@ var ItemTree = class ItemTree extends LibraryTree {
|
|||
|
||||
_displayColumnPickerMenu = (event) => {
|
||||
if (!this.props.columnPicker) return;
|
||||
const prefix = 'zotero-column-picker-';
|
||||
const doc = document;
|
||||
let popupset = document.querySelector('#zotero-column-picker-popupset');
|
||||
if (!popupset) {
|
||||
popupset = doc.createXULElement('popupset');
|
||||
popupset = document.createXULElement('popupset');
|
||||
popupset.id = 'zotero-column-picker-popupset';
|
||||
document.children[0].appendChild(popupset);
|
||||
}
|
||||
|
||||
const menupopup = doc.createXULElement('menupopup');
|
||||
const menupopup = document.createXULElement('menupopup');
|
||||
menupopup.id = 'zotero-column-picker';
|
||||
menupopup.addEventListener('popuphiding', (event) => {
|
||||
if (event.target.id == menupopup.id) {
|
||||
|
@ -3549,145 +3723,7 @@ var ItemTree = class ItemTree extends LibraryTree {
|
|||
}
|
||||
});
|
||||
|
||||
// Filter out ignored columns
|
||||
const columns = this._getColumns();
|
||||
let columnMenuitemElements = {};
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
if (column.showInColumnPicker === false) continue;
|
||||
let label = formatColumnName(column);
|
||||
let menuitem = doc.createXULElement('menuitem');
|
||||
menuitem.setAttribute('type', 'checkbox');
|
||||
menuitem.setAttribute('label', label);
|
||||
menuitem.setAttribute('colindex', i);
|
||||
menuitem.addEventListener('command', () => this.tree._columns.toggleHidden(i));
|
||||
if (!column.hidden) {
|
||||
menuitem.setAttribute('checked', true);
|
||||
}
|
||||
if (column.disabledIn && column.disabledIn.includes(this.collectionTreeRow.visibilityGroup)) {
|
||||
menuitem.setAttribute('disabled', true);
|
||||
}
|
||||
columnMenuitemElements[column.dataKey] = menuitem;
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
|
||||
try {
|
||||
// More Columns menu
|
||||
let id = prefix + 'more-menu';
|
||||
|
||||
let moreMenu = doc.createXULElement('menu');
|
||||
moreMenu.setAttribute('label', Zotero.getString('pane.items.columnChooser.moreColumns'));
|
||||
moreMenu.setAttribute('anonid', id);
|
||||
|
||||
let moreMenuPopup = doc.createXULElement('menupopup');
|
||||
moreMenuPopup.setAttribute('anonid', id + '-popup');
|
||||
|
||||
let moreItems = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const column = columns[i];
|
||||
if (column.columnPickerSubMenu) {
|
||||
moreItems.push(columnMenuitemElements[column.dataKey]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort fields and move to submenu
|
||||
var collation = Zotero.getLocaleCollation();
|
||||
moreItems.sort(function (a, b) {
|
||||
return collation.compareString(1, a.getAttribute('label'), b.getAttribute('label'));
|
||||
});
|
||||
moreItems.forEach(function (elem) {
|
||||
moreMenuPopup.appendChild(menupopup.removeChild(elem));
|
||||
});
|
||||
|
||||
let sep = doc.createXULElement('menuseparator');
|
||||
menupopup.appendChild(sep);
|
||||
moreMenu.appendChild(moreMenuPopup);
|
||||
menupopup.appendChild(moreMenu);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// Secondary Sort menu
|
||||
//
|
||||
if (!this.collectionTreeRow.isFeedsOrFeed()) {
|
||||
try {
|
||||
const id = prefix + 'sort-menu';
|
||||
const primaryField = this.getSortField();
|
||||
const sortFields = this.getSortFields();
|
||||
let secondaryField = false;
|
||||
if (sortFields[1]) {
|
||||
secondaryField = sortFields[1];
|
||||
}
|
||||
|
||||
const primaryFieldLabel = formatColumnName(columns.find(c => c.dataKey == primaryField));
|
||||
|
||||
const sortMenu = doc.createXULElement('menu');
|
||||
sortMenu.setAttribute('label',
|
||||
Zotero.getString('pane.items.columnChooser.secondarySort', primaryFieldLabel));
|
||||
sortMenu.setAttribute('anonid', id);
|
||||
|
||||
const sortMenuPopup = doc.createXULElement('menupopup');
|
||||
sortMenuPopup.setAttribute('anonid', id + '-popup');
|
||||
|
||||
// Generate menuitems
|
||||
const sortOptions = [
|
||||
'title',
|
||||
'firstCreator',
|
||||
'itemType',
|
||||
'date',
|
||||
'year',
|
||||
'publisher',
|
||||
'publicationTitle',
|
||||
'dateAdded',
|
||||
'dateModified'
|
||||
];
|
||||
for (let field of sortOptions) {
|
||||
// Hide current primary field, and don't show Year for Date, since it would be a no-op
|
||||
if (field == primaryField || (primaryField == 'date' && field == 'year')) {
|
||||
continue;
|
||||
}
|
||||
let column = columns.find(c => c.dataKey == field);
|
||||
let label = formatColumnName(column);
|
||||
|
||||
let sortMenuItem = doc.createXULElement('menuitem');
|
||||
sortMenuItem.setAttribute('fieldName', field);
|
||||
sortMenuItem.setAttribute('label', label);
|
||||
sortMenuItem.setAttribute('type', 'checkbox');
|
||||
if (field == secondaryField) {
|
||||
sortMenuItem.setAttribute('checked', 'true');
|
||||
}
|
||||
sortMenuItem.addEventListener('command', async () => {
|
||||
if (this._setSecondarySortField(field)) {
|
||||
await this.sort();
|
||||
}
|
||||
})
|
||||
sortMenuPopup.appendChild(sortMenuItem);
|
||||
}
|
||||
|
||||
sortMenu.appendChild(sortMenuPopup);
|
||||
menupopup.appendChild(sortMenu);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
Zotero.debug(e, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let sep = doc.createXULElement('menuseparator');
|
||||
// sep.setAttribute('anonid', prefix + 'sep');
|
||||
menupopup.appendChild(sep);
|
||||
|
||||
//
|
||||
// Restore Default Column Order
|
||||
//
|
||||
let menuitem = doc.createXULElement('menuitem');
|
||||
menuitem.setAttribute('label', Zotero.getString('zotero.items.restoreColumnOrder.label'));
|
||||
menuitem.setAttribute('anonid', prefix + 'restore-order');
|
||||
menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
|
||||
menupopup.appendChild(menuitem);
|
||||
this.buildColumnPickerMenu(menupopup);
|
||||
|
||||
popupset.appendChild(menupopup);
|
||||
menupopup.openPopupAtScreen(
|
||||
|
|
|
@ -1624,6 +1624,16 @@ var ZoteroPane = new function()
|
|||
});
|
||||
ZoteroPane.itemsView.onRefresh.addListener(() => ZoteroPane.setTagScope());
|
||||
ZoteroPane.itemsView.waitForLoad().then(() => Zotero.uiIsReady());
|
||||
|
||||
let sortSubmenuKeys = document.getElementById('sortSubmenuKeys');
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let key = document.createElement('key');
|
||||
key.id = 'key_sortCol' + i;
|
||||
key.setAttribute('modifiers', Zotero.isMac ? 'accel alt control' : 'accel alt');
|
||||
key.setAttribute('key', (i + 1) % 10);
|
||||
key.addEventListener('command', () => ZoteroPane.itemsView.toggleSort(i, true));
|
||||
sortSubmenuKeys.append(key);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
|
@ -6166,6 +6176,31 @@ var ZoteroPane = new function()
|
|||
|
||||
this.itemPane.handleResize();
|
||||
}
|
||||
|
||||
this.onColumnPickerPopupShowing = function (event) {
|
||||
let menuPopup = document.getElementById('column-picker-submenu').menupopup;
|
||||
if (event.target !== menuPopup) {
|
||||
return;
|
||||
}
|
||||
menuPopup.replaceChildren();
|
||||
this.itemsView?.buildColumnPickerMenu(menuPopup);
|
||||
};
|
||||
|
||||
this.onSortPopupShowing = function (event) {
|
||||
let menuPopup = document.getElementById('sort-submenu').menupopup;
|
||||
if (event.target !== menuPopup) {
|
||||
return;
|
||||
}
|
||||
menuPopup.replaceChildren();
|
||||
this.itemsView?.buildSortMenu(menuPopup);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
if (!menuPopup.children[i]) {
|
||||
break;
|
||||
}
|
||||
menuPopup.children[i].setAttribute('key', 'key_sortCol' + i);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens the about dialog
|
||||
|
|
|
@ -191,6 +191,8 @@
|
|||
<key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
|
||||
</keyset>
|
||||
|
||||
<keyset id="sortSubmenuKeys"/>
|
||||
|
||||
<vbox id="titlebar">
|
||||
<hbox class="titlebar-icon-container">
|
||||
<html:div class="titlebar-icon"></html:div>
|
||||
|
@ -624,6 +626,19 @@
|
|||
oncommand="ZoteroStandalone.onViewMenuItemClick(event); event.stopPropagation();"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
<menuseparator class="menu-type-library" />
|
||||
<menu id="column-picker-submenu"
|
||||
class="menu-type-library"
|
||||
label="&columns.label;"
|
||||
onpopupshowing="ZoteroPane_Local.onColumnPickerPopupShowing(event)">
|
||||
<menupopup/>
|
||||
</menu>
|
||||
<menu id="sort-submenu"
|
||||
class="menu-type-library"
|
||||
label="&sortBy.label;"
|
||||
onpopupshowing="ZoteroPane_Local.onSortPopupShowing(event)">
|
||||
<menupopup/>
|
||||
</menu>
|
||||
<menuseparator/>
|
||||
<menuitem id="view-menuitem-recursive-collections"
|
||||
class="menu-type-library"
|
||||
|
|
|
@ -60,6 +60,8 @@
|
|||
<!ENTITY recursiveCollections.label "Show Items from Subcollections">
|
||||
<!ENTITY fontSize.label "Font Size">
|
||||
<!ENTITY noteFontSize.label "Note Font Size">
|
||||
<!ENTITY columns.label "Columns">
|
||||
<!ENTITY sortBy.label "Sort By">
|
||||
|
||||
<!--TOOLS MENU-->
|
||||
<!ENTITY toolsMenu.label "Tools">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue