keyboard navigation
This commit is contained in:
parent
dc93ff8be3
commit
a9c4cfd10f
5 changed files with 386 additions and 13 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue