d3条形图,以使用手风琴显示/隐藏子数据加号图标或雪佛龙

问题描述

我已经在salesforce中实现了条形图,如下所示。现在,我想显示/隐藏子数据,如果有任何使用手风琴/加号图标的孩子。我尝试使用d3.hierarchy函数,但没有运气。

d3 bar chart

我想获得上面的图表,当单击+(加号)图标时,该图标应展开并显示子项(如果有),然后再次单击以隐藏子项。

Sample picture for an idea

//function to create Gantt Chart 
d3gantchart: function(component,event,helper,items) {
  let exp_label = $A.get("$Label.c.Legend_expected");
  let act_label = $A.get("$Label.c.Legend_Actual");
  let chart_title = $A.get("$Label.c.Gannt_Chart_Title");

  function formatDate(dt) {
    var year,mon,day;
    year = dt.getFullYear();
    mon = dt.getMonth() + 1;
    day = dt.getDate();
    return year + '-' + mon + '-' + day;
  }

  function createDate(year,day) {
    return new Date(year,day);
  }

  var min_domain = formatDate(createDate(component.get('v.curr_year'),component.get('v.first_month'),1));
  var max_domain = formatDate(createDate(component.get('v.curr_year'),component.get('v.last_month'),0));
  const slicedData = getSlicedDates(items);

  var svg_width = 670;

  var svg_height = window.innerHeight;
  var title_x_height = 16;

  d3.selectAll(".chart").selectAll("svg").remove();
  var svg = d3.selectAll(".chart")
    .append("svg")
    .attr("width",svg_width)
    .attr("height",svg_height)
    .attr("preserveAspectRatio","xMinYMax meet")
    .attr("viewBox",[0,svg_width,svg_height].join(' '))
    .attr("class","svg");

  var dateFormat = d3.timeParse("%Y-%m-%d");
  var timeScale = d3.scaleTime()
    .domain([d3.min(slicedData,function(d) {
        return dateFormat(min_domain);
      }),d3.max(slicedData,function(d) {
        return dateFormat(max_domain);
      })
    ])
    .range([0,svg_width - 180])

  var categories = [];

  for (var i = 0; i < slicedData.length; i++) {
    categories.push({
      Name: slicedData[i].Name,Id: slicedData[i].Id
    });
  }

  var catsUnfiltered = categories;

  makeGant(slicedData,svg_height);

  function makeGant(slicedData,pageWidth,pageHeight) {

    var barHeight = 15;
    var gap = barHeight + 22;
    var topPadding = 40;
    var sidePadding = 172;

    var colorScale = d3.scaleLinear()
      .domain([0,categories.length])
      .range(["#00B9FA","#F95002"])
      .interpolate(d3.interpolateHcl);

    makeGrid(sidePadding,topPadding,pageHeight);
    drawRects(slicedData,gap,sidePadding,barHeight,colorScale,pageHeight);
    vertLabels(gap,colorScale);
  }


  function drawRects(slicedData,theGap,thetopPad,theSidePad,theBarHeight,theColorScale,width,height) {

    var rectangles = svg.append('g')
      .selectAll("rect")
      .data(slicedData)
      .enter();

    // draw legend group
    var legend = svg.selectAll("g")
      .data(slicedData)
      .enter().append("g")
      .attr("class","legend");

    // draw legend rect
    legend.append("rect")
      .attr("x",width - 200)
      .attr("y",title_x_height + 18)
      .attr("rx",8)
      .attr("ry",8)
      .attr("width",30)
      .attr("height",theBarHeight)
      .style("fill","#78ADD2");

    // draw legend text
    legend.append("text")
      .attr("x",width - 106)
      .attr("y",title_x_height + 26)
      .attr("dy",".35em")
      .style("text-anchor","end")
      .attr("font-size",14)
      .text(exp_label);

    // draw legend colored rectangles
    legend.append("rect")
      .attr("x",width - 95)
      .attr("y",theBarHeight)
      //.attr("background","#0d053d")
      .style("fill","#0072AA");

    // draw legend text
    legend.append("text")
      .attr("x",width - 21)
      .attr("y",14)
      .text(act_label);

    //draw rectangles for expected date range
    var expRects = rectangles.append("rect")
      .attr("rx",8)
      .attr("x",function(d) {
        return timeScale(dateFormat(d.expStartTime)) + theSidePad;
      })
      .attr("y",function(d,i) {
        return i * theGap + thetopPad + title_x_height + 50;
      })
      .attr("width",function(d) {
        return (timeScale(dateFormat(d.expEndTime)) - timeScale(dateFormat(d.expStartTime)));
      })
      .attr("height",theBarHeight)
      .attr("stroke","none")
      .attr("fill","#78ADD2")

    //draw rectangles for Actual date range
    var actualRects = rectangles.append("rect")
      .attr("rx",function(d) {
        return timeScale(dateFormat(d.actualStartTime)) + theSidePad;
      })
      .attr("y",function(d) {
        return (timeScale(dateFormat(d.actualEndTime)) - timeScale(dateFormat(d.actualStartTime)));
      })
      .attr("height","#0072AA")

    rectangles.append("rect")
      .attr("rx",0)
      .attr("ry",0)
      .attr("x",-0)
      .attr("y",title_x_height + 10)
      .attr("width",670)
      .attr("height",30)
      .attr("fill","#f2f0ed")

    //display Current year
    svg.append("text")
      .attr('transform','translate(360' + ',' + (title_x_height + 30) + ')')
      .attr("font-size",16)
      .attr("font-weight","bold")
      .style("text-anchor","middle")
      .text(component.get('v.curr_year'));

    rectangles.append("rect")
      .attr("rx",title_x_height + 40)
      .attr("width",40)
      .attr("fill","#d6d2ce")
      .attr("opacity",0.1);

    //decrement Current year
    d3.select("svg").append("g").append("foreignObject")
      .attr("width",30)
      .attr('transform','translate(250' + ',' + (title_x_height + 10) + ')')
      .append("xhtml:a")
      .append('xhtml:i').attr('class','arrow left').on("click",function() {
        component.set('v.curr_year',component.get('v.curr_year') - 1);
        component.set('v.first_month',0);
        component.set('v.last_month',6);
        helper.d3gantchart(component,items);
      })
      .attr("height","12px")
      .attr("width","12px");


    //Increment Current year
    d3.select("svg").append("g").append("foreignObject")
      .attr("width",'translate(420' + ','arrow right').on("click",component.get('v.curr_year') + 1);
        component.set('v.first_month',"12px");

    //decrement the month
    d3.select("svg").append("g").append("foreignObject")
      .attr("width",40)
      .attr("height",40)
      .attr('transform','translate(20' + ',' + (title_x_height + 45) + ')')
      .append("xhtml:a")
      .append('xhtml:i').attr('class','arrow_mon left_mon').on("click",component.get('v.first_month') === 0 ? component.get('v.curr_year') - 1 : component.get('v.curr_year'));
        component.set('v.first_month',component.get('v.first_month') === 0 ? 6 : 0);
        component.set('v.last_month',component.get('v.last_month') === 6 ? 12 : 6);
        helper.d3gantchart(component,"12px");

    //Increment the month
    d3.select("svg").append("g").append("foreignObject")
      .attr("width",'translate(610' + ',' + (title_x_height + 43) + ')')
      .append("xhtml:a")
      .append('xhtml:i').attr('class','arrow_mon right_mon').on("click",component.get('v.last_month') === 12 ? component.get('v.curr_year') + 1 : component.get('v.curr_year'));
        component.set('v.first_month',component.get('v.first_month') == 0 ? 6 : 12);
        helper.d3gantchart(component,items);
      })

    // Actual rect mouSEOver tool-tip
    actualRects.on('mouSEOver',function(e) {
      var tag = "";
      if (e != undefined) {
        tag = "<b>Project Title</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:" + e.Name + "<br/>" +
          "<b>Act Start Date</b>&nbsp;:" + e.actualStartTime + "<br/>" +
          "<b>Act End Date</b>&nbsp;&nbsp;:" + e.actualEndTime;
      }
      var output = document.getElementById("tag");
      var i;
      for (i = 0; i < output.length; i++) {
        output[i].style.display = 'none';
      }
      var x = (this.x.animVal.value + this.width.animVal.value / 4) + "px";
      var y = this.y.animVal.value + 25 + "px";
      output.innerHTML = tag;
      output.style.top = d3.event.layerY + "px";
      output.style.left = d3.event.layerX + "px";
      output.style.display = "block";
    }).on('mouSEOut',function() {
      var output = document.getElementById("tag");
      output.style.display = "none";
    });

    // Expected rect mouSEOver tool-tip
    expRects.on('mouSEOver',function(e) {
      var tag_exp = "";
      if (e != undefined) {
        tag_exp = "<b>Project Title</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:" + e.Name + "<br/>" +
          "<b>Exp Start Date</b>&nbsp;:" + e.expStartTime + "<br/>" +
          "<b>Exp End Date</b>&nbsp;&nbsp;:" + e.expEndTime;
      }
      var output = document.getElementById("tag");
      var i;
      for (i = 0; i < output.length; i++) {
        output[i].style.display = 'none';
      }
      var x = (this.x.animVal.value + this.width.animVal.value / 4) + "px";
      var y = this.y.animVal.value + 25 + "px";
      output.innerHTML = tag_exp;
      output.style.top = d3.event.layerY + "px";
      output.style.left = d3.event.layerX + "px";
      output.style.display = "block";
    }).on('mouSEOut',function() {
      var output = document.getElementById("tag");
      output.style.display = "none";
    });
  }


  function makeGrid(theSidePad,height) {

    var xAxis = d3.axisBottom(timeScale)
      .ticks(d3.timeMonth)
      .tickFormat(d3.timeFormat("%b"));

    var grid = svg.append('g')
      .attr('class','grid')
      .attr('transform','translate(' + theSidePad + ',' + (title_x_height + 40) + ')')
      .call(xAxis)
      .selectAll("text")
      .style("text-anchor","middle")
      .attr("fill","#FF0000")
      .attr("font-size","bold")
      .attr("dy","1em");
  }

  function vertLabels(theGap,theColorScale) {
    var numOccurances = [];
    var prevGap = 0;

    for (var i = 0; i < categories.length; i++) {
      numOccurances[i] = [categories[i],getCount(categories[i],catsUnfiltered)];
    }

    // Titles of the Projects
    var axisText = svg.append("g").attr("class","entityLink")
      .selectAll("text")
      .data(slicedData)
      .enter()
      .append("text")
      .text(function(d) {
        let name = d["Name"].length > 10 ? d["Name"].substring(0,10) + '...' : d["Name"];
        return name;
      })
      .attr("x",31)
      .attr("y",i) {
        console.log('theGap :',' thetopPad :',' title_x_height :',title_x_height);
        return i * theGap + thetopPad + title_x_height + 63;
      })
      .attr("font-size",20)
      .attr("font-weight","bold")
      .attr("text-anchor","start")
      .attr("fill","#006dcc")
      .style("cursor","pointer").style("text-decoration","underline");

    *****// (+) Accordion for the projects
    svg.append("g")
      .selectAll("text")
      .data(slicedData)
      .enter()
      .append("foreignObject").attr("width",20).attr("height",20).attr("x",10).attr("y",i) {
        return i * theGap + thetopPad + title_x_height + 45;
      }).append('xhtml:button').attr('class','accordion').on("click",function() {})*****

    // Fire salesforce event on mouse click
    d3.selectAll(".entityLink text")
      .on("click",$A.getCallback(function(d,i) {
        var clickevt = $A.get('e.c:GanttChartAppEvent');
        console.log('click 2 => ',d);
        clickevt.setParams({
          "projects": d.Id,"projectdata": component.get("v.projectsData")
        });
        clickevt.fire();
      }));

    // Project title mouSEOver tool-tip
    d3.selectAll(".entityLink text")
      .on('mouSEOver',function(e) {
        d3.select(this).style("stroke","#006dcc");
        var tag_exp = "";
        if (e != undefined) {
          tag_exp = "<b>Project Title</b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:" + e.Name + "<br/>" +
            "<b>Exp Start Date</b>&nbsp;:" + e.expStartTime + "<br/>" +
            "<b>Exp End Date</b>&nbsp;&nbsp;:" + e.expEndTime;
        }
        var element = document.getElementById("tag");
        var i;
        for (i = 0; i < element.length; i++) {
          element[i].style.display = 'none';
        }
        element.innerHTML = tag_exp;
        element.style.top = d3.event.layerY + "px";
        element.style.left = d3.event.layerX + "px";
        element.style.display = "block";
      }).on('mouSEOut',function() {
        var element = document.getElementById("tag");
        element.style.display = "none";
        d3.select(this).style("stroke","none");
      })

  }

  function checkUnique(arr) {
    var hash = {},result = [];
    for (var i = 0,l = arr.length; i < l; ++i) {
      if (!hash.hasOwnProperty(arr[i])) { //it works with objects! in FF,at least
        hash[arr[i]] = true;
        result.push(arr[i]);
      }
    }
    return result;
  }

  function getCounts(arr) {
    var i = arr.length,// var to loop over
      obj = {}; // obj to store results
    while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
    return obj;
  }

  // get specific from everything
  function getCount(word,arr) {
    return getCounts(arr)[word] || 0;
  }

  //Slicing the dates for the 6 months range; When month or year changed (i.e.,Paginated) will show next set of six months data
  function getSlicedDates(data) {
    const adjustedData = data.map(item => {
      let temp_obj = {
        "Id": item.Id,"Name": item.Name,"expStartTime": item.expStartTime,"expEndTime": item.expEndTime,"actualStartTime": item.actualStartTime,"actualEndTime": item.actualEndTime
      };

      if (new Date(item.expStartTime) <= new Date(min_domain)) {
        if (new Date(item.expEndTime) >= new Date(min_domain)) {
          temp_obj['expStartTime'] = min_domain;
        }
      }
      if (new Date(item.expStartTime) <= new Date(min_domain)) {
        if (new Date(item.expEndTime) >= new Date(max_domain)) {
          temp_obj['expStartTime'] = min_domain;
          temp_obj['expEndTime'] = max_domain;
        }
        if (new Date(item.expEndTime) <= new Date(max_domain)) {
          temp_obj['expStartTime'] = min_domain;
        }
      }
      if (new Date(item.expStartTime) <= new Date(max_domain)) {
        if (new Date(item.expEndTime) >= new Date(max_domain)) {
          temp_obj['expEndTime'] = max_domain;
        }
      }
      if (new Date(item.actualStartTime) <= new Date(min_domain)) {
        if (new Date(item.actualEndTime) >= new Date(min_domain)) {
          temp_obj['actualStartTime'] = min_domain;
        }
      }
      if (new Date(item.actualStartTime) <= new Date(min_domain)) {
        if (new Date(item.actualEndTime) >= new Date(max_domain)) {
          temp_obj['actualStartTime'] = min_domain;
          temp_obj['actualEndTime'] = max_domain;
        }
        if (new Date(item.actualEndTime) <= new Date(max_domain)) {
          temp_obj['actualStartTime'] = min_domain;
        }
      }
      if (new Date(item.actualStartTime) <= new Date(max_domain)) {
        if (new Date(item.actualEndTime) >= new Date(max_domain)) {
          temp_obj['actualEndTime'] = max_domain;
        }
      }
      return temp_obj;
    })
    return adjustedData;
  }

}
<div class="slds-grid">
  <div class="container">
    <div class="chart"></div>
    <div Id="tag"></div>
  </div>
</div>

感谢您的帮助!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)