Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

311 linhas
10 KiB

  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
  6. var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
  7. var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
  8. var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
  9. var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
  10. var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
  11. var _createClass2 = require('babel-runtime/helpers/createClass');
  12. var _createClass3 = _interopRequireDefault(_createClass2);
  13. var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
  14. var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
  15. var _inherits2 = require('babel-runtime/helpers/inherits');
  16. var _inherits3 = _interopRequireDefault(_inherits2);
  17. var _toArray2 = require('babel-runtime/helpers/toArray');
  18. var _toArray3 = _interopRequireDefault(_toArray2);
  19. var _simpleAssign = require('simple-assign');
  20. var _simpleAssign2 = _interopRequireDefault(_simpleAssign);
  21. var _react = require('react');
  22. var _react2 = _interopRequireDefault(_react);
  23. var _propTypes = require('prop-types');
  24. var _propTypes2 = _interopRequireDefault(_propTypes);
  25. var _reactDom = require('react-dom');
  26. var _reactDom2 = _interopRequireDefault(_reactDom);
  27. var _TransitionGroup = require('react-transition-group/TransitionGroup');
  28. var _TransitionGroup2 = _interopRequireDefault(_TransitionGroup);
  29. var _dom = require('../utils/dom');
  30. var _dom2 = _interopRequireDefault(_dom);
  31. var _CircleRipple = require('./CircleRipple');
  32. var _CircleRipple2 = _interopRequireDefault(_CircleRipple);
  33. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  34. // Remove the first element of the array
  35. var shift = function shift(_ref) {
  36. var _ref2 = (0, _toArray3.default)(_ref),
  37. newArray = _ref2.slice(1);
  38. return newArray;
  39. };
  40. var TouchRipple = function (_Component) {
  41. (0, _inherits3.default)(TouchRipple, _Component);
  42. function TouchRipple(props, context) {
  43. (0, _classCallCheck3.default)(this, TouchRipple);
  44. // Touch start produces a mouse down event for compat reasons. To avoid
  45. // showing ripples twice we skip showing a ripple for the first mouse down
  46. // after a touch start. Note we don't store ignoreNextMouseDown in this.state
  47. // to avoid re-rendering when we change it.
  48. var _this = (0, _possibleConstructorReturn3.default)(this, (TouchRipple.__proto__ || (0, _getPrototypeOf2.default)(TouchRipple)).call(this, props, context));
  49. _this.handleMouseDown = function (event) {
  50. // only listen to left clicks
  51. if (event.button === 0) {
  52. _this.start(event, false);
  53. }
  54. };
  55. _this.handleMouseUp = function () {
  56. _this.end();
  57. };
  58. _this.handleMouseLeave = function () {
  59. _this.end();
  60. };
  61. _this.handleTouchStart = function (event) {
  62. event.stopPropagation();
  63. // If the user is swiping (not just tapping), save the position so we can
  64. // abort ripples if the user appears to be scrolling.
  65. if (_this.props.abortOnScroll && event.touches) {
  66. _this.startListeningForScrollAbort(event);
  67. _this.startTime = Date.now();
  68. }
  69. _this.start(event, true);
  70. };
  71. _this.handleTouchEnd = function () {
  72. _this.end();
  73. };
  74. _this.handleTouchMove = function (event) {
  75. // Stop trying to abort if we're already 300ms into the animation
  76. var timeSinceStart = Math.abs(Date.now() - _this.startTime);
  77. if (timeSinceStart > 300) {
  78. _this.stopListeningForScrollAbort();
  79. return;
  80. }
  81. // If the user is scrolling...
  82. var deltaY = Math.abs(event.touches[0].clientY - _this.firstTouchY);
  83. var deltaX = Math.abs(event.touches[0].clientX - _this.firstTouchX);
  84. // Call it a scroll after an arbitrary 6px (feels reasonable in testing)
  85. if (deltaY > 6 || deltaX > 6) {
  86. var currentRipples = _this.state.ripples;
  87. var ripple = currentRipples[0];
  88. // This clone will replace the ripple in ReactTransitionGroup with a
  89. // version that will disappear immediately when removed from the DOM
  90. var abortedRipple = _react2.default.cloneElement(ripple, { aborted: true });
  91. // Remove the old ripple and replace it with the new updated one
  92. currentRipples = shift(currentRipples);
  93. currentRipples = [].concat((0, _toConsumableArray3.default)(currentRipples), [abortedRipple]);
  94. _this.setState({ ripples: currentRipples }, function () {
  95. // Call end after we've set the ripple to abort otherwise the setState
  96. // in end() merges with this and the ripple abort fails
  97. _this.end();
  98. });
  99. }
  100. };
  101. _this.ignoreNextMouseDown = false;
  102. _this.state = {
  103. // This prop allows us to only render the ReactTransitionGroup
  104. // on the first click of the component, making the inital render faster.
  105. hasRipples: false,
  106. nextKey: 0,
  107. ripples: []
  108. };
  109. return _this;
  110. }
  111. (0, _createClass3.default)(TouchRipple, [{
  112. key: 'start',
  113. value: function start(event, isRippleTouchGenerated) {
  114. var theme = this.context.muiTheme.ripple;
  115. if (this.ignoreNextMouseDown && !isRippleTouchGenerated) {
  116. this.ignoreNextMouseDown = false;
  117. return;
  118. }
  119. var ripples = this.state.ripples;
  120. // Add a ripple to the ripples array
  121. ripples = [].concat((0, _toConsumableArray3.default)(ripples), [_react2.default.createElement(_CircleRipple2.default, {
  122. key: this.state.nextKey,
  123. style: !this.props.centerRipple ? this.getRippleStyle(event) : {},
  124. color: this.props.color || theme.color,
  125. opacity: this.props.opacity,
  126. touchGenerated: isRippleTouchGenerated
  127. })]);
  128. this.ignoreNextMouseDown = isRippleTouchGenerated;
  129. this.setState({
  130. hasRipples: true,
  131. nextKey: this.state.nextKey + 1,
  132. ripples: ripples
  133. });
  134. }
  135. }, {
  136. key: 'end',
  137. value: function end() {
  138. var currentRipples = this.state.ripples;
  139. this.setState({
  140. ripples: shift(currentRipples)
  141. });
  142. if (this.props.abortOnScroll) {
  143. this.stopListeningForScrollAbort();
  144. }
  145. }
  146. // Check if the user seems to be scrolling and abort the animation if so
  147. }, {
  148. key: 'startListeningForScrollAbort',
  149. value: function startListeningForScrollAbort(event) {
  150. this.firstTouchY = event.touches[0].clientY;
  151. this.firstTouchX = event.touches[0].clientX;
  152. // Note that when scolling Chrome throttles this event to every 200ms
  153. // Also note we don't listen for scroll events directly as there's no general
  154. // way to cover cases like scrolling within containers on the page
  155. document.body.addEventListener('touchmove', this.handleTouchMove);
  156. }
  157. }, {
  158. key: 'stopListeningForScrollAbort',
  159. value: function stopListeningForScrollAbort() {
  160. document.body.removeEventListener('touchmove', this.handleTouchMove);
  161. }
  162. }, {
  163. key: 'getRippleStyle',
  164. value: function getRippleStyle(event) {
  165. var el = _reactDom2.default.findDOMNode(this);
  166. var elHeight = el.offsetHeight;
  167. var elWidth = el.offsetWidth;
  168. var offset = _dom2.default.offset(el);
  169. var isTouchEvent = event.touches && event.touches.length;
  170. var pageX = isTouchEvent ? event.touches[0].pageX : event.pageX;
  171. var pageY = isTouchEvent ? event.touches[0].pageY : event.pageY;
  172. var pointerX = pageX - offset.left;
  173. var pointerY = pageY - offset.top;
  174. var topLeftDiag = this.calcDiag(pointerX, pointerY);
  175. var topRightDiag = this.calcDiag(elWidth - pointerX, pointerY);
  176. var botRightDiag = this.calcDiag(elWidth - pointerX, elHeight - pointerY);
  177. var botLeftDiag = this.calcDiag(pointerX, elHeight - pointerY);
  178. var rippleRadius = Math.max(topLeftDiag, topRightDiag, botRightDiag, botLeftDiag);
  179. var rippleSize = rippleRadius * 2;
  180. var left = pointerX - rippleRadius;
  181. var top = pointerY - rippleRadius;
  182. return {
  183. directionInvariant: true,
  184. height: rippleSize,
  185. width: rippleSize,
  186. top: top,
  187. left: left
  188. };
  189. }
  190. }, {
  191. key: 'calcDiag',
  192. value: function calcDiag(a, b) {
  193. return Math.sqrt(a * a + b * b);
  194. }
  195. }, {
  196. key: 'render',
  197. value: function render() {
  198. var _props = this.props,
  199. children = _props.children,
  200. style = _props.style;
  201. var _state = this.state,
  202. hasRipples = _state.hasRipples,
  203. ripples = _state.ripples;
  204. var prepareStyles = this.context.muiTheme.prepareStyles;
  205. var rippleGroup = void 0;
  206. if (hasRipples) {
  207. var mergedStyles = (0, _simpleAssign2.default)({
  208. height: '100%',
  209. width: '100%',
  210. position: 'absolute',
  211. top: 0,
  212. left: 0,
  213. overflow: 'hidden',
  214. pointerEvents: 'none',
  215. zIndex: 1 // This is also needed so that ripples do not bleed past a parent border radius.
  216. }, style);
  217. rippleGroup = _react2.default.createElement(
  218. _TransitionGroup2.default,
  219. { style: prepareStyles(mergedStyles) },
  220. ripples
  221. );
  222. }
  223. return _react2.default.createElement(
  224. 'div',
  225. {
  226. onMouseUp: this.handleMouseUp,
  227. onMouseDown: this.handleMouseDown,
  228. onMouseLeave: this.handleMouseLeave,
  229. onTouchStart: this.handleTouchStart,
  230. onTouchEnd: this.handleTouchEnd
  231. },
  232. rippleGroup,
  233. children
  234. );
  235. }
  236. }]);
  237. return TouchRipple;
  238. }(_react.Component);
  239. TouchRipple.defaultProps = {
  240. abortOnScroll: true
  241. };
  242. TouchRipple.contextTypes = {
  243. muiTheme: _propTypes2.default.object.isRequired
  244. };
  245. TouchRipple.propTypes = process.env.NODE_ENV !== "production" ? {
  246. abortOnScroll: _propTypes2.default.bool,
  247. centerRipple: _propTypes2.default.bool,
  248. children: _propTypes2.default.node,
  249. color: _propTypes2.default.string,
  250. opacity: _propTypes2.default.number,
  251. style: _propTypes2.default.object
  252. } : {};
  253. exports.default = TouchRipple;