feat: added main.min.js
This commit is contained in:
parent
4af0fc7cfe
commit
06d19988c7
1 changed files with 175 additions and 0 deletions
175
static/js/main.min.js
vendored
Normal file
175
static/js/main.min.js
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
(() => {
|
||||
'use strict';
|
||||
|
||||
const navbar = document.querySelector('.navbar');
|
||||
const featureCards = document.querySelectorAll('.feature-card');
|
||||
const newsletterCards = document.querySelectorAll('.newsletter-card');
|
||||
const progressBar = document.querySelector('.reading-progress');
|
||||
const emailInput = document.getElementById('email-input');
|
||||
const notifyBtn = document.getElementById('notify-button');
|
||||
const emailRE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
document.addEventListener(
|
||||
'click',
|
||||
(e) => {
|
||||
const a = e.target.closest('a[href^="#"]');
|
||||
if (!a) return;
|
||||
|
||||
const href = a.getAttribute('href');
|
||||
if (!href || href === '#') return;
|
||||
|
||||
const target = document.querySelector(href);
|
||||
if (!target) return;
|
||||
|
||||
e.preventDefault();
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries, obs) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
obs.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
|
||||
);
|
||||
|
||||
featureCards.forEach((card) => {
|
||||
card.classList.add('will-animate');
|
||||
observer.observe(card);
|
||||
});
|
||||
} else {
|
||||
featureCards.forEach((card) => card.classList.add('is-visible'));
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
document
|
||||
.querySelectorAll('.newsletter-header, .newsletter-content')
|
||||
.forEach((el, i) => {
|
||||
el.style.transitionDelay = `${i * 0.2}s`;
|
||||
el.classList.add('is-visible');
|
||||
});
|
||||
|
||||
newsletterCards.forEach((card, i) => {
|
||||
card.style.transitionDelay = `${i * 0.1}s`;
|
||||
card.classList.add('is-visible');
|
||||
});
|
||||
});
|
||||
|
||||
let lastY = 0;
|
||||
let ticking = false;
|
||||
|
||||
function onScroll() {
|
||||
lastY = window.scrollY || window.pageYOffset;
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(updateOnScroll);
|
||||
ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
function updateOnScroll() {
|
||||
if (navbar) {
|
||||
navbar.classList.toggle('navbar--scrolled', lastY > 50);
|
||||
navbar.classList.toggle('navbar--deeper', lastY > 100);
|
||||
}
|
||||
|
||||
if (progressBar) {
|
||||
const max = document.body.scrollHeight - window.innerHeight;
|
||||
const progress = max > 0 ? Math.min(Math.max(lastY / max, 0), 1) : 0;
|
||||
progressBar.style.width = `${progress * 100}%`;
|
||||
}
|
||||
|
||||
ticking = false;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
updateOnScroll(); // initial state
|
||||
|
||||
if (notifyBtn && emailInput) {
|
||||
let inFlight = false;
|
||||
const controller = new AbortController();
|
||||
|
||||
notifyBtn.addEventListener('click', async () => {
|
||||
const email = emailInput.value.trim();
|
||||
if (!emailRE.test(email)) {
|
||||
alert('Please enter a valid email address.');
|
||||
emailInput.focus();
|
||||
return;
|
||||
}
|
||||
if (inFlight) return;
|
||||
|
||||
inFlight = true;
|
||||
notifyBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const res = await fetch('/subscribe', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email }),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
let message = 'Thank you for subscribing!';
|
||||
if (res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
message = data.message || message;
|
||||
} else {
|
||||
message = "Thanks! We'll notify you when we launch.";
|
||||
}
|
||||
|
||||
alert(message);
|
||||
emailInput.value = '';
|
||||
} catch (err) {
|
||||
console.error('Subscribe error:', err);
|
||||
alert("Thanks! We'll notify you when we launch.");
|
||||
emailInput.value = '';
|
||||
} finally {
|
||||
notifyBtn.disabled = false;
|
||||
inFlight = false;
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => controller.abort(), {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
window.shareNewsletter = async function shareNewsletter() {
|
||||
try {
|
||||
if (navigator.share) {
|
||||
await navigator.share({
|
||||
title: document.title,
|
||||
text: 'Check out this newsletter from RideAware',
|
||||
url: location.href,
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('navigator.share error/cancel:', err);
|
||||
}
|
||||
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(location.href);
|
||||
alert('Newsletter URL copied to clipboard!');
|
||||
return;
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
}
|
||||
|
||||
const tmp = document.createElement('input');
|
||||
tmp.value = location.href;
|
||||
document.body.appendChild(tmp);
|
||||
tmp.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(tmp);
|
||||
alert('Newsletter URL copied to clipboard!');
|
||||
};
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue