Parcourir la source

[update] components

RoyLiu il y a 5 ans
Parent
commit
18c76c2414

+ 57 - 0
components/Annotations/index.tsx

@@ -0,0 +1,57 @@
+import React from 'react';
+
+import Icon from '../Icon';
+import Drawer from '../Drawer';
+import Typography from '../Typography';
+import Divider from '../Divider';
+
+import { AnnotationBox, PageNumber, Content, Info } from './styled';
+import { Separator } from '../../global/otherStyled';
+import { Wrapper, Head, Body, IconWrapper } from '../../global/sidebarStyled'
+
+type Props = {
+  isActive: boolean;
+};
+
+const Annotations: React.FunctionComponent<Props> = ({
+  isActive,
+}) => {
+  return (
+    <Drawer anchor="right" open={isActive}>
+      <Wrapper>
+        <Head>
+          <IconWrapper>
+            <Icon glyph="right-back" />
+          </IconWrapper>
+          <Separator />
+          <IconWrapper>
+            <Icon glyph="sort" />
+          </IconWrapper>
+          <IconWrapper>
+            <Icon glyph="annotation-export" />
+          </IconWrapper>
+          <IconWrapper>
+            <Icon glyph="import" />
+          </IconWrapper>
+        </Head>
+        <Body>
+          <Typography light>2 Annotations</Typography>
+          <Divider orientation="horizontal" />
+          <PageNumber>Page 1</PageNumber>
+          <AnnotationBox>
+            <Content>
+              If the Photographer fails to appear at the place and time specified above, the deposit shall be refunded to the Client.
+            </Content>
+            <Info>
+              Gameboy
+              <Separator />
+              2016/07/28 18:18
+            </Info>
+          </AnnotationBox>
+        </Body>
+      </Wrapper>
+    </Drawer>
+  );
+};
+
+export default Annotations;

+ 48 - 0
components/Annotations/styled.ts

@@ -0,0 +1,48 @@
+import styled from 'styled-components';
+
+import { color } from '../../constants/style';
+
+export const Wrapper = styled.div`
+  margin-top: 60px;
+  padding: 24px 8px;
+`;
+
+export const Head = styled.div`
+  display: flex;
+`;
+
+export const Body = styled.div`
+  padding: 8px;
+`;
+
+export const IconWrapper = styled.span`
+  display: inline-block;
+  padding: 8px;
+`;
+
+export const PageNumber = styled.div`
+  font-size: 12px;
+  font-weight: bold;
+  color: ${color.primary};
+  text-align: right;
+  margin-bottom: 5px;
+`;
+
+export const AnnotationBox = styled.div`
+  border-radius: 4px;
+  border: solid 1px ${color.black38};
+  padding: 12px 12px 8px;
+  width: 235px;
+`;
+
+export const Content = styled.div`
+  background-color: ${color['lemon-yellow']};
+`;
+
+export const Info = styled.div`
+  margin-top: 8px;
+  display: flex;
+  color: ${color.black38};
+  font-size: 12px;
+  font-weight: bold;
+`;

+ 1 - 1
components/Button/index.tsx

