Work on layout
This commit is contained in:
parent
705aaeba0e
commit
e09d80c9fc
|
@ -1,22 +1,23 @@
|
|||
import React, { Component } from 'react'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import { createGlobalStyle } from 'styled-components'
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
#root, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
`
|
||||
|
||||
import Routes from './Routes'
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
if (!token) {
|
||||
return (
|
||||
<>
|
||||
<Redirect to="/login" />
|
||||
<Routes />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return <Routes />
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<Routes />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import React, { Component } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const Container = styled.div`
|
||||
height: 100%;
|
||||
/* display: grid;
|
||||
grid-template-columns: 80px 1fr 500px; */
|
||||
`
|
||||
|
||||
const LeftSidebar = styled.div`
|
||||
height: 100%;
|
||||
width: 80px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #eee;
|
||||
`
|
||||
|
||||
const RightSidebar = styled.div`
|
||||
height: 100%;
|
||||
width: 500px;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background-color: #eee;
|
||||
`
|
||||
|
||||
const Content = styled.div`
|
||||
margin-left: 80px;
|
||||
margin-right: 500px;
|
||||
padding: 0 8px;
|
||||
`
|
||||
|
||||
class Layout extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<LeftSidebar>Left</LeftSidebar>
|
||||
<Content>{this.props.children}</Content>
|
||||
<RightSidebar>Right</RightSidebar>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Layout
|
|
@ -1,230 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import gql from 'graphql-tag'
|
||||
import { Query } from 'react-apollo'
|
||||
import styled from 'styled-components'
|
||||
import { useSpring, animated } from 'react-spring'
|
||||
|
||||
const albumQuery = gql`
|
||||
query albumQuery($id: ID) {
|
||||
album(id: $id) {
|
||||
title
|
||||
photos {
|
||||
id
|
||||
thumbnail {
|
||||
path
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Gallery = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Photo = styled.img`
|
||||
margin: 4px;
|
||||
background-color: #eee;
|
||||
display: inline-block;
|
||||
height: 200px;
|
||||
flex-grow: 1;
|
||||
object-fit: cover;
|
||||
|
||||
&:nth-last-child(6) ~ img {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
${props =>
|
||||
props.active &&
|
||||
`
|
||||
will-change: transform;
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
`}
|
||||
`
|
||||
|
||||
const Dimmer = ({ onClick, active }) => {
|
||||
const [props, set, stop] = useSpring(() => ({ opacity: 0 }))
|
||||
|
||||
set({
|
||||
opacity: active ? 1 : 0,
|
||||
})
|
||||
|
||||
const AnimatedDimmer = styled(animated.div)`
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
`
|
||||
|
||||
return (
|
||||
<AnimatedDimmer
|
||||
onClick={onClick}
|
||||
style={{
|
||||
...props,
|
||||
pointerEvents: active ? 'auto' : 'none',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
class AlbumPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
activeImage: -1,
|
||||
}
|
||||
|
||||
this.setActiveImage = this.setActiveImage.bind(this)
|
||||
this.animateImage = this.animateImage.bind(this)
|
||||
|
||||
this.photoAmount = 1
|
||||
this.previousActive = false
|
||||
|
||||
this.scrollEvent = () => {
|
||||
if (this.state.activeImage != -1) {
|
||||
this.dismissPopup()
|
||||
}
|
||||
}
|
||||
|
||||
this.keyUpEvent = e => {
|
||||
const activeImage = this.state.activeImage
|
||||
if (activeImage != -1) {
|
||||
if (e.key == 'ArrowRight') {
|
||||
this.setActiveImage((activeImage + 1) % this.photoAmount)
|
||||
}
|
||||
|
||||
if (e.key == 'ArrowLeft') {
|
||||
if (activeImage <= 0) {
|
||||
this.setActiveImage(this.photoAmount - 1)
|
||||
} else {
|
||||
this.setActiveImage(activeImage - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('scroll', this.scrollEvent)
|
||||
document.addEventListener('keyup', this.keyUpEvent)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('scroll', this.scrollEvent)
|
||||
document.removeEventListener('keyup', this.keyUpEvent)
|
||||
}
|
||||
|
||||
setActiveImage(index) {
|
||||
this.previousActive = this.state.activeImage != -1
|
||||
this.setState({
|
||||
activeImage: index,
|
||||
})
|
||||
}
|
||||
|
||||
dismissPopup() {
|
||||
this.previousActive = true
|
||||
this.setState({
|
||||
activeImage: -1,
|
||||
})
|
||||
}
|
||||
|
||||
animateImage(target) {
|
||||
const margin = 32
|
||||
|
||||
const windowWidth = window.innerWidth
|
||||
const windowHeight = window.innerHeight
|
||||
|
||||
const viewportWidth = windowWidth - margin * 2
|
||||
const viewportHeight = windowHeight - margin * 2
|
||||
|
||||
const { naturalWidth, naturalHeight } = target
|
||||
const { width, height, top, left } = target.getBoundingClientRect()
|
||||
|
||||
const scaleX = Math.min(naturalWidth, viewportWidth) / width
|
||||
const scaleY = Math.min(naturalHeight, viewportHeight) / height
|
||||
const scale = Math.min(scaleX, scaleY)
|
||||
|
||||
const translateX = (-left + (windowWidth - width) / 2) / scale
|
||||
const translateY = (-top + (windowHeight - height) / 2) / scale
|
||||
|
||||
if (!this.previousActive) target.style.transition = `transform 0.4s`
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
target.style.transform = `scale(${scale})
|
||||
translate3d(${translateX}px, ${translateY}px, 0)`
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const albumId = this.props.match.params.id
|
||||
|
||||
// let dimmer = this.state.activeImage != -1 && (
|
||||
// <Dimmer onClick={() => this.setActiveImage(-1)} />
|
||||
// )
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Dimmer
|
||||
onClick={() => this.setActiveImage(-1)}
|
||||
active={this.state.activeImage != -1}
|
||||
/>
|
||||
<Query query={albumQuery} variables={{ id: albumId }}>
|
||||
{({ loading, error, data }) => {
|
||||
if (error) return <div>Error</div>
|
||||
if (loading) return <div>Loading</div>
|
||||
|
||||
this.photoAmount = data.album.photos.length
|
||||
|
||||
const { activeImage } = this.state
|
||||
|
||||
const photos = data.album.photos.map((photo, index) => {
|
||||
const active = index == activeImage
|
||||
let ref = null
|
||||
let style = {}
|
||||
|
||||
if (active) {
|
||||
ref = target => {
|
||||
if (target == null) return
|
||||
this.animateImage(target)
|
||||
}
|
||||
} else {
|
||||
style.transform = null
|
||||
}
|
||||
|
||||
return (
|
||||
<Photo
|
||||
ref={ref}
|
||||
active={active}
|
||||
key={photo.id}
|
||||
onClick={() => {
|
||||
requestAnimationFrame(() => {
|
||||
if (activeImage != index) {
|
||||
this.setActiveImage(index)
|
||||
} else {
|
||||
this.dismissPopup()
|
||||
}
|
||||
})
|
||||
}}
|
||||
style={style}
|
||||
src={photo.thumbnail.path}
|
||||
></Photo>
|
||||
)
|
||||
})
|
||||
|
||||
return <Gallery>{photos}</Gallery>
|
||||
}}
|
||||
</Query>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default AlbumPage
|
|
@ -0,0 +1,106 @@
|
|||
import React, { Component } from 'react'
|
||||
import gql from 'graphql-tag'
|
||||
import { Query } from 'react-apollo'
|
||||
import { Gallery, Photo, PhotoFiller } from './styledElements'
|
||||
import Layout from '../../Layout'
|
||||
|
||||
const albumQuery = gql`
|
||||
query albumQuery($id: ID) {
|
||||
album(id: $id) {
|
||||
title
|
||||
photos {
|
||||
id
|
||||
thumbnail {
|
||||
path
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
class AlbumPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
activeImage: -1,
|
||||
}
|
||||
|
||||
this.setActiveImage = this.setActiveImage.bind(this)
|
||||
|
||||
this.photoAmount = 1
|
||||
this.previousActive = false
|
||||
|
||||
this.keyUpEvent = e => {
|
||||
const activeImage = this.state.activeImage
|
||||
if (activeImage != -1) {
|
||||
if (e.key == 'ArrowRight') {
|
||||
this.setActiveImage((activeImage + 1) % this.photoAmount)
|
||||
}
|
||||
|
||||
if (e.key == 'ArrowLeft') {
|
||||
if (activeImage <= 0) {
|
||||
this.setActiveImage(this.photoAmount - 1)
|
||||
} else {
|
||||
this.setActiveImage(activeImage - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keyup', this.keyUpEvent)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keyup', this.keyUpEvent)
|
||||
}
|
||||
|
||||
setActiveImage(index) {
|
||||
this.previousActive = this.state.activeImage != -1
|
||||
this.setState({
|
||||
activeImage: index,
|
||||
})
|
||||
}
|
||||
|
||||
dismissPopup() {
|
||||
this.setState({
|
||||
activeImage: -1,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const albumId = this.props.match.params.id
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Query query={albumQuery} variables={{ id: albumId }}>
|
||||
{({ loading, error, data }) => {
|
||||
if (error) return <div>Error</div>
|
||||
if (loading) return <div>Loading</div>
|
||||
|
||||
this.photoAmount = data.album.photos.length
|
||||
|
||||
const { activeImage } = this.state
|
||||
|
||||
const photos = data.album.photos.map((photo, index) => {
|
||||
return <Photo key={photo.id} src={photo.thumbnail.path}></Photo>
|
||||
})
|
||||
|
||||
return (
|
||||
<Gallery>
|
||||
{photos}
|
||||
<PhotoFiller />
|
||||
</Gallery>
|
||||
)
|
||||
}}
|
||||
</Query>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default AlbumPage
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react'
|
||||
import { useSpring, animated } from 'react-spring'
|
||||
|
||||
const Dimmer = ({ onClick, active }) => {
|
||||
const [props, set, stop] = useSpring(() => ({ opacity: 0 }))
|
||||
|
||||
set({
|
||||
opacity: active ? 1 : 0,
|
||||
})
|
||||
|
||||
const AnimatedDimmer = styled(animated.div)`
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
`
|
||||
|
||||
return (
|
||||
<AnimatedDimmer
|
||||
onClick={onClick}
|
||||
style={{
|
||||
...props,
|
||||
pointerEvents: active ? 'auto' : 'none',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dimmer
|
|
@ -0,0 +1,28 @@
|
|||
import styled from 'styled-components'
|
||||
|
||||
export const Gallery = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export const Photo = styled.img`
|
||||
margin: 4px;
|
||||
background-color: #eee;
|
||||
height: 200px;
|
||||
flex-grow: 1;
|
||||
object-fit: cover;
|
||||
|
||||
${props =>
|
||||
props.active &&
|
||||
`
|
||||
will-change: transform;
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
`}
|
||||
`
|
||||
|
||||
export const PhotoFiller = styled.div`
|
||||
height: 200px;
|
||||
flex-grow: 999999;
|
||||
`
|
|
@ -1,13 +1,14 @@
|
|||
import React, { Component } from 'react'
|
||||
import Albums from '../Albums'
|
||||
import Albums from './Albums'
|
||||
import Layout from '../../Layout'
|
||||
|
||||
class HomePage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Layout>
|
||||
<h1>Home</h1>
|
||||
<Albums />
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,13 +2,21 @@ import React, { Component } from 'react'
|
|||
import { Route, Switch } from 'react-router-dom'
|
||||
|
||||
import LoginPage from './Pages/LoginPage'
|
||||
import HomePage from './Pages/HomePage'
|
||||
import AlbumPage from './Pages/AlbumPage'
|
||||
import HomePage from './Pages/HomePage/HomePage'
|
||||
import AlbumPage from './Pages/AlbumPage/AlbumPage'
|
||||
|
||||
class Routes extends Component {
|
||||
render() {
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
let unauthorizedRedirect = null
|
||||
if (!token) {
|
||||
unauthorizedRedirect = <Redirect to="/login" />
|
||||
}
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
{unauthorizedRedirect}
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route exact path="/" component={HomePage} />
|
||||
<Route path="/album/:id" component={AlbumPage} />
|
||||
|
|
Loading…
Reference in New Issue