Add next/previous keybindings in present view
This commit is contained in:
parent
3cc8b4b5cb
commit
5e41e4d34d
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -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
|
|
|
@ -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,17 +28,55 @@ 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() {
|
||||||
|
const albumImageCount = this.albums[this.state.activeAlbumIndex].photos
|
||||||
|
.length
|
||||||
|
|
||||||
|
if (this.state.activePhotoIndex + 1 < albumImageCount) {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeIndex: index,
|
activePhotoIndex: this.state.activePhotoIndex + 1,
|
||||||
activeAlbum: album,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousImage() {
|
||||||
|
if (this.state.activePhotoIndex > 0) {
|
||||||
|
this.setState({
|
||||||
|
activePhotoIndex: this.state.activePhotoIndex - 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -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 (
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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 (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() {
|
if (e.key == 'ArrowRight') {
|
||||||
// document.addEventListener('keydown', this.keyDownEvent)
|
this.props.nextImage && this.props.nextImage()
|
||||||
// }
|
}
|
||||||
|
|
||||||
// componentWillUnmount() {
|
if (e.key == 'ArrowLeft') {
|
||||||
// document.removeEventListener('keydown', this.keyDownEvent)
|
this.props.nextImage && this.props.previousImage()
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
if (e.key == 'Escape') {
|
||||||
|
this.props.setPresenting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.addEventListener('keydown', this.keyDownEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue