mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
More functionality to Tree Component
- Make a basecomponent to encapsulate bootstrapping of WC - Remove old tree.js - Refactor Tree Components into JS and HTML
This commit is contained in:
parent
2909cd74b7
commit
9878055ddf
43
client/components/baseComponent.js
Normal file
43
client/components/baseComponent.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
let templates = {};
|
||||||
|
|
||||||
|
class BaseComponent extends HTMLElement {
|
||||||
|
constructor(name) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._name = name;
|
||||||
|
this._shadowRoot = this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
|
if (!templates[name]) {
|
||||||
|
makeTemplate(name, this.templateHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
let template = getTemplate(name);
|
||||||
|
this._shadowRoot.appendChild(template);
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerEvent(eventName, detail = {}) {
|
||||||
|
const event = new CustomEvent(eventName, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail
|
||||||
|
});
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeTemplate(name, html) {
|
||||||
|
if (!templates[name]) {
|
||||||
|
let template = document.createElement('template');
|
||||||
|
template.id = name;
|
||||||
|
template.innerHTML = html;
|
||||||
|
|
||||||
|
templates[name] = template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplate(name) {
|
||||||
|
return templates[name].content.cloneNode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BaseComponent;
|
11
client/components/tree/index.html
Normal file
11
client/components/tree/index.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!-- styles -->
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- template -->
|
||||||
|
<div class="tree">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
19
client/components/tree/index.js
Normal file
19
client/components/tree/index.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const BaseComponent = require('../baseComponent');
|
||||||
|
const TreeNode = require('./treeNode');
|
||||||
|
|
||||||
|
class Tree extends BaseComponent {
|
||||||
|
get templateHTML() {
|
||||||
|
return require('./index.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('Tree');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('f-tree', Tree);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Tree,
|
||||||
|
TreeNode
|
||||||
|
}
|
58
client/components/tree/treeNode.html
Normal file
58
client/components/tree/treeNode.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!-- styles -->
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node:hover {
|
||||||
|
background-color: gray;
|
||||||
|
background-color: var(--tree-node-hover);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node:hover .tree-node-actions {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-actions {
|
||||||
|
display: none;
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-icon {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-icon svg {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-children {
|
||||||
|
padding-left: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- template -->
|
||||||
|
<div class="tree-node">
|
||||||
|
<div class="tree-node-content">
|
||||||
|
<div class="tree-node-icon"></div>
|
||||||
|
<div class="tree-node-label"></div>
|
||||||
|
</div>
|
||||||
|
<div class="tree-node-actions">
|
||||||
|
<slot name="actions"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tree-node-children">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
94
client/components/tree/treeNode.js
Normal file
94
client/components/tree/treeNode.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
const octicons = require('octicons');
|
||||||
|
const BaseComponent = require('../baseComponent');
|
||||||
|
|
||||||
|
const iconSet = {
|
||||||
|
open: octicons["triangle-down"].toSVG({ width: "12", height: "12", "class": "tree-icon-open" }),
|
||||||
|
close: octicons["triangle-right"].toSVG({ width: "12", height: "12", "class": "tree-icon-closed" })
|
||||||
|
};
|
||||||
|
|
||||||
|
class TreeNode extends BaseComponent {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['label', 'expanded'];
|
||||||
|
}
|
||||||
|
|
||||||
|
get templateHTML() {
|
||||||
|
return require('./treeNode.html');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('TreeNode');
|
||||||
|
|
||||||
|
let shadowRoot = this._shadowRoot;
|
||||||
|
this.iconEl = shadowRoot.querySelector('.tree-node-icon');
|
||||||
|
this.labelEl = shadowRoot.querySelector('.tree-node-label');
|
||||||
|
this.actionsEl = shadowRoot.querySelector('.tree-node-actions');
|
||||||
|
this.childrenEl = shadowRoot.querySelector('.tree-node-children');
|
||||||
|
|
||||||
|
this.addEventListener('click', e => {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (e.target.matches('[slot="actions"]')) {
|
||||||
|
this.triggerEvent('tree-node-action', {
|
||||||
|
actionEl: e.target
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.expanded) {
|
||||||
|
this.removeAttribute('expanded');
|
||||||
|
} else {
|
||||||
|
this.setAttribute('expanded', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.onExpand();
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
switch(name) {
|
||||||
|
case 'label': {
|
||||||
|
this.labelEl.innerHTML = newValue || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'expanded': {
|
||||||
|
const isExpanded = this.hasAttribute('expanded');
|
||||||
|
this.onExpand(isExpanded);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpand(isExpanded = false) {
|
||||||
|
if (this.isLeaf) return;
|
||||||
|
|
||||||
|
if (isExpanded) {
|
||||||
|
this.iconEl.innerHTML = iconSet.open;
|
||||||
|
this.childrenEl.style.display = '';
|
||||||
|
} else {
|
||||||
|
this.iconEl.innerHTML = iconSet.close;
|
||||||
|
this.childrenEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.triggerEvent('tree-node-expand', {
|
||||||
|
expanded: isExpanded
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRoot() {
|
||||||
|
return this.hasAttribute('is-root');
|
||||||
|
}
|
||||||
|
|
||||||
|
get expanded() {
|
||||||
|
return this.hasAttribute('expanded');
|
||||||
|
}
|
||||||
|
|
||||||
|
get isLeaf() {
|
||||||
|
return this.hasAttribute('is-leaf');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('f-tree-node', TreeNode);
|
||||||
|
|
||||||
|
module.exports = TreeNode;
|
@ -4,120 +4,6 @@
|
|||||||
padding: $spacer-3 $spacer-4;
|
padding: $spacer-3 $spacer-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
f-tree, f-tree-node {
|
f-tree-node {
|
||||||
display: block;
|
--tree-node-hover: $gray-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
f-tree-node:hover {
|
|
||||||
background-color: $gray-100;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// .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-200;
|
|
||||||
// width: 24px;
|
|
||||||
// height: 24px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .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;
|
|
||||||
// }
|
|
||||||
|
@ -3,12 +3,13 @@ const Dropdown = require('./dropdown');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create(tag, obj) {
|
create(tag, obj) {
|
||||||
if(!obj) {
|
if(tag.includes('<')) {
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
div.innerHTML = tag.trim();
|
div.innerHTML = tag.trim();
|
||||||
return div.firstChild;
|
return div.firstChild;
|
||||||
}
|
}
|
||||||
let element = document.createElement(tag);
|
let element = document.createElement(tag);
|
||||||
|
obj = obj || {};
|
||||||
|
|
||||||
let $ = (expr, con) => {
|
let $ = (expr, con) => {
|
||||||
return typeof expr === "string"
|
return typeof expr === "string"
|
||||||
|
@ -1,262 +0,0 @@
|
|||||||
const frappe = require('frappejs');
|
|
||||||
const octicons = require('octicons');
|
|
||||||
const utils = require('frappejs/client/ui/utils');
|
|
||||||
|
|
||||||
const iconSet = {
|
|
||||||
open: octicons["triangle-down"].toSVG({ width: "12", height: "12", "class": "tree-icon-open" }),
|
|
||||||
close: octicons["triangle-right"].toSVG({ width: "12", height: "12", "class": "tree-icon-closed" })
|
|
||||||
};
|
|
||||||
|
|
||||||
let TreeTemplate = document.createElement('template');
|
|
||||||
TreeTemplate.innerHTML = `
|
|
||||||
<div class="tree">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
class Tree extends HTMLElement {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.attachShadow({ mode: 'open' })
|
|
||||||
.appendChild(TreeTemplate.content.cloneNode(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.customElements.define('f-tree', Tree);
|
|
||||||
|
|
||||||
let TreeNodeTemplate = document.createElement('template');
|
|
||||||
TreeNodeTemplate.innerHTML = `
|
|
||||||
<style>
|
|
||||||
.tree-node-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-node-icon {
|
|
||||||
width: 2rem;
|
|
||||||
height: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tree-node-icon svg {
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div class="tree-node-content">
|
|
||||||
<div class="tree-node-icon"></div>
|
|
||||||
<div class="tree-node-label"></div>
|
|
||||||
<div class="tree-node-actions"></div>
|
|
||||||
</div>
|
|
||||||
<slot></slot>
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
|
||||||
class TreeNode extends HTMLElement {
|
|
||||||
static get observedAttributes() {
|
|
||||||
return ['label', 'expanded']
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
let shadowRoot = this.attachShadow({ mode: 'open' });
|
|
||||||
shadowRoot.appendChild(TreeNodeTemplate.content.cloneNode(true));
|
|
||||||
this.iconEl = shadowRoot.querySelector('.tree-node-icon');
|
|
||||||
this.labelEl = shadowRoot.querySelector('.tree-node-label');
|
|
||||||
this.actionsEl = shadowRoot.querySelector('.tree-node-actions');
|
|
||||||
}
|
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
|
||||||
console.log(name, oldValue, newValue);
|
|
||||||
|
|
||||||
switch(name) {
|
|
||||||
case 'label': {
|
|
||||||
this.labelEl.innerHTML = newValue || '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'expanded': {
|
|
||||||
this.expanded = this.hasAttribute('expanded');
|
|
||||||
|
|
||||||
if (this.expanded) {
|
|
||||||
this.iconEl.innerHTML = iconSet.open;
|
|
||||||
} else {
|
|
||||||
this.iconEl.innerHTML = iconSet.close;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.customElements.define('f-tree-node', TreeNode);
|
|
||||||
|
|
||||||
class TreeOld {
|
|
||||||
constructor({ parent, label, iconSet, withSkeleton, method }) {
|
|
||||||
Object.assign(this, arguments[0]);
|
|
||||||
this.nodes = {};
|
|
||||||
if (!iconSet) {
|
|
||||||
this.iconSet = {
|
|
||||||
open: octicons["triangle-down"].toSVG({ "width": 10, "class": "node-parent" }),
|
|
||||||
closed: octicons["triangle-right"].toSVG({ "width": 5, "class": "node-parent" }),
|
|
||||||
leaf: octicons["primitive-dot"].toSVG({ "width": 7, "class": "node-leaf" })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.make();
|
|
||||||
}
|
|
||||||
|
|
||||||
make() {
|
|
||||||
this.tree = frappe.ui.create('div', {
|
|
||||||
inside: this.parent,
|
|
||||||
className: 'tree ' + (this.withSkeleton ? 'with-skeleton' : '')
|
|
||||||
});
|
|
||||||
|
|
||||||
this.rootNode = this.makeNode(this.label, this.label, true, null, this.tree);
|
|
||||||
this.expandNode(this.rootNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
// this.selectedNode.parentNode &&
|
|
||||||
// this.loadChildren(this.selectedNode.parentNode, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadChildren(node, deep = false) {
|
|
||||||
let children = !deep ? await this.method(node) : await this.getAllNodes(node);
|
|
||||||
this.renderNodeChildren(node, children);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChildrenDeep(dataList) {
|
|
||||||
dataList.map(d => { this.renderNodeChildren(this.nodes[d.parent], d.data); });
|
|
||||||
}
|
|
||||||
|
|
||||||
renderNodeChildren(node, dataSet = []) {
|
|
||||||
frappe.ui.empty(node.childrenList);
|
|
||||||
|
|
||||||
dataSet.forEach(data => {
|
|
||||||
let parentNode = this.nodes[node.value];
|
|
||||||
let childNode = this.makeNode(data.label || data.value, data.value,
|
|
||||||
data.expandable, parentNode);
|
|
||||||
childNode.treeLink.dataset.nodeData = data;
|
|
||||||
});
|
|
||||||
node.expanded = false;
|
|
||||||
|
|
||||||
// As children loaded
|
|
||||||
node.loaded = true;
|
|
||||||
this.onNodeClick(node, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllNodes() { }
|
|
||||||
|
|
||||||
makeNode(label, value, expandable, parentNode, parentEl) {
|
|
||||||
let node = {
|
|
||||||
label: label,
|
|
||||||
value: value,
|
|
||||||
loaded: 0,
|
|
||||||
expanded: 0,
|
|
||||||
expandable: expandable,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (parentNode) {
|
|
||||||
node.parentNode = parentNode;
|
|
||||||
node.parent = parentNode.childrenList;
|
|
||||||
node.isRoot = 0;
|
|
||||||
} else {
|
|
||||||
node.isRoot = 1;
|
|
||||||
node.parent = parentEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nodes[value] = node;
|
|
||||||
this.buildNodeElement(node);
|
|
||||||
this.onRender && this.onRender(node);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
buildNodeElement(node) {
|
|
||||||
node.parentLi = frappe.ui.create('li', {
|
|
||||||
inside: node.parent,
|
|
||||||
className: 'tree-node'
|
|
||||||
});
|
|
||||||
|
|
||||||
let iconHtml = '';
|
|
||||||
if (this.iconSet) {
|
|
||||||
iconHtml = node.expandable ? this.iconSet.closed : '';
|
|
||||||
}
|
|
||||||
let labelEl = `<span class="tree-label"> ${node.label}</span>`;
|
|
||||||
|
|
||||||
node.treeLink = frappe.ui.create('span', {
|
|
||||||
inside: node.parentLi,
|
|
||||||
className: 'tree-link',
|
|
||||||
'data-label': node.label,
|
|
||||||
innerHTML: iconHtml + labelEl
|
|
||||||
});
|
|
||||||
node.treeLink.dataset.node = node;
|
|
||||||
node.treeLink.addEventListener('click', () => {
|
|
||||||
this.onNodeClick(node);
|
|
||||||
});
|
|
||||||
|
|
||||||
node.childrenList = frappe.ui.create('ul', {
|
|
||||||
inside: node.parentLi,
|
|
||||||
className: 'tree-children hide'
|
|
||||||
});
|
|
||||||
|
|
||||||
// if(this.toolbar) {
|
|
||||||
// node.toolbar = this.getToolbar(node).insertAfter(node.treeLink);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
async onNodeClick(node, click = true) {
|
|
||||||
this.setSelectedNode(node);
|
|
||||||
if (click) {
|
|
||||||
this.onClick && this.onClick(node);
|
|
||||||
}
|
|
||||||
await this.expandNode(node);
|
|
||||||
// select link
|
|
||||||
utils.activate(this.tree, node.treeLink, 'tree-link', 'active');
|
|
||||||
if (node.toolbar) this.showToolbar(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
async expandNode(node) {
|
|
||||||
if (node.expandable) {
|
|
||||||
await this.toggleNode(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.expanded = !node.expanded;
|
|
||||||
// node.parent.classList.toggle('opened', node.expanded);
|
|
||||||
node.parent.classList.add('opened');
|
|
||||||
node.parentLi.classList.add('opened');
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleNode(node) {
|
|
||||||
if (!node.loaded) await this.loadChildren(node);
|
|
||||||
|
|
||||||
// expand children
|
|
||||||
if (node.childrenList) {
|
|
||||||
if (node.childrenList.innerHTML.length) {
|
|
||||||
if (node.expanded) {
|
|
||||||
node.childrenList.classList.add('hide');
|
|
||||||
} else {
|
|
||||||
node.childrenList.classList.remove('hide');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// open close icon
|
|
||||||
if (this.iconSet) {
|
|
||||||
const oldIcon = node.treeLink.querySelector('svg');
|
|
||||||
const newIconKey = node.expanded ? 'closed' : 'open';
|
|
||||||
const newIcon = frappe.ui.create(this.iconSet[newIconKey]);
|
|
||||||
node.treeLink.replaceChild(newIcon, oldIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSelectedNode() { return this.selectedNode; }
|
|
||||||
|
|
||||||
setSelectedNode(node) { this.selectedNode = node; }
|
|
||||||
|
|
||||||
showToolbar() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Tree;
|
|
@ -1,6 +1,6 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const BaseList = require('./list');
|
const BaseList = require('./list');
|
||||||
const Tree = require('frappejs/client/ui/tree');
|
const Tree = require('frappejs/client/components/tree');
|
||||||
// const keyboard = require('frappejs/client/ui/keyboard');
|
// const keyboard = require('frappejs/client/ui/keyboard');
|
||||||
|
|
||||||
module.exports = class BaseTree extends BaseList {
|
module.exports = class BaseTree extends BaseList {
|
||||||
@ -56,83 +56,62 @@ module.exports = class BaseTree extends BaseList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTree(rootLabel) {
|
renderTree(rootLabel) {
|
||||||
// const tree = new Tree();
|
|
||||||
// tree.getChildNodes = async node => {
|
|
||||||
// const children = await this.getData(node) || [];
|
|
||||||
// return children.map(d => ({
|
|
||||||
// label: d.name,
|
|
||||||
// value: d.name,
|
|
||||||
// expandable: d.isGroup
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
// tree.rootNode = {
|
|
||||||
// label: rootLabel,
|
|
||||||
// value: rootLabel,
|
|
||||||
// isRoot: 1,
|
|
||||||
// expandable: 1
|
|
||||||
// }
|
|
||||||
// this.body.appendChild(tree);
|
|
||||||
|
|
||||||
this.rootNode = {
|
this.rootNode = {
|
||||||
label: rootLabel,
|
label: rootLabel,
|
||||||
value: rootLabel,
|
value: rootLabel,
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
expanded: true,
|
isGroup: true,
|
||||||
children: []
|
children: null
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNodeHTML = node =>
|
|
||||||
`<f-tree-node
|
|
||||||
label="${node.label}"
|
|
||||||
value="${node.value}"
|
|
||||||
${node.expanded ? 'expanded' : ''}
|
|
||||||
${node.isRoot ? 'is-root' : ''}>
|
|
||||||
</f-tree-node>`;
|
|
||||||
|
|
||||||
this.treeWrapper = frappe.ui.create('f-tree');
|
|
||||||
|
|
||||||
this.rootNode.el = frappe.ui.create(getNodeHTML(this.rootNode), {
|
|
||||||
inside: this.treeWrapper
|
|
||||||
});
|
|
||||||
|
|
||||||
this.treeWrapper = frappe.ui.create(`
|
this.treeWrapper = frappe.ui.create(`
|
||||||
<f-tree>
|
<f-tree>
|
||||||
${getNodeHTML(this.rootNode)}
|
${this.getTreeNodeHTML(this.rootNode)}
|
||||||
</f-tree>
|
</f-tree>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const rootNode = this.treeWrapper.querySelector('f-tree-node[is-root]');
|
||||||
|
rootNode.props = this.rootNode;
|
||||||
|
|
||||||
this.body.appendChild(this.treeWrapper);
|
this.body.appendChild(this.treeWrapper);
|
||||||
|
|
||||||
frappe.ui.on(this.treeWrapper, 'click', 'f-tree-node', async (e, treeNode) => {
|
frappe.ui.on(this.treeWrapper, 'tree-node-expand', 'f-tree-node', async (e, treeNode) => {
|
||||||
if (treeNode.expanded) {
|
if (!treeNode.expanded) return;
|
||||||
treeNode.removeAttribute('expanded');
|
|
||||||
} else {
|
if (!treeNode.props.children) {
|
||||||
treeNode.setAttribute('expanded', '');
|
const data = await this.getData(treeNode.props);
|
||||||
|
const children = data.map(d => ({
|
||||||
|
label: d.name,
|
||||||
|
value: d.name,
|
||||||
|
isGroup: d.isGroup,
|
||||||
|
doc: d
|
||||||
|
}));
|
||||||
|
treeNode.props.children = children;
|
||||||
|
|
||||||
|
for (let child of children) {
|
||||||
|
const childNode = frappe.ui.create(this.getTreeNodeHTML(child));
|
||||||
|
childNode.props = child;
|
||||||
|
treeNode.appendChild(childNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = null;
|
|
||||||
// if (treeNode.hasAttribute('is-root')) {
|
|
||||||
// node = this.rootNode;
|
|
||||||
// } else {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.ui.on(this.treeWrapper, 'tree-node-action', 'f-tree-node', (e, treeNode) => {
|
||||||
|
if (treeNode.isRoot) return;
|
||||||
|
|
||||||
// this.tree = new Tree({
|
const button = e.detail.actionEl;
|
||||||
// label: rootLabel,
|
const action = button.getAttribute('data-action');
|
||||||
// parent: this.body,
|
|
||||||
// method: async node => {
|
if (action === 'edit') {
|
||||||
// const children = await this.getData(node) || [];
|
this.edit(treeNode.props.doc.name);
|
||||||
// return children.map(d => ({
|
}
|
||||||
// label: d.name,
|
});
|
||||||
// value: d.name,
|
|
||||||
// expandable: d.isGroup
|
rootNode.click(); // open the root node
|
||||||
// }));
|
}
|
||||||
// }
|
|
||||||
// });
|
edit(name) {
|
||||||
|
frappe.desk.showFormModal(this.doctype, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData(node) {
|
async getData(node) {
|
||||||
@ -154,63 +133,36 @@ module.exports = class BaseTree extends BaseList {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTreeNodeHTML(node) {
|
||||||
|
return (
|
||||||
|
`<f-tree-node
|
||||||
|
label="${node.label}"
|
||||||
|
value="${node.value}"
|
||||||
|
${node.expanded ? 'expanded' : ''}
|
||||||
|
${node.isRoot ? 'is-root' : ''}
|
||||||
|
${node.isGroup ? '' : 'is-leaf'}
|
||||||
|
>
|
||||||
|
${this.getActionButtonsHTML()}
|
||||||
|
</f-tree-node>`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionButtonsHTML() {
|
||||||
|
return [
|
||||||
|
{ id: 'edit', label: frappe._('Edit') }
|
||||||
|
// { id: 'addChild', label: frappe._('Add Child') },
|
||||||
|
// { id: 'delete', label: frappe._('Delete') },
|
||||||
|
].map(button => {
|
||||||
|
return `<button class="btn btn-link btn-sm m-0" slot="actions" data-action="${button.id}">
|
||||||
|
${button.label}
|
||||||
|
</button>`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
getFields() {
|
getFields() {
|
||||||
let fields = [this.treeSettings.parentField, 'isGroup']
|
let fields = [this.treeSettings.parentField, 'isGroup']
|
||||||
this.updateStandardFields(fields);
|
this.updateStandardFields(fields);
|
||||||
return 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…
Reference in New Issue
Block a user