index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React, { useEffect, useState } from 'react';
  2. import useCursorPosition from '../../hooks/useCursorPosition';
  3. import { getAbsoluteCoordinate } from '../../helpers/position';
  4. import theme from '../../helpers/theme';
  5. import { SVG, Circle } from './styled';
  6. type Props = LinePositionType & {
  7. top: number;
  8. left: number;
  9. width: number;
  10. height: number;
  11. onMove: (position: LinePositionType) => void;
  12. onClick?: (event: React.MouseEvent) => void;
  13. onMouseUp: () => void;
  14. };
  15. const MARGIN_DISTANCE = 10;
  16. const initState = {
  17. start: { x: 0, y: 0 },
  18. end: { x: 0, y: 0 },
  19. operator: '',
  20. clickX: 0,
  21. clickY: 0,
  22. };
  23. const index: React.FC<Props> = ({
  24. top,
  25. left,
  26. width,
  27. height,
  28. start,
  29. end,
  30. onMove,
  31. onClick,
  32. onMouseUp,
  33. }: Props) => {
  34. const [state, setState] = useState(initState);
  35. const [cursorPosition, setRef] = useCursorPosition(25);
  36. const handleMouseDown = (e: React.MouseEvent): void => {
  37. const operatorId = (e.target as HTMLElement).getAttribute(
  38. 'data-id',
  39. ) as string;
  40. const coord = getAbsoluteCoordinate(document.body, e);
  41. setRef(document.body);
  42. setState({
  43. start,
  44. end,
  45. operator: operatorId,
  46. clickX: coord.x,
  47. clickY: coord.y,
  48. });
  49. };
  50. const handleMouseUp = (): void => {
  51. setRef(null);
  52. setState(initState);
  53. onMouseUp();
  54. };
  55. useEffect(() => {
  56. if (cursorPosition.x && cursorPosition.y) {
  57. if (state.operator === 'start') {
  58. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  59. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  60. onMove({
  61. start: { x: x1, y: y1 },
  62. end,
  63. });
  64. } else if (state.operator === 'end') {
  65. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  66. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  67. onMove({
  68. start,
  69. end: { x: x2, y: y2 },
  70. });
  71. } else if (state.operator === 'move') {
  72. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  73. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  74. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  75. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  76. onMove({
  77. start: { x: x1, y: y1 },
  78. end: { x: x2, y: y2 },
  79. });
  80. }
  81. }
  82. }, [cursorPosition, state]);
  83. useEffect(() => {
  84. window.addEventListener('mouseup', handleMouseUp);
  85. return (): void => {
  86. window.removeEventListener('mouseup', handleMouseUp);
  87. };
  88. }, []);
  89. return (
  90. <SVG
  91. data-id="move"
  92. viewBox={`${left} ${top} ${width + MARGIN_DISTANCE}
  93. ${height + MARGIN_DISTANCE}`}
  94. style={{
  95. left: `${left - MARGIN_DISTANCE}px`,
  96. top: `${top - MARGIN_DISTANCE}px`,
  97. width: `${width + MARGIN_DISTANCE}px`,
  98. height: `${height + MARGIN_DISTANCE}px`,
  99. }}
  100. onClick={onClick}
  101. onMouseDown={handleMouseDown}
  102. >
  103. <Circle
  104. data-id="start"
  105. onMouseDown={handleMouseDown}
  106. fill={theme.colors.primary}
  107. cx={start.x + MARGIN_DISTANCE}
  108. cy={start.y + MARGIN_DISTANCE}
  109. r={6}
  110. />
  111. <Circle
  112. data-id="end"
  113. onMouseDown={handleMouseDown}
  114. fill={theme.colors.primary}
  115. cx={end.x + MARGIN_DISTANCE}
  116. cy={end.y + MARGIN_DISTANCE}
  117. r={6}
  118. />
  119. </SVG>
  120. );
  121. };
  122. export default index;