diff --git a/frappe/public/js/frappe/form/controls/table.js b/frappe/public/js/frappe/form/controls/table.js
index 0a93db4363..45cd144bc2 100644
--- a/frappe/public/js/frappe/form/controls/table.js
+++ b/frappe/public/js/frappe/form/controls/table.js
@@ -16,6 +16,11 @@ frappe.ui.form.ControlTable = class ControlTable extends frappe.ui.form.Control
this.frm.grids[this.frm.grids.length] = this;
}
+ this.$wrapper.on("keydown", (e) => {
+ if (e.which == 9) {
+ this.grid.grid_rows[this.grid.grid_rows.length - 1].toggle_editable_row(true);
+ }
+ });
this.$wrapper.on("paste", ":text", (e) => {
const table_field = this.df.fieldname;
const grid = this.grid;
diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js
index 40e44f642f..61ed9ae58f 100644
--- a/frappe/public/js/frappe/form/grid.js
+++ b/frappe/public/js/frappe/form/grid.js
@@ -382,6 +382,7 @@ export default class Grid {
frm: this.frm,
grid: this,
configure_columns: true,
+ header_row: true,
});
this.header_search = new GridRow({
@@ -939,7 +940,7 @@ export default class Grid {
setTimeout(() => {
this.grid_rows[idx].row
- .find('input[type="Text"],textarea,select')
+ .find('input[type="checkbox"],input[type="Text"],textarea,select')
.filter(":visible:first")
.focus();
}, 100);
diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js
index 2344d90c91..073d76b97e 100644
--- a/frappe/public/js/frappe/form/grid_row.js
+++ b/frappe/public/js/frappe/form/grid_row.js
@@ -255,6 +255,10 @@ export default class GridRow {
? this.doc.idx
: __("No.", null, "Title of the 'row number' column");
+ if (this.header_row) {
+ this.row_check_html = $(this.row_check_html).attr("tabindex", -1).get(0).outerHTML;
+ }
+
this.row_check = $(
`
${this.row_check_html}
@@ -339,7 +343,7 @@ export default class GridRow {
if (this.doc && !this.grid.df.in_place_edit) {
// remove row
if (!this.open_form_button) {
- this.open_form_button = $('
').appendTo(this.row);
+ this.open_form_button = $('
').appendTo(this.row);
if (!this.configure_columns) {
const edit_msg = __("Edit", "", "Edit grid row");
@@ -353,6 +357,14 @@ export default class GridRow {
me.toggle_view();
return false;
});
+ $(this.open_form_button)
+ .parent()
+ .on("keydown", function (ev) {
+ if (ev.key == "Enter") {
+ me.toggle_view();
+ return false;
+ }
+ });
this.open_form_button.tooltip({ delay: { show: 600, hide: 100 } });
}
@@ -361,6 +373,10 @@ export default class GridRow {
// narrow
this.open_form_button.css({ "margin-right": "-2px" });
}
+ // focus open form button after escape
+ $(document).on("escape", function () {
+ me.open_form_button.parent().focus();
+ });
}
}
}
@@ -1191,7 +1207,6 @@ export default class GridRow {
this.on_grid_fields_dict[df.fieldname] = field;
this.on_grid_fields.push(field);
}
-
set_arrow_keys(field) {
var me = this;
let ignore_fieldtypes = ["Text", "Small Text", "Code", "Text Editor", "HTML Editor"];
@@ -1242,28 +1257,7 @@ export default class GridRow {
}
// TAB
- if (e.which === TAB && !e.shiftKey) {
- var last_column = me.wrapper.find(":input:enabled:last").get(0);
- var is_last_column = $(this).attr("data-last-input") || last_column === this;
-
- if (is_last_column) {
- // last row
- if (me.doc.idx === values.length) {
- setTimeout(function () {
- me.grid.add_new_row(null, null, true);
- me.grid.grid_rows[
- me.grid.grid_rows.length - 1
- ].toggle_editable_row();
- me.grid.set_focus_on_row();
- }, 100);
- } else {
- // last column before last row
- me.grid.grid_rows[me.doc.idx].toggle_editable_row();
- me.grid.set_focus_on_row(me.doc.idx);
- return false;
- }
- }
- } else if (e.which === UP_ARROW) {
+ if (e.which === UP_ARROW) {
if (me.doc.idx > 1) {
var prev = me.grid.grid_rows[me.doc.idx - 2];
if (move_up_down(prev)) {
@@ -1360,7 +1354,6 @@ export default class GridRow {
this.hide_form();
}
callback && callback();
-
return this;
}
show_form() {
@@ -1373,6 +1366,7 @@ export default class GridRow {
row: this,
});
}
+ this.grid_form.wrapper.css("display", "block");
this.grid_form.render();
this.row.toggle(false);
// this.form_panel.toggle(true);
@@ -1417,7 +1411,11 @@ export default class GridRow {
}
this.refresh();
if (cur_frm) cur_frm.cur_grid = null;
+ if (this.grid_form) {
+ this.grid_form.wrapper.css("display", "none");
+ }
this.wrapper.removeClass("grid-row-open");
+ this.open_form_button.parent().focus();
}
has_prev() {
return this.doc.idx > 1;
diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js
index 6caa979497..fc50b9bfcd 100644
--- a/frappe/public/js/frappe/form/layout.js
+++ b/frappe/public/js/frappe/form/layout.js
@@ -36,7 +36,6 @@ frappe.ui.form.Layout = class Layout {
this.setup_tabbed_layout();
}
- this.setup_tab_events();
this.frm && this.setup_tooltip_events();
this.render();
}
diff --git a/frappe/public/js/frappe/form/section.js b/frappe/public/js/frappe/form/section.js
index 5d14abf1e3..62da79d0bc 100644
--- a/frappe/public/js/frappe/form/section.js
+++ b/frappe/public/js/frappe/form/section.js
@@ -78,8 +78,14 @@ export default class Section {
this.collapse_link = this.head.on("click", () => {
this.collapse();
});
+ const me = this;
+ this.collapse_link.enterKey(function () {
+ me.collapse();
+ });
this.set_icon();
this.indicator.show();
+ this.head.attr("tabindex", 0);
+ this.indicator.attr("tabindex", 0);
}
}
diff --git a/frappe/public/js/frappe/form/tab.js b/frappe/public/js/frappe/form/tab.js
index 6ba6123c1e..c6778c2448 100644
--- a/frappe/public/js/frappe/form/tab.js
+++ b/frappe/public/js/frappe/form/tab.js
@@ -16,14 +16,14 @@ export default class Tab {
const id = `${frappe.scrub(this.doctype, "-")}-${this.df.fieldname}`;
this.tab_link = $(`
-
${__(this.label, null, this.doctype)}
-
+
`).appendTo(this.tab_link_container);
diff --git a/frappe/public/js/frappe/ui/keyboard.js b/frappe/public/js/frappe/ui/keyboard.js
index 61e89e8281..020999f768 100644
--- a/frappe/public/js/frappe/ui/keyboard.js
+++ b/frappe/public/js/frappe/ui/keyboard.js
@@ -331,6 +331,7 @@ frappe.ui.keyCode = {
function handle_escape_key() {
close_grid_and_dialog();
document.activeElement?.blur();
+ $(document).trigger("escape");
}
function close_grid_and_dialog() {
@@ -365,3 +366,14 @@ frappe.ui.keys.add_shortcut({
},
description: __("Open console"),
});
+
+$.fn.enterKey = function (fnc) {
+ return this.each(function () {
+ $(this).keypress(function (ev) {
+ var keycode = ev.keyCode ? ev.keyCode : ev.which;
+ if (keycode == "13") {
+ fnc.call(this, ev);
+ }
+ });
+ });
+};
diff --git a/frappe/public/scss/common/buttons.scss b/frappe/public/scss/common/buttons.scss
index 96c567ad3b..a2aaf5192b 100644
--- a/frappe/public/scss/common/buttons.scss
+++ b/frappe/public/scss/common/buttons.scss
@@ -82,6 +82,9 @@
background-color: var(--btn-default-hover-bg);
color: var(--text-color);
}
+ &:focus {
+ box-shadow: var(--focus-default) !important;
+ }
}
.btn.btn-default {
diff --git a/frappe/public/scss/common/grid.scss b/frappe/public/scss/common/grid.scss
index 75f797036b..379f600837 100644
--- a/frappe/public/scss/common/grid.scss
+++ b/frappe/public/scss/common/grid.scss
@@ -136,6 +136,9 @@
.row-index {
left: 31px;
}
+ button.col:focus {
+ outline: 1px solid #c9c9c9;
+ }
}
.grid-empty,
diff --git a/frappe/public/scss/desk/form.scss b/frappe/public/scss/desk/form.scss
index 81ee202d99..15f6092f2f 100644
--- a/frappe/public/scss/desk/form.scss
+++ b/frappe/public/scss/desk/form.scss
@@ -50,6 +50,12 @@
margin-left: 10px;
position: relative;
padding: 0px;
+ &:focus {
+ outline: 1px solid #c9c9c9;
+ }
+ }
+ &:focus {
+ outline: 1px solid #c9c9c9;
}
}
@@ -500,13 +506,18 @@
color: var(--text-muted);
padding: 10px 0;
margin: 0 var(--margin-md);
-
+ background-color: var(--card-bg);
+ border: 0;
&.active {
font-weight: 600;
border-bottom: 1px solid var(--text-color);
color: var(--text-color);
padding-bottom: 9px;
}
+ &:focus {
+ outline: none;
+ font-weight: 600;
+ }
}
}
}