Cet article a pour but de montrer comment réaliser son propre composant de stepper en VueJS, bien que le code reste très facilement adaptable pour n’importe quel autre framework front.
Ce type de stepper est généralement utilisé pour illustrer simplement des processus, par étapes, à l’utilisateur, et lui faire comprendre rapidement les étapes qu’il a réalisées et celles qu’il lui reste avant de terminer le processus. Les steppers créent un chemin clair vers la finalisation d’un processus. Il est vrai qu'offrir aux utilisateurs une idée précise du nombre d'étapes nécessaires pour atteindre la cible finale pourrait réduire considérablement le taux d'abandon. On retrouve ce type de stepper dans un tunnel d’achat par exemple, mais il peut servir à tout autre cas d’utilisation qui nécessite un processus défini par avance (enregistrement d’utilisateur, recherche avancée, constitution d’un dossier, prise de rendez-vous, …).
À la fin de cet article, vous saurez comment réaliser le composant suivant, en VueJS 2, TypeScript, HTML, CSS. Il a été réalisé avec quelques classes utilitaires de Bootstrap relatives aux flexboxes. Il est responsive puisqu’il permet de mettre environ une vingtaine d’étapes sans qu’elles soient trop serrées, ce qui est déjà bien trop pour un stepper de ce type !
1 - Créer une étape
Commençons par créer une seule étape de notre stepper. Nous allons d’abord créer un nouveau composant qui s’appellera “Stepper.vue
”. Dans le template, le code suivant permet d’avoir une simple puce, ou bullet en anglais :
Stepper.vue - Template
<div class="step row no-gutters col justify-content-center align-items-center">
<div class="bullet row justify-content-center align-items-center">
<div>1</div>
</div>
<div class="title col-12">
Étape 1
</div>
</div>
Nous aurons aussi besoin du code SCSS suivant :
Stepper.vue - Styles
.step {
.bullet {
border-radius: 50%;
background-color: #26318d;
color: white;
font-weight: bold;
width: 2rem;
height: 2rem;
}
.title {
font-size: small;
color: #26318d;
text-align: center;
margin-top: 0.3rem;
}
}
Ici nous définissons donc une div step
qui est un composant flexbox dont les enfants sont centrés (row no-gutters justify-content-center align-items-center
). La classe no-gutters
permet d’enlever les gouttières (padding) sur le côté de notre composant, ainsi que ses enfants.
Le premier enfant de step
est une div bullet
qui contient le numéro de l’étape que nous créons. Le texte est parfaitement centré encore une fois grâce aux flexboxes. En matière de CSS, on lui donne un border-radius
de 50% pour rendre ronde notre bullet, et on fixe sa taille à 2rem.
Enfin, nous avons le dernier enfant title
, qui contient le nom de l’étape actuelle. On lui donne la classe col-12
pour qu’il puisse prendre toute la largeur disponible.
2 - Agencer plusieurs étapes
Dans cette deuxième étape, nous allons essayer de rendre notre composant un peu plus paramétrable, en donnant en entrée une liste d’étapes à afficher. Pour cela nous allons créer notre fichier “Stepper.ts
”, et y ajouter le code qui suit. Il suffit ensuite de faire un lien vers ce fichier dans notre composant Vue. Les deux méthodes fonctionnent et sont équivalentes.
Stepper.ts
@Component
export default class Stepper extends Vue {
@Prop({ default: [] })
public steps: string[];
}
Ici nous introduisons donc un nouvel attribut à notre composant : steps
. C’est une prop (une donnée d’entrée de notre composant) qui est par défaut un tableau vide.
Stepper.vue - Template
<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
<div class="step row no-gutters col justify-content-center align-items-center"
v-for="(step, index) in steps"
:key="step">
<div class="bullet row justify-content-center align-items-center">
<div>{{ index + 1 }}</div>
</div>
<div class="title col-12">
{{ step }}
</div>
</div>
</div>
Dans notre template HTML, nous itérons sur ce tableau pour afficher une étape pour chaque élément de notre tableau steps
. Pour cela, nous entourons notre code existant par une div steps-container
qui, grâce aux flexboxes, centre ses enfants verticalement (align-items-center
) et les écarte au maximum à l’horizontal (justify-content-between
). Il est important de rajouter l’attribut :key="step"
ici puisqu’il permet à la directive v-for
de garder la trace de l’élément en cours, si jamais ce dernier change dynamiquement, en lui donnant un identifiant.
3 - Relier les bullets par une ligne
Cette étape est souvent la plus difficile, puisqu’elle nécessite un peu de logique et de dextérité, comparé aux étapes précédentes. C’est parti !
Stepper.vue - Template
<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
<div class="step row no-gutters col justify-content-center align-items-center"
v-for="(step, index) in steps"
:key="step">
<div class="bullet row justify-content-center align-items-center">
<div>{{ index + 1 }}</div>
</div>
<div class="title col-12">
{{ step }}
</div>
<div class="line"
v-if="index < steps.length - 1">
</div>
</div>
</div>
Nous allons donc créer une ligne pour chaque étape que nous avons, sauf la dernière étape bien sûr, sinon on dépasserait (d’où la condition du v-if
).
Stepper.vue - Styles
.step {
position: relative;
.line {
width: 100%;
height: 2px;
background-color: #26318d;
position: absolute;
top: 25%;
left: 50%;
z-index: -1;
}
[...]
}
Cette ligne va prendre toute la largeur de notre bloc (donc la largeur d’une col
auto-calculée par flexbox). Nous lui donnons une position absolue, qui va donc se référer à son parent relatif le plus proche, donc nous rendons son parent step
positionné relativement. Ainsi, nous pouvons utiliser les propriétés left
et top
pour positionner librement notre ligne dans son conteneur parent, comme le montre le GIF ci-dessous.
4 - Gérer l’état de chaque étape
Enfin, il ne nous reste plus qu’à gérer l’état de chaque étape. Nous devons gérer l’état disabled des bullet
, title
et line
lorsque l’étape est postérieure à l’étape actuelle. Les étapes précédentes, elles, doivent afficher une coche verte en lieu et place du numéro de l’étape. Pour cela, nous avons besoin d’une nouvelle prop qui identifiera l’étape actuelle.
Stepper.ts
@Component({
components: {
CheckIcon,
},
})
export default class Stepper extends Vue {
@Prop({ default: [] })
public steps: string[];
@Prop({ default: 0 })
public currentStep: number;
}
Nous définissons cette prop dans notre composant Stepper, sous le nom currentStep
qui sera un nombre, par défaut 0, et qui correspond à l’index de l’étape actuelle dans le tableau steps
. On en profite aussi pour injecter le composant CheckIcon
de vue-feather-icons qui nous servira pour afficher la coche verte.
Stepper.vue - Template
<div class="steps-container w-100 row no-gutters justify-content-between align-items-center">
<div class="step row no-gutters col justify-content-center align-items-center"
v-for="(step, index) in steps"
v-bind:class="{ disabled: index > currentStep }"
:key="step">
<div class="bullet row justify-content-center align-items-center">
<check-icon v-if="index < currentStep || currentStep === steps.length - 1"
size="1x"
class="check-icon"></check-icon>
<div v-else>{{ index + 1 }}</div>
</div>
<div class="title col-12">
{{ step }}
</div>
<div class="line"
v-if="index < steps.length - 1"
v-bind:class="{ disabled: index === currentStep }">
</div>
</div>
</div>
Nous ajoutons ici dynamiquement la classe disabled
sur notre step
lorsque l’index est supérieur à l’index de notre currentStep
. Cette règle va permettre de donner un style disabled à tous les éléments enfants de notre step
, que nous définissons ci-dessous. Cependant, puisque notre line
correspond au lien entre notre étape et la suivante, nous devons la rendre disabled pour notre étape actuelle également.
Enfin, l’icône de coche verte a été ajoutée dans la bullet
, avec la condition d’apparaître uniquement si nous avons déjà passé l’étape OU si le processus est terminé (auquel cas toutes les bullet
auront donc une coche verte).
Stepper.vue - Styles
.step {
position: relative;
.line {
[...]
// For disabling line for currentStep
&.disabled {
background-color: lighten(#26318d, 40%);
}
}
[...]
.check-icon {
color: #00d992;
}
// For disabling everything for next steps
&.disabled {
.line, .bullet {
background-color: lighten(#26318d, 40%);
}
.title {
color: lighten(#26318d, 40%);
}
}
}
Pour gérer l’état disabled de notre line
actuelle, nous changeons juste son background-color
en utilisant une fonction bien pratique de SCSS qui s’appelle lighten
et permet d’éclaircir une couleur d’un certain pourcentage. Nous utilisons la même technique pour le deuxième bloc de code qui correspond à l’état disabled des étapes suivantes.
Et voilà, c’est déjà fini ! Voici le résultat sous forme d’animation pour se rendre compte de l’état de notre stepper dans chaque étape :
Aller plus loin
Ce stepper est plutôt simple et ne permet pas d’interaction. On pourrait rajouter des actions lors d’un clic sur une étape précédente par exemple, ce qui permettrait un retour en arrière. On pourrait aussi ajouter un peu d’animation lors du passage d’une étape à l’autre, pour rendre le composant plus agréable.