HighlightTools.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import React, { useEffect, useState, useCallback } from 'react';
  2. import Icon from '../components/Icon';
  3. import Button from '../components/Button';
  4. import ExpansionPanel from '../components/ExpansionPanel';
  5. import HighlightOption from '../components/HighlightOption';
  6. import useActions from '../actions';
  7. import useStore from '../store';
  8. import { getMarkupWithSelection } from '../helpers/markup';
  9. type Props = {
  10. title: string;
  11. isActive: boolean;
  12. onClick: () => void;
  13. };
  14. let timer = 0;
  15. let textLayer: HTMLElement | null = null;
  16. const HighlightTools: React.FC<Props> = ({
  17. title,
  18. isActive,
  19. onClick,
  20. }: Props) => {
  21. const [currentId, setCurrentId] = useState('');
  22. const [data, setData] = useState({
  23. type: 'highlight',
  24. color: '#FBB705',
  25. opacity: 35,
  26. });
  27. const [{ scale, annotations }, dispatch] = useStore();
  28. const { addAnnots, updateAnnots } = useActions(dispatch);
  29. const setDataState = (obj: OptionPropsType): void => {
  30. setData((prev) => ({
  31. ...prev,
  32. ...obj,
  33. }));
  34. };
  35. const handleDown = () => {
  36. timer = setTimeout(() => {
  37. setCurrentId('');
  38. }, 1500);
  39. };
  40. const handleMove = () => {
  41. if (timer) {
  42. clearTimeout(timer);
  43. }
  44. };
  45. const handleUp = useCallback(
  46. (e: MouseEvent | TouchEvent): void => {
  47. const selection = document.getSelection();
  48. if (!selection?.isCollapsed && !currentId) {
  49. if (e.target) {
  50. textLayer = (e.target as HTMLElement).parentNode as HTMLElement;
  51. if (textLayer.getAttribute('data-id') !== 'text-layer') {
  52. textLayer = textLayer.querySelector(
  53. '[data-id="text-layer"]',
  54. ) as HTMLElement;
  55. }
  56. if (textLayer) {
  57. const newMarkup = getMarkupWithSelection({
  58. ...data,
  59. scale,
  60. textLayer,
  61. });
  62. if (newMarkup) {
  63. setCurrentId(newMarkup.id as string);
  64. addAnnots([newMarkup]);
  65. }
  66. }
  67. }
  68. }
  69. if (selection?.isCollapsed) {
  70. setCurrentId('');
  71. }
  72. },
  73. [data, scale, currentId],
  74. );
  75. const handleSelectStart = () => {
  76. setCurrentId('');
  77. };
  78. const handleSelectChange = useCallback(() => {
  79. const selection = document.getSelection();
  80. if (!selection?.isCollapsed && currentId) {
  81. if (textLayer) {
  82. const newMarkup = getMarkupWithSelection({
  83. ...data,
  84. scale,
  85. textLayer,
  86. });
  87. if (newMarkup) {
  88. const array: AnnotationType[] = [];
  89. annotations.forEach((ele) => {
  90. if (ele.id === currentId) {
  91. // eslint-disable-next-line no-param-reassign
  92. ele.obj_attr.position = newMarkup.obj_attr.position;
  93. }
  94. array.push(ele);
  95. });
  96. updateAnnots(array);
  97. }
  98. }
  99. }
  100. }, [annotations, data, scale, currentId]);
  101. const subscribeEvent = () => {
  102. document.addEventListener('mousedown', handleDown);
  103. document.addEventListener('mousemove', handleMove);
  104. document.addEventListener('mouseup', handleUp);
  105. document.addEventListener('selectstart', handleSelectStart);
  106. document.addEventListener('touchstart', handleDown);
  107. document.addEventListener('touchmove', handleMove);
  108. document.addEventListener('touchend', handleUp);
  109. document.addEventListener('selectionchange', handleSelectChange);
  110. };
  111. const unsubscribeEvent = () => {
  112. document.removeEventListener('mousedown', handleDown);
  113. document.removeEventListener('mousemove', handleMove);
  114. document.removeEventListener('mouseup', handleUp);
  115. document.removeEventListener('selectstart', handleSelectStart);
  116. document.removeEventListener('touchstart', handleDown);
  117. document.removeEventListener('touchmove', handleMove);
  118. document.removeEventListener('touchend', handleUp);
  119. document.removeEventListener('selectionchange', handleSelectChange);
  120. };
  121. useEffect(() => {
  122. if (isActive) {
  123. subscribeEvent();
  124. } else {
  125. unsubscribeEvent();
  126. }
  127. return (): void => {
  128. unsubscribeEvent();
  129. };
  130. }, [isActive, handleUp, handleSelectChange]);
  131. const Label = (
  132. <Button
  133. shouldFitContainer
  134. align="left"
  135. onClick={onClick}
  136. isActive={isActive}
  137. >
  138. <Icon glyph="highlight" style={{ marginRight: '10px' }} />
  139. {title}
  140. </Button>
  141. );
  142. return (
  143. <ExpansionPanel label={Label} isActive={isActive} showBottomBorder>
  144. <HighlightOption {...data} setDataState={setDataState} />
  145. </ExpansionPanel>
  146. );
  147. };
  148. export default HighlightTools;