HighlightTools.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. textLayer.style.zIndex = '10';
  58. const newMarkup = getMarkupWithSelection({
  59. ...data,
  60. scale,
  61. textLayer,
  62. });
  63. if (newMarkup) {
  64. setCurrentId(newMarkup.id as string);
  65. addAnnots([newMarkup]);
  66. }
  67. }
  68. }
  69. }
  70. if (selection?.isCollapsed) {
  71. if (textLayer) {
  72. textLayer.style.zIndex = '0';
  73. }
  74. setCurrentId('');
  75. }
  76. },
  77. [data, scale, currentId],
  78. );
  79. const handleSelectStart = () => {
  80. setCurrentId('');
  81. };
  82. const handleSelectChange = useCallback(() => {
  83. const selection = document.getSelection();
  84. if (!selection?.isCollapsed && currentId) {
  85. if (textLayer) {
  86. const newMarkup = getMarkupWithSelection({
  87. ...data,
  88. scale,
  89. textLayer,
  90. });
  91. if (newMarkup) {
  92. const array: AnnotationType[] = [];
  93. annotations.forEach((ele) => {
  94. if (ele.id === currentId) {
  95. // eslint-disable-next-line no-param-reassign
  96. ele.obj_attr.position = newMarkup.obj_attr.position;
  97. }
  98. array.push(ele);
  99. });
  100. updateAnnots(array);
  101. }
  102. }
  103. }
  104. }, [annotations, data, scale, currentId]);
  105. useEffect(() => {
  106. if (isActive) {
  107. document.addEventListener('mousedown', handleDown);
  108. document.addEventListener('mousemove', handleMove);
  109. document.addEventListener('mouseup', handleUp);
  110. document.addEventListener('selectstart', handleSelectStart);
  111. document.addEventListener('touchstart', handleDown);
  112. document.addEventListener('touchmove', handleMove);
  113. document.addEventListener('touchend', handleUp);
  114. document.addEventListener('selectionchange', handleSelectChange);
  115. } else if (textLayer) {
  116. textLayer.style.zIndex = '0';
  117. }
  118. return (): void => {
  119. document.removeEventListener('mousedown', handleDown);
  120. document.removeEventListener('mousemove', handleMove);
  121. document.removeEventListener('mouseup', handleUp);
  122. document.removeEventListener('selectstart', handleSelectStart);
  123. document.removeEventListener('touchstart', handleDown);
  124. document.removeEventListener('touchmove', handleMove);
  125. document.removeEventListener('touchend', handleUp);
  126. document.removeEventListener('selectionchange', handleSelectChange);
  127. };
  128. }, [isActive, handleUp, handleSelectChange]);
  129. const Label = (
  130. <Button
  131. shouldFitContainer
  132. align="left"
  133. onClick={onClick}
  134. isActive={isActive}
  135. >
  136. <Icon glyph="highlight" style={{ marginRight: '10px' }} />
  137. {title}
  138. </Button>
  139. );
  140. return (
  141. <ExpansionPanel label={Label} isActive={isActive} showBottomBorder>
  142. <HighlightOption {...data} setDataState={setDataState} />
  143. </ExpansionPanel>
  144. );
  145. };
  146. export default HighlightTools;