Browse Source

optimization component and support touch event

RoyLiu 5 years ago
parent
commit
9d9518cef7
44 changed files with 442 additions and 293 deletions
  1. 48 0
      components/AnnotationItem/data.ts
  2. 6 42
      components/AnnotationItem/index.tsx
  3. 3 0
      components/AnnotationItem/styled.ts
  4. 2 1
      components/AnnotationList/index.tsx
  5. 1 1
      components/AnnotationSelector/index.tsx
  6. 3 1
      components/AnnotationWrapper/index.tsx
  7. 1 0
      components/AnnotationWrapper/styled.ts
  8. 3 1
      components/Button/index.tsx
  9. 6 3
      components/OuterRect/index.tsx
  10. 2 2
      components/SelectBox/styled.ts
  11. 26 12
      components/Sliders/index.tsx
  12. 15 11
      components/StickyNote/index.tsx
  13. 32 47
      components/SvgShapeElement/index.tsx
  14. 1 0
      components/TextField/index.tsx
  15. 6 6
      components/Toolbar/data.ts
  16. 7 10
      components/Typography/index.tsx
  17. 2 2
      components/Watermark/index.tsx
  18. 5 18
      components/WatermarkOption/index.tsx
  19. 3 3
      components/WatermarkTextBox/index.tsx
  20. 0 1
      constants/index.ts
  21. 1 1
      constants/type.ts
  22. 32 34
      containers/Annotation.tsx
  23. 3 3
      containers/AnnotationList.tsx
  24. 4 2
      containers/FreeTextTools.tsx
  25. 19 5
      containers/FreehandTools.tsx
  26. 3 1
      containers/HighlightTools.tsx
  27. 2 3
      containers/PdfViewer.tsx
  28. 1 0
      containers/Search.tsx
  29. 20 6
      containers/ShapeTools.tsx
  30. 17 17
      containers/Sidebar.tsx
  31. 4 2
      containers/StickyNoteTools.tsx
  32. 7 6
      containers/WatermarkTool.tsx
  33. 1 1
      helpers/annotation.ts
  34. 10 5
      helpers/position.ts
  35. 2 2
      helpers/utility.ts
  36. 24 4
      helpers/watermark.ts
  37. 70 25
      hooks/useCursorPosition.ts
  38. 1 1
      i18n.js
  39. 2 1
      package.json
  40. 6 1
      pages/_error.tsx
  41. 6 3
      polyfills.js
  42. 5 5
      server.js
  43. 1 1
      store/initialPdfState.ts
  44. 29 3
      yarn.lock

+ 48 - 0
components/AnnotationItem/data.ts

@@ -0,0 +1,48 @@
+const data: Record<string, any> = {
+  Highlight: {
+    text: 'highlight',
+    icon: 'highlight',
+  },
+  Underline: {
+    text: 'underline',
+    icon: 'underline',
+  },
+  Squiggly: {
+    text: 'squiggly',
+    icon: 'squiggly',
+  },
+  StrikeOut: {
+    text: 'strikeout',
+    icon: 'strikeout',
+  },
+  Ink: {
+    text: 'freehand',
+    icon: 'freehand',
+  },
+  FreeText: {
+    text: 'text box',
+    icon: 'text',
+  },
+  Text: {
+    text: 'sticky note',
+    icon: 'sticky-note',
+  },
+  Square: {
+    text: 'square',
+    icon: 'square',
+  },
+  Circle: {
+    text: 'circle',
+    icon: 'circle',
+  },
+  Line: {
+    text: 'line',
+    icon: 'line',
+  },
+  Arrow: {
+    text: 'arrow line',
+    icon: 'arrow',
+  },
+};
+
+export default data;

+ 6 - 42
components/AnnotationItem/index.tsx

@@ -1,11 +1,12 @@
-import React, { useEffect, useState } from 'react';
+import React from 'react';
 
+import Icon from '../Icon';
 import Divider from '../Divider';
-import Markup from '../Markup';
 import { scrollIntoView } from '../../helpers/utility';
 
