123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- import React, { useEffect, useState, useCallback } from 'react';
- import { v4 as uuidv4 } from 'uuid';
- import { ANNOTATION_TYPE } from '../constants';
- import Button from '../components/Button';
- import ExpansionPanel from '../components/ExpansionPanel';
- import Icon from '../components/Icon';
- import ShapeOption from '../components/ShapeOption';
- 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 Shape: React.FC<Props> = ({ title, isActive, onClick }: Props) => {
- const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });
- const [uuid, setUuid] = useState('');
- const [data, setData] = useState({
- shape: 'square',
- type: 'fill',
- color: '#FBB705',
- opacity: 35,
- width: 0,
- });
- const [cursorPosition, setRef] = useCursorPosition();
- const [{ viewport, scale, annotations }, dispatch] = useStore();
- const { addAnnots, updateAnnots } = useActions(dispatch);
- const setDataState = (obj: OptionPropsType): void => {
- setData(prev => ({
- ...prev,
- ...obj,
- }));
- };
- const convertPosition = (
- type: string,
- x1: number,
- y1: number,
- x2: number,
- y2: number
- ): any => {
- switch (type) {
- case 'Line':
- return {
- start: {
- x: x1,
- y: y1,
- },
- end: {
- x: x2,
- y: y2,
- },
- };
- default:
- return {
- top: y2 > y1 ? y1 : y2,
- left: x2 > x1 ? x1 : x2,
- right: x2 > x1 ? x2 : x1,
- bottom: y2 > y1 ? y2 : y1,
- };
- }
- };
- const addShape = useCallback(
- (
- pageEle: HTMLElement,
- event: MouseEvent | TouchEvent,
- attributes: OptionPropsType
- ): void => {
- const { shape = '', type, opacity, color, width = 0 } = attributes;
- const pageNum = pageEle.getAttribute('data-page-num') || 0;
- const coordinate = getAbsoluteCoordinate(pageEle, event);
- const id = uuidv4();
- setUuid(id);
- setStartPosition(coordinate);
- const shapeType = ANNOTATION_TYPE[shape];
- const position = convertPosition(
- shapeType,
- coordinate.x - 8,
- coordinate.y - 8,
- coordinate.x + 8,
- coordinate.y + 8
- );
- const annoteData = {
- id,
- obj_type: shapeType,
- obj_attr: {
- page: pageNum as number,
- position,
- bdcolor: color,
- fcolor: type === 'fill' ? color : undefined,
- transparency: opacity,
- ftransparency: type === 'fill' ? opacity : undefined,
- bdwidth: width,
- is_arrow: shape === 'arrow',
- },
- };
- const shapeAnnotation = appendUserIdAndDate(
- parseAnnotationObject(annoteData, viewport.height, scale)
- );
- addAnnots([shapeAnnotation]);
- },
- [viewport, scale, data]
- );
- const handleMouseDown = (event: MouseEvent | TouchEvent): void => {
- switchPdfViewerScrollState('hidden');
- const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;
- if (pageEle.hasAttribute('data-page-num')) {
- addShape(pageEle, event, data);
- setRef(pageEle);
- }
- };
- const handleMouseUp = (): void => {
- switchPdfViewerScrollState('scroll');
- setRef(null);
- setUuid('');
- setStartPosition({ x: 0, y: 0 });
- };
- 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, addShape]);
- const handleUpdate = useCallback(
- (start: Record<string, any>, end: Record<string, any>): void => {
- const index = annotations.length - 1;
- const { x: x1, y: y1 } = start;
- const { x: x2, y: y2 } = end;
- if (annotations[index] && annotations[index].id === uuid) {
- const type = annotations[index].obj_type;
- const position = convertPosition(type, x1, y1, x2, y2);
- annotations[index].obj_attr.position = parsePositionForBackend(
- type,
- position,
- viewport.height,
- scale
- );
- annotations[index] = appendUserIdAndDate(annotations[index]);
- updateAnnots([...annotations]);
- }
- },
- [annotations, viewport, scale, uuid]
- );
- useEffect(() => {
- if (
- startPosition.x &&
- startPosition.y &&
- cursorPosition.x &&
- cursorPosition.y
- ) {
- handleUpdate(startPosition, cursorPosition);
- }
- }, [startPosition, cursorPosition]);
- const Label = (
- <Button
- shouldFitContainer
- align="left"
- onClick={onClick}
- isActive={isActive}
- >
- <Icon glyph="shape" style={{ marginRight: '10px' }} />
- {title}
- </Button>
- );
- return (
- <ExpansionPanel isActive={isActive} label={Label}>
- <ShapeOption {...data} setDataState={setDataState} />
- </ExpansionPanel>
- );
- };
- export default Shape;
|