onAppearCancelled = onEnterCancelled,
} = baseProps
- const finishEnter = (el: Element, isAppear: boolean, done?: () => void) => {
+ const finishEnter = (
+ el: Element & { _enterCancelled?: boolean },
+ isAppear: boolean,
+ done?: () => void,
+ isCancelled?: boolean,
+ ) => {
+ el._enterCancelled = isCancelled
removeTransitionClass(el, isAppear ? appearToClass : enterToClass)
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass)
done && done()
},
onEnter: makeEnterHook(false),
onAppear: makeEnterHook(true),
- onLeave(el: Element & { _isLeaving?: boolean }, done) {
+ onLeave(
+ el: Element & { _isLeaving?: boolean; _enterCancelled?: boolean },
+ done,
+ ) {
el._isLeaving = true
const resolve = () => finishLeave(el, done)
addTransitionClass(el, leaveFromClass)
}
// add *-leave-active class before reflow so in the case of a cancelled enter transition
// the css will not get the final state (#10677)
- addTransitionClass(el, leaveActiveClass)
- // force reflow so *-leave-from classes immediately take effect (#2593)
- forceReflow()
+ if (!el._enterCancelled) {
+ // force reflow so *-leave-from classes immediately take effect (#2593)
+ forceReflow()
+ addTransitionClass(el, leaveActiveClass)
+ } else {
+ addTransitionClass(el, leaveActiveClass)
+ forceReflow()
+ }
nextFrame(() => {
if (!el._isLeaving) {
// cancelled
callHook(onLeave, [el, resolve])
},
onEnterCancelled(el) {
- finishEnter(el, false)
+ finishEnter(el, false, undefined, true)
callHook(onEnterCancelled, [el])
},
onAppearCancelled(el) {
- finishEnter(el, true)
+ finishEnter(el, true, undefined, true)
callHook(onAppearCancelled, [el])
},
onLeaveCancelled(el) {
import { Transition, createApp, h, nextTick, ref } from 'vue'
describe('e2e: Transition', () => {
- const { page, html, classList, isVisible, timeout, nextFrame, click } =
+ const { page, html, classList, style, isVisible, timeout, nextFrame, click } =
setupPuppeteer()
const baseUrl = `file://${path.resolve(__dirname, './transition.html')}`
)
})
+ test('reflow after *-leave-from before *-leave-active', async () => {
+ await page().evaluate(() => {
+ const { createApp, ref } = (window as any).Vue
+ createApp({
+ template: `
+ <div id="container">
+ <transition name="test-reflow">
+ <div v-if="toggle" class="test-reflow">content</div>
+ </transition>
+ </div>
+ <button id="toggleBtn" @click="click">button</button>
+ `,
+ setup: () => {
+ const toggle = ref(false)
+ const click = () => (toggle.value = !toggle.value)
+ return {
+ toggle,
+ click,
+ }
+ },
+ }).mount('#app')
+ })
+
+ // if transition starts while there's v-leave-active added along with v-leave-from, its bad, it has to start when it doesnt have the v-leave-from
+
+ // enter
+ await classWhenTransitionStart()
+ await transitionFinish()
+
+ // leave
+ expect(await classWhenTransitionStart()).toStrictEqual([
+ 'test-reflow',
+ 'test-reflow-leave-from',
+ 'test-reflow-leave-active',
+ ])
+
+ expect(await style('.test-reflow', 'opacity')).toStrictEqual('0.9')
+
+ await nextFrame()
+ expect(await classList('.test-reflow')).toStrictEqual([
+ 'test-reflow',
+ 'test-reflow-leave-active',
+ 'test-reflow-leave-to',
+ ])
+
+ await transitionFinish()
+ expect(await html('#container')).toBe('<!--v-if-->')
+ })
+
test('warn when used on multiple elements', async () => {
createApp({
render() {
value(selector: string): Promise<string>
html(selector: string): Promise<string>
classList(selector: string): Promise<string[]>
+ style(selector: string, property: keyof CSSStyleDeclaration): Promise<any>
children(selector: string): Promise<any[]>
isVisible(selector: string): Promise<boolean>
isChecked(selector: string): Promise<boolean>
return page.$eval(selector, (node: any) => [...node.children])
}
+ async function style(
+ selector: string,
+ property: keyof CSSStyleDeclaration,
+ ): Promise<any> {
+ return await page.$eval(
+ selector,
+ (node, property) => {
+ return window.getComputedStyle(node)[property]
+ },
+ property,
+ )
+ }
+
async function isVisible(selector: string): Promise<boolean> {
const display = await page.$eval(selector, node => {
return window.getComputedStyle(node).display
value,
html,
classList,
+ style,
children,
isVisible,
isChecked,
.test-appear,
.test-enter,
.test-leave-active,
+ .test-reflow-enter,
+ .test-reflow-leave-to,
.hello,
.bye.active,
.changed-enter {
opacity: 0;
}
+ .test-reflow-leave-active,
+ .test-reflow-enter-active {
+ -webkit-transition: opacity 50ms ease;
+ transition: opacity 50ms ease;
+ }
+ .test-reflow-leave-from {
+ opacity: 0.9;
+ }
.test-anim-enter-active {
animation: test-enter 50ms;
-webkit-animation: test-enter 50ms;