amCharts (5) Force-Directed Tree legend background transparent (overlaps content unnecessarily)

37 Views Asked by At

Problem: When using a Legend in a Force-Directed Tree, it overlaps content unnecessarily. How can I make the background transparent?

Demo: https://codepen.io/DavidDunham/pen/KKEeMPX (the code snipped on Stackoverflow does not seem to work hence this link)

// https://www.amcharts.com/docs/v5/charts/hierarchy/#Legend
var legend = container.children.push(
  am5.Legend.new(root, {
    centerX: am5.p100,
    x: am5.p100,
    layout: root.verticalLayout,
    clickTarget: 'none'
  })
);

enter image description here

console.clear();

var chartData = [
    {
        "name": "Root",
        "value": 0,
        "children": [
            
            {
                "name": "Rechtlicher Hintergrund",
                "shortName": "Legal",
                "radius": 90,
                "children": [
                    {
                        "hidden": true,
                        "radius": 90,
                        "action": "chip",
                        "list": [
                            "[underline]IDType[/]",
                            "Identitätskarte",
                            "??BREAK??",
                            "[underline]IDAuthority[/]",
                            "Kanton Bern"
                        ],
                        "_list": [
                            "Steuer-ID: XXX",
                            "Ausweisnummer: XXX",
                            "IDExpiryDate: 11.11.1111",
                            "IDType: TYPE",
                            "IDIssueDate: XXX",
                            "IDAuthority: XXX"
                        ]
                    }
                ]
            },
            {
                "name": "Vermögen",
                "shortName": "Vermögen",
                "radius": 90,
                "children": [
                    {
                        "hidden": true,
                        "action": "table",
                        "data": [
                            {
                                "sizes": [
                                    15,
                                    5,
                                    10,
                                    5
                                ],
                                "align": [
                                    0,
                                    0,
                                    1,
                                    1
                                ]
                            },
                            [
                                "[underline]Vermögen[/]"
                            ],
                            [
                                "??BREAK??"
                            ],
                            [
                                "Depot",
                                "Bank",
                                "Assets",
                                "YTD"
                            ],
                            [
                                "DISPL.CS-001_SIERRA_CHF",
                                "CREDIT SUISSE ZH",
                                "5’448’508",
                                "+0.06"
                            ],
                            [
                                "UBS-001_SIERRA_CHF",
                                "UBS",
                                "3’904’721",
                                "-0.17"
                            ],
                            [
                                "JB_001_SIERRA_CHF",
                                "JULIUS BAER",
                                "511’320",
                                "-0.07"
                            ],
                            [
                                "",
                                "",
                                "9’864’549"
                            ]
                        ]
                    },
                    {
                        "hidden": true,
                        "action": "table",
                        "data": [
                            {
                                "sizes": [
                                    15,
                                    10,
                                    10
                                ],
                                "align": [
                                    0,
                                    1,
                                    1
                                ]
                            },
                            [
                                "[underline]Performance[/]"
                            ],
                            [
                                "??BREAK??"
                            ],
                            [
                                "Total Vermögen",
                                "29.12.2023",
                                "9’868’671"
                            ],
                            [
                                "Mittelfluss",
                                "YTD 2024",
                                "0"
                            ],
                            [
                                "Total Vermögen",
                                "19.01.2024",
                                "9’864’549"
                            ],
                            [
                                "Erfolg in CHF",
                                "YTD 2024",
                                "-4’121"
                            ],
                            [
                                "Erfolg in %",
                                "YTD 2024",
                                "-0.04"
                            ]
                        ]
                    }
                ]
            },
            {
                "name": "CRM Todos",
                "shortName": "CRM Todos",
                "radius": 90,
                "children": [
                    {
                        "hidden": true,
                        "action": "table",
                        "data": [
                            {
                                "sizes": [
                                    4,
                                    10,
                                    10,
                                    5
                                ],
                                "align": [
                                    0,
                                    0,
                                    0,
                                    1
                                ]
                            },
                            [
                                "[underline]CRM Feeds aktuell[/]"
                            ],
                            [
                                "??BREAK??"
                            ],
                            [
                                "ID",
                                "Betreff",
                                "Text",
                                "Wann"
                            ],
                            [
                                4408,
                                "test 99",
                                "test 99",
                                "18.09.2023 12:19"
                            ],
                            [
                                4407,
                                "test 77",
                                "test 77",
                                "18.09.2023 11:59"
                            ],
                            [
                                4406,
                                "test 55",
                                "test 55",
                                "18.09.2023 11:58"
                            ],
                            [
                                4405,
                                "test",
                                "test",
                                "18.09.2023 11:44"
                            ]
                        ]
                    }
                ]
            }
        ]
    }
];

