1
Fork 0

Add next/previous keybindings in present view

This commit is contained in:
viktorstrate 2019-07-29 18:06:53 +02:00
parent 3cc8b4b5cb
commit 5e41e4d34d
7 changed files with 193 additions and 90 deletions

View File

@ -17,6 +17,7 @@
"graphql-tag": "^2.10.1", "graphql-tag": "^2.10.1",
"parcel-bundler": "^1.12.3", "parcel-bundler": "^1.12.3",
"prettier": "^1.18.2", "prettier": "^1.18.2",
"prop-types": "^15.7.2",
"react": "^0.14.0 || ^15.0.0 || ^16.0.0", "react": "^0.14.0 || ^15.0.0 || ^16.0.0",
"react-apollo": "^2.5.5", "react-apollo": "^2.5.5",
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0", "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0",

View File

@ -3,7 +3,9 @@ import gql from 'graphql-tag'
import { Query } from 'react-apollo' import { Query } from 'react-apollo'
import Layout from '../../Layout' import Layout from '../../Layout'
import PhotoSidebar from '../../components/sidebar/PhotoSidebar' import PhotoSidebar from '../../components/sidebar/PhotoSidebar'
import PhotoGallery from '../../components/photoGallery/PhotoGallery' import PhotoGallery, {
presentIndexFromHash,
} from '../../components/photoGallery/PhotoGallery'
import AlbumGallery from '../AllAlbumsPage/AlbumGallery' import AlbumGallery from '../AllAlbumsPage/AlbumGallery'
const albumQuery = gql` const albumQuery = gql`
@ -37,18 +39,63 @@ class AlbumPage extends Component {
this.state = { this.state = {
activeImage: -1, activeImage: -1,
activeImageId: null, presenting: false,
}
const presentIndex = presentIndexFromHash(location.hash)
if (presentIndex) {
this.state.activeImage = presentIndex
this.state.presenting = true
} }
this.setActiveImage = this.setActiveImage.bind(this) this.setActiveImage = this.setActiveImage.bind(this)
this.nextImage = this.nextImage.bind(this)
this.previousImage = this.previousImage.bind(this)
this.setPresenting = this.setPresenting.bind(this)
this.photoAmount = 1 this.photos = []
} }
setActiveImage(index, id) { setActiveImage(index) {
this.setState({ this.setState({
activeImage: index, activeImage: index,
activeImageId: id, })
}
nextImage() {
this.setState({
activeImage: (this.state.activeImage + 1) % this.photos.length,
})
}
previousImage() {
if (this.state.activeImage <= 0) {
this.setState({
activeImage: this.photos.length - 1,
})
} else {
this.setState({
activeImage: this.state.activeImage - 1,
})
}
}
componentDidUpdate(prevProps, prevState) {
if (this.state.presenting) {
history.replaceState(
null,
null,
document.location.pathname + '#' + `present=${this.state.activeImage}`
)
} else if (presentIndexFromHash(location.hash)) {
history.replaceState(null, null, document.location.pathname.split('#')[0])
}
}
setPresenting(presenting) {
console.log('Presenting', presenting, this)
this.setState({
presenting,
}) })
} }
@ -64,7 +111,7 @@ class AlbumPage extends Component {
let subAlbumElement = null let subAlbumElement = null
if (data.album) { if (data.album) {
this.photoAmount = data.album.photos.length this.photos = data.album.photos
if (data.album.subAlbums.length > 0) { if (data.album.subAlbums.length > 0) {
subAlbumElement = ( subAlbumElement = (
@ -88,11 +135,21 @@ class AlbumPage extends Component {
loading={loading} loading={loading}
photos={data.album && data.album.photos} photos={data.album && data.album.photos}
activeIndex={this.state.activeImage} activeIndex={this.state.activeImage}
presenting={this.state.presenting}
onSelectImage={index => { onSelectImage={index => {
this.setActiveImage(index, data.album.photos[index].id) this.setActiveImage(index)
}} }}
setPresenting={this.setPresenting}
nextImage={this.nextImage}
previousImage={this.previousImage}
/>
<PhotoSidebar
imageId={
this.photos.length > 0 && this.state.activeImage != -1
? this.photos[this.state.activeImage].id
: null
}
/> />
<PhotoSidebar imageId={this.state.activeImageId} />
</div> </div>
) )
}} }}

View File

@ -1,31 +0,0 @@
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

View File

@ -10,7 +10,7 @@ const photoQuery = gql`
myAlbums(orderBy: title_asc) { myAlbums(orderBy: title_asc) {
title title
id id
photos(orderBy: title_desc) { photos(orderBy: title_desc, first: 12) {
id id
title title
thumbnail { thumbnail {
@ -28,16 +28,54 @@ class PhotosPage extends Component {
super(props) super(props)
this.state = { this.state = {
activeAlbum: null, activeAlbumIndex: -1,
activeIndex: null, activePhotoIndex: -1,
presenting: false,
}
this.setPresenting = this.setPresenting.bind(this)
this.nextImage = this.nextImage.bind(this)
this.previousImage = this.previousImage.bind(this)
this.albums = []
}
setActiveImage(album, photo) {
this.setState({
activePhotoIndex: photo,
activeAlbumIndex: album,
})
}
setPresenting(presenting, index) {
if (presenting) {
this.setState({
presenting: index,
})
} else {
this.setState({
presenting: false,
})
} }
} }
setActiveImage(album, index) { nextImage() {
this.setState({ const albumImageCount = this.albums[this.state.activeAlbumIndex].photos
activeIndex: index, .length
activeAlbum: album,
}) if (this.state.activePhotoIndex + 1 < albumImageCount) {
this.setState({
activePhotoIndex: this.state.activePhotoIndex + 1,
})
}
}
previousImage() {
if (this.state.activePhotoIndex > 0) {
this.setState({
activePhotoIndex: this.state.activePhotoIndex - 1,
})
}
} }
render() { render() {
@ -49,31 +87,40 @@ class PhotosPage extends Component {
let galleryGroups = [] let galleryGroups = []
this.albums = data.myAlbums
if (data.myAlbums) { if (data.myAlbums) {
galleryGroups = data.myAlbums.map(album => ( galleryGroups = data.myAlbums.map((album, index) => (
<div key={album.id}> <div key={album.id}>
<h1>{album.title}</h1> <h1>{album.title}</h1>
<PhotoGallery <PhotoGallery
onSelectImage={index => { onSelectImage={photoIndex => {
this.setActiveImage(album.id, index) this.setActiveImage(index, photoIndex)
}} }}
activeIndex={ activeIndex={
this.state.activeAlbum == album.id this.state.activeAlbumIndex == index
? this.state.activeIndex ? this.state.activePhotoIndex
: -1 : -1
} }
presenting={this.state.presenting === index}
setPresenting={presenting =>
this.setPresenting(presenting, index)
}
loading={loading} loading={loading}
photos={album.photos} photos={album.photos}
nextImage={this.nextImage}
previousImage={this.previousImage}
/> />
</div> </div>
)) ))
} }
let activeImage = null let activeImage = null
if (this.state.activeAlbum) { if (this.state.activeAlbumIndex != -1) {
activeImage = data.myAlbums.find( activeImage =
album => album.id == this.state.activeAlbum data.myAlbums[this.state.activeAlbumIndex].photos[
).photos[this.state.activeIndex].id this.state.activePhotoIndex
].id
} }
return ( return (

View File

@ -58,6 +58,11 @@ const PhotoOverlay = styled.div`
props.active && props.active &&
` `
border: 4px solid rgba(65, 131, 196, 0.6); border: 4px solid rgba(65, 131, 196, 0.6);
& ${HoverIcon} {
top: -4px !important;
left: -4px !important;
}
`} `}
` `
@ -67,6 +72,7 @@ const HoverIcon = styled(Icon)`
color: white !important; color: white !important;
text-shadow: 0 0 4px black; text-shadow: 0 0 4px black;
opacity: 0 !important; opacity: 0 !important;
position: relative;
border-radius: 50%; border-radius: 50%;
width: 34px !important; width: 34px !important;
@ -84,7 +90,14 @@ const HoverIcon = styled(Icon)`
transition: opacity 100ms, background-color 100ms; transition: opacity 100ms, background-color 100ms;
` `
export const Photo = ({ photo, onSelectImage, minWidth, index, active }) => ( export const Photo = ({
photo,
onSelectImage,
minWidth,
index,
active,
setPresenting,
}) => (
<PhotoContainer <PhotoContainer
key={photo.id} key={photo.id}
style={{ style={{
@ -100,7 +113,8 @@ export const Photo = ({ photo, onSelectImage, minWidth, index, active }) => (
<HoverIcon <HoverIcon
name="expand" name="expand"
onClick={() => { onClick={() => {
window.location.hash = `present=${photo.id}` // window.location.hash = `present=${photo.id}`
setPresenting(true)
}} }}
/> />
<HoverIcon name="heart outline" /> <HoverIcon name="heart outline" />

View File

@ -3,6 +3,7 @@ import styled from 'styled-components'
import { Loader } from 'semantic-ui-react' import { Loader } from 'semantic-ui-react'
import { Photo } from './Photo' import { Photo } from './Photo'
import PresentView from './PresentView' import PresentView from './PresentView'
import PropTypes from 'prop-types'
const Gallery = styled.div` const Gallery = styled.div`
display: flex; display: flex;
@ -15,47 +16,47 @@ const PhotoFiller = styled.div`
flex-grow: 999999; flex-grow: 999999;
` `
const presentIdFromHash = hash => { export const presentIndexFromHash = hash => {
let match = hash.match(/present=([a-z0-9\-]+)/) let match = hash.match(/present=(\d+)/)
return match && match[1] return match && parseInt(match[1])
} }
class PhotoGallery extends React.Component { class PhotoGallery extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props)
// this.keyDownEvent = e => { this.keyDownEvent = e => {
// if (!this.props.onSelectImage) { if (!this.props.onSelectImage || this.props.activeIndex == -1) {
// return return
// } }
// const activeImage = this.state.activeImage if (e.key == 'ArrowRight') {
// if (activeImage != -1) { this.props.nextImage && this.props.nextImage()
// if (e.key == 'ArrowRight') { }
// this.setActiveImage((activeImage + 1) % this.photoAmount)
// }
// if (e.key == 'ArrowLeft') { if (e.key == 'ArrowLeft') {
// if (activeImage <= 0) { this.props.nextImage && this.props.previousImage()
// this.setActiveImage(this.photoAmount - 1) }
// } else {
// this.setActiveImage(activeImage - 1) if (e.key == 'Escape') {
// } this.props.setPresenting(false)
// } }
// } }
// }
} }
// componentDidMount() { componentDidMount() {
// document.addEventListener('keydown', this.keyDownEvent) document.addEventListener('keydown', this.keyDownEvent)
// } }
// componentWillUnmount() { componentWillUnmount() {
// document.removeEventListener('keydown', this.keyDownEvent) document.removeEventListener('keydown', this.keyDownEvent)
// } }
render() { render() {
const { activeIndex = -1, photos, loading, onSelectImage } = this.props const { activeIndex = -1, photos, loading, onSelectImage } = this.props
const activeImage = photos && activeIndex != -1 && photos[activeIndex]
let photoElements = null let photoElements = null
if (photos) { if (photos) {
photoElements = photos.map((photo, index) => { photoElements = photos.map((photo, index) => {
@ -73,6 +74,7 @@ class PhotoGallery extends React.Component {
key={photo.id} key={photo.id}
photo={photo} photo={photo}
onSelectImage={onSelectImage} onSelectImage={onSelectImage}
setPresenting={this.props.setPresenting}
minWidth={minWidth} minWidth={minWidth}
index={index} index={index}
active={active} active={active}
@ -81,8 +83,6 @@ class PhotoGallery extends React.Component {
}) })
} }
console.log(presentIdFromHash(location.hash))
return ( return (
<div> <div>
<Gallery> <Gallery>
@ -90,9 +90,24 @@ class PhotoGallery extends React.Component {
{photoElements} {photoElements}
<PhotoFiller /> <PhotoFiller />
</Gallery> </Gallery>
<PresentView image={presentIdFromHash(location.hash)} /> <PresentView
presenting={this.props.presenting}
image={activeImage && activeImage.id}
/>
</div> </div>
) )
} }
} }
PhotoGallery.propTypes = {
loading: PropTypes.bool,
photos: PropTypes.array,
activeIndex: PropTypes.number,
presenting: PropTypes.bool,
onSelectImage: PropTypes.func,
setPresenting: PropTypes.func,
nextImage: PropTypes.func,
previousImage: PropTypes.func,
}
export default PhotoGallery export default PhotoGallery

View File

@ -43,9 +43,9 @@ const PresentImage = styled.img`
export default class PresentView extends React.Component { export default class PresentView extends React.Component {
render() { render() {
const { image } = this.props const { image, presenting } = this.props
if (!image) { if (!image || !presenting) {
return null return null
} }