How To Create A Custom Stepper Component In Vue.js

In this article, we will learn how to create a custom stepper using vue.js

Steppers:

This component visualizes the progress of a process by dividing it into chronological steps.

Step 1: Create a new vue project:

vue create vue-stepper-demo

Step 2: Install require packages:

npm i bootstrap

Step 3: Open main.js file and add the following in it:

import Vue from 'vue'
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.min.css"
import 'bootstrap/dist/js/bootstrap.bundle.min.js'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

Step 4: Create CustomStepper.vue file and add the following in it:

<template>
  <div>
    <div class="d-flex flex-column">
      <div class="app-stepper mdl-shadow--2dp w-full">
        <div class="mdl-card__supporting-text">
          <div class="mdl-stepper-horizontal-alternative bg-white rounded-3xl">
            <!-- Add step-done class for right mark -->
            <!-- Add editable-step class for pencil icon -->
            <div
              v-for="(step, idx) in steps"
              :key="idx"
              class="mdl-stepper-step"
              :class="{
                'active-step': step.isActive,
                'step-done': step.isCompleted,
                'd-none': step.hide,
                'disabled-feature': step.disabled,
              }"
              @click="goToStep(step)"
            >
              <div class="mdl-stepper-circle">
                <span>{{ step.number }}</span>
              </div>
              <div class="mdl-stepper-bar-left"></div>
              <div class="mdl-stepper-bar-right"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    props: {
      steps: {
        type: Array,
        required: true,
        default: () => [],
      },
    },
    data() {
      return {};
    },
    computed: {

      visibleSteps: function () {
        return this.steps.filter((x) => !x.hide);
      },
    },
    methods: {
      goToStep: function (step) {
        if (step.isCompleted) {
          this.$emit('go-to-step', step);
        }
      },
    },
  };
</script>
<style>
.mdl-card__supporting-text {
  width: 100%;
  padding: 0;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step {
  width: 25%;
}

.mdl-stepper-horizontal-alternative {
  display: flex;
  width: 100%;
  margin: 0 auto;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step {
  display: table-cell;
  position: relative;
  padding: 20px;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step:first-child:active {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step:last-child:active {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step:first-child .mdl-stepper-bar-left,
.mdl-stepper-horizontal-alternative .mdl-stepper-step:last-child .mdl-stepper-bar-right {
  display: none;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-circle {
  width: 40px;
  height: 40px;
  margin: 0 auto;
  background-color: #7b9cb1;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  /* text-align: center; */
  color: white;
  font: normal normal bold 19px/26px Trade Gothic Next LT Pro BdCn;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step.active-step .mdl-stepper-circle {
  background-color: #0089c7;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step.editable-step .mdl-stepper-circle {
  -moz-transform: scaleX(-1);
  -o-transform: scaleX(-1);
  -webkit-transform: scaleX(-1);
  transform: scaleX(-1);
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-title {
  margin-top: 16px;
  font-size: 14px;
  font-weight: normal;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-title,
.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-optional {
  text-align: center;
  color: rgba(0, 0, 0, 0.26);
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step.active-step .mdl-stepper-title {
  font-weight: bold;
  color: rgba(0, 0, 0, 0.87);
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step.active-step.step-done .mdl-stepper-title,
.mdl-stepper-horizontal-alternative .mdl-stepper-step.active-step.editable-step .mdl-stepper-title {
  font-weight: 300;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-optional {
  font-size: 12px;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step.active-step .mdl-stepper-optional {
  color: rgba(0, 0, 0, 0.54);
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-bar-left,
.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-bar-right {
  position: absolute;
  top: 36px;
  height: 1px;
  border-top: 8px solid #7b9cb1;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-bar-right {
  right: 0;
  left: 50%;
  margin-left: 15px;
}

.mdl-stepper-horizontal-alternative .mdl-stepper-step .mdl-stepper-bar-left {
  left: 0;
  right: 50%;
  margin-right: 15px;
}

.mdl-stepper-step.step-done .mdl-stepper-circle {
  background-color: #0089c7 !important;
}

.mdl-stepper-step.active-step .mdl-stepper-bar-left,
.mdl-stepper-step.active-step .mdl-stepper-bar-right {
  border-top: 8px solid #0089c7;
}

.mdl-stepper-step.step-done .mdl-stepper-bar-left,
.mdl-stepper-step.step-done .mdl-stepper-bar-right {
  border-top: 8px solid #0089c7;
}

.mdl-stepper-step.step-done .mdl-stepper-title {
  color: rgba(0, 0, 0, 0.87) !important;
  font-weight: 500;
}
</style>

Step 5: Open App.vue file and add the following in it:

<template>
  <div id="app" class="container">
    <Stepper :steps="steps"/>
    <div v-if="step == 1" class="d-flex flex-column align-items-center p-4">
      <h1>Step 1</h1> 
      <input type="text" name="Name" id="Name" placeholder="Enter Your Name" class="col-4 align-self-center p-2 my-3" />
      <button @click="nextStep" class="btn btn-primary col-4 align-self-center p-2 my-3">Next</button>
    </div>
    <div v-if="step == 2" class="d-flex flex-column align-items-center p-4">
      <h1>Step 2</h1>
        <p class="fs-3">This is a second Step.</p>
      <div class="d-flex flex-row">
        <button @click="back" class="btn btn-secondary p-2 me-2">Back</button>
        <button @click="nextStep" class="btn btn-primary p-2 ms-2">Next</button>
      </div>
    </div>
    <div v-if="step==3" class="d-flex flex-column align-items-center p-4">
      <h1>Step 3</h1>
      <p class="fs-3">This is a Third Step.</p>
      <button @click="back" class="btn btn-secondary">Back</button>
    </div>
  </div>
</template>

<script>
import Stepper from './components/CustomStepper.vue'

export default {
  name: 'App',
  components: {
    Stepper
  },
  data(){
    return {
      step:1,
      steps:[
        {
          number: 1,
          isActive: true,
          disabled: false,
          isCompleted: false,
        },
        {
          number: 2,
          isActive: false,
          disabled: false,
          isCompleted: false,
        },
        {
          number: 3,
          isActive: false,
          disabled: false,
          isCompleted: false,
        }
      ],
    }
  },
  methods:{
    nextStep(){
      const activeStep = this.steps.find((x) => x.isActive);
        if (this.step === activeStep.number) {
          const nextStep = this.steps.find((x) => x.number === this.step + 1);
          if (nextStep) {
            nextStep.isActive = true;
            this.step = nextStep.number;
          }
          activeStep.isActive = false;
          activeStep.isCompleted = true;
        }
      },
      back(){
        const activeStep = this.steps.find((x) => x.isActive);
        if (this.step === activeStep.number) {
          const nextStep = this.steps.find(
            (x) => x.number === activeStep.number - 1,
          );
          if (nextStep) {
            nextStep.isActive = true;
            this.step=nextStep.number;
          }
          activeStep.isActive = false;
          activeStep.isCompleted = false;
        }
      }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Code in Action:

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories