mirror of
https://github.com/frappe/books.git
synced 2025-04-02 00:01:51 +00:00
commit
f7db1206b0
@ -395,17 +395,18 @@ module.exports = class Database extends Observable {
|
|||||||
for (let key in filters) {
|
for (let key in filters) {
|
||||||
const value = filters[key];
|
const value = filters[key];
|
||||||
if (value instanceof Array) {
|
if (value instanceof Array) {
|
||||||
|
const condition = value[0];
|
||||||
// if its like, we should add the wildcard "%" if the user has not added
|
// if its like, we should add the wildcard "%" if the user has not added
|
||||||
if (value[0].toLowerCase()==='includes') {
|
if (condition.toLowerCase()==='includes') {
|
||||||
value[0] = 'like';
|
condition = 'like';
|
||||||
}
|
}
|
||||||
if (['like', 'includes'].includes(value[0].toLowerCase()) && !value[1].includes('%')) {
|
if (['like', 'includes'].includes(condition.toLowerCase()) && !value[1].includes('%')) {
|
||||||
value[1] = `%${value[1]}%`;
|
value[1] = `%${value[1]}%`;
|
||||||
}
|
}
|
||||||
conditions.push(`${key} ${value[0]} ?`);
|
conditions.push(`ifnull(${key}, '') ${condition} ?`);
|
||||||
values.push(value[1]);
|
values.push(value[1]);
|
||||||
} else {
|
} else {
|
||||||
conditions.push(`${key} = ?`);
|
conditions.push(`ifnull(${key}, '') = ?`);
|
||||||
values.push(value);
|
values.push(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ const Page = require('frappejs/client/view/page');
|
|||||||
const views = {};
|
const views = {};
|
||||||
views.Form = require('./formpage');
|
views.Form = require('./formpage');
|
||||||
views.List = require('./listpage');
|
views.List = require('./listpage');
|
||||||
|
views.Tree = require('./treepage');
|
||||||
views.Print = require('./printpage');
|
views.Print = require('./printpage');
|
||||||
views.FormModal = require('./formmodal');
|
views.FormModal = require('./formmodal');
|
||||||
views.Table = require('./tablepage');
|
views.Table = require('./tablepage');
|
||||||
@ -75,6 +76,10 @@ module.exports = class Desk {
|
|||||||
await this.showViewPage('List', params.doctype);
|
await this.showViewPage('List', params.doctype);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.router.add('tree/:doctype', async (params) => {
|
||||||
|
await this.showViewPage('Tree', params.doctype);
|
||||||
|
});
|
||||||
|
|
||||||
frappe.router.add('table/:doctype', async (params) => {
|
frappe.router.add('table/:doctype', async (params) => {
|
||||||
await this.showViewPage('Table', params.doctype, params);
|
await this.showViewPage('Table', params.doctype, params);
|
||||||
})
|
})
|
||||||
|
31
client/desk/treepage.js
Normal file
31
client/desk/treepage.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
const Page = require('frappejs/client/view/page');
|
||||||
|
const view = require('frappejs/client/view');
|
||||||
|
|
||||||
|
module.exports = class TreePage extends Page {
|
||||||
|
constructor(name) {
|
||||||
|
const hasRoute = true;
|
||||||
|
|
||||||
|
super({
|
||||||
|
title: frappe._("Tree"),
|
||||||
|
parent: hasRoute ? frappe.desk.body : frappe.desk.center,
|
||||||
|
hasRoute: hasRoute
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fullPage = true;
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
this.tree = new (view.getTreeClass(name))({
|
||||||
|
doctype: name,
|
||||||
|
parent: this.body,
|
||||||
|
page: this
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async show(params) {
|
||||||
|
super.show();
|
||||||
|
this.setTitle(this.name===this.tree.meta.name ? (this.tree.meta.label || this.tree.meta.name) : this.name);
|
||||||
|
await this.tree.refresh();
|
||||||
|
}
|
||||||
|
}
|
@ -6,16 +6,9 @@
|
|||||||
@import "node_modules/frappe-datatable/dist/frappe-datatable";
|
@import "node_modules/frappe-datatable/dist/frappe-datatable";
|
||||||
// @import "node_modules/octicons/build/build.css";
|
// @import "node_modules/octicons/build/build.css";
|
||||||
|
|
||||||
$spacer-1: 0.25rem;
|
@import "./variables.scss";
|
||||||
$spacer-2: 0.5rem;
|
|
||||||
$spacer-3: 1rem;
|
|
||||||
$spacer-4: 2rem;
|
|
||||||
$spacer-5: 3rem;
|
|
||||||
|
|
||||||
@import "./indicators.scss";
|
@import "./indicators.scss";
|
||||||
|
|
||||||
$page-width: 500px;
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
@ -269,117 +262,7 @@ mark {
|
|||||||
margin: $spacer-3 0px;
|
margin: $spacer-3 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tree {
|
@import "./tree.scss";
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree li {
|
|
||||||
list-style: none;
|
|
||||||
margin: 2px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tree-children {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link .node-parent {
|
|
||||||
color: $gray-600;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link .node-leaf {
|
|
||||||
color: $gray-400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link .node-parent, .tree-link .node-leaf {
|
|
||||||
margin-right: 5px;
|
|
||||||
margin-left: 2px;
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link.active {
|
|
||||||
svg {
|
|
||||||
color: $blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $gray-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-hover {
|
|
||||||
background-color: $gray-200;
|
|
||||||
min-height: 20px;
|
|
||||||
border: 1px solid $gray-600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-node-toolbar {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0px 5px;
|
|
||||||
margin-left: 15px;
|
|
||||||
margin-bottom: -4px;
|
|
||||||
margin-top: -8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @media (max-width: @screen-xs) {
|
|
||||||
// ul.tree-children {
|
|
||||||
// padding-left: 10px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// decoration
|
|
||||||
// .tree, .tree-node {
|
|
||||||
.tree.with-skeleton, .tree.with-skeleton .tree-node {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&.opened::before, &:last-child::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 12px;
|
|
||||||
left: 7px;
|
|
||||||
height: calc(100% - 23px);
|
|
||||||
width: 1px;
|
|
||||||
background: $gray-400;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child::after {
|
|
||||||
top: 11px;
|
|
||||||
left: -13px;
|
|
||||||
height: calc(100% - 15px);
|
|
||||||
width: 3px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.opened > .tree-children > .tree-node > .tree-link::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
width: 18px;
|
|
||||||
height: 1px;
|
|
||||||
top: 10px;
|
|
||||||
left: -12px;
|
|
||||||
z-index: -1;
|
|
||||||
background: $gray-400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree.with-skeleton.opened::before {
|
|
||||||
left: 22px;
|
|
||||||
top: 33px;
|
|
||||||
height: calc(100% - 67px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-link.active ~ .balance-area {
|
|
||||||
color: $gray-600 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// just for accounting
|
// just for accounting
|
||||||
|
|
||||||
|
111
client/style/tree.scss
Normal file
111
client/style/tree.scss
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
@import "./variables.scss";
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
padding: $spacer-3 $spacer-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree-children {
|
||||||
|
padding-left: $spacer-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link:hover {
|
||||||
|
background-color: $gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-parent {
|
||||||
|
color: $gray-600;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-leaf {
|
||||||
|
color: $gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-parent, .tree-link .node-leaf {
|
||||||
|
padding: $spacer-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link.active {
|
||||||
|
a {
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-hover {
|
||||||
|
background-color: $gray-200;
|
||||||
|
min-height: 20px;
|
||||||
|
border: 1px solid $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-toolbar {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0px 5px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: -4px;
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @media (max-width: @screen-xs) {
|
||||||
|
// ul.tree-children {
|
||||||
|
// padding-left: 10px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// decoration
|
||||||
|
// .tree, .tree-node {
|
||||||
|
.tree.with-skeleton, .tree.with-skeleton .tree-node {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.opened::before, &:last-child::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 7px;
|
||||||
|
height: calc(100% - 23px);
|
||||||
|
width: 1px;
|
||||||
|
background: $gray-400;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child::after {
|
||||||
|
top: 11px;
|
||||||
|
left: -13px;
|
||||||
|
height: calc(100% - 15px);
|
||||||
|
width: 3px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.opened > .tree-children > .tree-node > .tree-link::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 18px;
|
||||||
|
height: 1px;
|
||||||
|
top: 10px;
|
||||||
|
left: -12px;
|
||||||
|
z-index: -1;
|
||||||
|
background: $gray-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree.with-skeleton.opened::before {
|
||||||
|
left: 22px;
|
||||||
|
top: 33px;
|
||||||
|
height: calc(100% - 67px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link.active ~ .balance-area {
|
||||||
|
color: $gray-600 !important;
|
||||||
|
}
|
6
client/style/variables.scss
Normal file
6
client/style/variables.scss
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
$spacer-1: 0.25rem;
|
||||||
|
$spacer-2: 0.5rem;
|
||||||
|
$spacer-3: 1rem;
|
||||||
|
$spacer-4: 2rem;
|
||||||
|
$spacer-5: 3rem;
|
||||||
|
$page-width: 500px;
|
@ -1,6 +1,6 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const DataTable = require('frappe-datatable');
|
const DataTable = require('frappe-datatable');
|
||||||
const controls = require('frappejs/client/view/controls');
|
|
||||||
const Modal = require('frappejs/client/ui/modal');
|
const Modal = require('frappejs/client/ui/modal');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
|
||||||
@ -50,6 +50,7 @@ module.exports = class ModelTable {
|
|||||||
|
|
||||||
getControl(field, parent) {
|
getControl(field, parent) {
|
||||||
field.onlyInput = true;
|
field.onlyInput = true;
|
||||||
|
const controls = require('frappejs/client/view/controls');
|
||||||
const control = controls.makeControl({field: field, parent: parent});
|
const control = controls.makeControl({field: field, parent: parent});
|
||||||
|
|
||||||
// change will be triggered by datatable
|
// change will be triggered by datatable
|
||||||
|
@ -4,168 +4,168 @@ const utils = require('frappejs/client/ui/utils');
|
|||||||
|
|
||||||
class Tree {
|
class Tree {
|
||||||
constructor({parent, label, iconSet, withSkeleton, method}) {
|
constructor({parent, label, iconSet, withSkeleton, method}) {
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
this.nodes = {};
|
this.nodes = {};
|
||||||
if(!iconSet) {
|
if(!iconSet) {
|
||||||
this.iconSet = {
|
this.iconSet = {
|
||||||
open: octicons["triangle-down"].toSVG({ "width": 10, "class": "node-parent"}),
|
open: octicons["triangle-down"].toSVG({ "width": 10, "class": "node-parent"}),
|
||||||
closed: octicons["triangle-right"].toSVG({ "width": 5, "class": "node-parent"}),
|
closed: octicons["triangle-right"].toSVG({ "width": 5, "class": "node-parent"}),
|
||||||
leaf: octicons["primitive-dot"].toSVG({ "width": 7, "class": "node-leaf"})
|
leaf: octicons["primitive-dot"].toSVG({ "width": 7, "class": "node-leaf"})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.make();
|
this.make();
|
||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
this.tree = frappe.ui.create('div', {
|
this.tree = frappe.ui.create('div', {
|
||||||
inside: this.parent,
|
inside: this.parent,
|
||||||
className: 'tree ' + (this.withSkeleton ? 'with-skeleton' : '')
|
className: 'tree ' + (this.withSkeleton ? 'with-skeleton' : '')
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootNode = this.makeNode(this.label, this.label, true, null, this.tree);
|
this.rootNode = this.makeNode(this.label, this.label, true, null, this.tree);
|
||||||
this.expandNode(this.rootNode);
|
this.expandNode(this.rootNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
// this.selectedNode.parentNode &&
|
// this.selectedNode.parentNode &&
|
||||||
// this.loadChildren(this.selectedNode.parentNode, true);
|
// this.loadChildren(this.selectedNode.parentNode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadChildren(node, deep=false) {
|
async loadChildren(node, deep=false) {
|
||||||
if(!deep) {
|
let children = !deep ? await this.method(node) : await this.getAllNodes(node);
|
||||||
this.renderNodeChildren(node, this.method(node.value));
|
this.renderNodeChildren(node, children);
|
||||||
} else {
|
}
|
||||||
this.renderChildrenDeep(node, this.getAllNodes(node.value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChildrenDeep(dataList) {
|
renderChildrenDeep(dataList) {
|
||||||
dataList.map(d => { this.renderNodeChildren(this.nodes[d.parent], d.data); });
|
dataList.map(d => { this.renderNodeChildren(this.nodes[d.parent], d.data); });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNodeChildren(node, dataSet=[]) {
|
renderNodeChildren(node, dataSet=[]) {
|
||||||
frappe.ui.empty(node.childrenList);
|
frappe.ui.empty(node.childrenList);
|
||||||
|
|
||||||
dataSet.forEach(data => {
|
dataSet.forEach(data => {
|
||||||
let parentNode = this.nodes[node.value];
|
let parentNode = this.nodes[node.value];
|
||||||
let childNode = this.makeNode(data.label || data.value, data.value,
|
let childNode = this.makeNode(data.label || data.value, data.value,
|
||||||
data.expandable, parentNode);
|
data.expandable, parentNode);
|
||||||
childNode.treeLink.dataset.nodeData = data;
|
childNode.treeLink.dataset.nodeData = data;
|
||||||
});
|
});
|
||||||
node.expanded = false;
|
node.expanded = false;
|
||||||
|
|
||||||
// As children loaded
|
// As children loaded
|
||||||
node.loaded = true;
|
node.loaded = true;
|
||||||
this.onNodeClick(node, false);
|
this.onNodeClick(node, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNodes() { }
|
getAllNodes() { }
|
||||||
|
|
||||||
makeNode(label, value, expandable, parentNode, parentEl) {
|
makeNode(label, value, expandable, parentNode, parentEl) {
|
||||||
let node = {
|
let node = {
|
||||||
parent: parent,
|
label: label,
|
||||||
label: label,
|
value: value,
|
||||||
value: value,
|
loaded: 0,
|
||||||
loaded: 0,
|
expanded: 0,
|
||||||
expanded: 0,
|
expandable: expandable,
|
||||||
expandable: expandable,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if(parentNode){
|
if(parentNode){
|
||||||
node.parentNode = parentNode;
|
node.parentNode = parentNode;
|
||||||
node.parent = parentNode.childrenList;
|
node.parent = parentNode.childrenList;
|
||||||
node.isRoot = 0;
|
node.isRoot = 0;
|
||||||
} else {
|
} else {
|
||||||
node.isRoot = 1;
|
node.isRoot = 1;
|
||||||
node.parent = parentEl;
|
node.parent = parentEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nodes[value] = node;
|
this.nodes[value] = node;
|
||||||
this.buildNodeElement(node);
|
this.buildNodeElement(node);
|
||||||
this.onRender && this.onRender(node);
|
this.onRender && this.onRender(node);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildNodeElement(node) {
|
buildNodeElement(node) {
|
||||||
node.parentLi = frappe.ui.create('li', {
|
node.parentLi = frappe.ui.create('li', {
|
||||||
inside: node.parent,
|
inside: node.parent,
|
||||||
className: 'tree-node'
|
className: 'tree-node'
|
||||||
});
|
});
|
||||||
|
|
||||||
let iconHtml = '';
|
let iconHtml = '';
|
||||||
if(this.iconSet) {
|
if(this.iconSet) {
|
||||||
iconHtml = node.expandable ? this.iconSet.closed : this.iconSet.leaf;
|
iconHtml = node.expandable ? this.iconSet.closed : this.iconSet.leaf;
|
||||||
}
|
}
|
||||||
let labelEl = `<a class="tree-label"> ${node.label}</a>`;
|
let labelEl = `<span class="tree-label"> ${node.label}</span>`;
|
||||||
|
|
||||||
node.treeLink = frappe.ui.create('span', {
|
node.treeLink = frappe.ui.create('span', {
|
||||||
inside: node.parentLi,
|
inside: node.parentLi,
|
||||||
className: 'tree-link',
|
className: 'tree-link',
|
||||||
'data-label': node.label,
|
'data-label': node.label,
|
||||||
innerHTML: iconHtml + labelEl
|
innerHTML: iconHtml + labelEl
|
||||||
});
|
});
|
||||||
node.treeLink.dataset.node = node;
|
node.treeLink.dataset.node = node;
|
||||||
node.treeLink.addEventListener('click', () => {
|
node.treeLink.addEventListener('click', () => {
|
||||||
this.onNodeClick(node);
|
this.onNodeClick(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
node.childrenList = frappe.ui.create('ul', {
|
node.childrenList = frappe.ui.create('ul', {
|
||||||
inside: node.parentLi,
|
inside: node.parentLi,
|
||||||
className: 'tree-children hide'
|
className: 'tree-children hide'
|
||||||
});
|
});
|
||||||
|
|
||||||
// if(this.toolbar) {
|
// if(this.toolbar) {
|
||||||
// node.toolbar = this.getToolbar(node).insertAfter(node.treeLink);
|
// node.toolbar = this.getToolbar(node).insertAfter(node.treeLink);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodeClick(node, click = true) {
|
async onNodeClick(node, click = true) {
|
||||||
this.setSelectedNode(node);
|
this.setSelectedNode(node);
|
||||||
if(click) {
|
if(click) {
|
||||||
this.onClick && this.onClick(node);
|
this.onClick && this.onClick(node);
|
||||||
}
|
}
|
||||||
this.expandNode(node);
|
await this.expandNode(node);
|
||||||
// select link
|
// select link
|
||||||
utils.activate(this.tree, node.treeLink, 'tree-link', 'active');
|
utils.activate(this.tree, node.treeLink, 'tree-link', 'active');
|
||||||
if(node.toolbar) this.showToolbar(node);
|
if(node.toolbar) this.showToolbar(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
expandNode(node) {
|
async expandNode(node) {
|
||||||
if(node.expandable) {
|
if(node.expandable) {
|
||||||
this.toggleNode(node);
|
await this.toggleNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
node.expanded = !node.expanded;
|
node.expanded = !node.expanded;
|
||||||
// node.parent.classList.toggle('opened', node.expanded);
|
// node.parent.classList.toggle('opened', node.expanded);
|
||||||
node.parent.classList.add('opened');
|
node.parent.classList.add('opened');
|
||||||
node.parentLi.classList.add('opened');
|
node.parentLi.classList.add('opened');
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleNode(node) {
|
async toggleNode(node) {
|
||||||
if(!node.loaded) this.loadChildren(node);
|
if(!node.loaded) await this.loadChildren(node);
|
||||||
|
|
||||||
// expand children
|
// expand children
|
||||||
if(node.childrenList) {
|
if(node.childrenList) {
|
||||||
if(node.childrenList.innerHTML.length) {
|
if(node.childrenList.innerHTML.length) {
|
||||||
node.childrenList.classList.toggle('hide', !node.expanded);
|
if (node.expanded) {
|
||||||
}
|
node.childrenList.classList.add('hide');
|
||||||
|
} else {
|
||||||
|
node.childrenList.classList.remove('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// open close icon
|
// open close icon
|
||||||
if(this.iconSet) {
|
if(this.iconSet) {
|
||||||
const oldIcon = node.treeLink.querySelector('svg');
|
const oldIcon = node.treeLink.querySelector('svg');
|
||||||
const newIconKey = !node.expanded ? 'closed' : 'open';
|
const newIconKey = node.expanded ? 'closed' : 'open';
|
||||||
const newIcon = frappe.ui.create(this.iconSet[newIconKey]);
|
const newIcon = frappe.ui.create(this.iconSet[newIconKey]);
|
||||||
node.treeLink.replaceChild(newIcon, oldIcon);
|
node.treeLink.replaceChild(newIcon, oldIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedNode() { return this.selectedNode; }
|
getSelectedNode() { return this.selectedNode; }
|
||||||
|
|
||||||
setSelectedNode(node) { this.selectedNode = node; }
|
setSelectedNode(node) { this.selectedNode = node; }
|
||||||
|
|
||||||
showToolbar() { }
|
showToolbar() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Tree;
|
module.exports = Tree;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const BaseList = require('frappejs/client/view/list');
|
const BaseList = require('frappejs/client/view/list');
|
||||||
|
const BaseTree = require('frappejs/client/view/tree');
|
||||||
const BaseForm = require('frappejs/client/view/form');
|
const BaseForm = require('frappejs/client/view/form');
|
||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
@ -8,5 +9,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
getListClass(doctype) {
|
getListClass(doctype) {
|
||||||
return (frappe.views['List'] && frappe.views['List'][doctype]) || BaseList;
|
return (frappe.views['List'] && frappe.views['List'][doctype]) || BaseList;
|
||||||
|
},
|
||||||
|
getTreeClass(doctype) {
|
||||||
|
return (frappe.views['Tree'] && frappe.views['Tree'][doctype] || BaseTree);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,9 +5,11 @@ const Observable = require('frappejs/utils/observable');
|
|||||||
module.exports = class BaseList extends Observable {
|
module.exports = class BaseList extends Observable {
|
||||||
constructor({doctype, parent, fields=[], page}) {
|
constructor({doctype, parent, fields=[], page}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
this.meta = frappe.getMeta(this.doctype);
|
this.meta = frappe.getMeta(this.doctype);
|
||||||
|
|
||||||
this.start = 0;
|
this.start = 0;
|
||||||
@ -17,14 +19,14 @@ module.exports = class BaseList extends Observable {
|
|||||||
this.rows = [];
|
this.rows = [];
|
||||||
this.data = [];
|
this.data = [];
|
||||||
|
|
||||||
this.setupListSettings();
|
this.setupTreeSettings();
|
||||||
|
|
||||||
frappe.db.on(`change:${this.doctype}`, (params) => {
|
frappe.db.on(`change:${this.doctype}`, (params) => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setupListSettings() {
|
setupTreeSettings() {
|
||||||
// list settings that can be overridden by meta
|
// list settings that can be overridden by meta
|
||||||
this.listSettings = {
|
this.listSettings = {
|
||||||
getFields: list => list.fields,
|
getFields: list => list.fields,
|
||||||
|
151
client/view/tree.js
Normal file
151
client/view/tree.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
const BaseList = require('./list');
|
||||||
|
const Tree = require('frappejs/client/ui/tree');
|
||||||
|
// const keyboard = require('frappejs/client/ui/keyboard');
|
||||||
|
|
||||||
|
module.exports = class BaseTree extends BaseList {
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.meta = frappe.getMeta(this.doctype);
|
||||||
|
|
||||||
|
this.body = null;
|
||||||
|
this.data = [];
|
||||||
|
|
||||||
|
this.setupTreeSettings();
|
||||||
|
|
||||||
|
frappe.db.on(`change:${this.doctype}`, (params) => {
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTreeSettings() {
|
||||||
|
// tree settings that can be overridden by meta
|
||||||
|
this.treeSettings = {
|
||||||
|
parentField: `parent${this.doctype}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.meta.treeSettings) {
|
||||||
|
Object.assign(this.treeSettings, this.meta.treeSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
return await this.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
this.makeBody();
|
||||||
|
this.body.innerHTML = '';
|
||||||
|
this.dirty = false;
|
||||||
|
|
||||||
|
let accountingSettings = await frappe.db.getSingle('AccountingSettings');
|
||||||
|
let rootLabel = accountingSettings.companyName;
|
||||||
|
|
||||||
|
this.renderTree(rootLabel);
|
||||||
|
this.trigger('state-change');
|
||||||
|
}
|
||||||
|
|
||||||
|
makeBody() {
|
||||||
|
if (!this.body) {
|
||||||
|
this.makeToolbar();
|
||||||
|
this.parent.classList.add('tree-page');
|
||||||
|
this.body = frappe.ui.add('div', 'tree-body', this.parent);
|
||||||
|
this.body.setAttribute('data-doctype', this.doctype);
|
||||||
|
this.bindKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTree(rootLabel) {
|
||||||
|
this.tree = new Tree({
|
||||||
|
label: rootLabel,
|
||||||
|
parent: this.body,
|
||||||
|
method: async node => {
|
||||||
|
const children = await this.getData(node) || [];
|
||||||
|
return children.map(d => ({
|
||||||
|
label: d.name,
|
||||||
|
value: d.name,
|
||||||
|
expandable: d.isGroup
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData(node) {
|
||||||
|
let fields = this.getFields();
|
||||||
|
let filters = {};
|
||||||
|
|
||||||
|
if (node.isRoot) {
|
||||||
|
filters[this.treeSettings.parentField] = '';
|
||||||
|
} else {
|
||||||
|
filters[this.treeSettings.parentField] = node.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await frappe.db.getAll({
|
||||||
|
doctype: this.doctype,
|
||||||
|
fields,
|
||||||
|
filters,
|
||||||
|
order_by: 'name',
|
||||||
|
order: 'asc'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFields() {
|
||||||
|
let fields = [this.treeSettings.parentField, 'isGroup']
|
||||||
|
this.updateStandardFields(fields);
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeToolbar() {
|
||||||
|
this.makeSearch();
|
||||||
|
|
||||||
|
this.btnNew = this.page.addButton(frappe._('New'), 'btn-primary', async () => {
|
||||||
|
await frappe.router.setRoute('new', this.doctype);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.btnDelete = this.page.addButton(frappe._('Delete'), 'btn-secondary hide', async () => {
|
||||||
|
await frappe.db.deleteMany(this.doctype, this.getCheckedRowNames());
|
||||||
|
await this.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.btnReport = this.page.addButton(frappe._('Report'), 'btn-outline-secondary hide', async () => {
|
||||||
|
await frappe.router.setRoute('table', this.doctype);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('state-change', () => {
|
||||||
|
const checkedCount = this.getCheckedRowNames().length;
|
||||||
|
this.btnDelete.classList.toggle('hide', checkedCount ? false : true);
|
||||||
|
this.btnNew.classList.toggle('hide', checkedCount ? true : false);
|
||||||
|
this.btnReport.classList.toggle('hide', checkedCount ? true : false);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.page.body.addEventListener('click', (event) => {
|
||||||
|
if(event.target.classList.contains('checkbox')) {
|
||||||
|
this.trigger('state-change');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSearch() {
|
||||||
|
this.toolbar = frappe.ui.add('div', 'list-toolbar', this.parent);
|
||||||
|
this.toolbar.innerHTML = `
|
||||||
|
<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>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.searchInput = this.toolbar.querySelector('input');
|
||||||
|
this.searchInput.addEventListener('keypress', (event) => {
|
||||||
|
if (event.keyCode===13) {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.btnSearch = this.toolbar.querySelector('.btn-search');
|
||||||
|
this.btnSearch.addEventListener('click', (event) => {
|
||||||
|
this.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user