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" 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) // 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
. 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" } const TRANSITION_MEDIA = "(max-width: 1023.98px) and (prefers-reduced-motion: no-preference)" function screenTransition(newFrame) { if (!document.startViewTransition) return null if (!window.matchMedia(TRANSITION_MEDIA).matches) return null const from = document.querySelector("turbo-frame#game main")?.classList[0] const to = newFrame.querySelector("main")?.classList[0] return SCREEN_TRANSITIONS[`${from}>${to}`] } // 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 const transition = document.startViewTransition(() => render(currentElement, newElement)) transition.finished.then(clear, clear) return transition.updateCallbackDone.catch(clear) } }) // Configure Stimulus development experience application.debug = false window.Stimulus = application export { application }