fx-compat: Item box: Fix multiline fields & autocomplete

This commit is contained in:
Abe Jellinek 2022-05-24 15:42:54 -06:00
parent 02bcb1712c
commit fdd73d4ada
3 changed files with 131 additions and 59 deletions

View file

@ -26,6 +26,10 @@
"use strict";
{
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://zotero/content/elements/shadowAutocompleteInput.js", this);
class ItemBox extends XULElement {
constructor() {
super();
@ -1146,13 +1150,7 @@
}
ensureElementIsVisible(elem) {
try {
var sbo = this.boxObject;
sbo.ensureElementIsVisible(elem);
}
catch (e) {
Zotero.logError(e);
}
elem.scrollIntoView();
}
changeTypeTo(itemTypeID, menu) {
@ -1441,7 +1439,7 @@
}
showEditor(elem) {
return (async function () {
return (async () => {
Zotero.debug(`Showing editor for ${elem.getAttribute('fieldname')}`);
var label = elem.closest('tr').querySelector('th');
@ -1508,61 +1506,67 @@
}
}
var t = document.createElement("input");
t.setAttribute('id', `itembox-field-textbox-${fieldName}`);
t.setAttribute('value', value);
t.setAttribute('fieldname', fieldName);
t.setAttribute('ztabindex', tabindex);
t.setAttribute('flex', '1');
if (creatorField=='lastName') {
t.setAttribute('fieldMode', elem.getAttribute('fieldMode'));
t.setAttribute('newlines','pasteintact');
}
var t;
if (Zotero.ItemFields.isMultiline(fieldName) || Zotero.ItemFields.isLong(fieldName)) {
t.setAttribute('multiline', true);
t = document.createElement("textarea");
t.setAttribute('rows', 8);
}
else {
// Add auto-complete for certain fields
if (Zotero.ItemFields.isAutocompleteField(fieldName)
|| fieldName == 'creator') {
t.setAttribute('type', 'autocomplete');
t.setAttribute('autocompletesearch', 'zotero');
// Add auto-complete for certain fields
else if (Zotero.ItemFields.isAutocompleteField(fieldName)
|| fieldName == 'creator') {
t = document.createElement("input", { is: 'shadow-autocomplete-input' });
t.setAttribute('autocompletesearch', 'zotero');
let params = {
fieldName: fieldName,
libraryID: this.item.libraryID
};
if (field == 'creator') {
params.fieldMode = parseInt(elem.getAttribute('fieldMode'));
let params = {
fieldName: fieldName,
libraryID: this.item.libraryID
};
if (field == 'creator') {
params.fieldMode = parseInt(elem.getAttribute('fieldMode'));
// Include itemID and creatorTypeID so the autocomplete can
// avoid showing results for creators already set on the item
let row = elem.closest('tr');
let creatorTypeID = parseInt(
row.getElementsByClassName('creator-type-label')[0]
.getAttribute('typeid')
);
if (itemID) {
params.itemID = itemID;
params.creatorTypeID = creatorTypeID;
}
// Return
t.setAttribute('ontextentered',
'this.handleCreatorAutoCompleteSelect(this, true)');
// Tab/Shift-Tab
t.setAttribute('onchange',
'this.handleCreatorAutoCompleteSelect(this)');
};
t.setAttribute(
'autocompletesearchparam', JSON.stringify(params)
// Include itemID and creatorTypeID so the autocomplete can
// avoid showing results for creators already set on the item
let row = elem.closest('tr');
let creatorTypeID = parseInt(
row.getElementsByClassName('creator-type-label')[0]
.getAttribute('typeid')
);
t.setAttribute('completeselectedindex', true);
if (itemID) {
params.itemID = itemID;
params.creatorTypeID = creatorTypeID;
}
// Return
t.addEventListener('keydown', (event) => {
if (event.key == 'Enter') {
this.handleCreatorAutoCompleteSelect(t, true);
}
});
// Tab/Shift-Tab
t.setAttribute('onchange',
'this.handleCreatorAutoCompleteSelect(this)');
if (creatorField == 'lastName') {
t.setAttribute('fieldMode', elem.getAttribute('fieldMode'));
}
}
t.setAttribute(
'autocompletesearchparam', JSON.stringify(params)
);
t.setAttribute('completeselectedindex', true);
}
if (!t) {
t = document.createElement("input");
}
t.id = `itembox-field-textbox-${fieldName}`;
t.value = value;
t.dataset.originalValue = value;
t.style.mozBoxFlex = 1;
t.setAttribute('fieldname', fieldName);
t.setAttribute('ztabindex', tabindex);
var box = elem.parentNode;
box.replaceChild(t, elem);
@ -1597,7 +1601,7 @@
t.onkeypress = (event) => this.handleKeyPress(event);
return t;
}.bind(this))();
})();
}
@ -1701,7 +1705,7 @@
// Shift-enter adds new creator row
if (fieldname.indexOf('creator-') == 0 && event.shiftKey) {
// Value hasn't changed
if (target.getAttribute('value') == target.value) {
if (target.dataset.originalValue == target.value) {
Zotero.debug("Value hasn't changed");
// If + button is disabled, just focus next creator row
if (target.closest('tr').lastChild.lastChild.disabled) {
@ -1732,7 +1736,7 @@
case event.DOM_VK_ESCAPE:
// Reset field to original value
target.value = target.getAttribute('value');
target.value = target.dataset.originalValue;
focused.blur();

View file

@ -0,0 +1,60 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2022 Corporation for Digital Scholarship
Vienna, Virginia, USA
https://www.zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
"use strict";
{
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
// Load specific element because customElements.js loads on element creation only
Services.scriptloader.loadSubScript("chrome://global/content/elements/autocomplete-input.js", this);
/**
* Extends AutocompleteInput to fix document.activeElement checks that
* don't work in a shadow DOM context.
*/
class ShadowAutocompleteInput extends customElements.get('autocomplete-input') {
get focused() {
// document.activeElement by itself doesn't traverse shadow DOMs; see
// https://www.abeautifulsite.net/posts/finding-the-active-element-in-a-shadow-root/
function activeElement(root) {
let activeHere = root.activeElement;
if (activeHere?.shadowRoot) {
return activeElement(activeHere.shadowRoot);
}
else {
return activeHere;
}
}
return this === activeElement(document);
}
}
customElements.define("shadow-autocomplete-input", ShadowAutocompleteInput, {
extends: "input",
});
}

View file

@ -25,6 +25,10 @@ td > [fieldname] {
width: 100%;
}
.value.multiline {
white-space: pre-line;
}
/*td > vbox > description
{
margin: 0 !important;
@ -66,6 +70,10 @@ label[singleField=false]:after
margin: 0;
}*/
textarea {
font: inherit;
}
/* metadata field names */
th, .creator-type-label {
font-weight: normal;