diff --git a/chrome/content/zotero/customElements.js b/chrome/content/zotero/customElements.js index 736d3eebb8..c027e611cd 100644 --- a/chrome/content/zotero/customElements.js +++ b/chrome/content/zotero/customElements.js @@ -29,6 +29,7 @@ Services.scriptloader.loadSubScript("chrome://zotero/content/include.js", this); Services.scriptloader.loadSubScript("chrome://global/content/customElements.js", this); Services.scriptloader.loadSubScript("chrome://zotero/content/elements/base.js", this); Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemPaneSection.js', this); +Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemPaneContainer.js', this); Services.scriptloader.loadSubScript('chrome://zotero/content/elements/itemTreeMenuBar.js', this); { diff --git a/chrome/content/zotero/elements/abstractBox.js b/chrome/content/zotero/elements/abstractBox.js index 23779a065c..b3e4b34dba 100644 --- a/chrome/content/zotero/elements/abstractBox.js +++ b/chrome/content/zotero/elements/abstractBox.js @@ -39,6 +39,10 @@ `); + _mode = 'view'; + + _fieldAlternatives = {}; + get item() { return this._item; } @@ -66,6 +70,36 @@ super.editable = editable; } + get mode() { + return this._mode; + } + + set mode(val) { + switch (val) { + case 'view': + case 'edit': + case 'merge': + break; + case 'fieldmerge': + this._fieldAlternatives = {}; + break; + default: + throw new Error(`Invalid mode '${val}'`); + } + this._mode = val; + this.setAttribute('mode', val); + } + + set fieldAlternatives(val) { + if (val.constructor.name != 'Object') { + throw Error('fieldAlternatives must be an Object in .fieldAlternatives'); + } + if (this._mode != 'fieldmerge') { + throw Error('fieldAlternatives is valid only in fieldmerge mode in .fieldAlternatives'); + } + this._fieldAlternatives = val; + } + init() { this._notifierID = Zotero.Notifier.registerObserver(this, ['item', 'tab'], 'abstractBox'); @@ -157,6 +191,59 @@ } this._abstractField.readOnly = !this.editable; this._abstractField.setAttribute('aria-label', Zotero.ItemFields.getLocalizedString('abstractNote')); + + this._renderFieldVersionButton(); + } + + _renderFieldVersionButton() { + let existing = this.querySelector('.zotero-field-version-button'); + if (existing) { + existing.remove(); + } + + if (this._mode !== 'fieldmerge') { + return; + } + + let alternatives = this._fieldAlternatives?.abstractNote; + if (!alternatives || !alternatives.length) { + return; + } + + let button = document.createXULElement('toolbarbutton'); + button.className = 'zotero-field-version-button zotero-clicky-merge'; + document.l10n.setAttributes(button, 'itembox-button-merge', { + field: Zotero.ItemFields.getLocalizedString('abstractNote') || '' + }); + + let popup = button.appendChild(document.createXULElement('menupopup')); + for (let v of alternatives) { + let menuitem = document.createXULElement('menuitem'); + let sv = Zotero.Utilities.ellipsize(v, 60); + menuitem.setAttribute('label', sv); + if (v != sv) { + menuitem.setAttribute('tooltiptext', v); + } + menuitem.setAttribute('originalValue', v); + menuitem.addEventListener('command', () => { + this.item.setField('abstractNote', menuitem.getAttribute('originalValue')); + this._forceRenderAll(); + }); + popup.appendChild(menuitem); + } + + button.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + if (event.screenX) { + popup.openPopupAtScreen(event.screenX - 5, event.screenY + 5, true); + } + else { + popup.openPopup(button, 'after_start'); + } + }); + + this.querySelector('.body').appendChild(button); } _ensureFeedAbstractBrowserExists = Zotero.Utilities.Internal.serial(async () => { diff --git a/chrome/content/zotero/elements/collapsibleSection.js b/chrome/content/zotero/elements/collapsibleSection.js index 00cbc7b13b..a5335b9497 100644 --- a/chrome/content/zotero/elements/collapsibleSection.js +++ b/chrome/content/zotero/elements/collapsibleSection.js @@ -199,7 +199,7 @@ } _buildContextMenu() { - let containerRoot = this.closest('.zotero-view-item-container, context-notes-list'); + let containerRoot = this.closest('.item-pane-container-root, .zotero-view-item-container, context-notes-list'); let contextMenu = document.createXULElement('menupopup'); @@ -298,7 +298,7 @@ let canMoveUp = sidenav?.isPaneMovable(this.dataset.pane, 'up'); let canMoveDown = sidenav?.isPaneMovable(this.dataset.pane, 'down'); - let canReset = sidenav?.isOrderChanged(); + let canReset = !!sidenav?.container?.supportsReorder && sidenav?.isOrderChanged(); moveSectionUp.hidden = !canMoveUp; moveSectionDown.hidden = !canMoveDown; diff --git a/chrome/content/zotero/elements/duplicatesMergePane.js b/chrome/content/zotero/elements/duplicatesMergePane.js index e0b7d6a14d..e3e850e051 100644 --- a/chrome/content/zotero/elements/duplicatesMergePane.js +++ b/chrome/content/zotero/elements/duplicatesMergePane.js @@ -29,26 +29,29 @@ mergeItems: "chrome://zotero/content/mergeItems.mjs", }); - class DuplicatesMergePane extends XULElementBase { + class DuplicatesMergePane extends ItemPaneContainerBase { content = MozXULElement.parseXULToFragment(` - -