Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. import _defineProperty from 'babel-runtime/helpers/defineProperty';
  2. import _extends from 'babel-runtime/helpers/extends';
  3. import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
  4. import _createClass from 'babel-runtime/helpers/createClass';
  5. import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
  6. import _inherits from 'babel-runtime/helpers/inherits';
  7. /* eslint-disable react/default-props-match-prop-types, react/no-multi-comp */
  8. import React from 'react';
  9. import PropTypes from 'prop-types';
  10. import { polyfill } from 'react-lifecycles-compat';
  11. import findDOMNode from 'rc-util/es/Dom/findDOMNode';
  12. import classNames from 'classnames';
  13. import raf from 'raf';
  14. import { getTransitionName, animationEndName, transitionEndName, supportTransition } from './util/motion';
  15. var STATUS_NONE = 'none';
  16. var STATUS_APPEAR = 'appear';
  17. var STATUS_ENTER = 'enter';
  18. var STATUS_LEAVE = 'leave';
  19. export var MotionPropTypes = {
  20. eventProps: PropTypes.object, // Internal usage. Only pass by CSSMotionList
  21. visible: PropTypes.bool,
  22. children: PropTypes.func,
  23. motionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
  24. motionAppear: PropTypes.bool,
  25. motionEnter: PropTypes.bool,
  26. motionLeave: PropTypes.bool,
  27. motionLeaveImmediately: PropTypes.bool, // Trigger leave motion immediately
  28. motionDeadline: PropTypes.number,
  29. removeOnLeave: PropTypes.bool,
  30. leavedClassName: PropTypes.string,
  31. onAppearStart: PropTypes.func,
  32. onAppearActive: PropTypes.func,
  33. onAppearEnd: PropTypes.func,
  34. onEnterStart: PropTypes.func,
  35. onEnterActive: PropTypes.func,
  36. onEnterEnd: PropTypes.func,
  37. onLeaveStart: PropTypes.func,
  38. onLeaveActive: PropTypes.func,
  39. onLeaveEnd: PropTypes.func
  40. };
  41. /**
  42. * `transitionSupport` is used for none transition test case.
  43. * Default we use browser transition event support check.
  44. */
  45. export function genCSSMotion(config) {
  46. var transitionSupport = config;
  47. var forwardRef = !!React.forwardRef;
  48. if (typeof config === 'object') {
  49. transitionSupport = config.transitionSupport;
  50. forwardRef = 'forwardRef' in config ? config.forwardRef : forwardRef;
  51. }
  52. function isSupportTransition(props) {
  53. return !!(props.motionName && transitionSupport);
  54. }
  55. var CSSMotion = function (_React$Component) {
  56. _inherits(CSSMotion, _React$Component);
  57. function CSSMotion() {
  58. _classCallCheck(this, CSSMotion);
  59. var _this = _possibleConstructorReturn(this, (CSSMotion.__proto__ || Object.getPrototypeOf(CSSMotion)).call(this));
  60. _this.onDomUpdate = function () {
  61. var _this$state = _this.state,
  62. status = _this$state.status,
  63. newStatus = _this$state.newStatus;
  64. var _this$props = _this.props,
  65. onAppearStart = _this$props.onAppearStart,
  66. onEnterStart = _this$props.onEnterStart,
  67. onLeaveStart = _this$props.onLeaveStart,
  68. onAppearActive = _this$props.onAppearActive,
  69. onEnterActive = _this$props.onEnterActive,
  70. onLeaveActive = _this$props.onLeaveActive,
  71. motionAppear = _this$props.motionAppear,
  72. motionEnter = _this$props.motionEnter,
  73. motionLeave = _this$props.motionLeave;
  74. if (!isSupportTransition(_this.props)) {
  75. return;
  76. }
  77. // Event injection
  78. var $ele = _this.getElement();
  79. if (_this.$cacheEle !== $ele) {
  80. _this.removeEventListener(_this.$cacheEle);
  81. _this.addEventListener($ele);
  82. _this.$cacheEle = $ele;
  83. }
  84. // Init status
  85. if (newStatus && status === STATUS_APPEAR && motionAppear) {
  86. _this.updateStatus(onAppearStart, null, null, function () {
  87. _this.updateActiveStatus(onAppearActive, STATUS_APPEAR);
  88. });
  89. } else if (newStatus && status === STATUS_ENTER && motionEnter) {
  90. _this.updateStatus(onEnterStart, null, null, function () {
  91. _this.updateActiveStatus(onEnterActive, STATUS_ENTER);
  92. });
  93. } else if (newStatus && status === STATUS_LEAVE && motionLeave) {
  94. _this.updateStatus(onLeaveStart, null, null, function () {
  95. _this.updateActiveStatus(onLeaveActive, STATUS_LEAVE);
  96. });
  97. }
  98. };
  99. _this.onMotionEnd = function (event) {
  100. var _this$state2 = _this.state,
  101. status = _this$state2.status,
  102. statusActive = _this$state2.statusActive;
  103. var _this$props2 = _this.props,
  104. onAppearEnd = _this$props2.onAppearEnd,
  105. onEnterEnd = _this$props2.onEnterEnd,
  106. onLeaveEnd = _this$props2.onLeaveEnd;
  107. if (status === STATUS_APPEAR && statusActive) {
  108. _this.updateStatus(onAppearEnd, { status: STATUS_NONE }, event);
  109. } else if (status === STATUS_ENTER && statusActive) {
  110. _this.updateStatus(onEnterEnd, { status: STATUS_NONE }, event);
  111. } else if (status === STATUS_LEAVE && statusActive) {
  112. _this.updateStatus(onLeaveEnd, { status: STATUS_NONE }, event);
  113. }
  114. };
  115. _this.setNodeRef = function (node) {
  116. var internalRef = _this.props.internalRef;
  117. _this.node = node;
  118. if (typeof internalRef === 'function') {
  119. internalRef(node);
  120. } else if (internalRef && 'current' in internalRef) {
  121. internalRef.current = node;
  122. }
  123. };
  124. _this.getElement = function () {
  125. try {
  126. return findDOMNode(_this.node || _this);
  127. } catch (e) {
  128. /**
  129. * Fallback to cache element.
  130. * This is only happen when `motionDeadline` trigger but element removed.
  131. */
  132. return _this.$cacheEle;
  133. }
  134. };
  135. _this.addEventListener = function ($ele) {
  136. if (!$ele) return;
  137. $ele.addEventListener(transitionEndName, _this.onMotionEnd);
  138. $ele.addEventListener(animationEndName, _this.onMotionEnd);
  139. };
  140. _this.removeEventListener = function ($ele) {
  141. if (!$ele) return;
  142. $ele.removeEventListener(transitionEndName, _this.onMotionEnd);
  143. $ele.removeEventListener(animationEndName, _this.onMotionEnd);
  144. };
  145. _this.updateStatus = function (styleFunc, additionalState, event, callback) {
  146. var statusStyle = styleFunc ? styleFunc(_this.getElement(), event) : null;
  147. if (statusStyle === false || _this._destroyed) return;
  148. var nextStep = void 0;
  149. if (callback) {
  150. nextStep = function nextStep() {
  151. _this.nextFrame(callback);
  152. };
  153. }
  154. _this.setState(_extends({
  155. statusStyle: typeof statusStyle === 'object' ? statusStyle : null,
  156. newStatus: false
  157. }, additionalState), nextStep); // Trigger before next frame & after `componentDidMount`
  158. };
  159. _this.updateActiveStatus = function (styleFunc, currentStatus) {
  160. // `setState` use `postMessage` to trigger at the end of frame.
  161. // Let's use requestAnimationFrame to update new state in next frame.
  162. _this.nextFrame(function () {
  163. var status = _this.state.status;
  164. if (status !== currentStatus) return;
  165. var motionDeadline = _this.props.motionDeadline;
  166. _this.updateStatus(styleFunc, { statusActive: true });
  167. if (motionDeadline > 0) {
  168. setTimeout(function () {
  169. _this.onMotionEnd({
  170. deadline: true
  171. });
  172. }, motionDeadline);
  173. }
  174. });
  175. };
  176. _this.nextFrame = function (func) {
  177. _this.cancelNextFrame();
  178. _this.raf = raf(func);
  179. };
  180. _this.cancelNextFrame = function () {
  181. if (_this.raf) {
  182. raf.cancel(_this.raf);
  183. _this.raf = null;
  184. }
  185. };
  186. _this.state = {
  187. status: STATUS_NONE,
  188. statusActive: false,
  189. newStatus: false,
  190. statusStyle: null
  191. };
  192. _this.$cacheEle = null;
  193. _this.node = null;
  194. _this.raf = null;
  195. return _this;
  196. }
  197. _createClass(CSSMotion, [{
  198. key: 'componentDidMount',
  199. value: function componentDidMount() {
  200. this.onDomUpdate();
  201. }
  202. }, {
  203. key: 'componentDidUpdate',
  204. value: function componentDidUpdate() {
  205. this.onDomUpdate();
  206. }
  207. }, {
  208. key: 'componentWillUnmount',
  209. value: function componentWillUnmount() {
  210. this._destroyed = true;
  211. this.removeEventListener(this.$cacheEle);
  212. this.cancelNextFrame();
  213. }
  214. }, {
  215. key: 'render',
  216. value: function render() {
  217. var _classNames;
  218. var _state = this.state,
  219. status = _state.status,
  220. statusActive = _state.statusActive,
  221. statusStyle = _state.statusStyle;
  222. var _props = this.props,
  223. children = _props.children,
  224. motionName = _props.motionName,
  225. visible = _props.visible,
  226. removeOnLeave = _props.removeOnLeave,
  227. leavedClassName = _props.leavedClassName,
  228. eventProps = _props.eventProps;
  229. if (!children) return null;
  230. if (status === STATUS_NONE || !isSupportTransition(this.props)) {
  231. if (visible) {
  232. return children(_extends({}, eventProps), this.setNodeRef);
  233. } else if (!removeOnLeave) {
  234. return children(_extends({}, eventProps, { className: leavedClassName }), this.setNodeRef);
  235. }
  236. return null;
  237. }
  238. return children(_extends({}, eventProps, {
  239. className: classNames((_classNames = {}, _defineProperty(_classNames, getTransitionName(motionName, status), status !== STATUS_NONE), _defineProperty(_classNames, getTransitionName(motionName, status + '-active'), status !== STATUS_NONE && statusActive), _defineProperty(_classNames, motionName, typeof motionName === 'string'), _classNames)),
  240. style: statusStyle
  241. }), this.setNodeRef);
  242. }
  243. }], [{
  244. key: 'getDerivedStateFromProps',
  245. value: function getDerivedStateFromProps(props, _ref) {
  246. var prevProps = _ref.prevProps,
  247. prevStatus = _ref.status;
  248. if (!isSupportTransition(props)) return {};
  249. var visible = props.visible,
  250. motionAppear = props.motionAppear,
  251. motionEnter = props.motionEnter,
  252. motionLeave = props.motionLeave,
  253. motionLeaveImmediately = props.motionLeaveImmediately;
  254. var newState = {
  255. prevProps: props
  256. };
  257. // Clean up status if prop set to false
  258. if (prevStatus === STATUS_APPEAR && !motionAppear || prevStatus === STATUS_ENTER && !motionEnter || prevStatus === STATUS_LEAVE && !motionLeave) {
  259. newState.status = STATUS_NONE;
  260. newState.statusActive = false;
  261. newState.newStatus = false;
  262. }
  263. // Appear
  264. if (!prevProps && visible && motionAppear) {
  265. newState.status = STATUS_APPEAR;
  266. newState.statusActive = false;
  267. newState.newStatus = true;
  268. }
  269. // Enter
  270. if (prevProps && !prevProps.visible && visible && motionEnter) {
  271. newState.status = STATUS_ENTER;
  272. newState.statusActive = false;
  273. newState.newStatus = true;
  274. }
  275. // Leave
  276. if (prevProps && prevProps.visible && !visible && motionLeave || !prevProps && motionLeaveImmediately && !visible && motionLeave) {
  277. newState.status = STATUS_LEAVE;
  278. newState.statusActive = false;
  279. newState.newStatus = true;
  280. }
  281. return newState;
  282. }
  283. }]);
  284. return CSSMotion;
  285. }(React.Component);
  286. CSSMotion.propTypes = _extends({}, MotionPropTypes, {
  287. internalRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func])
  288. });
  289. CSSMotion.defaultProps = {
  290. visible: true,
  291. motionEnter: true,
  292. motionAppear: true,
  293. motionLeave: true,
  294. removeOnLeave: true
  295. };
  296. polyfill(CSSMotion);
  297. if (!forwardRef) {
  298. return CSSMotion;
  299. }
  300. return React.forwardRef(function (props, ref) {
  301. return React.createElement(CSSMotion, _extends({ internalRef: ref }, props));
  302. });
  303. }
  304. export default genCSSMotion(supportTransition);