HighlightTools.tsx 4.8 KB

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