feat(transition): handle persisted mode

This commit is contained in:
Evan You 2019-11-22 23:21:39 -05:00
parent cbc2044752
commit 1b8236615e
4 changed files with 75 additions and 26 deletions

View File

@ -17,6 +17,12 @@ import { ShapeFlags } from '../shapeFlags'
export interface TransitionProps {
mode?: 'in-out' | 'out-in' | 'default'
appear?: boolean
// If true, indicates this is a transition that doesn't actually insert/remove
// the element, but toggles the show / hidden status instead.
// The transition hooks are injected, but will be skipped by the renderer.
// Instead, a custom directive can control the transition by calling the
// injected hooks (e.g. v-show).
persisted?: boolean
// enter
onBeforeEnter?: (el: any) => void
onEnter?: (el: any, done: () => void) => void
@ -139,6 +145,7 @@ if (__DEV__) {
;(TransitionImpl as ComponentOptions).props = {
mode: String,
appear: Boolean,
persisted: Boolean,
// enter
onBeforeEnter: Function,
onEnter: Function,
@ -161,6 +168,7 @@ export const Transition = (TransitionImpl as any) as {
}
export interface TransitionHooks {
persisted: boolean
beforeEnter(el: object): void
enter(el: object): void
leave(el: object, remove: () => void): void
@ -173,6 +181,7 @@ export interface TransitionHooks {
function resolveTransitionHooks(
{
appear,
persisted = false,
onBeforeEnter,
onEnter,
onAfterEnter,
@ -188,6 +197,7 @@ function resolveTransitionHooks(
performDelayedLeave: () => void
): TransitionHooks {
return {
persisted,
beforeEnter(el) {
if (!isMounted && !appear) {
return
@ -202,7 +212,10 @@ function resolveTransitionHooks(
if (!isMounted && !appear) {
return
}
let called = false
const afterEnter = (pendingCallbacks.enter = (cancelled?) => {
if (called) return
called = true
if (cancelled) {
callHook(onEnterCancelled, [el])
} else {
@ -223,7 +236,10 @@ function resolveTransitionHooks(
pendingCallbacks.enter(true /* cancelled */)
}
callHook(onBeforeLeave, [el])
let called = false
const afterLeave = (pendingCallbacks.leave = (cancelled?) => {
if (called) return
called = true
remove()
if (cancelled) {
callHook(onLeaveCancelled, [el])

View File

@ -367,7 +367,7 @@ export function createRenderer<
invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
}
}
if (transition != null) {
if (transition != null && !transition.persisted) {
transition.beforeEnter(el)
}
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
@ -385,11 +385,14 @@ export function createRenderer<
}
hostInsert(el, container, anchor)
const vnodeMountedHook = props && props.onVnodeMounted
if (vnodeMountedHook != null || transition != null) {
if (
vnodeMountedHook != null ||
(transition != null && !transition.persisted)
) {
queuePostRenderEffect(() => {
vnodeMountedHook &&
invokeDirectiveHook(vnodeMountedHook, parentComponent, vnode)
transition && transition.enter(el)
transition && !transition.persisted && transition.enter(el)
}, parentSuspense)
}
}
@ -1468,11 +1471,19 @@ export function createRenderer<
const remove = () => {
hostRemove(vnode.el!)
if (anchor != null) hostRemove(anchor)
if (transition != null && transition.afterLeave) {
if (
transition != null &&
!transition.persisted &&
transition.afterLeave
) {
transition.afterLeave()
}
}
if (vnode.shapeFlag & ShapeFlags.ELEMENT && transition != null) {
if (
vnode.shapeFlag & ShapeFlags.ELEMENT &&
transition != null &&
!transition.persisted
) {
const { leave, delayLeave } = transition
const performLeave = () => leave(el!, remove)
if (delayLeave) {

View File

@ -6,6 +6,7 @@ import {
FunctionalComponent
} from '@vue/runtime-core'
import { isObject } from '@vue/shared'
import { currentRenderingInstance } from 'packages/runtime-core/src/componentRenderUtils'
const TRANSITION = 'transition'
const ANIMATION = 'animation'
@ -18,12 +19,12 @@ export interface CSSTransitionProps extends TransitionProps {
enterFromClass?: string
enterActiveClass?: string
enterToClass?: string
appearFromClass?: string
appearActiveClass?: string
appearToClass?: string
leaveFromClass?: string
leaveActiveClass?: string
leaveToClass?: string
// if present, indicates this is a v-show transition by toggling the
// CSS display property instead of actually removing the element.
show?: boolean
}
export const CSSTransition: FunctionalComponent = (
@ -36,10 +37,13 @@ if (__DEV__) {
...(BaseTransition as any).props,
name: String,
type: String,
enterClass: String,
enterFromClass: String,
enterActiveClass: String,
enterToClass: String,
leaveClass: String,
appearFromClass: String,
appearActiveClass: String,
appearToClass: String,
leaveFromClass: String,
leaveActiveClass: String,
leaveToClass: String,
duration: Object
@ -53,6 +57,9 @@ function resolveCSSTransitionProps({
enterFromClass = `${name}-enter-from`,
enterActiveClass = `${name}-enter-active`,
enterToClass = `${name}-enter-to`,
appearFromClass = enterFromClass,
appearActiveClass = enterActiveClass,
appearToClass = enterToClass,
leaveFromClass = `${name}-leave-from`,
leaveActiveClass = `${name}-leave-active`,
leaveToClass = `${name}-leave-to`,
@ -61,7 +68,26 @@ function resolveCSSTransitionProps({
const durations = normalizeDuration(duration)
const enterDuration = durations && durations[0]
const leaveDuration = durations && durations[1]
const { onBeforeEnter, onEnter, onLeave } = baseProps
const { appear, onBeforeEnter, onEnter, onLeave } = baseProps
// is appearing
if (appear && !currentRenderingInstance!.subTree) {
enterFromClass = appearFromClass
enterActiveClass = appearActiveClass
enterToClass = appearToClass
}
function finishEnter(el: Element, done?: () => void) {
removeTransitionClass(el, enterToClass)
removeTransitionClass(el, enterActiveClass)
done && done()
}
function finishLeave(el: Element, done?: () => void) {
removeTransitionClass(el, leaveToClass)
removeTransitionClass(el, leaveActiveClass)
done && done()
}
return {
...baseProps,
@ -72,11 +98,7 @@ function resolveCSSTransitionProps({
},
onEnter(el, done) {
nextFrame(() => {
const resolve = () => {
removeTransitionClass(el, enterToClass)
removeTransitionClass(el, enterActiveClass)
done()
}
const resolve = () => finishEnter(el, done)
onEnter && onEnter(el, resolve)
removeTransitionClass(el, enterFromClass)
addTransitionClass(el, enterToClass)
@ -93,11 +115,7 @@ function resolveCSSTransitionProps({
addTransitionClass(el, leaveActiveClass)
addTransitionClass(el, leaveFromClass)
nextFrame(() => {
const resolve = () => {
removeTransitionClass(el, leaveToClass)
removeTransitionClass(el, leaveActiveClass)
done()
}
const resolve = () => finishLeave(el, done)
onLeave && onLeave(el, resolve)
removeTransitionClass(el, leaveFromClass)
addTransitionClass(el, leaveToClass)
@ -109,7 +127,9 @@ function resolveCSSTransitionProps({
}
}
})
}
},
onEnterCancelled: finishEnter,
onLeaveCancelled: finishLeave
}
}
@ -161,9 +181,11 @@ function addTransitionClass(el: ElementWithTransition, cls: string) {
function removeTransitionClass(el: ElementWithTransition, cls: string) {
el.classList.remove(cls)
el._vtc!.delete(cls)
if (!el._vtc!.size) {
el._vtc = undefined
if (el._vtc) {
el._vtc.delete(cls)
if (!el._vtc!.size) {
el._vtc = undefined
}
}
}

View File

@ -66,7 +66,7 @@ export {
export { withModifiers, withKeys } from './directives/vOn'
// DOM-only components
export { CSSTransition } from './components/CSSTransition'
export { CSSTransition, CSSTransitionProps } from './components/CSSTransition'
// re-export everything from core
// h, Component, reactivity API, nextTick, flags & types