Viewer: Finally fix Carousel navigation depending on URL

- Instead of detecting every case using popState event or whatnot, used a Sapper page stores subscribe to go to the current photo from the URL param (findIndex in photos)
- No page transition when navigating between photos in Viewer, but runs In and Out
- Also shortened dispatches to direct value over an object
This commit is contained in:
2020-03-29 18:13:27 +02:00
parent 25fa9e1c07
commit ca07fe364e
6 changed files with 73 additions and 81 deletions

View File

@@ -14,7 +14,7 @@
class:active={index === currentIndex} class:active={index === currentIndex}
class:small={index < currentIndex - 2 || index > currentIndex + 2} class:small={index < currentIndex - 2 || index > currentIndex + 2}
class:hidden={index < currentIndex - 3 || index > currentIndex + 3} class:hidden={index < currentIndex - 3 || index > currentIndex + 3}
on:click={() => dispatch('goToIndex', { index })} on:click={() => dispatch('goToIndex', index)}
> >
<button aria-label="Go to photo #{index + 1}"></button> <button aria-label="Go to photo #{index + 1}"></button>
</li> </li>

View File

@@ -18,13 +18,12 @@
// Props // Props
export let photos export let photos
export let viewer = false export let viewer = false
export let init = undefined
// Variables // Variables
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const { page } = stores() const { page } = stores()
let scope let scope
let hover let swiped
let currentIndex = 0 let currentIndex = 0
$: currentPhoto = photos[currentIndex] || null $: currentPhoto = photos[currentIndex] || null
$: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1] $: prevPhoto = photos[currentIndex - 1] || photos[photos.length - 1]
@@ -34,11 +33,6 @@
/* /*
** Navigate to a photo ** Navigate to a photo
*/ */
// Go to specific photo on Init
if (init) {
currentIndex = photos.findIndex(photo => photo.slug === init.params.photo)
}
// Change current index from param // Change current index from param
const goToPhoto = to => { const goToPhoto = to => {
if (to === 'prev') { if (to === 'prev') {
@@ -52,25 +46,50 @@
} }
// Dispatch current photo // Dispatch current photo
sendCurrentPhoto() dispatch('photoChange', photos[currentIndex])
// Reset fullscreen value if open // Reset fullscreen value if open
fullscreen.set() if ($fullscreen) fullscreen.set()
} }
// Send current photo to event // Hover on controls
const sendCurrentPhoto = (init = false) => { const hoverPhoto = event => {
dispatch('photoChange', { const button = event.currentTarget.querySelector('button')
currentPhoto: photos[currentIndex], const photoActive = scope.querySelector('.gallery__photo--active')
init
}) let photoToHover
if (event.currentTarget.dataset.to === 'prev') {
photoToHover = (photoActive.previousSibling != null) ? photoActive.previousSibling : photoActive.parentNode.lastChild
} else {
photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild
}
// Toggle class and focus of the button
if (event.type === 'mouseenter') {
photoToHover.classList.add('hover')
button.classList.add('hover')
} else if (event.type === 'mouseleave') {
photoToHover.classList.remove('hover')
button.classList.remove('hover')
}
}
// Open fullscreen
const openFullscreen = event => {
if (!swiped) {
fullscreen.set(currentPhoto)
}
// Reset swiped event if fired
swiped = false
} }
/* /*
** Drag/touch navigation ** Navigation
*/ */
// Drag and swipe
const swipe = directions => { const swipe = directions => {
swiped = true
if (directions.right) { if (directions.right) {
goToPhoto('prev') goToPhoto('prev')
} else if (directions.left) { } else if (directions.left) {
@@ -78,10 +97,7 @@
} }
} }
// Keyboard navigation
/*
** Keyboard navigation
*/
const keyboardNav = event => { const keyboardNav = event => {
if ([37,80,74].includes(event.keyCode)) { if ([37,80,74].includes(event.keyCode)) {
goToPhoto('prev') goToPhoto('prev')
@@ -103,30 +119,10 @@
// Enable gestures // Enable gestures
const touch = SwipeListener(scope) const touch = SwipeListener(scope)
// Controls hover // Viewer: Navigate to photo on init and URL change
hover = event => { if (viewer) {
const button = event.currentTarget.querySelector('button') page.subscribe(page => goToPhoto(photos.findIndex(photo => photo.slug === page.params.photo)))
const photoActive = document.querySelector('.gallery__photo--active')
let photoToHover
if (event.currentTarget.dataset.to === 'prev') {
photoToHover = (photoActive.previousSibling != null) ? photoActive.previousSibling : photoActive.parentNode.lastChild
} else {
photoToHover = (photoActive.nextSibling != null) ? photoActive.nextSibling : photoActive.parentNode.firstChild
} }
// Toggle class and focus of the button
if (event.type === 'mouseenter') {
photoToHover.classList.add('hover')
button.classList.add('hover')
} else if (event.type === 'mouseleave') {
photoToHover.classList.remove('hover')
button.classList.remove('hover')
}
}
// Dispatch current photo for init
sendCurrentPhoto({ init: true })
}) })
</script> </script>
@@ -145,7 +141,7 @@
class:gallery__photo--prev={photo === prevPhoto} class:gallery__photo--prev={photo === prevPhoto}
class:gallery__photo--active={photo === currentPhoto} class:gallery__photo--active={photo === currentPhoto}
class:gallery__photo--next={photo === nextPhoto} class:gallery__photo--next={photo === nextPhoto}
on:click={event => fullscreen.set(currentPhoto)} on:click={openFullscreen}
> >
<source media="(min-width: 968px)" srcset={getThumbnail(photo.image.private_hash, 1400)}> <source media="(min-width: 968px)" srcset={getThumbnail(photo.image.private_hash, 1400)}>
<source media="(min-width: 800px)" srcset={getThumbnail(photo.image.private_hash, 900)}> <source media="(min-width: 800px)" srcset={getThumbnail(photo.image.private_hash, 900)}>
@@ -158,7 +154,7 @@
<div class="carousel__controls"> <div class="carousel__controls">
<div class="carousel__area carousel__area--prev" data-to="prev" <div class="carousel__area carousel__area--prev" data-to="prev"
on:mouseenter={hover} on:mouseleave={hover} on:mouseenter={hoverPhoto} on:mouseleave={hoverPhoto}
on:click={() => goToPhoto('prev')} on:click={() => goToPhoto('prev')}
> >
<button class="button-control button-control--white dir-left" aria-label="Previous"> <button class="button-control button-control--white dir-left" aria-label="Previous">
@@ -168,7 +164,7 @@
</div> </div>
<div class="carousel__area carousel__area--next" data-to="next" <div class="carousel__area carousel__area--next" data-to="next"
on:mouseenter={hover} on:mouseleave={hover} on:mouseenter={hoverPhoto} on:mouseleave={hoverPhoto}
on:click={() => goToPhoto('next')} on:click={() => goToPhoto('next')}
> >
<button class="button-control button-control--white dir-right" aria-label="Next"> <button class="button-control button-control--white dir-right" aria-label="Next">
@@ -205,7 +201,7 @@
</div> </div>
<PaginationDots className="carousel__dots" {photos} {currentIndex} <PaginationDots className="carousel__dots" {photos} {currentIndex}
on:goToIndex={event => currentIndex = event.detail.index} on:goToIndex={event => currentIndex = event.detail}
/> />
</div> </div>
{/if} {/if}

