You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1036 lines
35 KiB
1036 lines
35 KiB
//Pickle tree component created by Kadir Barış Bozat |
|
|
|
class PickleTree { |
|
/** |
|
* |
|
* @param {object} obj as tree object |
|
*/ |
|
constructor(obj) { |
|
//target div id |
|
this.target = obj.c_target; |
|
//building area |
|
this.area = ""; |
|
//available nodes list |
|
this.nodeList = {}; |
|
//row create callback |
|
this.rowCreateCallback = obj.rowCreateCallback; |
|
//draw callback |
|
this.drawCallback = obj.drawCallback; |
|
//switch callback |
|
this.switchCallback = obj.switchCallback; |
|
//drag callback |
|
this.dragCallback = obj.dragCallback; |
|
//drop callback |
|
this.dropCallback = obj.dropCallback; |
|
//order callback |
|
this.orderCallback = obj.orderCallback; |
|
//node removed callback |
|
this.nodeRemove = obj.nodeRemoveCallback; |
|
//tree json data |
|
this.data = obj.c_data; |
|
//build tree |
|
this.build(obj.c_config); |
|
//start events |
|
this.staticEvents(); |
|
} |
|
|
|
/** |
|
* this method will contains static events for tree |
|
*/ |
|
staticEvents() { |
|
//close menu |
|
this.main_container.addEventListener("click", (e) => { |
|
let elm = e.target; |
|
//close all first |
|
document.querySelectorAll(".ptreemenuCont").forEach((menu) => { |
|
menu.outerHTML = ""; |
|
}); |
|
if (elm.classList.contains("menuIcon")) { |
|
//menu toggle event for node |
|
setTimeout(() => { |
|
this.getMenu(e.target, this.getNode(elm.id.split("node_")[1])); |
|
}, 10); |
|
} |
|
}); |
|
|
|
//drag - drop events |
|
if (this.config.drag) { |
|
this.invalid_area = { |
|
container: null, |
|
top: 0, |
|
left: 0, |
|
right: 0, |
|
bottom: 0, |
|
}; |
|
|
|
//drag start |
|
this.main_container.addEventListener("dragstart", async (e) => { |
|
//give border to container |
|
//container |
|
this.invalid_area.container = document.getElementById(this.target + "node_" + e.target.id.split("node_")[1]); |
|
this.invalid_area.top = this.invalid_area.container.getBoundingClientRect().top; |
|
this.invalid_area.left = this.invalid_area.container.getBoundingClientRect().left; |
|
this.invalid_area.right = this.invalid_area.left + this.invalid_area.container.offsetWidth; |
|
this.invalid_area.bottom = this.invalid_area.top + this.invalid_area.container.offsetHeight; |
|
setTimeout(() => { |
|
this.invalid_area.container.classList.add("valid"); |
|
this._lock(); |
|
}, 300); |
|
//drag callback |
|
if (this.dragCallback) { |
|
this.dragCallback(this.nodeList[parseInt(e.target.id.split("node_")[1])]); |
|
} |
|
/////// ***** /////// |
|
//draging |
|
//clone element when drag start |
|
const id = e.target.id.split("node_")[1]; |
|
this.clone = document.getElementById(this.target +'node_' + id).cloneNode(true); |
|
this.clone.style.position = 'absolute'; |
|
this.clone.style.zIndex = 1000; |
|
this.clone.querySelectorAll('div').forEach(el=>el.style.backgroundColor = 'grey'); |
|
this.clone.querySelectorAll('li').forEach(el=>el.style.border = 'unset !important'); |
|
this.clone.style.width = '50vh'; |
|
//this.clone.querySelector('ul').remove(); |
|
this.clone.querySelectorAll('.switch').forEach(el => el.remove()); |
|
const rul = document.createElement('ul'); |
|
rul.appendChild(this.clone); |
|
const rdiv = document.createElement('div'); |
|
rdiv.appendChild(rul); |
|
rdiv.classList.add('dragging-element'); |
|
this.clone = rdiv; |
|
|
|
this.main_container.appendChild(this.clone); |
|
/////// ***** /////// |
|
}); |
|
|
|
//draging |
|
this.main_container.addEventListener("drag", (e) => { |
|
const bounds = e.currentTarget.getBoundingClientRect(); |
|
/////// ***** /////// |
|
this.clone.style.position = "absolute"; |
|
this.clone.style.left = `${e.clientX - bounds.left + 30}px`; |
|
this.clone.style.top = `${e.clientY - bounds.top + 30 }px`; |
|
/////// ***** /////// |
|
}); |
|
|
|
//drag end |
|
this.main_container.addEventListener("dragend", async (e) => { |
|
this.clone.remove(); |
|
//console.log('drag end') |
|
//remove border to container |
|
this.invalid_area.container.classList.remove("invalid"); |
|
this.invalid_area.container.classList.remove("valid"); |
|
//make all elements pointer clean |
|
this._lock(false); |
|
|
|
//clear old targets |
|
this.clearDebris(); |
|
//get node |
|
|
|
const node = this.nodeList[parseInt(e.target.id.split("node_")[1])]; |
|
|
|
//check is valid |
|
node.old_parent = node.parent; |
|
if (!this.invalid_area.valid) { |
|
node.parent = { |
|
id: 0 |
|
}; |
|
} else { |
|
console.log('target' , this.drag_target); |
|
const drop = this.getNode(this.drag_target); |
|
if (this.drag_target === parseInt(e.target.id.split("node_")[1]) || this.drag_target === undefined || drop === undefined || drop.parent.value === node.value) { |
|
//this means it dragged to outside |
|
node.parent = { |
|
id: 0 |
|
}; |
|
} else { |
|
node.parent = drop; |
|
} |
|
} |
|
|
|
this.nodeList[node.value] = node.updateNode(); |
|
|
|
console.log(this.nodeList[node.value]); |
|
|
|
|
|
//drop callback |
|
if (this.dropCallback) { |
|
this.dropCallback(node); |
|
} |
|
}); |
|
//drag location |
|
this.main_container.addEventListener("dragenter", (e) => { |
|
//console.log('drag enter') |
|
this.clearDebris(); |
|
try { |
|
//check position is valid |
|
let target = { |
|
left: e.target.getBoundingClientRect().left, |
|
top: e.target.getBoundingClientRect().top, |
|
}; |
|
|
|
if (target.top > this.invalid_area.top && target.top < this.invalid_area.bottom && target.left > this.invalid_area.left && target.left < this.invalid_area.right) { |
|
this.invalid_area.valid = false; |
|
this.invalid_area.container.classList.add("invalid"); |
|
this.invalid_area.container.classList.remove("valid"); |
|
} else { |
|
this.invalid_area.valid = true; |
|
this.invalid_area.container.classList.remove("invalid"); |
|
this.invalid_area.container.classList.add("valid"); |
|
} |
|
|
|
if (e.target.classList) { |
|
if (e.target.classList.contains("drop_target")) { |
|
e.target.classList.add("drag_triggered"); |
|
//this is for updating node parent to current |
|
this.drag_target = parseInt(e.target.id.split("node_")[1]); |
|
} |
|
} |
|
} catch (e) { |
|
//console.log('dragging have exception..'); |
|
this.drag_target = undefined; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
//#region Helper Methods |
|
/** |
|
* |
|
*/ |
|
async destroy() { |
|
//remove all menus |
|
document.querySelectorAll(".ptreemenuCont").forEach((menu) => { |
|
menu.outerHTML = ""; |
|
}); |
|
//remove all items |
|
document.getElementById(this.target).innerHTML = ""; |
|
} |
|
|
|
/** |
|
* this method will lock elements when dragging |
|
*/ |
|
async _lock(type = true) { |
|
const elms = document.querySelectorAll(".drop_target"); |
|
for (let i = 0; i < elms.length; i++) { |
|
if (type) { |
|
elms[i].classList.add("disabled"); |
|
} else { |
|
elms[i].classList.remove("disabled"); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* |
|
* @param {string} message for log messages |
|
*/ |
|
log(message) { |
|
if (this.config.logMode) { |
|
console.log(message); |
|
} |
|
} |
|
|
|
/** |
|
* Building main details |
|
*/ |
|
build(c_config) { |
|
//set default config |
|
this.config = { |
|
key: new Date().getTime(), |
|
//logs are open or close |
|
logMode: false, |
|
//switch mode |
|
switchMode: false, |
|
hasLink: false, |
|
//family mode |
|
//for child |
|
autoChild: true, |
|
//for parent |
|
autoParent: true, |
|
//fold icon |
|
foldedIcon: "fa fa-plus", |
|
//unfold icon |
|
unFoldedIcon: "fa fa-minus", |
|
//menu icon |
|
menuIcon: ["fa", "fa-list-ul"], |
|
//link icon |
|
linkIcon: "fa fa-list-ul", |
|
//start status is collapsed or not |
|
foldedStatus: false, |
|
//drag |
|
drag: false, |
|
//order |
|
order: false, |
|
// context menu position |
|
contextPos: 'after' |
|
}; |
|
//check config here!! |
|
for (let key in this.config) { |
|
if (c_config[key] !== undefined) { |
|
this.config[key] = c_config[key]; |
|
} |
|
} |
|
//check if key is exist somewhere in document |
|
if (document.getElementById(this.config.key + "_div_pickletree") !== null) { |
|
this.config.key = new Date().getTime() + 10; |
|
} |
|
//referance for some events |
|
this.main_container = document.getElementById(this.target); |
|
this.main_container.classList.add('ptree'); |
|
this.main_container.innerHTML = '<div id="' + this.config.key + '_div_pickletree"><ul id="' + this.config.key + '_tree_picklemain"></ul></div>'; |
|
//console.log(this.main_container.getElementById(this.config.key+'_tree_picklemain')); |
|
|
|
this.area = document.getElementById(this.config.key + "_tree_picklemain"); |
|
this.log("tree build started.."); |
|
this.drawData(); |
|
} |
|
|
|
/** |
|
* |
|
* @param {integer} id node id for finding node |
|
*/ |
|
getNode(id) { |
|
this.log("node returned.."); |
|
//return node |
|
return this.nodeList[id]; |
|
} |
|
|
|
/** |
|
* set child nodes for parent node |
|
* @param {object} node |
|
*/ |
|
setChildNodes(node) { |
|
//update node parent |
|
for (let key in this.nodeList) { |
|
if (this.nodeList[key].id === node.parent.id) { |
|
this.nodeList[key].childs.push(node.id); |
|
//show icon for childs |
|
const ic = document.getElementById("i_" + this.nodeList[key].id); |
|
if (ic !== null) ic.style.display = ""; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* this method will return switched nodes |
|
*/ |
|
getSelected() { |
|
let nodes = []; |
|
//get all checked nodes |
|
for (let key in this.nodeList) { |
|
if (this.nodeList[key].checkStatus) nodes.push(this.nodeList[key]); |
|
} |
|
return nodes; |
|
} |
|
|
|
/** |
|
* this method will reset switched nodes |
|
*/ |
|
resetSelected(type = false) { |
|
//get all checked nodes |
|
for (let key in this.nodeList) { |
|
this.nodeList[key].checkStatus = type; |
|
this.checkNode(this.nodeList[key]); |
|
} |
|
return true; |
|
} |
|
//#endregion |
|
|
|
//#region drag - drop events helpers |
|
/** |
|
* this method will clean entered areas after drag events |
|
*/ |
|
clearDebris() { |
|
//first clean all entered areas |
|
let elms = document.querySelectorAll(".drag_triggered"); |
|
for (let i = 0; i < elms.length; i++) { |
|
elms[i].classList.remove("drag_triggered"); |
|
} |
|
} |
|
|
|
//#endregion |
|
|
|
//#region Node Events |
|
/** |
|
* this method will order element |
|
* @param {event} e |
|
*/ |
|
orderNode(e) { |
|
const isBefore = e.target.dataset.target == 1; |
|
const main = e.target.parentNode.parentNode.parentNode; |
|
const target = isBefore ? main.previousElementSibling : main.nextElementSibling; |
|
//get nodes |
|
if (target !== null) { |
|
|
|
//replace data |
|
const targetNode = this.getNode(target.id.split("_").at(-1)); |
|
const mainNode = this.getNode(main.id.split("_").at(-1)); |
|
const currentOrder = mainNode.order; |
|
const targetOrder = targetNode.order === mainNode.order ? (isBefore ? targetNode.order - 1 : targetNode.order + 1) : targetNode.order; |
|
//change order data |
|
targetNode.order = currentOrder; |
|
mainNode.order = targetOrder; |
|
|
|
target.dataset.order = 'order_'+currentOrder; |
|
main.dataset.order = 'order_'+targetOrder; |
|
|
|
//replace element |
|
main.parentNode.replaceChild(main, target); |
|
main.parentNode.insertBefore(target, isBefore ? main.nextSibling : main); |
|
} |
|
if (typeof this.orderCallback == "function") this.orderCallback(main, target); |
|
} |
|
/** |
|
* get child nodes list of node |
|
* @param {object} node |
|
*/ |
|
getChilds(node) { |
|
let list = []; |
|
for (let key in this.nodeList) { |
|
if (node.childs.includes(this.nodeList[key].id)) { |
|
list.push(this.nodeList[key]); |
|
} |
|
} |
|
this.log("node childs returned.."); |
|
return list; |
|
} |
|
|
|
/** |
|
* toggle open or close node childs |
|
* @param {object} node |
|
*/ |
|
toggleNode(node) { |
|
if (node.childs.length > 0) { |
|
let ie = document.getElementById("i_" + node.id); |
|
let ule = document.getElementById("c_" + node.id); |
|
if (node.foldedStatus === false) { |
|
//change icon |
|
ie.classList.remove("fa-minus"); |
|
ie.classList.add("fa-plus"); |
|
//hide element |
|
//ule.style.display = "none"; |
|
ule.classList.remove("active"); |
|
ule.classList.add("not-active"); |
|
} else { |
|
//change icon |
|
ie.classList.remove("fa-plus"); |
|
ie.classList.add("fa-minus"); |
|
//show element |
|
//ule.style.display = ""; |
|
ule.classList.remove("not-active"); |
|
ule.classList.add("active"); |
|
} |
|
node.foldedStatus = !node.foldedStatus; |
|
//change node status |
|
for (let key in this.nodeList) { |
|
if (this.nodeList[key].id === node.id) { |
|
this.nodeList[key].foldedStatus = node.foldedStatus; |
|
} |
|
} |
|
this.log("node toggled.."); |
|
} else { |
|
this.log("node not has childs...!"); |
|
} |
|
} |
|
|
|
/** |
|
* remove node from dom |
|
* @param {object} node |
|
*/ |
|
deleteNode(node) { |
|
//remove node from old parent's child data !!!! |
|
|
|
let elm = document.getElementById(node.id); |
|
let childs = node.getChilds(); |
|
if (childs.length > 0) { |
|
for (let i = 0; i < childs.length; i++) { |
|
this.deleteNode(childs[i]); |
|
} |
|
} |
|
//remove node from container |
|
//delete this.nodeList[node.value]; |
|
|
|
if (elm !== null) elm.parentNode.removeChild(elm); |
|
this.log("node removed..(" + node.id + ")"); |
|
if (this.nodeRemove !== undefined) this.nodeRemove(node); |
|
} |
|
|
|
/** |
|
* this method will check node and its family. |
|
* @param {object} node |
|
*/ |
|
checkNode(node) { |
|
//console.log(node); |
|
//then if is checked and folded unfold and open childs |
|
let clength = node.childs.length; |
|
if (node.checkStatus && clength > 0) { |
|
//make element looks like is folded |
|
node.foldedStatus = true; |
|
this.toggleNode(node); |
|
} |
|
//trigger callback if exists |
|
if (typeof this.switchCallback == "function") this.switchCallback(node); |
|
//check html element if family mode is open |
|
document.getElementById("ck_" + node.id).checked = node.checkStatus; |
|
} |
|
|
|
/** |
|
* this method will check node childs and his parents if not checked. |
|
* @param {object} node |
|
*/ |
|
checkNodeFamily(node) { |
|
let status = node.checkStatus; |
|
let parentCheck = async (node) => { |
|
//first check if has parent |
|
if (node.parent.id !== 0) { |
|
//get parent node |
|
node = node.parent; |
|
let trans = () => { |
|
//change parent node status |
|
node.checkStatus = status; |
|
//check parent node |
|
this.checkNode(node); |
|
//then restart process |
|
parentCheck(node); |
|
}; |
|
//decide for uncheck |
|
if (!status) { |
|
//if all childs is unchecked or child count is equal to 1 |
|
let valid = true; |
|
let childs = node.getChilds(); |
|
for (let i = 0; i < childs.length; i++) { |
|
if (childs[i].checkStatus) { |
|
valid = false; |
|
} |
|
} |
|
if (valid) trans(); |
|
} else { |
|
trans(); |
|
} |
|
} |
|
}; |
|
|
|
let childCheck = async (node) => { |
|
//first check main node |
|
this.checkNode(node); |
|
//then check childs if exist |
|
if (node.childs.length > 0) { |
|
//foreach child |
|
for (let i = 0; i < node.childs.length; i++) { |
|
let c_node = this.getNode(node.childs[i].split("node_")[1]); |
|
c_node.checkStatus = status; |
|
//restart process |
|
childCheck(c_node); |
|
} |
|
} |
|
}; |
|
if (this.config.autoChild) childCheck(node); |
|
if (this.config.autoParent) parentCheck(node); |
|
} |
|
|
|
/** |
|
* this method will unfold all parents of node |
|
* @param {object} node |
|
*/ |
|
async showFamily(node) { |
|
//check if has parent |
|
if (node.parent.id !== 0) { |
|
//then make node status closed |
|
node.parent.foldedStatus = true; |
|
//after send parent node for toggle |
|
this.toggleNode(node.parent); |
|
//make recursive for another parents |
|
this.showFamily(node.parent); |
|
} |
|
} |
|
//#endregion |
|
|
|
//#region Node Creator |
|
|
|
/** |
|
* creating node |
|
* @param {object} obj |
|
*/ |
|
createNode(obj) { |
|
const id = Date.now(); |
|
const node = { |
|
//node value |
|
value: id, |
|
//node id |
|
id: this.target + "node_" + id, |
|
//node title |
|
title: "untitled " + id, |
|
//node html elements |
|
elements: [], |
|
//order number |
|
order: null, |
|
//node parent element |
|
parent: { |
|
id: 0 |
|
}, |
|
// child element ids |
|
childs: [], |
|
//addional info |
|
addional: {}, |
|
//childs status (child list opened or not) |
|
foldedStatus: this.config.foldedStatus, |
|
//check status for node |
|
checkStatus: false, |
|
//this method will return child nodes |
|
getChilds: () => this.getChilds(node), |
|
//this method will remove node from dom |
|
deleteNode: () => this.deleteNode(node), |
|
//this method will update node |
|
updateNode: () => this.updateNode(node), |
|
//this method will toggle node |
|
toggleNode: () => this.toggleNode(node), |
|
//this method will show node location |
|
showFamily: () => this.showFamily(node), |
|
//check node |
|
toggleCheck: (status) => { |
|
node.checkStatus = status; |
|
this.checkNode(node); |
|
}, |
|
//scroll to node |
|
scroll:() => document.getElementById(node.id).scrollIntoView(), |
|
//find child nodes from text |
|
find :(text) => { |
|
const nodes = []; |
|
document.getElementById(node.id).querySelectorAll('li').forEach(el=>{ |
|
if(el.innerHTML.includes(text)){ |
|
nodes.push(this.getNode(el.id.split("node_")[1])) |
|
} |
|
}); |
|
return nodes; |
|
} |
|
}; |
|
|
|
//check setted values here!! |
|
for (let key in obj) { |
|
if (obj[key] !== undefined) node[key.split("_")[1]] = obj[key]; |
|
if (key === "n_id") node["id"] = this.target + "node_" + obj["n_id"]; |
|
} |
|
|
|
if (node.order === null) node.order = 0; |
|
|
|
//node is added to container |
|
this.nodeList[obj["n_id"]] = node; |
|
//node is drawed |
|
this.drawNode(node); |
|
//logged |
|
this.log("Node is created (" + node.id + ")"); |
|
//node is returned |
|
return node; |
|
} |
|
|
|
/** |
|
* this method will update node |
|
* !! id is recommended |
|
*/ |
|
updateNode(node) { |
|
//first remove old node |
|
//console.log(this.getNode(node.id.split('_')[1])) |
|
this.getNode(node.id.split("node_")[1]).deleteNode(); |
|
//clear old parent's childs if old parent info is exist |
|
if (node.old_parent !== undefined && node.old_parent.id !== 0) { |
|
this.nodeList[node.old_parent.value].childs = this.nodeList[node.old_parent.value].childs.filter((x) => { |
|
return x !== node.id; |
|
}); |
|
//if child count is 0 then remove minus icon |
|
if (this.nodeList[node.old_parent.value].childs.length === 0) { |
|
document.getElementById("i_" + node.old_parent.id).style.display = "none"; |
|
} |
|
} |
|
//draw new node with childs |
|
const set = (data) => { |
|
this.drawNode(data); |
|
let childs = data.getChilds(); |
|
if (childs.length > 0) { |
|
for (let i = 0; i < childs.length; i++) { |
|
set(childs[i]); |
|
} |
|
} |
|
}; |
|
|
|
set(node); |
|
|
|
//log |
|
this.log("Node is created (" + node.id + ")"); |
|
//return node |
|
return node; |
|
} |
|
|
|
/** |
|
* |
|
* @param {object} node object for creating html element |
|
*/ |
|
drawNode(node) { |
|
let icon = this.config.unFoldedIcon; |
|
let style = ""; |
|
let defaultClass = "active"; |
|
|
|
if (node.foldedStatus) { |
|
icon = this.config.foldedIcon; |
|
style = "none"; |
|
defaultClass = "not-active"; |
|
} |
|
//#region elements |
|
|
|
//node li item |
|
let li_item = document.createElement("li"); |
|
//node a item |
|
let a_item = document.createElement("a"); |
|
//node i item |
|
let i_item = document.createElement("i"); |
|
//node ul item |
|
let ul_item = document.createElement("ul"); |
|
//node group item |
|
let div_item = document.createElement("div"); |
|
|
|
//make node ordarable |
|
if (this.config.order) { |
|
const o_div = document.createElement("div"); |
|
o_div.id = "order_" + node.id; |
|
//create buttons |
|
const up_i = document.createElement("i"); |
|
const dw_i = document.createElement("i"); |
|
|
|
o_div.classList.add("ptree_order_div"); |
|
|
|
up_i.classList.add("fa", "fa-arrow-up"); |
|
up_i.dataset.target = "1"; |
|
dw_i.classList.add("fa", "fa-arrow-down"); |
|
dw_i.dataset.target = "0"; |
|
|
|
o_div.appendChild(up_i); |
|
o_div.appendChild(dw_i); |
|
//ordering event |
|
o_div.onclick = (e) => (e.target.tagName == "I" ? this.orderNode(e) : false); |
|
|
|
div_item.appendChild(o_div); |
|
} |
|
|
|
//make node dragable |
|
if (this.config.drag) { |
|
//add drag button to start |
|
const a_ditem = document.createElement("a"); |
|
const i_ditem = document.createElement("i"); |
|
//set icon drag button |
|
i_ditem.classList.add("fa"); |
|
i_ditem.classList.add("fa-bars"); |
|
a_ditem.classList.add("drag-handler"); |
|
|
|
a_ditem.id = "a_dr_" + node.id; |
|
a_ditem.appendChild(i_ditem); |
|
a_ditem.href = "javascript:;"; |
|
a_ditem.setAttribute("dragable", true); |
|
a_ditem.setAttribute("drag-title", node.title); |
|
//icon added to div |
|
div_item.appendChild(a_ditem); |
|
div_item.classList.add("drop_target"); |
|
} |
|
|
|
//set i item id |
|
i_item.id = "i_" + node.id; |
|
//set i item style |
|
i_item.style.color = "black"; |
|
//set i item icon |
|
icon = icon.split(" "); |
|
for (let i = 0; i < icon.length; i++) { |
|
i_item.classList.add(icon[i]); |
|
} |
|
i_item.style.display = "none"; |
|
|
|
//set ul item id |
|
ul_item.id = "c_" + node.id; |
|
|
|
//set ul item style |
|
//ul_item.style.display = style; |
|
|
|
//set ul item class |
|
ul_item.classList.add(defaultClass); |
|
|
|
//set a item id |
|
a_item.id = "a_toggle_" + node.id; |
|
//set i tag to a item |
|
a_item.appendChild(i_item); |
|
//set a item href |
|
a_item.href = "javascript:;"; |
|
//set a_item title |
|
a_item.innerHTML += " " + node.title; |
|
|
|
a_item.onclick = (e) => this.toggleNode(node); |
|
|
|
//set li item id |
|
li_item.id = node.id; |
|
li_item.dataset.order = "order_" + node.order; |
|
|
|
div_item.id = "div_g_" + node.id; |
|
//set a tag to div item |
|
div_item.appendChild(a_item); |
|
|
|
|
|
//set switch to li item if user is wanted |
|
if (this.config.switchMode) { |
|
const sw_item = document.createElement("label"); |
|
const ck_item = document.createElement("input"); |
|
const spn_item = document.createElement("span"); |
|
spn_item.classList.add("slider"); |
|
spn_item.classList.add("round"); |
|
ck_item.type = "checkbox"; |
|
sw_item.classList.add("switch"); |
|
|
|
sw_item.appendChild(ck_item); |
|
sw_item.appendChild(spn_item); |
|
|
|
//id definitions |
|
ck_item.id = "ck_" + node.id; |
|
sw_item.id = "sw_" + node.id; |
|
|
|
ck_item.value = node.value; |
|
//if item created as checked |
|
ck_item.checked = node.checkStatus; |
|
|
|
ck_item.onclick = (e) => { |
|
node.checkStatus = e.target.checked; |
|
if (this.config.autoChild || this.config.autoParent) { |
|
this.checkNodeFamily(node); |
|
}else{ |
|
this.checkNode(node); |
|
} |
|
|
|
}; |
|
|
|
//switch is added to li element |
|
div_item.appendChild(sw_item); |
|
} |
|
|
|
|
|
|
|
//if node has extra elements |
|
if (node.elements.length > 0) { |
|
//add menu button to end |
|
let a_item = document.createElement("a"); |
|
let i_item = document.createElement("i"); |
|
//set icon for menu |
|
|
|
for (let i = 0; i < this.config.menuIcon.length; i++) { |
|
i_item.classList.add(this.config.menuIcon[i]); |
|
} |
|
|
|
a_item.id = "a_me_" + node.id; |
|
a_item.appendChild(i_item); |
|
// a_item.href = "javascript:;"; |
|
a_item.classList.add("menuIcon"); |
|
//icon added to div |
|
div_item.appendChild(a_item); |
|
} |
|
|
|
li_item.appendChild(div_item); |
|
//set ul tag to li item |
|
li_item.appendChild(ul_item); |
|
|
|
//#endregion |
|
|
|
//if is main node |
|
//check if element is exist for preventing copy elements |
|
if (node.parent.id === 0) { |
|
//put item to area |
|
this.area.appendChild(li_item); |
|
} else { |
|
//if has parent set to parents childs |
|
this.setChildNodes(node); |
|
//then put item |
|
const cont = document.getElementById("c_" + node.parent.id); |
|
if (cont !== null) cont.appendChild(li_item); |
|
} |
|
|
|
//node.element = li_item; |
|
|
|
//set node events |
|
this.setNodeEvents(node, div_item); |
|
|
|
//draw callback method |
|
if (typeof this.rowCreateCallback == "function") this.rowCreateCallback(node); |
|
} |
|
|
|
setNodeEvents(node, parent) { |
|
//order event for node |
|
if (this.config.order) { |
|
document.getElementById('order_' + node.id).addEventListener('click', e => { |
|
if (e.target.tagName == 'I') { |
|
const isBefore = e.target.dataset.target == 1; |
|
const main = e.target.parentNode.parentNode.parentNode; |
|
const target = isBefore ? main.previousElementSibling : main.nextElementSibling; |
|
//get nodes |
|
if (target !== null) { |
|
//replace data |
|
const targetNode = this.getNode(target.id.split("_").at(-1)); |
|
const mainNode = this.getNode(main.id.split("_").at(-1)); |
|
//console.log( mainNode.order,targetNode.order); |
|
const currentOrder = mainNode.order; |
|
const targetOrder = targetNode.order === mainNode.order ? (isBefore ? targetNode.order - 1 : targetNode.order + 1) : targetNode.order; |
|
//change order data |
|
targetNode.order = currentOrder; |
|
mainNode.order = targetOrder; |
|
//console.log( mainNode.order,targetNode.order); |
|
//replace element |
|
main.parentNode.replaceChild(main, target); |
|
main.parentNode.insertBefore(target, isBefore ? main.nextSibling : main); |
|
} |
|
} |
|
|
|
}); |
|
} |
|
} |
|
|
|
/** |
|
* this method will draw multiple data |
|
*/ |
|
drawData() { |
|
//start loading |
|
|
|
//if data is exist |
|
if (this.data.length > 0) { |
|
//first reshape data |
|
let order = (list, p = { |
|
n_id: 0, |
|
Child: [] |
|
}, tree = []) => { |
|
let childrens = list.filter((y) => y.n_parentid === p.n_id); |
|
if (childrens.length > 0) { |
|
// order items by order_num param if exist |
|
childrens.sort((a, b) => parseFloat(a.n_order_num === undefined ? 0 : a.n_order_num) - parseFloat(b.n_order_num === undefined ? 0 : b.n_order_num)); |
|
if (p.n_id === 0) { |
|
tree = childrens; |
|
} else { |
|
p.Child = childrens; |
|
} |
|
for (let i = 0; i < childrens.length; i++) { |
|
order(list, childrens[i]); |
|
} |
|
} |
|
return tree; |
|
}; |
|
|
|
//then create nodes |
|
let set = (list) => { |
|
for (let i = 0; i < list.length; i++) { |
|
this.createNode({ |
|
n_data: list[i], |
|
n_addional: list[i].n_addional, |
|
n_value: list[i].n_id, |
|
n_title: list[i].n_title, |
|
n_id: list[i].n_id, |
|
n_elements: list[i].n_elements, |
|
n_parent: this.getNode(list[i].n_parentid), |
|
n_checkStatus: list[i].n_checkStatus === undefined ? false : list[i].n_checkStatus, |
|
n_order: list[i].n_order_num, |
|
}); |
|
if (list[i].Child) { |
|
set(list[i].Child); |
|
} |
|
} |
|
}; |
|
//start chain |
|
set(order(this.data)); |
|
} |
|
|
|
//start drawcallback |
|
if (this.drawCallback !== undefined) this.drawCallback(); |
|
//end loading |
|
} |
|
|
|
//#endregion |
|
|
|
//#region Menu |
|
// brings up the context menu |
|
getMenu(element, node) { |
|
//get element location |
|
const rect = element.getBoundingClientRect(); |
|
const origin = { |
|
node: node, |
|
left: rect.x, |
|
top: rect.y + rect.height, |
|
}; |
|
//draw menu |
|
this.drawMenu(origin); |
|
} |
|
|
|
drawMenu(obj) { |
|
//check if menu already exist |
|
if (document.getElementById("div_menu_" + obj.node.id) === null) { |
|
//create menu div |
|
const menu_item = document.createElement("div"); |
|
//add to body |
|
document.body.appendChild(menu_item); |
|
menu_item.id = "div_menu_" + obj.node.id; |
|
menu_item.classList.add("ptreemenuCont"); |
|
|
|
//for each menu item |
|
let span_item; |
|
let icon; |
|
for (let i = 0; i < obj.node.elements.length; i++) { |
|
span_item = document.createElement("span"); |
|
span_item.setAttribute("data-node", obj.node.id); |
|
icon = obj.node.elements[i].icon.trim().length > 0 ? '<i class="' + obj.node.elements[i].icon.trim() + '"></i>' : ""; |
|
span_item.innerHTML = icon + " " + obj.node.elements[i].title.trim(); |
|
|
|
menu_item.appendChild(span_item); |
|
|
|
//then add click event |
|
span_item.addEventListener("click", (e) => { |
|
obj.node.elements[i].onClick(this.getNode(e.target.getAttribute("data-node").split("node_")[1])); |
|
//remove menu after click |
|
menu_item.outerHTML = ""; |
|
}); |
|
} |
|
|
|
switch (this.config.contextPos) { |
|
case 'above': |
|
menu_item.offsetPosition = -menu_item.offsetHeight; |
|
break; |
|
case 'after': |
|
menu_item.offsetPosition = menu_item.offsetWidth; |
|
break; |
|
case 'below': |
|
menu_item.offsetPosition = menu_item.offsetHeight; |
|
break; |
|
case 'before': |
|
menu_item.offsetPosition = -menu_item.offsetWidth; |
|
break; |
|
default: |
|
} |
|
|
|
//calculate location |
|
if (screen.width - obj.left < menu_item.offsetWidth) { |
|
menu_item.style.left = obj.left - menu_item.offsetWidth + "px"; |
|
} else { |
|
menu_item.style.left = obj.left + menu_item.offsetPosition + "px"; |
|
} |
|
menu_item.style.top = obj.top + "px"; |
|
|
|
// listen mouse out |
|
menu_item.onmouseleave = (e) => { |
|
menu_item.remove(); |
|
}; |
|
} |
|
} |
|
|
|
|
|
collapseAll() { |
|
for( let key in this.nodeList) { |
|
this.getNode(key).foldedStatus = true; |
|
this.updateNode(this.getNode(key)) |
|
} |
|
} |
|
|
|
uncheckAll() { |
|
this.getSelected().forEach(n => { |
|
n.checkStatus = false; |
|
this.checkNode(n) |
|
}); |
|
} |
|
//#endregion |
|
}
|
|
|