diff --git a/client/res/templates/record/detail.tpl b/client/res/templates/record/detail.tpl index 4deee03107..ee1d12f515 100644 --- a/client/res/templates/record/detail.tpl +++ b/client/res/templates/record/detail.tpl @@ -1,4 +1,4 @@ -
+
{{#unless buttonsDisabled}}
diff --git a/client/res/templates/record/edit.tpl b/client/res/templates/record/edit.tpl index a4fbd157f5..f436a68975 100644 --- a/client/res/templates/record/edit.tpl +++ b/client/res/templates/record/edit.tpl @@ -1,4 +1,4 @@ -
+
{{#unless buttonsDisabled}}
diff --git a/client/src/views/modal.js b/client/src/views/modal.js index f9950ad316..359c813653 100644 --- a/client/src/views/modal.js +++ b/client/src/views/modal.js @@ -375,10 +375,10 @@ define('views/modal', ['view'], function (Dep) { setupFinal: function () { if (this.shortcutKeys) { this.events['keydown.modal-base'] = e => { - let key = e.key.toLowerCase(); + let key = e.code; if (e.ctrlKey) { - key = 'ctrl+' + key; + key = 'Control+' + key; } if (typeof this.shortcutKeys[key] === 'function') { diff --git a/client/src/views/modals/detail.js b/client/src/views/modals/detail.js index 3c7898cbcf..f3852056ac 100644 --- a/client/src/views/modals/detail.js +++ b/client/src/views/modals/detail.js @@ -67,7 +67,7 @@ define('views/modals/detail', ['views/modal', 'helpers/action-item-setup'], func duplicateAction: false, shortcutKeys: { - 'e': function (e) { + 'KeyE': function (e) { if (this.editDisabled) { return; } @@ -82,7 +82,7 @@ define('views/modals/detail', ['views/modal', 'helpers/action-item-setup'], func this.actionEdit() .then(view => { view.$el - .find('.form-control:not([disabled])') + .find('.middle-tabs > button.active, .form-control, .form-control:not([disabled])') .first() .focus(); }); diff --git a/client/src/views/modals/edit.js b/client/src/views/modals/edit.js index e4727736b5..492404af9b 100644 --- a/client/src/views/modals/edit.js +++ b/client/src/views/modals/edit.js @@ -58,6 +58,60 @@ define('views/modals/edit', ['views/modal'], function (Dep) { bottomDisabled: false, + shortcutKeys: { + 'Control+Enter': function (e) { + if (this.saveDisabled) { + return; + } + + if (this.buttonList.findIndex(item => item.name === 'save' && !item.hidden) === -1) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + this.actionSave(); + }, + 'Control+KeyS': function (e) { + if (this.saveDisabled) { + return; + } + + if (this.buttonList.findIndex(item => item.name === 'save' && !item.hidden) === -1) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + this.actionSaveAndContinueEditing(); + }, + 'Escape': function (e) { + if (this.saveDisabled) { + return; + } + + e.stopPropagation(); + e.preventDefault(); + + let focusedFieldView = this.getRecordView().getFocusedFieldView(); + + if (focusedFieldView) { + this.model.set(focusedFieldView.fetch()); + } + + if (this.getRecordView().isChanged) { + this.confirm(this.translate('confirmLeaveOutMessage', 'messages')) + .then(() => this.actionClose()); + + return; + } + + this.actionClose(); + }, + }, + setup: function () { this.buttonList = []; @@ -71,45 +125,6 @@ define('views/modals/edit', ['views/modal'], function (Dep) { label: 'Save', style: 'primary', }); - - this.events['keydown'] = (e) => { - if (e.key === 'Enter' && e.ctrlKey) { - e.stopPropagation(); - - this.actionSave(); - - return; - } - - if ((e.key === 's' || e.key === 'S') && e.ctrlKey) { - e.preventDefault(); - e.stopPropagation(); - - this.actionSaveAndContinueEditing(); - - return; - } - - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - - let focusedFieldView = this.getRecordView().getFocusedFieldView(); - - if (focusedFieldView) { - this.model.set(focusedFieldView.fetch()); - } - - if (this.getRecordView().isChanged) { - this.confirm(this.translate('confirmLeaveOutMessage', 'messages')) - .then(() => this.actionClose()); - - return; - } - - this.actionClose(); - } - } } this.fullFormDisabled = this.options.fullFormDisabled || this.fullFormDisabled; diff --git a/client/src/views/record/detail.js b/client/src/views/record/detail.js index f70efe807c..b29e78560d 100644 --- a/client/src/views/record/detail.js +++ b/client/src/views/record/detail.js @@ -417,13 +417,16 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { * @type {?Object.} */ shortcutKeys: { - 'ctrl+enter': function (e) { + 'Control+Enter': function (e) { this.handleShortcutKeyCtrlEnter(e); }, - 'ctrl+s': function (e) { + 'Control+KeyS': function (e) { this.handleShortcutKeyCtrlS(e); }, - 'escape': function (e) { + 'KeyE': function (e) { + this.handleShortcutKeyE(e); + }, + 'Escape': function (e) { this.handleShortcutKeyEscape(e); }, }, @@ -496,6 +499,7 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { if (!this.lastSaveCancelReason || this.lastSaveCancelReason === 'notModified') { this.setDetailMode(); + this.focusOnFirstDiv(); $(window).scrollTop(0); } }, @@ -503,9 +507,14 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { actionCancelEdit: function () { this.cancelEdit(); + this.focusOnFirstDiv(); $(window).scrollTop(0); }, + focusOnFirstDiv: function () { + this.$el.find('> div').focus(); + }, + /** * A `save-and-continue-editing` action. */ @@ -1891,10 +1900,10 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { if (this.shortcutKeys && this.options.shortcutKeysEnabled) { // @todo Move to util (the same for the `views/modal`.). this.events['keydown.record-detail'] = e => { - let key = e.key.toLowerCase(); + let key = e.code; if (e.ctrlKey) { - key = 'ctrl+' + key; + key = 'Control+' + key; } if (typeof this.shortcutKeys[key] === 'function') { @@ -1916,6 +1925,8 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { this[methodName](); }; + + this.once('after:render', () => this.focusOnFirstDiv()); } }, @@ -3392,91 +3403,6 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { .find('.dropdown-edit-item-list-button'); }, - /** - * @protected - * @param {JQueryKeyEventObject} e - */ - handleKeydownEvent: function (e) { - if (!this.options.shortcutKeysEnabled) { - return; - } - - /*if (e.key === 'Enter' && e.ctrlKey) { - if (this.inlineEditModeIsOn || this.buttonsDisabled || !this.shortcutEnterAction) { - return; - } - - if (this.mode !== this.MODE_EDIT) { - return; - } - - if ( - this.type === this.TYPE_DETAIL && - this.buttonEditList.findIndex(item => item.name === this.shortcutEnterAction) === -1 - ) { - return; - } - - if ( - this.type === this.TYPE_EDIT && - this.buttonList.findIndex(item => item.name === this.shortcutEnterAction) === -1 - ) { - return; - } - - e.stopPropagation(); - - let methodName = 'action' + Espo.Utils.upperCaseFirst(this.shortcutEnterAction); - - this[methodName](); - - return; - }*/ - - /*if ((e.key === 's' || e.key === 'S') && e.ctrlKey) { - if (this.inlineEditModeIsOn || this.buttonsDisabled) { - return; - } - - if (this.mode !== this.MODE_EDIT) { - return; - } - - if (!this.saveAndContinueEditingAction) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - this.actionSaveAndContinueEditing(); - - return; - }*/ - - if (e.key === 'Escape') { - if (this.inlineEditModeIsOn || this.buttonsDisabled) { - return; - } - - if (this.type === this.TYPE_DETAIL && this.mode === this.MODE_EDIT) { - e.stopPropagation(); - - // Fetching a currently edited form element. - this.model.set(this.fetch()); - - if (this.isChanged) { - this.confirm(this.translate('confirmLeaveOutMessage', 'messages')) - .then(() => this.actionCancelEdit()); - - return; - } - - this.actionCancelEdit(); - } - } - }, - /** * @protected * @param {JQueryKeyEventObject} e @@ -3494,14 +3420,14 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { if ( this.type === this.TYPE_DETAIL && - this.buttonEditList.findIndex(item => item.name === action) === -1 + this.buttonEditList.findIndex(item => item.name === action && !item.hidden) === -1 ) { return; } if ( this.type === this.TYPE_EDIT && - this.buttonList.findIndex(item => item.name === action) === -1 + this.buttonList.findIndex(item => item.name === action && !item.hidden) === -1 ) { return; } @@ -3537,6 +3463,38 @@ function (Dep, ViewRecordHelper, ActionItemSetup) { this.actionSaveAndContinueEditing(); }, + /** + * @protected + * @param {JQueryKeyEventObject} e + */ + handleShortcutKeyE: function (e) { + if (this.inlineEditModeIsOn || this.buttonsDisabled) { + return; + } + + if (this.type !== this.TYPE_DETAIL || this.mode !== this.MODE_DETAIL) { + return; + } + + if (this.buttonList.findIndex(item => item.name === 'edit' && !item.hidden) === -1) { + return; + } + + $(e.currentTarget) + + e.preventDefault(); + e.stopPropagation(); + + this.actionEdit(); + + if (!this.editModeDisabled) { + setTimeout(() => { + this.$el.find('.middle-tabs > button.active, .form-control:not([disabled])') + .first().focus(); + }, 200); + } + }, + /** * @protected * @param {JQueryKeyEventObject} e diff --git a/frontend/less/espo/elements/record.less b/frontend/less/espo/elements/record.less index 38ce19d7ee..250aa826ad 100644 --- a/frontend/less/espo/elements/record.less +++ b/frontend/less/espo/elements/record.less @@ -17,6 +17,10 @@ } } +.record > div[tabindex="-1"]:focus-visible { + outline: none; +} + .modal-body { .record .record-grid { grid-column-gap: @padding-base-horizontal;