ui book page footer navbar menu font

demo
taha12322 3 years ago
parent d4aa9d6e64
commit 16cac55ec0
  1. 1
      package.json
  2. 23
      public/index.html
  3. 38
      src/components/footer/MoveBtn.tsx
  4. 4
      src/components/footer/Wrapper.tsx
  5. 1
      src/components/nav/NavItem.tsx
  6. 32
      src/components/sideMenu/Wrapper.tsx
  7. 30
      src/containers/Footer.tsx
  8. 17
      src/containers/Header.tsx
  9. 64
      src/containers/Reader.tsx
  10. 151
      src/containers/commons/ContextMenu.tsx
  11. 82
      src/containers/menu/Note.tsx
  12. 162
      src/containers/menu/Option.tsx
  13. 4
      src/lib/hooks/useHighlight.ts
  14. 12
      src/slices/book.ts
  15. 7
      yarn.lock

@ -41,6 +41,7 @@
}, },
"dependencies": { "dependencies": {
"@material-ui/core": "^4.12.3", "@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"epubjs": "^0.3.88", "epubjs": "^0.3.88",
"node-sass": "4.14.1", "node-sass": "4.14.1",
"react-epub-viewer": "^0.1.7", "react-epub-viewer": "^0.1.7",

@ -2,30 +2,11 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>React-Epub-Viewer Demo</title> <title>Book reader</title>
<meta name="description" content="React-Epub-Viewer Demo Page" /> <meta name="description" content="React-Epub-Viewer Demo Page" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link
rel="icon"
sizes="24x24"
href="%PUBLIC_URL%/favicons/favicon_24x24.png"
/>
<link
rel="icon"
sizes="48x48"
href="%PUBLIC_URL%/favicons/favicon_48x48.png"
/>
<link
rel="icon"
sizes="96x96"
href="%PUBLIC_URL%/favicons/favicon_96x96.png"
/>
<link
rel="apple-touch-icon"
href="%PUBLIC_URL%/favicons/favicon_96x96.png"
/>
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/css/global.css" /> <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%/css/global.css" />
<style> <style>
@font-face { @font-face {
@ -42,7 +23,7 @@
font-family: "SansNum"; font-family: "SansNum";
src: url(../fonts/IRANSansWebFaNum.woff); src: url(../fonts/IRANSansWebFaNum.woff);
} }
body { * , body {
font-family: Sans, Nazanin; font-family: Sans, Nazanin;
} }
</style> </style>

@ -1,46 +1,52 @@
import styled from 'styled-components' import styled from "styled-components";
// icons // icons
import { PrevIcon, NextIcon } from 'lib/svg' import { PrevIcon, NextIcon } from "lib/svg";
// lib // lib
import * as styles from 'lib/styles/styles' import * as styles from "lib/styles/styles";
const MoveBtn = ({ type, onClick }: Props) => { const MoveBtn = ({ type, onClick }: Props) => {
const Icon = type === "PREV" const Icon = type === "PREV" ? NextIcon : PrevIcon;
? NextIcon
: PrevIcon;
const msg = type === "PREV" const msg = type === "PREV" ? "이전 페이지" : "다음 페이지";
? "이전 페이지"
: "다음 페이지";
return ( return (
<Container onClick={onClick} title={msg}> <Container onClick={onClick} title={msg}>
<Content> <Content>
<Icon /> {window.innerWidth > 768 ? <Icon /> : <Icon style={{ opacity: "0" }} />}
</Content> </Content>
</Container> </Container>
); );
} };
const Container = styled.button` const Container = styled.button`
min-width: 60px; min-width: 160px;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
padding-top: 5px;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
outline: none; outline: none;
&:focus, &:hover { &:focus,
filter: invert(40%) sepia(85%) saturate(1256%) hue-rotate(210deg) brightness(113%) contrast(101%); &:hover {
filter: invert(40%) sepia(85%) saturate(1256%) hue-rotate(210deg)
brightness(113%) contrast(101%);
} }
`; `;
const contentM = styled.div`
display: flex;
align-items: center;
justify-content: center;
transition: 0.1s ${styles.transition};
`;
const Content = styled.div` const Content = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
transition: .1s ${styles.transition}; transition: 0.1s ${styles.transition};
& > svg { & > svg {
width: 18px; width: 18px;
@ -49,7 +55,7 @@ const Content = styled.div`
`; `;
interface Props { interface Props {
type: "PREV" | "NEXT" type: "PREV" | "NEXT";
onClick: () => void; onClick: () => void;
} }

@ -7,7 +7,7 @@ const Wrapper = styled.div`
left: 0; left: 0;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
height: 60px; height: 37px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -15,3 +15,5 @@ const Wrapper = styled.div`
`; `;
export default Wrapper export default Wrapper

@ -21,6 +21,7 @@ const Content = styled.button`
cursor: pointer; cursor: pointer;
background-color: ${palette.gray0}; background-color: ${palette.gray0};
outline: none; outline: none;
direction: rtl;
& > span { & > span {
transition: .2s ${styles.transition}; transition: .2s ${styles.transition};

@ -1,11 +1,11 @@
import React from 'react' import React from "react";
import styled from 'styled-components' import styled from "styled-components";
// components // components
import CloseBtn from 'components/sideMenu/CloseBtn' import CloseBtn from "components/sideMenu/CloseBtn";
// lib // lib
import * as styles from 'lib/styles/styles' import * as styles from "lib/styles/styles";
import zIndex from 'lib/styles/zIndex' import zIndex from "lib/styles/zIndex";
import palette from 'lib/styles/palette' import palette from "lib/styles/palette";
const Wrapper = ({ title, show, onClose, children }: Props, ref: any) => { const Wrapper = ({ title, show, onClose, children }: Props, ref: any) => {
return ( return (
@ -16,10 +16,11 @@ const Wrapper = ({ title, show, onClose, children }: Props, ref: any) => {
</Header> </Header>
{children} {children}
</Container> </Container>
) );
} };
const Container = styled.div` const Container = styled.div`
direction: rtl;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: fixed; position: fixed;
@ -29,15 +30,15 @@ const Container = styled.div`
top: 0; top: 0;
right: 0; right: 0;
z-index: ${zIndex.menu}; z-index: ${zIndex.menu};
box-shadow: -4px 0 8px 0 rgba(0,0,0,.16); box-shadow: -4px 0 8px 0 rgba(0, 0, 0, 0.16);
background-color: ${palette.white}; background-color: ${palette.white};
border-radius: 16px 0 0 16px; border-radius: 16px 0 0 16px;
transform: ${({ show }: { show: boolean }) => show transform: ${({ show }: { show: boolean }) =>
? `translateX(0px) scale(1)` show ? `translateX(0px) scale(1)` : `translateX(420px) scale(.9)`};
: `translateX(420px) scale(.9)` transition: 0.4s ${styles.transition};
};
transition: .4s ${styles.transition};
overflow-y: auto; overflow-y: auto;
text-align: right;
direction: rtl;
${styles.scrollbar(0)}; ${styles.scrollbar(0)};
`; `;
@ -48,6 +49,7 @@ const Header = styled.div`
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding-top: 4px; padding-top: 4px;
direction: rtl;
& > span { & > span {
padding-left: 24px; padding-left: 24px;
@ -63,4 +65,4 @@ interface Props {
children?: React.ReactNode; children?: React.ReactNode;
} }
export default React.forwardRef(Wrapper) export default React.forwardRef(Wrapper);

@ -2,17 +2,30 @@
import Wrapper from "components/footer/Wrapper"; import Wrapper from "components/footer/Wrapper";
import Item from "components/footer/Item"; import Item from "components/footer/Item";
import MoveBtn from "components/footer/MoveBtn"; import MoveBtn from "components/footer/MoveBtn";
import styled from "styled-components";
const Footer = ({ title, nowPage, totalPage, onPageMove }: Props) => { const Footer = ({ title, nowPage, totalPage, onPageMove }: Props) => {
return ( return (
<>
<Wrapper> <Wrapper>
<MoveBtn type="NEXT" onClick={() => onPageMove("NEXT")} /> {/* <Item text={`${nowPage} / ${totalPage}`} /> */}
<Item text={`${nowPage} / ${totalPage}`} /> {window.innerWidth < 768 ? <Button>خرید کتاب</Button> : null}
<MoveBtn type="PREV" onClick={() => onPageMove("PREV")} />
</Wrapper> </Wrapper>
</>
); );
}; };
const Button = styled.button`
width: 100%;
justify-content: center;
display: flex;
height: 35px;
padding: 2px 2rem;
color: #fff;
font-family: inherit;
background-color: rgb(0, 154, 168);
`;
interface Props { interface Props {
title: string; title: string;
nowPage: number; nowPage: number;
@ -21,3 +34,14 @@ interface Props {
} }
export default Footer; export default Footer;
const WrapperBtn = styled.div`
position: fixed;
left: 0;
top: 60px;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
`;

@ -28,7 +28,7 @@ const Header = ({
/> />
</nav> </nav>
<nav> <nav>
<Button>خرید کتاب</Button> {window.innerWidth > 768 ? <Button>خرید کتاب</Button> : null}
</nav> </nav>
<nav <nav
style={{ style={{
@ -38,8 +38,9 @@ const Header = ({
}} }}
> >
<Item text={title as any} /> <Item text={title as any} />
<span style={{ color: "#fff", marginRight: "10px" }}>||</span> <span style={{ color: "#fff", marginRight: "10px" }}>|</span>
<p style={{ margin: "0", color: "#fff" }}>نام کتاب </p> <p style={{ margin: "0", color: "#fff" }}>
</p>
<Logo /> <Logo />
<Img <Img
src="/react-epub-viewer/images/menu.svg" src="/react-epub-viewer/images/menu.svg"
@ -58,17 +59,21 @@ const Header = ({
const Button = styled.button` const Button = styled.button`
display: flex; display: flex;
margin-top: 11px; margin-top: 15px;
border-radius: 5px; border-radius: 5px;
border: solid 2px #fff; border: solid 2px #fff;
padding: 3px 2rem; padding: 2px 2rem;
color: #fff; color: #fff;
font-family: inherit; font-family: inherit;
&:hover {
background-color: #fff;
color: rgb(0, 154, 168);
}
`; `;
const Img = styled.img` const Img = styled.img`
width: 25px; width: 25px;
`; `;
interface Props { interface Props {
title: String; title: String;

@ -2,6 +2,7 @@ import { useState, useRef } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { ReactEpubViewer } from "react-epub-viewer"; import { ReactEpubViewer } from "react-epub-viewer";
import styled from "styled-components";
// containers // containers
import Header from "containers/Header"; import Header from "containers/Header";
import Footer from "containers/Footer"; import Footer from "containers/Footer";
@ -13,6 +14,7 @@ import Snackbar from "containers/commons/Snackbar";
// components // components
import ViewerWrapper from "components/commons/ViewerWrapper"; import ViewerWrapper from "components/commons/ViewerWrapper";
import LoadingView from "LoadingView"; import LoadingView from "LoadingView";
import BookInfo from "components/nav/BookInfo";
// slices // slices
import store from "slices"; import store from "slices";
import { updateBook, updateCurrentPage, updateToc } from "slices/book"; import { updateBook, updateCurrentPage, updateToc } from "slices/book";
@ -29,11 +31,13 @@ import Book, { BookStyle, BookOption } from "types/book";
import Page from "types/page"; import Page from "types/page";
import Toc from "types/toc"; import Toc from "types/toc";
import { title } from "node:process"; import { title } from "node:process";
import ArrowBackIosRoundedIcon from "@material-ui/icons/ArrowBackIosRounded";
import ArrowForwardIosRoundedIcon from "@material-ui/icons/ArrowForwardIosRounded";
import MoveBtn from "components/footer/MoveBtn";
const Reader = ({ url, loadingView }: Props) => { const Reader = ({ url, loadingView }: Props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const currentLocation = useSelector<RootState, Page>((state) => { const currentLocation = useSelector<RootState, Page>((state) => {
console.log({ state });
return state.book.currentLocation; return state.book.currentLocation;
}); });
@ -59,7 +63,7 @@ const Reader = ({ url, loadingView }: Props) => {
}); });
const [navControl, onNavToggle] = useMenu(navRef, 300); const [navControl, onNavToggle] = useMenu(navRef, 300);
const [optionControl, onOptionToggle, emitEvent] = useMenu(optionRef, 300); const [optionControl, onOptionToggle, emitEvent] = useMenu(optionRef, 5000);
const [learningControl, onLearningToggle] = useMenu(learningRef, 300); const [learningControl, onLearningToggle] = useMenu(learningRef, 300);
const { const {
selection, selection,
@ -77,7 +81,7 @@ const Reader = ({ url, loadingView }: Props) => {
const loadFont = () => { const loadFont = () => {
const iframe = document.querySelector("iframe"); const iframe = document.querySelector("iframe");
const new_style_element = document.createElement("style"); const new_style_element = document.createElement("style");
new_style_element.textContent = ` @font-face { new_style_element.textContent = `@font-face {
font-family: "Nazanin"; font-family: "Nazanin";
src: url(/react-epub-viewer/css/Nazanin.woff); src: url(/react-epub-viewer/css/Nazanin.woff);
} }
@ -155,13 +159,13 @@ const Reader = ({ url, loadingView }: Props) => {
/** ContextMenu off */ /** ContextMenu off */
const onContextmMenuRemove = () => setIsContextMenu(false); const onContextmMenuRemove = () => setIsContextMenu(false);
console.log({ viewerRef });
const [settingicon, setsettingicon] = useState(false); const [settingicon, setsettingicon] = useState(false);
const changeiconsettinghandler = () => { const changeiconsettinghandler = () => {
setsettingicon(!settingicon); setsettingicon(!settingicon);
}; };
return ( return (
<> <>
<ViewerWrapper> <ViewerWrapper>
@ -171,7 +175,35 @@ const Reader = ({ url, loadingView }: Props) => {
onOptionToggle={onOptionToggle} onOptionToggle={onOptionToggle}
onLearningToggle={onLearningToggle} onLearningToggle={onLearningToggle}
/> />
<nav
style={{
display: "flex",
justifyContent: "center",
marginTop: "1rem",
}}
>
{window.innerWidth < 768 ? (
<button
style={{
opacity: "0",
position: "absolute",
left: "0",
width: "20%",
height: "85%",
top: "4rem",
zIndex: 10,
}}
onClick={() => onPageMove("NEXT")}
>
<ArrowBackIosRoundedIcon />
</button>
) : (
<button onClick={() => onPageMove("NEXT")}>
<ArrowBackIosRoundedIcon />
</button>
)}
<nav style={{ overflow: "hidden" }}>
<ReactEpubViewer <ReactEpubViewer
url={url} url={url}
viewerLayout={viewerLayout} viewerLayout={viewerLayout}
@ -184,6 +216,27 @@ const Reader = ({ url, loadingView }: Props) => {
loadingView={loadingView || <LoadingView />} loadingView={loadingView || <LoadingView />}
ref={viewerRef} ref={viewerRef}
/> />
</nav>
{window.innerWidth < 768 ? (
<button
style={{
opacity: "0",
position: "absolute",
right: "0",
width: "20%",
height: "85%",
top: "4rem",
}}
onClick={() => onPageMove("PREV")}
>
<ArrowForwardIosRoundedIcon />
</button>
) : (
<button onClick={() => onPageMove("PREV")}>
<ArrowForwardIosRoundedIcon />
</button>
)}
</nav>
<Footer <Footer
title={currentLocation.chapterName} title={currentLocation.chapterName}
@ -230,7 +283,6 @@ const Reader = ({ url, loadingView }: Props) => {
onUpdateHighlight={onUpdateHighlight} onUpdateHighlight={onUpdateHighlight}
onContextmMenuRemove={onContextmMenuRemove} onContextmMenuRemove={onContextmMenuRemove}
/> />
<Snackbar /> <Snackbar />
</> </>
); );
@ -238,9 +290,11 @@ const Reader = ({ url, loadingView }: Props) => {
const ReaderWrapper = ({ url, loadingView }: Props) => { const ReaderWrapper = ({ url, loadingView }: Props) => {
return ( return (
<>
<Provider store={store}> <Provider store={store}>
<Reader url={url} loadingView={loadingView} /> <Reader url={url} loadingView={loadingView} />
</Provider> </Provider>
</>
); );
}; };

@ -1,18 +1,17 @@
import { useState, useEffect, useCallback, useRef } from 'react' import { useState, useEffect, useCallback, useRef } from "react";
import { useSelector } from 'react-redux' import { useSelector } from "react-redux";
// components // components
import Wrapper from 'components/contextMenu/Wrapper' import Wrapper from "components/contextMenu/Wrapper";
import ColorItem from 'components/contextMenu/ColorItem' import ColorItem from "components/contextMenu/ColorItem";
import Item from 'components/contextMenu/Item' import Item from "components/contextMenu/Item";
// utils // utils
import { getParagraphCfi } from 'lib/utils/commonUtil' import { getParagraphCfi } from "lib/utils/commonUtil";
// slices // slices
import { contextmenuWidth } from 'lib/styles/viewerLayout' import { contextmenuWidth } from "lib/styles/viewerLayout";
// types // types
import { RootState } from 'slices' import { RootState } from "slices";
import Highlight, { Color } from 'types/highlight' import Highlight, { Color } from "types/highlight";
import Selection from 'types/selection' import Selection from "types/selection";
const ContextMenu = ({ const ContextMenu = ({
active, active,
@ -21,10 +20,14 @@ const ContextMenu = ({
onAddHighlight, onAddHighlight,
onRemoveHighlight, onRemoveHighlight,
onUpdateHighlight, onUpdateHighlight,
onContextmMenuRemove onContextmMenuRemove,
}: Props) => { }: Props) => {
const highlights = useSelector<RootState, Highlight[]>(state => state.book.highlights); const highlights = useSelector<RootState, Highlight[]>(
const colorList = useSelector<RootState, Color[]>(state => state.book.colorList); (state) => state.book.highlights
);
const colorList = useSelector<RootState, Color[]>(
(state) => state.book.colorList
);
const menuRef = useRef<HTMLDivElement | null>(null); const menuRef = useRef<HTMLDivElement | null>(null);
@ -37,16 +40,18 @@ const ContextMenu = ({
const [height, setHeight] = useState<number>(0); const [height, setHeight] = useState<number>(0);
const [y, setY] = useState<number>(selection.y); const [y, setY] = useState<number>(selection.y);
const ColorList = colorList.map( const ColorList = colorList.map((color) => (
color => <ColorItem key={color.code} <ColorItem
key={color.code}
name={color.name} name={color.name}
color={color.code} color={color.code}
onClick={selection.update onClick={
selection.update
? () => onUpdateHighlight(highlight, color.code) ? () => onUpdateHighlight(highlight, color.code)
: () => onAddHighlight(color.code) : () => onAddHighlight(color.code)
} /> }
); />
));
/** Remove highlight */ /** Remove highlight */
const onRemoveHighlight_ = useCallback(() => { const onRemoveHighlight_ = useCallback(() => {
@ -63,23 +68,28 @@ const ContextMenu = ({
* @param e Mouse Event * @param e Mouse Event
* @param e.path * @param e.path
*/ */
const onRemove = useCallback(({ path }: any) => { const onRemove = useCallback(
({ path }: any) => {
if (!menuRef.current) return; if (!menuRef.current) return;
if ([...path].includes(menuRef.current)) return; if ([...path].includes(menuRef.current)) return;
onContextmMenuRemove(); onContextmMenuRemove();
}, [menuRef, onContextmMenuRemove]); },
[menuRef, onContextmMenuRemove]
);
/** /**
* Arrow event * Arrow event
* @param e Keyboard Event * @param e Keyboard Event
* @param e.key * @param e.key
*/ */
const onKeyPress = useCallback(({ key }: any) => { const onKeyPress = useCallback(
({ key }: any) => {
key && key === "ArrowLeft" && onContextmMenuRemove(); key && key === "ArrowLeft" && onContextmMenuRemove();
key && key === "ArrowRight" && onContextmMenuRemove(); key && key === "ArrowRight" && onContextmMenuRemove();
}, [onContextmMenuRemove]); },
[onContextmMenuRemove]
);
/** Check whether the menu button is visible */ /** Check whether the menu button is visible */
useEffect(() => { useEffect(() => {
@ -88,7 +98,9 @@ const ContextMenu = ({
const paragraphCfi = getParagraphCfi(selection.cfiRange); const paragraphCfi = getParagraphCfi(selection.cfiRange);
if (!paragraphCfi) return; if (!paragraphCfi) return;
const filtered = highlights.filter(highlight => highlight.key === paragraphCfi + selection.cfiRange); const filtered = highlights.filter(
(highlight) => highlight.key === paragraphCfi + selection.cfiRange
);
if (!filtered.length) return; if (!filtered.length) return;
const highlight_ = filtered[0]; const highlight_ = filtered[0];
@ -99,49 +111,41 @@ const ContextMenu = ({
} else { } else {
setIsEraseBtn(false); setIsEraseBtn(false);
} }
}, [ }, [active, highlights, selection.cfiRange, selection.update]);
active,
highlights,
selection.cfiRange,
selection.update
]);
/** Register contextmenu events */ /** Register contextmenu events */
useEffect(() => { useEffect(() => {
if (!viewerRef.current) return; if (!viewerRef.current) return;
const iframe = document.querySelector('iframe'); const iframe = document.querySelector("iframe");
const node = iframe && iframe.contentWindow && iframe.contentWindow.document; const node =
const scrolledTarget = viewerRef.current.querySelector('div'); iframe && iframe.contentWindow && iframe.contentWindow.document;
const scrolledTarget = viewerRef.current.querySelector("div");
if (active) { if (active) {
setDisplay(true); setDisplay(true);
scrolledTarget && scrolledTarget.addEventListener('scroll', onContextmMenuRemove); scrolledTarget &&
node && node.addEventListener('mousedown', onRemove); scrolledTarget.addEventListener("scroll", onContextmMenuRemove);
node && node.addEventListener('keyup', onKeyPress); node && node.addEventListener("mousedown", onRemove);
document.addEventListener('mousedown', onRemove); node && node.addEventListener("keyup", onKeyPress);
document.addEventListener('keyup', onKeyPress); document.addEventListener("mousedown", onRemove);
document.addEventListener("keyup", onKeyPress);
} else { } else {
setDisplay(false); setDisplay(false);
scrolledTarget && scrolledTarget.removeEventListener('scroll', onContextmMenuRemove); scrolledTarget &&
node && node.removeEventListener('mousedown', onRemove); scrolledTarget.removeEventListener("scroll", onContextmMenuRemove);
node && node.removeEventListener('keyup', onKeyPress); node && node.removeEventListener("mousedown", onRemove);
document.removeEventListener('mousedown', onRemove); node && node.removeEventListener("keyup", onKeyPress);
document.removeEventListener('keyup', onKeyPress); document.removeEventListener("mousedown", onRemove);
}; document.removeEventListener("keyup", onKeyPress);
}
return () => { return () => {
node && node.removeEventListener('mousedown', onContextmMenuRemove); node && node.removeEventListener("mousedown", onContextmMenuRemove);
node && node.removeEventListener('keyup', onKeyPress); node && node.removeEventListener("keyup", onKeyPress);
document.removeEventListener('keyup', onKeyPress); document.removeEventListener("keyup", onKeyPress);
} };
}, [ }, [viewerRef, active, onRemove, onContextmMenuRemove, onKeyPress]);
viewerRef,
active,
onRemove,
onContextmMenuRemove,
onKeyPress
]);
/** Set modified contextmenu height & whether menu is reverse */ /** Set modified contextmenu height & whether menu is reverse */
useEffect(() => { useEffect(() => {
@ -156,7 +160,7 @@ const ContextMenu = ({
const { innerHeight } = window; const { innerHeight } = window;
if (selection.y + defaultHeight > innerHeight) { if (selection.y + defaultHeight > innerHeight) {
y_ = selection.y - selection.height - (defaultHeight); y_ = selection.y - selection.height - defaultHeight;
if (y_ < 0) { if (y_ < 0) {
setHeight(defaultHeight + y_ - 8); setHeight(defaultHeight + y_ - 8);
y_ = 8; y_ = 8;
@ -170,29 +174,28 @@ const ContextMenu = ({
} }
setY(y_); setY(y_);
}, [ }, [selection.y, selection.height, ColorList, isEraseBtn, setHeight]);
selection.y,
selection.height,
ColorList,
isEraseBtn,
setHeight
]);
return (
return (<> <>
{display && <Wrapper x={selection.x} {display && (
<Wrapper
x={selection.x}
y={y} y={y}
width={contextmenuWidth} width={contextmenuWidth}
height={height} height={height}
isReverse={isReverse} isReverse={isReverse}
ref={menuRef}> ref={menuRef}
>
<div> <div>
{ColorList} {ColorList}
{isEraseBtn && <Item text="Remove" onClick={onRemoveHighlight_} />} {isEraseBtn && <Item text="حذف" onClick={onRemoveHighlight_} />}
</div> </div>
</Wrapper>} </Wrapper>
</>); )}
} </>
);
};
interface Props { interface Props {
active: boolean; active: boolean;
@ -204,4 +207,4 @@ interface Props {
onContextmMenuRemove: () => void; onContextmMenuRemove: () => void;
} }
export default ContextMenu export default ContextMenu;

@ -1,60 +1,60 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from "react";
import { useSelector } from 'react-redux' import { useSelector } from "react-redux";
// containers // containers
import Highlight from 'containers/menu/commons/Highlight' import Highlight from "containers/menu/commons/Highlight";
// components // components
import Wrapper from 'components/sideMenu/Wrapper' import Wrapper from "components/sideMenu/Wrapper";
import LearningLayout from 'components/note/Layout' import LearningLayout from "components/note/Layout";
import MenuEmpty from 'components/sideMenu/MenuEmpty' import MenuEmpty from "components/sideMenu/MenuEmpty";
// types // types
import { RootState } from 'slices' import { RootState } from "slices";
import HighlightType from 'types/highlight' import HighlightType from "types/highlight";
import { MenuControl } from 'lib/hooks/useMenu' import { MenuControl } from "lib/hooks/useMenu";
const Note = (
const Note = ({ { control, onToggle, onClickHighlight, emitEvent, viewerRef }: Props,
control, ref: any
onToggle, ) => {
onClickHighlight, const highlights = useSelector<RootState, HighlightType[]>(
emitEvent, (state) => state.book.highlights
viewerRef );
}: Props, ref: any) => {
const highlights = useSelector<RootState, HighlightType[]>(state => state.book.highlights);
const [highlightList, setHighlightList] = useState<any[]>([]); const [highlightList, setHighlightList] = useState<any[]>([]);
/** Set highlight contents */ /** Set highlight contents */
useEffect(() => { useEffect(() => {
const Items = highlights.map(h => <Highlight key={h.key} const Items = highlights.map((h) => (
<Highlight
key={h.key}
highlight={h} highlight={h}
onClick={onClickHighlight} onClick={onClickHighlight}
emitEvent={emitEvent} emitEvent={emitEvent}
viewerRef={viewerRef} /> viewerRef={viewerRef}
); />
));
setHighlightList(Items); setHighlightList(Items);
}, [ }, [viewerRef, highlights, onClickHighlight, emitEvent, setHighlightList]);
viewerRef,
highlights,
onClickHighlight,
emitEvent,
setHighlightList
]);
return (
return (<> <>
{control.display && <Wrapper title="نشانه گذار" {control.display && (
<Wrapper
title="نشانه گذار"
show={control.open} show={control.open}
onClose={onToggle} onClose={onToggle}
ref={ref} > ref={ref}
>
<LearningLayout> <LearningLayout>
{highlightList.length > 0 {highlightList.length > 0 ? (
? highlightList highlightList
: <MenuEmpty text="! نشانه ای موجود نیست " /> ) : (
} <MenuEmpty text="! نشانه ای موجود نیست " />
)}
</LearningLayout> </LearningLayout>
</Wrapper>} </Wrapper>
</>); )}
} </>
);
};
interface Props { interface Props {
control: MenuControl; control: MenuControl;
@ -64,4 +64,4 @@ interface Props {
viewerRef: any; viewerRef: any;
} }
export default React.forwardRef(Note) export default React.forwardRef(Note);

@ -139,86 +139,11 @@ const Option = (
onClose={onToggle} onClose={onToggle}
ref={ref} ref={ref}
> >
{/* <OptionLayout> <OptionLayout>
<ControlIconBtnWrapper title="View Direction"> <Setting/>
<ControlIconBtn </OptionLayout>
type="ScrollHorizontal"
alt="Horizontal View"
active={true}
isSelected={isScrollHorizontal}
onClick={() => onClickDirection("Horizontal")}
/>
<ControlIconBtn
type="ScrollVertical"
alt="Vertical View"
active={true}
isSelected={!isScrollHorizontal}
onClick={() => onClickDirection("Vertical")}
/>
</ControlIconBtnWrapper>
<ControlIconBtnWrapper title="View Spread">
<ControlIconBtn
type="BookOpen"
alt="Two Page View"
active={viewType.active}
isSelected={viewType.spread}
onClick={() => onClickViewType(true)}
/>
<ControlIconBtn
type="BookClose"
alt="One Page View"
active={viewType.active}
isSelected={!viewType.spread}
onClick={() => onClickViewType(false)}
/>
</ControlIconBtnWrapper>
<OptionDropdown
title="Font"
defaultValue={fontFamily}
valueList={["Nazanin", "Sans"]}
onSelect={onSelectFontFamily}
/>
<OptionSlider
active={true}
title="Size"
minValue={8}
maxValue={36}
defaultValue={fontSize}
step={1}
onChange={(e) => onChangeSlider("FontSize", e)}
/>
<OptionSlider
active={true}
title="Line height"
minValue={1}
maxValue={3}
defaultValue={lineHeight}
step={0.1}
onChange={(e) => onChangeSlider("LineHeight", e)}
/>
<OptionSlider
active={true}
title="Horizontal margin"
minValue={0}
maxValue={100}
defaultValue={marginHorizontal}
step={1}
onChange={(e) => onChangeSlider("MarginHorizontal", e)}
/>
<OptionSlider
active={bookFlow === "paginated"}
title="Vertical margin"
minValue={0}
maxValue={100}
defaultValue={marginVertical}
step={1}
onChange={(e) => onChangeSlider("MarginVertical", e)}
/>
</OptionLayout> */}
<Setting
id="t"
/>
</Wrapper> </Wrapper>
)} )}
</> </>
@ -248,3 +173,80 @@ type ViewType = {
}; };
export default React.forwardRef(Option); export default React.forwardRef(Option);
{/* <ControlIconBtnWrapper title="View Direction">
<ControlIconBtn
type="ScrollHorizontal"
alt="Horizontal View"
active={true}
isSelected={isScrollHorizontal}
onClick={() => onClickDirection("Horizontal")}
/>
<ControlIconBtn
type="ScrollVertical"
alt="Vertical View"
active={true}
isSelected={!isScrollHorizontal}
onClick={() => onClickDirection("Vertical")}
/>
</ControlIconBtnWrapper>
<ControlIconBtnWrapper title="View Spread">
<ControlIconBtn
type="BookOpen"
alt="Two Page View"
active={viewType.active}
isSelected={viewType.spread}
onClick={() => onClickViewType(true)}
/>
<ControlIconBtn
type="BookClose"
alt="One Page View"
active={viewType.active}
isSelected={!viewType.spread}
onClick={() => onClickViewType(false)}
/>
</ControlIconBtnWrapper>
<OptionDropdown
title="Font"
defaultValue={fontFamily}
valueList={["Nazanin", "Sans"]}
onSelect={onSelectFontFamily}
/>
<OptionSlider
active={true}
title="Size"
minValue={8}
maxValue={36}
defaultValue={fontSize}
step={1}
onChange={(e) => onChangeSlider("FontSize", e)}
/>
<OptionSlider
active={true}
title="Line height"
minValue={1}
maxValue={3}
defaultValue={lineHeight}
step={0.1}
onChange={(e) => onChangeSlider("LineHeight", e)}
/>
<OptionSlider
active={true}
title="Horizontal margin"
minValue={0}
maxValue={100}
defaultValue={marginHorizontal}
step={1}
onChange={(e) => onChangeSlider("MarginHorizontal", e)}
/>
<OptionSlider
active={bookFlow === "paginated"}
title="Vertical margin"
minValue={0}
maxValue={100}
defaultValue={marginVertical}
step={1}
onChange={(e) => onChangeSlider("MarginVertical", e)}
/> */}

@ -84,7 +84,7 @@ const useHighlight = (
if (filtered.length > 0) { if (filtered.length > 0) {
dispatch(newSnackbar({ dispatch(newSnackbar({
text: "There are already registered highlights in your selection.", text: ".انتخاب شما از قبل نشانه گذاری شده است",
type: "WARNING" type: "WARNING"
})); }));
@ -193,7 +193,7 @@ const useHighlight = (
dispatch(popHighlight(key)); dispatch(popHighlight(key));
dispatch(newSnackbar({ dispatch(newSnackbar({
text: "The highlight removed successful!", text: " !نشانه گذاری شما با موفقیت حذف شد",
type: "INFO" type: "INFO"
})); }));

@ -36,12 +36,12 @@ const initialCurrentLocation: Page = {
/** 초기 색상표 상태 */ /** 초기 색상표 상태 */
const initialColorList: Color[] = [ const initialColorList: Color[] = [
{ name: 'Red', code: palette.red4 }, { name: 'قرمز', code: palette.red4 },
{ name: 'Orange', code: palette.orange4 }, { name: 'نارنجی', code: palette.orange4 },
{ name: 'Yellow', code: palette.yellow4 }, { name: 'زرد', code: palette.yellow4 },
{ name: 'Green', code: palette.green4 }, { name: 'سبز', code: palette.green4 },
{ name: 'Blue', code: palette.blue4 }, { name: 'آبی', code: palette.blue4 },
{ name: 'Purple', code: palette.purple4 } { name: 'بنفش', code: palette.purple4 }
] ]
const initialState: BookState = { const initialState: BookState = {

@ -1650,6 +1650,13 @@
react-is "^16.8.0 || ^17.0.0" react-is "^16.8.0 || ^17.0.0"
react-transition-group "^4.4.0" react-transition-group "^4.4.0"
"@material-ui/icons@^4.11.2":
version "4.11.2"
resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5"
integrity sha512-fQNsKX2TxBmqIGJCSi3tGTO/gZ+eJgWmMJkgDiOfyNaunNaxcklJQFaFogYcFl0qFuaEz1qaXYXboa/bUXVSOQ==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/styles@^4.11.4": "@material-ui/styles@^4.11.4":
version "4.11.4" version "4.11.4"
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d" resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d"

Loading…
Cancel
Save