123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import React, { useState, useEffect, useCallback } from 'react';
- import { v4 as uuidv4 } from 'uuid';
- import { ANNOTATION_TYPE } from '../constants';
- import Icon from '../components/Icon';
- import Button from '../components/Button';
- import ExpansionPanel from '../components/ExpansionPanel';
- import InkOption from '../components/InkOption';
- import {
- getAbsoluteCoordinate,
- parsePositionForBackend,
- } from '../helpers/position';
- import {
- parseAnnotationObject,
- appendUserIdAndDate,
- } from '../helpers/annotation';
- import { switchPdfViewerScrollState } from '../helpers/pdf';
- import useCursorPosition from '../hooks/useCursorPosition';
- import useActions from '../actions';
- import useStore from '../store';
- type Props = {
- title: string;
- isActive: boolean;
- onClick: () => void;
- };
- const FreehandTool: React.FC<Props> = ({ title, isActive, onClick }: Props) => {
- const [cursorPosition, setRef] = useCursorPosition(40);
- const [uuid, setUuid] = useState('');
- const [data, setData] = useState({
- type: 'pen',
- opacity: 100,
- color: '#FF0000',
- width: 3,
- });
- const [{ viewport, scale, annotations }, dispatch] = useStore();
- const { addAnnots, updateAnnots } = useActions(dispatch);
- const setDataState = (obj: OptionPropsType): void => {
- setData(prev => ({
- ...prev,
- ...obj,
- }));
- };
- const handleMouseDown = useCallback(
- (event: MouseEvent | TouchEvent): void => {
- const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;
- switchPdfViewerScrollState('hidden');
- if (pageEle.hasAttribute('data-page-num')) {
- setRef(pageEle);
- const pageNum = pageEle.getAttribute('data-page-num') || 0;
- const coordinate = getAbsoluteCoordinate(pageEle, event);
- const id = uuidv4();
- setUuid(id);
- const annotData = {
- id,
- obj_type: ANNOTATION_TYPE.ink,
- obj_attr: {
- page: pageNum as number,
- bdcolor: data.color,
- bdwidth: data.width,
- position: [[coordinate]],
- transparency: data.opacity,
- },
- };
- const freehand = appendUserIdAndDate(
- parseAnnotationObject(annotData, viewport.height, scale)
- );
- addAnnots([freehand]);
- }
- },
- [data, viewport, scale]
- );
- const handleMouseUp = useCallback((): void => {
- switchPdfViewerScrollState('scroll');
- const index = annotations.length - 1;
- if (annotations[index]) {
- const position = annotations[index].obj_attr.position as PointType[][];
- if (!position[0]) return;
- if (position[0].length === 1 && annotations[index].id === uuid) {
- const point = position[0][0];
- annotations[index].obj_attr.position = [
- [
- { x: point.x - 5, y: point.y - 5 },
- { x: point.x + 5, y: point.y + 5 },
- ],
- ];
- annotations[index] = appendUserIdAndDate(annotations[index]);
- updateAnnots([...annotations]);
- }
- setRef(null);
- setUuid('');
- }
- }, [annotations, uuid]);
- useEffect(() => {
- const index = annotations.length - 1;
- if (
- annotations[index] &&
- annotations[index].id === uuid &&
- cursorPosition.x &&
- cursorPosition.y
- ) {
- const type = annotations[index].obj_type;
- const position = annotations[index].obj_attr.position as PointType[][];
- const coordinates = parsePositionForBackend(
- type,
- { x: cursorPosition.x, y: cursorPosition.y },
- viewport.height,
- scale
- ) as PointType;
- const lastPosition = position[0].slice(-1)[0];
- if (
- coordinates.x !== lastPosition.x &&
- coordinates.y !== lastPosition.y
- ) {
- position[0].push(coordinates);
- annotations[index].obj_attr.position = position;
- annotations[index] = appendUserIdAndDate(annotations[index]);
- updateAnnots([...annotations]);
- }
- }
- }, [annotations, cursorPosition, uuid]);
- const subscribeEvent = (): void => {
- const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
- pdfViewer.addEventListener('mousedown', handleMouseDown);
- pdfViewer.addEventListener('mouseup', handleMouseUp);
- pdfViewer.addEventListener('touchstart', handleMouseDown);
- pdfViewer.addEventListener('touchend', handleMouseUp);
- };
- const unsubscribeEvent = (): void => {
- const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
- pdfViewer.removeEventListener('mousedown', handleMouseDown);
- pdfViewer.removeEventListener('mouseup', handleMouseUp);
- pdfViewer.removeEventListener('touchstart', handleMouseDown);
- pdfViewer.removeEventListener('touchend', handleMouseUp);
- };
- useEffect(() => {
- const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
- if (isActive && pdfViewer) {
- subscribeEvent();
- }
- return (): void => {
- if (pdfViewer) {
- unsubscribeEvent();
- }
- };
- }, [isActive, handleMouseDown, handleMouseUp]);
- const Label = (
- <Button
- shouldFitContainer
- align="left"
- onClick={onClick}
- isActive={isActive}
- >
- <Icon glyph="freehand" style={{ marginRight: '10px' }} />
- {title}
- </Button>
- );
- return (
- <ExpansionPanel label={Label} isActive={isActive} showBottomBorder>
- <InkOption
- type={data.type}
- color={data.color}
- opacity={data.opacity}
- width={data.width}
- setDataState={setDataState}
- />
- </ExpansionPanel>
- );
- };
- export default FreehandTool;
|