2
0
mirror of https://github.com/frappe/books.git synced 2025-02-02 12:08:27 +00:00
This commit is contained in:
Rushabh Mehta 2018-02-15 15:23:28 +05:30
parent a10f75b195
commit 1b42e46462
13 changed files with 202 additions and 137 deletions

View File

@ -33,11 +33,6 @@ module.exports = class FormModal extends Modal {
async showWith(doctype, name) {
await this.form.setDoc(doctype, name);
if (this.form.doc._notInserted) {
this.setTitle(frappe._('New {0}', doctype));
} else {
this.setTitle(`${doctype} ${name}`);
}
this.show();
this.$modal.find('input:first').focus();
}

View File

@ -36,8 +36,21 @@ module.exports = class FormPage extends Page {
async showDoc(doctype, name) {
try {
await this.form.setDoc(doctype, name);
this.setActiveListRow(doctype, 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');
}
}
}

View File

@ -14,9 +14,9 @@ module.exports = class Desk {
frappe.router.listen();
let body = document.querySelector('body');
this.navbar = new Navbar();
this.container = frappe.ui.add('div', 'container-fluid', body);
this.containerRow = frappe.ui.add('div', 'row', this.container)
//this.navbar = new Navbar();
this.container = frappe.ui.add('div', '', body);
this.containerRow = frappe.ui.add('div', 'row no-gutters', this.container)
this.makeColumns(columns);
this.pages = {

View File

@ -8,27 +8,29 @@ module.exports = class DeskMenu {
}
make() {
this.listGroup = frappe.ui.add('div', 'list-group list-group-flush', this.parent);
this.listGroup = frappe.ui.add('div', 'list-body', this.parent);
}
addItem(label, action) {
let item = frappe.ui.add('a', 'list-group-item list-group-item-action', this.listGroup);
let item = frappe.ui.add('div', 'list-row', this.listGroup);
item.textContent = label;
if (typeof action === 'string') {
item.href = action;
this.routeItems[action] = item;
} else {
item.addEventListener('click', () => {
action();
this.setActive(item);
});
}
item.addEventListener('click', async () => {
if (typeof action === 'string') {
await frappe.router.setRoute(action);
} else {
action();
}
this.setActive(item);
});
}
setActive() {
if (this.routeItems[window.location.hash]) {
let item = this.routeItems[window.location.hash];
let className = 'list-group-item-secondary';
let className = 'active';
let activeItem = this.listGroup.querySelector('.' + className);
if (activeItem) {

View File

@ -16,7 +16,7 @@ html {
.desk-body {
border-left: 1px solid $gray-300;
min-height: calc(100vh - 50px);
min-height: 100vh;
}
.desk-center {
@ -24,17 +24,11 @@ html {
}
.desk-menu {
margin-right: -1px;
.list-group-item {
padding: $spacer-2 $spacer-3;
border: none;
}
.list-group-flush {
margin-left: -$spacer-3;
margin-right: -$spacer-3;
}
}
.hide {
@ -44,28 +38,34 @@ html {
.page {
max-width: $page-width;
padding-bottom: $spacer-4;
}
.page-head {
padding-top: $spacer-3;
padding-bottom: $spacer-3;
.page-head {
padding: $spacer-2 $spacer-3;
background-color: $gray-800;
color: $gray-100;
.btn {
margin-right: $spacer-2;
.page-title {
display: inline-block;
margin-top: 0.3rem;
}
.btn {
margin-left: $spacer-2;
}
}
.page-error {
text-align: center;
padding: 200px 0px;
}
}
.page-error {
text-align: center;
padding: 200px 0px;
}
.form-body {
padding: $spacer-3;
max-width: $page-width;
.form-toolbar {
height: $spacer-4;
margin-bottom: $spacer-3;
.form-control.font-weight-bold {
background-color: lightyellow;
}
.alert {
@ -73,14 +73,16 @@ html {
}
}
.list-search {
padding: $spacer-3;
}
.list-body {
margin-left: -15px;
margin-right: -15px;
.list-row {
padding: $spacer-2 15px;
border-bottom: 1px solid $gray-200;
cursor: pointer;
.checkbox {
margin-right: $spacer-2;
}
@ -89,6 +91,14 @@ html {
color: $gray-800;
}
}
.list-row:hover {
background-color: $gray-100;
}
.list-row.active {
background-color: $gray-200;
}
}
@ -98,37 +108,40 @@ html {
.awesomplete {
display: block;
> ul > li {
padding: .75rem .375rem;
}
> ul > li:hover {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"] {
background: $gray-300;
color: $body-color;
}
> ul > li[aria-selected="true"]:hover {
background: $gray-300;
color: $body-color;
}
li[aria-selected="true"] mark, li[aria-selected="false"] mark {
background: inherit;
color: inherit;
padding: 0px;
}
}
.awesomplete > ul > li {
padding: .75rem .375rem;
}
.awesomplete > ul > li:hover {
background: $gray-300;
color: $body-color;
}
.awesomplete > ul > li[aria-selected="true"] {
background: $gray-300;
color: $body-color;
}
.awesomplete > ul > li[aria-selected="true"]:hover {
background: $gray-300;
color: $body-color;
}
mark {
padding: none;
background: inherit;
}
.awesomplete li[aria-selected="true"] mark, .awesomplete li[aria-selected="false"] mark {
background: inherit;
color: inherit;
padding: 0px;
}
.table-wrapper {
margin-top: $spacer-4;
@ -139,24 +152,30 @@ mark {
margin-top: $spacer-2;
}
.data-table thead td {
border-bottom: 0px !important;
background-color: $gray-200 !important;
}
.data-table-col .edit-cell {
padding: 0px !important;
input, textarea {
border-radius: none;
margin: none;
padding: $spacer-1;
.data-table {
.body-scrollable {
border-bottom: 0px !important;
}
.awesomplete > ul {
position: fixed;
left: auto;
width: auto;
min-width: 120px;
thead td {
border-bottom: 0px !important;
background-color: $gray-200 !important;
}
.data-table-col .edit-cell {
padding: 0px !important;
input, textarea {
border-radius: none;
margin: none;
padding: $spacer-1;
}
.awesomplete > ul {
position: fixed;
left: auto;
width: auto;
min-width: 120px;
}
}
}

View File

@ -3,7 +3,7 @@ const bootstrap = require('bootstrap');
const $ = require('jquery');
class Dropdown {
constructor({parent, label, items = [], right}) {
constructor({parent, label, items = [], right, cssClass='btn-secondary'}) {
Object.assign(this, arguments[0]);
Dropdown.instances += 1;
this.id = 'dropdownMenuButton-' + Dropdown.instances;
@ -20,7 +20,7 @@ class Dropdown {
make() {
this.$dropdown = $(`<div class="dropdown ${this.right ? 'float-right' : ''}">
<button class="btn btn-outline-secondary dropdown-toggle"
<button class="btn ${this.cssClass} dropdown-toggle"
type="button" id="${this.id}" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">${this.label}
</button>

View File

@ -18,6 +18,7 @@ class BaseControl {
if (this.setup) {
this.setup();
}
this.make();
}
bind(doc) {
@ -26,7 +27,6 @@ class BaseControl {
}
refresh() {
this.make();
this.setDocValue();
}
@ -37,20 +37,18 @@ class BaseControl {
}
make() {
if (!this.input) {
if (!this.onlyInput) {
this.makeFormGroup();
this.makeLabel();
}
this.makeInput();
this.setInputName();
this.setRequiredAttribute();
this.setDisabled();
if (!this.onlyInput) {
this.makeDescription();
}
this.addChangeHandler();
if (!this.onlyInput) {
this.makeFormGroup();
this.makeLabel();
}
this.makeInput();
this.setInputName();
this.setRequiredAttribute();
this.setDisabled();
if (!this.onlyInput) {
this.makeDescription();
}
this.addChangeHandler();
}
makeFormGroup() {

View File

@ -1,4 +1,4 @@
const control_classes = {
const controlClasses = {
Data: require('./data'),
Date: require('./date'),
Currency: require('./currency'),
@ -13,12 +13,11 @@ const control_classes = {
module.exports = {
getControlClass(fieldtype) {
return control_classes[fieldtype];
return controlClasses[fieldtype];
},
makeControl({field, form, parent}) {
const control_class = this.getControlClass(field.fieldtype);
let control = new control_class({field:field, form:form, parent:parent});
control.make();
const controlClass = this.getControlClass(field.fieldtype);
let control = new controlClass({field:field, form:form, parent:parent});
return control;
}
}

View File

@ -6,6 +6,10 @@ class LinkControl extends BaseControl {
make() {
super.make();
this.input.setAttribute('type', 'text');
this.setupAwesomplete();
}
setupAwesomplete() {
this.awesomplete = new Awesomplete(this.input, {
autoFirst: true,
minChars: 0,
@ -39,6 +43,7 @@ class LinkControl extends BaseControl {
})
}
});
}
async getList(query) {

View File

@ -6,20 +6,21 @@ const Modal = require('frappejs/client/ui/modal');
class TableControl extends BaseControl {
make() {
if (!this.datatable) {
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.getInputParent());
this.wrapper.innerHTML =
`<div class="datatable-wrapper"></div>
<div class="table-toolbar">
<button type="button" class="btn btn-sm btn-outline-secondary btn-add">
${frappe._("Add")}</button>
<button type="button" class="btn btn-sm btn-outline-secondary btn-remove">
${frappe._("Remove")}</button>
</div>`;
this.makeWrapper();
this.makeDatatable();
this.setupToolbar();
}
this.makeDatatable();
this.setupToolbar();
}
makeWrapper() {
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.getInputParent());
this.wrapper.innerHTML =
`<div class="datatable-wrapper"></div>
<div class="table-toolbar">
<button type="button" class="btn btn-sm btn-outline-secondary btn-add">
${frappe._("Add")}</button>
<button type="button" class="btn btn-sm btn-outline-secondary btn-remove">
${frappe._("Remove")}</button>
</div>`;
}
makeDatatable() {

View File

@ -71,7 +71,7 @@ module.exports = class BaseForm extends Observable {
if (this.meta.settings && this.actions.includes('settings')) {
let menu = this.container.getDropdown(frappe._('Menu'));
menu.addItem(frappe._('Settings...'), () => {
frappe.router.setRoute('edit', frappe.slug(this.meta.settings), this.meta.settings);
frappe.desk.showFormModal(frappe.slug(this.meta.settings), this.meta.settings);
});
}
@ -95,6 +95,18 @@ module.exports = class BaseForm extends Observable {
// flag so that name is cleared only once
await this.doc.set('name', '');
}
this.setTitle();
}
setTitle() {
const doctype = this.doc.meta.name;
if (this.doc.meta.isSingle || !this.doc.meta.showTitle) {
this.container.setTitle(doctype);
} else if (this.doc._notInserted) {
this.container.setTitle(frappe._('New {0}', doctype));
} else {
this.container.setTitle(`${doctype} ${this.doc.name}`);
}
}
async bindEvents(doc) {

View File

@ -67,17 +67,19 @@ module.exports = class BaseList {
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();
}
}
makeToolbar() {
this.makeSearch();
this.btnNew = this.page.addButton(frappe._('New'), 'btn-outline-primary', async () => {
this.btnNew = this.page.addButton(frappe._('New'), 'btn-primary', async () => {
await frappe.router.setRoute('new', frappe.slug(this.doctype));
})
this.btnDelete = this.page.addButton(frappe._('Delete'), 'btn-outline-secondary hide', async () => {
this.btnDelete = this.page.addButton(frappe._('Delete'), 'btn-secondary hide', async () => {
await frappe.db.deleteMany(this.doctype, this.getCheckedRowNames());
await this.refresh();
});
@ -91,14 +93,10 @@ module.exports = class BaseList {
makeSearch() {
this.toolbar = frappe.ui.add('div', 'list-toolbar', this.parent);
this.toolbar.innerHTML = `
<div class="row">
<div class="col-12" style="max-width: 300px">
<div class="input-group list-search mb-2">
<input class="form-control" type="text" placeholder="Search...">
<div class="input-group-append">
<button class="btn btn-outline-secondary btn-search">Search</button>
</div>
</div>
<div class="input-group list-search">
<input class="form-control" type="text" placeholder="Search...">
<div class="input-group-append">
<button class="btn btn-outline-secondary btn-search">Search</button>
</div>
</div>
`;
@ -127,20 +125,32 @@ module.exports = class BaseList {
renderRow(i, data) {
let row = this.getRow(i);
row.innerHTML = this.getRowBodyHTML(data);
row.style.display = 'block';
row.setAttribute('data-name', data.name);
row.style.display = 'flex';
}
getRowBodyHTML(data) {
return `<input class="checkbox" type="checkbox" data-name="${data.name}"> ` + this.getRowHTML(data);
return `<div class="col-1">
<input class="checkbox" type="checkbox" data-name="${data.name}">
</div>` + this.getRowHTML(data);
}
getRowHTML(data) {
return `<a href="#edit/${this.doctype}/${data.name}">${data.name}</a>`;
return `<div class="col-11">${data.name}</div>`;
}
getRow(i) {
if (!this.rows[i]) {
this.rows[i] = frappe.ui.add('div', 'list-row', this.body);
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];
}

View File

@ -11,14 +11,28 @@ module.exports = class Page extends Observable {
}
this.make();
this.dropdowns = {};
if(this.title) {
this.setTitle(this.title);
}
}
make() {
this.wrapper = frappe.ui.add('div', 'page hide', this.parent);
this.wrapper.innerHTML = `<div class="page-head hide"></div>
this.wrapper.innerHTML = `<div class="page-head clearfix hide">
<span class="page-title"></span>
</div>
<div class="page-body"></div>`
this.head = this.wrapper.querySelector('.page-head');
this.body = this.wrapper.querySelector('.page-body');
this.titleElement = this.head.querySelector('.page-title');
}
setTitle(title) {
this.titleElement.textContent = title;
if (this.hasRoute) {
document.title = title;
}
}
hide() {
@ -28,7 +42,7 @@ module.exports = class Page extends Observable {
addButton(label, className, action) {
this.head.classList.remove('hide');
this.button = frappe.ui.add('button', 'btn ' + this.getClassName(className), this.head);
this.button = frappe.ui.add('button', 'btn btn-sm float-right ' + this.getClassName(className), this.head);
this.button.innerHTML = label;
this.button.addEventListener('click', action);
return this.button;
@ -36,7 +50,8 @@ module.exports = class Page extends Observable {
getDropdown(label) {
if (!this.dropdowns[label]) {
this.dropdowns[label] = new Dropdown({parent: this.head, label: label, right: true});
this.dropdowns[label] = new Dropdown({parent: this.head, label: label,
right: true, cssClass: 'btn-secondary btn-sm'});
}
return this.dropdowns[label];
}
@ -55,10 +70,6 @@ module.exports = class Page extends Observable {
this.parent.activePage = this;
if (this.hasRoute) {
document.title = this.title;
}
await this.trigger('show', params);
}