console.log("chartData", JSON.stringify(chartData) );

// Create root element
// https://www.amcharts.com/docs/v5/getting-started/#Root_element
var root = am5.Root.new("chartdiv");

// Set themes
// https://www.amcharts.com/docs/v5/concepts/themes/
root.setThemes([am5themes_Animated.new(root)]);

// Create wrapper container
var container = root.container.children.push(
  am5.Container.new(root, {
    width: am5.percent(100),
    height: am5.percent(100),
    layout: root.horizontalLayout
  })
);

// Create series
// https://www.amcharts.com/docs/v5/charts/hierarchy/#Adding
var series = container.children.push(
  am5hierarchy.ForceDirected.new(root, {
    singleBranchOnly: false,
    topDepth: 1,
    downDepth: 1,
    initialDepth: 2,
    valueField: "value",
    categoryField: "name",
    childDataField: "children",
    fillField: "color",
    centerStrength: 0.8,
    //minRadius: 30,
    //maxRadius: am5.percent(10),
    legendLabelText: '[#FFFFFF fontSize: 20px] {shortName}'
  })
);

// Make labels wrapped
series.labels.template.setAll({
  oversizedBehavior: "wrap",
  minScale: 0.5,
  textAlign: "center",
  fontSize: 18,
  fill: am5.color(0x000000),
  isMeasured: true
});

//
//
//
//
//
//
// Set up nodes
// - Tooltip text
// - Disable toggling
series.nodes.template.setAll({
  tooltipText: "{category}",
  toggleKey: "none",
  cursorOverStyle: "default",
  layout: root.verticalLayout
});

// https://www.amcharts.com/docs/v4/tutorials/different-tooltip-content-per-each-level-of-force-directed-nodes/
// series.nodes.template.tooltipText = "{name}: {value}";
//console.log('tooltip', series.nodes.template);
series.nodes.template.adapters.add('tooltipText', function(text, target) {
  console.log('tooltip?', target);
  return null; // disable all tooltips
  
  if (target.dataItem) {
    switch(target.dataItem.level) {
      case 0:
        return "#1: {name}";
      case 1:
        return "#2: {parent.name} > {name} ({value})";
      case 2:
        return "#3: {parent.parent.name} > {parent.name} > {name} ({value})";
    }
  }
  return text;
});


// alter position for bezugspersonen
series.labels.template.adapters.add("y", function (y, target) {
  var dataItem = target.dataItem;
  var dataContext = target.dataItem.dataContext;
  if ("action" in dataContext) {
    if (dataContext.action == "icon") {
      console.log("y dataItem", dataItem);
      console.log("y dataContext", dataContext);
      return 40;
    }
  }
});

