diff options
Diffstat (limited to 'node_modules/vue/src/platforms/web/runtime/modules/transition.js')
| -rw-r--r-- | node_modules/vue/src/platforms/web/runtime/modules/transition.js | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/node_modules/vue/src/platforms/web/runtime/modules/transition.js b/node_modules/vue/src/platforms/web/runtime/modules/transition.js new file mode 100644 index 00000000..0796b5da --- /dev/null +++ b/node_modules/vue/src/platforms/web/runtime/modules/transition.js @@ -0,0 +1,343 @@ +/* @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) { + transitionNode = transitionNode.parent + context = transitionNode.context + } + + 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._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() + } + } +} : {} |
