Annotation.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import React, { useEffect, useState } from 'react';
  2. import AnnotationWrapper from '../components/AnnotationWrapper';
  3. import Highlight from '../components/Highlight';
  4. import Ink from '../components/Ink';
  5. import FreeText from '../components/FreeText';
  6. import StickyNote from '../components/StickyNote';
  7. import Shape from '../components/Shape';
  8. import Line from '../components/Line';
  9. import Image from '../components/Image';
  10. import TextField from '../components/TextField';
  11. import DeleteDialog from '../components/DeleteDialog';
  12. import useCursorPosition from '../hooks/useCursorPosition';
  13. import ClickAwayListener from '../components/ClickAwayListener';
  14. import { appendUserIdAndDate } from '../helpers/annotation';
  15. import useStore from '../store';
  16. import useActions from '../actions';
  17. type Props = AnnotationType & {
  18. index: number;
  19. scale: number;
  20. };
  21. const Annotation: React.FC<Props> = ({
  22. id,
  23. obj_type,
  24. obj_attr,
  25. index,
  26. scale,
  27. }: Props) => {
  28. const [isCollapse, setCollapse] = useState(true);
  29. const [isEdit, setEdit] = useState(false);
  30. const [isCovered, setMouseOver] = useState(false);
  31. const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
  32. const [openDialog, setDialog] = useState(false);
  33. const [cursorPosition, ref] = useCursorPosition();
  34. const [{ viewport, annotations }, dispatch] = useStore();
  35. const { updateAnnots } = useActions(dispatch);
  36. const handleClick = (): void => {
  37. if (isCollapse && !isEdit) {
  38. setCollapse(false);
  39. setMousePosition({
  40. x: cursorPosition.x || 0,
  41. y: cursorPosition.y || 0,
  42. });
  43. }
  44. };
  45. const handleMouseOver = (): void => {
  46. setMouseOver(true);
  47. };
  48. const handleMouseOut = (): void => {
  49. setMouseOver(false);
  50. };
  51. const handleUpdate: OnUpdateType = data => {
  52. const newAttributes = {
  53. ...annotations[index].obj_attr,
  54. ...data,
  55. isInit: false,
  56. };
  57. annotations[index].obj_attr = newAttributes;
  58. annotations[index] = appendUserIdAndDate(annotations[index]);
  59. updateAnnots(annotations);
  60. };
  61. const handleEdit = (): void => {
  62. setEdit(true);
  63. setCollapse(true);
  64. };
  65. const deleteDialogToggle = (): void => {
  66. setDialog(!openDialog);
  67. };
  68. const handleDelete = (): void => {
  69. annotations.splice(index, 1);
  70. setDialog(false);
  71. updateAnnots(annotations);
  72. };
  73. const handleBlur = (): void => {
  74. setCollapse(true);
  75. };
  76. const handleInputBlur = (): void => {
  77. setEdit(false);
  78. if (obj_type === 'FreeText' && !obj_attr.content) {
  79. handleDelete();
  80. }
  81. };
  82. const handleKeyDown = (e: React.KeyboardEvent): void => {
  83. if (e.key === 'Delete') {
  84. deleteDialogToggle();
  85. }
  86. };
  87. useEffect(() => {
  88. if (obj_type === 'FreeText' && !obj_attr.content) {
  89. setEdit(true);
  90. }
  91. }, [obj_type, obj_attr]);
  92. useEffect(() => {
  93. return () => {
  94. handleMouseOut();
  95. handleBlur();
  96. setEdit(false);
  97. };
  98. }, []);
  99. const childProps = {
  100. id,
  101. obj_type,
  102. obj_attr,
  103. scale,
  104. isCollapse,
  105. isCovered,
  106. mousePosition,
  107. onUpdate: handleUpdate,
  108. onDelete: deleteDialogToggle,
  109. viewport,
  110. onEdit: handleEdit,
  111. isEdit,
  112. onBlur: handleInputBlur,
  113. onMouseOver: handleMouseOver,
  114. onMouseOut: handleMouseOut,
  115. };
  116. const wrapperProps = {
  117. onMouseDown: handleClick,
  118. onKeyDown: handleKeyDown,
  119. };
  120. return (
  121. <ClickAwayListener onClick={handleBlur}>
  122. <AnnotationWrapper ref={ref} {...wrapperProps}>
  123. {((): React.ReactNode => {
  124. switch (obj_type) {
  125. case 'Ink':
  126. return <Ink {...childProps} />;
  127. case 'FreeText':
  128. return <FreeText {...childProps} />;
  129. case 'Text':
  130. return <StickyNote {...childProps} />;
  131. case 'Square':
  132. case 'Circle':
  133. return <Shape {...childProps} />;
  134. case 'Line':
  135. case 'Arrow':
  136. return <Line {...childProps} />;
  137. case 'Image':
  138. return <Image {...childProps} />;
  139. case 'textfield':
  140. case 'checkbox':
  141. return <TextField {...childProps} />;
  142. default:
  143. return <Highlight {...childProps} />;
  144. }
  145. })()}
  146. {openDialog && (
  147. <DeleteDialog onCancel={deleteDialogToggle} onDelete={handleDelete} />
  148. )}
  149. </AnnotationWrapper>
  150. </ClickAwayListener>
  151. );
  152. };
  153. export default Annotation;