feat(transition): support in templates

This commit is contained in:
Evan You
2019-11-24 18:37:59 -05:00
parent a834807942
commit 1765985ec2
4 changed files with 67 additions and 33 deletions

View File

@@ -9,6 +9,7 @@ import { isKeepAlive } from './KeepAlive'
import { toRaw } from '@vue/reactivity'
import { callWithAsyncErrorHandling, ErrorCodes } from '../errorHandling'
import { ShapeFlags } from '../shapeFlags'
import { onBeforeUnmount, onMounted } from '../apiLifecycle'
export interface BaseTransitionProps {
mode?: 'in-out' | 'out-in' | 'default'
@@ -22,9 +23,8 @@ export interface BaseTransitionProps {
persisted?: boolean
// Hooks. Using camel casef for easier usage in render functions & JSX.
// In templates these will be written as @before-enter="xxx"
// The compiler has special handling to convert them into the proper cases.
// enter
// In templates these can be written as @before-enter="xxx" as prop names
// are camelized
onBeforeEnter?: (el: any) => void
onEnter?: (el: any, done: () => void) => void
onAfterEnter?: (el: any) => void
@@ -41,17 +41,29 @@ type TransitionHookCaller = (
args?: any[]
) => void
interface PendingCallbacks {
enter?: (cancelled?: boolean) => void
leave?: (cancelled?: boolean) => void
interface TransitionState {
isMounted: boolean
isLeaving: boolean
isUnmounting: boolean
pendingEnter?: (cancelled?: boolean) => void
pendingLeave?: (cancelled?: boolean) => void
}
const BaseTransitionImpl = {
name: `BaseTransition`,
setup(props: BaseTransitionProps, { slots }: SetupContext) {
const instance = getCurrentInstance()!
const pendingCallbacks: PendingCallbacks = {}
let isLeaving = false
const state: TransitionState = {
isMounted: false,
isLeaving: false,
isUnmounting: false
}
onMounted(() => {
state.isMounted = true
})
onBeforeUnmount(() => {
state.isUnmounting = true
})
const callTransitionHook: TransitionHookCaller = (hook, args) => {
hook &&
@@ -88,17 +100,17 @@ const BaseTransitionImpl = {
// at this point children has a guaranteed length of 1.
const child = children[0]
if (isLeaving) {
if (state.isLeaving) {
return placeholder(child)
}
let delayedLeave: (() => void) | undefined
const performDelayedLeave = () => delayedLeave && delayedLeave()
const transitionHooks = (child.transition = resolveTransitionHooks(
rawProps,
state,
callTransitionHook,
instance.isMounted,
pendingCallbacks,
performDelayedLeave
))
@@ -118,10 +130,10 @@ const BaseTransitionImpl = {
updateHOCTransitionData(oldChild, transitionHooks)
// switching between different views
if (mode === 'out-in') {
isLeaving = true
state.isLeaving = true
// return placeholder node and queue update when leave finishes
transitionHooks.afterLeave = () => {
isLeaving = false
state.isLeaving = false
instance.update()
}
return placeholder(child)
@@ -187,29 +199,28 @@ function resolveTransitionHooks(
onAfterLeave,
onLeaveCancelled
}: BaseTransitionProps,
state: TransitionState,
callHook: TransitionHookCaller,
isMounted: boolean,
pendingCallbacks: PendingCallbacks,
performDelayedLeave: () => void
): TransitionHooks {
return {
persisted,
beforeEnter(el) {
if (!isMounted && !appear) {
return
if (state.pendingLeave) {
state.pendingLeave(true /* cancelled */)
}
if (pendingCallbacks.leave) {
pendingCallbacks.leave(true /* cancelled */)
if (!appear && !state.isMounted) {
return
}
callHook(onBeforeEnter, [el])
},
enter(el) {
if (!isMounted && !appear) {
if (!appear && !state.isMounted) {
return
}
let called = false
const afterEnter = (pendingCallbacks.enter = (cancelled?) => {
const afterEnter = (state.pendingEnter = (cancelled?) => {
if (called) return
called = true
if (cancelled) {
@@ -218,7 +229,7 @@ function resolveTransitionHooks(
callHook(onAfterEnter, [el])
performDelayedLeave()
}
pendingCallbacks.enter = undefined
state.pendingEnter = undefined
})
if (onEnter) {
onEnter(el, afterEnter)
@@ -228,12 +239,15 @@ function resolveTransitionHooks(
},
leave(el, remove) {
if (pendingCallbacks.enter) {
pendingCallbacks.enter(true /* cancelled */)
if (state.pendingEnter) {
state.pendingEnter(true /* cancelled */)
}
if (state.isUnmounting) {
return remove()
}
callHook(onBeforeLeave, [el])
let called = false
const afterLeave = (pendingCallbacks.leave = (cancelled?) => {
const afterLeave = (state.pendingLeave = (cancelled?) => {
if (called) return
called = true
remove()
@@ -242,7 +256,7 @@ function resolveTransitionHooks(
} else {
callHook(onAfterLeave, [el])
}
pendingCallbacks.leave = undefined
state.pendingLeave = undefined
})
if (onLeave) {
onLeave(el, afterLeave)