I-Plan Gantt mockup (2016-03)

by Anton

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<div id="gantt">
  <div class="workcentre">
    <label><i class="fa fa-caret-right"></i>Paper Machine 1</label>
    
    <div class="run opportunistic" style="left:30px;width:30px" title="Opportunistic Run">
      <label>Opp.run 1</label>
    </div>
    
    <div class="run production" style="left:60px;width:180px" title="Run 01 / Grade A">
      <div class="stock ok"></div>
      <div class="run-part switchover" style="width:10px;"></div><div class="run-part orders" style="width:50px;"></div><div class="run-part allocations" style="width:30px;"></div>
      <label>Run 01 / Grade A</label>
    </div>
    
    <div class="shut overlapped" style="left:150px;width:40px" title="Shut 01"></div>
    
    <div class="run production selected" style="left:240px;width:150px" title="Run 02 / Grade C">
      <div class="stock ss"></div>
      <div class="run-part switchover" style="width:15px;"></div><div class="run-part allocations" style="width:100px;"></div>
      <label>Run 02 / Grade C</label>
    </div>
  </div> <!-- WKC -->
  
  <div class="workcentre">
    <label><i class="fa fa-caret-right"></i>Paper Machine 2</label>
    <div class="run production" style="left:90px;width:250px" title="Run 02 / Grade C">
      <div class="stock out"></div>
      <div class="run-part switchover" style="width:25px;"></div><div class="run-part orders" style="width:180px;"></div><div class="run-part allocations" style="width:20px;"></div>
      <label>Run 03 / Grade B</label>
    </div>
    
    <div class="shut" style="left:390px;width:70px" title="Shut 02"></div>
    
    <div class="run production" style="left:480px;width:60px" title="Run 04 / Grade A">
      <div class="stock ok"></div>
      <div class="run-part switchover" style="width:5px;"></div><div class="run-part orders" style="width:10px;"></div><div class="run-part allocations" style="width:2px;"></div>
     ...

CSS

body {
  background-color: #fff;
}

#gantt {
  position: relative;
  margin: 10px;
  padding: 10px;
  background-color: #f0f0f0;
  box-shadow: rgba(0,10,30,0.1) 0 0 7px inset;
  overflow: auto;
}

legend {
  position: relative;
}

legend ul {
  list-style: none;
  margin: 10px;
  padding: 0;
}

legend li {
  display: inline-block;
  padding: 3px 7px;
  margin-right: 15px;
}

legend li > span {
  display: inline-block;
  height: 16px;
  width: 16px;
  margin-right: 5px;
  margin-bottom: -3px;
}

.workcentre {
  position: relative;
  height: 90px;
  margin-bottom: 10px;
  transition: height 300ms;
}

.time-line {
  position: absolute;
  top: 0;
  width: 1px;
  height: 100%;
  background-color: #e0e0e0;
  z-index: 10;
}

.wkc-line {
  position: absolute;
  top: 45px;
  height: 20px;
  border-radius: 10px;
  opacity: 1;
  left: 0;
  background-color: #e0e0e0;
  z-index: 15;
  transition: top 300ms, height 300ms, padding 300ms, margin 300ms,
              opacity 300ms, border-radius 300ms;
}

.expanded .wkc-line {
  top: 0;
  height: 100%;
  padding: 0 10px;
  margin-left: -10px;
  border-radius: 0;
  opacity: 0.5;
}

label,
legend li {
  font-family: 'Oswald', sans-serif;
  white-space: nowrap;
  -webkit-user-select: none;  /* Chrome all / Safari all */
  -moz-user-select: none;     /* Firefox all */
  -ms-user-select: none;      /* IE 10+ */
  user-select: none;
  cursor: pointer;
}

.workcentre > label {
  position: absolute;
  left: 5px;
  top: 5px;
  color: #444;
  text-shadow: 0 1px 3px rgba(255,255,255,0.8);
  z-index: 20;
  opacity: 0.5;
}

.workcentre:hover > label {
  opacity: 1;
}

.workcentre > label i {
  margin-right: 3px;
}

.run label {
  position: absolute;
  top: 0;
  left: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  width: calc(100% - 6px);
}

.expanded .run label {
  overflow: visible;
}

.run label,
legend li {
  color: #444;
  font-size: 13px;
}

#gantt .shut {
  position: absolute;
  top: 45px;
  height: 20px;
  z-index: 30;
 ...

JavaScript

$(function() {
	var gantt = $('#gantt'),
  		timelineOffset = 50;
  
  for(var i = 1; i < gantt.width() / timelineOffset; i++) {
  	$('<div class="time-line"></div>')
    	.appendTo(gantt)
    	.css('left', i * timelineOffset);
  }
  
  $('.workcentre').each(function() {
  	var wkc = $(this);
    
    $('<div class="wkc-line"></div>')
    	.appendTo(wkc)
      .width(gantt.width());
  });
  
  $('.run, .shut').on('click', function() {
  	$('#gantt .selected').removeClass('selected');
    $(this).addClass('selected');
  });
  
  $('.workcentre > label').on('click', function() {
  	var wkc = $(this).closest('.workcentre'),
    		arrow = wkc.children('label').find('i'),
    		isCollapsed = !wkc.hasClass('expanded');
    
    wkc.toggleClass('expanded');
    arrow.toggleClass('fa-caret-right', !isCollapsed).toggleClass('fa-caret-down', isCollapsed);
    
    if(isCollapsed) {
    	var runHeight = wkc.find('.run').outerHeight(),
      		maxOffset = 0;
          
      // Offset runs
    	wkc.find('.run').each(function(i) {
      	var offset = runHeight * i;
        maxOffset = Math.max(maxOffset, offset);
      	$(this).css('margin-top', runHeight * i);
      });
      
      // Increase WKC height to allow for expanded runs
      wkc.css('height', wkc.height() + maxOffset);
    } else {
    	// Collapse runs back
      wkc.find('.run').each(function(i) {
      	$(this).css('margin-top', '');
      });
      
      // Restore WKC height
      wkc.css('height', '');
    }
  });
})