mirror of
https://github.com/withastro/astro.git
synced 2025-02-03 22:29:08 -05:00
263 lines
7.5 KiB
JavaScript
263 lines
7.5 KiB
JavaScript
|
import {
|
||
|
getNavigationType,
|
||
|
getPathId,
|
||
|
isBackNavigation,
|
||
|
shouldNotIntercept,
|
||
|
updateTheDOMSomehow,
|
||
|
useTvFragment,
|
||
|
} from './utils'
|
||
|
|
||
|
// View Transitions support cross-document navigations.
|
||
|
// Should compare performace.
|
||
|
// https://github.com/WICG/view-transitions/blob/main/explainer.md#cross-document-same-origin-transitions
|
||
|
// https://github.com/WICG/view-transitions/blob/main/explainer.md#script-events
|
||
|
function shouldDisableSpa() {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
navigation.addEventListener('navigate', (navigateEvent) => {
|
||
|
if (shouldDisableSpa()) return
|
||
|
if (shouldNotIntercept(navigateEvent)) return
|
||
|
|
||
|
const toUrl = new URL(navigateEvent.destination.url)
|
||
|
const toPath = toUrl.pathname
|
||
|
const fromPath = location.pathname
|
||
|
const navigationType = getNavigationType(fromPath, toPath)
|
||
|
|
||
|
if (location.origin !== toUrl.origin) return
|
||
|
|
||
|
switch (navigationType) {
|
||
|
case 'home-to-movie':
|
||
|
case 'tv-to-show':
|
||
|
handleHomeToMovieTransition(navigateEvent, getPathId(toPath))
|
||
|
break
|
||
|
case 'movie-to-home':
|
||
|
case 'show-to-tv':
|
||
|
handleMovieToHomeTransition(navigateEvent, getPathId(fromPath))
|
||
|
break
|
||
|
case 'movie-to-person':
|
||
|
handleMovieToPersonTransition(
|
||
|
navigateEvent,
|
||
|
getPathId(fromPath),
|
||
|
getPathId(toPath)
|
||
|
)
|
||
|
break
|
||
|
case 'person-to-movie':
|
||
|
case 'person-to-show':
|
||
|
handlePersonToMovieTransition(
|
||
|
navigateEvent,
|
||
|
getPathId(fromPath),
|
||
|
getPathId(toPath)
|
||
|
)
|
||
|
break
|
||
|
default:
|
||
|
return
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#transitions-as-an-enhancement
|
||
|
function handleHomeToMovieTransition(navigateEvent, movieId) {
|
||
|
navigateEvent.intercept({
|
||
|
async handler() {
|
||
|
const fragmentUrl = useTvFragment(navigateEvent)
|
||
|
? '/fragments/TvDetails'
|
||
|
: '/fragments/MovieDetails'
|
||
|
const response = await fetch(`${fragmentUrl}/${movieId}`)
|
||
|
const data = await response.text()
|
||
|
|
||
|
if (!document.startViewTransition) {
|
||
|
updateTheDOMSomehow(data);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const thumbnail = document.getElementById(`movie-poster-${movieId}`)
|
||
|
if (thumbnail) {
|
||
|
thumbnail.style.viewTransitionName = 'movie-poster'
|
||
|
}
|
||
|
|
||
|
const transition = document.startViewTransition(() => {
|
||
|
if (thumbnail) {
|
||
|
thumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
document.getElementById('container').scrollTop = 0
|
||
|
updateTheDOMSomehow(data)
|
||
|
})
|
||
|
|
||
|
await transition.finished
|
||
|
},
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function handleMovieToHomeTransition(navigateEvent, movieId) {
|
||
|
navigateEvent.intercept({
|
||
|
scroll: 'manual',
|
||
|
async handler() {
|
||
|
const fragmentUrl = useTvFragment(navigateEvent)
|
||
|
? '/fragments/TvList'
|
||
|
: '/fragments/MovieList'
|
||
|
const response = await fetch(fragmentUrl)
|
||
|
const data = await response.text()
|
||
|
|
||
|
if (!document.startViewTransition) {
|
||
|
updateTheDOMSomehow(data)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const tempHomePage = document.createElement('div')
|
||
|
const moviePoster = document.getElementById(`movie-poster`)
|
||
|
let thumbnail
|
||
|
|
||
|
// If the movie poster is not in the home page, removes the transition style so that
|
||
|
// the poster doesn't stay on the page while transitioning
|
||
|
tempHomePage.innerHTML = data
|
||
|
if (!tempHomePage.querySelector(`#movie-poster-${movieId}`)) {
|
||
|
moviePoster?.classList.remove('movie-poster')
|
||
|
}
|
||
|
|
||
|
const transition = document.startViewTransition(() => {
|
||
|
updateTheDOMSomehow(data)
|
||
|
|
||
|
thumbnail = document.getElementById(`movie-poster-${movieId}`)
|
||
|
if (thumbnail) {
|
||
|
thumbnail.scrollIntoViewIfNeeded()
|
||
|
thumbnail.style.viewTransitionName = 'movie-poster'
|
||
|
}
|
||
|
})
|
||
|
|
||
|
await transition.finished
|
||
|
|
||
|
if (thumbnail) {
|
||
|
thumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function handleMovieToPersonTransition(navigateEvent, movieId, personId) {
|
||
|
// TODO: https://developer.chrome.com/docs/web-platform/view-transitions/#not-a-polyfill
|
||
|
// ...has example of `back-transition` class applied to document
|
||
|
const isBack = isBackNavigation(navigateEvent)
|
||
|
|
||
|
navigateEvent.intercept({
|
||
|
async handler() {
|
||
|
const response = await fetch('/fragments/PersonDetails/' + personId)
|
||
|
const data = await response.text()
|
||
|
|
||
|
if (!document.startViewTransition) {
|
||
|
updateTheDOMSomehow(data)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let personThumbnail
|
||
|
let moviePoster
|
||
|
let movieThumbnail
|
||
|
|
||
|
if (!isBack) {
|
||
|
// We're transitioning the person photo; we need to remove the transition of the poster
|
||
|
// so that it doesn't stay on the page while transitioning
|
||
|
moviePoster = document.getElementById(`movie-poster`)
|
||
|
if (moviePoster) {
|
||
|
moviePoster.classList.remove('movie-poster')
|
||
|
}
|
||
|
|
||
|
personThumbnail = document.getElementById(`person-photo-${personId}`)
|
||
|
if (personThumbnail) {
|
||
|
personThumbnail.style.viewTransitionName = 'person-photo'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const transition = document.startViewTransition(() => {
|
||
|
updateTheDOMSomehow(data)
|
||
|
|
||
|
if (personThumbnail) {
|
||
|
personThumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
|
||
|
if (isBack) {
|
||
|
// If we're coming back to the person page, we're transitioning
|
||
|
// into the movie poster thumbnail, so we need to add the tag to it
|
||
|
movieThumbnail = document.getElementById(`movie-poster-${movieId}`)
|
||
|
if (movieThumbnail) {
|
||
|
movieThumbnail.scrollIntoViewIfNeeded()
|
||
|
movieThumbnail.style.viewTransitionName = 'movie-poster'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.getElementById('container').scrollTop = 0
|
||
|
})
|
||
|
|
||
|
await transition.finished
|
||
|
|
||
|
if (movieThumbnail) {
|
||
|
movieThumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
}
|
||
|
|
||
|
function handlePersonToMovieTransition(navigateEvent, personId, movieId) {
|
||
|
const isBack = isBackNavigation(navigateEvent)
|
||
|
|
||
|
navigateEvent.intercept({
|
||
|
scroll: 'manual',
|
||
|
async handler() {
|
||
|
const fragmentUrl = useTvFragment(navigateEvent)
|
||
|
? '/fragments/TvDetails'
|
||
|
: '/fragments/MovieDetails'
|
||
|
const response = await fetch(`${fragmentUrl}/${movieId}`)
|
||
|
const data = await response.text()
|
||
|
|
||
|
if (!document.startViewTransition) {
|
||
|
updateTheDOMSomehow(data)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
let thumbnail
|
||
|
let moviePoster
|
||
|
let movieThumbnail
|
||
|
|
||
|
if (!isBack) {
|
||
|
movieThumbnail = document.getElementById(`movie-poster-${movieId}`)
|
||
|
if (movieThumbnail) {
|
||
|
movieThumbnail.style.viewTransitionName = 'movie-poster'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const transition = document.startViewTransition(() => {
|
||
|
updateTheDOMSomehow(data)
|
||
|
|
||
|
if (isBack) {
|
||
|
moviePoster = document.getElementById(`movie-poster`)
|
||
|
if (moviePoster) {
|
||
|
moviePoster.classList.remove('movie-poster')
|
||
|
}
|
||
|
|
||
|
if (personId) {
|
||
|
thumbnail = document.getElementById(`person-photo-${personId}`)
|
||
|
if (thumbnail) {
|
||
|
thumbnail.scrollIntoViewIfNeeded()
|
||
|
thumbnail.style.viewTransitionName = 'person-photo'
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
document.getElementById('container').scrollTop = 0
|
||
|
|
||
|
if (movieThumbnail) {
|
||
|
movieThumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
|
||
|
await transition.finished
|
||
|
|
||
|
if (thumbnail) {
|
||
|
thumbnail.style.viewTransitionName = ''
|
||
|
}
|
||
|
|
||
|
if (moviePoster) {
|
||
|
moviePoster.classList.add('movie-poster')
|
||
|
}
|
||
|
},
|
||
|
})
|
||
|
}
|