Files
rea/website/public/assets/home.js
T
7c962cc70b feat(website): simplify the reader journey and beginner setup (#3274)
Lead new readers from compact setup through the preserved dinosaur and
Calculator examples, with code, comparisons, and verification in disclosures.
Expand Windows, macOS, and Linux onboarding, client/provider choices,
on-site curl instructions, and the supplied first investigation.
Complete Get started translations while retaining executable literals,
historical evidence, and main's explicitly partial Greek fallback.

Verified the integrated website, its 12 maintenance regressions, all tracked
formatting, typecheck/lint, 38 locale/base-path browser combinations, and
the public Funnel preview. Native installers and providers were not run.
The separate manual production publication workflow is not triggered here.

Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-10-14 08:36:00 +00:00

135 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { t } from "./i18n.js";
function initializePercentDemo() {
const demo = document.querySelector("[data-percent-demo]");
if (demo === null) return;
const display = demo.querySelector("[data-percent-display]");
const expression = demo.querySelector("[data-percent-expression]");
const status = demo.querySelector("[data-percent-status]");
const equals = demo.querySelector('[data-percent-key="equals"]');
const operations = demo.querySelectorAll("[data-percent-operation]");
let operation = "add";
let stage = "input";
const render = () => {
const adding = operation === "add";
const previous = 200;
const current = 10;
const percent = adding ? (current * previous) / 100 : current / 100;
const result = adding ? previous + percent : previous * percent;
const operator = adding ? "+" : "×";
expression.textContent =
stage === "result" ? `200 ${operator} ${percent} =` : `200 ${operator}`;
display.textContent = String(
stage === "input" ? current : stage === "percent" ? percent : result,
);
equals.disabled = stage !== "percent";
status.textContent =
stage === "input"
? t("Press % to see what happens to 10.")
: stage === "percent"
? adding
? t("10% of 200 is 20. Press = to add it.")
: t("For multiplication, 10 becomes 0.1. Press = to multiply.")
: `200 ${operator} ${percent} = ${result}.`;
};
operations.forEach((button) => {
button.addEventListener("click", () => {
operation = button.getAttribute("data-percent-operation");
stage = "input";
operations.forEach((candidate) => {
candidate.setAttribute("aria-pressed", String(candidate === button));
});
render();
});
});
demo.querySelectorAll("[data-percent-key]").forEach((button) => {
button.addEventListener("click", () => {
const key = button.getAttribute("data-percent-key");
stage =
key === "reset" ? "input" : key === "percent" ? "percent" : "result";
render();
});
});
demo.querySelector(".percent-operation").hidden = false;
demo.querySelector(".percent-controls").hidden = false;
render();
}
function initializeHomeNavigation() {
const menu = document.querySelector("[data-home-nav]");
if (menu === null) return;
const currentLabel = menu.querySelector("[data-home-nav-current]");
if (currentLabel === null) return;
const wideScreen = window.matchMedia("(min-width: 1440px)");
const entries = Array.from(menu.querySelectorAll('a[href^="#"]'))
.map((link) => ({
link,
target: document.getElementById(link.getAttribute("href").slice(1)),
}))
.filter((entry) => entry.target !== null);
if (entries.length === 0) return;
const updatePosition = () => {
const offset = wideScreen.matches ? 40 : 88;
let current = entries[0];
entries.forEach((entry) => {
if (entry.target.getBoundingClientRect().top <= offset) current = entry;
});
if (
window.scrollY + window.innerHeight >=
document.documentElement.scrollHeight - 2
) {
const selected = entries.find(
(entry) => entry.link.hash === window.location.hash,
);
const selectedTop = selected?.target.getBoundingClientRect().top;
current =
selectedTop !== undefined &&
selectedTop >= 0 &&
selectedTop < window.innerHeight
? selected
: entries[entries.length - 1];
}
entries.forEach((entry) => {
if (entry === current)
entry.link.setAttribute("aria-current", "location");
else entry.link.removeAttribute("aria-current");
});
currentLabel.textContent = current.link.textContent;
};
const updateLayout = () => {
menu.open = wideScreen.matches;
updatePosition();
};
let scheduled = false;
const schedulePosition = () => {
if (scheduled) return;
scheduled = true;
window.requestAnimationFrame(() => {
scheduled = false;
updatePosition();
});
};
entries.forEach(({ link, target }) => {
link.addEventListener("click", () => {
if (!wideScreen.matches) menu.open = false;
const heading = target.querySelector("h2") ?? target;
heading.setAttribute("tabindex", "-1");
window.requestAnimationFrame(() =>
heading.focus({ preventScroll: true }),
);
});
});
wideScreen.addEventListener("change", updateLayout);
window.addEventListener("scroll", schedulePosition, { passive: true });
window.addEventListener("resize", schedulePosition, { passive: true });
window.addEventListener("hashchange", schedulePosition);
window.addEventListener("load", schedulePosition);
updateLayout();
}
initializePercentDemo();
initializeHomeNavigation();