You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

118 lines
4.6 KiB

import "@hotwired/turbo-rails"
import { Application } from "@hotwired/stimulus"
import LanguageMenuController from "language_menu_controller"
import CarouselController from "carousel_controller"
import IntroController from "intro_controller"
import ChanceController from "chance_controller"
import ShareController from "share_controller"
import ModalController from "modal_controller"
import RatingController from "rating_controller"
import StartController from "start_controller"
import AnswerController from "answer_controller"
const application = Application.start()
application.register("language-menu", LanguageMenuController)
application.register("carousel", CarouselController)
application.register("intro", IntroController)
application.register("chance", ChanceController)
application.register("share", ShareController)
application.register("modal", ModalController)
application.register("rating", RatingController)
application.register("start", StartController)
application.register("answer", AnswerController)
// The whole game runs inside one turbo-frame so the URL never changes. Frame
// navigations don't reset scroll the way a full page visit does, so each new
// screen would otherwise open at the previous screen's scroll offset.
document.addEventListener("turbo:frame-render", (event) => {
if (event.target.id === "game") window.scrollTo(0, 0)
})
// Which screen pairs swap with a view transition instead of appearing outright,
// keyed by the template class the layout puts on <main>. The value names the
// animation, which lives in application.css under [data-screen-transition].
const SCREEN_TRANSITIONS = {
"intro>stage": "slide-left",
"stage>good_answer": "answer",
"stage>bad_answer": "answer",
"good_answer>stage": "stage-in",
"bad_answer>stage": "stage-in",
"good_answer>last_save": "last-save-in",
"bad_answer>last_save": "last-save-in",
"last_save>good_answer": "answer",
"last_save>bad_answer": "answer",
"good_answer>results": "slide-up",
"bad_answer>results": "slide-up"
}
// The desktop layout is a different composition -- two columns rather than one
// stack -- so the pairs that animate there, and how, are their own map.
const WIDE_SCREEN_TRANSITIONS = {
"intro>stage": "stage-in-wide",
"stage>good_answer": "answer-wide",
"stage>bad_answer": "answer-wide",
"good_answer>stage": "stage-in-wide",
"bad_answer>stage": "stage-in-wide",
"good_answer>last_save": "stage-in-wide",
"bad_answer>last_save": "stage-in-wide",
"last_save>good_answer": "answer-wide",
"last_save>bad_answer": "answer-wide",
"good_answer>results": "results-in-wide",
"bad_answer>results": "results-in-wide"
}
const TRANSITION_MEDIA = "(max-width: 1023.98px) and (prefers-reduced-motion: no-preference)"
const WIDE_TRANSITION_MEDIA = "(min-width: 1024px) and (prefers-reduced-motion: no-preference)"
function screenTransition(newFrame) {
if (!document.startViewTransition) return null
const from = document.querySelector("turbo-frame#game main")?.classList[0]
const to = newFrame.querySelector("main")?.classList[0]
const pair = `${from}>${to}`
if (window.matchMedia(TRANSITION_MEDIA).matches) return SCREEN_TRANSITIONS[pair]
if (window.matchMedia(WIDE_TRANSITION_MEDIA).matches) return WIDE_SCREEN_TRANSITIONS[pair]
return null
}
// Turbo hands over the function that swaps the frame's contents, so running it
// inside a view transition puts the old and the new screen on screen together
// for as long as the animation lasts.
document.addEventListener("turbo:before-frame-render", (event) => {
if (event.target.id !== "game") return
const name = screenTransition(event.detail.newFrame)
if (!name) return
const render = event.detail.render
event.detail.render = (currentElement, newElement) => {
document.documentElement.dataset.screenTransition = name
// Two navigations at once -- the intro video ending on top of a tap -- leave
// the first transition aborted, so the settled state is reached either way.
const clear = () => delete document.documentElement.dataset.screenTransition
// Anything a screen wants to animate once it has landed hangs off this: the
// snapshots are gone by then, so it plays on the real elements.
const settle = () => {
clear()
document.querySelector("turbo-frame#game main")?.classList.add("is-settling")
}
const transition = document.startViewTransition(() => render(currentElement, newElement))
transition.finished.then(settle, clear)
return transition.updateCallbackDone.catch(clear)
}
})
// Configure Stimulus development experience
application.debug = false
window.Stimulus = application
export { application }