Update Components

demo
NB 4 years ago
parent 802d8d8c08
commit eb0dc1fdf2
  1. 5
      README.md
  2. 1
      package.json
  3. BIN
      public/images/logo.png
  4. BIN
      public/images/reactEpubIcon.png
  5. 7
      src/components/footer/Item.tsx
  6. 3
      src/components/header/Layout.tsx
  7. 13
      src/components/header/Logo.tsx
  8. 34
      src/components/learning/SortedBtn.tsx
  9. 10
      src/components/learning/SortedBtnWrapper.tsx
  10. 4
      src/components/nav/NavItem.tsx
  11. 0
      src/components/note/Layout.tsx
  12. 0
      src/components/note/highlight/PageNum.tsx
  13. 0
      src/components/note/highlight/Post.tsx
  14. 0
      src/components/note/highlight/Title.tsx
  15. 0
      src/components/note/highlight/Wrapper.tsx
  16. 0
      src/components/option/ControlIconBtn.tsx
  17. 29
      src/components/option/ControlIconBtnWrapper.tsx
  18. 7
      src/components/sideMenu/Wrapper.tsx
  19. 97
      src/containers/Header.tsx
  20. 6
      src/containers/Reader.tsx
  21. 43
      src/containers/menu/Note.tsx
  22. 85
      src/containers/menu/Option.tsx
  23. 8
      src/containers/menu/commons/Highlight.tsx
  24. 2
      src/index.tsx
  25. 4
      src/lib/styles/viewerLayout.ts

@ -1,11 +1,10 @@
<div align=left> <div align=center>
<br /> <br />
<a href="https://github.com/altmshfkgudtjr/react-epub-viewer"><img src="https://user-images.githubusercontent.com/47492535/115000155-73a3ae80-9edd-11eb-8ae9-a19e1c012d35.png" alt="react_epub" style="zoom:50%;" /></a> <a href="https://github.com/altmshfkgudtjr/react-epub-viewer"><img src="https://user-images.githubusercontent.com/47492535/115026922-02c0be80-9efe-11eb-8c0c-40379e3249d1.png" alt="react_epub" style="zoom:50%;" /></a>
<br /> <br />
<br /> <br />
</div> </div>
# React-Epub-Viewer # React-Epub-Viewer
[![Latest Stable Version](https://img.shields.io/npm/v/react-epub-viewer.svg?style=for-the-badge)](https://www.npmjs.com/package/react-epub-viewer) [![License](https://img.shields.io/badge/license-mit-red.svg?style=for-the-badge)](https://www.npmjs.com/package/react-epub-viewer) [![Latest Stable Version](https://img.shields.io/npm/v/react-epub-viewer.svg?style=for-the-badge)](https://www.npmjs.com/package/react-epub-viewer) [![License](https://img.shields.io/badge/license-mit-red.svg?style=for-the-badge)](https://www.npmjs.com/package/react-epub-viewer)

@ -18,7 +18,6 @@
"lib", "lib",
"README.md" "README.md"
], ],
"homepage": "https://altmshfkgudtjr.github.io/react-epub-viewer",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/altmshfkgudtjr/react-epub-viewer" "url": "https://github.com/altmshfkgudtjr/react-epub-viewer"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

