useDrag.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  2. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  3. import * as React from 'react';
  4. import useEvent from "rc-util/es/hooks/useEvent";
  5. import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
  6. import { UnstableContext } from "../context";
  7. /** Drag to delete offset. It's a user experience number for dragging out */
  8. var REMOVE_DIST = 130;
  9. function getPosition(e) {
  10. var obj = 'targetTouches' in e ? e.targetTouches[0] : e;
  11. return {
  12. pageX: obj.pageX,
  13. pageY: obj.pageY
  14. };
  15. }
  16. function useDrag(containerRef, direction, rawValues, min, max, formatValue, triggerChange, finishChange, offsetValues, editable, minCount) {
  17. var _React$useState = React.useState(null),
  18. _React$useState2 = _slicedToArray(_React$useState, 2),
  19. draggingValue = _React$useState2[0],
  20. setDraggingValue = _React$useState2[1];
  21. var _React$useState3 = React.useState(-1),
  22. _React$useState4 = _slicedToArray(_React$useState3, 2),
  23. draggingIndex = _React$useState4[0],
  24. setDraggingIndex = _React$useState4[1];
  25. var _React$useState5 = React.useState(false),
  26. _React$useState6 = _slicedToArray(_React$useState5, 2),
  27. draggingDelete = _React$useState6[0],
  28. setDraggingDelete = _React$useState6[1];
  29. var _React$useState7 = React.useState(rawValues),
  30. _React$useState8 = _slicedToArray(_React$useState7, 2),
  31. cacheValues = _React$useState8[0],
  32. setCacheValues = _React$useState8[1];
  33. var _React$useState9 = React.useState(rawValues),
  34. _React$useState10 = _slicedToArray(_React$useState9, 2),
  35. originValues = _React$useState10[0],
  36. setOriginValues = _React$useState10[1];
  37. var mouseMoveEventRef = React.useRef(null);
  38. var mouseUpEventRef = React.useRef(null);
  39. var touchEventTargetRef = React.useRef(null);
  40. var _React$useContext = React.useContext(UnstableContext),
  41. onDragStart = _React$useContext.onDragStart,
  42. onDragChange = _React$useContext.onDragChange;
  43. useLayoutEffect(function () {
  44. if (draggingIndex === -1) {
  45. setCacheValues(rawValues);
  46. }
  47. }, [rawValues, draggingIndex]);
  48. // Clean up event
  49. React.useEffect(function () {
  50. return function () {
  51. document.removeEventListener('mousemove', mouseMoveEventRef.current);
  52. document.removeEventListener('mouseup', mouseUpEventRef.current);
  53. if (touchEventTargetRef.current) {
  54. touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current);
  55. touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current);
  56. }
  57. };
  58. }, []);
  59. var flushValues = function flushValues(nextValues, nextValue, deleteMark) {
  60. // Perf: Only update state when value changed
  61. if (nextValue !== undefined) {
  62. setDraggingValue(nextValue);
  63. }
  64. setCacheValues(nextValues);
  65. var changeValues = nextValues;
  66. if (deleteMark) {
  67. changeValues = nextValues.filter(function (_, i) {
  68. return i !== draggingIndex;
  69. });
  70. }
  71. triggerChange(changeValues);
  72. if (onDragChange) {
  73. onDragChange({
  74. rawValues: nextValues,
  75. deleteIndex: deleteMark ? draggingIndex : -1,
  76. draggingIndex: draggingIndex,
  77. draggingValue: nextValue
  78. });
  79. }
  80. };
  81. var updateCacheValue = useEvent(function (valueIndex, offsetPercent, deleteMark) {
  82. if (valueIndex === -1) {
  83. // >>>> Dragging on the track
  84. var startValue = originValues[0];
  85. var endValue = originValues[originValues.length - 1];
  86. var maxStartOffset = min - startValue;
  87. var maxEndOffset = max - endValue;
  88. // Get valid offset
  89. var offset = offsetPercent * (max - min);
  90. offset = Math.max(offset, maxStartOffset);
  91. offset = Math.min(offset, maxEndOffset);
  92. // Use first value to revert back of valid offset (like steps marks)
  93. var formatStartValue = formatValue(startValue + offset);
  94. offset = formatStartValue - startValue;
  95. var cloneCacheValues = originValues.map(function (val) {
  96. return val + offset;
  97. });
  98. flushValues(cloneCacheValues);
  99. } else {
  100. // >>>> Dragging on the handle
  101. var offsetDist = (max - min) * offsetPercent;
  102. // Always start with the valueIndex origin value
  103. var cloneValues = _toConsumableArray(cacheValues);
  104. cloneValues[valueIndex] = originValues[valueIndex];
  105. var next = offsetValues(cloneValues, offsetDist, valueIndex, 'dist');
  106. flushValues(next.values, next.value, deleteMark);
  107. }
  108. });
  109. var onStartMove = function onStartMove(e, valueIndex, startValues) {
  110. e.stopPropagation();
  111. // 如果是点击 track 触发的,需要传入变化后的初始值,而不能直接用 rawValues
  112. var initialValues = startValues || rawValues;
  113. var originValue = initialValues[valueIndex];
  114. setDraggingIndex(valueIndex);
  115. setDraggingValue(originValue);
  116. setOriginValues(initialValues);
  117. setCacheValues(initialValues);
  118. setDraggingDelete(false);
  119. var _getPosition = getPosition(e),
  120. startX = _getPosition.pageX,
  121. startY = _getPosition.pageY;
  122. // We declare it here since closure can't get outer latest value
  123. var deleteMark = false;
  124. // Internal trigger event
  125. if (onDragStart) {
  126. onDragStart({
  127. rawValues: initialValues,
  128. draggingIndex: valueIndex,
  129. draggingValue: originValue
  130. });
  131. }
  132. // Moving
  133. var onMouseMove = function onMouseMove(event) {
  134. event.preventDefault();
  135. var _getPosition2 = getPosition(event),
  136. moveX = _getPosition2.pageX,
  137. moveY = _getPosition2.pageY;
  138. var offsetX = moveX - startX;
  139. var offsetY = moveY - startY;
  140. var _containerRef$current = containerRef.current.getBoundingClientRect(),
  141. width = _containerRef$current.width,
  142. height = _containerRef$current.height;
  143. var offSetPercent;
  144. var removeDist;
  145. switch (direction) {
  146. case 'btt':
  147. offSetPercent = -offsetY / height;
  148. removeDist = offsetX;
  149. break;
  150. case 'ttb':
  151. offSetPercent = offsetY / height;
  152. removeDist = offsetX;
  153. break;
  154. case 'rtl':
  155. offSetPercent = -offsetX / width;
  156. removeDist = offsetY;
  157. break;
  158. default:
  159. offSetPercent = offsetX / width;
  160. removeDist = offsetY;
  161. }
  162. // Check if need mark remove
  163. deleteMark = editable ? Math.abs(removeDist) > REMOVE_DIST && minCount < cacheValues.length : false;
  164. setDraggingDelete(deleteMark);
  165. updateCacheValue(valueIndex, offSetPercent, deleteMark);
  166. };
  167. // End
  168. var onMouseUp = function onMouseUp(event) {
  169. event.preventDefault();
  170. document.removeEventListener('mouseup', onMouseUp);
  171. document.removeEventListener('mousemove', onMouseMove);
  172. if (touchEventTargetRef.current) {
  173. touchEventTargetRef.current.removeEventListener('touchmove', mouseMoveEventRef.current);
  174. touchEventTargetRef.current.removeEventListener('touchend', mouseUpEventRef.current);
  175. }
  176. mouseMoveEventRef.current = null;
  177. mouseUpEventRef.current = null;
  178. touchEventTargetRef.current = null;
  179. finishChange(deleteMark);
  180. setDraggingIndex(-1);
  181. setDraggingDelete(false);
  182. };
  183. document.addEventListener('mouseup', onMouseUp);
  184. document.addEventListener('mousemove', onMouseMove);
  185. e.currentTarget.addEventListener('touchend', onMouseUp);
  186. e.currentTarget.addEventListener('touchmove', onMouseMove);
  187. mouseMoveEventRef.current = onMouseMove;
  188. mouseUpEventRef.current = onMouseUp;
  189. touchEventTargetRef.current = e.currentTarget;
  190. };
  191. // Only return cache value when it mapping with rawValues
  192. var returnValues = React.useMemo(function () {
  193. var sourceValues = _toConsumableArray(rawValues).sort(function (a, b) {
  194. return a - b;
  195. });
  196. var targetValues = _toConsumableArray(cacheValues).sort(function (a, b) {
  197. return a - b;
  198. });
  199. var counts = {};
  200. targetValues.forEach(function (val) {
  201. counts[val] = (counts[val] || 0) + 1;
  202. });
  203. sourceValues.forEach(function (val) {
  204. counts[val] = (counts[val] || 0) - 1;
  205. });
  206. var maxDiffCount = editable ? 1 : 0;
  207. var diffCount = Object.values(counts).reduce(function (prev, next) {
  208. return prev + Math.abs(next);
  209. }, 0);
  210. return diffCount <= maxDiffCount ? cacheValues : rawValues;
  211. }, [rawValues, cacheValues, editable]);
  212. return [draggingIndex, draggingValue, draggingDelete, returnValues, onStartMove];
  213. }
  214. export default useDrag;