In some CSS libraries, i.e. Bootstrap, many components have opacity set to 0 by default, and only have opacity set to 1 when a class is applied. i.e. Bootstrap's fade + show classes:
.fade:not(.show) { opacity: 0; }
The current <transition> behaviour does not support retaining the enter-to-class (as it is immediately removed once the transition completes).
Currently one has to use javascript hooks and VM data to add/remove the show class, which makes it impossible to make re-usable transition components, as simply adding a class via javascript to the el reference (via el.classList.add/remove('show')) doesn't guarantee persistence if the content in the default slot manipulates it's own classes, meaning the transition component _must_ be integrated into the custom component, not as a re-usable utility wrapper component).
It would be nice to either be able to retain the enter-to-class class after the transition completes, or create a new prop that provides this behaviour for a particular class name i.e. enter-to-retain-class. The class would be removed on leave.
The other option would be to create a new prop active-class="classname" which is added one frame after insertion, and removed when the leave transition is to start (basically rolling the enter-to-class and leave-class into one prop, but retain the class until leave is initiated)
In our (BootstrapVue's) use case, we have to hard-code the transition component and code into each component that uses the fade+show Bootstrap V4 transition classes (modals, toasts, alerts, tabs, etc), causing unnecessary code duplication, and making it difficult for us to allow users to provide their own custom transitions (which has been a common ask for the past while), due to the need for us to mix CSS+Javascript to make Vue's transition system work with Bootstrap.
Ran into this issue today. +1 for adding it to core. Here's the workaround I came up with today:
Transparent wrapper around the default transition component with js hooks to add classes after the enter transitioning is done, and remove those classes before our leave transition starts.
after-enter-class is the prop I added which in my case just mirrors enter-to-class.
https://codesandbox.io/s/serene-darwin-r7h4o?file=/src/components/BetterTransition.vue
<template>
<transition
v-bind="$attrs"
v-on="$listeners"
v-on:after-enter="afterEnter"
v-on:before-leave="beforeLeave"
>
<slot></slot>
</transition>
</template>
<script>
export default {
props: {
afterEnterClass: {
type: String,
default: ""
}
},
computed: {
classes() {
return this.afterEnterClass.split(" ").filter(i => i);
}
},
methods: {
afterEnter(el) {
this.classes.forEach(cssClass => el.classList.add(cssClass));
},
beforeLeave(el) {
this.classes.forEach(cssClass => el.classList.remove(cssClass));
}
}
};
</script>
Usage
<better-transition
enter-active-class="transition duration-1000"
enter-class="opacity-0"
enter-to-class="opacity-50"
after-enter-class="opacity-50"
leave-active-class="transition duration-1000"
leave-class="opacity-50"
leave-to-class="opacity-0"
>
<div v-if="open" class="mt-5">Our transition</div>
</better-transition>
We tried something similar, but ran into issue if the root element of the default slot updates (i.e. classes dynamically added or attributes changed), that the class added by the afterEnter hook can get trashed/removed
Ahhh. Yup I'm getting the same thing. Easiest workaround is to wrap the content for the slot in a tag using and ensure it doesn't ever update. Not ideal; works for me since I'm in complete control of everything, but I can see this not being something you'd want to support in a public package.
<better-transition
enter-active-class="transition duration-1000"
enter-class="opacity-0"
enter-to-class="opacity-50"
after-enter-class="opacity-50"
leave-active-class="transition duration-1000"
leave-class="opacity-50"
leave-to-class="opacity-0"
>
<div v-if="open">
<div class="mt-5" :class="{'text-white': isActive}">Our transition</div>
</div>
</better-transition>
@tmorehouse On a side note, thanks for all your work on bootstrap-vue. We've used it in a few projects and it's been great!
Most helpful comment
Ahhh. Yup I'm getting the same thing. Easiest workaround is to wrap the content for the slot in a tag using and ensure it doesn't ever update. Not ideal; works for me since I'm in complete control of everything, but I can see this not being something you'd want to support in a public package.
@tmorehouse On a side note, thanks for all your work on bootstrap-vue. We've used it in a few projects and it's been great!