@@ -6,7 +6,7 @@ import {
 } from './styled';
 
 export type Props = {
-  appearance?: 'default' | 'primary' | 'primary-hollow' | 'default-hollow';
+  appearance?: 'default' | 'primary' | 'primary-hollow' | 'default-hollow' | 'link';
   id?: string;
   isDisabled?: boolean;
   onClick?: () => void;

+ 13 - 1
components/Button/styled.ts

@@ -70,6 +70,18 @@ const theme: {[index: string]: any} = {
       background-color: ${color.black38};
     }
   `,
+  link: css`
+    color: ${color.primary};
+    background-color: transparent;
+    border: none;
+    text-decoration: underline;
+    padding: 5px 10px;
+
+    &:hover {
+    }
+    &:focus, &:active {
+    }
+  `,
 };
 
 export const NormalButton = styled('button')<{appearance: string, shouldFitContainer: boolean, align: string}>`
@@ -79,7 +91,7 @@ export const NormalButton = styled('button')<{appearance: string, shouldFitConta
     width: 100%;
   ` : null}
 
-  display: flex;
+  display: inline-flex;
   justify-content: ${props => align[props.align!]};
   align-items: center;
 `;

+ 4 - 0
components/ColorSelect/data.ts

@@ -1,14 +1,18 @@
 export default [
   {
+    key: 'lemon-yellow',
     color: 'lemon-yellow',
   },
   {
+    key: 'strong-pink',
     color: 'strong-pink',
   },
   {
+    key: 'neon-green',
     color: 'neon-green',
   },
   {
+    key: 'light-blue',
     color: 'light-blue',
   },
 ];

+ 1 - 1
components/ColorSelect/index.tsx

@@ -13,7 +13,7 @@ const ColorSelect = () => {
       <Typography variant="subtitle" style={{marginTop: '8px'}}>Color</Typography>
       <Group>
         {data.map((ele) => (
-          <Item>
+          <Item key={ele.key}>
             <Circle color={ele.color} />
           </Item>
         ))}

+ 50 - 0
components/CreateForm/index.tsx

@@ -0,0 +1,50 @@
+import React from 'react';
+
+import Button from '../Button';
+import Icon from '../Icon';
+import ExpansionPanel from '../ExpansionPanel';
+
+import { BtnWrapper } from '../../global/toolStyled';
+
+type Props = {
+  sidebarState: string;
+  onClick: (state: string) => void;
+};
+
+const CreateForm: React.FunctionComponent<Props> = ({
+  onClick,
+  sidebarState,
+}) => {
+  return (
+    <ExpansionPanel
+      label={
+        <Button shouldFitContainer align="left" onClick={() => { onClick('create-form'); }}>
+          <Icon glyph="create-form" style={{marginRight: '10px'}}/>
+          Create Form
+        </Button>
+      }
+      isActive={sidebarState === 'create-form'}
+    >
+      <BtnWrapper>
+        <Button shouldFitContainer align="left">
+          <Icon glyph="text-field" style={{marginRight: '10px'}}/>
+          Text Field
+        </Button>
+      </BtnWrapper>
+      <BtnWrapper>
+        <Button shouldFitContainer align="left">
+          <Icon glyph="checkbox" style={{marginRight: '10px'}}/>
+          Check box
+        </Button>
+      </BtnWrapper>
+      <BtnWrapper>
+        <Button shouldFitContainer align="left">
+          <Icon glyph="radio-button" style={{marginRight: '10px'}}/>
+          Radio Button
+        </Button>
+      </BtnWrapper>
+    </ExpansionPanel>
+  );
+};
+
+export default CreateForm;

+ 2 - 0
components/Divider/index.tsx

@@ -7,6 +7,7 @@ export type Props = {
   orientation?: 'vertical' | 'horizontal';
   variant?: 'fullWidth' | 'inset' | 'middle';
   light?: boolean;
+  style?: Object;
 }
 
 const Divider: React.FunctionComponent<Props> = (props) => {
@@ -22,6 +23,7 @@ Divider.defaultProps = {
   orientation: 'vertical',
   variant: 'fullWidth',
   light: false,
+  style: {},
 };
 
 export default Divider;

+ 7 - 1
components/ExpansionPanel/index.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
 
 import {
   Wrapper, Label, ContentWrapper, Content,
@@ -7,11 +7,13 @@ import {
 type Props = {
   label: React.ReactNode;
   children?: React.ReactNode;
+  isActive?: boolean;
 };
 
 const Collapse: React.FunctionComponent<Props> = ({
   label,
   children = '',
+  isActive = false,
 }) => {
   const [isCollapse, setCollapse] = useState(true);
 
@@ -19,6 +21,10 @@ const Collapse: React.FunctionComponent<Props> = ({
     setCollapse(!isCollapse);
   };
 
+  useEffect(() => {
+    setCollapse(!isActive);
+  }, [isActive]);
+
   return (
     <Wrapper>
       <Label onClick={handleClick}>

+ 4 - 14
components/HighlightTools/data.ts

@@ -1,30 +1,20 @@
 export default {
   lineType: [
     {
+      key: 'highlight',
       icon: 'highlight',
     },
     {
+      key: 'underline',
       icon: 'underline',
     },
     {
+      key: 'squiggly',
       icon: 'squiggly',
     },
     {
+      key: 'strikeout',
       icon: 'strikeout',
     },
   ],
-  colorItems: [
-    {
-      color: 'lemon-yellow',
-    },
-    {
-      color: 'strong-pink',
-    },
-    {
-      color: 'neon-green',
-    },
-    {
-      color: 'light-blue',
-    },
-  ],
 };

+ 1 - 1
components/HighlightTools/index.tsx

@@ -23,7 +23,7 @@ const Highlight = () => {
       <Typography variant="subtitle" style={{marginTop: '8px'}}>Style</Typography>
       <Group>
         {data.lineType.map((ele) => (
-          <Item>
+          <Item key={ele.key}>
             <Icon glyph={ele.icon} />
           </Item>
         ))}

+ 93 - 57
components/Icon/data.ts

@@ -1,63 +1,72 @@
 // @ts-ignore
 import React from 'react';
 
-import Close from '../../public/static/icons/close.svg';
-import DownArrow from '../../public/static/icons/arrow00.svg';
-import Edit from '../../public/static/icons/navbar/rename00.svg';
-import EditActive from '../../public/static/icons/navbar/rename01.svg';
-import Search from '../../public/static/icons/navbar/Search00.svg';
-import SearchActive from '../../public/static/icons/navbar/Search01.svg';
-import Annotation from '../../public/static/icons/navbar/MyAnnotation00.svg';
-import AnnotationActive from '../../public/static/icons/navbar/MyAnnotation01.svg';
-import Thumbnail from '../../public/static/icons/navbar/Thumbnail00.svg';
-import ThumbnailActive from '../../public/static/icons/navbar/Thumbnail01.svg';
-import Print from '../../public/static/icons/navbar/Print00.svg';
-import PrintActive from '../../public/static/icons/navbar/Print01.svg';
-import Export from '../../public/static/icons/navbar/Export00.svg';
-import ExportActive from '../../public/static/icons/navbar/Export01.svg';
-import ZoomIn from '../../public/static/icons/toolbar/zoom00.svg';
-import ZoomInActive from '../../public/static/icons/toolbar/zoom01.svg';
-import ZoomOut from '../../public/static/icons/toolbar/zoomOut00.svg';
-import ZoomOutActive from '../../public/static/icons/toolbar/zoomOut01.svg';
-import Hand from '../../public/static/icons/toolbar/hand00.svg';
-import HandActive from '../../public/static/icons/toolbar/hand01.svg';
-import RotateLeft from '../../public/static/icons/toolbar/RotateLeft00.svg';
-import RotateLeftActive from '../../public/static/icons/toolbar/RotateLeft01.svg';
-import RotateRight from '../../public/static/icons/toolbar/RotateRight00.svg';
-import RotateRightActive from '../../public/static/icons/toolbar/RotateRight01.svg';
-import MarkupTools from '../../public/static/icons/sidebar/Markuptools00.svg';
-import Highlight from '../../public/static/icons/sidebar/Highlight.svg';
-import Freehand from '../../public/static/icons/sidebar/Freehand.svg';
-import Markpen from '../../public/static/icons/sidebar/markpen.svg';
-import Text from '../../public/static/icons/sidebar/Text.svg';
-import Shape from '../../public/static/icons/sidebar/Shape.svg';
-import CreateForm from '../../public/static/icons/sidebar/CreatForm00.svg';
-import AddImage from '../../public/static/icons/sidebar/AddImage00.svg';
-import Watermark from '../../public/static/icons/sidebar/Watermark00.svg';
-import Underline from '../../public/static/icons/sidebar/Underline.svg';
-import Squiggly from '../../public/static/icons/sidebar/Squiggly.svg';
-import Strikeout from '../../public/static/icons/sidebar/Strikeout.svg';
-import ColorPicker from '../../public/static/icons/sidebar/Colorpicker.svg';
-import Eraser from '../../public/static/icons/sidebar/eraser00.svg';
-import EraserActive from '../../public/static/icons/sidebar/eraser01.svg';
-import Undo from '../../public/static/icons/sidebar/undo.svg';
-import Redo from '../../public/static/icons/sidebar/redo.svg';
-import Bold from '../../public/static/icons/sidebar/Bold00.svg';
-import BoldActive from '../../public/static/icons/sidebar/Bold01.svg';
-import Italic from '../../public/static/icons/sidebar/Italic00.svg';
-import ItalicActive from '../../public/static/icons/sidebar/Italic01.svg';
-import AlignCenter from '../../public/static/icons/sidebar/Aligncenter00.svg';
-import AlignCenterActive from '../../public/static/icons/sidebar/Aligncenter01.svg';
-import AlignLeft from '../../public/static/icons/sidebar/Alignleft00.svg';
-import AlignLeftActive from '../../public/static/icons/sidebar/Alignleft01.svg';
-import AlignRight from '../../public/static/icons/sidebar/Alignright00.svg';
-import AlignRightActive from '../../public/static/icons/sidebar/Alignright01.svg';
-import Circle from '../../public/static/icons/sidebar/Circle.svg';
-import Rectangle from '../../public/static/icons/sidebar/Rectangle.svg';
-import Line from '../../public/static/icons/sidebar/Line.svg';
-import Arrow from '../../public/static/icons/sidebar/Arrow.svg';
-import Border from '../../public/static/icons/sidebar/Border.svg';
-import Fill from '../../public/static/icons/sidebar/Fill.svg';
+import Close from '../../static/icons/close.svg';
+import DownArrow from '../../static/icons/arrow00.svg';
+import Edit from '../../static/icons/navbar/rename00.svg';
+import EditActive from '../../static/icons/navbar/rename01.svg';
+import Search from '../../static/icons/navbar/Search00.svg';
+import SearchActive from '../../static/icons/navbar/Search01.svg';
+import Annotation from '../../static/icons/navbar/MyAnnotation00.svg';
+import AnnotationActive from '../../static/icons/navbar/MyAnnotation01.svg';
+import Thumbnail from '../../static/icons/navbar/Thumbnail00.svg';
+import ThumbnailActive from '../../static/icons/navbar/Thumbnail01.svg';
+import Print from '../../static/icons/navbar/Print00.svg';
+import PrintActive from '../../static/icons/navbar/Print01.svg';
+import Export from '../../static/icons/navbar/Export00.svg';
+import ExportActive from '../../static/icons/navbar/Export01.svg';
+import ZoomIn from '../../static/icons/toolbar/zoom00.svg';
+import ZoomInActive from '../../static/icons/toolbar/zoom01.svg';
+import ZoomOut from '../../static/icons/toolbar/zoomOut00.svg';
+import ZoomOutActive from '../../static/icons/toolbar/zoomOut01.svg';
+import Hand from '../../static/icons/toolbar/hand00.svg';
+import HandActive from '../../static/icons/toolbar/hand01.svg';
+import RotateLeft from '../../static/icons/toolbar/RotateLeft00.svg';
+import RotateLeftActive from '../../static/icons/toolbar/RotateLeft01.svg';
+import RotateRight from '../../static/icons/toolbar/RotateRight00.svg';
+import RotateRightActive from '../../static/icons/toolbar/RotateRight01.svg';
+import MarkupTools from '../../static/icons/sidebar/Markuptools00.svg';
+import Highlight from '../../static/icons/sidebar/Highlight.svg';
+import Freehand from '../../static/icons/sidebar/Freehand.svg';
+import Markpen from '../../static/icons/sidebar/markpen.svg';
+import Text from '../../static/icons/sidebar/Text.svg';
+import StickyNote from '../../static/icons/sidebar/StickyNote.svg';
+import Shape from '../../static/icons/sidebar/Shape.svg';
+import CreateForm from '../../static/icons/sidebar/CreatForm00.svg';
+import AddImage from '../../static/icons/sidebar/AddImage00.svg';
+import Watermark from '../../static/icons/sidebar/Watermark00.svg';
+import Underline from '../../static/icons/sidebar/Underline.svg';
+import Squiggly from '../../static/icons/sidebar/Squiggly.svg';
+import Strikeout from '../../static/icons/sidebar/Strikeout.svg';
+import ColorPicker from '../../static/icons/sidebar/Colorpicker.svg';
+import Eraser from '../../static/icons/sidebar/eraser00.svg';
+import EraserActive from '../../static/icons/sidebar/eraser01.svg';
+import Undo from '../../static/icons/sidebar/undo.svg';
+import Redo from '../../static/icons/sidebar/redo.svg';
+import Bold from '../../static/icons/sidebar/Bold00.svg';
+import BoldActive from '../../static/icons/sidebar/Bold01.svg';
+import Italic from '../../static/icons/sidebar/Italic00.svg';
+import ItalicActive from '../../static/icons/sidebar/Italic01.svg';
+import AlignCenter from '../../static/icons/sidebar/Aligncenter00.svg';
+import AlignCenterActive from '../../static/icons/sidebar/Aligncenter01.svg';
+import AlignLeft from '../../static/icons/sidebar/Alignleft00.svg';
+import AlignLeftActive from '../../static/icons/sidebar/Alignleft01.svg';
+import AlignRight from '../../static/icons/sidebar/Alignright00.svg';
+import AlignRightActive from '../../static/icons/sidebar/Alignright01.svg';
+import Circle from '../../static/icons/sidebar/Circle.svg';
+import Rectangle from '../../static/icons/sidebar/Rectangle.svg';
+import Line from '../../static/icons/sidebar/Line.svg';
+import Arrow from '../../static/icons/sidebar/Arrow.svg';
+import Border from '../../static/icons/sidebar/Border.svg';
+import Fill from '../../static/icons/sidebar/Fill.svg';
+import TextField from '../../static/icons/sidebar/Textfiled.svg';
+import Checkbox from '../../static/icons/sidebar/checkBox.svg';
+import RadioButton from '../../static/icons/sidebar/radiobutton.svg';
+import LeftBack from '../../static/icons/sidebar/LeftBack.svg';
+import RightBack from '../../static/icons/sidebar/RightBack.svg';
+import Sort from '../../static/icons/annotation/sort.svg';
+import Import from '../../static/icons/annotation/import.svg';
+import AnnotationExport from '../../static/icons/annotation/Export.svg';
 
 const data : {[index: string]: any} = {
   close: {
@@ -122,6 +131,9 @@ const data : {[index: string]: any} = {
   text: {
     Normal: Text,
   },
+  'sticky-note': {
+    Normal: StickyNote,
+  },
   shape: {
     Normal: Shape,
   },
@@ -197,6 +209,30 @@ const data : {[index: string]: any} = {
   fill: {
     Normal: Fill,
   },
+  'text-field': {
+    Normal: TextField,
+  },
+  checkbox: {
+    Normal: Checkbox,
+  },
+  'radio-button': {
+    Normal: RadioButton,
+  },
+  'right-back': {
+    Normal: RightBack,
+  },
+  'left-back': {
+    Normal: LeftBack,
+  },
+  sort: {
+    Normal: Sort,
+  },
+  import: {
+    Normal: Import,
+  },
+  'annotation-export': {
+    Normal: AnnotationExport,
+  },
 };
 
 export default data;

+ 3 - 0
components/Icon/index.tsx

@@ -12,6 +12,7 @@ type Props = {
   onClick?: () => void;
   onBlur?: () => void;
   style?: {};
+  isActive? : boolean;
 };
 
 const Icon : React.FunctionComponent<Props> = ({
@@ -20,6 +21,7 @@ const Icon : React.FunctionComponent<Props> = ({
   onClick = () => {},
   onBlur = () => {},
   style,
+  isActive = false,
 }) => {
   const {
     Normal,
@@ -33,6 +35,7 @@ const Icon : React.FunctionComponent<Props> = ({
     >
       {Normal && <Normal data-status="normal" />}
       {Hover && <Hover data-status="hover" />}
+      {isActive && Hover ? <Hover data-status="active" /> : null}
       <ClickZone
         id={id}
         tabIndex={0}

+ 9 - 0
components/Icon/styled.ts

@@ -23,6 +23,15 @@ export const IconWrapper = styled('div')<{isHover: boolean}>`
     right: 0;
     margin: auto;
   }
+  [data-status='active'] {
+    transition-delay: 100ms;
+    opacity: 1;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    margin: auto;
+  }
 
   ${props => props.isHover ? css`
     :hover {

+ 46 - 0
components/MarkupTools/index.tsx

@@ -0,0 +1,46 @@
+import React from 'react';
+
+import Button from '../Button';
+import ExpansionPanel from '../ExpansionPanel';
+import Icon from '../Icon';
+import HighlightTools from '../HighlightTools';
+import Freehand from '../Freehand';
+import TextTools from '../TextTools';
+import Shape from '../Shape';
+
+import { BtnWrapper } from '../../global/toolStyled';
+
+type Props = {
+  sidebarState: string;
+  onClick: (state: string) => void;
+};
+
+const MarkupTools: React.FunctionComponent<Props> = ({
+  onClick,
+  sidebarState,
+}) => {
+  return (
+    <ExpansionPanel
+      label={
+        <Button shouldFitContainer align="left" onClick={() => { onClick('markup-tools'); }}>
+          <Icon glyph="markup-tools" style={{marginRight: '10px'}}/>
+          Markup Tools
+        </Button>
+      }
+      isActive={sidebarState === 'markup-tools'}
+    >
+      <HighlightTools />
+      <Freehand />
+      <TextTools />
+      <BtnWrapper>
+        <Button shouldFitContainer align="left">
+          <Icon glyph="sticky-note" style={{marginRight: '10px'}}/>
+          Sticky Note
+        </Button>
+      </BtnWrapper>
+      <Shape />
+    </ExpansionPanel>
+  );
+};
+
+export default MarkupTools;

+ 24 - 0
components/Navbar/data.ts

@@ -0,0 +1,24 @@
+export default {
+  btnGroup: [
+    {
+      key: 'nav-search',
+      content: 'search',
+    },
+    {
+      key: 'nav-annotation',
+      content: 'annotation',
+    },
+    {
+      key: 'nav-thumbnail',
+      content: 'thumbnail',
+    },
+    {
+      key: 'nav-print',
+      content: 'print',
+    },
+    {
+      key: 'nav-export',
+      content: 'export',
+    },
+  ],
+};

+ 23 - 8
components/Navbar/index.tsx

@@ -2,26 +2,41 @@ import React from 'react';
 
 import Icon from '../Icon';
 import Typography from '../Typography';
+import Search from '../Search';
+import Annotations from '../Annotations';
+import Thumbnails from '../Thumbnails';
 
-import { Wrapper, Separator } from './styled';
+import { Wrapper } from './styled';
+import { Separator } from '../../global/otherStyled';
+import data from './data';
 
 type Props = {
-
+  onClick: (state: string) => void;
+  navbarState: string;
 };
 
 const Navbar: React.FunctionComponent<Props> = ({
-
+  onClick,
+  navbarState,
 }) => {
   return (
     <Wrapper>
       <Typography variant="title">Title</Typography>
       <Icon glyph="edit" />
       <Separator />
-      <Icon glyph="search" />
-      <Icon glyph="annotation" />
-      <Icon glyph="thumbnail" />
-      <Icon glyph="print" />
-      <Icon glyph="export" />
+      {
+        data.btnGroup.map((ele) => (
+          <Icon
+            key={ele.key}
+            glyph={ele.content}
+            isActive={navbarState === ele.content}
+            onClick={() => { onClick(ele.content); }}
+          />
+        ))
+      }
+      <Search isActive={navbarState === 'search'} close={() => { onClick(''); }} />
+      <Annotations isActive={navbarState === 'annotation'} />
+      <Thumbnails isActive={navbarState === 'thumbnail'} />
     </Wrapper>
   );
 };

+ 1 - 4
components/Navbar/styled.ts

@@ -11,8 +11,5 @@ export const Wrapper = styled.div`
   align-items: center;
   padding: 0 24px;
   background-color: white;
-`;
-
-export const Separator = styled.div`
-  flex: 1 1 auto;
+  z-index: 1;
 `;

+ 4 - 4
components/Pagination/index.tsx

@@ -1,6 +1,6 @@
 import React, { useState, useRef } from 'react';
 
-import { Wrapper, Text, Input, Button } from './styled';
+import { Wrapper, Text, Input, ArrowButton } from './styled';
 
 type Props = {
   defaultPage?: number;
@@ -45,8 +45,8 @@ const Pagination: React.FunctionComponent<Props> = ({
 
   return (
     <Wrapper>
-      <Text>page</Text>
-      <Button onClick={handleLeftClick} variant="left" />
+      <Text>Page</Text>
+      <ArrowButton onClick={handleLeftClick} variant="left" />
       <Input
         type="tel"
         ref={inputRef}
@@ -54,7 +54,7 @@ const Pagination: React.FunctionComponent<Props> = ({
         onKeyDown={handleKeyDown}
         value={currentPage}
       />
-      <Button onClick={handleRightClick} variant="right" />
+      <ArrowButton onClick={handleRightClick} variant="right" />
       <Text>of {total}</Text>
     </Wrapper>
   );

+ 2 - 2
components/Pagination/styled.ts

@@ -1,4 +1,4 @@
-import styled, { css } from 'styled-components';
+import styled, { css }from 'styled-components';
 
 import { color } from '../../constants/style';
 
@@ -23,7 +23,7 @@ export const Input = styled.input`
   padding: 5px;
 `;
 
-export const Button = styled('button')<{variant: string}>`
+export const ArrowButton = styled('button')<{variant: string}>`
   height: 30px;
   width: 26px;
   outline: none;

+ 30 - 8
components/Pdf.tsx

@@ -1,9 +1,12 @@
 import React, { useEffect, useState, useRef } from 'react';
 // @ts-ignore
-import pdfjs from 'pdfjs-dist';
+import pdfjs, { AnnotationLayer } from 'pdfjs-dist';
 // @ts-ignore
 import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
 
+const CMAP_URL = '../../node_modules/pdfjs-dist/cmaps/';
+const CMAP_PACKED = true;
+
 pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;
 
 type Props = {
@@ -19,16 +22,22 @@ const PdfComponent: React.FunctionComponent<Props> = ({
   src,
 }) => {
   const canvasRef = useRef<HTMLCanvasElement>(null);
+  const annotationRef = useRef<HTMLDivElement>(null);
   const [viewportState, setViewport] = useState({width: 0, height: 0});
 
   const fetchPdf = async () => {
-    const loadingTask = pdfjs.getDocument(src);
-    const pdf = await loadingTask.promise;
+    const loadingTask = pdfjs.getDocument({
+      url: src,
+      cMapUrl: CMAP_URL,
+      cMapPacked: CMAP_PACKED,
+    });
 
     loadingTask.onProgress = (progress: PropsProgress) => {
       console.log(progress);
     };
 
+    const pdf = await loadingTask.promise;
+
     const totalNum = pdf.numPages;
     console.log(totalNum);
 
@@ -56,6 +65,16 @@ const PdfComponent: React.FunctionComponent<Props> = ({
     const renderTask = page.render(renderContext);
 
     await renderTask.promise;
+
+    page.getAnnotations({ intent: 'display' }).then((annotations : []) => {
+      console.log(annotations);
+      AnnotationLayer.update({
+        viewport: viewport.clone({ dontFlip: true, }),
+        annotations,
+        div: annotationRef.current,
+        page,
+      });
+    })
   };
 
   useEffect(() => {
@@ -63,11 +82,14 @@ const PdfComponent: React.FunctionComponent<Props> = ({
   }, []);
 
   return (
-    <canvas
-      ref={canvasRef}
-      width={viewportState.width}
-      height={viewportState.height}
-    />
+    <>
+      <canvas
+        ref={canvasRef}
+        width={viewportState.width}
+        height={viewportState.height}
+      />
+      <div ref={annotationRef} />
+    </>
   );
 };
 

+ 32 - 0
components/Search/index.tsx

@@ -0,0 +1,32 @@
+import React from 'react';
+
+import Button from '../Button';
+import Portal from '../Portal';
+
+import { Wrapper, InputWrapper, Input, ResultInfo, ArrowButton } from './styled';
+
+type Props = {
+  isActive: boolean;
+  close: () => void;
+};
+
+const Search: React.FunctionComponent<Props> = ({
+  isActive,
+  close,
+}) => {
+  return (
+    <Portal>
+      <Wrapper open={isActive}>
+        <InputWrapper>
+          <Input />
+          <ResultInfo>2 of 14</ResultInfo>
+        </InputWrapper>
+        <ArrowButton variant="top" />
+        <ArrowButton variant="bottom" />
+        <Button appearance="primary" style={{marginLeft: '16px'}} onClick={close}>Close</Button>
+      </Wrapper>
+    </Portal>
+  );
+};
+
+export default Search;

+ 92 - 0
components/Search/styled.ts

@@ -0,0 +1,92 @@
+import styled, { css } from 'styled-components';
+
+import { color } from '../../constants/style';
+
+export const Wrapper = styled('div')<{open: boolean}>`
+  border-bottom-left-radius: 4px;
+  border-bottom-right-radius: 4px;
+  background-color: white;
+  position: fixed;
+  top: 60px;
+  right: 10px;
+  display: inline-flex;
+  padding: 9px 16px;
+  box-shadow: 0 4px 10px 0 rgba(0, 0, 0, 0.38);
+  z-index: 0;
+  transform: translateY(${props => props.open ? '0' : '-60px'});
+  transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;
+`;
+
+export const InputWrapper = styled.div`
+  width: 230px;
+  height: 32px;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+  overflow: hidden;
+  background-color: ${color["light-gray"]};
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px 14px;
+`;
+
+export const Input = styled.input`
+  background-color: ${color["light-gray"]};
+  outline: none;
+  border: none;
+  width: auto;
+  height: 32px;
+  box-sizing: border-box;
+  padding: 0;
+  flex: 1 2 auto;
+`;
+
+export const ResultInfo = styled.span`
+  display: block;
+  flex: 1 1 auto;
+`;
+
+export const ArrowButton = styled('button')<{variant: string}>`
+  height: 32px;
+  width: 30px;
+  outline: none;
+  border: none;
+  background-color: ${color["light-gray"]};
+  cursor: pointer;
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  ${props => props.variant === 'top' ? css`
+    margin-left: 1px;
+    :after {
+      content: '';
+      width: 0; 
+      height: 0; 
+      border-left: 5px solid transparent;
+      border-right: 5px solid transparent;
+      border-bottom: 5px solid black;
+      position: absolute;
+    }
+    :hover:after {
+      border-bottom: 5px solid ${color.black38};
+    }
+  ` : css`
+    border-top-right-radius: 4px;
+    border-bottom-right-radius: 4px;
+    margin-left: 1px;
+    :after {
+      content: '';
+      width: 0; 
+      height: 0; 
+      border-left: 5px solid transparent;
+      border-right: 5px solid transparent;
+      border-top: 5px solid black;
+      position: absolute;
+    }
+    :hover:after {
+      border-top: 5px solid ${color.black38};
+    }
+  `}
+`;

+ 0 - 57
components/Sidebar/index.tsx

@@ -1,57 +0,0 @@
-import React from 'react';
-
-import Button from '../Button';
-import ExpansionPanel from '../ExpansionPanel';
-import Typography from '../Typography';
-import Icon from '../Icon';
-import HighlightTools from '../HighlightTools';
-import Freehand from '../Freehand';
-import TextTools from '../TextTools';
-import Shape from '../Shape';
-
-import { Wrapper, BtnWrapper } from './styled';
-
-const Sidebar = () => {
-  return (
-    <Wrapper>
-      <Typography light style={{marginLeft: '30px', marginTop: '46px'}}>Main Menu</Typography>
-      <ExpansionPanel 
-        label={
-          <Button shouldFitContainer align="left">
-            <Icon glyph="markup-tools" style={{marginRight: '10px'}}/>
-            Markup Tools
-          </Button>
-        }
-      >
-        <HighlightTools />
-        <Freehand />
-        <TextTools />
-        <Button shouldFitContainer align="left">
-          <Icon glyph="create-form" style={{marginRight: '10px'}}/>
-          Sticky Note
-        </Button>
-        <Shape />
-      </ExpansionPanel>
-      <BtnWrapper>
-        <Button shouldFitContainer align="left">
-          <Icon glyph="create-form" style={{marginRight: '10px'}}/>
-          Create Form
-        </Button>
-      </BtnWrapper>
-      <BtnWrapper>
-        <Button shouldFitContainer align="left">
-          <Icon glyph="add-image" style={{marginRight: '10px'}}/>
-          Add Image
-        </Button>
-      </BtnWrapper>
-      <BtnWrapper>
-        <Button shouldFitContainer align="left">
-          <Icon glyph="watermark" style={{marginRight: '10px'}}/>
-          Watermark
-        </Button>
-      </BtnWrapper>
-    </Wrapper>
-  );
-};
-
-export default Sidebar;

+ 0 - 16
components/Sidebar/styled.ts

@@ -1,16 +0,0 @@
-import styled from 'styled-components';
-
-export const Wrapper = styled.div`
-  position: fixed;
-  top: 60px;
-  left: 0;
-  bottom: 0;
-  right: auto;
-  width: 267px;
-  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.24);
-  background-color: white;
-`;
-
-export const BtnWrapper = styled.div`
-  padding: 8px;
-`;

+ 44 - 0
components/Tabs/index.tsx

@@ -0,0 +1,44 @@
+import React, { useState } from 'react';
+
+import { Wrapper, BtnGroup, Btn } from './styled';
+
+type OptionProps = {
+  key: string;
+  content: React.ReactNode;
+  child: React.ReactNode;
+};
+
+type Props = {
+  options: OptionProps[];
+};
+
+const Tabs: React.FunctionComponent<Props> = ({
+  options,
+}) => {
+  const [selectedIndex, setSelect] = useState(0);
+
+  const handleClick = (index: number) => {
+    setSelect(index);
+  };
+
+  return (
+    <Wrapper>
+      <BtnGroup>
+        {
+          options.map((ele, index) => (
+            <Btn
+              key={ele.key}
+              isActive={index === selectedIndex}
+              onClick={() => { handleClick(index); }}
+            >
+              {ele.content}
+            </Btn>
+          ))
+        }
+      </BtnGroup>
+      {options[selectedIndex] ? options[selectedIndex].child : null}
+    </Wrapper>
+  );
+};
+
+export default Tabs;

+ 34 - 0
components/Tabs/styled.ts

@@ -0,0 +1,34 @@
+import styled, { css } from 'styled-components';
+
+import { color } from '../../constants/style';
+
+export const Wrapper = styled.div`
+  width: 100%;
+`;
+
+export const BtnGroup = styled.div`
+  background-color: white;
+  border-radius: 4px;
+  overflow: hidden;
+  display: flex;
+  justify-content: space-around;
+  border: 1.5px solid ${color.primary};
+`;
+
+export const Btn = styled('button')<{isActive?: boolean}>`
+  height: 30px;
+  width: 100%;
+  outline: none;
+  background-color: white;
+  border: none;
+  cursor: pointer;
+  color: ${color.primary};
+  font-size: 14px;
+  font-weight: bold;
+  box-sizing: border-box;
+
+  ${props => props.isActive ? css`
+    background-color: ${color.primary};
+    color: white;
+  ` : null}
+`;

+ 28 - 15
components/TextField/index.tsx

@@ -1,6 +1,6 @@
 import React, { forwardRef } from 'react';
 
-import { Input } from './styled';
+import { Input, TextArea } from './styled';
 
 type Props = {
   id?: string;
@@ -12,9 +12,10 @@ type Props = {
   disabled?: boolean;
   error?: boolean;
   shouldFitContainer?: boolean;
+  variant?: 'standard' | 'multiline';
 }
 
-type Ref = HTMLInputElement;
+type Ref = any;
 
 const TextField = forwardRef<Ref, Props>(({
   id = '',
@@ -26,27 +27,39 @@ const TextField = forwardRef<Ref, Props>(({
   disabled = false,
   error = false,
   shouldFitContainer = false,
+  variant = 'standard',
 }, ref) => {
 
-  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
     if (!disabled) {
       onChange(e.target.value);  
     }
   };
 
+  const transferProps = {
+    id,
+    name,
+    defaultValue,
+    placeholder,
+    onChange: handleChange,
+    onBlur,
+    disabled,
+    error,
+    shouldFitContainer,
+  }
+
   return (
-    <Input
-      ref={ref}
-      id={id}
-      name={name}
-      defaultValue={defaultValue}
-      placeholder={placeholder}
-      onChange={handleChange}
-      onBlur={onBlur}
-      disabled={disabled}
-      error={error}
-      shouldFitContainer={shouldFitContainer}
-    />
+    variant === 'standard' ? (
+      <Input
+        ref={ref}
+        {...transferProps}
+      />  
+    ) : (
+      <TextArea
+        ref={ref}
+        {...transferProps}
+      />
+    )
   );
 });
 

+ 18 - 2
components/TextField/styled.ts

@@ -2,20 +2,24 @@ import styled, { css } from 'styled-components';
 
 import { color } from '../../constants/style';
 
-export const Input = styled('input')<{error: boolean, shouldFitContainer: boolean}>`
+const baseStyles = css`
   border: 1.5px solid ${color.black38};
   border-radius: 4px;
   padding: 7px 15px;
   outline: none;
   transition: border 200ms cubic-bezier(0.0, 0, 0.2, 1) 0ms;
   font-size: 14px;
-  width: ${props => props.shouldFitContainer ? '100%' : 'auto'};
   box-sizing: border-box;
 
   :disabled {
     color: ${color.black56};
     cursor: not-allowed;
   }
+`;
+
+export const Input = styled('input')<{error: boolean, shouldFitContainer: boolean}>`
+  ${baseStyles}
+  width: ${props => props.shouldFitContainer ? '100%' : 'auto'};
 
   ${props => props.error ? css`
     border: 1.5px solid ${color.error};
@@ -26,5 +30,17 @@ export const Input = styled('input')<{error: boolean, shouldFitContainer: boolea
   `}
 `;
 
+export const TextArea = styled('textarea')<{error: boolean, shouldFitContainer: boolean}>`
+  ${baseStyles}
+  height: 54px;
+  width: ${props => props.shouldFitContainer ? '100%' : 'auto'};
 
+  ${props => props.error ? css`
+    border: 1.5px solid ${color.error};
+  ` : css`
+    :focus {
+      border: 1.5px solid ${color.primary};
+    }
+  `}
+`;
 

+ 35 - 0
components/Thumbnails/index.tsx

@@ -0,0 +1,35 @@
+import React from 'react';
+
+import Icon from '../Icon';
+import Drawer from '../Drawer';
+
+import { ThumbnailWrapper, Page, Thumbnail } from './styled';
+import { Wrapper, Head, Body, IconWrapper } from '../../global/sidebarStyled'
+
+type Props = {
+  isActive: boolean;
+};
+
+const Thumbnails : React.FunctionComponent<Props> = ({
+  isActive,
+}) => {
+  return (
+    <Drawer anchor="right" open={isActive}>
+      <Wrapper>
+        <Head>
+          <IconWrapper>
+            <Icon glyph="right-back" />
+          </IconWrapper>
+        </Head>
+        <Body>
+          <ThumbnailWrapper>
+            <Page>1</Page>
+            <Thumbnail src="/static/thumbnail1.png" />
+          </ThumbnailWrapper>
+        </Body>
+      </Wrapper>
+    </Drawer>
+  );
+};
+
+export default Thumbnails;

+ 21 - 0
components/Thumbnails/styled.ts

@@ -0,0 +1,21 @@
+import styled from 'styled-components';
+
+import { color } from '../../constants/style';
+
+export const ThumbnailWrapper = styled.div`
+  display: flex;
+  justify-content: center;
+  align-items: center;
+`;
+
+export const Page = styled.div`
+  margin-right: 20px;
+`;
+
+export const Thumbnail = styled.img`
+  border: 1px solid ${color.black38};
+  width: 100%;
+  height: 100%;
+  max-width: 146px;
+  box-sizing: border-box;
+`;

+ 39 - 0
components/Tooltip/index.tsx

@@ -0,0 +1,39 @@
+import React from 'react';
+import { Manager, Reference, Popper } from 'react-popper';
+
+import { TargetContainer, TooltipContainer, Arrow } from './styled';
+import Portal from '../Portal';
+
+type Props = {
+  anchor?: 'left' | 'right' | 'top' | 'bottom';
+  children: React.ReactNode;
+  content: React.ReactNode;
+};
+
+const Tooltip : React.FunctionComponent<Props> = ({
+  anchor = 'left',
+  children,
+  content,
+}) => (
+  <Manager>
+    <Reference>
+      {({ ref }) => (
+        <TargetContainer ref={ref}>
+          {children}
+        </TargetContainer>
+      )}
+    </Reference>
+    <Portal>
+      <Popper placement={anchor}>
+        {({ ref, style, placement, arrowProps }) => (
+          <TooltipContainer ref={ref} style={style} data-placement={placement}>
+            {content}
+            <Arrow data-placement={placement} ref={arrowProps.ref} style={arrowProps.style} />
+          </TooltipContainer>
+        )}
+      </Popper>
+    </Portal>
+  </Manager>
+);
+
+export default Tooltip;

+ 93 - 0
components/Tooltip/styled.ts

@@ -0,0 +1,93 @@
+import styled from 'styled-components';
+
+export const TargetContainer = styled.div`
+  display: inline-block;
+  margin-top: 100px;
+  margin-left: 100px;
+`;
+
+export const TooltipContainer = styled('div')<{}>`
+  z-index: 1000;
+  color: #fff;
+  border-radius: 2px;
+  font-size: 12px;
+  background-color: rgba(0, 0, 0, 0.6);
+  min-height: 50px;
+  min-width: 86px;
+  box-sizing: border-box;
+
+  &[data-placement*='bottom'] {
+    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.30);
+    margin-top: 10px;
+  }
+  &[data-placement*='top'] {
+    box-shadow: 0 -4px 8px 0 rgba(0, 0, 0, 0.30);
+    margin-bottom: 10px;
+  }
+  &[data-placement*='left'] {
+    box-shadow: -4px 0 8px 0 rgba(0, 0, 0, 0.30);
+    margin-right: 14px;
+  }
+  &[data-placement*='right'] {
+    box-shadow: 4px 0 8px 0 rgba(0, 0, 0, 0.30);
+    margin-left: 14px;
+  }
+`;
+
+export const Arrow = styled('div')`
+  position: absolute;
+  width: 3em;
+  height: 3em;
+  box-sizing: border-box;
+
+  &[data-placement*='bottom'] {
+    top: -1px;
+    left: 0;
+    margin-top: -0.9em;
+    width: 3em;
+    height: 1em;
+    &::before {
+      border-width: 0 12px 12px 12px;
+      border-color: transparent transparent rgba(0, 0, 0, 0.6) transparent;
+    }
+  }
+  &[data-placement*='top'] {
+    bottom: -1px;
+    left: 0;
+    margin-bottom: -0.9em;
+    width: 3em;
+    height: 1em;
+    &::before {
+      border-width: 12px 12px 0 12px;
+      border-color: rgba(0, 0, 0, 0.6) transparent transparent transparent;
+    }
+  }
+  &[data-placement*='right'] {
+    left: -1px;
+    margin-left: -0.9em;
+    height: 3em;
+    width: 1em;
+    &::before {
+      border-width: 12px 12px 12px 0;
+      border-color: transparent rgba(0, 0, 0, 0.6) transparent transparent;
+    }
+  }
+  &[data-placement*='left'] {
+    right: -1px;
+    margin-right: -0.9em;
+    height: 3em;
+    width: 1em;
+    &::before {
+      border-width: 12px 0 12px 12px;
+      border-color: transparent transparent transparent rgba(0, 0, 0, 0.6);
+    }
+  }
+  &::before {
+    content: '';
+    margin: auto;
+    display: block;
+    width: 0;
+    height: 0;
+    border-style: solid;
+  }
+`;

+ 2 - 0
components/Typography/styled.ts

@@ -6,12 +6,14 @@ export const Title = styled('p')<{light: boolean}>`
   font-size: 16px;
   color: ${props => props.light ? color.black38 : '#000000' };
   margin: 5px;
+  white-space: nowrap;
 `;
 
 export const Subtitle = styled('p')<{light: boolean}>`
   font-size: 14px;
   color: ${props => props.light ? color.black38 : '#000000' };
   margin: 5px;
+  white-space: nowrap;
 `;
 
 export const Body = styled('p')<{light: boolean}>`

+ 105 - 0
components/Watermark/index.tsx

@@ -0,0 +1,105 @@
+import React, { useState } from 'react';
+
+import Button from '../Button';
+import Icon from '../Icon';
+import Drawer from '../Drawer';
+import Typography from '../Typography';
+import Tabs from '../Tabs';
+import Sliders from '../Sliders';
+import Divider from '../Divider';
+import ColorSelect from '../ColorSelect';
+import TextField from '../TextField';
+
+import { BtnWrapper, ContentWrapper } from './styled';
+import { Wrapper, Head, Body, IconWrapper } from '../../global/sidebarStyled';
+import { Group, SliderWrapper } from '../../global/toolStyled';
+
+type Props = {
+  sidebarState: string;
+  onClick: (state: string) => void;
+};
+
+const TextContent = () => {
+  return (
+    <>
+      <ContentWrapper>
+        <Typography variant="subtitle">Content</Typography>
+      </ContentWrapper>
+      <TextField variant="multiline" shouldFitContainer />
+    </>
+  );
+}
+
+const ImageContent = () => {
+  return (
+    <ContentWrapper>
+      <Icon glyph="add-image" />
+      <Button appearance="link">Select Image</Button>
+    </ContentWrapper>
+  );
+}
+
+const Watermark: React.FunctionComponent<Props> = ({
+  onClick,
+}) => {
+  const [isActive, setActive] = useState(false);
+
+  const handleClick = () => {
+    setActive(!isActive);
+    onClick(!isActive ? 'watermark' : '');
+  }
+
+  return (
+    <>
+      <BtnWrapper>
+        <Button shouldFitContainer align="left" onClick={handleClick}>
+          <Icon glyph="watermark" style={{marginRight: '10px'}} />
+          Watermark
+        </Button>
+      </BtnWrapper>
+      <Drawer open={isActive} anchor="left">
+        <Wrapper>
+          <Head>
+            <IconWrapper>
+              <Icon glyph="left-back" onClick={handleClick} />
+            </IconWrapper>
+            <Typography light>Watermark</Typography>
+          </Head>
+          <Body>
+            <Typography variant="subtitle">Type</Typography>
+            <Tabs
+              options={[
+                {
+                  key: 'text',
+                  content: 'Text',
+                  child: <TextContent />,
+                },
+                {
+                  key: 'image',
+                  content: 'Image',
+                  child: <ImageContent />,
+                }
+              ]}
+            />
+            <Divider orientation="horizontal" style={{ margin: '20px 0' }} />
+            <ColorSelect />
+            <Typography variant="subtitle">Opacity</Typography>
+            <Group>
+              <SliderWrapper>
+                <Sliders />
+              </SliderWrapper>
+              40%
+            </Group>
+            <Divider orientation="horizontal" style={{ margin: '20px 0' }} />
+            <Group>
+              <Typography variant="subtitle">Page Range</Typography>
+              <Button appearance="primary-hollow" style={{width: '50%'}}>All Pages</Button>
+            </Group>
+          </Body>
+        </Wrapper>
+      </Drawer>
+    </>
+  );
+};
+
+export default Watermark;

+ 10 - 0
components/Watermark/styled.ts

@@ -0,0 +1,10 @@
+import styled from 'styled-components';
+
+export const BtnWrapper = styled.div`
+  padding: 8px;
+`;
+
+export const ContentWrapper = styled.div`
+  margin-top: 20px;
+  display: flex;
+`;