keyboard navigation

This commit is contained in:
Prssanna Desai 2019-04-29 22:59:01 +05:30
parent dc93ff8be3
commit a9c4cfd10f
5 changed files with 386 additions and 13 deletions

View file

@ -173,10 +173,11 @@ frappe.views.BaseList = class BaseList {
}
this.menu_items.map(item => {
// console.log('item', item)
if (item.condition && item.condition() === false) {
return;
}
const $item = this.page.add_menu_item(item.label, item.action, item.standard);
const $item = this.page.add_menu_item(item.label, item.action, item.standard, item.shortcut);
if (item.class) {
$item && $item.addClass(item.class);
}

View file

@ -202,7 +202,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
} else {
this.make_new_doc();
}
}, 'octicon octicon-plus');
}, 'octicon octicon-plus','ctrl+b');
} else {
this.page.clear_primary_action();
}
@ -226,6 +226,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.setup_events();
this.settings.onload && this.settings.onload(this);
this.show_restricted_list_indicator_if_applicable();
}
setup_freeze_area() {
@ -753,8 +754,93 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.setup_like();
this.setup_realtime_updates();
this.setup_action_handler();
this.setup_keyboard_nav();
}
setup_keyboard_nav() {
let selected, next, new_class, is_nav;
let index = 0;
$(document).on('keydown',null, (e)=> {
if ($('[role="listbox"]').is(":visible")) {
console.log('true');
is_nav = true;
}
else {
is_nav = false;
let list = this.$result.find('.list-row-container');
list = list.add($('.list-paging-area').find('.btn'));
if(e.which === 40) {
if(selected) {
new_class = list.eq(index).is('button') ? 'btn-selected': 'selected';
// console.log('presseddd')
selected.removeClass(new_class);
index++;
next = list.eq(index);
if(next.length > 0) {
new_class = list.eq(index).is('button') ? 'btn-selected': 'selected';
selected = next.addClass(new_class);
} else {
new_class = list.eq(0).is('button') ? 'btn-selected': 'selected';
selected = list.eq(0).addClass(new_class);
index = 0;
}
} else {
new_class = list.eq(0).is('button') ? 'btn-selected': 'selected';
selected = list.eq(0).addClass(new_class);
}
} else if(e.which === 38) {
if(selected) {
new_class = list.eq(index).is('button') ? 'btn-selected': 'selected';
selected.removeClass(new_class);
if(index>0) index--;
next = list.eq(index);
if(next.length > 0) {
new_class = list.eq(index).is('button') ? 'btn-selected': 'selected';
selected = next.addClass(new_class);
} else {
new_class = list.eq(list.length-1).is('button') ? 'btn-selected': 'selected';
selected = list.eq(list.length - 1).addClass(new_class);
index = list.length - 1;
}
} else {
new_class = list.eq(list.length-1).is('button') ? 'btn-selected': 'selected';
selected = list.eq(list.length - 1).addClass(new_class);
}
} else if(e.which === 13) {
console.log('is nav', is_nav)
if(!is_nav) {
if(selected.is('button') ) {
selected.click();
selected.removeClass('btn-selected');
index = 0;
}
else selected.find('.list-row').click();
index = 0;
}
}
}
});
}
// down_navigation(list) {
// let selected_element;
// if(selected_element) {
// console.log('presseddd')
// selected_element.removeClass('selected');
// next = selected_element.next();
// console.log('next', next)
// if(next.length > 0){
// selected_element = next.addClass('selected');
// } else {
// selected_element = list.eq(0).addClass('selected');
// }
// } else{
// selected_element = list.eq(0).addClass('selected');
// console.log('liSelected', liSelected);
// }
// }
setup_filterable() {
// filterable events
this.$result.on('click', '.filterable', e => {
@ -1030,6 +1116,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (frappe.model.can_import(doctype)) {
items.push({
label: __('Import'),
// shortcut: 'Ctrl + I',
action: () => frappe.set_route('List', 'Data Import', {
reference_doctype: doctype
}),
@ -1040,6 +1127,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (frappe.model.can_set_user_permissions(doctype)) {
items.push({
label: __('User Permissions'),
// shortcut: 'Ctrl + U',
action: () => frappe.set_route('List', 'User Permission', {
allow: doctype
}),
@ -1050,6 +1138,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (frappe.user_roles.includes('System Manager')) {
items.push({
label: __('Role Permissions Manager'),
// shortcut: 'Ctrl + M',
action: () => frappe.set_route('permission-manager', {
doctype
}),
@ -1068,6 +1157,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
});
}
},
// shortcut: 'Alt + C',
standard: true
});
}
@ -1075,12 +1165,14 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
items.push({
label: __('Toggle Sidebar'),
action: () => this.toggle_side_bar(),
// shortcut: 'Ctrl + T',
standard: true
});
items.push({
label: __('Share URL'),
action: () => this.share_url(),
// shortcut: 'Alt + S',
standard: true
});
@ -1088,6 +1180,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
// edit doctype
items.push({
label: __('Edit DocType'),
// shortcut: 'Ctrl + E',
action: () => frappe.set_route('Form', 'DocType', doctype),
standard: true
});
@ -1096,6 +1189,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (frappe.user.has_role('System Manager')) {
items.push({
label: __('Settings'),
// shortcut: 'Ctrl + Shift + S',
action: () => this.show_list_settings(),
standard: true
});

View file

@ -42,6 +42,193 @@ frappe.ui.Page = Class.extend({
make: function() {
this.wrapper = $(this.parent);
this.add_main_section();
this.setup_nav();
},
keyboard_shortcut(shortcut, element) {
let shortcut_key = shortcut.replace(/ /g,'');
// console.log('true', shortcut);
let namespace = 'keyup.'+ shortcut.charAt(shortcut.length-1);
$(document).on(namespace,null, shortcut_key, (e)=> {
console.log('heree');
// e.stopImmediatePropagation();
// console.log($li, shortcut);
element.click()
})
},
setup_nav() {
console.log('MENU', this.menu);
let menu_el = $('.menu-btn-group');
console.log('menu', menu_el);
let buttons = $('.page-actions').find('button'), menu_list, actions_list, el_list;
let timer = false;
$(document).on('keydown', null, 'alt', ()=> {
menu_list = $('.menu-btn-group ul li a').filter(':visible');
actions_list = $('.actions-btn-group ul li a').filter(':visible');
console.log('menu list', menu_list);
clearTimeout(timer);
timer=false;
if(!timer) timer = setTimeout(()=> {
if(menu_list.length) {
el_list = this.underline_alt_elements(menu_list);
} else if(actions_list.length) {
el_list = this.underline_alt_elements(actions_list);
}
else el_list = this.underline_alt_elements(buttons);
},1000);
});
$(document).on('keyup',null, (e)=> {
let index = 0, liSelected, next;
clearTimeout(timer);
timer = false;
console.log('el list', el_list);
if(el_list) {
this.remove_alt_elements(el_list);
}
menu_list = $('.menu-btn-group ul li a').filter(':visible');
console.log('prssanna', menu_list);
if(menu_list.length) {
console.log('yasss');
let li = menu_list;
console.log('list', li);
if(e.which === 40) {
console.log('down');
if(liSelected) {
liSelected.removeClass('selected');
next = liSelected.next();
if(next.length > 0) {
liSelected = next.addClass('selected');
} else {
liSelected = li.eq(0).addClass('selected');
}
} else {
liSelected = li.eq(0).addClass('selected');
}
} else if(e.which === 38) {
console.log('up');
if(liSelected) {
liSelected.removeClass('selected');
next = liSelected.prev();
if(next.length > 0) {
liSelected = next.addClass('selected');
} else {
liSelected = li.last().addClass('selected');
}
} else {
liSelected = li.last().addClass('selected');
}
}
}
})
// $(document).on('keydown',null,(e)=> {
// console.log('hereee');
// let menu = $('.menu-btn-group.open')
// console.log('menu', menu);
// if(menu.length) {
// console.log('yasss');
// let li = menu_list;
// console.log('list', li);
// if(e.which === 40) {
// if(liSelected) {
// liSelected.removeClass('selected');
// next = liSelected.next();
// if(next.length > 0) {
// liSelected = next.addClass('selected');
// } else {
// liSelected = li.eq(0).addClass('selected');
// }
// } else {
// liSelected = li.eq(0).addClass('selected');
// }
// } else if(e.which === 38) {
// if(liSelected) {
// liSelected.removeClass('selected');
// next = liSelected.prev();
// if(next.length > 0) {
// liSelected = next.addClass('selected');
// } else {
// liSelected = li.last().addClass('selected');
// }
// } else {
// liSelected = li.last().addClass('selected');
// }
// }
// }
// })
},
underline_alt_elements($list) {
console.log('list', $list);
let char_list = [];
let el_list = [];
$.each($list,(i,v)=> {
let $el = $(v);
if($el.is(':visible')) {
let el_obj={};
el_obj.$el = $el;
let text = $el.text().trim();
let i = 0;
let char = text.charAt(0);
console.log(char_list);
while(char_list.includes(char.toUpperCase())) {
i++;
char = text.charAt(i);
}
char_list.push(char.toUpperCase());
console.log(char);
let new_text = this.underline_character(text,i);
let new_html = $el.html().replace(text, new_text);
let shortcut = 'alt+'+text.charAt(i);
el_obj.shortcut = shortcut;
el_list.push(el_obj);
// char_list.push(text.charAt(0));
$el.html(new_html);
}
});
console.log('element list', el_list);
el_list.forEach((element)=> {
console.log('here');
let namespace = 'keyup.'+ element.shortcut.charAt(element.shortcut.length-1);
$(document).off(namespace);
if(element.$el.is(':visible')) {
console.log('visible');
this.keyboard_shortcut(element.shortcut, element.$el);
}
})
return el_list;
},
remove_alt_elements($list) {
$.each($list,(i,v)=> {
// let $el = $(v);
if(v.$el.is(':visible')) {
// let text = $el.text().trim();
// let new_text = this.remove_underline(text);
// console.log('new text', new_text);
let new_html = this.remove_underline(v.$el.html());
console.log(new_html);
v.$el.html(new_html);
let namespace = 'keyup.'+ v.shortcut.charAt(v.shortcut.length-1);
$(document).off(namespace);
}
});
},
underline_character(str, n) {
console.log('str', str);
let new_str;
new_str = str.slice(0,n) + "<u>"+str.charAt(n)+"</u>" + str.slice(n+1);
console.log('new str', new_str);
return new_str;
},
remove_underline(str) {
let new_str = str.replace('<u>','').replace('</u>','');
return new_str;
},
get_empty_state: function(title, message, primary_action) {
@ -166,14 +353,17 @@ frappe.ui.Page = Class.extend({
}
},
set_primary_action: function(label, click, icon, working_label) {
set_primary_action: function(label, click, icon, shortcut, working_label) {
this.set_action(this.btn_primary, {
label: label,
click: click,
icon: icon,
working_label: working_label
working_label: working_label,
shortcut: shortcut
});
if(shortcut) {
this.keyboard_shortcut(shortcut,this.btn_primary);
}
return this.btn_primary;
},
@ -212,8 +402,9 @@ frappe.ui.Page = Class.extend({
//--- Menu --//
add_menu_item: function(label, click, standard) {
return this.add_dropdown_item(label, click, standard, this.menu);
add_menu_item: function(label, click, standard, shortcut) {
// console.log('shortcut', shortcut)
return this.add_dropdown_item(label, click, standard, this.menu, shortcut);
},
clear_menu: function() {
@ -271,15 +462,14 @@ frappe.ui.Page = Class.extend({
* @param {object} parent - DOM object representing the parent of the drop down item lists
* @param {Boolean} show_parent - Whether to show the dropdown button if dropdown item is added
*/
add_dropdown_item: function(label, click, standard, parent, show_parent=true) {
add_dropdown_item: function(label, click, standard, parent, shortcut, show_parent=true) {
let item_selector = 'li > a.grey-link';
if(show_parent) {
parent.parent().removeClass("hide");
}
var $li = $('<li><a class="grey-link">'+ label +'</a><li>'),
$link = $li.find("a").on("click", click);
if(shortcut) var $li = $('<li><a class="grey-link">'+ label + '<span class="text-muted"> ('+shortcut+')</span></a><li>');
else var $li = $('<li><a class="grey-link">'+ label +'</a><li>');
var $link = $li.find("a").on("click", click);
if (this.is_in_group_button_dropdown(parent, item_selector, label)) return;
if(standard) {
@ -291,7 +481,10 @@ frappe.ui.Page = Class.extend({
}
$li.addClass("user-action").insertBefore(this.divider);
}
// console.log('parent', parent)
if(shortcut) {
this.keyboard_shortcut(shortcut, $li.find('a'));
}
return $link;
},

View file

@ -82,5 +82,82 @@ frappe.views.FormFactory = class FormFactory extends frappe.views.Factory {
load(dt, dn) {
frappe.container.change_to("Form/" + dt);
frappe.views.formview[dt].frm.refresh(dn);
// console.log('this', this);
this.setup_modal();
}
setup_modal() {
$(document).on('keydown',null, 'ctrl+shift+m', (e)=> {
// e.stopImmediatePropagation();
if($(this.page).is(':hidden')) {
console.log('yesss');
return;
}
console.log('this', this.page);
console.log('modal');
let fields = this.get_section_fields();
let field_labels = [];
fields.forEach((f)=> {field_labels.push(f.label)});
console.log('labels', field_labels);
const d = new frappe.ui.Dialog({
title: __('Go to Field'),
fields: [
{
fieldtype: 'Select',
fieldname: 'go_to_field',
label: 'Select Field',
options: field_labels,
reqd: 1
}
]
});
d.set_primary_action("Go", () => {
let field = d.get_values().go_to_field;
console.log('field', field);
let element = fields.find( f=> f.label == field).section_body;
console.log('element', element);
if(element.hasClass('hide')) {
console.log('true');
element.removeClass('hide');
}
frappe.utils.scroll_to(element);
// this.group_by = this.meta.fields.find( f => f.label == d.get_values().group_by).fieldname;
// this.aggregate_on = this.meta.fields.find( f => f.label == d.get_values().aggregate_on).fieldname;
// let sql_aggregate_function = {
// Count: "count",
// Sum: "sum",
// Average: "avg",
// }
// this.aggregate_function = sql_aggregate_function[d.get_values().aggregate_function];
d.hide();
});
d.show();
})
}
get_section_fields() {
let fields = [];
// this.page.frm.fields.forEach((f) => {
// if(f.df.fieldtype!=='Section Break' && !f.df.hidden && f.disp_status!=='None' && f.df.label) {
// fields.push(f.df.label);
// }
// });
// console.log('fields', fields);
// return fields;
this.page.frm.layout.sections.forEach((section)=> {
section.fields_list.forEach((f)=> {
if(f.df.fieldtype!=='Section Break' && !f.df.hidden && f.disp_status!=='None' && f.df.label) {
let field = {};
field.label = f.df.label;
field.section_body = section.body;
field.section = section.df.label;
fields.push(field);
}
})
})
console.log('fields', fields);
return fields;
}
}

View file

@ -90,6 +90,14 @@
}
}
.selected {
background-color: @panel-bg;
}
.btn-selected {
background: #cfdce5;
}
.list-row-head {
background-color: @panel-bg;
border-bottom: 1px solid @border-color !important;