// Add buttons to "actionable" nodes
series.nodes.template.setup = function (target) {
  target.events.once("dataitemchanged", function (ev) {
    var node = ev.target;

    if (node.dataItem && node.dataItem.dataContext.action == "icon") {
      var dataItem = node.dataItem;
      var dataContext = dataItem.dataContext;
      var icon = dataContext.icon;

      console.log('icon type', icon);
      console.log('icon dataItem', dataItem);
      //console.log(dataItem.component.nodes.template);
      console.log("series.labels", series);
      
      // make text white
      // ! this is an internal parameter, probably should not use it
      dataItem._settings.category = '[#fff]' + dataItem._settings.category.replace(/\n/gm, '[/]\n[#fff]') + '[/]';
      // = '[#fff]' + dataItem._settings.category + '[/]';

      var iconSrc = icon;
      

      let iconInject = target.children.push(
        am5.Picture.new(root, {
          width: 70,
          height: 70,
          centerX: am5.percent(50),
          centerY: am5.percent(100), // at top
          src: iconSrc
        })
      );
    } else if (
      node.dataItem &&
      (node.dataItem.dataContext.action == "list" ||
        node.dataItem.dataContext.action == "chip" ||
        node.dataItem.dataContext.action == "table")
    ) {
      var dataItem = node.dataItem;
      var dataContext = dataItem.dataContext;
      var type = node.dataItem.dataContext.action;

      // Determine color
      var color = node.dataItem.get("fill");
      var list = node.dataItem.dataContext.list;

      console.log("type!", type);

      if (type == "table") {
        var data = dataContext.data;
        console.log("table", data);
        var list = [];
        var sizes = []; if('sizes' in data[0]){ sizes = data[0].sizes };
        var align = []; if('align' in data[0]){ align = data[0].align };
        console.log('vermoegen sizes', sizes);
        data.forEach((line, i_row) => {
          if(i_row==0){return;} // skip definition row
          
          // if only 1 item in row don't break into columns, just print
          if( line.length==1 ){
            list.push(line);
            console.log('line is 1 item', line)
            return;
          }
          
          var row = [];
          line.forEach((col,i_col) => {
            var thisSize = sizes[i_col];
            var thisAlign = align[i_col];
            
            console.log('table col', i_col , col);
            var len = thisSize;
            var name = String(col).substring(0, len).padEnd(len, ' ');
            
            // 0 = left, 1 = right
            if(thisAlign==1){
              name = String(col).substring(0, len).padStart(len, ' ');
            }
            
            row.push(name);
          });
          
          // table column seperator
          //list.push(row.join('|')); // push line
          //list.push(row.join('[#FF3FA5]|[/]')); // push line
          list.push(row.join(' ')); // push line
        });
      } else {
        // could cut text
        var maxTextLength = 30;
        list.forEach((x, i) => {
          var text = x;
          if (text.length >= maxTextLength) {
            text = text.substring(0, maxTextLength - 4) + "...";
          }
          
          list[i] = text;
        });
      }

      // add bullet icon
      if (type == "list") {
        list.forEach((x, i) => {
          list[i] = "•" + x;
        });
      }

      console.log("dataItem", node.dataItem);
      console.log("dataItem list", list);

      //
      
      // join empty lines small
      var text = '';
      list.forEach((x,i,arr)=>{
        var currentItem = x;
        var nextItem = arr[i+1];
        if(nextItem == '??BREAK??'){
          list[i] = list[i] + '[fontSize: 6px]';
          list[i+1] = '[/]';// + nextItem;
        }
        console.log('line break currentItem / next', i, currentItem, nextItem);
      });
      var text = list.join("\n");
      
      var radius = dataItem.dataContext.radius;
      var maxLength = 0;
      list.forEach((x) => {
        if (x.length > maxLength) {
          maxLength = x.length;
        }
      });
      console.log("maxLength", maxLength);

      var maxTextLength = 0;
      // calculate new radius
      // one char in monospace uses 8 pixels (assuming 8pt idk haaa)
      // then the radius is 50% of the diameter
      var newRadius = (Math.max(maxTextLength, maxLength) * 8) / 2;
      dataContext.newRadius = newRadius;

      var button = node.children.push(
        am5.Button.new(root, {
          label: am5.Label.new(root, {
            text: text, //"Open\nzwei",
            fill: am5.color(0x000000),
            position: "relative",
            fontFamily: 'Monospace', 
            fontStyle: type == "chip" ? "italic" : "normal",
            fontSize: 16
            // width: 240
          }),
          centerX: am5.p50,
          centerY: am5.p50
        })
      );

      console.log("node element", node.children);

      // https://www.amcharts.com/docs/v5/concepts/common-elements/buttons/
      // https://www.amcharts.com/docs/v5/reference/roundedrectangle/#Settings
      button.get("background").setAll({
        fill: am5.color(0xf3f3f3),
        fillOpacity: 0.8,
        stroke: am5.color(0x000000),
        strokeOpacity: 0.5,
        cornerRadiusTL: 0,
        cornerRadiusTR: 0,
        cornerRadiusBR: 0,
        cornerRadiusBL: 0
      });

      /*
        button.get("background").states.create("hover", {
          fill: am5.Color.lighten(color, 0.5)
        });
         // Add button click event
        button.events.on("click", function(ev) {
          console.log("Open", ev);
        });
        */

      // Reposition button at the bottom of the label
      // disabled because of the connection line, when
      // the box is below the circle the connection line seems long, because it connects to the top
      // of the box, when the box is below it is short,
      // the center of gravity must be the center of the box, not the top
      /*
        node.dataItem.on("label", function (label) {
          label.events.on("boundschanged", function (ev) {
            //button.set("dx", label.width());
            button.set("dy", label.height() * -0.5);
          });
        });
        */
    }
  });
};

