Просмотр исходного кода

custom color picker

* upgrade old version react-color
* on blur component
* tool bar auto hide
RoyLiu 4 лет назад
Родитель
Сommit
2662b5e6b7

+ 9 - 7
components/AnnotationList/index.tsx

@@ -24,6 +24,7 @@ const AnnotationList: React.FC<Props> = ({
   scale,
   pdf,
 }: Props) => {
+  let observer: any = null;
   const { t } = useTranslation('sidebar');
   const [renderQueue, setQueue] = useState<AnnotationType[]>([]);
   const containerRef = useRef<HTMLDivElement>(null);
@@ -54,7 +55,7 @@ const AnnotationList: React.FC<Props> = ({
         renderQueue.length !== annotations.length
       ) {
         const start = renderQueue.length;
-        const end = renderQueue.length + 10;
+        const end = renderQueue.length + 15;
         const newQueue = [...renderQueue, ...annotations.slice(start, end)];
         setQueue(newQueue);
       }
@@ -63,16 +64,18 @@ const AnnotationList: React.FC<Props> = ({
   );
 
   useEffect(() => {
-    const state = watchScroll(containerRef.current, scrollUpdate);
+    observer = watchScroll(containerRef.current, scrollUpdate);
 
     return (): void => {
-      state.subscriber.unsubscribe();
+      if (observer) {
+        observer.subscriber.unsubscribe();
+      }
     };
-  }, [scrollUpdate]);
+  }, [containerRef, scrollUpdate]);
 
   useEffect(() => {
     if (isActive) {
-      setQueue(annotations.slice(0, 10));
+      setQueue(annotations.slice(0, 15));
     }
   }, [isActive, annotations]);
 
@@ -102,8 +105,7 @@ const AnnotationList: React.FC<Props> = ({
                 bdcolor={bdcolor || ''}
                 transparency={transparency || 0}
                 getText={(): Promise<any> =>
-                  getText(actualPage, position as PositionType[])
-                }
+                  getText(actualPage, position as PositionType[])}
                 showPageNum={actualPage !== prevPage}
               />
             );

+ 6 - 5
components/AnnotationSelector/index.tsx

@@ -16,7 +16,7 @@ type Props = {
   opacityProps?: number;
 };
 
-const index: React.FC<Props> = ({
+const AnnotationOptions: React.FC<Props> = ({
   onUpdate,
   onDelete,
   colorProps,
@@ -41,7 +41,7 @@ const index: React.FC<Props> = ({
 
   return (
     <Container>
-      {openSlider ? (
+      {openSlider && (
         <>
           <Box w="40px" h="36px" d="flex" j="center">
             <Icon glyph="right-arrow" onClick={sliderToggle} />
@@ -52,7 +52,8 @@ const index: React.FC<Props> = ({
           </SliderWrapper>
           <Subtitle>{`${opacity} %`}</Subtitle>
         </>
-      ) : (
+      )}
+      {!openSlider && (
         <>
           <ColorSelector selectedColor={colorProps} onClick={handleClick} />
           <Subtitle>opacity</Subtitle>
@@ -63,7 +64,7 @@ const index: React.FC<Props> = ({
           >
             {`${opacity} %`}
           </Button>
-          <Divider style={{ margin: '0 6px 0 15px' }} />
+          <Divider style={{ height: '44px', margin: '0 6px 0 15px' }} />
           <Box w="48px" h="36px" d="flex" j="center">
             <Icon glyph="trash" onClick={onDelete} />
           </Box>
@@ -73,4 +74,4 @@ const index: React.FC<Props> = ({
   );
 };
 
-export default index;
+export default AnnotationOptions;

+ 4 - 3
components/AnnotationSelector/styled.ts

@@ -2,13 +2,14 @@ import styled from 'styled-components';
 
 export const Container = styled.div`
   min-width: 300px;
-  height: 53px;
-  border-radius: 2px;
+  height: auto;
+  min-height: 53px;
+  border-radius: 5px;
+  padding: 5px;
   box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.38);
   background-color: rgba(0, 0, 0, 0.6);
   position: relative;
   display: flex;
-  padding: 0 5px;
   align-items: center;
 
   :before {

+ 1 - 10
components/AnnotationWrapper/index.tsx

@@ -3,7 +3,6 @@ import React, { forwardRef } from 'react';
 import { Wrapper } from './styled';
 
 type Props = {
-  onBlur: () => void;
   onMouseDown: () => void;
   onMouseOver: () => void;
   onMouseOut: () => void;
@@ -14,15 +13,7 @@ type Props = {
 type Ref = HTMLDivElement;
 
 const index = forwardRef<Ref, Props>(({ children, ...rest }: Props, ref) => (
-  <Wrapper
-    ref={ref}
-    tabIndex={0}
-    role="button"
-    onFocus={(): void => {
-      // do nothing
-    }}
-    {...rest}
-  >
+  <Wrapper ref={ref} tabIndex={0} role="form" {...rest}>
     {children}
   </Wrapper>
 ));

+ 40 - 0
components/ClickAwayListener.tsx

@@ -0,0 +1,40 @@
+import React, { useEffect, useRef } from 'react';
+
+type Props = {
+  onClick: (e: MouseEvent | TouchEvent) => void;
+  children: React.ReactNode;
+};
+
+const ClickAwayListener: React.FC<Props> = ({
+  onClick,
+  children,
+  ...rest
+}: Props) => {
+  const node = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    const handleClick = (e: MouseEvent | TouchEvent) => {
+      if (node.current && node.current.contains(e.target as HTMLElement)) {
+        return;
+      }
+
+      onClick(e);
+    };
+
+    document.addEventListener('mousedown', handleClick);
+    document.addEventListener('touchstart', handleClick);
+
+    return () => {
+      document.removeEventListener('mousedown', handleClick);
+      document.removeEventListener('touchstart', handleClick);
+    };
+  }, [onClick]);
+
+  return (
+    <div ref={node} {...rest}>
+      {children}
+    </div>
+  );
+};
+
+export default ClickAwayListener;

+ 99 - 0
components/ColorPicker/index.tsx

@@ -0,0 +1,99 @@
+/* eslint-disable no-param-reassign */
+import React from 'react';
+
+import { CustomPicker } from 'react-color';
+import {
+  EditableInput,
+  Hue,
+  Saturation,
+} from 'react-color/lib/components/common';
+
+import Button from '../Button';
+
+import {
+  Wrapper,
+  HueWrapper,
+  SaturationWrapper,
+  Label,
+  InputWrapper,
+  Swatch,
+} from './styled';
+
+type ColorType = {
+  h?: string;
+  s?: string;
+  l?: string;
+  v?: string;
+  a?: string;
+  hex?: string;
+};
+
+type Props = {
+  hex: string;
+  hsl: object;
+  hsv: object;
+  onChange: (info: ColorType) => void;
+  onSubmit: (color: any) => void;
+};
+
+const ColorPicker: React.FC<Props> = ({
+  hex,
+  hsl,
+  hsv,
+  onChange,
+  onSubmit,
+}: Props) => {
+  const styles = {
+    input: {
+      height: 28,
+      width: '80px',
+      border: `1px solid white`,
+      color: 'white',
+      backgroundColor: 'transparent',
+      paddingLeft: 10,
+      borderRadius: '4px',
+    },
+  };
+
+  const handleClick = () => {
+    onSubmit({ hex, hsl, hsv });
+  };
+
+  return (
+    <Wrapper>
+      <SaturationWrapper>
+        <Saturation hsl={hsl} hsv={hsv} onChange={onChange} />
+      </SaturationWrapper>
+      <HueWrapper>
+        <Hue hsl={hsl} onChange={onChange} />
+      </HueWrapper>
+
+      <InputWrapper>
+        <Swatch bg={hex} />
+        <Label>Hex</Label>
+        <EditableInput
+          label="hex"
+          style={{ input: styles.input }}
+          value={hex}
+          onChange={onChange}
+        />
+        <Button
+          appearance="primary-hollow"
+          style={{ marginLeft: '100px' }}
+          onClick={handleClick}
+        >
+          OK
+        </Button>
+      </InputWrapper>
+    </Wrapper>
+  );
+};
+
+const areEqual = (prevProps: any, nextProps: any) => {
+  if (prevProps.color !== nextProps.color) {
+    return false;
+  }
+  return true;
+};
+
+export default React.memo(CustomPicker(ColorPicker), areEqual);

+ 46 - 0
components/ColorPicker/styled.ts

@@ -0,0 +1,46 @@
+import styled from 'styled-components';
+
+export const Wrapper = styled.div`
+  width: 360px;
+  border-radius: 5px;
+  background-color: rgba(0, 0, 0, 0.5);
+  padding-bottom: 1px;
+  overflow: hidden;
+  position: relative;
+`;
+
+export const HueWrapper = styled.div`
+  height: 10px;
+  position: relative;
+  margin: 0 10px 10px;
+`;
+
+export const SaturationWrapper = styled.div`
+  height: 146px;
+  position: relative;
+  margin-bottom: 10px;
+`;
+
+export const Label = styled.label`
+  color: white;
+  margin-right: 5px;
+`;
+
+export const InputWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+  margin-left: 10px;
+
+  span {
+    display: none;
+  }
+`;
+
+export const Swatch = styled('div')<{ bg: string }>`
+  width: 28px;
+  height: 28px;
+  border-radius: 14px;
+  background-color: ${props => props.bg};
+  margin-right: 10px;
+`;

+ 9 - 7
components/ColorSelector/index.tsx

@@ -1,8 +1,8 @@
 import React from 'react';
-import { ChromePicker } from 'react-color';
 
 import Icon from '../Icon';
 import Typography from '../Typography';
+import ColorPicker from '../ColorPicker';
 
 import {
   Group,
@@ -20,7 +20,7 @@ type Props = {
   onClick: (color: string) => void;
   mode?: 'normal' | 'shape' | 'watermark';
   isCollapse: boolean;
-  pickerToggle: (e: React.MouseEvent<HTMLElement>) => void;
+  pickerToggle: (e?: React.MouseEvent<HTMLElement>) => void;
 };
 
 type ColorItem = {
@@ -66,17 +66,19 @@ const ColorSelector: React.FC<Props> = ({
             )}
           </Item>
         ))}
-        <Item onMouseDown={pickerToggle}>
+        <Item
+          onMouseDown={pickerToggle}
+          selected={!colorList.find(ele => selectedColor === ele.color)}
+        >
           <Icon glyph="color-picker" />
         </Item>
         {!isCollapse ? (
           <PickerContainer>
             <Blanket onMouseDown={pickerToggle} />
-            <ChromePicker
+            <ColorPicker
               color={selectedColor}
-              disableAlpha
-              onChange={(color: any, event: any): void => {
-                event.preventDefault();
+              onSubmit={(color: any): void => {
+                pickerToggle();
                 onClick(color.hex);
               }}
             />

+ 3 - 5
components/Highlight/index.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-import AnnotationSelector from '../AnnotationSelector';
+import AnnotationOptions from '../AnnotationOptions';
 import Markup from '../Markup';
 import { rectCalc } from '../../helpers/position';
 import { strip } from '../../helpers/utility';
@@ -42,16 +42,14 @@ const Highlight: React.SFC<AnnotationElementPropsType> = ({
       })}
     {!isCollapse ? (
       <Popper position={mousePosition}>
-        <AnnotationSelector
+        <AnnotationOptions
           onUpdate={onUpdate}
           onDelete={onDelete}
           colorProps={bdcolor}
           opacityProps={transparency ? strip(transparency * 100) : 0}
         />
       </Popper>
-    ) : (
-      ''
-    )}
+    ) : null}
   </>
 );
 

+ 11 - 23
components/Toolbar/index.tsx

@@ -15,13 +15,16 @@ type Props = {
   currentPage: number;
   setCurrentPage: (num: number) => void;
   changeScale: (num: number | string) => void;
-  changeRotate: (num: number) => void;
+  handleClockwiseRotation: () => void;
+  handleCounterclockwiseRotation: () => void;
+  handleZoomIn: () => void;
+  handleZoomOut: () => void;
   scale: number;
-  rotation: number;
   viewport: ViewportType;
   displayMode: string;
   toggleDisplayMode: (state: string) => void;
   handleHandClick: () => void;
+  hidden: boolean;
 };
 
 const Toolbar: React.FC<Props> = ({
@@ -29,27 +32,20 @@ const Toolbar: React.FC<Props> = ({
   currentPage,
   setCurrentPage,
   changeScale,
-  changeRotate,
+  handleClockwiseRotation,
+  handleCounterclockwiseRotation,
+  handleZoomIn,
+  handleZoomOut,
   scale,
-  rotation,
   viewport,
   displayMode,
   toggleDisplayMode,
   handleHandClick,
+  hidden,
 }: Props) => {
   const { t } = useTranslation('toolbar');
   const data = dataset(t);
 
-  const handleClockwiseRotation = (): void => {
-    const r = rotation + 90;
-    changeRotate(r);
-  };
-
-  const handleCounterclockwiseRotation = (): void => {
-    const r = rotation - 90;
-    changeRotate(r);
-  };
-
   const handleScaleSelect = async (
     selected: SelectOptionType
   ): Promise<any> => {
@@ -63,17 +59,9 @@ const Toolbar: React.FC<Props> = ({
     }
   };
 
-  const handleZoomIn = (): void => {
-    changeScale(scaleCheck(scale * 100 + 10));
-  };
-
-  const handleZoomOut = (): void => {
-    changeScale(scaleCheck(scale * 100 - 10));
-  };
-
   return (
     <>
-      <Container isHidden={displayMode === 'full'}>
+      <Container hidden={hidden} displayMode={displayMode}>
         <Pagination
           totalPage={totalPage}
           currentPage={currentPage}

+ 20 - 10
components/Toolbar/styled.ts

@@ -1,9 +1,23 @@
-import styled, { css } from 'styled-components';
+import styled, { css, keyframes } from 'styled-components';
 
-export const Container = styled('div')<{ isHidden: boolean }>`
+const closeToolbar = keyframes`
+  from {
+    pointer-events: auto;
+    opacity: 1;
+  }
+  to {
+    pointer-events: none;
+    opacity: 0;
+  }
+`;
+
+export const Container = styled('div')<{
+  displayMode: string;
+  hidden: boolean;
+}>`
   position: fixed;
   top: 86px;
-  left: 267px;
+  left: ${props => (props.displayMode === 'normal' ? '267px' : 0)};
   right: 0;
   margin: auto;
   max-width: 572px;
@@ -21,15 +35,11 @@ export const Container = styled('div')<{ isHidden: boolean }>`
   transition: all 225ms ease-in-out;
 
   ${props =>
-    props.isHidden
+    props.hidden
       ? css`
-          opacity: 0;
-          visibility: hidden;
+          animation: ${closeToolbar} 3s forwards;
         `
-      : css`
-          opacity: 1;
-          visibility: visible;
-        `}
+      : ''}
 `;
 
 export const ToggleButton = styled.div`

+ 34 - 24
containers/Annotation.tsx

@@ -9,6 +9,7 @@ import Shape from '../components/Shape';
 import Line from '../components/Line';
 import DeleteDialog from '../components/DeleteDialog';
 import useCursorPosition from '../hooks/useCursorPosition';
+import ClickAwayListener from '../components/ClickAwayListener';
 
 import useStore from '../store';
 import useActions from '../actions';
@@ -103,6 +104,14 @@ const Annotation: React.FC<Props> = ({
     }
   }, [obj_type, obj_attr]);
 
+  useEffect(() => {
+    return () => {
+      handleMouseOut();
+      handleBlur();
+      setEdit(false);
+    };
+  }, []);
+
   const childProps = {
     id,
     obj_type,
@@ -120,7 +129,6 @@ const Annotation: React.FC<Props> = ({
   };
 
   const wrapperProps = {
-    onBlur: handleBlur,
     onMouseDown: handleClick,
     onMouseOver: handleMouseOver,
     onMouseOut: handleMouseOut,
@@ -128,29 +136,31 @@ const Annotation: React.FC<Props> = ({
   };
 
   return (
-    <AnnotationWrapper ref={ref} {...wrapperProps}>
-      {((): 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} />;
-        }
-      })()}
-      {openDialog && (
-        <DeleteDialog onCancel={deleteDialogToggle} onDelete={handleDelete} />
-      )}
-    </AnnotationWrapper>
+    <ClickAwayListener onClick={handleBlur}>
+      <AnnotationWrapper ref={ref} {...wrapperProps}>
+        {((): 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} />;
+          }
+        })()}
+        {openDialog && (
+          <DeleteDialog onCancel={deleteDialogToggle} onDelete={handleDelete} />
+        )}
+      </AnnotationWrapper>
+    </ClickAwayListener>
   );
 };
 

+ 3 - 4
containers/ColorSelector.tsx

@@ -18,16 +18,15 @@ const ColorSelector: React.FC<Props> = ({
   mode,
 }: Props) => {
   const [isCollapse, setCollapse] = useState(true);
-  const [{ sidebarState, markupToolState }] = useStore();
+  const [{ sidebarState }] = useStore();
 
-  const pickerToggle = (e: React.MouseEvent<HTMLElement>): void => {
-    e.preventDefault();
+  const pickerToggle = (): void => {
     setCollapse(!isCollapse);
   };
 
   useEffect(() => {
     setCollapse(true);
-  }, [markupToolState, sidebarState]);
+  }, [sidebarState]);
 
   return (
     <ColorSelectorComp

+ 8 - 1
containers/HighlightTools.tsx

@@ -40,11 +40,18 @@ const HighlightTools: React.FC<Props> = ({
   const selectRange = useCallback(
     (e: MouseEvent | TouchEvent): void => {
       if (e.target && isActive) {
-        const textLayer = (e.target as HTMLElement).parentNode as HTMLElement;
+        let textLayer = (e.target as HTMLElement).parentNode as HTMLElement;
+        if (textLayer.getAttribute('data-id') !== 'text-layer') {
+          textLayer = textLayer.querySelector(
+            '[data-id="text-layer"]'
+          ) as HTMLElement;
+        }
+
         if (textLayer) {
           const newMarkup = getMarkupWithSelection({
             ...data,
             scale,
+            textLayer,
           });
 
           if (newMarkup) {

+ 41 - 39
containers/MarkupTools.tsx

@@ -1,24 +1,33 @@
-import React, { useEffect } from 'react';
+import React from 'react';
 import { useTranslation } from 'react-i18next';
 
-import Button from '../components/Button';
-import ExpansionPanel from '../components/ExpansionPanel';
-import Icon from '../components/Icon';
+// import Button from '../components/Button';
+// import ExpansionPanel from '../components/ExpansionPanel';
+// import Icon from '../components/Icon';
 import HighlightTools from './HighlightTools';
 import FreehandTools from './FreehandTools';
 import TextTools from './FreeTextTools';
 import StickyNoteTools from './StickyNoteTools';
 import ShapeTools from './ShapeTools';
+import WatermarkTool from './WatermarkTool';
 
 import useActions from '../actions';
 import useStore from '../store';
 
 const MarkupTools: React.FC = () => {
   const { t } = useTranslation('sidebar');
-  const [{ sidebarState, markupToolState }, dispatch] = useStore();
-  const { setSidebar, setMarkupTool } = useActions(dispatch);
+  const [{ sidebarState }, dispatch] = useStore();
+  const { setSidebar } = useActions(dispatch);
 
-  const onClickSidebar = (state: string): void => {
+  // const onClickSidebar = (state: string): void => {
+  //   if (state === sidebarState) {
+  //     setSidebar('');
+  //   } else {
+  //     setSidebar(state);
+  //   }
+  // };
+
+  const onClickTool = (state: string): void => {
     if (state === sidebarState) {
       setSidebar('');
     } else {
@@ -26,71 +35,64 @@ const MarkupTools: React.FC = () => {
     }
   };
 
-  const onClickTool = (state: string): void => {
-    if (state === markupToolState) {
-      setMarkupTool('');
-    } else {
-      setMarkupTool(state);
-    }
-  };
-
-  useEffect(() => {
-    if (sidebarState !== 'markup-tools') {
-      setMarkupTool('');
-    }
-  }, []);
+  // useEffect(() => {
+  //   if (sidebarState !== 'markup-tools') {
+  //     setSidebar('');
+  //   }
+  // }, []);
 
-  const Label = (
-    <Button
-      shouldFitContainer
-      align="left"
-      onClick={(): void => {
-        onClickSidebar('markup-tools');
-      }}
-    >
-      <Icon glyph="markup-tools" style={{ marginRight: '10px' }} />
-      {t('markupTool')}
-    </Button>
-  );
+  // const Label = (
+  //   <Button
+  //     shouldFitContainer
+  //     align="left"
+  //     onClick={(): void => {
+  //       onClickSidebar('markup-tools');
+  //     }}
+  //   >
+  //     <Icon glyph="markup-tools" style={{ marginRight: '10px' }} />
+  //     {t('markupTool')}
+  //   </Button>
+  // );
 
   return (
-    <ExpansionPanel label={Label} isActive={sidebarState === 'markup-tools'}>
+    <>
       <HighlightTools
         title={t('annotate')}
-        isActive={markupToolState === 'highlight'}
+        isActive={sidebarState === 'highlight'}
         onClick={(): void => {
           onClickTool('highlight');
         }}
       />
       <FreehandTools
         title={t('freehand')}
-        isActive={markupToolState === 'freehand'}
+        isActive={sidebarState === 'freehand'}
         onClick={(): void => {
           onClickTool('freehand');
         }}
       />
       <TextTools
         title={t('textBox')}
-        isActive={markupToolState === 'text'}
+        isActive={sidebarState === 'text'}
         onClick={(): void => {
           onClickTool('text');
         }}
       />
       <StickyNoteTools
         title={t('stickyNote')}
-        isActive={markupToolState === 'sticky'}
+        isActive={sidebarState === 'sticky'}
         onClick={(): void => {
           onClickTool('sticky');
         }}
       />
       <ShapeTools
         title={t('shape')}
-        isActive={markupToolState === 'shape'}
+        isActive={sidebarState === 'shape'}
         onClick={(): void => {
           onClickTool('shape');
         }}
       />
-    </ExpansionPanel>
+      <WatermarkTool />
+    </>
   );
 };
 

+ 1 - 6
containers/PdfPage.tsx

@@ -27,12 +27,7 @@ const PdfPage: React.FC<Props> = ({ index, renderingState }: Props) => {
 
       if (page === pageNum) {
         result.push(
-          <Annotation
-            key={`annotations_${pageNum + i}`}
-            scale={scale}
-            index={i}
-            {...ele}
-          />
+          <Annotation key={ele.id} scale={scale} index={i} {...ele} />
         );
       }
     });

+ 0 - 2
containers/Sidebar.tsx

@@ -6,7 +6,6 @@ import Typography from '../components/Typography';
 // import Icon from '../components/Icon';
 // import CreateForm from './CreateForm';
 import MarkupTools from './MarkupTools';
-import WatermarkTool from './WatermarkTool';
 
 // import useActions from '../actions';
 import useStore from '../store';
@@ -44,7 +43,6 @@ const Sidebar: React.FC = () => {
           {t('addImages')}
         </Button>
       </BtnWrapper> */}
-      <WatermarkTool />
     </SidebarWrapper>
   );
 };

+ 77 - 6
containers/Toolbar.tsx

@@ -1,23 +1,27 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 
 import ToolbarComponent from '../components/Toolbar';
 
 import useActions from '../actions';
 import useStore from '../store';
 
-import { scrollIntoView } from '../helpers/utility';
+import { scrollIntoView, scaleCheck } from '../helpers/utility';
+import { delay } from '../helpers/time';
 
 const Toolbar: React.FC = () => {
   const [
     { totalPage, currentPage, scale, rotation, viewport, displayMode },
     dispatch,
   ] = useStore();
+  const [hideToolbar, setHideToolbar] = useState(false);
+  let scrollTimeout: any = null;
+
   const {
     setCurrentPage: setCurrentPageAction,
     changeScale,
     changeRotate,
     toggleDisplayMode,
-    setMarkupTool,
+    setSidebar,
   } = useActions(dispatch);
 
   const setCurrentPage = (num: number): void => {
@@ -28,25 +32,92 @@ const Toolbar: React.FC = () => {
       scrollIntoView(ele);
     }
     setCurrentPageAction(num);
+    setHideToolbar(false);
   };
 
   const handleHandClick = (): void => {
-    setMarkupTool('');
+    setSidebar('');
+  };
+
+  const timer = () => {
+    if (scrollTimeout > 0) {
+      clearTimeout(scrollTimeout);
+      setHideToolbar(false);
+    }
+
+    scrollTimeout = setTimeout(() => {
+      setHideToolbar(true);
+    }, 3000);
+  };
+
+  const handleScroll = () => {
+    setHideToolbar(false);
+  };
+
+  const handleClockwiseRotation = (): void => {
+    const r = rotation + 90;
+    changeRotate(r);
+    setHideToolbar(false);
+  };
+
+  const handleCounterclockwiseRotation = (): void => {
+    const r = rotation - 90;
+    changeRotate(r);
+    setHideToolbar(false);
   };
 
+  const handleZoomIn = (): void => {
+    changeScale(scaleCheck(scale * 100 + 10));
+    setHideToolbar(false);
+  };
+
+  const handleZoomOut = (): void => {
+    changeScale(scaleCheck(scale * 100 - 10));
+    setHideToolbar(false);
+  };
+
+  useEffect(() => {
+    const viewer = document.getElementById('pdf_viewer');
+
+    if (viewer) {
+      viewer.addEventListener('scroll', handleScroll);
+    }
+
+    return () => {
+      if (viewer) {
+        viewer.removeEventListener('scroll', handleScroll);
+      }
+    };
+  }, [viewport]);
+
+  useEffect(() => {
+    if (!hideToolbar) {
+      timer();
+    }
+  }, [hideToolbar]);
+
+  useEffect(() => {
+    delay(3000).then(() => {
+      setHideToolbar(true);
+    });
+  }, []);
+
   return (
     <ToolbarComponent
       totalPage={totalPage}
       currentPage={currentPage}
       setCurrentPage={setCurrentPage}
       changeScale={changeScale}
-      changeRotate={changeRotate}
+      handleClockwiseRotation={handleClockwiseRotation}
+      handleCounterclockwiseRotation={handleCounterclockwiseRotation}
+      handleZoomIn={handleZoomIn}
+      handleZoomOut={handleZoomOut}
       handleHandClick={handleHandClick}
       scale={scale}
-      rotation={rotation}
       viewport={viewport}
       displayMode={displayMode}
       toggleDisplayMode={toggleDisplayMode}
+      hidden={hideToolbar}
     />
   );
 };

+ 1 - 0
custom.d.ts

@@ -8,6 +8,7 @@ declare module 'pdfjs-dist/build/pdf.worker.entry';
 declare module 'query-string';
 declare module 'react-toast-notifications';
 declare module 'react-color';
+declare module 'react-color/lib/components/common';
 
 type SelectOptionType = {
   key: string | number;

+ 1 - 0
global/sidebarStyled.ts

@@ -19,6 +19,7 @@ export const Body = styled.div`
   padding: 8px;
   text-align: center;
   position: relative;
+  height: calc(100vh - 140px);
 `;
 
 export const Footer = styled.div`

+ 0 - 4
global/toolStyled.ts

@@ -64,10 +64,6 @@ export const PickerContainer = styled.div`
   display: flex;
   justify-content: center;
   align-items: center;
-
-  div[class^='chrome-picker'] {
-    width: 350px !important;
-  }
 `;
 
 export const Blanket = styled.div`

+ 1 - 1
helpers/annotation.ts

@@ -205,7 +205,7 @@ export const parseAnnotationObject = (
   pageHeight: number,
   scale: number
 ): AnnotationType => ({
-  id,
+  id: id || uuidv4(),
   obj_type,
   obj_attr: {
     page: page - 1,

+ 4 - 12
helpers/markup.ts

@@ -11,6 +11,7 @@ type GetMarkupWithSelectionFunc = ({
   type: string;
   opacity: number;
   scale: number;
+  textLayer: HTMLElement;
 }) => AnnotationType | null;
 
 export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
@@ -18,10 +19,11 @@ export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
   type,
   opacity,
   scale,
+  textLayer,
 }) => {
   const selection: Selection | null = window.getSelection();
 
-  if (!selection || !selection.rangeCount) return null;
+  if (!selection || selection.isCollapsed) return null;
 
   const {
     startContainer,
@@ -32,22 +34,12 @@ export const getMarkupWithSelection: GetMarkupWithSelectionFunc = ({
   const startElement = startContainer.parentNode as HTMLElement;
   const endElement = endContainer.parentNode as HTMLElement;
   const startPage = startElement?.parentNode?.parentNode as HTMLElement;
-  const endPage = endElement?.parentNode?.parentNode as HTMLElement;
   const startPageNum = parseInt(
     startPage.getAttribute('data-page-num') as string,
     10
   );
-  const endPageNum = parseInt(
-    endPage.getAttribute('data-page-num') as string,
-    10
-  );
-  const textLayer = startPage.querySelector(
-    '[data-id="text-layer"]'
-  ) as HTMLElement;
-  const pageHeight = startPage.offsetHeight;
 
-  if (startPageNum !== endPageNum) return null;
-  if (startOffset === endOffset && startOffset === endOffset) return null;
+  const pageHeight = startPage.offsetHeight;
 
   const startCloneEle = startElement.cloneNode(true) as HTMLElement;
   const endCloneEle = endElement.cloneNode(true) as HTMLElement;

+ 1 - 1
package.json

@@ -26,7 +26,7 @@
     "print-js": "^1.0.63",
     "query-string": "5",
     "react": "16.13.0",
-    "react-color": "^2.18.0",
+    "react-color": "2.17.3",
     "react-dom": "16.13.0",
     "react-i18next": "^11.3.4",
     "react-popper": "^1.3.6",

+ 1 - 0
reducers/middleware/index.ts

@@ -1,4 +1,5 @@
 import { Dispatch } from 'react';
+
 import {
   CHANGE_SCALE,
   TOGGLE_DISPLAY_MODE,

+ 11 - 2
store/initialMainState.ts

@@ -1,7 +1,16 @@
 export type StateType = {
   displayMode: 'full' | 'normal';
   navbarState: 'search' | 'annotations' | 'thumbnails' | '';
-  sidebarState: 'markup-tools' | 'create-form' | 'watermark' | '';
+  sidebarState:
+    | 'markup-tools'
+    | 'create-form'
+    | 'watermark'
+    | 'highlight'
+    | 'freehand'
+    | 'text'
+    | 'sticky'
+    | 'shape'
+    | '';
   markupToolState: 'highlight' | 'freehand' | 'text' | 'sticky' | 'shape' | '';
   info: { token: string; id: string };
   initiated: boolean;
@@ -11,7 +20,7 @@ export type StateType = {
 export default {
   displayMode: 'full',
   navbarState: '',
-  sidebarState: '',
+  sidebarState: 'highlight',
   markupToolState: '',
   info: {},
   initiated: false,

+ 4 - 4
yarn.lock

@@ -7334,10 +7334,10 @@ raw-body@2.4.0:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
-react-color@^2.18.0:
-  version "2.18.1"
-  resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.18.1.tgz#2cda8cc8e06a9e2c52ad391a30ddad31972472f4"
-  integrity sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ==
+react-color@2.17.3:
+  version "2.17.3"
+  resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.17.3.tgz#b8556d744f95193468c7061d2aa19180118d4a48"
+  integrity sha512-1dtO8LqAVotPIChlmo6kLtFS1FP89ll8/OiA8EcFRDR+ntcK+0ukJgByuIQHRtzvigf26dV5HklnxDIvhON9VQ==
   dependencies:
     "@icons/material" "^0.2.4"
     lodash "^4.17.11"