index.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import React, { useEffect, useState } from 'react';
  2. import theme from '../../helpers/theme';
  3. import useCursorPosition from '../../hooks/useCursorPosition';
  4. import { getAbsoluteCoordinate } from '../../helpers/position';
  5. import { calcDragAndDropScale } from '../../helpers/utility';
  6. import generateCirclesData from './data';
  7. import { SVG, Rect, Circle } from './styled';
  8. type Props = {
  9. left: number;
  10. top: number;
  11. width: number;
  12. height: number;
  13. onMove?: (moveCoord: CoordType) => void;
  14. onScale?: (scaleCoord: CoordType) => void;
  15. onClick?: (event: React.MouseEvent) => void;
  16. onDoubleClick?: () => void;
  17. };
  18. type ObjPositionType = {
  19. top: number;
  20. left: number;
  21. width: number;
  22. height: number;
  23. operator: string;
  24. clickX: number;
  25. clickY: number;
  26. };
  27. const initState = {
  28. top: 0,
  29. left: 0,
  30. width: 0,
  31. height: 0,
  32. operator: '',
  33. clickX: 0,
  34. clickY: 0,
  35. };
  36. const index: React.FC<Props> = ({
  37. left,
  38. top,
  39. width,
  40. height,
  41. onMove,
  42. onScale,
  43. onClick,
  44. onDoubleClick,
  45. }: Props) => {
  46. const data = generateCirclesData(width, height);
  47. const [state, setState] = useState(initState);
  48. const [cursorPosition, setRef] = useCursorPosition(25);
  49. const handleMouseDown = (e: React.MouseEvent | React.TouchEvent): void => {
  50. e.preventDefault();
  51. const operatorId = (e.target as HTMLElement).getAttribute(
  52. 'data-id',
  53. ) as string;
  54. const coord = getAbsoluteCoordinate(document.body, e);
  55. setRef(document.body);
  56. setState({
  57. top,
  58. left,
  59. width,
  60. height,
  61. operator: operatorId,
  62. clickX: coord.x,
  63. clickY: coord.y,
  64. });
  65. };
  66. const handleMouseUp = (): void => {
  67. setRef(null);
  68. setState(initState);
  69. };
  70. const calcMoveResult = (
  71. currentPosition: PointType,
  72. startPosition: PointType,
  73. objPosition: ObjPositionType,
  74. ): CoordType => ({
  75. left: currentPosition.x - (startPosition.x - objPosition.left),
  76. top: currentPosition.y - (startPosition.y - objPosition.top),
  77. });
  78. const calcScaleResult = (
  79. currentPosition: PointType,
  80. objPosition: ObjPositionType,
  81. ): CoordType => {
  82. const scaleData = calcDragAndDropScale({
  83. ...objPosition,
  84. moveX: currentPosition.x || 0,
  85. moveY: currentPosition.y || 0,
  86. });
  87. const scaleWidth = scaleData.width || 0;
  88. const scaleHeight = scaleData.height || 0;
  89. const maxTop = objPosition.top + objPosition.height;
  90. const maxLeft = objPosition.left + objPosition.width;
  91. scaleData.left = scaleData.left > maxLeft ? maxLeft : scaleData.left;
  92. scaleData.top = scaleData.top > maxTop ? maxTop : scaleData.top;
  93. scaleData.width = scaleWidth > 0 ? scaleWidth : 0;
  94. scaleData.height = scaleHeight > 0 ? scaleHeight : 0;
  95. return scaleData;
  96. };
  97. useEffect(() => {
  98. if (cursorPosition.x && cursorPosition.y && state.clickX) {
  99. if (state.operator === 'move' && onMove) {
  100. onMove(
  101. calcMoveResult(
  102. { x: cursorPosition.x, y: cursorPosition.y },
  103. { x: state.clickX, y: state.clickY },
  104. state,
  105. ),
  106. );
  107. } else if (onScale) {
  108. onScale(
  109. calcScaleResult(
  110. {
  111. x: cursorPosition.x,
  112. y: cursorPosition.y,
  113. },
  114. state,
  115. ),
  116. );
  117. }
  118. }
  119. }, [cursorPosition, state]);
  120. useEffect(() => {
  121. window.addEventListener('mouseup', handleMouseUp);
  122. window.addEventListener('touchend', handleMouseUp);
  123. return (): void => {
  124. window.removeEventListener('mouseup', handleMouseUp);
  125. window.removeEventListener('touchend', handleMouseUp);
  126. };
  127. }, []);
  128. return (
  129. <SVG
  130. style={{
  131. left: `${left - 12}px`,
  132. top: `${top - 12}px`,
  133. width: `${width + 24}px`,
  134. height: `${height + 24}px`,
  135. }}
  136. onClick={onClick}
  137. onDoubleClick={onDoubleClick}
  138. >
  139. <Rect
  140. x={6}
  141. y={6}
  142. width={width + 12}
  143. height={height + 12}
  144. stroke={onScale ? theme.colors.primary : 'transparency'}
  145. onMouseDown={handleMouseDown}
  146. onTouchStart={handleMouseDown}
  147. data-id="move"
  148. />
  149. {onScale &&
  150. data.map((attr: CircleType) => (
  151. <Circle
  152. key={attr.direction}
  153. data-id={attr.direction}
  154. onMouseDown={handleMouseDown}
  155. onTouchStart={handleMouseDown}
  156. fill={theme.colors.primary}
  157. {...attr}
  158. />
  159. ))}
  160. </SVG>
  161. );
  162. };
  163. export default index;