Mais Vue.JS nous permet d'appliquer des transitions via des composants directement dans le code et d'agir sur plusieurs moments de cette transition. Ainsi, on peut appliquer une transition sur un ensemble de balises du DOM qui s'activera dès qu'un ajout, une modification un une suppression d'un élément s'effectuera.

Malheureusement pour te répondre Arthur, il va falloir attendre que nous finissions ce cours et que l'on réalise le prochain pour que tu saches pleinement ce que sont les composants et comment les utiliser. Les composants sont au coeur de Vue.JS mais nous avons besoins de bases avant !

Non aucun problème ! Je vais rester très léger sur cette partie, si tu veux en savoir plus n'hésite pas à regarder la documentation officielle de Vue.
Les transitions d'entrée et de sortie
Ces transitions s'exécutent quand un élément apparait ou disparait (utilisation de v-if, v-else, v-else-if ou v-show). Elles s'exécutent aussi en cas de composants dynamiques, mais on verra ça bien plus tard.
Comme je l'ai dit précédemment, il faut utiliser un composant qui englobe le contenu sur lequel on veut appliquer la transition. Un composant n'est ni plus moins, dans le DOM, qu'une balise. Comme toute balise, on peut lui renseigner des attributs. Dans le cadre d'une transition, il faut lui attribuer un nom grâce à l'attribut name pour ensuite appliquer du style sur cette transition.
Je reprends un de nos anciens exemples :
{"language":"text/html","content":"<main id=\"app\" class=\"px-4 pt-8 w-full flex flex-wrap\">\n <button v-on:click=\"open = !open\">Afficher le contenu</button>\n <Transition name=\"fading\">\n <div v-show=\"open\">Je suis le contenu à afficher</div>\n </Transition>\n\n</main>\n<script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n open: false,\n }\n }\n }).mount('#app')\n</script>","filename":"index.html"}

C'est normal, on n'a pas stylisé notre transition.
Une transition peut se voir stylisée à 6 moments différents (un peu comme des hooks) qui sont nommés :
- v-enter-from : cette phase est celle où un élément qui doit subir un changement (être ajouté, apparaitre) n'a pas encore eu lieu, c'est juste avant. Elle est supprimée une fois que l'élément a subi son changement.
- v-enter-to : cette phase est celle où un élément qui doit subir un changement a eu lieu. Elle est déclenchée donc juste après v-enter-from. Elle est ensuite enlevée une fois que la transition est terminée.
- v-enter-active : cette phase couvre toute la période de v-enter-from à v-enter-to. On utilise cette phase pour renseigner la durée, le délai et la courbe d'évolution de la transition
- v-leave-from : pareil que v-enter-from mais quand l'étudiant disparait ou est supprimé
- v-leave-to : pareil que v-leave-to mais quand l'étudiant disparait ou est supprimé
- v-leave-active : pareil que v-leave-active mais quand l'étudiant disparait ou est supprimé
Ces hooks peuvent être utilisés dans le CSS pour styliser la transition en précisant dans le CSS ces noms de classe :
{"language":"text/html","content":"<style>\n\t.fading-enter-from{\n\n\t}\n\n\t.fading-enter-active{\n\n\t}\n\n\t.fading-enter-to{\n\n\t}\n\t.fading-leave-from{\n\n\t}\n\n\t.fading-leave-active{\n\n\t}\n\n\t.fading-leave-to{\n\n\t}\n</style>","filename":""}
Évidemment, j'ai appelé mes classes fading-xxxx car j'ai donné à ma transition le nom de fading dans mon exemple. Ce nom change donc à chaque fois qu'on donne un nom différent aux transitions.
Voici un exemple entier :
{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"fr\">\n\n<head>\n <meta charset=\"utf-8\">\n <title>Mes premiers pas avec Vue 3</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n [v-cloak] {\n display: none;\n }\n\n .fading-enter-active,\n .fading-leave-active {\n transition: opacity 1s ease;\n }\n\n .fading-enter-to,\n .fading-leave-from {\n background: darkcyan;\n }\n\n .fading-enter-from,\n .fading-leave-to {\n opacity: 0;\n }\n </style>\n</head>\n\n<body>\n <main id=\"app\" class=\"px-4 pt-8 w-full flex flex-wrap\" v-cloak>\n <button v-on:click=\"open = !open\">Afficher le contenu</button>\n <Transition name=\"fading\">\n <div v-show=\"open\">Je suis le contenu à afficher</div>\n </Transition>\n\n </main>\n <script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n open: false,\n }\n }\n }).mount('#app')\n </script>\n</body>\n\n</html>","filename":"index.html"}
Je te laisse l'essayer pour mieux comprendre et manipuler les transitions.
C'est compris ?

