inline edit confirm unsaved switch to another field

This commit is contained in:
Yuri Kuznetsov
2025-06-19 20:37:19 +03:00
parent dd7017fabe
commit b0579c9acf
4 changed files with 86 additions and 50 deletions
@@ -390,6 +390,7 @@
"massRemoveResult": "{count} records have been removed",
"massRemoveResultSingle": "{count} record has been removed",
"noRecordsRemoved": "No records were removed",
"changesLossConfirmation": "Unsaved changes will be lost. Are you sure?",
"clickToRefresh": "Click to refresh",
"writeYourCommentHere": "Write your comment here",
"writeMessageToUser": "Write a message to {user}",
+32
View File
@@ -35,6 +35,14 @@ import {Events} from 'bullbone';
*/
class ViewRecordHelper {
/**
* @private
* @type {{
* isChanged: boolean,
* }}
*/
state
/**
* @param {Object.<string, *>} [defaultFieldStates] Default field states.
* @param {Object.<string, *>} [defaultPanelStates] Default panel states.
@@ -61,6 +69,10 @@ class ViewRecordHelper {
this.hiddenPanels = {};
/** @private */
this.fieldOptionListMap = {};
this.state = {
isChanged: false,
};
}
/**
@@ -209,6 +221,26 @@ class ViewRecordHelper {
hasFieldOptionList(field) {
return (field in this.fieldOptionListMap);
}
/**
* Is changed.
*
* @return {boolean}
* @since 9.2.0
*/
isChanged() {
return this.state.isChanged;
}
/**
* Set is changed.
*
* @param {boolean} isChanged
* @since 9.2.0
*/
setIsChanged(isChanged) {
this.state.isChanged = isChanged;
}
}
Object.assign(ViewRecordHelper.prototype, Events);
+51 -50
View File
@@ -1411,64 +1411,65 @@ class BaseFieldView extends View {
*
* @return {Promise}
*/
inlineEdit() {
async inlineEdit() {
if (this.recordHelper && this.recordHelper.isChanged()) {
await this.confirm(this.translate('changesLossConfirmation', 'messages'));
}
this.trigger('edit', this);
this.initialAttributes = this.model.getClonedAttributes();
this._isInlineEditMode = true;
const promise = this.setEditMode()
.then(() => this.reRender(true))
.then(() => this.addInlineEditLinks())
.then(() => {
this.$el.on('keydown.inline-edit', e => {
const key = Espo.Utils.getKeyFromKeyEvent(e);
if (key === 'Control+Enter') {
e.stopPropagation();
if (document.activeElement instanceof HTMLInputElement) {
// Fields may need to fetch data first.
document.activeElement.dispatchEvent(new Event('change', {bubbles: true}));
}
this.fetchToModel();
this.inlineEditSave();
setTimeout(() => {
this.get$cell().focus();
}, 100);
return;
}
if (key === 'Escape') {
e.stopPropagation();
this.inlineEditClose()
.then(() => {
this.get$cell().focus();
});
return;
}
if (key === 'Control+KeyS') {
e.preventDefault();
e.stopPropagation();
this.fetchToModel();
this.inlineEditSave({bypassClose: true});
}
});
setTimeout(() => this.focusOnInlineEdit(), 10);
});
this.trigger('inline-edit-on');
return promise;
await this.setEditMode();
await this.reRender(true);
await this.addInlineEditLinks();
this.$el.on('keydown.inline-edit', e => {
const key = Espo.Utils.getKeyFromKeyEvent(e);
if (key === 'Control+Enter') {
e.stopPropagation();
if (document.activeElement instanceof HTMLInputElement) {
// Fields may need to fetch data first.
document.activeElement.dispatchEvent(new Event('change', {bubbles: true}));
}
this.fetchToModel();
this.inlineEditSave();
setTimeout(() => {
this.get$cell().focus();
}, 100);
return;
}
if (key === 'Escape') {
e.stopPropagation();
this.inlineEditClose()
.then(() => {
this.get$cell().focus();
});
return;
}
if (key === 'Control+KeyS') {
e.preventDefault();
e.stopPropagation();
this.fetchToModel();
this.inlineEditSave({bypassClose: true});
}
});
setTimeout(() => this.focusOnInlineEdit(), 10);
}
/**
+2
View File
@@ -2304,6 +2304,7 @@ class DetailRecordView extends BaseRecordView {
setIsChanged() {
this.isChanged = true;
this.recordHelper.setIsChanged(true);
if (this.confirmLeaveDisabled) {
return;
@@ -2314,6 +2315,7 @@ class DetailRecordView extends BaseRecordView {
setIsNotChanged() {
this.isChanged = false;
this.recordHelper.setIsChanged(false);
if (this.confirmLeaveDisabled) {
return;