| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- /* @flow */
- import { inBrowser, isIE9 } from 'core/util/index'
- import { cached, extend } from 'shared/util'
- import { mergeVNodeHook } from 'core/vdom/helpers/index'
- import { activeInstance } from 'core/instance/lifecycle'
- import {
- nextFrame,
- addTransitionClass,
- removeTransitionClass,
- whenTransitionEnds
- } from '../transition-util'
- export function enter (vnode: VNodeWithData, toggleDisplay: ?() => void) {
- const el: any = vnode.elm
- // call leave callback now
- if (el._leaveCb) {
- el._leaveCb.cancelled = true
- el._leaveCb()
- }
- const data = resolveTransition(vnode.data.transition)
- if (!data) {
- return
- }
- /* istanbul ignore if */
- if (el._enterCb || el.nodeType !== 1) {
- return
- }
- const {
- css,
- type,
- enterClass,
- enterToClass,
- enterActiveClass,
- appearClass,
- appearToClass,
- appearActiveClass,
- beforeEnter,
- enter,
- afterEnter,
- enterCancelled,
- beforeAppear,
- appear,
- afterAppear,
- appearCancelled
- } = data
- // activeInstance will always be the <transition> component managing this
- // transition. One edge case to check is when the <transition> is placed
- // as the root node of a child component. In that case we need to check
- // <transition>'s parent for appear check.
- let context = activeInstance
- let transitionNode = activeInstance.$vnode
- while (transitionNode && transitionNode.parent) {
- transitionNode = transitionNode.parent
- context = transitionNode.context
- }
- const isAppear = !context._isMounted || !vnode.isRootInsert
- if (isAppear && !appear && appear !== '') {
- return
- }
- const startClass = isAppear ? appearClass : enterClass
- const activeClass = isAppear ? appearActiveClass : enterActiveClass
- const toClass = isAppear ? appearToClass : enterToClass
- const beforeEnterHook = isAppear ? (beforeAppear || beforeEnter) : beforeEnter
- const enterHook = isAppear ? (typeof appear === 'function' ? appear : enter) : enter
- const afterEnterHook = isAppear ? (afterAppear || afterEnter) : afterEnter
- const enterCancelledHook = isAppear ? (appearCancelled || enterCancelled) : enterCancelled
- const expectsCSS = css !== false && !isIE9
- const userWantsControl =
- enterHook &&
- // enterHook may be a bound method which exposes
- // the length of original fn as _length
- (enterHook._length || enterHook.length) > 1
- const cb = el._enterCb = once(() => {
- if (expectsCSS) {
- removeTransitionClass(el, toClass)
- removeTransitionClass(el, activeClass)
- }
- if (cb.cancelled) {
- if (expectsCSS) {
- removeTransitionClass(el, startClass)
- }
- enterCancelledHook && enterCancelledHook(el)
- } else {
- afterEnterHook && afterEnterHook(el)
- }
- el._enterCb = null
- })
- if (!vnode.data.show) {
- // remove pending leave element on enter by injecting an insert hook
- mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {
- const parent = el.parentNode
- const pendingNode = parent && parent._pending && parent._pending[vnode.key]
- if (pendingNode &&
- pendingNode.tag === vnode.tag &&
- pendingNode.elm._leaveCb) {
- pendingNode.elm._leaveCb()
- }
- enterHook && enterHook(el, cb)
- }, 'transition-insert')
- }
- // start enter transition
- beforeEnterHook && beforeEnterHook(el)
- if (expectsCSS) {
- addTransitionClass(el, startClass)
- addTransitionClass(el, activeClass)
- nextFrame(() => {
- addTransitionClass(el, toClass)
- removeTransitionClass(el, startClass)
- if (!cb.cancelled && !userWantsControl) {
- whenTransitionEnds(el, type, cb)
- }
- })
- }
- if (vnode.data.show) {
- toggleDisplay && toggleDisplay()
- enterHook && enterHook(el, cb)
- }
- if (!expectsCSS && !userWantsControl) {
- cb()
- }
- }
- export function leave (vnode: VNodeWithData, rm: Function) {
- const el: any = vnode.elm
- // call enter callback now
- if (el._enterCb) {
- el._enterCb.cancelled = true
- el._enterCb()
- }
- const data = resolveTransition(vnode.data.transition)
- if (!data) {
- return rm()
- }
- /* istanbul ignore if */
- if (el._leaveCb || el.nodeType !== 1) {
- return
- }
- const {
- css,
- type,
- leaveClass,
- leaveToClass,
- leaveActiveClass,
- beforeLeave,
- leave,
- afterLeave,
- leaveCancelled,
- delayLeave
- } = data
- const expectsCSS = css !== false && !isIE9
- const userWantsControl =
- leave &&
- // leave hook may be a bound method which exposes
- // the length of original fn as _length
- (leave._length || leave.length) > 1
- const cb = el._leaveCb = once(() => {
- if (el.parentNode && el.parentNode._pending) {
- el.parentNode._pending[vnode.key] = null
- }
- if (expectsCSS) {
- removeTransitionClass(el, leaveToClass)
- removeTransitionClass(el, leaveActiveClass)
- }
- if (cb.cancelled) {
- if (expectsCSS) {
- removeTransitionClass(el, leaveClass)
- }
- leaveCancelled && leaveCancelled(el)
- } else {
- rm()
- afterLeave && afterLeave(el)
- }
- el._leaveCb = null
- })
- if (delayLeave) {
- delayLeave(performLeave)
- } else {
- performLeave()
- }
- function performLeave () {
- // the delayed leave may have already been cancelled
- if (cb.cancelled) {
- return
- }
- // record leaving element
- if (!vnode.data.show) {
- (el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key] = vnode
- }
- beforeLeave && beforeLeave(el)
- if (expectsCSS) {
- addTransitionClass(el, leaveClass)
- addTransitionClass(el, leaveActiveClass)
- nextFrame(() => {
- addTransitionClass(el, leaveToClass)
- removeTransitionClass(el, leaveClass)
- if (!cb.cancelled && !userWantsControl) {
- whenTransitionEnds(el, type, cb)
- }
- })
- }
- leave && leave(el, cb)
- if (!expectsCSS && !userWantsControl) {
- cb()
- }
- }
- }
- function resolveTransition (def?: string | Object): ?Object {
- if (!def) {
- return
- }
- /* istanbul ignore else */
- if (typeof def === 'object') {
- const res = {}
- if (def.css !== false) {
- extend(res, autoCssTransition(def.name || 'v'))
- }
- extend(res, def)
- return res
- } else if (typeof def === 'string') {
- return autoCssTransition(def)
- }
- }
- const autoCssTransition: (name: string) => Object = cached(name => {
- return {
- enterClass: `${name}-enter`,
- leaveClass: `${name}-leave`,
- appearClass: `${name}-enter`,
- enterToClass: `${name}-enter-to`,
- leaveToClass: `${name}-leave-to`,
- appearToClass: `${name}-enter-to`,
- enterActiveClass: `${name}-enter-active`,
- leaveActiveClass: `${name}-leave-active`,
- appearActiveClass: `${name}-enter-active`
- }
- })
- function once (fn: Function): Function {
- let called = false
- return () => {
- if (!called) {
- called = true
- fn()
- }
- }
- }
- function _enter (_: any, vnode: VNodeWithData) {
- if (!vnode.data.show) {
- enter(vnode)
- }
- }
- export default inBrowser ? {
- create: _enter,
- activate: _enter,
- remove (vnode: VNode, rm: Function) {
- /* istanbul ignore else */
- if (!vnode.data.show) {
- leave(vnode, rm)
- } else {
- rm()
- }
- }
- } : {}
|