fx-compat: Item box: Fix multiline fields & autocomplete
This commit is contained in:
parent
02bcb1712c
commit
fdd73d4ada
3 changed files with 131 additions and 59 deletions
|
@ -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();
|
||||
|
||||
|
|
60
chrome/content/zotero/elements/shadowAutocompleteInput.js
Normal file
60
chrome/content/zotero/elements/shadowAutocompleteInput.js
Normal 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",
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue