Progress bar

by namlth

HTML

<script src="//code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
<script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://github.com/SteveSanderson/knockout-projections/blob/master/dist/knockout-projections.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/1.2.1/lodash.min.js"></script>
<div id="testId" class="d-flex align-items-center justify-content-between">
  <div class="div-side"></div>
  <div class="progress align-items-center justify-content-between">
    <div class="step first-type">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
    <div id="step2" class="step active first">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
    <div id="step3" class="step active">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
    <div id="step4" class="step active">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
    <div id="step5" class="step active last">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
    <div id="step6" class="step last-type">
      <div class="step-icon">
        <i class="fas fa-plane"></i>
      </div>
    </div>
  </div>
  <div class="div-side"></div>
</div>
<!-- Dynamic -->
<div class="d-flex align-items-center justify-content-between">
  <div class="div-side"></div>
  <div class="progress align-items-center justify-content-between"
       data-bind="foreach: { data: items, as: 'item' }">
...

SCSS

.progress{
  width: 100%;
  height: 100px;
  //background: lightblue;
}

.div-side{
  width: 100px;
  height: 100px;
  background: lightgreen;
}

.horizontal-line{
  height: 10px;
  width: 100%;
  //border-bottom: 10px solid red;
  border-top-left-radius: 60px;
  border-bottom-left-radius: 60px;
  border-top-right-radius: 60px;
  border-bottom-right-radius: 60px;
  padding-left: 10px;
  padding-right: 10px;
  background: red;
  position: absolute;
}

.step {
  width: 100%;
  //background-color: lightyellow;
  text-align: center;
  position: relative;
  display: flex;
  justify-content: center;
  list-style: none;
  margin-left: 0.1rem;
  margin-right: 0.1rem;

}

.step:before,
.step:after{
  content: "";
  //width: 500%;
  top: 50%;
  z-index: 1;
  border-bottom: 1px dashed lightgrey;
  position: absolute;
  //-webkit-transition: width 2s, height 4s; /* For Safari 3.1 to 6.0 */
  //transition: width 2s, height 4s;
}

.step.active:before {
  content: "";
  //width: 500%;
  top: 50%;
  z-index: 2;
  border-bottom: 1px dashed black;
  position: absolute;
  //-webkit-transition: width 2s, height 4s; /* For Safari 3.1 to 6.0 */
  //transition: width 2s, height 4s;
}

.step:before {
  left: 0;
  right: 50%;
}

.step:after {
  left: 50%;
  right: 0;
}

.step.active:before {
  left: 0;
  right: 50%;
}

.step.first-type:before {
  border: none !important;
}

.step.last-type:after {
  border: none !important;
}

.step.active.first:before {
  border-bottom: 1px dashed lightgrey;
}

.step.active.last:after {
  border-bottom: 1px dashed lightgrey;
}
.step.first-type:after {
  border-bottom: 1px dashed lightgrey;
}

.step.active:after {
  content: "";
  top: 50%;
  z-index: 2;
  border: 1px dashed black;
  position: absolute;
  left: 50%;
  right: 0;
}

.step.active.only:before,
.step.active.only:after {
  border: 1px dashed lightgrey;
}

.step-icon{
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: lightgray;
  position: relative;
  display: flex;
 ...

JavaScript

function myModel() {
  var that = this;
  that.items = ko.observableArray([
    { name: "item1", isFirstOfType: ko.observable(true),  isLastOfType: ko.observable(false),
     isActive: ko.observable(false), isFirstActive: ko.observable(false), isLastActive: ko.observable(false), isOnly: ko.observable(false) },
    { name: "item2", isFirstOfType: ko.observable(false), isLastOfType: ko.observable(false),
     isActive: ko.observable(true),  isFirstActive: ko.observable(true),  isLastActive: ko.observable(false), isOnly: ko.observable(false) },
    { name: "item3", isFirstOfType: ko.observable(false), isLastOfType: ko.observable(false),
     isActive: ko.observable(true),  isFirstActive: ko.observable(false), isLastActive: ko.observable(false), isOnly: ko.observable(false) },
    { name: "item4", isFirstOfType: ko.observable(false), isLastOfType: ko.observable(false), 
     isActive: ko.observable(true),  isFirstActive: ko.observable(false), isLastActive: ko.observable(true),  isOnly: ko.observable(false) },
    { name: "item5", isFirstOfType: ko.observable(false), isLastOfType: ko.observable(false), 
     isActive: ko.observable(false), isFirstActive: ko.observable(false), isLastActive: ko.observable(false), isOnly: ko.observable(false) },
    { name: "item6", isFirstOfType: ko.observable(false), isLastOfType: ko.observable(true),  
     isActive: ko.observable(false), isFirstActive: ko.observable(false), isLastActive: ko.observable(false), isOnly: ko.observable(false) }
  ]);
  that.activeItems = ko.computed(() => {
    var activeItems = that.items().filter((x) => { return x.isActive(); });
    return activeItems;
  });
  that.firstActiveItem = ko.computed(() => {
    if (that.activeItems().length > 0) return that.activeItems()[0];
    else return null;
  });
  that.firstActiveItemName = ko.computed(() => {
    if (that.activeItems().length > 0) return that.firstActiveItem().name;
    else return "";
  });
  that.lastActiveItem = ko.computed(() => {
    if...