Annotation.tsx 4.1 KB

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