d3.js Gantt Timeline_v2

by alketraz

HTML

<body>
  <h1>Timeline</h1>
  <br />
  <p>
  <strong>Request</strong>
  </p>
  <ul>
    <li>Activity within the same group should be placed on the same line</li>
  </ul>
  <br />
  <strong>Issue / Ideal</strong>
  <ul>
    <li>This results in the last mysql activity overlapping with the one above</li>
    <li>If possible, in this case the last mySql should be on its own line, so the database group would have 3 rows, 2 x mysql and 1 x postgresql</li>
  </ul>
  <script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
  <script src="https://d3js.org/d3-array.v2.min.js"></script>
</body>

CSS

*,
*::before,
*::after {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

svg {
  border: thin solid lightgray;
  background-color: lightcyan;
}

.svg-container {
  display: block;
  position: absolute;
  width: 100%;
  height: 90%;
}

.svg-content-responsive {
  display: inline-block;
  position: absolute;
  top: 0;
  left: 0;
  max-height: 100%;
  margin: 10px;
}

rect.activity {
  opacity: 0.8;
  stroke: black;
  stroke-opacity: 0.8;
}

rect.frontend {
  fill: orange;
}

rect.operations {
  fill: blueviolet;
}

rect.devops {
  fill: aqua;
}

rect.backend {
  fill: salmon;
}

rect.database {
  fill: green;
}

rect.marketing {
  fill: rgb(255, 207, 14);
}

.tick line {
  visibility: hidden;
}

g.swimlane-activity rect {
  stroke: white;
  stroke-width: 2px;
  opacity: 0.5;
}

JavaScript

let settings = [
  {
    group: "operations",
    colour: "rgba(0, 0, 0, 0.1)",
  },
  {
    group: "devops",
    colour: "rgba(0, 0, 255, 0.1)",
  },
  {
    group: "frontend",
    colour: "rgba(0, 255, 0, 0.1)",
  },
  {
    group: "database",
    colour: "rgba(255, 0, 0, 0.1)",
  },
];

const projects = [
  {
    id: "1",
    activity: "foo",
    group: "operations",
    start_date: "2018-01-01",
    end_date: "2019-12-31",
  },
  {
    id: "2",
    activity: "bar",
    group: "operations",
    start_date: "2020-10-31",
    end_date: "2020-11-09",
  },
  {
    id: "3",
    activity: "foo",
    group: "operations",
    start_date: "2020-05-09",
    end_date: "2020-08-09",
  },
  {
    id: "4",
    activity: "foo",
    group: "devops",
    start_date: "2018-04-01",
    end_date: "2020-12-31",
  },
  {
    id: "5",
    activity: "bar",
    group: "devops",
    start_date: "2018-01-01",
    end_date: "2019-09-30",
  },
  {
    id: "6",
    activity: "foo",
    group: "frontend",
    start_date: "2017-01-01",
    end_date: "2019-01-31",
  },
  {
    id: "9",
    activity: "mysql",
    group: "database",
    start_date: "2015-01-01",
    end_date: "2015-09-15",
  },
  {
    id: "10",
    activity: "postgresql",
    group: "database",
    start_date: "2016-01-01",
    end_date: "2017-12-31",
  },
  {
    id: "11",
    activity: "mysql",
    group: "database",
    start_date: "2018-05-01",
    end_date: "2018-12-31",
  },
  {
    id: "12",
    activity: "mysql",
    group: "database",
    start_date: "2018-09-01",
    end_date: "2019-12-31",
  },
];

// Process data
projects.sort((a, b) => d3.ascending(a.start_date, b.start_date));

Array.prototype.groupBy = function (props) {
  var arr = this;
  var partialResult = {};

  arr.forEach((el) => {
    var grpObj = {};

    props.forEach((prop) => {
      grpObj[prop] = el[prop];
    });

    var key = JSON.stringify(grpObj);

    if (!partialResult[key]) partialResult[key] = [];

    partialResult[key].push(el);
  });

 ...