Item pane header customization (#3791)

This commit is contained in:
Abe Jellinek 2024-05-10 08:23:26 -04:00 committed by GitHub
parent 8278140492
commit 59b1d75b98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 664 additions and 112 deletions

View file

@ -79,7 +79,7 @@ var Zotero_File_Interface_Bibliography = new function() {
}
// See note in style.js
if (!Zotero.Styles.initialized) {
if (!Zotero.Styles.initialized()) {
// Initialize styles
yield Zotero.Styles.init();
}

View file

@ -439,14 +439,7 @@
}
get _ignoreFields() {
let value = ['title', 'abstractNote']
.flatMap(field => [
field,
...(Zotero.ItemFields.getTypeFieldsFromBase(field, true) || [])
]);
// Cache the result
Object.defineProperty(this, '_ignoreFields', { value });
return value;
return ['abstractNote'];
}
get _linkMenu() {
@ -696,7 +689,7 @@
var button = document.createXULElement("toolbarbutton");
button.className = 'zotero-field-version-button zotero-clicky-merge';
button.setAttribute('type', 'menu');
button.setAttribute('wantdropmarker', true);
button.setAttribute('data-l10n-id', 'itembox-button-merge');
var popup = button.appendChild(document.createXULElement("menupopup"));
@ -760,8 +753,13 @@
// Creator rows
// Place, in order of preference, after type or at beginning
let field = this._infoTable.querySelector('[fieldname="itemType"]');
// Place, in order of preference, after title, after type,
// or at beginning
var titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this.item.itemTypeID, 'title');
var field = this._infoTable.querySelector(`[fieldname="${Zotero.ItemFields.getName(titleFieldID)}"]`);
if (!field) {
field = this._infoTable.querySelector('[fieldName="itemType"]');
}
if (field) {
this._beforeRow = field.parentNode.nextSibling;
}
@ -2069,6 +2067,13 @@
var fieldName = label.getAttribute('fieldname');
this._modifyField(fieldName, newValue);
if (Zotero.ItemFields.isFieldOfBase(fieldName, 'title')) {
let shortTitleVal = this.item.getField('shortTitle');
if (newValue.toLowerCase().startsWith(shortTitleVal.toLowerCase())) {
this._modifyField('shortTitle', newValue.substring(0, shortTitleVal.length));
}
}
if (this.saveOnEdit) {
// If a field is open, blur it, which will trigger a save and cause
// the saveTx() to be a no-op

View file

@ -26,30 +26,31 @@
"use strict";
{
let htmlDoc = document.implementation.createHTMLDocument();
class PaneHeader extends ItemPaneSectionElementBase {
content = MozXULElement.parseXULToFragment(`
<html:div class="head">
<html:div class="title">
<editable-text />
</html:div>
<html:div class="menu-button">
<toolbarbutton
class="zotero-tb-button expand-button"
tooltiptext="&zotero.toolbar.openURL.label;"
type="menu"
wantdropmarker="true"
tabindex="0">
<menupopup onpopupshowing="Zotero_LocateMenu.buildLocateMenu(this)"/>
</toolbarbutton>
</html:div>
</html:div>
<html:div class="custom-head">
<html:div class="title">
<editable-text />
</html:div>
<html:div class="creator-year" />
<html:div class="bib-entry" />
<popupset>
<menupopup class="secondary-popup">
<menuitem data-l10n-id="text-action-copy" />
<menuseparator />
<menu data-l10n-id="item-pane-header-view-as">
<menupopup class="view-as-popup" />
</menu>
</menupopup>
</popupset>
<html:div class="custom-head"/>
`, ['chrome://zotero/locale/zotero.dtd']);
showInFeeds = true;
_item = null;
_titleFieldID = null;
@ -65,9 +66,51 @@
init() {
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'paneHeader');
this._prefsObserverIDs = [
Zotero.Prefs.registerObserver('itemPaneHeader', () => {
// TEMP?: _forceRenderAll() doesn't do anything if the section is hidden, so un-hide first
this.hidden = false;
this._forceRenderAll();
}),
Zotero.Prefs.registerObserver('itemPaneHeader.bibEntry.style', () => this._forceRenderAll()),
Zotero.Prefs.registerObserver('itemPaneHeader.bibEntry.locale', () => this._forceRenderAll()),
];
this.titleField = this.querySelector('.title editable-text');
this.menuButton = this.querySelector('.menu-button');
this.title = this.querySelector('.title');
this.titleField = this.title.querySelector('editable-text');
this.creatorYear = this.querySelector('.creator-year');
this.bibEntry = this.querySelector('.bib-entry');
this.bibEntry.attachShadow({ mode: 'open' });
// Context menu for non-editable information (creator/year and bib entry)
this.secondaryPopup = this.querySelector('.secondary-popup');
this.secondaryPopup.firstElementChild.addEventListener('command', () => this._handleSecondaryCopy());
this.creatorYear.addEventListener('contextmenu', (event) => {
if (this.item) {
this.secondaryPopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true);
}
});
this.bibEntryContent = document.createElement('div');
this.bibEntryContent.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
this.bibEntryContent.addEventListener('click', (event) => {
event.preventDefault();
if (event.target.matches('a[href]')) {
Zotero.launchURL(event.target.href);
}
});
this.bibEntryContent.addEventListener('contextmenu', (event) => {
if (this._item && Zotero.Styles.initialized()) {
this.secondaryPopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true);
}
});
this.bibEntry.shadowRoot.append(this.bibEntryContent);
this.viewAsPopup = this.querySelector('.view-as-popup');
this.viewAsPopup.addEventListener('popupshowing', () => this._buildViewAsMenu(this.viewAsPopup));
this._bibEntryCache = new LRUCache();
this.titleField.addEventListener('change', () => this.save());
this.titleField.ariaLabel = Zotero.getString('itemFields.title');
@ -81,6 +124,17 @@
this._setTransformedValue(newValue);
},
});
menupopup.append(document.createXULElement('menuseparator'));
let viewAsMenu = document.createXULElement('menu');
viewAsMenu.setAttribute('data-l10n-id', 'item-pane-header-view-as');
viewAsMenu.setAttribute('type', 'menu');
let viewAsPopup = document.createXULElement('menupopup');
this._buildViewAsMenu(viewAsPopup);
viewAsMenu.append(viewAsPopup);
menupopup.append(viewAsMenu);
this.ownerDocument.querySelector('popupset').append(menupopup);
menupopup.addEventListener('popuphidden', () => menupopup.remove());
menupopup.openPopupAtScreen(event.screenX + 1, event.screenY + 1, true);
@ -89,9 +143,18 @@
destroy() {
Zotero.Notifier.unregisterObserver(this._notifierID);
for (let id of this._prefsObserverIDs) {
Zotero.Prefs.unregisterObserver(id);
}
}
notify(action, type, ids) {
if (action == 'modify' || action == 'delete') {
for (let id of ids) {
this._bibEntryCache.delete(id);
}
}
if (action == 'modify' && this.item && ids.includes(this.item.id)) {
this._forceRenderAll();
}
@ -102,15 +165,15 @@
this._item.setField(this._titleFieldID, newValue);
let shortTitleVal = this._item.getField('shortTitle');
if (newValue.toLowerCase().startsWith(shortTitleVal.toLowerCase())) {
this._item.setField('shortTitle', newValue.substr(0, shortTitleVal.length));
this._item.setField('shortTitle', newValue.substring(0, shortTitleVal.length));
}
await this._item.saveTx();
}
async save() {
if (this.item) {
this.item.setField(this._titleFieldID, this.titleField.value);
await this.item.saveTx();
if (this._item) {
this._item.setField(this._titleFieldID, this.titleField.value);
await this._item.saveTx();
}
this._forceRenderAll();
}
@ -121,29 +184,189 @@
await this.save();
}
}
render() {
if (!this.item) {
if (!this._item) {
return;
}
if (this._isAlreadyRendered()) return;
this._titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this.item.itemTypeID, 'title');
let headerMode = Zotero.Prefs.get('itemPaneHeader');
if (this._item.isAttachment()) {
headerMode = 'title';
}
let title = this.item.getField(this._titleFieldID);
// If focused, update the value that will be restored on Escape;
// otherwise, update the displayed value
if (this.titleField.focused) {
this.titleField.initialValue = title;
if (headerMode === 'none') {
this.hidden = true;
return;
}
else {
this.titleField.value = title;
this.hidden = false;
this.title.hidden = true;
this.creatorYear.hidden = true;
this.bibEntry.hidden = true;
if (headerMode === 'bibEntry') {
if (!Zotero.Styles.initialized()) {
this.bibEntryContent.textContent = Zotero.getString('general.loading');
this.bibEntry.classList.add('loading');
this.bibEntry.hidden = false;
Zotero.Styles.init().then(() => this._forceRenderAll());
return;
}
if (this._renderBibEntry()) {
this.bibEntry.hidden = false;
return;
}
// Fall back to Title/Creator/Year if style is not found
headerMode = 'titleCreatorYear';
}
this.titleField.readOnly = !this.editable;
if (this._titleFieldID) {
this.titleField.placeholder = Zotero.ItemFields.getLocalizedString(this._titleFieldID);
if (headerMode === 'title' || headerMode === 'titleCreatorYear') {
this._titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this._item.itemTypeID, 'title');
let title = this.item.getField(this._titleFieldID);
// If focused, update the value that will be restored on Escape;
// otherwise, update the displayed value
if (this.titleField.focused) {
this.titleField.initialValue = title;
}
else {
this.titleField.value = title;
}
this.titleField.readOnly = !this.editable;
if (this._titleFieldID) {
this.titleField.placeholder = Zotero.ItemFields.getLocalizedString(this._titleFieldID);
}
this.title.hidden = false;
}
this.menuButton.hidden = !this.item.isRegularItem() && !this.item.isAttachment();
if (headerMode === 'titleCreatorYear') {
let firstCreator = this._item.getField('firstCreator');
let year = this._item.getField('year');
let creatorYearString = '';
if (firstCreator) {
creatorYearString += firstCreator;
}
if (year) {
creatorYearString += ` (${year})`;
}
if (creatorYearString) {
this.creatorYear.textContent = creatorYearString;
this.creatorYear.hidden = false;
}
else {
this.creatorYear.hidden = true;
}
}
// Make title field padding tighter if creator/year is visible below it
this.titleField.toggleAttribute('tight',
headerMode === 'titleCreatorYear' && !this.creatorYear.hidden);
}
_renderBibEntry() {
let style = Zotero.Styles.get(Zotero.Prefs.get('itemPaneHeader.bibEntry.style'));
if (!style) {
Zotero.warn('Style not found: ' + Zotero.Prefs.get('itemPaneHeader.bibEntry.style'));
return false;
}
let locale = Zotero.Prefs.get('itemPaneHeader.bibEntry.locale');
// Create engine if not cached (first run with this style)
if (this._cslEngineStyleID !== style.styleID || this._cslEngineLocale !== locale) {
this._cslEngine = style.getCiteProc(locale, 'html');
this._cslEngineStyleID = style.styleID;
this._cslEngineLocale = locale;
this._bibEntryCache.clear();
}
// Create bib entry if not cached (first run on this item or item data has changed)
if (!this._bibEntryCache.has(this._item.id)) {
// Force refresh items - without this, entries won't change when item data changes
this._cslEngine.updateItems([]);
this._bibEntryCache.set(this._item.id,
Zotero.Cite.makeFormattedBibliographyOrCitationList(this._cslEngine,
[this._item], 'html', false));
}
htmlDoc.body.innerHTML = this._bibEntryCache.get(this._item.id);
// Remove .loading (added above if styles weren't yet initialized)
this.bibEntry.classList.remove('loading');
// Remove existing children and *then* append new ones to avoid "scripts are blocked internally"
// error in log
this.bibEntryContent.replaceChildren();
this.bibEntryContent.append(...htmlDoc.body.childNodes);
let body = this.bibEntryContent.querySelector('.csl-bib-body');
if (!body) {
Zotero.debug('No .csl-bib-body found in bib entry');
return false;
}
// Remove any custom indentation/line height set by the style
body.style.marginLeft = body.style.marginRight = '';
body.style.textIndent = '';
body.style.lineHeight = '';
if (style.categories === 'numeric') {
// Remove number from entry if present
let number = body.querySelector('.csl-entry > .csl-left-margin:first-child');
if (number) {
let followingContent = number.nextElementSibling;
if (followingContent?.classList.contains('csl-right-inline')) {
followingContent.classList.remove('csl-right-inline');
followingContent.style = '';
}
number.remove();
}
}
return true;
}
_handleSecondaryCopy() {
let selectedMode = Zotero.Prefs.get('itemPaneHeader');
if (selectedMode === 'titleCreatorYear') {
Zotero.Utilities.Internal.copyTextToClipboard(this.creatorYear.textContent);
}
else if (selectedMode === 'bibEntry') {
Zotero_File_Interface.copyItemsToClipboard(
[this._item],
Zotero.Prefs.get('itemPaneHeader.bibEntry.style'),
Zotero.Prefs.get('itemPaneHeader.bibEntry.locale'),
false,
false
);
}
}
_buildViewAsMenu(menupopup) {
menupopup.replaceChildren();
let selectedMode = Zotero.Prefs.get('itemPaneHeader');
for (let headerMode of ['title', 'titleCreatorYear', 'bibEntry']) {
let menuitem = document.createXULElement('menuitem');
menuitem.setAttribute('data-l10n-id', 'item-pane-header-' + headerMode);
menuitem.setAttribute('type', 'radio');
menuitem.setAttribute('checked', headerMode === selectedMode);
menuitem.addEventListener('command', () => {
Zotero.Prefs.set('itemPaneHeader', headerMode);
});
menupopup.append(menuitem);
}
menupopup.append(document.createXULElement('menuseparator'));
let moreOptionsMenuitem = document.createXULElement('menuitem');
moreOptionsMenuitem.setAttribute('data-l10n-id', 'item-pane-header-more-options');
moreOptionsMenuitem.addEventListener('command', () => {
Zotero.Utilities.Internal.openPreferences('zotero-prefpane-general');
});
menupopup.append(moreOptionsMenuitem);
}
renderCustomHead(callback) {
@ -159,4 +382,46 @@
}
}
customElements.define("pane-header", PaneHeader);
/**
* Simple LRU cache that stores bibliography entries for the 100 most recently viewed items.
*/
class LRUCache {
static CACHE_SIZE = 100;
_map = new Map();
clear() {
this._map.clear();
}
has(key) {
return this._map.has(key);
}
get(key) {
if (!this._map.has(key)) {
return undefined;
}
let value = this._map.get(key);
// Maps are sorted by insertion order, so delete and add back at the end
this._map.delete(key);
this._map.set(key, value);
return value;
}
set(key, value) {
this._map.delete(key);
// Delete the first (= inserted earliest) elements until we're under CACHE_SIZE
while (this._map.size >= this.constructor.CACHE_SIZE) {
this._map.delete(this._map.keys().next().value);
}
this._map.set(key, value);
return this;
}
delete(key) {
return this._map.delete(key);
}
}
}