// ... except for central node
/*
series.circles.template.adapters.add('forceHidden', function(forceHidden, target) {
  var dataItem = target.dataItem;
  var dataContext = target.dataItem.dataContext;
  
  if('hidden' in dataContext){
    return true;
  }
  //console.log('circle', dataItem);
  //console.log('circle', dataContext);
  
  //return target.dataItem.get("depth") == 0 ? false: forceHidden;
});
*/

series.circles.template.adapters.add(
  "fillOpacity",
  function (strokeOpacity, target) {
    var dataItem = target.dataItem;
    var dataContext = target.dataItem.dataContext;
    if ("hidden" in dataContext) {
      return 0;
    }
  }
);

console.log('tooltip', series);

// Set custom radius for circles, based on data
series.circles.template.adapters.add("radius", function (radius, target) {
  return target.dataItem.dataContext.radius || radius;
});

series.circles.template.adapters.add("radius", function (radius, target) {
  var dataItem = target.dataItem;
  var dataContext = target.dataItem.dataContext;
  if ("newRadius" in dataContext) {
    //console.log('newRadius', dataContext.newRadius);
    //    console.log('target', radius);
    return dataContext.newRadius;
  } else {
    //console.log('oldRadius', radius);
  }

  return radius;
});

//
//
//
//
//
//
//

// Generate and set data
// https://www.amcharts.com/docs/v5/charts/hierarchy/#Setting_data
series.data.setAll(chartData);
series.set("selectedDataItem", series.dataItems[0]);

// Add legend
// https://www.amcharts.com/docs/v5/charts/hierarchy/#Legend
var legend = container.children.push(
  am5.Legend.new(root, {
    centerX: am5.p100,
    x: am5.p100,
    layout: root.verticalLayout,
    clickTarget: 'none'
  })
);

// Hide value labels
legend.valueLabels.template.set("forceHidden", true);

// Add handler for clicks
legend.itemContainers.template.events.on("click", function (ev) {
  am5.array.each(series.dataItems[0].get("children"), function (item) {
    if (item === ev.target.dataItem.dataContext || item.isHidden()) {
      item.show();
    } else {
      item.hide();
    }
  });
});



// Set legend data
legend.data.setAll(series.dataItems[0].get("children"));

// Make stuff animate on load
series.appear(100, 100);
body {
  font-family: "monospace";
  background-color: #4C4E54;
}

#chartdiv {
  width: 100%;
  height: 95vh;
}
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/hierarchy.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
<div id="chartdiv"></div>

0

There are 0 best solutions below