Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import * as d3 from 'd3';
- class Tree {
- constructor(id, treeData, opts) {
- this.opts = opts || {};
- this.data = treeData || {};
- this.id = id || console.error('ID is required!');
- this.elem = document.getElementById(this.id);
- this.i = 0;
- this.margin = {
- top: 20,
- right: 120,
- bottom: 20,
- left: 120
- };
- this.node_attribute = {
- width: 50,
- height: 100
- };
- this.translation = opts.translationKeys;
- this.duration = 500;
- this.radius = opts.radius || 30;
- this.viewerWidth = parseFloat(getComputedStyle(this.elem, null).width.replace("px", "")); //$(this.id).width();
- this.viewerHeight = window.innerHeight - this.elem.getBoundingClientRect().top;
- this.rootStartPosX = this.viewerWidth * 0.5;
- this.rootStartPosY = this.viewerHeight * 0.2;
- this.direction = opts.direction || 'v'; //v= vertical, h = horizontal
- this.ease = opts.ease || 'easeSin';
- this.defaultDisplayLevel = opts.defaultDisplayLevel || 1;
- this.line = opts.line || 'c';
- this.user_var = opts.user_var || 'user_id';
- this.lavel_var = opts.lavel_var || 'rank';
- this.level_icons = opts.level_icons || null;
- /*
- "easeElastic",
- "easeBounce",
- "easeLinear",
- "easeSin",
- "easeQuad",
- "easeCubic",
- "easePoly",
- "easeCircle",
- "easeExp",
- "easeBack"*/
- this.treemap = d3.tree()
- .nodeSize([this.node_attribute.height, this.node_attribute.height])
- this.div = d3.select(this.elem)
- .append("div")
- .attr("class", "tooltip")
- .style("padding", "10px")
- .style("opacity", 0)
- .style("right", 50 + "px")
- .style("top", 10 + "px");
- this.viewportHeight = Math.max(500, this.viewerHeight + this.margin.top + this.margin.bottom);
- this.setHeight(this.elem, this.viewportHeight);
- this.svg = d3.select(this.elem)
- .append("svg")
- .attr("width", '100%')
- .attr("height", this.viewportHeight);
- this.arc = d3.arc();
- this.zoomLayer = this.svg.append("g")
- .attr("transform", "translate(" + this.rootStartPosX + ", " + this.rootStartPosY + ")");
- // resize svg
- $(window).resize(() => {
- this.viewerWidth = parseFloat(getComputedStyle(this.elem, null).width.replace("px", ""));
- this.svg.attr("width", this.viewerWidth)
- });
- this.root = d3.hierarchy(treeData);
- this.root.x = this.viewerHeight / 2;
- this.root.x0 = this.viewerHeight / 2;
- this.root.descendants().forEach((d, i) => {
- d.id = i;
- d._children = d.children;
- if (d.depth && d.depth >= this.defaultDisplayLevel) d.children = null;
- });
- this.update(this.root);
- }
- init(d) {
- if (d.children) {
- d.children.forEach(this.collapse);
- }
- }
- collapse(d) {
- if (d._children) {
- d._children.forEach(this.collapse);
- }
- if (d.children) {
- d._children = d.children;
- d._children.forEach(this.collapse);
- d.children = null;
- }
- }
- update(source = null) {
- var parent = this;
- // Assigns the x and y position for the nodes
- var nodes_data = this.treemap(this.root);
- // Compute the new tree layout.
- var nodes = nodes_data.descendants(),
- links = nodes_data.descendants().slice(1)
- // Normalize for fixed-depth.
- nodes.forEach(function (d) {
- d.y = d.depth * 180;
- })
- // ****************** Nodes section ***************************
- // Update the nodes...
- var node = this.zoomLayer.selectAll('g.node')
- .data(nodes, function (d) {
- return d.id || (d.id = ++this.i);
- })
- // Enter any new nodes at the parent's previous position.
- var nodeEnter = node.enter().append('g')
- .attr('class', 'node')
- .attr("transform", (d) => {
- if(this.direction == 'h'){
- return "translate(" + source.y +"," + source.x + ")";
- }else{
- return "translate(" + source.x + "," + source.y + ")";
- }
- })
- .on('click touch', this.click)
- .on("mouseenter", function (d) {
- parent.onMouseOver(this, d)
- })
- .on("mousemove", this.move_tooltip)
- .on("mouseout", function () {
- // don't hide tooltip when hover on child elements
- var e = event.toElement || event.relatedTarget;
- if (e && (e == this || (e.parentNode && e.parentNode == this ))) {
- return;
- } else {
- parent.hide_tooltip()
- d3.select(this).classed("active", false);
- }
- });
- // Add design for the nodes
- this.drawchild(nodeEnter)
- // Add tooltip for the nodes
- // nodeEnter.append("foreignObject")
- // .attr("width", 20)
- // .attr("height", 20)
- // .attr("x", function (d) { return (node_attribute.width / 2) - 20 })
- // .attr("y", 0)
- // .attr("opacity", 1)
- // .attr("class", "tooltip-info")
- // UPDATE
- var nodeUpdate = node.merge(nodeEnter)
- this.updateChild(nodeUpdate);
- // Remove any exiting nodes
- var nodeExit = node.exit()
- .transition()
- .duration(this.duration)
- .ease(d3[this.ease])
- .attr("transform", (d)=> {
- if(this.direction == 'h'){
- return "translate(" + source.y + "," + source.x + ")";
- }else{
- return "translate(" + source.x + "," + source.y + ")";
- }
- })
- .remove();
- // On exit reduce the node circles size to 0
- nodeExit
- .select("circle, path, text")
- .style('opacity', 1e-6)
- // ****************** links section ***************************
- // Update the links...
- var link = this.zoomLayer.selectAll('path.tree-link')
- .data(links, function (d) {
- return d.id
- });
- // Enter any new links at the parent's previous position.
- var linkEnter = link.enter().insert('path', "g")
- .attr("class", "tree-link")
- .attr('d', (d) => {
- // var o = { x: source.x0, y: source.y0 }
- return this.drawConnection(d.parent, d.parent)
- })
- // UPDATE
- var linkUpdate = linkEnter.merge(link)
- // Transition back to the parent element position
- linkUpdate.transition()
- .duration(this.duration)
- .ease(d3[this.ease])
- .attr('d', (d) => {
- return this.drawConnection(d, d.parent)
- })
- // Remove any exiting links
- var linkExit = link.exit()
- .transition()
- .duration(this.duration)
- .ease(d3[this.ease])
- .attr('d', (d) => {
- return this.drawConnection(source, source)
- })
- .remove()
- // Store the old positions for transition.
- nodes.forEach(function (d) {
- d.x0 = d.x
- d.y0 = d.y
- })
- this.svg.call(d3.zoom()
- .scaleExtent([1 / 2, 12])
- .on("zoom", this.zoomed))
- }
- onMouseOver = (node, d) => {
- d3.select(node).classed("active", true);
- this.show_tooltip(d);
- }
- onMouseOut = (e, node) => {
- if (e.parentNode == node || e == node) {
- return;
- } else {
- this.hide_tooltip()
- d3.select(node).classed("active", false);
- }
- }
- drawchild = (node) => {
- if(this.level_icons){
- node
- .append("g")
- .attr("transform", "translate(" + -this.radius * 1.8 / 2 + ", 0)")
- .append("svg:image")
- .attr("xlink:href", (d)=>{
- if(d.data[this.lavel_var] && this.level_icons[d.data[this.lavel_var]]){
- return this.level_icons[d.data[this.lavel_var]];
- }else{
- return this.level_icons[Object.keys(this.level_icons)[0]];
- }
- })
- .attr("width", this.radius * 1.8)
- .attr("height", this.radius * 1.8);
- }else if (this.opts.customChild) {
- node
- .append("g")
- .attr("transform", "translate(" + -this.radius * 1.8 / 2 + ", 0)")
- .append("svg:image")
- .attr("xlink:href", (d)=>{
- return (d.data.children_count > 0) ? this.opts.customChildMultiple : this.opts.customChild;}
- )
- .attr("width", this.radius * 1.8)
- .attr("height", this.radius * 1.8);
- } else {
- node
- .append('path')
- .attr("transform", "translate( 0 ," + this.radius * 1.8 + ")")
- .attr("class", function (d) {
- return ( d.data.children_count > 0) ? "people_body_leader" : "people_body";
- })
- .attr('d', this.arc({
- innerRadius: 0,
- outerRadius: this.radius * 1.2,
- startAngle: -Math.PI * 0.5,
- endAngle: Math.PI * 0.5
- }));
- node
- .append("circle")
- .attr("r", this.radius)
- .attr("class", function (d) {
- return ( d.data.children_count > 0) ? "people_head_leader" : "people_head";
- })
- .attr('cursor', function (d) {
- return ( d.data.children_count > 0) ? "pointer" : "default";
- })
- }
- // Add labels for the nodes
- node.append('text')
- .attr("dy", this.radius * 1.75 + 15)
- .attr("text-anchor", "middle")
- .attr("width", 100)
- .attr("height", 50)
- .attr("class", "people_text")
- .text((d) => {
- if (this.opts.displayname && d.data[this.opts.displayname]) {
- var text = d.data[this.opts.displayname],
- count = text.length,
- limit = 9
- if (count > limit) text = text.slice(0, limit) + ".."
- return text;
- }
- return '';
- })
- .style('opacity', 1);
- return node;
- }
- updateChild = (nodeUpdate) =>{
- // Transition to the proper position for the node
- nodeUpdate
- .transition()
- .duration(this.duration)
- .ease(d3[this.ease])
- .attr("transform", (d)=> {
- if(this.direction == 'h'){
- return "translate(" + d.y + "," +(parseInt(d.x) - this.radius) + ")";
- }else{
- return "translate(" + d.x + "," + d.y + ")";
- }
- })
- .attr('cursor', function (d) {
- return (_.isEmpty(d.children) && d.data.children_count > 1) ? "pointer" : "default";
- });
- // if(this.opts.customChild){
- // if(this.opts.customChildMultiple){
- // nodeUpdate
- // .select("image")
- // .attr("xlink:href", (d)=> {
- // return (d.data.children_count > 0) ? this.opts.customChildMultiple : this.opts.customChild;
- // })
- // }
- // }else{
- // this.drawchild(nodeUpdate);
- nodeUpdate
- .select('path')
- .attr("transform", "translate( 0 ," + this.radius * 1.8 + ")")
- // .attr("class", function (d) {
- // return (_.isEmpty(d.children) && d.data.children_count > 0) ? "people_body_leader" : "people_body";
- // })
- .attr('d', this.arc({
- innerRadius: 0,
- outerRadius: this.radius * 1.2,
- startAngle: -Math.PI * 0.5,
- endAngle: Math.PI * 0.5
- }));
- // Update the node attributes and style
- nodeUpdate
- .select('circle')
- .attr("r", this.radius)
- // .attr("class", function (d) {
- // return (_.isEmpty(d.children) && d.data.children_count > 0) ? "people_head_leader" : "people_head";
- // })
- .attr('cursor', function (d) {
- return (_.isEmpty(d.children) && d.data.children_count > 0) ? "pointer" : "default";
- })
- // }
- }
- drawConnection = (node, parent) => {
- if (this.direction == 'h') {
- if(this.line == 'c'){
- return this.diagonalHorizontal({source: node, target: parent});
- }else{
- return this.elbowHorizontal(node, parent);
- }
- }else{
- if(this.line == 'c'){
- return this.diagonalVertical({source: node, target: parent});
- }else{
- return this.elbowVertical(node, parent);
- }
- }
- }
- elbowHorizontal(d, i) {
- return "M" + d.y + "," + d.x +
- "H" + (((d.y - i.y) / 2) + i.y) +
- "V" + i.x + "H" + i.y;
- }
- elbowVertical(i,d) {
- return "M" + d.x + "," + (d.y+this.radius) +
- "V" + (((d.y - i.y) / 2) + i.y+this.radius) +
- "H" + i.x + "V" + (i.y+this.radius);
- }
- /*
- diagonal(s, d) {
- return `M ${s.x} ${s.y}
- C ${s.x} ${(s.y + d.y) / 2},
- ${d.x} ${(s.y + d.y) / 2},
- ${d.x} ${d.y}`;
- }*/
- diagonalHorizontal = d3.linkHorizontal().x(d => d.y).y(d => d.x)
- diagonalVertical = d3.linkVertical().x(d => d.x).y(d => d.y+this.radius)
- zoomed = () => {
- // this.zoomLayer.attr("transform", d3.event.transform)
- var data = d3.event.transform
- this.zoomLayer.attr("transform", "translate(" + (data.x + (this.rootStartPosX * data.k)) + "," + (data.y + (this.rootStartPosY * data.k)) + ")scale(" + data.k + ")");
- }
- populate_tooltip_content = (data) => {
- if (this.opts.tooltip) {
- const tooltipDetail = this.opts.tooltip;
- let content = "";
- for (let [key, value] of Object.entries(tooltipDetail)) {
- if (data[value]) {
- content += this.translation[key] + ": " + data[value] + "<br/>"
- }
- }
- if (content != "") {
- this.div.html("<div>" + content + "</div>")
- .style("padding", "10px")
- .style("width", 'auto')
- .style("height", 'auto')
- } else {
- this.div.html("<div>" + this.translation.no_record + "</div>")
- .style("padding", "10px")
- .style("width", 'auto')
- .style("height", 'auto')
- }
- } else {
- this.div.style('opacity', 0);
- }
- }
- show_tooltip(d) {
- //console.log(d, d.data);
- if (this.opts.urlDetail && d.data[this.user_var]) {
- const url = this.opts.urlDetail.replace(':id', d.data[this.user_var]);
- //const url = opts.treeType == 'sponsor' ? 'sponsor-tree' : 'placement-tree';
- // d3.json('/sponsor-tree/' + `${d.data.user_id}` + '/details')
- //axios.get(`/${url}/${d.data.user_id}/details`)
- //d3.json(opts.urlDetail)
- axios.get(url)
- .then((response) => {
- this.populate_tooltip_content(response.data.data);
- })
- .catch(err => {
- console.log(err);
- });
- } else {
- this.populate_tooltip_content(d.data);
- }
- }
- move_tooltip = () => {
- this.div.style('opacity', 1);
- /*var event = d3.event;
- var posX = event.pageX - $(id).offset().left + 50,
- posY = event.pageY - $(id).offset().top + 15;
- div
- .style('opacity', 1)
- .style("left", posX + "px")
- .style("top", posY + "px");*/
- }
- hide_tooltip = () => {
- this.div.style('opacity', 0);
- /*div
- .style('opacity', 0)
- .html("")
- .style("padding", 0)
- .style("left", 0)
- .style("top", 0)
- .style("width", 0)
- .style("height", 0)*/
- }
- click = async (d) => {
- // if (div.style('opacity') == 0) hide_tooltip();
- if (d.children) {
- d._children = d.children;
- d.children = null;
- this.update(d);
- } else {
- const parent = this;
- if (!d.children && !d._children && d.data.children_count > 0 && this.opts.url && d.data[this.user_var]) {
- const url = this.opts.url.replace(':id', d.data[this.user_var]);
- //await d3.json(`${opts.url}/${d.data.user_id}`)
- //await axios.get(`${opts.url}/${d.data.user_id}`)
- //d3.json(`${opts.url}`)
- await axios.get(url)
- .then(function (data) {
- let child = [];
- d.children = []
- d.data.children = []
- const dataChild = data.data.data.children;
- dataChild.forEach(function (i) {
- child = d3.hierarchy(i);
- child.depth = d.depth + 1;
- child.height = d.height - 1;
- child.parent = d;
- d.children.push(child);
- d.data.children.push(child.data);
- parent.update(d);
- })
- })
- .catch(function (error) {
- let errMsg = ""
- if (error.response.data.errors) {
- const err = error.response.data.errors;
- for (var prop in err) {
- if (err.hasOwnProperty(prop)) {
- console.log(prop + " = " + err[prop]);
- errMsg += err[prop] + "<br>";
- }
- }
- } else {
- errMsg = error.response.data.message;
- }
- CreateNoty({
- 'text': errMsg,
- 'type': 'error'
- });
- })
- }else {
- d.children = d._children;
- d._children = null;
- this.update(d);
- }
- }
- // this.update(d);
- }
- setHeight(el, val) {
- if (typeof val === "function") val = val();
- if (typeof val === "string") el.style.height = val;
- else el.style.height = val + "px";
- }
- }
- export default Tree
- /**
- * How to use
- * Tree('tree-vertical',data,{
- customChild:"http://www.clker.com/cliparts/1/4/5/a/1331068897296558865Sitting%20Racoon.svg", //for custom display logo (optional)
- customChildMultiple: "https://imgc.apk.tools/300/d/3/1/com.leomaz.flix.png", //for custom display logo have downline (optional)
- radius: 15,
- url: "{{route('admin.sponsor.children',':id')}}", //for custom show the children list
- displayname: "nickname", // text for display
- urlDetail: "{{route('admin.sponsor.details',':id')}}", //for display user detail (optional)
- translationKeys: {
- nickname: '{{ __("a_sponsor.nickname") }}',
- total: '{{ __("a_sponsor.total") }}',
- rank: '{{ __("a_sponsor.rank") }}',
- shareholder: '{{ __("a_sponsor.shareholder") }}',
- allocated_amount: '{{ __("a_sponsor.allocated_amount") }}',
- downlines_allocated_amount: '{{__("m_sponsor.downline allocated amount")}}',
- account_status: '{{__("m_sponsor.account status")}}',
- no_record:'{{ __("a_sponsor.no_record") }}'
- },
- tooltip: {
- nickname: "nickname",
- total: "total_package",
- rank: "rank",
- shareholder: "total_shareholder",
- allocated_amount: "allocated_amount",
- downlines_allocated_amount: "downlines_allocated_amount",
- account_status: "account_status"
- },
- direction: "v",//default v= vertical, h = horizontal (optional),
- ease: "",// easeElastic,easeBounce,easeLinear,easeSin,easeQuad,easeCubic,easePoly,easeCircle,easeExp,easeBack
- defaultDisplayLevel: 1,
- line: 's', //s=stright, c=cave
- user_var: 'user_id',
- lavel_var:'rank',
- level_icons:{
- 1:"{{asset('img/Childless.png')}}",
- 2:"{{asset('img/Childful.png')}}",
- },
- })
- */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement