119 lines
4.2 KiB
JavaScript
119 lines
4.2 KiB
JavaScript
/* The Founder's Break - nav state, scroll reveal, RSVP submit.
|
|
No scroll listeners: nav state and reveal both ride IntersectionObserver. */
|
|
|
|
(function () {
|
|
"use strict";
|
|
|
|
var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
|
|
/* ── Nav background once the page has moved off the top ── */
|
|
var nav = document.getElementById("nav");
|
|
var sentinel = document.createElement("div");
|
|
sentinel.style.cssText = "position:absolute;top:0;left:0;width:1px;height:1px;pointer-events:none";
|
|
document.body.prepend(sentinel);
|
|
new IntersectionObserver(function (entries) {
|
|
nav.classList.toggle("scrolled", !entries[0].isIntersecting);
|
|
}).observe(sentinel);
|
|
|
|
/* ── Scroll reveal: sequences sections in as they arrive ── */
|
|
var reveals = document.querySelectorAll(".reveal");
|
|
if (reduceMotion) {
|
|
reveals.forEach(function (el) { el.classList.add("in"); });
|
|
} else {
|
|
var io = new IntersectionObserver(function (entries, obs) {
|
|
entries.forEach(function (entry, i) {
|
|
if (!entry.isIntersecting) return;
|
|
var delay = Math.min(i, 4) * 70;
|
|
setTimeout(function () { entry.target.classList.add("in"); }, delay);
|
|
obs.unobserve(entry.target);
|
|
});
|
|
}, { threshold: 0.15, rootMargin: "0px 0px -8% 0px" });
|
|
reveals.forEach(function (el) { io.observe(el); });
|
|
}
|
|
|
|
/* ── RSVP ── */
|
|
var form = document.getElementById("rsvp-form");
|
|
if (!form) return;
|
|
|
|
var submit = document.getElementById("rsvp-submit");
|
|
var status = document.getElementById("form-status");
|
|
var done = document.getElementById("rsvp-done");
|
|
var nameField = document.getElementById("f-name");
|
|
var emailField = document.getElementById("f-email");
|
|
|
|
function setError(input, message) {
|
|
var field = input.closest(".field");
|
|
field.classList.toggle("has-error", Boolean(message));
|
|
var slot = field.querySelector(".field-error");
|
|
if (slot) slot.textContent = message || "";
|
|
input.setAttribute("aria-invalid", message ? "true" : "false");
|
|
}
|
|
|
|
function validate() {
|
|
var ok = true;
|
|
if (!nameField.value.trim()) { setError(nameField, "We need a name to put on the list."); ok = false; }
|
|
else setError(nameField, "");
|
|
|
|
var email = emailField.value.trim();
|
|
if (!email) { setError(emailField, "We need an email to send the reminder to."); ok = false; }
|
|
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(email)) { setError(emailField, "That email does not look right."); ok = false; }
|
|
else setError(emailField, "");
|
|
|
|
return ok;
|
|
}
|
|
|
|
[nameField, emailField].forEach(function (input) {
|
|
input.addEventListener("input", function () {
|
|
if (input.closest(".field").classList.contains("has-error")) validate();
|
|
});
|
|
});
|
|
|
|
form.addEventListener("submit", function (event) {
|
|
event.preventDefault();
|
|
status.textContent = "";
|
|
|
|
if (!validate()) {
|
|
form.querySelector(".has-error input").focus();
|
|
return;
|
|
}
|
|
|
|
submit.setAttribute("aria-busy", "true");
|
|
|
|
var payload = new URLSearchParams();
|
|
new FormData(form).forEach(function (value, key) {
|
|
if (key !== "_redirect") payload.append(key, value);
|
|
});
|
|
|
|
fetch(form.action, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
|
Accept: "application/json",
|
|
},
|
|
body: payload.toString(),
|
|
})
|
|
.then(function (res) {
|
|
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
return res.json().catch(function () { return { ok: true }; });
|
|
})
|
|
.then(function (body) {
|
|
if (body && body.ok === false) throw new Error(body.error || "rejected");
|
|
showDone();
|
|
})
|
|
.catch(function () {
|
|
submit.removeAttribute("aria-busy");
|
|
status.textContent =
|
|
"That did not go through. Try again, or email contact@onetouch.studio and we will add you by hand.";
|
|
});
|
|
});
|
|
|
|
function showDone() {
|
|
form.hidden = true;
|
|
done.hidden = false;
|
|
done.setAttribute("tabindex", "-1");
|
|
done.focus({ preventScroll: true });
|
|
}
|
|
|
|
/* No-JS fallback lands back here with ?rsvp=ok after the plain form POST. */
|
|
if (new URLSearchParams(window.location.search).get("rsvp") === "ok") showDone();
|
|
})();
|