Alors continuons. Il est également possible d'agir sur ces "hooks" directement dans Vue en tant que méthodes. Pour ça, on va ajouter des listeners sur chacun des hooks avec v-on.
{"language":"text/html","content":"<Transition\n v-on:before-enter=\"enterFrom\"\n v-on:enter=\"enterActive\"\n v-on:after-enter=\"enterTo\"\n v-on:enter-cancelled=\"enterCancelled\"\n v-on:before-leave=\"leaveFrom\"\n v-on:leave=\"leaveActive\"\n v-on:after-leave=\"leaveTo\"\n v-on:leave-cancelled=\"leaveCancelled\"\n>\n <!-- ... -->\n</Transition>","filename":"index.html"}
Et dans nos méthodes :
{"language":"text/javascript","content":"methods: {\n // Enter\n\n enterFrom(el) {\n // ...\n },\n // la fonction de callback done() est optionnelle quand on utilise du CSS en + du JS\n enterActive(el, done) {\n done()\n },\n\n enterTo(el) {\n // ...\n },\n enterCancelled(el) {\n /// ...\n }\n\n\n // Leave\n\n leaveFrom(el) {\n // ...\n },\n // Pareillement\n leaveActive(el, done) {\n // ...\n done()\n },\n leaveTo(el) {\n // ...\n },\n leaveCancelled(el) {\n\n },\n}","filename":"vue.js"}
On peut alors faire une action quand la transition est déclenchée et donc faire des effets plus puissants. D'ailleurs, on peut préciser à Vue d'utiliser uniquement nos hooks sur les transitions dans Vue plutôt que du CSS en précisant :css="false" sur <Transition>.
Je ne fournirai pas d'exemple sur cette partie car c'est vrai qu'en général, le CSS suffit.
Bien. Je n'ai couvert qu'une petite partie des possibilités, mais pour notre niveau ça suffit. On va maintenant voir un deuxième type de transition : les transitions de groupe
Transitions de groupe
Dans le cas où on voudrait appliquer une transition sur une liste, c'est à dire sur des éléments générés par v-for, on ne peut pas utiliser <Transition>. Il faut utiliser <TransitionGroup>, mais l'idée est strictement la même.
{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"fr\">\n\n<head>\n <meta charset=\"utf-8\">\n <title>Mes premiers pas avec Vue 3</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n [v-cloak] {\n display: none;\n }\n\n span{\n\t\t\tdisplay:inline-block;\n\t\t\tpadding:0 1rem;\n\t\t}\n\n\t\t.blink-enter-active {\n\t \t\tanimation: blink 2s;\n\t\t}\n\t\t.blink-leave-active {\n\t\t \tanimation: blink 0.5s reverse;\n\t\t}\n\n\t\t@keyframes blink {\n\t \t0% {\n\t \topacity:0;\n\t \ttransform:scale(0.5);\n\t \tcolor:yellow;\n\t \t}\n\t \t50% {\n\t \topacity:0.7;\n\t \ttransform:scale(1.2);\n\t \tcolor:red;\n\t \t}\n\t \t100% {\n\t \ttransform: scale(1);\n\t \topacity:1;\n\t \tcolor:black;\n\t \t}\n\t}\n </style>\n</head>\n\n<body>\n <main id=\"app\" class=\"px-4 pt-8 w-full flex flex-wrap\" v-cloak>\n\t\t<button @click=\"add\">Ajouter un nombre</button>\n\t\t<TransitionGroup name=\"blink\">\n\t\t\t<span v-for=\"(number, key) in random\" :key=\"key\">\n\t\t\t\t{{ number }}\n\t\t\t</span>\n\t\t</TransitionGroup>\n\t</main>\n <script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n const randomNumber = (max) => {\n \t\t\treturn Math.floor(Math.random() * Math.floor(max));\n\t\t}\n createApp({\n data() {\n\t\t\t return {\n\t\t\t \trandom:[1, 8, 5, 2, 9]\n\t\t\t }\n\t\t\t},\n\t\t\tmethods:{\n\t\t\t\tadd() {\n\t\t\t\t\tthis.random.push(randomNumber(15))\n\t\t\t\t}\n\t\t\t}\n }).mount('#app')\n </script>\n</body>\n\n</html>","filename":"index.html"}