View File

@@ -22,9 +22,7 @@
photosToAppend.forEach(photo => photo.hidden = false) photosToAppend.forEach(photo => photo.hidden = false)
// Merge new photos to show to the existing ones // Merge new photos to show to the existing ones
dispatch('updatePagination', { dispatch('updatePagination', [...paginatedPhotos, ...photosToAppend])
paginatedPhotos: [...paginatedPhotos, ...photosToAppend]
})
// Increment current index // Increment current index
currentIndex = currentIndex + photosPerPage currentIndex = currentIndex + photosPerPage

View File

@@ -80,7 +80,7 @@
let paginatedPhotos = photos.filter(photo => photo.hidden === false) let paginatedPhotos = photos.filter(photo => photo.hidden === false)
// Update pagination event from Pagination component // Update pagination event from Pagination component
const updatePagination = event => paginatedPhotos = event.detail.paginatedPhotos const updatePagination = event => paginatedPhotos = event.detail
/* /*

View File

@@ -39,7 +39,7 @@
pageReady, pageReady,
pageTransition pageTransition
} from '../../../../utils/store' } from '../../../../utils/store'
import { getThumbnail, analyticsUpdate } from '../../../../utils/functions' import { getThumbnail } from '../../../../utils/functions'
// Components // Components
import IconGlobe from '../../../../atoms/IconGlobe' import IconGlobe from '../../../../atoms/IconGlobe'
@@ -59,31 +59,21 @@
const { page } = stores() const { page } = stores()
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let windowWidth let windowWidth
let gotoLink
let currentPhoto = photos.find(photo => photo.slug === $page.params.photo) let currentPhoto = photos.find(photo => photo.slug === $page.params.photo)
// Update store current location // Update store current location
if (!$currentLocation) currentLocation.set($locations.find(loc => loc.slug === $page.params.place)) if (!$currentLocation) currentLocation.set($locations.find(loc => loc.slug === $page.params.place))
if (!$currentPhotos) currentPhotos.set(photos) if (!$currentPhotos) currentPhotos.set(photos)
// Photo has changed from the Carousel component // Photo has changed (from Carousel)
const photoChanged = event => { const photoChanged = event => {
currentPhoto = event.detail.currentPhoto
// Change the URL to the current photo
if (!event.detail.init) {
const windowPathname = window.location.pathname const windowPathname = window.location.pathname
const newUrl = windowPathname.substring(0, windowPathname.lastIndexOf('/') + 1) + currentPhoto.slug const newUrl = windowPathname.substring(0, windowPathname.lastIndexOf('/') + 1) + event.detail.slug
history.pushState('', document.title, newUrl) // Go to page via a sapper-noscroll link to avoid scroll jump
analyticsUpdate(newUrl) gotoLink.href = newUrl
gotoLink.click()
} }
}
// Access a direct photo or navigate in history
const photoSlug = $page.params.photo
// On init, send an event to the Carousel component with the photoSlug to set currentIndex and then change the photo
// Pop event from browser (prev/next)
const changedUrl = event => dispatch('changedUrl', { currentPhoto: currentPhoto })
/* /*
@@ -92,8 +82,6 @@
onMount(() => { onMount(() => {
// Page is loaded // Page is loaded
pageReady.set(true) pageReady.set(true)
dispatch('changeUrl', { page: $page })
}) })
</script> </script>
@@ -129,12 +117,12 @@
<IconCross color="#fff" width="18" class="icon" hidden="true" /> <IconCross color="#fff" width="18" class="icon" hidden="true" />
</a> </a>
</div> </div>
<a href="/" bind:this={gotoLink} aria-hidden="true" hidden class="hidden" sapper-noscroll>&nbsp;</a>
</div> </div>
<Carousel <Carousel
viewer="true" viewer="true"
photos={photos} photos={photos}
init={$page}
on:photoChange={photoChanged} on:photoChange={photoChanged}
/> />

View File

@@ -22,9 +22,15 @@
*/ */
let scope let scope
let firstLoad = true let firstLoad = true
let previousPage = ''
// Check if viewer
const isExcluded = path => path.includes(['/viewer/'])
// 1. Watch page change // 1. Watch page change
page.subscribe(page => { page.subscribe(page => {
// Run transition if page is not excluded
if (!isExcluded(previousPage) || !isExcluded(page.path)) {
// Run the loader animation (only after first load) // Run the loader animation (only after first load)
if (!firstLoad) { if (!firstLoad) {
animateIn(scope) animateIn(scope)
@@ -34,6 +40,10 @@
// Set pageReady to false (?) // Set pageReady to false (?)
pageReady.set(false) pageReady.set(false)
}
// Update page for viewer navigation checking
previousPage = page.path
}) })
// 2. Watch when loaded changes // 2. Watch when loaded changes