View file

@ -54,6 +54,7 @@
<linkset>
<html:link rel="localization" href="branding/brand.ftl"/>
<html:link rel="localization" href="zotero.ftl"/>
<html:link rel="localization" href="preferences.ftl"/>
</linkset>

View file

@ -53,6 +53,7 @@ Zotero_Preferences.General = {
}
this.refreshLocale();
this._initItemPaneHeaderUI();
this.updateAutoRenameFilesUI();
this._updateFileHandlerUI();
this._initEbookFontFamilyMenu();
@ -136,6 +137,83 @@ Zotero_Preferences.General = {
Zotero.Utilities.Internal.quitZotero(true);
}
},
_initItemPaneHeaderUI() {
let pane = document.querySelector('#zotero-prefpane-general');
let headerMenu = document.querySelector('#item-pane-header-menulist');
let styleMenu = document.querySelector('#item-pane-header-style-menu');
this._updateItemPaneHeaderStyleUI();
pane.addEventListener('showing', () => this._updateItemPaneHeaderStyleUI());
// menulists stop responding to clicks if we replace their items while
// they're closing. Yield back to the event loop before updating to
// avoid this.
let updateUI = () => {
setTimeout(() => {
this._updateItemPaneHeaderStyleUI();
});
};
headerMenu.addEventListener('command', updateUI);
styleMenu.addEventListener('command', updateUI);
},
_updateItemPaneHeaderStyleUI: Zotero.Utilities.Internal.serial(async function () {
let optionsContainer = document.querySelector('#item-pane-header-bib-entry-options');
let styleMenu = document.querySelector('#item-pane-header-style-menu');
let localeMenu = document.querySelector('#item-pane-header-locale-menu');
optionsContainer.hidden = Zotero.Prefs.get('itemPaneHeader') !== 'bibEntry';
if (optionsContainer.hidden) {
return;
}
if (!Zotero.Styles.initialized()) {
let menus = [styleMenu, localeMenu];
for (let menu of menus) {
menu.selectedItem = null;
menu.setAttribute('label', Zotero.getString('general.loading'));
menu.disabled = true;
}
await Zotero.Styles.init();
for (let menu of menus) {
menu.disabled = false;
}
}
let currentStyle = Zotero.Styles.get(styleMenu.value);
let currentLocale = Zotero.Prefs.get('itemPaneHeader.bibEntry.locale');
styleMenu.menupopup.replaceChildren();
for (let style of Zotero.Styles.getVisible()) {
let menuitem = document.createXULElement('menuitem');
menuitem.label = style.title;
menuitem.value = style.styleID;
styleMenu.menupopup.append(menuitem);
}
if (currentStyle) {
if (currentStyle.styleID !== styleMenu.value) {
// Style has been renamed
styleMenu.value = currentStyle.styleID;
}
if (!localeMenu.menupopup.childElementCount) {
Zotero.Styles.populateLocaleList(localeMenu);
}
Zotero.Styles.updateLocaleList(localeMenu, currentStyle, currentLocale);
}
else {
// Style is unknown/removed - show placeholder
let shortName = styleMenu.value.replace('http://www.zotero.org/styles/', '');
let missingLabel = await document.l10n.formatValue(
'preferences-item-pane-header-missing-style',
{ shortName }
);
styleMenu.selectedItem = null;
styleMenu.setAttribute('label', missingLabel);
}
}),
updateAutoRenameFilesUI: function () {
setTimeout(() => {

View file

@ -44,6 +44,39 @@
<menupopup onpopuphidden="Zotero_Preferences.General.onLocaleChange()"/>
</menulist>
</hbox>
<hbox align="center">
<label data-l10n-id="preferences-item-pane-header"/>
<menulist id="item-pane-header-menulist" preference="extensions.zotero.itemPaneHeader" native="true">
<menupopup>
<menuitem data-l10n-id="item-pane-header-none" value="none"/>
<menuitem data-l10n-id="item-pane-header-title" value="title"/>
<menuitem data-l10n-id="item-pane-header-titleCreatorYear" value="titleCreatorYear"/>
<menuitem data-l10n-id="item-pane-header-bibEntry" value="bibEntry"/>
</menupopup>
</menulist>
</hbox>
<vbox id="item-pane-header-bib-entry-options" class="indented-pref">
<hbox align="center">
<label data-l10n-id="preferences-item-pane-header-style"/>
<menulist
id="item-pane-header-style-menu"
preference="extensions.zotero.itemPaneHeader.bibEntry.style"
native="true"
flex="1"
><menupopup/></menulist>
</hbox>
<hbox align="center">
<label data-l10n-id="preferences-item-pane-header-locale"/>
<menulist
id="item-pane-header-locale-menu"
preference="extensions.zotero.itemPaneHeader.bibEntry.locale"
native="true"
><menupopup/></menulist>
</hbox>
</vbox>
</groupbox>
<groupbox id="zotero-prefpane-file-handling-groupbox">

View file

@ -507,7 +507,6 @@ Zotero.Styles = new function() {
* Populate menulist with locales
*
* @param {xul:menulist} menulist
* @return {Promise}
*/
this.populateLocaleList = function (menulist) {
if (!_initialized) {

View file

@ -33,6 +33,11 @@ preferences-color-scheme-light =
preferences-color-scheme-dark =
.label = Dark
preferences-item-pane-header = Item Pane Header:
preferences-item-pane-header-style = Header Citation Style:
preferences-item-pane-header-locale = Header Language:
preferences-item-pane-header-missing-style = Missing style: <{ $shortName }>
preferences-advanced-language-and-region-title = Language and Region
preferences-advanced-enable-bidi-ui =
.label = Enable bidirectional text editing utilities

View file

@ -114,6 +114,8 @@ item-button-view-online =
itembox-button-options =
.tooltiptext = Open Context Menu
itembox-button-merge =
.aria-label = Select Version
reader-use-dark-mode-for-content =
.label = Use Dark Mode for Content
@ -494,3 +496,16 @@ quicksearch-input =
.aria-label = Quick Search
.placeholder = { $placeholder }
.aria-description = { $placeholder }
item-pane-header-view-as =
.label = View As
item-pane-header-none =
.label = None
item-pane-header-title =
.label = Title
item-pane-header-titleCreatorYear =
.label = Title, Creator, Year
item-pane-header-bibEntry =
.label = Bibliography Entry
item-pane-header-more-options =
.label = More Options

View file

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 9.29298V2.707L5.707 5L5 4.293L8.5 0.792999L12 4.293L11.293 5L9 2.707V9.29284L15 15.2929L14.2929 16L8.50008 10.2071L2.70711 16L2 15.2929L8 9.29298Z" fill="context-fill"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 1H15V3H9V1ZM8 1C8 0.447715 8.44771 0 9 0H15C15.5523 0 16 0.447715 16 1V3C16 3.55228 15.5523 4 15 4H9C8.44771 4 8 3.55228 8 3V1ZM9 7H15V9H9V7ZM8 7C8 6.44772 8.44771 6 9 6H15C15.5523 6 16 6.44772 16 7V9C16 9.55228 15.5523 10 15 10H9C8.44771 10 8 9.55228 8 9V7ZM15 13H9V15H15V13ZM9 12C8.44771 12 8 12.4477 8 13V15C8 15.5523 8.44771 16 9 16H15C15.5523 16 16 15.5523 16 15V13C16 12.4477 15.5523 12 15 12H9ZM7 2C5.34315 2 4 3.34315 4 5V6C4 7.10457 3.10457 8 2 8H1H-1V9H1H2C3.10457 9 4 9.89543 4 11V12C4 13.6569 5.34315 15 7 15V14C5.89543 14 5 13.1046 5 12V11C5 10.2316 4.71115 9.53076 4.2361 9H7V8H4.23611C4.71115 7.46924 5 6.76835 5 6V5C5 3.89543 5.89543 3 7 3V2Z" fill="context-fill"/>
</svg>

Before

Width:  |  Height:  |  Size: 325 B

After

Width:  |  Height:  |  Size: 838 B

View file

@ -74,6 +74,9 @@ pref("extensions.zotero.sortCreatorAsString", false);
pref("extensions.zotero.uiDensity", "comfortable");
pref("extensions.zotero.itemPaneHeader", "title");
pref("extensions.zotero.itemPaneHeader.bibEntry.style", "http://www.zotero.org/styles/apa");
pref("extensions.zotero.itemPaneHeader.bibEntry.locale", "");
//Tag Selector
pref("extensions.zotero.tagSelector.showAutomatic", true);

View file

@ -1,20 +1,37 @@
duplicates-merge-pane {
display: flex;
flex-direction: column;
groupbox {
margin: 8px 0 0 0;
min-height: 0;
&, #zotero-duplicates-merge-version-select, #zotero-duplicates-merge-item-box-container {
display: flex;
flex-direction: column;
gap: 8px;
}
#zotero-duplicates-merge-button
{
font-size: 13px;
padding-top: 9px;
> groupbox {
// Override default margin/padding that breaks our styles here
margin: 0;
padding: 0;
padding-inline: 8px;
min-height: 0;
> :is(description, label, button) {
margin: 0;
}
}
#zotero-duplicates-merge-field-select {
margin-bottom: 9px;
}
#zotero-duplicates-merge-item-box-container {
overflow-y: auto;
padding: 0 8px;
flex: 1;
padding-inline: 8px;
overflow-y: scroll;
border-top: var(--material-border-quinary);
collapsible-section > .head {
display: none;
}
}
/* Show duplicates date list item as selected even when not focused

View file

@ -1,11 +1,17 @@
@include comfortable {
--editable-text-padding-inline: 4px;
--editable-text-padding-block: 4px;
--editable-text-tight-padding-inline: 4px;
--editable-text-tight-padding-block: 2px;
}
@include compact {
--editable-text-padding-inline: 4px;
--editable-text-padding-block: 1px;
--editable-text-tight-padding-inline: 3px;
--editable-text-tight-padding-block: 1px;
}
editable-text {
@ -14,15 +20,8 @@ editable-text {
--max-visible-lines: 1;
&[tight] {
@include comfortable {
--editable-text-padding-inline: 4px;
--editable-text-padding-block: 2px;
}
@include compact {
--editable-text-padding-inline: 3px;
--editable-text-padding-block: 1px;
}
--editable-text-padding-inline: var(--editable-text-tight-padding-inline);
--editable-text-padding-block: var(--editable-text-tight-padding-block);
}
// Fun auto-sizing approach from CSSTricks:

View file

@ -14,6 +14,10 @@ item-box {
#info-table {
@include meta-table;
.meta-row .zotero-field-version-button {
padding: 3px;
}
}
.creator-type-label, #more-creators-label {
@ -169,12 +173,6 @@ item-box {
align-self: center;
}
/* Merge pane in duplicates view */
.zotero-field-version-button {
margin: 0;
padding: 0;
}
/*
* Retraction box
*/

View file

@ -1,46 +1,44 @@
pane-header {
display: flex;
&:not([hidden]) {
display: flex;
}
flex-direction: column;
align-items: flex-start;
padding: 6px 8px;
gap: 6px;
align-items: stretch;
padding: 8px;
border-bottom: 1px solid var(--fill-quinary);
.head {
display: flex;
align-self: stretch;
gap: 4px;
max-height: 25%;
overflow-y: auto;
scrollbar-color: var(--color-scrollbar) var(--color-scrollbar-background);
scrollbar-gutter: stable;
.title {
margin-top: calc(0px - var(--editable-text-padding-block));
flex: 1 1 0;
font-weight: 600;
line-height: 1.333;
.title {
align-self: center;
margin-top: calc(0px - var(--editable-text-padding-block));
padding: 2px 0px 1px 0px;
flex: 1 1 0;
font-weight: 600;
line-height: 1.333;
editable-text {
flex: 1;
}
}
.menu-button {
align-self: start;
}
.menu-button toolbarbutton {
@include svgicon-menu("go-to", "universal", "20");
editable-text {
flex: 1;
}
}
.menu-button {
align-self: start;
.creator-year {
color: var(--fill-secondary);
}
.menu-button toolbarbutton {
@include svgicon-menu("go-to", "universal", "20");
--width-focus-border: 2px;
@include focus-ring;
.bib-entry {
line-height: 1.5;
&.loading {
color: var(--fill-secondary);
}
}
.creator-year, .bib-entry {
// Set padding to match editable-text in tight mode, plus 1px for border
padding-inline: calc(var(--editable-text-tight-padding-inline) + 1px);
overflow-wrap: anywhere;
}
.custom-head {

View file

@ -18,6 +18,10 @@
height: 16px;
}
#item-pane-header-locale-menu {
min-width: 12em;
}
@media (-moz-platform: windows) {
button, menulist, radio, checkbox, input {
margin-block: 4px;

View file

@ -9,6 +9,138 @@ describe("Item pane", function () {
after(function () {
win.close();
});
describe("Item pane header", function () {
let itemData = {
itemType: 'book',
title: 'Birds - A Primer of Ornithology (Teach Yourself Books)',
creators: [{
creatorType: 'author',
lastName: 'Hyde',
firstName: 'George E.'
}]
};
before(async function () {
await Zotero.Styles.init();
});
after(function () {
Zotero.Prefs.clear('itemPaneHeader');
Zotero.Prefs.clear('itemPaneHeader.bibEntry.style');
Zotero.Prefs.clear('itemPaneHeader.bibEntry.locale');
});
it("should be hidden when set to None mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'none');
await createDataObject('item', itemData);
assert.isTrue(doc.querySelector('pane-header').hidden);
});
it("should show title when set to Title mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'title');
let item = await createDataObject('item', itemData);
assert.isFalse(doc.querySelector('pane-header .title').hidden);
assert.isTrue(doc.querySelector('pane-header .creator-year').hidden);
assert.isTrue(doc.querySelector('pane-header .bib-entry').hidden);
assert.equal(doc.querySelector('pane-header .title editable-text').value, item.getField('title'));
});
it("should show title/creator/year when set to Title/Creator/Year mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'titleCreatorYear');
let item = await createDataObject('item', itemData);
item.setField('date', '1962-05-01');
await item.saveTx();
assert.isTrue(doc.querySelector('pane-header .bib-entry').hidden);
assert.isFalse(doc.querySelector('pane-header .title').hidden);
assert.isFalse(doc.querySelector('pane-header .creator-year').hidden);
assert.equal(doc.querySelector('pane-header .title editable-text').value, item.getField('title'));
let creatorYearText = doc.querySelector('pane-header .creator-year').textContent;
assert.include(creatorYearText, 'Hyde');
assert.include(creatorYearText, '1962');
});
it("should show bib entry when set to Bibliography Entry mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'bibEntry');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/apa');
await createDataObject('item', itemData);
assert.isFalse(doc.querySelector('pane-header .bib-entry').hidden);
assert.isTrue(doc.querySelector('pane-header .title').hidden);
assert.isTrue(doc.querySelector('pane-header .creator-year').hidden);
let bibEntry = doc.querySelector('pane-header .bib-entry').shadowRoot.firstElementChild.textContent;
assert.equal(bibEntry.trim(), 'Hyde, G. E. (n.d.). Birds—A Primer of Ornithology (Teach Yourself Books).');
});
it("should update bib entry on item change when set to Bibliography Entry mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'bibEntry');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/apa');
let item = await createDataObject('item', itemData);
let bibEntryElem = doc.querySelector('pane-header .bib-entry').shadowRoot.firstElementChild;
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, G. E. (n.d.). Birds—A Primer of Ornithology (Teach Yourself Books).');
item.setField('date', '1962-05-01');
await item.saveTx();
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, G. E. (1962). Birds—A Primer of Ornithology (Teach Yourself Books).');
item.setCreators([
{
creatorType: 'author',
lastName: 'Smith',
firstName: 'John'
}
]);
await item.saveTx();
assert.equal(bibEntryElem.textContent.trim(), 'Smith, J. (1962). Birds—A Primer of Ornithology (Teach Yourself Books).');
item.setField('title', 'Birds');
await item.saveTx();
assert.equal(bibEntryElem.textContent.trim(), 'Smith, J. (1962). Birds.');
});
it("should update bib entry on style change when set to Bibliography Entry mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'bibEntry');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/apa');
await createDataObject('item', itemData);
let bibEntryElem = doc.querySelector('pane-header .bib-entry').shadowRoot.firstElementChild;
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, G. E. (n.d.). Birds—A Primer of Ornithology (Teach Yourself Books).');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/chicago-author-date');
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, George E. n.d. Birds - A Primer of Ornithology (Teach Yourself Books).');
});
it("should update bib entry on locale change when set to Bibliography Entry mode", async function () {
Zotero.Prefs.set('itemPaneHeader', 'bibEntry');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/apa');
await createDataObject('item', itemData);
let bibEntryElem = doc.querySelector('pane-header .bib-entry').shadowRoot.firstElementChild;
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, G. E. (n.d.). Birds—A Primer of Ornithology (Teach Yourself Books).');
Zotero.Prefs.set('itemPaneHeader.bibEntry.locale', 'de-DE');
assert.equal(bibEntryElem.textContent.trim(), 'Hyde, G. E. (o. J.). Birds—A Primer of Ornithology (Teach Yourself Books).');
});
it("should fall back to Title/Creator/Year when citation style is missing", async function () {
Zotero.Prefs.set('itemPaneHeader', 'bibEntry');
Zotero.Prefs.set('itemPaneHeader.bibEntry.style', 'http://www.zotero.org/styles/an-id-that-does-not-match-any-citation-style');
await createDataObject('item', itemData);
assert.isTrue(doc.querySelector('pane-header .bib-entry').hidden);
assert.isFalse(doc.querySelector('pane-header .title').hidden);
assert.isFalse(doc.querySelector('pane-header .creator-year').hidden);
});
});
describe("Info pane", function () {
it("should refresh on item update", function* () {