JSFiddle - React, Tailwind, and code Playground

by Mike Jacobson

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
<div ng-app="myapp">

  <timeline>
    
  </timeline>

</div>

CSS

.timeline-simple {
  font-size: 10px;
  height: 150px;
  overflow-x: scroll;
  white-space: nowrap;
}

.timeline-simple .timeline-bar {
  background-color: #ddd;
  bottom: 2px;
  height: 4px;
  position: relative;
  top: 100px;
  width: 100%;
}

.timeline-simple ol {
  padding: 80px 0 20px 20px;
  position: relative;
  //width: 100vw;
}

.timeline-simple ol li {
  display: block;
  list-style-type: none;
  position: absolute;
  white-space: normal;
  width: 170px;

  &::before { // line
    background: #ddd;
    bottom: -10px;
    content: '';
    display: block;
    height: 20px;
    left: calc(20% + 2px);
    position: absolute;
    width: 2px;
  }

  &::after { // dot
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 50%;
    bottom: 0;
    content: '';
    height: 10px;
    left: 14px;
    position: absolute;
    top: 2px;
    width: 10px;
  }
}

.timeline-simple-item {
  background-color: transparent;
  border: 1px solid #ddd;
  border-right-color: transparent;
  position: relative;
  height: 120px;
}

.timeline-simple-item-upper-left {
  border: 1px solid #ddd;
  border-left-color: #fff;
  border-top-color: #fff;
  height: 90px;
  left: -1px;
  width: 67%;
  position: absolute;
  top: -1px;
}

.timeline-simple-item-content {
  background-color: #fff;
  border: 1px solid #ddd;
  height: 50px;
  left: calc(67% - 20px);
  position: absolute;
  top: -1px;
  width: 80px;
}

.timeline-simple .fa-circle {
  font-size: 6px;
}

JavaScript

angular.module('myapp', [])
	.directive('timeline', timeline)

var data = [
	{ dueDateValue: 1470034800000, display: '08/01/2016' },
	{ dueDateValue: 1470034800000, display: '08/01/2016' },

	{ dueDateValue: 1479628800000, display: '11/20/2016' },
	{ dueDateValue: 1486195200000, display: '02/04/2017' },
	{ dueDateValue: 1492153200000, display: '04/14/2017' },
	{ dueDateValue: 1493881200000, display: '05/04/2017' },
	{ dueDateValue: 1505113200000, display: '09/11/2017' },
	{ dueDateValue: 1506322800000, display: '09/25/2017' },
	{ dueDateValue: 1506668400000, display: '09/29/2017' },
	{ dueDateValue: 1507014000000, display: '10/03/2017' },
	{ dueDateValue: 1507359600000, display: '10/07/2017' },
	{ dueDateValue: 1520755200000, display: '03/11/2018' },
	{ dueDateValue: 1528700400000, display: '06/11/2018' }
];

var first = data[0].dueDateValue;
var last = data[data.length - 1].dueDateValue;
var range = last - first;
data.forEach(function(o) {
	o.offsetPercent = (((o.dueDateValue - first) / range) * 100) + '%';

});

function TimelineCtrl() {
	var ctrl = this;
  
  this.data = data;

}

function timeline() {
  return {
    restrict: 'E',
    template: `
            <div class="timeline-simple">
              <div class="timeline-bar"></div>
              <ol>
                <li ng-repeat="m in ctrl.data"
                    ng-style="{ 'left': m.offsetPercent }">
                  <div class="timeline-simple-item">
                    <div class="timeline-simple-item-upper-left"></div>
                    <div class="timeline-simple-item-content">{{ m.offsetPercent }}</div>
                  </div>
                </li>
              </ol>
            </div>

    `,
    controller: TimelineCtrl,
    controllerAs: 'ctrl'
  }

}