@ -16,8 +16,15 @@ const Container = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
& > span { & > span {
color: ${palette.gray4}; color: ${palette.gray4};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
`; `;

@ -9,8 +9,7 @@ const Layout = styled.div`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 54px; height: 64px;
background-color: ${palette.white};
z-index: ${zIndex.header}; z-index: ${zIndex.header};
`; `;

@ -2,11 +2,12 @@ import styled from 'styled-components'
// lib // lib
import * as styles from 'lib/styles/styles' import * as styles from 'lib/styles/styles'
import palette from 'lib/styles/palette' import palette from 'lib/styles/palette'
import { mediaQuery } from 'lib/styles/media'
const Logo = () => { const Logo = () => {
return ( return (
<Wrapper href="https://github.com/altmshfkgudtjr/react-epub-viewer" target="__blank__"> <Wrapper href="https://github.com/altmshfkgudtjr/react-epub-viewer" target="__blank__">
<Img src="react-epub-viewer/images/reactEpubIcon.png" alt="Logo" /> <Img src="/images/logo.png" alt="Logo" />
</Wrapper> </Wrapper>
); );
} }
@ -18,18 +19,22 @@ const Wrapper = styled.a`
outline: none; outline: none;
margin-left: 16px; margin-left: 16px;
background-color: rgba(0,0,0,0); background-color: rgba(0,0,0,0);
transition: .3s ${styles.transition}; transition: .2s ${styles.transition};
border-radius: 8px; border-radius: 8px;
padding: 4px 8px; padding: 4px 8px;
cursor: pointer; cursor: pointer;
${mediaQuery(700)} {
display: none;
}
&:focus, &:hover { &:focus, &:hover {
background-color: ${palette.blue1}; background-color: ${palette.blue0};
} }
`; `;
const Img = styled.img` const Img = styled.img`
height: 32px; height: 44px;
`; `;
export default Logo export default Logo

@ -1,34 +0,0 @@
import styled from 'styled-components'
// lib
import * as styles from 'lib/styles/styles'
import palette from 'lib/styles/palette'
const SortedBtn = ({ text, isSelected, onClick }: Props) => {
return <Btn isSelected={isSelected} onClick={onClick}>{text}</Btn>;
}
const Btn = styled.button<{isSelected: boolean}>`
height: 20px;
line-height: 20px;
font-size: 14px;
cursor: pointer;
text-align: center;
margin-left: 16px;
color: ${({isSelected}) => isSelected
? palette.blue3
: palette.gray4
};
transition: .2s ${styles.transition};
&:hover {
color: ${palette.blue3};
}
`;
interface Props {
text: string;
isSelected: boolean;
onClick: () => void;
}
export default SortedBtn

@ -1,10 +0,0 @@
import styled from 'styled-components'
const SortedBtnWrapper = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 24px 24px 24px;
`;
export default SortedBtnWrapper

@ -33,6 +33,10 @@ const Content = styled.button`
margin-left: 12px; margin-left: 12px;
} }
} }
&:last-child {
margin-bottom: 32px;
}
`; `;
interface Props { interface Props {

@ -0,0 +1,29 @@
import styled from 'styled-components'
// components
import OptionWrapper from 'components/option/OptionWrapper'
import OptionTitle from 'components/option/OptionTitle'
const ControlIconBtnWrapper = ({ title, children }: Props) => {
return (
<OptionWrapper>
<OptionTitle>{title}</OptionTitle>
<BtnWrapper>
{children}
</BtnWrapper>
</OptionWrapper>
);
}
const BtnWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-around;
padding: 8px 0;
`;
interface Props {
title: string;
children: any[]
}
export default ControlIconBtnWrapper

@ -24,6 +24,7 @@ const Container = styled.div`
flex-direction: column; flex-direction: column;
position: fixed; position: fixed;
width: 340px; width: 340px;
max-width: 95vw;
height: 100vh; height: 100vh;
top: 0; top: 0;
right: 0; right: 0;
@ -33,9 +34,9 @@ const Container = styled.div`
border-radius: 16px 0 0 16px; border-radius: 16px 0 0 16px;
transform: ${({ show }: { show: boolean }) => show transform: ${({ show }: { show: boolean }) => show
? `translateX(0px) scale(1)` ? `translateX(0px) scale(1)`
: `translateX(420px) scale(.8)` : `translateX(420px) scale(.9)`
}; };
transition: .3s ${styles.transition}; transition: .4s ${styles.transition};
overflow-y: auto; overflow-y: auto;
${styles.scrollbar()}; ${styles.scrollbar()};
`; `;
@ -46,7 +47,7 @@ const Header = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding-top: 30px; padding-top: 4px;
& > span { & > span {
padding-left: 24px; padding-left: 24px;

@ -1,109 +1,24 @@
import { useState } from 'react'
// components // components
import Wrapper from 'components/header/Wrapper' import Wrapper from 'components/header/Wrapper'
import Layout, { AutoLayout } from 'components/header/Layout' import Layout, { AutoLayout } from 'components/header/Layout'
import Logo from 'components/header/Logo' import Logo from 'components/header/Logo'
import ControlBtn from 'components/header/ControlBtn' import ControlBtn from 'components/header/ControlBtn'
import ControlIconBtn from 'components/header/ControlIconBtn'
import VerticalLine from 'components/header/VerticalLine'
// types
import { BookOption } from 'types/book'
const Header = ({ const Header = ({
title,
bookOption,
onNavToggle, onNavToggle,
onOptionToggle, onOptionToggle,
onLearningToggle, onLearningToggle
onBookOptionChange
}: Props) => { }: Props) => {
const [isScrollHorizontal, setIsScrollHorizontal] = useState<boolean>(true);
const [viewType, setViewType] = useState<ViewType>({
active: true,
spread: true
});
/**
* Select view direction
* @param type Direction
*/
const onClickDirection = (type: "Horizontal" | "Vertical") => {
if (type === "Horizontal") {
setIsScrollHorizontal(true);
setViewType({ ...viewType, active: true });
onBookOptionChange({
...bookOption,
flow: "paginated"
});
} else {
setIsScrollHorizontal(false);
setViewType({ ...viewType, active: false });
onBookOptionChange({
...bookOption,
flow: "scrolled-doc"
});
}
}
/**
* Select isSpread
* @param isSpread Whether spread view
*/
const onClickViewType = (isSpread: boolean) => {
if (isSpread) {
setViewType({ ...viewType, spread: true });
onBookOptionChange({
...bookOption,
spread: "auto"
});
} else {
setViewType({ ...viewType, spread: false });
onBookOptionChange({
...bookOption,
spread: "none"
});
}
}
return ( return (
<Wrapper> <Wrapper>
<Layout> <Layout>
<AutoLayout> <AutoLayout>
<Logo />
<div> <div>
<Logo />
<ControlBtn message="Contents" onClick={onNavToggle} /> <ControlBtn message="Contents" onClick={onNavToggle} />
<ControlBtn message="Setting" onClick={onOptionToggle} /> <ControlBtn message="Setting" onClick={onOptionToggle} />
<ControlBtn message="Highlights" onClick={onLearningToggle} /> <ControlBtn message="Highlights" onClick={onLearningToggle} />
</div> </div>
<div>
<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")} />
<VerticalLine />
<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)} />
</div>
</AutoLayout> </AutoLayout>
</Layout> </Layout>
</Wrapper> </Wrapper>
@ -111,17 +26,9 @@ const Header = ({
} }
interface Props { interface Props {
title: string;
bookOption: BookOption;
onNavToggle: (value?: boolean) => void; onNavToggle: (value?: boolean) => void;
onOptionToggle: (value?: boolean) => void; onOptionToggle: (value?: boolean) => void;
onLearningToggle: (value?: boolean) => void; onLearningToggle: (value?: boolean) => void;
onBookOptionChange: (bookOption: BookOption) => void;
}
type ViewType = {
active: boolean,
spread: boolean
} }
export default Header export default Header

@ -32,7 +32,6 @@ import Toc from 'types/toc'
const Reader = ({ url, loadingView }: Props) => { const Reader = ({ url, loadingView }: Props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const book = useSelector<RootState, Book>(state => state.book.book);
const currentLocation = useSelector<RootState, Page>(state => state.book.currentLocation); const currentLocation = useSelector<RootState, Page>(state => state.book.currentLocation);
const viewerRef = useRef<ViewerRef | null>(null); const viewerRef = useRef<ViewerRef | null>(null);
@ -138,12 +137,9 @@ const Reader = ({ url, loadingView }: Props) => {
return (<> return (<>
<ViewerWrapper> <ViewerWrapper>
<Header <Header
title={book.title}
bookOption={bookOption}
onNavToggle={onNavToggle} onNavToggle={onNavToggle}
onOptionToggle={onOptionToggle} onOptionToggle={onOptionToggle}
onLearningToggle={onLearningToggle} onLearningToggle={onLearningToggle}
onBookOptionChange={onBookOptionChange}
/> />
<Viewer <Viewer
@ -177,10 +173,12 @@ const Reader = ({ url, loadingView }: Props) => {
<Option <Option
control={optionControl} control={optionControl}
bookStyle={bookStyle} bookStyle={bookStyle}
bookOption={bookOption}
bookFlow={bookOption.flow} bookFlow={bookOption.flow}
onToggle={onOptionToggle} onToggle={onOptionToggle}
emitEvent={emitEvent} emitEvent={emitEvent}
onBookStyleChange={onBookStyleChange} onBookStyleChange={onBookStyleChange}
onBookOptionChange={onBookOptionChange}
ref={optionRef} ref={optionRef}
/> />

@ -4,9 +4,7 @@ import { useSelector } from 'react-redux'
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/learning/Layout' import LearningLayout from 'components/note/Layout'
import SortedBtnWrapper from 'components/learning/SortedBtnWrapper'
import SortedBtn from 'components/learning/SortedBtn'
import MenuEmpty from 'components/sideMenu/MenuEmpty' import MenuEmpty from 'components/sideMenu/MenuEmpty'
// types // types
import { RootState } from 'slices' import { RootState } from 'slices'
@ -22,36 +20,20 @@ const Note = ({
viewerRef viewerRef
}: Props, ref: any) => { }: Props, ref: any) => {
const highlights = useSelector<RootState, HighlightType[]>(state => state.book.highlights); const highlights = useSelector<RootState, HighlightType[]>(state => state.book.highlights);
const [sorted, setSorted] = useState<Sorted>("CART");
const [highlightList, setHighlightList] = useState<any[]>([]); const [highlightList, setHighlightList] = useState<any[]>([]);
/**
* Select sort type
* @param type Viewed or Created
*/
const onSortedClick = (type: Sorted) => setSorted(type);
/** Set highlight contents */ /** Set highlight contents */
useEffect(() => { useEffect(() => {
const sortedKey = sorted === "LOOK" const Items = highlights.map(h => <Highlight key={h.key}
? "accessTime" highlight={h}
: "createTime"; onClick={onClickHighlight}
emitEvent={emitEvent}
const highlightList = highlights.slice().sort(function (a, b) { viewerRef={viewerRef} />
return new Date(b[sortedKey]).getTime() - new Date(a[sortedKey]).getTime();
});
const Items = highlightList.map(h => <Highlight key={h.key}
highlight={h}
onClick={onClickHighlight}
emitEvent={emitEvent}
viewerRef={viewerRef} />
); );
setHighlightList(Items); setHighlightList(Items);
}, [ }, [
viewerRef, viewerRef,
sorted,
highlights, highlights,
onClickHighlight, onClickHighlight,
emitEvent, emitEvent,
@ -65,19 +47,6 @@ const Note = ({
onClose={onToggle} onClose={onToggle}
ref={ref} > ref={ref} >
<LearningLayout> <LearningLayout>
<SortedBtnWrapper>
<SortedBtn
text="viewed"
isSelected={sorted === "LOOK"}
onClick={() => onSortedClick("LOOK")}
/>
<SortedBtn
text="created"
isSelected={sorted === "CART"}
onClick={() => onSortedClick("CART")}
/>
</SortedBtnWrapper>
{highlightList.length > 0 {highlightList.length > 0
? highlightList ? highlightList
: <MenuEmpty text="Empty highlights!" /> : <MenuEmpty text="Empty highlights!" />

@ -5,23 +5,33 @@ import Wrapper from 'components/sideMenu/Wrapper'
import OptionLayout from 'components/option/Layout' import OptionLayout from 'components/option/Layout'
import OptionDropdown from 'components/option/Dropdown' import OptionDropdown from 'components/option/Dropdown'
import OptionSlider from 'components/option/Slider' import OptionSlider from 'components/option/Slider'
import ControlIconBtnWrapper from 'components/option/ControlIconBtnWrapper'
import ControlIconBtn from 'components/option/ControlIconBtn'
// types // types
import { BookStyle, BookFontFamily, BookFlow } from 'types/book' import { BookStyle, BookFontFamily, BookFlow } from 'types/book'
import { MenuControl } from 'lib/hooks/useMenu' import { MenuControl } from 'lib/hooks/useMenu'
import { BookOption } from 'types/book'
const Option = ({ const Option = ({
control, control,
bookStyle, bookStyle,
bookOption,
bookFlow, bookFlow,
onToggle, onToggle,
emitEvent, emitEvent,
onBookStyleChange onBookStyleChange,
onBookOptionChange
}: Props, ref: any) => { }: Props, ref: any) => {
const [fontFamily, setFontFamily] = useState<BookFontFamily>(bookStyle.fontFamily); const [fontFamily, setFontFamily] = useState<BookFontFamily>(bookStyle.fontFamily);
const [fontSize, setFontSize] = useState<number>(bookStyle.fontSize); const [fontSize, setFontSize] = useState<number>(bookStyle.fontSize);
const [lineHeight, setLineHeight] = useState<number>(bookStyle.lineHeight); const [lineHeight, setLineHeight] = useState<number>(bookStyle.lineHeight);
const [marginHorizontal, setMarginHorizontal] = useState<number>(bookStyle.marginHorizontal); const [marginHorizontal, setMarginHorizontal] = useState<number>(bookStyle.marginHorizontal);
const [marginVertical, setMarginVertical] = useState<number>(bookStyle.marginVertical); const [marginVertical, setMarginVertical] = useState<number>(bookStyle.marginVertical);
const [isScrollHorizontal, setIsScrollHorizontal] = useState<boolean>(true);
const [viewType, setViewType] = useState<ViewType>({
active: true,
spread: true
});
/** Change font family */ /** Change font family */
@ -48,6 +58,48 @@ const Option = ({
} }
} }
/**
* Select view direction
* @param type Direction
*/
const onClickDirection = (type: "Horizontal" | "Vertical") => {
if (type === "Horizontal") {
setIsScrollHorizontal(true);
setViewType({ ...viewType, active: true });
onBookOptionChange({
...bookOption,
flow: "paginated"
});
} else {
setIsScrollHorizontal(false);
setViewType({ ...viewType, active: false });
onBookOptionChange({
...bookOption,
flow: "scrolled-doc"
});
}
}
/**
* Select isSpread
* @param isSpread Whether spread view
*/
const onClickViewType = (isSpread: boolean) => {
if (isSpread) {
setViewType({ ...viewType, spread: true });
onBookOptionChange({
...bookOption,
spread: "auto"
});
} else {
setViewType({ ...viewType, spread: false });
onBookOptionChange({
...bookOption,
spread: "none"
});
}
}
/* Save userdata */ /* Save userdata */
// TODO Fix the infinite re-rendering issue, when inlcude `onBookStyleChange` to dependencies array. // TODO Fix the infinite re-rendering issue, when inlcude `onBookStyleChange` to dependencies array.
@ -83,6 +135,30 @@ const Option = ({
onClose={onToggle} onClose={onToggle}
ref={ref}> ref={ref}>
<OptionLayout> <OptionLayout>
<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" <OptionDropdown title="Font"
defaultValue={fontFamily} defaultValue={fontFamily}
valueList={["Origin", "Roboto"]} valueList={["Origin", "Roboto"]}
@ -123,10 +199,12 @@ const Option = ({
interface Props { interface Props {
control: MenuControl; control: MenuControl;
bookStyle: BookStyle; bookStyle: BookStyle;
bookOption: BookOption;
bookFlow: BookFlow; bookFlow: BookFlow;
onToggle: () => void; onToggle: () => void;
emitEvent: () => void; emitEvent: () => void;
onBookStyleChange: (bookStyle: BookStyle) => void; onBookStyleChange: (bookStyle: BookStyle) => void;
onBookOptionChange: (bookOption: BookOption) => void;
} }
type SliderType = "FontSize" type SliderType = "FontSize"
@ -134,4 +212,9 @@ type SliderType = "FontSize"
| "MarginHorizontal" | "MarginHorizontal"
| "MarginVertical"; | "MarginVertical";
type ViewType = {
active: boolean,
spread: boolean
}
export default React.forwardRef(Option) export default React.forwardRef(Option)

@ -1,9 +1,9 @@
import { useDispatch } from 'react-redux' import { useDispatch } from 'react-redux'
// components // components
import Wrapper from 'components/learning/highlight/Wrapper' import Wrapper from 'components/note/highlight/Wrapper'
import Title from 'components/learning/highlight/Title' import Title from 'components/note/highlight/Title'
import Post from 'components/learning/highlight/Post' import Post from 'components/note/highlight/Post'
import PageNum from 'components/learning/highlight/PageNum' import PageNum from 'components/note/highlight/PageNum'
// slices // slices
import { updateHighlight } from 'slices/book' import { updateHighlight } from 'slices/book'
// utils // utils

@ -1,6 +1,6 @@
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Reader from 'containers/Reader' import Reader from 'containers/Reader'
const EPUB_URL = "react-epub-viewer/files/Alices Adventures in Wonderland.epub"; const EPUB_URL = "/files/Alices Adventures in Wonderland.epub";
ReactDOM.render(<Reader url={EPUB_URL} />, document.getElementById('root')); ReactDOM.render(<Reader url={EPUB_URL} />, document.getElementById('root'));

@ -2,9 +2,9 @@ import ViewerLayout from 'types/viewerLayout'
/** Epub viewer layout size */ /** Epub viewer layout size */
const viewerLayout: ViewerLayout = { const viewerLayout: ViewerLayout = {
MIN_VIEWER_WIDTH: 600, MIN_VIEWER_WIDTH: 300,
MIN_VIEWER_HEIGHT: 300, MIN_VIEWER_HEIGHT: 300,
VIEWER_HEADER_HEIGHT: 54, VIEWER_HEADER_HEIGHT: 64,
VIEWER_FOOTER_HEIGHT: 60, VIEWER_FOOTER_HEIGHT: 60,
VIEWER_SIDEMENU_WIDTH: 0 VIEWER_SIDEMENU_WIDTH: 0
} }

Loading…
Cancel
Save