123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- /* @flow */
- import { inBrowser, isIE9, warn } from 'core/util/index'
- import { mergeVNodeHook } from 'core/vdom/helpers/index'
- import { activeInstance } from 'core/instance/lifecycle'
- import {
- once,
- isDef,
- isUndef,
- isObject,
- toNumber
- } from 'shared/util'
- import {
- nextFrame,
- resolveTransition,
- whenTransitionEnds,
- addTransitionClass,
- removeTransitionClass
- } from '../transition-util'
- export function enter (vnode: VNodeWithData, toggleDisplay: ?() => void) {
- const el: any = vnode.elm
- // call leave callback now
- if (isDef(el._leaveCb)) {
- el._leaveCb.cancelled = true
- el._leaveCb()
- }
- const data = resolveTransition(vnode.data.transition)
- if (isUndef(data)) {
- return
- }
- /* istanbul ignore if */
- if (isDef(el._enterCb) || el.nodeType !== 1) {
- return
- }
- const {
- css,
- type,
- enterClass,
- enterToClass,
- enterActiveClass,
- appearClass,
- appearToClass,
- appearActiveClass,
- beforeEnter,
- enter,
- afterEnter,
- enterCancelled,
- beforeAppear,
- appear,
- afterAppear,
- appearCancelled,
- duration
- } = 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) {
- context = transitionNode.context
- transitionNode = transitionNode.parent
- }
- const isAppear = !context._isMounted || !vnode.isRootInsert
- if (isAppear && !appear && appear !== '') {
- return
- }
- const startClass = isAppear && appearClass
- ? appearClass
- : enterClass
- const activeClass = isAppear && appearActiveClass
- ? appearActiveClass
- : enterActiveClass
- const toClass = isAppear && appearToClass
- ? 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 explicitEnterDuration: any = toNumber(
- isObject(duration)
- ? duration.enter
- : duration
- )
- if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) {
- checkDuration(explicitEnterDuration, 'enter', vnode)
- }
- const expectsCSS = css !== false && !isIE9
- const userWantsControl = getHookArgumentsLength(enterHook)
- 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, '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)
- })
- }
- // start enter transition
- beforeEnterHook && beforeEnterHook(el)
- if (expectsCSS) {
- addTransitionClass(el, startClass)
- addTransitionClass(el, activeClass)
- nextFrame(() => {
- removeTransitionClass(el, startClass)
- if (!cb.cancelled) {
- addTransitionClass(el, toClass)
- if (!userWantsControl) {
- if (isValidDuration(explicitEnterDuration)) {
- setTimeout(cb, explicitEnterDuration)
- } else {
- 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 (isDef(el._enterCb)) {
- el._enterCb.cancelled = true
- el._enterCb()
- }
- const data = resolveTransition(vnode.data.transition)
- if (isUndef(data) || el.nodeType !== 1) {
- return rm()
- }
- /* istanbul ignore if */
- if (isDef(el._leaveCb)) {
- return
- }
- const {
- css,
- type,
- leaveClass,
- leaveToClass,
- leaveActiveClass,
- beforeLeave,
- leave,
- afterLeave,
- leaveCancelled,
- delayLeave,
- duration
- } = data
- const expectsCSS = css !== false && !isIE9
- const userWantsControl = getHookArgumentsLength(leave)
- const explicitLeaveDuration: any = toNumber(
- isObject(duration)
- ? duration.leave
- : duration
- )
- if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) {
- checkDuration(explicitLeaveDuration, 'leave', vnode)
- }
- 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) {
- (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key: any)] = vnode
- }
- beforeLeave && beforeLeave(el)
- if (expectsCSS) {
- addTransitionClass(el, leaveClass)
- addTransitionClass(el, leaveActiveClass)
- nextFrame(() => {
- removeTransitionClass(el, leaveClass)
- if (!cb.cancelled) {
- addTransitionClass(el, leaveToClass)
- if (!userWantsControl) {
- if (isValidDuration(explicitLeaveDuration)) {
- setTimeout(cb, explicitLeaveDuration)
- } else {
- whenTransitionEnds(el, type, cb)
- }
- }
- }
- })
- }
- leave && leave(el, cb)
- if (!expectsCSS && !userWantsControl) {
- cb()
- }
- }
- }
- // only used in dev mode
- function checkDuration (val, name, vnode) {
- if (typeof val !== 'number') {
- warn(
- `<transition> explicit ${name} duration is not a valid number - ` +
- `got ${JSON.stringify(val)}.`,
- vnode.context
- )
- } else if (isNaN(val)) {
- warn(
- `<transition> explicit ${name} duration is NaN - ` +
- 'the duration expression might be incorrect.',
- vnode.context
- )
- }
- }
- function isValidDuration (val) {
- return typeof val === 'number' && !isNaN(val)
- }
- /**
- * Normalize a transition hook's argument length. The hook may be:
- * - a merged hook (invoker) with the original in .fns
- * - a wrapped component method (check ._length)
- * - a plain function (.length)
- */
- function getHookArgumentsLength (fn: Function): boolean {
- if (isUndef(fn)) {
- return false
- }
- const invokerFns = fn.fns
- if (isDef(invokerFns)) {
- // invoker
- return getHookArgumentsLength(
- Array.isArray(invokerFns)
- ? invokerFns[0]
- : invokerFns
- )
- } else {
- return (fn._length || fn.length) > 1
- }
- }
- function _enter (_: any, vnode: VNodeWithData) {
- if (vnode.data.show !== true) {
- enter(vnode)
- }
- }
- export default inBrowser ? {
- create: _enter,
- activate: _enter,
- remove (vnode: VNode, rm: Function) {
- /* istanbul ignore else */
- if (vnode.data.show !== true) {
- leave(vnode, rm)
- } else {
- rm()
- }
- }
- } : {}
|