diff --git a/public/nextjs_migration/client/js/landing.js b/public/nextjs_migration/client/js/landing.js new file mode 100644 index 00000000000..c3ff1256b2e --- /dev/null +++ b/public/nextjs_migration/client/js/landing.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** @type {HTMLDivElement} */ +// @ts-ignore: We guard against a null value by not calling init(): +const landingPartial = document.querySelector("[data-partial='landing']") + +if (landingPartial) { + init() +} + +async function init () { + const landingForm = landingPartial.querySelector('form.exposure-scan') + if (!(landingForm instanceof HTMLFormElement)) { + return + } + landingForm.addEventListener('submit', (e) => { + e.preventDefault() + const emailField = landingForm.elements.namedItem('email') + const newLocation = new URL(document.location) + newLocation.pathname = '/scan/' + newLocation.hash = `#email=${encodeURIComponent(emailField.value)}` + document.location = newLocation.href + }) + landingForm.hidden = false +} diff --git a/public/nextjs_migration/client/js/transitionObserver.js b/public/nextjs_migration/client/js/transitionObserver.js new file mode 100644 index 00000000000..3238fd61a4c --- /dev/null +++ b/public/nextjs_migration/client/js/transitionObserver.js @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// percentage a section that has to be in view in order to appear +const sectionThreshold = 0.1 +const queueIntervalDuration = 150 + +let observers +let queueInterval +// holds the sections so they can appear one after another +const entryQueue = [] + +function handleShowSection () { + if (!entryQueue.length && queueInterval) { + clearInterval(queueInterval) + queueInterval = null + return + } + + const nextEntry = entryQueue.shift() + nextEntry.target.dataset.enterTransition = 'visible' +} + +function setQueueInterval () { + queueInterval = setInterval(handleShowSection, queueIntervalDuration) +} + +function handleScroll (entries) { + entries.forEach(entry => { + const sectionElement = entry.target + const hasEntered = sectionElement.getAttribute('data-enter-transition') === 'visible' + + if (hasEntered) { + return + } + + const isInViewport = entry.isIntersecting + if (isInViewport) { + entryQueue.push(entry) + } + + if (!queueInterval) { + setQueueInterval() + } + }) +} + +function init (sections) { + const observer = new IntersectionObserver(handleScroll, { + threshold: sectionThreshold + }) + + observers = [...sections].map(section => { + section.dataset.enterTransition = 'entering' + + observer.observe(section) + return observer + }) + + setQueueInterval() +} + +if (!observers) { + const mediaQuery = window.matchMedia('(prefers-reduced-motion: no-preference)') + const allowMotion = mediaQuery && mediaQuery.matches + + const sections = document.querySelectorAll('[data-enter-transition]') + + // Don’t hide elements that are marked for entering transitions + // while users set their motion preferences. + mediaQuery.addEventListener('change', () => { + const documentStyle = document.documentElement.style + documentStyle.setProperty('--enter-transition-opacity', '1') + documentStyle.setProperty('--enter-transition-y', '0') + }) + + if (allowMotion) { + init(sections) + } +} diff --git a/src/app/(nextjs_migration)/(guest)/page.tsx b/src/app/(nextjs_migration)/(guest)/page.tsx new file mode 100644 index 00000000000..0fe134f423d --- /dev/null +++ b/src/app/(nextjs_migration)/(guest)/page.tsx @@ -0,0 +1,155 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import Image from "next/image"; +import Script from "next/script"; +import "../../../client/css/partials/landing.css"; + +import { getL10n } from "../../functions/server/l10n"; + +import HeroImage from "../../../client/images/landing-hero@2x.webp"; +import LaptopImage from "../../../client/images/landing-laptop@2x.webp"; +import LockImage from "../../../client/images/landing-lock@2x.webp"; +import MailImage from "../../../client/images/landing-mail@2x.webp"; +import NaturePhoneImage from "../../../client/images/landing-nature-phone@2x.webp"; + +export default function Home() { + const l10n = getL10n(); + + return ( +
+