+import data from './data';
 import {
-  PageNumber, AnnotationBox, Content, Inner, Text,
+  PageNumber, AnnotationBox,
 } from './styled';
 
 type Props = {
@@ -21,12 +22,7 @@ const AnnotationItem = ({
   type,
   page,
   showPageNum,
-  bdcolor,
-  getText,
-  transparency,
 }: Props): React.ReactElement => {
-  const [content, setContent] = useState([]);
-
   const handleClick = (): void => {
     const ele: HTMLElement | null = document.getElementById(`page_${page}`);
 
@@ -35,20 +31,6 @@ const AnnotationItem = ({
     }
   };
 
-  useEffect(() => {
-    getText().then((text) => {
-      let textArray = [];
-
-      if (text.includes(' ')) {
-        textArray = text.split(' ');
-      } else {
-        textArray = text.match(/.{1,12}/g);
-      }
-
-      setContent(textArray);
-    });
-  }, []);
-
   return (
     <>
       {showPageNum ? (
@@ -58,26 +40,8 @@ const AnnotationItem = ({
         </>
       ) : null}
       <AnnotationBox onClick={handleClick}>
-        <Content>
-          {
-            content.map((textContent: string, index: number): React.ReactElement | null => {
-              const key = `key_${index}`;
-              return textContent ? (
-                <Inner key={key}>
-                  <Text>{textContent}</Text>
-                  <Markup
-                    position={{
-                      top: '0', left: '0', width: '100%', height: '100%',
-                    }}
-                    markupType={type}
-                    bdcolor={bdcolor}
-                    opacity={transparency}
-                  />
-                </Inner>
-              ) : null;
-            })
-          }
-        </Content>
+        <Icon glyph={data[type].icon} />
+        {data[type].text}
       </AnnotationBox>
     </>
   );

+ 3 - 0
components/AnnotationItem/styled.ts

@@ -16,6 +16,9 @@ export const AnnotationBox = styled.div`
   padding: 12px;
   width: 235px;
   margin-bottom: 12px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 `;
 
 export const Content = styled.div`

+ 2 - 1
components/AnnotationList/index.tsx

@@ -100,7 +100,8 @@ const AnnotationList: React.FC<Props> = ({
             },
           } = ele;
           const actualPage = page + 1;
-          const prevPage = index > 0 ? annotations[index - 1].obj_attr.page + 1 : -1;
+          const prevAnnot = annotations[index - 1];
+          const prevPage = index > 0 && prevAnnot ? prevAnnot.obj_attr.page + 1 : -1;
 
           return (
             <Item

+ 1 - 1
components/AnnotationSelector/index.tsx

@@ -53,7 +53,7 @@ const index: React.FC<Props> = ({
             </Box>
             <Divider style={{ margin: '0 10px 0 10px' }} />
             <SliderWrapper>
-              <Sliders defaultValue={opacity} onChange={handleChange} />
+              <Sliders value={opacity} onChange={handleChange} />
             </SliderWrapper>
             <Subtitle>{`${opacity} %`}</Subtitle>
           </>

+ 3 - 1
components/AnnotationWrapper/index.tsx

@@ -21,7 +21,9 @@ const index = forwardRef<Ref, Props>(({
     ref={ref}
     tabIndex={0}
     role="button"
-    onFocus={(): void => { console.log('focus'); }}
+    onFocus={(): void => {
+      // do nothing
+    }}
     {...rest}
   >
     {children}

+ 1 - 0
components/AnnotationWrapper/styled.ts

@@ -2,4 +2,5 @@ import styled from 'styled-components';
 
 export const Wrapper = styled.div`
   touch-action: none;
+  outline: none;
 `;

+ 3 - 1
components/Button/index.tsx

@@ -46,7 +46,9 @@ Button.defaultProps = {
   shouldFitContainer: false,
   isDisabled: false,
   isActive: false,
-  onFocus: (): void => { console.log('focus'); },
+  onFocus: (): void => {
+    // do nothing
+  },
 };
 
 export default Button;

+ 6 - 3
components/OuterRect/index.tsx

@@ -52,9 +52,9 @@ const index: React.FC<Props> = ({
 }: Props) => {
   const data = generateCirclesData(width, height);
   const [state, setState] = useState(initState);
-  const [cursorPosition, setRef] = useCursorPosition(100);
+  const [cursorPosition, setRef] = useCursorPosition(60);
 
-  const handleMouseDown = (e: React.MouseEvent): void => {
+  const handleMouseDown = (e: React.MouseEvent | React.TouchEvent): void => {
     const operatorId = (e.target as HTMLElement).getAttribute('data-id') as string;
     const coord = getAbsoluteCoordinate(document.body, e);
 
@@ -106,7 +106,6 @@ const index: React.FC<Props> = ({
   };
 
   useEffect(() => {
-    console.log(cursorPosition);
     if (cursorPosition.x && cursorPosition.y && state.clickX) {
       if (state.operator === 'move' && onMove) {
         onMove(calcMoveResult(
@@ -128,9 +127,11 @@ const index: React.FC<Props> = ({
 
   useEffect(() => {
     window.addEventListener('mouseup', handleMouseUp);
+    window.addEventListener('touchend', handleMouseUp);
 
     return (): void => {
       window.removeEventListener('mouseup', handleMouseUp);
+      window.removeEventListener('touchend', handleMouseUp);
     };
   }, []);
 
@@ -152,6 +153,7 @@ const index: React.FC<Props> = ({
         height={height + 12}
         stroke={onScale ? color.primary : 'transparency'}
         onMouseDown={handleMouseDown}
+        onTouchStart={handleMouseDown}
         data-id="move"
       />
       {
@@ -160,6 +162,7 @@ const index: React.FC<Props> = ({
             key={attr.direction}
             data-id={attr.direction}
             onMouseDown={handleMouseDown}
+            onTouchStart={handleMouseDown}
             fill={color.primary}
             {...attr}
           />

+ 2 - 2
components/SelectBox/styled.ts

@@ -17,7 +17,7 @@ export const Selected = styled.div`
   background-color: ${color.gray};
   height: 32px;
   border-radius: 4px;
-  padding: 0 6px 0 14px;
+  padding: 0 6px 0 10px;
   transition: background-color 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms;
   outline: none;
   width: 100%;
@@ -27,7 +27,7 @@ export const InputContent = styled.input`
   background-color: ${color.gray};
   border: none;
   outline: none;
-  width: 100%;
+  width: 50%;
 `;
 
 export const Content = styled.div`

+ 26 - 12
components/Sliders/index.tsx

@@ -1,5 +1,5 @@
 import React, {
-  useEffect, useState, useRef, useCallback,
+  useEffect, useState, useRef,
 } from 'react';
 import { fromEvent } from 'rxjs';
 import {
@@ -30,16 +30,25 @@ const Sliders: React.FC<Props> = ({
   const sliderRef = useRef<HTMLDivElement>(null);
   const [valueState, setValueState] = useState(defaultValue);
   const [isActive, setActive] = useState(false);
-  let subscription: any = null;
+  let mouseSubscription: any = null;
+  let touchSubscription: any = null;
 
   const parseValueToPercent = (val: number): number => Math.floor(val * 100);
 
-  const getFingerMoveValue = (event: MouseEvent | React.MouseEvent<HTMLElement>): void => {
+  const getFingerMoveValue = (event: any): void => {
     const { current: slider } = sliderRef;
+    let clientX = 0;
+
+    if (event.touches) {
+      const touches = event.touches[0];
+      ({ clientX } = touches);
+    } else {
+      ({ clientX } = event);
+    }
 
     if (slider) {
       const { width, left } = slider.getBoundingClientRect();
-      const moveDistance = event.clientX - left;
+      const moveDistance = clientX - left;
       const moveRate = moveDistance / width;
       let percent = parseValueToPercent(moveRate);
 
@@ -56,27 +65,31 @@ const Sliders: React.FC<Props> = ({
     }
   };
 
-  const handleTouchMove = useCallback((event: any) => {
+  const handleTouchMove = (event: any): void => {
     event.preventDefault();
     getFingerMoveValue(event);
-  }, []);
+  };
 
-  const handleTouchEnd = useCallback((event: MouseEvent) => {
+  const handleTouchEnd = (event: MouseEvent): void => {
     event.preventDefault();
 
     setActive(false);
-    subscription.unsubscribe();
-  }, []);
+    mouseSubscription.unsubscribe();
+    touchSubscription.unsubscribe();
+  };
 
-  const handleMouseDown = useCallback((event: React.MouseEvent<HTMLElement>) => {
+  const handleMouseDown = (
+    event: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>,
+  ): void => {
     event.preventDefault();
 
     getFingerMoveValue(event);
     setActive(true);
 
-    subscription = fromEvent(document.body, 'mousemove').pipe(throttleTime(35)).subscribe(handleTouchMove);
+    mouseSubscription = fromEvent(document.body, 'mousemove').pipe(throttleTime(35)).subscribe(handleTouchMove);
+    touchSubscription = fromEvent(document.body, 'touchmove').pipe(throttleTime(35)).subscribe(handleTouchMove);
     document.body.addEventListener('mouseup', handleTouchEnd);
-  }, []);
+  };
 
   const setPercent = (): void => {
     const percent = parseValueToPercent((defaultValue || value) / maximum);
@@ -96,6 +109,7 @@ const Sliders: React.FC<Props> = ({
       <Wrapper
         ref={sliderRef}
         onMouseDown={handleMouseDown}
+        onTouchStart={handleMouseDown}
       >
         <Rail track={valueState} />
         <Track

+ 15 - 11
components/StickyNote/index.tsx

@@ -10,7 +10,7 @@ import { rectCalc } from '../../helpers/position';
 
 import { TextArea, TextAreaContainer, TrashCan } from './styled';
 
-const Note: React.SFC<AnnotationElementPropsType> = ({
+const StickyNote: React.SFC<AnnotationElementPropsType> = ({
   viewport,
   scale,
   obj_attr: {
@@ -63,6 +63,7 @@ const Note: React.SFC<AnnotationElementPropsType> = ({
         <Icon
           glyph="sticky-note-2"
           style={{ width: '30px' }}
+          onClick={handleClick}
         />
       </AnnotationContainer>
       {
@@ -73,7 +74,6 @@ const Note: React.SFC<AnnotationElementPropsType> = ({
           >
             <TextArea
               autoFocus
-              onBlur={onBlur}
               defaultValue={content}
               onChange={handleChange}
             />
@@ -86,16 +86,20 @@ const Note: React.SFC<AnnotationElementPropsType> = ({
           </TextAreaContainer>
         ) : null
       }
-      <OuterRect
-        top={annotRect.top}
-        left={annotRect.left}
-        width={25}
-        height={25}
-        onMove={handleMove}
-        onClick={handleClick}
-      />
+      {
+        isEdit ? (
+          <OuterRect
+            top={annotRect.top}
+            left={annotRect.left}
+            width={25}
+            height={25}
+            onMove={handleMove}
+            onClick={onBlur}
+          />
+        ) : null
+      }
     </>
   );
 };
 
-export default Note;
+export default StickyNote;

+ 32 - 47
components/SvgShapeElement/index.tsx

@@ -20,52 +20,37 @@ const SvgShapeElement: React.FC<Props> = ({
   transparency,
   fcolor,
   ftransparency,
-}: Props) => {
-  const generateShape = (): React.ReactNode => {
-    switch (shape) {
-      case 'Circle':
-        return (
-          <ellipse
-            cx="50%"
-            cy="50%"
-            rx={(width - bdwidth) / 2}
-            ry={(height - bdwidth) / 2}
-            stroke={bdcolor}
-            strokeWidth={bdwidth}
-            strokeOpacity={transparency}
-            fill={fcolor}
-            fillOpacity={ftransparency}
-          />
-        );
-      case 'Square': {
-        const indent = bdwidth / 2;
-        return (
-          <rect
-            x={indent}
-            y={indent}
-            width={`${width - bdwidth}px`}
-            height={`${height - bdwidth}px`}
-            stroke={bdcolor}
-            strokeWidth={bdwidth}
-            strokeOpacity={transparency}
-            fill={fcolor}
-            fillOpacity={ftransparency}
-          />
-        );
-      }
-      default: {
-        return {};
-      }
-    }
-  };
-
-  return (
-    <svg
-      viewBox={`0 0 ${width} ${height}`}
-    >
-      {generateShape()}
-    </svg>
-  );
-};
+}: Props) => (
+  <svg
+    viewBox={`0 0 ${width} ${height}`}
+  >
+    {shape === 'Circle' ? (
+      <ellipse
+        cx="50%"
+        cy="50%"
+        rx={(width - bdwidth) / 2}
+        ry={(height - bdwidth) / 2}
+        stroke={bdcolor}
+        strokeWidth={bdwidth}
+        strokeOpacity={transparency}
+        fill={fcolor}
+        fillOpacity={ftransparency}
+      />
+    ) : null}
+    {shape === 'Square' ? (
+      <rect
+        x={bdwidth / 2}
+        y={bdwidth / 2}
+        width={`${width - bdwidth}px`}
+        height={`${height - bdwidth}px`}
+        stroke={bdcolor}
+        strokeWidth={bdwidth}
+        strokeOpacity={transparency}
+        fill={fcolor}
+        fillOpacity={ftransparency}
+      />
+    ) : null}
+  </svg>
+);
 
 export default SvgShapeElement;

+ 1 - 0
components/TextField/index.tsx

@@ -7,6 +7,7 @@ type Props = {
   name?: string;
   onChange?: (val: string) => void;
   onBlur?: () => void;
+  value?: string | number;
   defaultValue?: string | number;
   placeholder?: string;
   disabled?: boolean;

+ 6 - 6
components/Toolbar/data.ts

@@ -3,32 +3,32 @@ export default (t: (key: string) => string): Record<string, any> => ({
     {
       key: 'fit',
       content: t('fit'),
-      value: 'fit',
+      child: 'fit',
     },
     {
       key: '50',
       content: '50 %',
-      value: 50,
+      child: 50,
     },
     {
       key: '100',
       content: '100 %',
-      value: 100,
+      child: 100,
     },
     {
       key: '150',
       content: '150 %',
-      value: 150,
+      child: 150,
     },
     {
       key: '200',
       content: '200 %',
-      value: 200,
+      child: 200,
     },
     {
       key: '250',
       content: '250 %',
-      value: 250,
+      child: 250,
     },
   ],
 });

+ 7 - 10
components/Typography/index.tsx

@@ -15,16 +15,13 @@ const Typography: React.FC<Props> = ({
   children,
   ...rest
 }: Props) => {
-  const getComponent = (): React.FC => {
-    if (variant === 'title') {
-      return Title;
-    }
-    if (variant === 'subtitle') {
-      return Subtitle;
-    }
-    return Body;
-  };
-  const Component = getComponent();
+  let Component = Body;
+  if (variant === 'title') {
+    Component = Title;
+  }
+  if (variant === 'subtitle') {
+    Component = Subtitle;
+  }
 
   return (
     <Component

+ 2 - 2
components/Watermark/index.tsx

@@ -22,7 +22,7 @@ const index: React.FC<Props> = ({
     <TextWrapper
       style={{
         fontSize: `${viewScale * scale * 24}px`,
-        opacity: `${opacity}%`,
+        opacity,
         color: textcolor,
         transform: `rotate(-${rotation}deg)`,
       }}
@@ -32,7 +32,7 @@ const index: React.FC<Props> = ({
   ) : (
     <Img
       style={{
-        opacity: `${opacity}%`,
+        opacity,
         transform: `scale(${viewScale * scale}) rotate(-${rotation}deg)`,
       }}
       src={imagepath}

+ 5 - 18
components/WatermarkOption/index.tsx

@@ -12,8 +12,6 @@ import Divider from '../Divider';
 import ColorSelect from '../ColorSelector';
 import SliderWithTitle from '../SliderWithTitle';
 import TextBox from '../WatermarkTextBox';
-import ImageSelector from '../WatermarkImageSelector';
-// import PageSelector from '../WatermarkPageSelector';
 
 import {
   Container, Head, Body, Footer, IconWrapper,
@@ -37,7 +35,7 @@ const WatermarkOption: React.FC<Props> = ({
   t,
   onClick,
   type,
-  opacity,
+  opacity = 0,
   scale = 1,
   rotation,
   textcolor,
@@ -69,21 +67,11 @@ const WatermarkOption: React.FC<Props> = ({
             child: (
               <TextBox
                 t={t}
-                defaultValue={text}
+                value={text}
                 onChange={(value: string): void => { setDataState({ text: value }); }}
               />
             ),
           },
-          {
-            key: 'image',
-            content: t('image'),
-            child: (
-              <ImageSelector
-                t={t}
-                onChange={(data: string): void => { setDataState({ imagepath: data }); }}
-              />
-            ),
-          },
         ]}
         onChange={(option: SelectOptionType): void => { setDataState({ type: option.key }); }}
       />
@@ -103,9 +91,9 @@ const WatermarkOption: React.FC<Props> = ({
       <Box mt="20">
         <SliderWithTitle
           title={t('opacity')}
-          value={opacity}
-          tips={`${opacity}%`}
-          onSlide={(val: number): void => { setDataState({ opacity: val }); }}
+          value={opacity * 100}
+          tips={`${(opacity * 100).toFixed(0)}%`}
+          onSlide={(val: number): void => { setDataState({ opacity: val * 0.01 }); }}
         />
       </Box>
       <Box mt="20">
@@ -128,7 +116,6 @@ const WatermarkOption: React.FC<Props> = ({
         />
       </Box>
       <Divider orientation="horizontal" style={{ margin: '20px 0' }} />
-      {/* <PageSelector t={t} /> */}
     </Body>
     <Footer>
       <Button appearance="primary" onClick={onSave}>

+ 3 - 3
components/WatermarkTextBox/index.tsx

@@ -8,13 +8,13 @@ import { ContentWrapper } from './styled';
 type Props = {
   t: (key: string) => string;
   onChange: (value: string) => void;
-  defaultValue?: string;
+  value?: string;
 };
 
 const TextBox: React.FC<Props> = ({
   t,
   onChange,
-  defaultValue = '',
+  value = '',
 }: Props): React.ReactElement => (
   <>
     <ContentWrapper>
@@ -23,7 +23,7 @@ const TextBox: React.FC<Props> = ({
     <TextField
       variant="multiline"
       onChange={onChange}
-      defaultValue={defaultValue}
+      value={value}
       shouldFitContainer
     />
   </>

+ 0 - 1
constants/index.ts

@@ -18,5 +18,4 @@ export const ANNOTATION_TYPE: Record<string, any> = {
   circle: 'Circle',
   line: 'Line',
   arrow: 'Line',
-  watermark: 'watermark',
 };

+ 1 - 1
constants/type.ts

@@ -61,7 +61,7 @@ export type AnnotationPositionType = (
 export type AnnotationAttributeType = {
   page: number;
   bdcolor?: string | undefined;
-  position: AnnotationPositionType;
+  position?: AnnotationPositionType;
   transparency?: number | undefined;
   content?: string | undefined;
   style?: number | undefined;

+ 32 - 34
containers/Annotation.tsx

@@ -104,39 +104,20 @@ const Annotation: React.FC<Props> = ({
     }
   }, [obj_type, obj_attr]);
 
-  const Component = (type: string): React.ReactNode => {
-    const childProps = {
-      id,
-      obj_type,
-      obj_attr,
-      scale,
-      isCollapse,
-      isCovered,
-      mousePosition,
-      onUpdate: handleUpdate,
-      onDelete: deleteDialogToggle,
-      viewport,
-      onEdit: handleEdit,
-      isEdit,
-      onBlur: handleInputBlur,
-    };
-
-    switch (type) {
-      case 'Ink':
-        return <Ink {...childProps} />;
-      case 'FreeText':
-        return <FreeText {...childProps} />;
-      case 'Text':
-        return <StickyNote {...childProps} />;
-      case 'Square':
-      case 'Circle':
-        return <Shape {...childProps} />;
-      case 'Line':
-      case 'Arrow':
-        return <Line {...childProps} />;
-      default:
-        return <Highlight {...childProps} />;
-    }
+  const childProps = {
+    id,
+    obj_type,
+    obj_attr,
+    scale,
+    isCollapse,
+    isCovered,
+    mousePosition,
+    onUpdate: handleUpdate,
+    onDelete: deleteDialogToggle,
+    viewport,
+    onEdit: handleEdit,
+    isEdit,
+    onBlur: handleInputBlur,
   };
 
   const wrapperProps = {
@@ -152,7 +133,24 @@ const Annotation: React.FC<Props> = ({
       ref={ref}
       {...wrapperProps}
     >
-      {Component(obj_type)}
+      {((): React.ReactNode => {
+        switch (obj_type) {
+          case 'Ink':
+            return <Ink {...childProps} />;
+          case 'FreeText':
+            return <FreeText {...childProps} />;
+          case 'Text':
+            return <StickyNote {...childProps} />;
+          case 'Square':
+          case 'Circle':
+            return <Shape {...childProps} />;
+          case 'Line':
+          case 'Arrow':
+            return <Line {...childProps} />;
+          default:
+            return <Highlight {...childProps} />;
+        }
+      })()}
       <DeleteDialog
         open={openDialog}
         onCancel={deleteDialogToggle}

+ 3 - 3
containers/AnnotationList.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-import { MARKUP_TYPE } from '../constants';
+import { ANNOTATION_TYPE } from '../constants';
 import { AnnotationType } from '../constants/type';
 import Head from './AnnotationListHead';
 import Drawer from '../components/Drawer';
@@ -12,7 +12,7 @@ import {
   Container,
 } from '../global/sidebarStyled';
 
-const MARKUP_TYPE_ARRAY = Object.values(MARKUP_TYPE);
+const ANNOT_TYPE_ARRAY = Object.values(ANNOTATION_TYPE);
 
 const AnnotationList: React.FC = () => {
   const [{
@@ -31,7 +31,7 @@ const AnnotationList: React.FC = () => {
         <Head />
         <Body
           annotations={annotations.filter(
-            (ele: AnnotationType) => MARKUP_TYPE_ARRAY.includes(ele.obj_type),
+            (ele: AnnotationType) => ANNOT_TYPE_ARRAY.includes(ele.obj_type),
           )}
           viewport={viewport}
           scale={scale}

+ 4 - 2
containers/FreeTextTools.tsx

@@ -43,7 +43,7 @@ const HighlightTools: React.FC<Props> = ({
 
   const addFreeText = (
     pageEle: HTMLElement,
-    event: MouseEvent,
+    event: MouseEvent | TouchEvent,
     attributes: OptionPropsType,
   ): void => {
     const {
@@ -75,7 +75,7 @@ const HighlightTools: React.FC<Props> = ({
     addAnnots([freeText], true);
   };
 
-  const handleMouseDown = useCallback((event: MouseEvent): void => {
+  const handleMouseDown = useCallback((event: MouseEvent | TouchEvent): void => {
     event.preventDefault();
     const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;
 
@@ -88,11 +88,13 @@ const HighlightTools: React.FC<Props> = ({
     const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
     if (isActive && pdfViewer) {
       pdfViewer.addEventListener('mousedown', handleMouseDown);
+      pdfViewer.addEventListener('touchstart', handleMouseDown);
     }
 
     return (): void => {
       if (pdfViewer) {
         pdfViewer.removeEventListener('mousedown', handleMouseDown);
+        pdfViewer.removeEventListener('touchstart', handleMouseDown);
       }
     };
   }, [isActive, handleMouseDown]);

+ 19 - 5
containers/FreehandTools.tsx

@@ -45,7 +45,7 @@ const FreehandTools: React.FC<Props> = ({
     }));
   };
 
-  const handleMouseDown = useCallback((e: MouseEvent): void => {
+  const handleMouseDown = useCallback((e: MouseEvent | TouchEvent): void => {
     const pageEle = (e.target as HTMLElement).parentNode as HTMLElement;
 
     if (pageEle.hasAttribute('data-page-num')) {
@@ -104,18 +104,32 @@ const FreehandTools: React.FC<Props> = ({
     }
   }, [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) {
-      pdfViewer.addEventListener('mousedown', handleMouseDown);
-      pdfViewer.addEventListener('mouseup', handleMouseUp);
+      subscribeEvent();
     }
 
     return (): void => {
       if (pdfViewer) {
-        pdfViewer.removeEventListener('mousedown', handleMouseDown);
-        pdfViewer.removeEventListener('mouseup', handleMouseUp);
+        unsubscribeEvent();
       }
     };
   }, [isActive, handleMouseDown]);

+ 3 - 1
containers/HighlightTools.tsx

@@ -38,7 +38,7 @@ const HighlightTools: React.FC<Props> = ({
     }));
   };
 
-  const selectRange = useCallback((e: MouseEvent): void => {
+  const selectRange = useCallback((e: MouseEvent | TouchEvent): void => {
     if (e.target && isActive) {
       const textLayer = (e.target as HTMLElement).parentNode as HTMLElement;
       if (textLayer && textLayer.getAttribute('data-id') === 'text-layer') {
@@ -55,10 +55,12 @@ const HighlightTools: React.FC<Props> = ({
 
   useEffect(() => {
     if (isActive) {
+      document.addEventListener('touchend', selectRange);
       document.addEventListener('mouseup', selectRange);
     }
 
     return (): void => {
+      document.removeEventListener('touchend', selectRange);
       document.removeEventListener('mouseup', selectRange);
     };
   }, [isActive, selectRange]);

+ 2 - 3
containers/PdfViewer.tsx

@@ -56,9 +56,8 @@ const PdfViewer: React.FC = () => {
       .then((xfdf) => {
         const annotations = parseAnnotationFromXml(xfdf);
         const watermark = parseWatermarkFromXml(xfdf);
-
-        addAnnots(annotations);
-        updateWatermark(watermark);
+        addAnnots([...annotations, watermark]);
+        updateWatermark(watermark.obj_attr);
       })
       .catch((error) => {
         console.log(error);

+ 1 - 0
containers/Search.tsx

@@ -63,6 +63,7 @@ const Search: React.FC = () => {
     const content = textContentItem[divIdx].substring(fromOffset, toOffset);
     const node = document.createTextNode(content);
     const span = document.createElement('span');
+    console.log(div);
     if (highlight) {
       span.style.backgroundColor = 'rgba(255, 211, 0, 0.5)';
       span.appendChild(node);

+ 20 - 6
containers/ShapeTools.tsx

@@ -71,7 +71,7 @@ const Shape: React.FC<Props> = ({
 
   const addShape = useCallback((
     pageEle: HTMLElement,
-    event: MouseEvent,
+    event: MouseEvent | TouchEvent,
     attributes: OptionPropsType,
   ): void => {
     const {
@@ -107,7 +107,7 @@ const Shape: React.FC<Props> = ({
     addAnnots([shapeAnnotation], true);
   }, [viewport, scale, data]);
 
-  const handleMouseDown = (event: MouseEvent): void => {
+  const handleMouseDown = (event: MouseEvent | TouchEvent): void => {
     event.preventDefault();
 
     const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;
@@ -124,18 +124,32 @@ const Shape: React.FC<Props> = ({
     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) {
-      pdfViewer.addEventListener('mousedown', handleMouseDown);
-      pdfViewer.addEventListener('mouseup', handleMouseUp);
+      subscribeEvent();
     }
 
     return (): void => {
       if (pdfViewer) {
-        pdfViewer.removeEventListener('mousedown', handleMouseDown);
-        pdfViewer.removeEventListener('mouseup', handleMouseUp);
+        unsubscribeEvent();
       }
     };
   }, [isActive, addShape]);

+ 17 - 17
containers/Sidebar.tsx

@@ -1,17 +1,17 @@
 import React from 'react';
 import { withTranslation } from '../i18n';
 
-import Button from '../components/Button';
+// import Button from '../components/Button';
 import Typography from '../components/Typography';
-import Icon from '../components/Icon';
-import CreateForm from './CreateForm';
+// import Icon from '../components/Icon';
+// import CreateForm from './CreateForm';
 import MarkupTools from './MarkupTools';
 import WatermarkTool from './WatermarkTool';
 
-import useActions from '../actions';
+// import useActions from '../actions';
 import useStore from '../store';
 
-import { BtnWrapper } from '../global/toolStyled';
+// import { BtnWrapper } from '../global/toolStyled';
 import { SidebarWrapper } from '../global/otherStyled';
 
 type Props = {
@@ -21,16 +21,16 @@ type Props = {
 const Sidebar: React.FC<Props> = ({
   t,
 }: Props) => {
-  const [{ sidebarState, displayMode }, dispatch] = useStore();
-  const { setSidebar } = useActions(dispatch);
+  const [{ displayMode }] = useStore();
+  // const { setSidebar } = useActions(dispatch);
 
-  const onClick = (state: string): void => {
-    if (state === sidebarState) {
-      setSidebar('');
-    } else {
-      setSidebar(state);
-    }
-  };
+  // const onClick = (state: string): void => {
+  //   if (state === sidebarState) {
+  //     setSidebar('');
+  //   } else {
+  //     setSidebar(state);
+  //   }
+  // };
 
   return (
     <SidebarWrapper isHidden={displayMode === 'full'}>
@@ -38,13 +38,13 @@ const Sidebar: React.FC<Props> = ({
         {t('mainMenu')}
       </Typography>
       <MarkupTools />
-      <CreateForm />
-      <BtnWrapper>
+      {/* <CreateForm /> */}
+      {/* <BtnWrapper>
         <Button shouldFitContainer align="left" onClick={(): void => { onClick('add-image'); }}>
           <Icon glyph="add-image" style={{ marginRight: '10px' }} />
           {t('addImages')}
         </Button>
-      </BtnWrapper>
+      </BtnWrapper> */}
       <WatermarkTool />
     </SidebarWrapper>
   );

+ 4 - 2
containers/StickyNoteTools.tsx

@@ -24,7 +24,7 @@ const StickyNoteTools: React.FC<Props> = ({
   const [{ viewport, scale }, dispatch] = useStore();
   const { addAnnots } = useActions(dispatch);
 
-  const addStickyNote = (pageEle: HTMLElement, event: MouseEvent): void => {
+  const addStickyNote = (pageEle: HTMLElement, event: MouseEvent | TouchEvent): void => {
     const pageNum = pageEle.getAttribute('data-page-num') || 0;
     const coordinate = getAbsoluteCoordinate(pageEle, event);
 
@@ -46,7 +46,7 @@ const StickyNoteTools: React.FC<Props> = ({
     addAnnots([stickyNote], true);
   };
 
-  const handleMouseDown = useCallback((event: MouseEvent): void => {
+  const handleMouseDown = useCallback((event: MouseEvent | TouchEvent): void => {
     event.preventDefault();
 
     const pageEle = (event.target as HTMLElement).parentNode as HTMLElement;
@@ -60,11 +60,13 @@ const StickyNoteTools: React.FC<Props> = ({
     const pdfViewer = document.getElementById('pdf_viewer') as HTMLDivElement;
 
     if (isActive && pdfViewer) {
+      window.addEventListener('touchstart', handleMouseDown);
       window.addEventListener('mousedown', handleMouseDown);
     }
 
     return (): void => {
       if (pdfViewer) {
+        window.removeEventListener('touchstart', handleMouseDown);
         window.removeEventListener('mousedown', handleMouseDown);
       }
     };

+ 7 - 6
containers/WatermarkTool.tsx

@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
 import queryString from 'query-string';
 import { withTranslation } from '../i18n';
 
-import { ANNOTATION_TYPE } from '../constants';
 import { WatermarkType, AnnotationType } from '../constants/type';
 import Icon from '../components/Icon';
 import Button from '../components/Button';
@@ -39,9 +38,11 @@ const WatermarkTool: React.FC<Props> = ({
   };
 
   const removeWatermarkInAnnots = (): void => {
-    const index = annotations.find((ele: AnnotationType) => ele.obj_type === 'watermark');
-    annotations.splice(index, 1);
-    updateAnnots(annotations);
+    const index = annotations.findIndex((ele: AnnotationType) => ele.obj_type === 'watermark');
+    if (index >= 0) {
+      annotations.splice(index, 1);
+      updateAnnots(annotations);
+    }
   };
 
   const handleClick = (): void => {
@@ -52,12 +53,12 @@ const WatermarkTool: React.FC<Props> = ({
   const handleSave = (): void => {
     const { type, imagepath, ...rest } = watermark;
     const watermarkData = {
-      obj_type: ANNOTATION_TYPE.watermark,
+      obj_type: 'watermark',
       obj_attr: {
         ...rest,
         type,
         pages: `0-${totalPage}`,
-        opacity: (watermark.opacity || 0) * 0.01,
+        opacity: watermark.opacity,
         original_image_name: imagepath ? `temp.${imagepath.match(/image\/(.*);base64/)[1]}` : '',
         image_data: type === 'image' ? imagepath.match(/base64,(.*)/)[1] : '',
       },

+ 1 - 1
helpers/annotation.ts

@@ -165,7 +165,7 @@ export const parseAnnotationObject = ({
     transparency,
     fcolor,
     ftransparency,
-    position,
+    position = '',
     content,
     style,
     bdwidth,

+ 10 - 5
helpers/position.ts

@@ -162,17 +162,22 @@ export const parsePositionForBackend = (
 type GetAbsoluteCoordinateType = (
   (
     parentElement: HTMLElement | SVGSVGElement | SVGCircleElement | null,
-    clickEvent: MouseEvent | React.MouseEvent
+    clickEvent: any
   ) => { x: number; y: number }
 );
 
 export const getAbsoluteCoordinate: GetAbsoluteCoordinateType = (parentElement, clickEvent) => {
   if (!parentElement) return { x: 0, y: 0 };
 
-  const {
-    pageX,
-    pageY,
-  } = clickEvent;
+  let pageX = 0;
+  let pageY = 0;
+  if (clickEvent.touches) {
+    const touch = clickEvent.touches[0];
+    ({ pageX, pageY } = touch);
+  } else {
+    ({ pageX, pageY } = clickEvent);
+  }
+
   const rect = parentElement.getBoundingClientRect();
   const offsetX = window.pageXOffset || window.scrollX || 0;
   const offsetY = window.pageYOffset || window.scrollY || 0;

+ 2 - 2
helpers/utility.ts

@@ -47,8 +47,8 @@ export const watchScroll = (
   };
 
   const subscriber = fromEvent(element, 'scroll').pipe(
-    throttleTime(200),
-    auditTime(300),
+    throttleTime(160),
+    auditTime(250),
   ).subscribe(debounceScroll);
 
   state.subscriber = subscriber;

+ 24 - 4
helpers/watermark.ts

@@ -4,8 +4,14 @@ import {
 import { floatToHex } from './utility';
 import { xmlParser, getElementsByTagname } from './dom';
 
-export const parseWatermarkFromXml = (xmlString: string): WatermarkType => {
-  if (!xmlString) return {};
+type WatermarkAnnotType = {
+  obj_type: string;
+  obj_attr: WatermarkType;
+};
+
+export const parseWatermarkFromXml = (xmlString: string): WatermarkAnnotType => {
+  if (!xmlString) return { obj_type: 'watermark', obj_attr: {} };
+
   const xmlDoc = xmlParser(xmlString);
   const elements = xmlDoc.firstElementChild || xmlDoc.firstChild;
   const data: WatermarkType = {};
@@ -19,7 +25,7 @@ export const parseWatermarkFromXml = (xmlString: string): WatermarkType => {
           data.text = array[index + 1].textContent.trim();
           break;
         case 'Opacity': {
-          data.opacity = ele.attributes.value.value * 100;
+          data.opacity = ele.attributes.value.value;
           break;
         }
         case 'Rotation': {
@@ -37,12 +43,26 @@ export const parseWatermarkFromXml = (xmlString: string): WatermarkType => {
           data.textcolor = floatToHex(r, g, b);
           break;
         }
+        case 'PageRange': {
+          data.pages = array[index + 1].textContent.trim();
+          break;
+        }
         default:
           break;
       }
     });
   }
-  return data;
+  return {
+    obj_type: 'watermark',
+    obj_attr: {
+      vertalign: 'center',
+      horizalign: 'center',
+      xoffset: 0,
+      yoffset: 0,
+      isfront: 'yes',
+      ...data,
+    },
+  };
 };
 
 export default parseWatermarkFromXml;

+ 70 - 25
hooks/useCursorPosition.ts

@@ -42,7 +42,7 @@ const useCursorPosition: UseCursorPositionType = (time = defaultTime) => {
   const entered = useRef(false);
   const isDown = useRef(false);
 
-  const onMoveEvent = useCallback((e: MouseEvent | Event): void => {
+  const onMouseMoveEvent = useCallback((e: MouseEvent | Event): void => {
     if (element) {
       const {
         clientX,
@@ -63,39 +63,84 @@ const useCursorPosition: UseCursorPositionType = (time = defaultTime) => {
     }
   }, [element]);
 
+  const onTouchMoveEvent = useCallback((e: TouchEvent | Event): void => {
+    if (element) {
+      const {
+        clientX,
+        clientY,
+        screenX,
+        screenY,
+      } = (e as TouchEvent).touches[0];
+      const coordinate = getAbsoluteCoordinate(element, e as TouchEvent);
+
+      setState({
+        x: coordinate.x,
+        y: coordinate.y,
+        clientX,
+        clientY,
+        screenX,
+        screenY,
+      });
+    }
+  }, [element]);
+
   useEffect(() => {
+    let mouseSubscription: any = null;
+    let touchSubscription: any = null;
+
+    const onEnter = (): void => {
+      entered.current = true;
+    };
+
+    const onLeave = (): void => {
+      entered.current = false;
+      setState(initialState);
+    };
+
+    const onDown = (e: any): void => {
+      entered.current = true;
+      isDown.current = true;
+      onMouseMoveEvent(e);
+    };
+
+    const onTouch = (e: any): void => {
+      entered.current = true;
+      isDown.current = true;
+      onTouchMoveEvent(e);
+    };
+
+    const onUp = (): void => {
+      entered.current = false;
+      isDown.current = false;
+      setState(initialState);
+      if (mouseSubscription) mouseSubscription.unsubscribe();
+      if (touchSubscription) touchSubscription.unsubscribe();
+    };
+
     if (element) {
-      let subscription: any = null;
-      const onEnter = (): void => {
-        entered.current = true;
-      };
-
-      const onLeave = (): void => {
-        entered.current = false;
-        setState(initialState);
-      };
-
-      const onDown = (e: any): void => {
-        entered.current = true;
-        isDown.current = true;
-        onMoveEvent(e);
-      };
-
-      subscription = fromEvent(element, 'mousemove').pipe(throttleTime(time)).subscribe(onMoveEvent);
-
-      const onUp = (): void => {
-        entered.current = false;
-        isDown.current = false;
-        setState(initialState);
-        if (subscription) subscription.unsubscribe();
-      };
+      mouseSubscription = fromEvent(element, 'mousemove').pipe(throttleTime(time)).subscribe(onMouseMoveEvent);
+      touchSubscription = fromEvent(element, 'touchmove').pipe(throttleTime(time)).subscribe(onTouchMoveEvent);
 
       const addEvent = element.addEventListener.bind(element);
       addEvent('mouseenter', onEnter);
       addEvent('mouseleave', onLeave);
       addEvent('mousedown', onDown);
+      addEvent('touchstart', onTouch);
       addEvent('mouseup', onUp);
+      addEvent('touchend', onUp);
     }
+
+    return (): void => {
+      if (element) {
+        const removeEvent = element.removeEventListener.bind(element);
+        removeEvent('mouseenter', onEnter);
+        removeEvent('mouseleave', onLeave);
+        removeEvent('mousedown', onDown);
+        removeEvent('touchstart', onTouch);
+        removeEvent('mouseup', onUp);
+        removeEvent('touchend', onUp);
+      }
+    };
   }, [element]);
 
   return [state, setElement];

+ 1 - 1
i18n.js

@@ -4,7 +4,7 @@ const NextI18NextInstance = new NextI18Next({
   defaultNS: 'home',
   lowerCaseLng: true,
   localeSubpaths: {
-    'zh-tw': 'zh-tw',
+    en: 'en',
   },
   defaultLanguage: 'zh-tw',
   otherLanguages: ['en'],

+ 2 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "create-next-example-app",
   "version": "1.0.0",
   "scripts": {
-    "dev": "node server.js",
+    "dev": "babel-node server.js",
     "build": "next build",
     "start": "NODE_ENV=production node server.js",
     "export": "NODE_ENV=production ENV=production npm run build && next export",
@@ -32,6 +32,7 @@
     "prop-types": "^15.6.2",
     "query-string": "^6.9.0",
     "react": "16.10.0",
+    "react-app-polyfill": "^1.0.6",
     "react-dom": "16.10.0",
     "react-popper": "^1.3.6",
     "redux-devtools-extension": "^2.13.5",

+ 6 - 1
pages/_error.tsx

@@ -1,9 +1,14 @@
 import React from 'react';
+import { NextPage } from 'next';
 
-const error: React.FC = () => (
+const error: NextPage = () => (
   <div>
     404 ERROR
   </div>
 );
 
+error.getInitialProps = async (): Promise<any> => ({
+  namespacesRequired: ['common'],
+});
+
 export default error;

+ 6 - 3
polyfills.js

@@ -1,3 +1,6 @@
-import '@babel/polyfill';
-import 'core-js/modules/es.promise';
-import 'core-js/modules/es.promise.finally';
+import 'react-app-polyfill/ie11';
+import 'react-app-polyfill/stable';
+// import '@babel/polyfill';
+// import 'core-js/modules/es.symbol';
+// import 'core-js/modules/es.promise';
+// import 'core-js/modules/es.promise.finally';

+ 5 - 5
server.js

@@ -1,8 +1,8 @@
-const express = require('express');
-const next = require('next');
-const nextI18NextMiddleware = require('next-i18next/middleware').default;
+import express from 'express';
+import next from 'next';
+import nextI18NextMiddleware from 'next-i18next/middleware';
 
-const nextI18next = require('./i18n');
+import nextI18next, { initPromise } from './i18n';
 
 const port = process.env.PORT || 8080;
 const app = next({ dev: process.env.NODE_ENV !== 'production' });
@@ -12,7 +12,7 @@ const handle = app.getRequestHandler();
   await app.prepare();
   const server = express();
 
-  await nextI18next.initPromise;
+  await initPromise;
   server.use(nextI18NextMiddleware(nextI18next));
 
   server.get('*', (req, res) => handle(req, res));

+ 1 - 1
store/initialPdfState.ts

@@ -35,7 +35,7 @@ export default {
   watermark: {
     type: 'text',
     textcolor: theme.gray,
-    opacity: 50,
+    opacity: 0.5,
     text: '',
     scale: 1,
     rotation: 0,

+ 29 - 3
yarn.lock

@@ -2007,7 +2007,7 @@ array.prototype.flat@^1.2.1:
     define-properties "^1.1.3"
     es-abstract "^1.17.0-next.1"
 
-asap@~2.0.3:
+asap@~2.0.3, asap@~2.0.6:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@@ -3089,7 +3089,7 @@ core-js-pure@^3.0.0:
   resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"
   integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
 
-core-js@3.6.4, core-js@^3.2.1:
+core-js@3.6.4, core-js@^3.2.1, core-js@^3.5.0:
   version "3.6.4"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647"
   integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==
@@ -7718,6 +7718,13 @@ promise@7.1.1:
   dependencies:
     asap "~2.0.3"
 
+promise@^8.0.3:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
+  integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==
+  dependencies:
+    asap "~2.0.6"
+
 prompts@^2.0.1:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.0.tgz#a444e968fa4cc7e86689a74050685ac8006c4cc4"
@@ -7861,6 +7868,13 @@ querystring@0.2.0, querystring@^0.2.0:
   resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
   integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
 
+raf@^3.4.1:
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
+  integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
+  dependencies:
+    performance-now "^2.1.0"
+
 randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -7891,6 +7905,18 @@ raw-body@2.4.0:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
+react-app-polyfill@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.6.tgz#890f8d7f2842ce6073f030b117de9130a5f385f0"
+  integrity sha512-OfBnObtnGgLGfweORmdZbyEz+3dgVePQBb3zipiaDsMHV1NpWm0rDFYIVXFV/AK+x4VIIfWHhrdMIeoTLyRr2g==
+  dependencies:
+    core-js "^3.5.0"
+    object-assign "^4.1.1"
+    promise "^8.0.3"
+    raf "^3.4.1"
+    regenerator-runtime "^0.13.3"
+    whatwg-fetch "^3.0.0"
+
 react-dom@16.10.0:
   version "16.10.0"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.10.0.tgz#319356767b5c044f3c016eef28518ef7726dce84"
@@ -9732,7 +9758,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
   dependencies:
     iconv-lite "0.4.24"
 
-whatwg-fetch@3.0.0:
+whatwg-fetch@3.0.0, whatwg-fetch@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
   integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==