mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
keyboard nav in listview
This commit is contained in:
parent
db181ddea0
commit
61294fd77f
@ -21,9 +21,9 @@ module.exports = class FormModal extends Modal {
|
||||
}
|
||||
|
||||
makeForm() {
|
||||
this.form = new (view.getFormClass(this.doctype))({
|
||||
doctype: this.doctype,
|
||||
parent: this.getBody(),
|
||||
this.form = new (view.getFormClass(this.doctype))({
|
||||
doctype: this.doctype,
|
||||
parent: this.getBody(),
|
||||
container: this,
|
||||
actions: ['submit']
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ module.exports = class FormPage extends Page {
|
||||
let meta = frappe.getMeta(doctype)
|
||||
super({title: `Edit ${meta.name}`});
|
||||
this.meta = meta;
|
||||
this.doctype = doctype;
|
||||
|
||||
this.form = new (view.getFormClass(doctype))({
|
||||
doctype: doctype,
|
||||
@ -32,6 +33,7 @@ module.exports = class FormPage extends Page {
|
||||
});
|
||||
|
||||
this.form.on('delete', async (params) => {
|
||||
this.hide();
|
||||
await frappe.router.setRoute('list', this.form.doctype);
|
||||
});
|
||||
}
|
||||
@ -39,21 +41,15 @@ module.exports = class FormPage extends Page {
|
||||
async showDoc(doctype, name) {
|
||||
try {
|
||||
await this.form.setDoc(doctype, name);
|
||||
this.setActiveListRow(doctype, name);
|
||||
this.setActiveListRow(name);
|
||||
} catch (e) {
|
||||
this.renderError(e.status_code, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
setActiveListRow(doctype, name) {
|
||||
let activeListRow = document.querySelector('.list-page .list-body .list-row.active');
|
||||
if (activeListRow) {
|
||||
activeListRow.classList.remove('active');
|
||||
}
|
||||
|
||||
let myListRow = document.querySelector(`.list-body[data-doctype="${doctype}"] .list-row[data-name="${name}"]`);
|
||||
if (myListRow) {
|
||||
myListRow.classList.add('active');
|
||||
setActiveListRow(name) {
|
||||
if (frappe.desk.pages.lists[this.doctype]) {
|
||||
frappe.desk.pages.lists[this.doctype].list.setActiveListRow(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,9 @@ module.exports = class Desk {
|
||||
let doc = await frappe.getNewDoc(params.doctype);
|
||||
// unset the name, its local
|
||||
await frappe.router.setRoute('edit', doc.doctype, doc.name);
|
||||
|
||||
// focus on new page
|
||||
frappe.desk.body.activePage.body.querySelector('input').focus();
|
||||
});
|
||||
|
||||
frappe.router.on('change', () => {
|
||||
|
@ -15,7 +15,7 @@ module.exports = class ListPage extends Page {
|
||||
hasRoute: hasRoute
|
||||
});
|
||||
|
||||
this.list = new (view.geListClass(doctype))({
|
||||
this.list = new (view.getListClass(doctype))({
|
||||
doctype: doctype,
|
||||
parent: this.body,
|
||||
page: this
|
||||
|
@ -6,7 +6,7 @@ module.exports = {
|
||||
getFormClass(doctype) {
|
||||
return (frappe.views['Form'] && frappe.views['Form'][doctype]) || BaseForm;
|
||||
},
|
||||
geListClass(doctype) {
|
||||
getListClass(doctype) {
|
||||
return (frappe.views['List'] && frappe.views['List'][doctype]) || BaseList;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
const frappe = require('frappejs');
|
||||
const keyboard = require('frappejs/client/ui/keyboard');
|
||||
|
||||
module.exports = class BaseList {
|
||||
constructor({doctype, parent, fields, page}) {
|
||||
@ -22,6 +23,17 @@ module.exports = class BaseList {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
makeBody() {
|
||||
if (!this.body) {
|
||||
this.makeToolbar();
|
||||
this.parent.classList.add('list-page');
|
||||
this.body = frappe.ui.add('div', 'list-body', this.parent);
|
||||
this.body.setAttribute('data-doctype', this.doctype);
|
||||
this.makeMoreBtn();
|
||||
this.bindKeys();
|
||||
}
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
return await this.run();
|
||||
}
|
||||
@ -44,6 +56,8 @@ module.exports = class BaseList {
|
||||
|
||||
this.clearEmptyRows();
|
||||
this.updateMore(data.length > this.pageLength);
|
||||
this.selectDefaultRow();
|
||||
this.setActiveListRow();
|
||||
}
|
||||
|
||||
async getData() {
|
||||
@ -73,13 +87,63 @@ module.exports = class BaseList {
|
||||
return filters;
|
||||
}
|
||||
|
||||
makeBody() {
|
||||
if (!this.body) {
|
||||
this.makeToolbar();
|
||||
this.parent.classList.add('list-page');
|
||||
this.body = frappe.ui.add('div', 'list-body', this.parent);
|
||||
this.body.setAttribute('data-doctype', this.doctype);
|
||||
this.makeMoreBtn();
|
||||
renderRow(i, data) {
|
||||
let row = this.getRow(i);
|
||||
row.innerHTML = this.getRowBodyHTML(data);
|
||||
row.docName = data.name;
|
||||
row.setAttribute('data-name', data.name);
|
||||
row.style.display = 'flex';
|
||||
|
||||
// make element focusable
|
||||
row.setAttribute('tabindex', -1);
|
||||
}
|
||||
|
||||
getRowBodyHTML(data) {
|
||||
return `<div class="col-1">
|
||||
<input class="checkbox" type="checkbox" data-name="${data.name}">
|
||||
</div>` + this.getRowHTML(data);
|
||||
}
|
||||
|
||||
getRowHTML(data) {
|
||||
return `<div class="col-11">${data.name}</div>`;
|
||||
}
|
||||
|
||||
getRow(i) {
|
||||
if (!this.rows[i]) {
|
||||
this.rows[i] = frappe.ui.add('div', 'list-row row no-gutters', this.body);
|
||||
|
||||
// open on click
|
||||
let me = this;
|
||||
this.rows[i].addEventListener('click', async function(e) {
|
||||
if (!e.target.tagName !== 'input') {
|
||||
await me.showItem(this.docName);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.rows[i];
|
||||
}
|
||||
|
||||
async showItem(name) {
|
||||
await frappe.router.setRoute('edit', this.doctype, name);
|
||||
}
|
||||
|
||||
getCheckedRowNames() {
|
||||
return [...this.body.querySelectorAll('.checkbox:checked')].map(check => check.getAttribute('data-name'));
|
||||
}
|
||||
|
||||
clearEmptyRows() {
|
||||
if (this.rows.length > this.data.length) {
|
||||
for (let i=this.data.length; i < this.rows.length; i++) {
|
||||
let row = this.getRow(i);
|
||||
row.innerHTML = '';
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectDefaultRow() {
|
||||
if (!frappe.desk.body.activePage && this.rows.length) {
|
||||
this.showItem(this.rows[0].docName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,6 +187,27 @@ module.exports = class BaseList {
|
||||
});
|
||||
}
|
||||
|
||||
bindKeys() {
|
||||
keyboard.bindKey(this.body, 'up', async (e) => await this.move('up'));
|
||||
keyboard.bindKey(this.body, 'down', async (e) => await this.move('down'))
|
||||
|
||||
keyboard.bindKey(this.body, 'right', () => {
|
||||
if (frappe.desk.body.activePage) {
|
||||
frappe.desk.body.activePage.body.querySelector('input').focus();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async move(direction) {
|
||||
let elementRef = direction === 'up' ? 'previousSibling' : 'nextSibling';
|
||||
if (document.activeElement && document.activeElement.classList.contains('list-row')) {
|
||||
let next = document.activeElement[elementRef];
|
||||
if (next && next.docName) {
|
||||
await this.showItem(next.docName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeMoreBtn() {
|
||||
this.btnMore = frappe.ui.add('button', 'btn btn-secondary hide', this.parent);
|
||||
this.btnMore.textContent = 'More';
|
||||
@ -131,53 +216,6 @@ module.exports = class BaseList {
|
||||
})
|
||||
}
|
||||
|
||||
renderRow(i, data) {
|
||||
let row = this.getRow(i);
|
||||
row.innerHTML = this.getRowBodyHTML(data);
|
||||
row.setAttribute('data-name', data.name);
|
||||
row.style.display = 'flex';
|
||||
}
|
||||
|
||||
getRowBodyHTML(data) {
|
||||
return `<div class="col-1">
|
||||
<input class="checkbox" type="checkbox" data-name="${data.name}">
|
||||
</div>` + this.getRowHTML(data);
|
||||
}
|
||||
|
||||
getRowHTML(data) {
|
||||
return `<div class="col-11">${data.name}</div>`;
|
||||
}
|
||||
|
||||
getRow(i) {
|
||||
if (!this.rows[i]) {
|
||||
this.rows[i] = frappe.ui.add('div', 'list-row row no-gutters', this.body);
|
||||
|
||||
// open on click
|
||||
let doctype = this.doctype;
|
||||
this.rows[i].addEventListener('click', function(e) {
|
||||
if (!e.target.tagName !== 'input') {
|
||||
let name = this.getAttribute('data-name');
|
||||
frappe.router.setRoute('edit', doctype, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.rows[i];
|
||||
}
|
||||
|
||||
getCheckedRowNames() {
|
||||
return [...this.body.querySelectorAll('.checkbox:checked')].map(check => check.getAttribute('data-name'));
|
||||
}
|
||||
|
||||
clearEmptyRows() {
|
||||
if (this.rows.length > this.data.length) {
|
||||
for (let i=this.data.length; i < this.rows.length; i++) {
|
||||
let row = this.getRow(i);
|
||||
row.innerHTML = '';
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateMore(show) {
|
||||
if (show) {
|
||||
this.btnMore.classList.remove('hide');
|
||||
@ -186,4 +224,26 @@ module.exports = class BaseList {
|
||||
}
|
||||
}
|
||||
|
||||
setActiveListRow(name) {
|
||||
let activeListRow = this.body.querySelector('.list-row.active');
|
||||
if (activeListRow) {
|
||||
activeListRow.classList.remove('active');
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
// get name from active page
|
||||
name = frappe.desk.body.activePage && frappe.desk.body.activePage.form.doc
|
||||
&& frappe.desk.body.activePage.form.doc.name;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
let myListRow = this.body.querySelector(`.list-row[data-name="${name}"]`);
|
||||
if (myListRow) {
|
||||
myListRow.classList.add('active');
|
||||
myListRow.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
@ -36,6 +36,7 @@ module.exports = class Page extends Observable {
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.parent.activePage = null;
|
||||
this.wrapper.classList.add('hide');
|
||||
this.trigger('hide');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user