index.tsx 3.4 KB

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