import { warn, extend } from 'core/util/index' import { transitionProps, extractTransitionData } from './transition' const props = extend({ tag: String, moveClass: String }, transitionProps) delete props.mode export default { props, created () { const dom = this.$requireWeexModule('dom') this.getPosition = el => new Promise((resolve, reject) => { dom.getComponentRect(el.ref, res => { if (!res.result) { reject(new Error(`failed to get rect for element: ${el.tag}`)) } else { resolve(res.size) } }) }) const animation = this.$requireWeexModule('animation') this.animate = (el, options) => new Promise(resolve => { animation.transition(el.ref, options, resolve) }) }, render (h) { const tag = this.tag || this.$vnode.data.tag || 'span' const map = Object.create(null) const prevChildren = this.prevChildren = this.children const rawChildren = this.$slots.default || [] const children = this.children = [] const transitionData = extractTransitionData(this) for (let i = 0; i < rawChildren.length; i++) { const c = rawChildren[i] if (c.tag) { if (c.key != null && String(c.key).indexOf('__vlist') !== 0) { children.push(c) map[c.key] = c ;(c.data || (c.data = {})).transition = transitionData } else if (process.env.NODE_ENV !== 'production') { const opts = c.componentOptions const name = opts ? (opts.Ctor.options.name || opts.tag) : c.tag warn(`<transition-group> children must be keyed: <${name}>`) } } } if (prevChildren) { const kept = [] const removed = [] prevChildren.forEach(c => { c.data.transition = transitionData // TODO: record before patch positions if (map[c.key]) { kept.push(c) } else { removed.push(c) } }) this.kept = h(tag, null, kept) this.removed = removed } return h(tag, null, children) }, beforeUpdate () { // force removing pass this.__patch__( this._vnode, this.kept, false, // hydrating true // removeOnly (!important, avoids unnecessary moves) ) this._vnode = this.kept }, updated () { const children = this.prevChildren const moveClass = this.moveClass || ((this.name || 'v') + '-move') const moveData = children.length && this.getMoveData(children[0].context, moveClass) if (!moveData) { return } // TODO: finish implementing move animations once // we have access to sync getComponentRect() // children.forEach(callPendingCbs) // Promise.all(children.map(c => { // const oldPos = c.data.pos // const newPos = c.data.newPos // const dx = oldPos.left - newPos.left // const dy = oldPos.top - newPos.top // if (dx || dy) { // c.data.moved = true // return this.animate(c.elm, { // styles: { // transform: `translate(${dx}px,${dy}px)` // } // }) // } // })).then(() => { // children.forEach(c => { // if (c.data.moved) { // this.animate(c.elm, { // styles: { // transform: '' // }, // duration: moveData.duration || 0, // delay: moveData.delay || 0, // timingFunction: moveData.timingFunction || 'linear' // }) // } // }) // }) }, methods: { getMoveData (context, moveClass) { const stylesheet = context.$options.style || {} return stylesheet['@TRANSITION'] && stylesheet['@TRANSITION'][moveClass] } } } // function callPendingCbs (c) { // /* istanbul ignore if */ // if (c.elm._moveCb) { // c.elm._moveCb() // } // /* istanbul ignore if */ // if (c.elm._enterCb) { // c.elm._enterCb() // } // }