zarm-web
Version:
基于 React 的桌面端UI库
264 lines (234 loc) • 7.09 kB
JavaScript
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import { Component, isValidElement, Children, cloneElement } from 'react';
import classnames from 'classnames';
import { noop } from '../utils';
const TRANSITION_TYPE = {
ENTERING: 'show',
EXITING: 'hide'
};
export default class Transition extends Component {
constructor(_props) {
super(_props);
this.nextStatus = void 0;
this.timeoutId = void 0;
this.computeInitialStatus = () => {
const {
visible,
mountOnShow,
transitionOnMount,
unmountOnHide
} = this.props;
if (visible) {
if (transitionOnMount) {
return {
initial: Transition.EXITED,
next: Transition.ENTERING
};
}
return {
initial: Transition.ENTERED
};
}
if (mountOnShow || unmountOnHide) return {
initial: Transition.UNMOUNTED
};
return {
initial: Transition.EXITED
};
};
this.computeNextStatus = () => {
const {
animating,
status
} = this.state;
if (animating) return status === Transition.ENTERING ? Transition.EXITING : Transition.ENTERING;
return status === Transition.ENTERED ? Transition.EXITING : Transition.ENTERING;
};
this.computeStatus = props => {
const {
status
} = this.state;
const {
visible
} = props;
if (visible) {
return {
status: status === Transition.UNMOUNTED && Transition.EXITED,
next: status !== Transition.ENTERING && status !== Transition.ENTERED && Transition.ENTERING
};
}
return {
next: (status === Transition.ENTERING || status === Transition.ENTERED) && Transition.EXITING
};
};
this.updateStatus = () => {
const {
animating
} = this.state;
if (this.nextStatus) {
this.nextStatus = this.computeNextStatus();
if (!animating) this.handleStart();
}
};
this.handleStart = () => {
const {
duration
} = this.props;
const status = this.nextStatus;
this.nextStatus = null;
this.setState({
status,
animating: true
}, () => {
this.props.onStart(_objectSpread({}, this.props, {
status
}));
this.timeoutId = setTimeout(() => {
this.handleComplete();
}, duration);
});
};
this.handleComplete = () => {
const {
status
} = this.state;
this.props.onComplete(_objectSpread({}, this.props, {
status
}));
if (this.nextStatus) {
return this.handleStart();
}
const completeStatus = this.computeCompletedStatus();
const beforeHook = status === Transition.ENTERING ? 'onBeforeShow' : 'onBeforeHide';
const callback = status === Transition.ENTERING ? 'onShow' : 'onHide';
this.props[beforeHook](_objectSpread({}, this.props, {
status: completeStatus
}));
this.setState({
status: completeStatus,
animating: false
}, () => {
this.props[callback](_objectSpread({}, this.props, {
status: completeStatus
}));
});
};
this.computeCompletedStatus = () => {
const {
unmountOnHide
} = this.props;
const {
status
} = this.state;
if (status === Transition.ENTERING) return Transition.ENTERED;
return unmountOnHide ? Transition.UNMOUNTED : Transition.EXITED;
};
this.computeClasses = () => {
const {
name,
children,
directional
} = this.props;
const {
animating,
status
} = this.state;
const childrenClass = children.props.className;
if (directional) {
return classnames(childrenClass, {
animating,
[`${name}`]: name,
[`${name}-in`]: status === Transition.ENTERING,
[`${name}-out`]: status === Transition.EXITING,
hidden: status === Transition.EXITED,
visible: status !== Transition.EXITED,
transition: true
});
}
return classnames(childrenClass, {
animating,
transition: true,
[`${name}`]: name
});
};
this.computeStyle = () => {
const {
children,
duration
} = this.props;
const {
status
} = this.state;
const childrenStyle = children.props.style;
const type = TRANSITION_TYPE[status];
const animationDuration = type && `${duration}ms`;
return _objectSpread({}, childrenStyle, {
animationDuration
});
};
const {
initial: _status,
next
} = this.computeInitialStatus();
this.nextStatus = next;
this.state = {
status: _status
};
}
componentDidMount() {
this.updateStatus();
}
componentWillReceiveProps(nextProps) {
const {
status,
next
} = this.computeStatus(nextProps);
this.nextStatus = next;
if (status) this.setState({
status
});
}
componentDidUpdate() {
this.updateStatus();
}
componentWillUnmount() {
clearTimeout(this.timeoutId);
}
render() {
const {
children
} = this.props;
const {
status
} = this.state;
if (status === Transition.UNMOUNTED) return null;
if (isValidElement(children) && Children.only(children)) {
return cloneElement(children, {
className: this.computeClasses(),
style: this.computeStyle()
});
}
}
}
Transition.defaultProps = {
name: 'fade',
duration: 500,
directional: true,
visible: true,
mountOnShow: true,
transitionOnMount: false,
unmountOnHide: false,
onStart: noop,
onShow: noop,
onHide: noop,
onComplete: noop,
onBeforeShow: noop,
onBeforeHide: noop
};
Transition.ENTERED = 'ENTERED';
Transition.ENTERING = 'ENTERING';
Transition.EXITED = 'EXITED';
Transition.EXITING = 'EXITING';
Transition.UNMOUNTED = 'UNMOUNTED';