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: