pull/9/head
tangjinzhou 2017-12-13 10:06:26 +08:00
parent 8736a23884
commit 37b40bcd38
8 changed files with 48 additions and 572 deletions

View File

@ -3,6 +3,7 @@ import PropTypes from 'vue-types'
import align from 'dom-align'
import addEventListener from '../_util/Dom/addEventListener'
import cloneElement from '../_util/cloneElement'
import isWindow from './isWindow'
function noop () {
}
@ -35,7 +36,39 @@ export default {
monitorWindowResize: PropTypes.bool.def(false),
disabled: PropTypes.bool.def(false),
},
watch: {
'$props': {
handler: function (props, prevProps) {
this.$nextTick(() => {
let reAlign = false
if (!props.disabled) {
if (prevProps.disabled || prevProps.align !== props.align) {
reAlign = true
} else {
const lastTarget = prevProps.target()
const currentTarget = props.target()
if (isWindow(lastTarget) && isWindow(currentTarget)) {
reAlign = false
} else if (lastTarget !== currentTarget) {
reAlign = true
}
}
}
if (reAlign) {
this.forceAlign()
}
if (props.monitorWindowResize && !props.disabled) {
this.startMonitorWindowResize()
} else {
this.stopMonitorWindowResize()
}
})
},
deep: true,
},
},
mounted () {
const props = this.$props
// if parent ref not attached .... use document.getElementById
@ -44,20 +77,7 @@ export default {
this.startMonitorWindowResize()
}
},
updated () {
const props = this.$props
this.forceAlign()
if (props.monitorWindowResize && !props.disabled) {
this.startMonitorWindowResize()
} else {
this.stopMonitorWindowResize()
}
},
beforeDestory () {
beforeDestroy () {
this.stopMonitorWindowResize()
},
methods: {
@ -86,6 +106,7 @@ export default {
},
render () {
console.log(4)
const { childrenProps } = this.$props
const child = this.$slots.default[0]
if (childrenProps) {

View File

@ -1,3 +0,0 @@
// do not modify this file
import Animate from './src/Animate'
export default Animate

View File

@ -1,333 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import {
toArrayChildren,
mergeChildren,
findShownChildInChildrenByKey,
findChildInChildrenByKey,
isSameChildren,
} from './ChildrenUtils'
import AnimateChild from './AnimateChild'
const defaultKey = `rc_animate_${Date.now()}`
import animUtil from './util'
function getChildrenFromProps (props) {
const children = props.children
if (React.isValidElement(children)) {
if (!children.key) {
return React.cloneElement(children, {
key: defaultKey,
})
}
}
return children
}
function noop () {
}
export default class Animate extends React.Component {
static propTypes = {
component: PropTypes.any,
componentProps: PropTypes.object,
animation: PropTypes.object,
transitionName: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
transitionEnter: PropTypes.bool,
transitionAppear: PropTypes.bool,
exclusive: PropTypes.bool,
transitionLeave: PropTypes.bool,
onEnd: PropTypes.func,
onEnter: PropTypes.func,
onLeave: PropTypes.func,
onAppear: PropTypes.func,
showProp: PropTypes.string,
}
static defaultProps = {
animation: {},
component: 'span',
componentProps: {},
transitionEnter: true,
transitionLeave: true,
transitionAppear: false,
onEnd: noop,
onEnter: noop,
onLeave: noop,
onAppear: noop,
}
constructor (props) {
super(props)
this.currentlyAnimatingKeys = {}
this.keysToEnter = []
this.keysToLeave = []
this.state = {
children: toArrayChildren(getChildrenFromProps(this.props)),
}
this.childrenRefs = {}
}
componentDidMount () {
const showProp = this.props.showProp
let children = this.state.children
if (showProp) {
children = children.filter((child) => {
return !!child.props[showProp]
})
}
children.forEach((child) => {
if (child) {
this.performAppear(child.key)
}
})
}
componentWillReceiveProps (nextProps) {
this.nextProps = nextProps
const nextChildren = toArrayChildren(getChildrenFromProps(nextProps))
const props = this.props
// exclusive needs immediate response
if (props.exclusive) {
Object.keys(this.currentlyAnimatingKeys).forEach((key) => {
this.stop(key)
})
}
const showProp = props.showProp
const currentlyAnimatingKeys = this.currentlyAnimatingKeys
// last props children if exclusive
const currentChildren = props.exclusive
? toArrayChildren(getChildrenFromProps(props))
: this.state.children
// in case destroy in showProp mode
let newChildren = []
if (showProp) {
currentChildren.forEach((currentChild) => {
const nextChild = currentChild && findChildInChildrenByKey(nextChildren, currentChild.key)
let newChild
if ((!nextChild || !nextChild.props[showProp]) && currentChild.props[showProp]) {
newChild = React.cloneElement(nextChild || currentChild, {
[showProp]: true,
})
} else {
newChild = nextChild
}
if (newChild) {
newChildren.push(newChild)
}
})
nextChildren.forEach((nextChild) => {
if (!nextChild || !findChildInChildrenByKey(currentChildren, nextChild.key)) {
newChildren.push(nextChild)
}
})
} else {
newChildren = mergeChildren(
currentChildren,
nextChildren
)
}
// need render to avoid update
this.setState({
children: newChildren,
})
nextChildren.forEach((child) => {
const key = child && child.key
if (child && currentlyAnimatingKeys[key]) {
return
}
const hasPrev = child && findChildInChildrenByKey(currentChildren, key)
if (showProp) {
const showInNext = child.props[showProp]
if (hasPrev) {
const showInNow = findShownChildInChildrenByKey(currentChildren, key, showProp)
if (!showInNow && showInNext) {
this.keysToEnter.push(key)
}
} else if (showInNext) {
this.keysToEnter.push(key)
}
} else if (!hasPrev) {
this.keysToEnter.push(key)
}
})
currentChildren.forEach((child) => {
const key = child && child.key
if (child && currentlyAnimatingKeys[key]) {
return
}
const hasNext = child && findChildInChildrenByKey(nextChildren, key)
if (showProp) {
const showInNow = child.props[showProp]
if (hasNext) {
const showInNext = findShownChildInChildrenByKey(nextChildren, key, showProp)
if (!showInNext && showInNow) {
this.keysToLeave.push(key)
}
} else if (showInNow) {
this.keysToLeave.push(key)
}
} else if (!hasNext) {
this.keysToLeave.push(key)
}
})
}
componentDidUpdate () {
const keysToEnter = this.keysToEnter
this.keysToEnter = []
keysToEnter.forEach(this.performEnter)
const keysToLeave = this.keysToLeave
this.keysToLeave = []
keysToLeave.forEach(this.performLeave)
}
performEnter = (key) => {
// may already remove by exclusive
if (this.childrenRefs[key]) {
this.currentlyAnimatingKeys[key] = true
this.childrenRefs[key].componentWillEnter(
this.handleDoneAdding.bind(this, key, 'enter')
)
}
}
performAppear = (key) => {
if (this.childrenRefs[key]) {
this.currentlyAnimatingKeys[key] = true
this.childrenRefs[key].componentWillAppear(
this.handleDoneAdding.bind(this, key, 'appear')
)
}
}
handleDoneAdding = (key, type) => {
const props = this.props
delete this.currentlyAnimatingKeys[key]
// if update on exclusive mode, skip check
if (props.exclusive && props !== this.nextProps) {
return
}
const currentChildren = toArrayChildren(getChildrenFromProps(props))
if (!this.isValidChildByKey(currentChildren, key)) {
// exclusive will not need this
this.performLeave(key)
} else {
if (type === 'appear') {
if (animUtil.allowAppearCallback(props)) {
props.onAppear(key)
props.onEnd(key, true)
}
} else {
if (animUtil.allowEnterCallback(props)) {
props.onEnter(key)
props.onEnd(key, true)
}
}
}
}
performLeave = (key) => {
// may already remove by exclusive
if (this.childrenRefs[key]) {
this.currentlyAnimatingKeys[key] = true
this.childrenRefs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
}
}
handleDoneLeaving = (key) => {
const props = this.props
delete this.currentlyAnimatingKeys[key]
// if update on exclusive mode, skip check
if (props.exclusive && props !== this.nextProps) {
return
}
const currentChildren = toArrayChildren(getChildrenFromProps(props))
// in case state change is too fast
if (this.isValidChildByKey(currentChildren, key)) {
this.performEnter(key)
} else {
const end = () => {
if (animUtil.allowLeaveCallback(props)) {
props.onLeave(key)
props.onEnd(key, false)
}
}
if (!isSameChildren(this.state.children,
currentChildren, props.showProp)) {
this.setState({
children: currentChildren,
}, end)
} else {
end()
}
}
}
isValidChildByKey (currentChildren, key) {
const showProp = this.props.showProp
if (showProp) {
return findShownChildInChildrenByKey(currentChildren, key, showProp)
}
return findChildInChildrenByKey(currentChildren, key)
}
stop (key) {
delete this.currentlyAnimatingKeys[key]
const component = this.childrenRefs[key]
if (component) {
component.stop()
}
}
render () {
const props = this.props
this.nextProps = props
const stateChildren = this.state.children
let children = null
if (stateChildren) {
children = stateChildren.map((child) => {
if (child === null || child === undefined) {
return child
}
if (!child.key) {
throw new Error('must set key for <rc-animate> children')
}
return (
<AnimateChild
key={child.key}
ref={node => this.childrenRefs[child.key] = node}
animation={props.animation}
transitionName={props.transitionName}
transitionEnter={props.transitionEnter}
transitionAppear={props.transitionAppear}
transitionLeave={props.transitionLeave}
>
{child}
</AnimateChild>
)
})
}
const Component = props.component
if (Component) {
let passedProps = props
if (typeof Component === 'string') {
passedProps = {
className: props.className,
style: props.style,
...props.componentProps,
}
}
return <Component {...passedProps}>{children}</Component>
}
return children[0] || null
}
}

View File

@ -1,86 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import cssAnimate, { isCssAnimationSupported } from 'css-animation'
import animUtil from './util'
const transitionMap = {
enter: 'transitionEnter',
appear: 'transitionAppear',
leave: 'transitionLeave',
}
export default class AnimateChild extends React.Component {
static propTypes = {
children: PropTypes.any,
}
componentWillUnmount () {
this.stop()
}
componentWillEnter (done) {
if (animUtil.isEnterSupported(this.props)) {
this.transition('enter', done)
} else {
done()
}
}
componentWillAppear (done) {
if (animUtil.isAppearSupported(this.props)) {
this.transition('appear', done)
} else {
done()
}
}
componentWillLeave (done) {
if (animUtil.isLeaveSupported(this.props)) {
this.transition('leave', done)
} else {
// always sync, do not interupt with react component life cycle
// update hidden -> animate hidden ->
// didUpdate -> animate leave -> unmount (if animate is none)
done()
}
}
transition (animationType, finishCallback) {
const node = ReactDOM.findDOMNode(this)
const props = this.props
const transitionName = props.transitionName
const nameIsObj = typeof transitionName === 'object'
this.stop()
const end = () => {
this.stopper = null
finishCallback()
}
if ((isCssAnimationSupported || !props.animation[animationType]) &&
transitionName && props[transitionMap[animationType]]) {
const name = nameIsObj ? transitionName[animationType] : `${transitionName}-${animationType}`
let activeName = `${name}-active`
if (nameIsObj && transitionName[`${animationType}Active`]) {
activeName = transitionName[`${animationType}Active`]
}
this.stopper = cssAnimate(node, {
name,
active: activeName,
}, end)
} else {
this.stopper = props.animation[animationType](node, end)
}
}
stop () {
const stopper = this.stopper
if (stopper) {
this.stopper = null
stopper.stop()
}
}
render () {
return this.props.children
}
}

View File

@ -1,101 +0,0 @@
import React from 'react'
export function toArrayChildren (children) {
const ret = []
React.Children.forEach(children, (child) => {
ret.push(child)
})
return ret
}
export function findChildInChildrenByKey (children, key) {
let ret = null
if (children) {
children.forEach((child) => {
if (ret) {
return
}
if (child && child.key === key) {
ret = child
}
})
}
return ret
}
export function findShownChildInChildrenByKey (children, key, showProp) {
let ret = null
if (children) {
children.forEach((child) => {
if (child && child.key === key && child.props[showProp]) {
if (ret) {
throw new Error('two child with same key for <rc-animate> children')
}
ret = child
}
})
}
return ret
}
export function findHiddenChildInChildrenByKey (children, key, showProp) {
let found = 0
if (children) {
children.forEach((child) => {
if (found) {
return
}
found = child && child.key === key && !child.props[showProp]
})
}
return found
}
export function isSameChildren (c1, c2, showProp) {
let same = c1.length === c2.length
if (same) {
c1.forEach((child, index) => {
const child2 = c2[index]
if (child && child2) {
if ((child && !child2) || (!child && child2)) {
same = false
} else if (child.key !== child2.key) {
same = false
} else if (showProp && child.props[showProp] !== child2.props[showProp]) {
same = false
}
}
})
}
return same
}
export function mergeChildren (prev, next) {
let ret = []
// For each key of `next`, the list of keys to insert before that key in
// the combined list
const nextChildrenPending = {}
let pendingChildren = []
prev.forEach((child) => {
if (child && findChildInChildrenByKey(next, child.key)) {
if (pendingChildren.length) {
nextChildrenPending[child.key] = pendingChildren
pendingChildren = []
}
} else {
pendingChildren.push(child)
}
})
next.forEach((child) => {
if (child && nextChildrenPending.hasOwnProperty(child.key)) {
ret = ret.concat(nextChildrenPending[child.key])
}
ret.push(child)
})
ret = ret.concat(pendingChildren)
return ret
}

View File

@ -1,21 +0,0 @@
const util = {
isAppearSupported(props) {
return props.transitionName && props.transitionAppear || props.animation.appear;
},
isEnterSupported(props) {
return props.transitionName && props.transitionEnter || props.animation.enter;
},
isLeaveSupported(props) {
return props.transitionName && props.transitionLeave || props.animation.leave;
},
allowAppearCallback(props) {
return props.transitionAppear || props.animation.appear;
},
allowEnterCallback(props) {
return props.transitionEnter || props.animation.enter;
},
allowLeaveCallback(props) {
return props.transitionLeave || props.animation.leave;
},
};
export default util;

View File

@ -1,5 +1,3 @@
import React from 'react'
export function noop () {
}
@ -9,18 +7,18 @@ export function getKeyFromChildrenIndex (child, menuEventKey, index) {
}
export function loopMenuItem (children, cb) {
let index = -1
React.Children.forEach(children, (c) => {
index++
if (c && c.type && c.type.isMenuItemGroup) {
React.Children.forEach(c.props.children, (c2) => {
index++
cb(c2, index)
})
} else {
cb(c, index)
}
})
// let index = -1
// React.Children.forEach(children, (c) => {
// index++
// if (c && c.type && c.type.isMenuItemGroup) {
// React.Children.forEach(c.props.children, (c2) => {
// index++
// cb(c2, index)
// })
// } else {
// cb(c, index)
// }
// })
}
export function loopMenuItemRecusively (children, keys, ret) {

View File

@ -77,6 +77,7 @@
"add-dom-event-listener": "^1.0.2",
"css-animation": "^1.4.1",
"eslint-plugin-vue": "^3.13.0",
"lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"omit.js": "^1.0.0",
"vue-types": "^1.0.2",