C'est exact aussi, comme je viens de le faire. Tu peux laisser libre recours à ton imagination, les possibilités sont donc infinies !
Petite indication également : tu peux ajouter un attribut "tag" sur TransitionGroup si tu veux que ce composant soit utilisé comme une vraie balise et remplacer par une vraie balise.
{"language":"text/html","content":"<TransitionGroup name=\"blink\" tag=\"section\">\n\t<span v-for=\"(number, key) in random\" :key=\"key\">\n\t\t{{ number }}\n\t</span>\n</TransitionGroup>","filename":""}
Avec ce code, on verra apparaître une balise <section></section> qui englobera nos span ;)
Bon, reprenons notre mini-projet. On veut faire apparaitre la partie concernant notre sélection de manière progressive.
Je te laisse essayer, et tu trouveras ma proposition ci-après !
{"language":"text/html","content":"<!DOCTYPE html>\n<html lang=\"fr\">\n\n<head>\n <meta charset=\"utf-8\">\n <title>Mes premiers pas avec Vue 3</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n [v-cloak] {\n display: none;\n }\n\n .fade-enter-active,\n .fade-leave-active {\n transition: all 0.5s ease;\n }\n\n .fade-enter-from,\n .fade-leave-to {\n opacity: 0;\n }\n </style>\n</head>\n\n<body>\n <main id=\"app\" v-cloak>\n <Transition name=\"fade\">\n <div v-show=\"modal == 'open'\"\n class=\"w-1/2 h-1/2 bg-white px-2 py-2 z-10 overflow-y-auto shadow-md rounded fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2\">\n <h2 class=\"text-xl py-4\">Mes articles en attente</h2>\n <a :href=\"`http://monsite.com/${post.id}`\" v-for=\"post in selection\" :key=\"post.id\" class=\"block\">{{\n post.title\n }}</a>\n </div>\n </Transition>\n <div v-if=\"loading\">Chargement en cours...</div>\n\n <p v-if=\"posts.length < 1\"\n class=\"max-w-7xl mx-auto border-l-4 border-yellow-400 bg-yellow-50 p-4 text-sm text-yellow-700\">\n Aucun article à afficher.\n </p>\n <div v-else class=\"max-w-7xl mx-auto grid gap-4 grid-cols-3\">\n <article v-for=\"post in posts\" class=\"shadow px-4 pb-8 pt-2 rounded relative\" :key=\"post.id\">\n <a :href=\"`http://monsite.com/${post.id}`\" class=\"mt-4 block\">\n <h2 class=\"text-xl font-semibold text-gray-900\">{{ post.title }}</h2>\n <p class=\"mt-3 text-base text-gray-500\">{{ post.body }}</p>\n </a>\n <button @click=\"toggleSelection(post, $event.target)\" class=\"text-sm absolute bottom-2 px-2 rounded\"\n :class=\"[selection.includes(post) ? css.ButtonRemove : css.ButtonAdd]\">\n Ajouter à ma liste\n </button>\n </article>\n </div>\n <footer v-if=\"selection.length > 0\" class=\"fixed bottom-0 right-2 px-2 py-4 rounded bg-gray-300\">\n <button @click=\"modal='open'\">Voir {{ selection.length > 1 ? 'les' : '' }} {{ selection.length }}\n article{{selection.length > 1 ? 's' : '' }} à lire plus tard</button>\n </footer>\n </main>\n\n <script type=\"module\">\n import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'\n createApp({\n data() {\n return {\n loading: true,\n posts: [],\n selection: [],\n css: {\n ButtonAdd: 'text-green-700 bg-green-200 hover:bg-green-300',\n ButtonRemove: ['text-yellow-700 bg-yellow-100 hover:bg-yellow-200'],\n },\n modal: 'close',\n }\n },\n methods: {\n toggleSelection(post, button) {\n if (this.selection.includes(post)) {\n this.selection.splice(this.selection.indexOf(post), 1)\n button.textContent = 'Ajouter à ma liste'\n } else {\n this.selection.push(post)\n button.textContent = 'Enlever de ma liste'\n }\n }\n },\n beforeCreate() {\n this.loading = true;\n },\n created() {\n fetch('https://jsonplaceholder.typicode.com/posts')\n .then((response) => response.json())\n .then((json) => { this.posts = json });\n },\n mounted() {\n this.loading = false\n }\n }).mount('#app')\n\n </script>\n</body>\n\n</html>","filename":"index.html"}

Pas grave, l'important c'est d'essayer et de pratiquer !
Bon maintenant qu'on a fait tout ça, on va ajouter quelques fonctionnalités à notre projet : on va créer un formulaire permettant à une personne d'ajouter un article. Évidemment, ce ne sera que côté front, donc encore une fois, pas de persistance des données, mais l'idée est là ! J'ai terminé cette partie