Don't close clicked field when clicking away from changed field

Fixes #1401

The item box badly wants to be redone in React.
This commit is contained in:
Dan Stillman 2018-01-03 02:33:36 -05:00
parent 374eefada1
commit 2baa537542

View File

@ -313,7 +313,6 @@
this.itemTypeMenu.parentNode.hidden = true; this.itemTypeMenu.parentNode.hidden = true;
} }
// //
// Clear and rebuild metadata fields // Clear and rebuild metadata fields
// //
@ -602,10 +601,11 @@
} }
// Move to next or previous field if (shift-)tab was pressed // Move to next or previous field if (shift-)tab was pressed
if (this._lastTabIndex && this._tabDirection) if (this._lastTabIndex && this._lastTabIndex != -1) {
{ this._focusNextField(this._lastTabIndex);
this._focusNextField(this._dynamicFields, this._lastTabIndex, this._tabDirection == -1);
} }
this._refreshed = true;
]]> ]]>
</body> </body>
</method> </method>
@ -1046,6 +1046,10 @@
var fields = this.getCreatorFields(row); var fields = this.getCreatorFields(row);
fields.fieldMode = fieldMode; fields.fieldMode = fieldMode;
this.modifyCreator(index, fields); this.modifyCreator(index, fields);
if (this.saveOnEdit) {
// See note in transformText()
this.blurOpenField().then(() => this.item.saveTx());
}
} }
]]> ]]>
</body> </body>
@ -1144,7 +1148,8 @@
this.item.setType(itemTypeID); this.item.setType(itemTypeID);
if (this.saveOnEdit) { if (this.saveOnEdit) {
this.item.saveTx(); // See note in transformText()
this.blurOpenField().then(() => this.item.saveTx());
} }
else { else {
this.refresh(); this.refresh();
@ -1412,11 +1417,25 @@
<method name="showEditor"> <method name="showEditor">
<parameter name="elem"/> <parameter name="elem"/>
<body> <body><![CDATA[
<![CDATA[ return (async function () {
// Blur any active fields Zotero.debug(`Showing editor for ${elem.getAttribute('fieldname')}`);
if (this._dynamicFields) {
this._dynamicFields.focus(); var lastTabIndex = this._lastTabIndex = parseInt(elem.getAttribute('ztabindex'));
// If a field is open, hide it before selecting the new field, which might
// trigger a refresh
var activeField = this._dynamicFields.querySelector('textbox');
if (activeField) {
this._refreshed = false;
await this.blurOpenField();
this._lastTabIndex = lastTabIndex;
// If the box was refreshed, the clicked element is no longer valid,
// so just focus by tab index
if (this._refreshed) {
this._focusNextField(this._lastTabIndex);
return;
}
} }
// In Firefox 45, when clicking a multiline field such as Extra, the event is // In Firefox 45, when clicking a multiline field such as Extra, the event is
@ -1425,8 +1444,6 @@
elem = elem.parentNode; elem = elem.parentNode;
} }
Zotero.debug('Showing editor');
var fieldName = elem.getAttribute('fieldname'); var fieldName = elem.getAttribute('fieldname');
var tabindex = elem.getAttribute('ztabindex'); var tabindex = elem.getAttribute('ztabindex');
@ -1552,12 +1569,9 @@
}); });
t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)"); t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)");
this._tabDirection = false;
this._lastTabIndex = tabindex;
return t; return t;
]]> }.bind(this))();
</body> ]]></body>
</method> </method>
@ -1634,11 +1648,13 @@
fields[creatorField] = creator[creatorField]; fields[creatorField] = creator[creatorField];
fields[otherField] = creator[otherField]; fields[otherField] = creator[otherField];
this.ignoreBlur = true; this.modifyCreator(creatorIndex, fields);
this.modifyCreator(creatorIndex, fields) if (this.saveOnEdit) {
.then(function () { this.ignoreBlur = true;
this.ignoreBlur = false; this.item.saveTx().then(() => {
}.bind(this)); this.ignoreBlur = false;
});
}
} }
// Otherwise let the autocomplete popup handle matters // Otherwise let the autocomplete popup handle matters
@ -1662,7 +1678,6 @@
break; break;
} }
// Prevent blur on containing textbox // Prevent blur on containing textbox
// DEBUG: what happens if this isn't present? // DEBUG: what happens if this isn't present?
event.preventDefault(); event.preventDefault();
@ -1674,7 +1689,7 @@
Zotero.debug("Value hasn't changed"); Zotero.debug("Value hasn't changed");
// If + button is disabled, just focus next creator row // If + button is disabled, just focus next creator row
if (Zotero.getAncestorByTagName(target, 'row').lastChild.lastChild.disabled) { if (Zotero.getAncestorByTagName(target, 'row').lastChild.lastChild.disabled) {
this._focusNextField(this._dynamicFields, this._lastTabIndex, false); this._focusNextField(this._lastTabIndex);
} }
else { else {
var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(target, 'row')); var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(target, 'row'));
@ -1714,10 +1729,12 @@
return false; return false;
case event.DOM_VK_TAB: case event.DOM_VK_TAB:
this._tabDirection = event.shiftKey ? -1 : 1; if (event.shiftKey) {
// Blur the old manually -- not sure why this is necessary, this._focusNextField(this._lastTabIndex, true);
// but it prevents an immediate blur() on the next tag }
focused.blur(); else {
this._focusNextField(++this._lastTabIndex);
}
return false; return false;
} }
@ -1747,8 +1764,10 @@
<method name="hideEditor"> <method name="hideEditor">
<parameter name="textbox"/> <parameter name="textbox"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () { return (async function () {
Zotero.debug('Hiding editor'); Zotero.debug(`Hiding editor for ${textbox.getAttribute('fieldname')}`);
this._lastTabIndex = -1;
// Prevent autocomplete breakage in Firefox 3 // Prevent autocomplete breakage in Firefox 3
if (textbox.mController) { if (textbox.mController) {
@ -1763,6 +1782,7 @@
var elem; var elem;
var [field, creatorIndex, creatorField] = fieldName.split('-'); var [field, creatorIndex, creatorField] = fieldName.split('-');
var newVal;
// Creator fields // Creator fields
if (field == 'creator') { if (field == 'creator') {
@ -1793,7 +1813,7 @@
if (creatorsToShift > 0) { if (creatorsToShift > 0) {
//Add extra creators //Add extra creators
for (var i=0;i<nameArray.length;i++) { for (var i=0;i<nameArray.length;i++) {
yield this.modifyCreator(i + initNumCreators, otherFields, true); this.modifyCreator(i + initNumCreators, otherFields);
} }
//Shift existing creators //Shift existing creators
@ -1815,7 +1835,7 @@
otherFields.lastName=tempName; otherFields.lastName=tempName;
otherFields.firstName=''; otherFields.firstName='';
} }
yield this.modifyCreator(creatorIndex, otherFields, true); this.modifyCreator(creatorIndex, otherFields);
creatorIndex++; creatorIndex++;
} }
this._tabDirection = tabDirectionBuffer; this._tabDirection = tabDirectionBuffer;
@ -1828,11 +1848,7 @@
} }
} }
else { else {
yield this.modifyCreator(creatorIndex, otherFields); this.modifyCreator(creatorIndex, otherFields);
}
if (this.saveOnEdit) {
yield this.item.saveTx();
} }
var val = this.item.getCreator(creatorIndex); var val = this.item.getCreator(creatorIndex);
@ -1849,7 +1865,17 @@
} }
} }
var newVal = val; newVal = val;
// Reset creator mode settings here so that flex attribute gets reset
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
document.getElementById("zotero-author-guidance").show({
forEl: creatorTypeLabels[creatorTypeLabels.length-1]
});
}
} }
// Fields // Fields
@ -1914,40 +1940,23 @@
} }
} }
yield this._modifyField(fieldName, value, this.saveOnEdit); this._modifyField(fieldName, value);
newVal = this.item.getField(fieldName);
var newVal = this.item.getField(fieldName);
} }
// If box is still open (due to field not being modified and there not being // Close box
// a refresh), close it manually elem = this.createValueElement(
if (textbox && textbox.parentNode) { newVal,
elem = this.createValueElement( fieldName,
newVal, tabindex
fieldName, );
tabindex var box = textbox.parentNode;
); box.replaceChild(elem, textbox);
var box = textbox.parentNode;
box.replaceChild(elem,textbox);
}
if(field === 'creator') { if (this.saveOnEdit) {
// Reset creator mode settings here so that flex attribute gets reset await this.item.saveTx();
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if(Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
document.getElementById("zotero-author-guidance").show({
forEl: creatorTypeLabels[creatorTypeLabels.length-1]
});
}
} }
}.bind(this))();
if (this._tabDirection) {
var focusBox = this._dynamicFields;
this._focusNextField(focusBox, this._lastTabIndex, this._tabDirection == -1);
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -1978,14 +1987,8 @@
<method name="_modifyField"> <method name="_modifyField">
<parameter name="field"/> <parameter name="field"/>
<parameter name="value"/> <parameter name="value"/>
<parameter name="save"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () { this.item.setField(field, value);
this.item.setField(field, value);
if (save) {
yield this.item.saveTx();
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -2022,7 +2025,7 @@
<parameter name="label"/> <parameter name="label"/>
<parameter name="mode"/> <parameter name="mode"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () { return (async function () {
var val = this._getFieldValue(label); var val = this._getFieldValue(label);
switch (mode) { switch (mode) {
case 'title': case 'title':
@ -2040,12 +2043,14 @@
throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()"); throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()");
} }
this._setFieldValue(label, newVal); this._setFieldValue(label, newVal);
this._modifyField(label.getAttribute('fieldname'), newVal);
if (this.saveOnEdit) { if (this.saveOnEdit) {
// See note in modifyCreator() // If a field is open, blur it, which will trigger a save and cause
yield this.blurOpenField(); // the saveTx() to be a no-op
await this.blurOpenField();
await this.item.saveTx();
} }
return this._modifyField(label.getAttribute('fieldname'), newVal, this.saveOnEdit); }.bind(this))();
}, this);
]]></body> ]]></body>
</method> </method>
@ -2085,9 +2090,7 @@
<method name="modifyCreator"> <method name="modifyCreator">
<parameter name="index"/> <parameter name="index"/>
<parameter name="fields"/> <parameter name="fields"/>
<parameter name="skipSave"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () {
var libraryID = this.item.libraryID; var libraryID = this.item.libraryID;
var firstName = fields.firstName; var firstName = fields.firstName;
var lastName = fields.lastName; var lastName = fields.lastName;
@ -2099,28 +2102,12 @@
// Don't save empty creators // Don't save empty creators
if (!firstName && !lastName){ if (!firstName && !lastName){
if (!oldCreator) { if (!oldCreator) {
return; return false;
} }
this.item.removeCreator(index); return this.item.removeCreator(index);
if (this.saveOnEdit && !skipSave) {
// Make sure any open field is saved, since a blur() isn't otherwise
// triggered clicking directly to a popup menu. (If a field is open, the
// saveTx() below will become a no-op.)
yield this.blurOpenField();
return this.item.saveTx();
}
return;
} }
var changed = this.item.setCreator(index, fields); return this.item.setCreator(index, fields);
if (changed && this.saveOnEdit && !skipSave) {
// See note above
yield this.blurOpenField();
return this.item.saveTx();
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -2131,7 +2118,7 @@
<method name="swapNames"> <method name="swapNames">
<parameter name="event"/> <parameter name="event"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.Promise.try(function () { return (async function () {
var row = Zotero.getAncestorByTagName(document.popupNode, 'row'); var row = Zotero.getAncestorByTagName(document.popupNode, 'row');
var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0]; var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0];
var creatorIndex = parseInt(typeBox.getAttribute('fieldname').split('-')[1]); var creatorIndex = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
@ -2140,8 +2127,13 @@
var firstName = fields.firstName; var firstName = fields.firstName;
fields.lastName = firstName; fields.lastName = firstName;
fields.firstName = lastName; fields.firstName = lastName;
return this.modifyCreator(creatorIndex, fields); this.modifyCreator(creatorIndex, fields);
}.bind(this)); if (this.saveOnEdit) {
// See note in transformText()
await this.blurOpenField();
await this.item.saveTx();
}
}.bind(this))();
]]></body> ]]></body>
</method> </method>
@ -2168,9 +2160,8 @@
this.item.setCreator(newIndex, a); this.item.setCreator(newIndex, a);
this.item.setCreator(index, b); this.item.setCreator(index, b);
if (this.saveOnEdit) { if (this.saveOnEdit) {
// See note in modifyCreator() // See note in transformText()
yield this.blurOpenField(); yield this.blurOpenField();
return this.item.saveTx(); return this.item.saveTx();
} }
}, this); }, this);
@ -2200,7 +2191,7 @@
<method name="focusFirstField"> <method name="focusFirstField">
<body> <body>
<![CDATA[ <![CDATA[
this._focusNextField(this._dynamicFields, 0, false); this._focusNextField(1);
]]> ]]>
</body> </body>
</method> </method>
@ -2215,11 +2206,11 @@
completes, so it doesn't know where it's supposed to go next.) completes, so it doesn't know where it's supposed to go next.)
--> -->
<method name="_focusNextField"> <method name="_focusNextField">
<parameter name="box"/>
<parameter name="tabindex"/> <parameter name="tabindex"/>
<parameter name="back"/> <parameter name="back"/>
<body> <body>
<![CDATA[ <![CDATA[
var box = this._dynamicFields;
tabindex = parseInt(tabindex); tabindex = parseInt(tabindex);
// Get all fields with ztabindex attributes // Get all fields with ztabindex attributes
@ -2241,9 +2232,9 @@
} }
} }
else { else {
Zotero.debug('Looking for next tabindex after ' + tabindex, 4); Zotero.debug('Looking for tabindex ' + tabindex, 4);
for (var pos = 0; pos < tabbableFields.length; pos++) { for (var pos = 0; pos < tabbableFields.length; pos++) {
if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) > tabindex) { if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) >= tabindex) {
next = tabbableFields[pos]; next = tabbableFields[pos];
break; break;
} }
@ -2278,12 +2269,13 @@
<method name="blurOpenField"> <method name="blurOpenField">
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () { return (async function () {
var textboxes = document.getAnonymousNodes(this)[0].getElementsByTagName('textbox'); var activeField = this._dynamicFields.querySelector('textbox');
if (textboxes && textboxes.length) { if (!activeField) {
yield this.blurHandler(textboxes[0]); return false;
} }
}, this); return this.blurHandler(activeField);
}.bind(this))();
]]></body> ]]></body>
</method> </method>
@ -2385,7 +2377,11 @@
}; };
itemBox._updateAutoCompleteParams(row, changedParams); itemBox._updateAutoCompleteParams(row, changedParams);
itemBox.modifyCreator(index, fields);"/> itemBox.modifyCreator(index, fields);
if (itemBox.saveOnEdit) {
itemBox.item.saveTx();
}
"/>
<menupopup id="zotero-field-transform-menu"> <menupopup id="zotero-field-transform-menu">
<menu label="&zotero.item.textTransform;"> <menu label="&zotero.item.textTransform;">
<menupopup> <menupopup>