Compare commits

..

1 Commits

Author SHA1 Message Date
0204a379f7 Merge pull request 'add view switcher' (#1) from pov into master 2020-05-08 17:02:50 +07:00
9 changed files with 94 additions and 236 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "image-viewer",
"version": "0.1.3",
"version": "0.1.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "image-viewer",
"version": "0.1.3",
"version": "0.1.2",
"private": false,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",

View File

@@ -1,8 +1,5 @@
import React, { createContext } from 'react'
import React, {createContext} from 'react'
import OnePageView from './OnePageView'
import TwoPagesView from './TwoPagesView.jsx'
import BookComponentsController from './BookComponentsController'
import './index.css'
export const BookContext = createContext({})
@@ -12,22 +9,16 @@ export interface IBookPage {
thumbnail?: string
}
export const getBookViewModes = (currIndex = 0, srcLength = 0) => [
export const bookViewModes = [
{
text: 'One Page',
value: 'one page',
icon: '',
pagesToTurn: 1,
nextDisable: currIndex >= srcLength - 1,
prevDisable: currIndex === 0,
icon: ''
},
{
text: 'Two Pages',
value: 'two pages',
icon: '',
pagesToTurn: 2,
nextDisable: currIndex >= srcLength - 2,
prevDisable: currIndex <= 1,
icon: ''
}
]
@@ -41,51 +32,33 @@ export interface IBookContext {
src?: IBookPage[],
flagToReverse?: boolean,
mode?: BookViewMode,
readonly setBookContextState: (newState: any) => undefined
readonly setBookContextState: (newState: any)=> undefined
}
const getPagesCountToTurn = (mode: string) => {
let pagesToTurn = getBookViewModes().find(item => item.value === mode)?.pagesToTurn
return pagesToTurn || 0
}
class BookComponent extends React.Component<any, any>{
constructor(props: any) {
class BookComponent extends React.Component<any>{
constructor(props: any){
super(props)
this.state = {
currIndex: 0,
src: [],
flagToReverse: false,
mode: "one page",
setBookContextState: (newState: IBookContext) => {
this.setState((oldState: any) => ({
setBookContextState: (newState: IBookContext)=>{
this.setState(oldState=> ({
...oldState,
...newState
}))
},
changePage: this.changePage,
pagesToTurn: 1,
}
}
}
}
changePage = (goToPrevious: boolean, indexToGo?: number) => {
const { mode, currIndex } = this.state
const pagesCountToTurn = indexToGo || getPagesCountToTurn(mode)
const prefixPageCount = goToPrevious ? -1 : 1
this.setState({
currIndex: (pagesCountToTurn * prefixPageCount) + currIndex,
flagToReverse: goToPrevious
})
}
componentDidMount() {
fetch('https://picsum.photos/v2/list?limit=30')
.then(response => {
componentDidMount(){
fetch('https://picsum.photos/v2/list')
.then(response=>{
return response.json()
}).then(data => {
data = data.map((item: any) => {
}).then(data=>{
data = data.map((item: any)=>{
return {
src: item.download_url,
alt: item.author
@@ -97,39 +70,13 @@ class BookComponent extends React.Component<any, any>{
})
})
}
getControlsDisable = () => {
const { mode, currIndex, src } = this.state
const { nextDisable, prevDisable } = getBookViewModes(currIndex, src.length).find(item => item.value === mode) || {
nextDisable: true,
prevDisable: true
}
return {
nextDisable,
prevDisable
}
}
render() {
const { mode } = this.state
return <BookContext.Provider value={{ ...this.state }}>
render(){
return <BookContext.Provider value={{...this.state}}>
<BookContext.Consumer>
{state => (
{state=>(
<>
{mode === "one page" && <OnePageView {...state} />}
{mode === "two pages" && <TwoPagesView {...state} />}
<BookComponentsController
onPrevClick={() => {
this.changePage(true)
}}
onNextClick={() => {
this.changePage(false)
}}
{...this.getControlsDisable()}
{...this.state}
/>
<OnePageView {...state}/>
</>
)}
</BookContext.Consumer>

View File

@@ -1,48 +1,45 @@
import React from 'react'
import styled from 'styled-components'
import {Button} from 'antd'
import {Button, Popover} from 'antd'
import ViewSwitcher from './ViewSwitcher'
import JumpControls from './JumpControls'
import {
CaretLeftOutlined,
CaretRightOutlined,
} from '@ant-design/icons';
const ControlsContanter = styled.div`
display: inline-flex;
justify-content: space-between;
width: 100%;
width: 500px;
`
export default function BookCoponentsController(props: any) {
const {onPrevClick, prevDisable, onNextClick, nextDisable, ...restProps} = props
return (
<ControlsContanter>
<JumpControls {...restProps}/>
<Button
onClick={()=>{
onPrevClick()
}}
disabled={prevDisable}
style={{
marginRight: 8
}}
icon={<CaretLeftOutlined />}
/>
>
Previous
</Button>
<Button
onClick={()=>{
onNextClick()
}}
disabled={nextDisable}
style={{
marginRight: 8
}}
icon={<CaretRightOutlined />}
>
Next
</Button>
<Popover
trigger="click"
content={<JumpControls {...restProps}/>}>
<Button>
Jump
</Button>
</Popover>
<ViewSwitcher {...restProps}/>
</ControlsContanter>

View File

@@ -1,23 +1,14 @@
import React, {useState, useCallback, useEffect} from 'react'
import {Slider, InputNumber} from 'antd'
import styled from 'styled-components'
const Container = styled.div`
width: 100%;
display: inline-flex;
`
import {Row, Col, Slider, InputNumber} from 'antd'
const JumpControls = (props: any)=>{
const {src, currIndex, setBookContextState, pagesToTurn} = props
const {src, currIndex, setBookContextState} = props
const [inputValue, setInputValue] = useState(currIndex)
const [sliderValue, setSliderValue] = useState(currIndex)
const onChange = useCallback(
(value) => {
if(value){
const newCurrIndex = value -1
if(newCurrIndex < src.length && newCurrIndex >=0){
setBookContextState({
currIndex: value - 1,
@@ -26,43 +17,31 @@ const JumpControls = (props: any)=>{
}
}
},
[inputValue, src.length, setBookContextState])
[inputValue])
useEffect(()=>{
setInputValue(currIndex + 1)
}, [currIndex])
useEffect(() => {
setSliderValue(inputValue)
}, [inputValue])
const sliderRestProps = ()=>{
return sliderValue === inputValue? {}: {value: inputValue}
}
return <Container>
return <Row>
<Col span={12}>
<Slider
style={{
flexGrow: 1
}}
min={1}
max={src.length}
onAfterChange={onChange}
step={pagesToTurn}
defaultValue={typeof inputValue === 'number' ? inputValue : 1}
{...sliderRestProps()}
onChange={onChange}
value={typeof inputValue === 'number' ? inputValue : 0}
/>
</Col>
<Col span={4}>
<InputNumber
className={"jump-control"}
min={1}
max={src.length}
style={{ margin: '0 8px' }}
value={inputValue}
onChange={onChange}
/>
</Container>
</Col>
</Row>
}
export default JumpControls

View File

@@ -1,6 +1,7 @@
import React from 'react'
import React, {useState, useCallback, useEffect} from 'react'
import {Transition, animated} from 'react-spring/renderprops'
import styled, { } from 'styled-components'
import BookComponentsController from './BookComponentsController'
const ComponentContainer = styled.div`
display: flex;
@@ -23,9 +24,10 @@ const PageContainer = styled.div`
height: 100%;
}
`
const StyledImage = styled(animated.div)<{src:string, width:number | string, height: number| string}>`
width: ${props=> props.width? props.width: '100%'};
height: ${props=> props.height? props.height: '100%'};
const StyledImage = styled(animated.div)<{src:string}>`
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
@@ -34,13 +36,30 @@ const StyledImage = styled(animated.div)<{src:string, width:number | string, hei
`
const PageComponent = (props: any)=>{
const {src, width, height} = props
return <StyledImage src={src} width={width} height={height}/>
const {src} = props
return <StyledImage src={src}/>
}
function BookComponent(props: any) {
const {src, currIndex, flagToReverse} = props
const {src, currIndex, setBookContextState, flagToReverse} = props
const [prevDisable, setPrevDisable] = useState(true)
const [nextDisable, setNextDisable] = useState(false)
const changePage = useCallback((indexToGo, goToPrevious)=>{
const newCurrIndex = currIndex + indexToGo
setBookContextState({
currIndex: newCurrIndex,
flagToReverse: goToPrevious
})
}, [currIndex])
useEffect(() => {
setNextDisable(currIndex >= src.length - 1)
setPrevDisable(currIndex <= 0)
}, [currIndex, src])
return (
<ComponentContainer>
<PageContainer>
@@ -58,6 +77,8 @@ function BookComponent(props: any) {
return(
<animated.div style={{
...style,
overflowX: 'hidden'
}}>
{React.createElement(()=><PageComponent src={src[currIndex]? src[currIndex].src: null}/>)}
</animated.div>
@@ -66,8 +87,17 @@ function BookComponent(props: any) {
}
</Transition>
</PageContainer>
<BookComponentsController
prevDisable={prevDisable}
onPrevClick={()=> changePage(-1, true)}
nextDisable={nextDisable}
onNextClick={()=>changePage(1, false)}
{...props}
/>
</ComponentContainer>
)
}
export {ComponentContainer, PageContainer, PageComponent, BookComponent as default }
export default BookComponent

View File

@@ -1,91 +0,0 @@
import React, { useState, useEffect } from 'react';
import { useTransition, animated } from 'react-spring';
import {
ComponentContainer,
PageComponent,
PageContainer,
} from './OnePageView';
export default function TwoPagesView(props) {
let { src, currIndex, flagToReverse } = props;
const [pagesToshow, setPagesToShow] = useState([
{
src: '',
alt: '',
thumbnail: '',
},
{
src: '',
alt: '',
thumbnail: '',
},
]);
const transitionFirst = useTransition(pagesToshow[0], (item) => item.src, {
from: {opacity: 1, transform: flagToReverse? "translate3d(-50%, 0, 0)": "translate3d(100%, 0, 0)"},
enter: { opacity: 1, transform: "translate3d(0%, 0, 0)" },
leave: { opacity: 0, transform: flagToReverse? "translate3d(100%, 0, 0)": "translate3d(-50%, 0, 0)"},
});
const transitionSecond = useTransition(pagesToshow[1], (item) => item.src, {
from: {opacity: 1, transform: flagToReverse? "translate3d(0%, 0, 0)": "translate3d(150%, 0, 0)"},
enter: { opacity: 1, transform: "translate3d(50%, 0, 0)" },
leave: { opacity: 0, transform: flagToReverse? "translate3d(150%, 0, 0)": "translate3d(0%, 0, 0)"},
});
useEffect(() => {
const shouldShowPage = (pageIndex) => {
return src.length > 0 && pageIndex <= src.length - 1;
};
const newCurrIndex = currIndex % 2 === 0 ? currIndex : currIndex - 1;
const shouldShowFirstPage = shouldShowPage(newCurrIndex);
if (shouldShowFirstPage) {
const shouldShowSecondPage = shouldShowPage(newCurrIndex + 1);
setPagesToShow([
src[newCurrIndex],
shouldShowSecondPage
? src[newCurrIndex + 1]
: {
src: '',
alt: '',
thumbnail: '',
},
]);
}
}, [currIndex, src]);
return (
<ComponentContainer>
<PageContainer>
{transitionFirst.map(({ item, key, props }) => {
return (
item && (
<animated.div style={{ ...props }} key={key}>
<PageComponent width={'50%'} src={item.src} />
</animated.div>
)
);
})}
{transitionSecond.map(({ item, key, props }) => {
return (
item && (
<animated.div
style={{
...props,
transformOrigin: 'left center',
}}
key={key}
>
<PageComponent width={'50%'} src={item.src} />
</animated.div>
)
);
})}
</PageContainer>
</ComponentContainer>
);
}

View File

@@ -1,6 +1,6 @@
import React, {useCallback} from 'react'
import {Select} from 'antd'
import {IBookContext, getBookViewModes} from './BookComponent'
import {IBookContext, bookViewModes} from './BookComponent'
const {Option} = Select
@@ -10,16 +10,15 @@ export default function ViewSwitcher(props: IBookContext) {
const viewModeChange = useCallback((mode) => {
setBookContextState({
mode,
pagesToTurn: getBookViewModes().find(item=> item.value === mode)?.pagesToTurn || 0
mode
})
},[setBookContextState])
},[])
return (
<Select value={mode} onSelect={viewModeChange}>
{
getBookViewModes().map((item: any)=>{
return <Option value={item.value} key={item.value}>{item.text}</Option>
bookViewModes.map((item: any)=>{
return <Option value={item.value}>{item.text}</Option>
})
}
</Select>

View File

@@ -1,3 +0,0 @@
.jump-control .ant-input-number-handler-wrap{
display: none;
}