Spaces:
Running
Running
arrow and highlight timeline step
Browse files
app.js
CHANGED
|
@@ -721,6 +721,8 @@ function renderTimeline() {
|
|
| 721 |
const v = state.visible || 1;
|
| 722 |
for (let i = 0; i < v; i++) addStep(host, STEPS[i], i + 1);
|
| 723 |
expandDetailsForStep(state.step);
|
|
|
|
|
|
|
| 724 |
}
|
| 725 |
|
| 726 |
function addStep(host, step, idx) {
|
|
@@ -890,6 +892,11 @@ function expandDetailsForStep(stepNum) {
|
|
| 890 |
const btn = document.querySelector(`button.toggle[data-fold="${current.key}"]`);
|
| 891 |
if (btn) btn.setAttribute("aria-expanded", "true");
|
| 892 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 893 |
}
|
| 894 |
|
| 895 |
/* ------------ Dots / progress ------------ */
|
|
@@ -915,6 +922,7 @@ function renderDots(visible) {
|
|
| 915 |
askCurrent();
|
| 916 |
scrollToStep(i);
|
| 917 |
highlightDot();
|
|
|
|
| 918 |
});
|
| 919 |
}
|
| 920 |
d.appendChild(dot);
|
|
@@ -959,6 +967,8 @@ function askCurrent() {
|
|
| 959 |
}
|
| 960 |
|
| 961 |
expandDetailsForStep(state.step);
|
|
|
|
|
|
|
| 962 |
|
| 963 |
if (state.step === 7) {
|
| 964 |
const seq = FLOW[7];
|
|
@@ -1014,6 +1024,7 @@ function goto(dest) {
|
|
| 1014 |
askCurrent();
|
| 1015 |
scrollToStep(state.step);
|
| 1016 |
save(true);
|
|
|
|
| 1017 |
}
|
| 1018 |
|
| 1019 |
/* Called when step 1 completes to auto reveal step 2 */
|
|
@@ -1175,7 +1186,58 @@ function clearAll() {
|
|
| 1175 |
updateProgress();
|
| 1176 |
}
|
| 1177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1178 |
/* ------------ Boot ------------ */
|
|
|
|
|
|
|
|
|
|
| 1179 |
load();
|
| 1180 |
renderPurposes();
|
| 1181 |
if (state.purpose && state.purpose !== "anon") {
|
|
|
|
| 721 |
const v = state.visible || 1;
|
| 722 |
for (let i = 0; i < v; i++) addStep(host, STEPS[i], i + 1);
|
| 723 |
expandDetailsForStep(state.step);
|
| 724 |
+
markCurrentStep(state.step);
|
| 725 |
+
requestAnimationFrame(drawLinkToFlow);
|
| 726 |
}
|
| 727 |
|
| 728 |
function addStep(host, step, idx) {
|
|
|
|
| 892 |
const btn = document.querySelector(`button.toggle[data-fold="${current.key}"]`);
|
| 893 |
if (btn) btn.setAttribute("aria-expanded", "true");
|
| 894 |
}
|
| 895 |
+
// highlight and link
|
| 896 |
+
markCurrentStep(stepNum);
|
| 897 |
+
// Wait for layout to settle then draw the link
|
| 898 |
+
requestAnimationFrame(drawLinkToFlow);
|
| 899 |
+
|
| 900 |
}
|
| 901 |
|
| 902 |
/* ------------ Dots / progress ------------ */
|
|
|
|
| 922 |
askCurrent();
|
| 923 |
scrollToStep(i);
|
| 924 |
highlightDot();
|
| 925 |
+
requestAnimationFrame(drawLinkToFlow);
|
| 926 |
});
|
| 927 |
}
|
| 928 |
d.appendChild(dot);
|
|
|
|
| 967 |
}
|
| 968 |
|
| 969 |
expandDetailsForStep(state.step);
|
| 970 |
+
markCurrentStep(state.step);
|
| 971 |
+
requestAnimationFrame(drawLinkToFlow);
|
| 972 |
|
| 973 |
if (state.step === 7) {
|
| 974 |
const seq = FLOW[7];
|
|
|
|
| 1024 |
askCurrent();
|
| 1025 |
scrollToStep(state.step);
|
| 1026 |
save(true);
|
| 1027 |
+
requestAnimationFrame(drawLinkToFlow);
|
| 1028 |
}
|
| 1029 |
|
| 1030 |
/* Called when step 1 completes to auto reveal step 2 */
|
|
|
|
| 1186 |
updateProgress();
|
| 1187 |
}
|
| 1188 |
|
| 1189 |
+
/* ------------ (a) mark the current step and (b) draw the arrow ------------ */
|
| 1190 |
+
function markCurrentStep(stepNum) {
|
| 1191 |
+
// Clear old
|
| 1192 |
+
$$("#timeline .step").forEach(el => el.classList.remove("current"));
|
| 1193 |
+
const current = document.querySelector(`.step:nth-of-type(${stepNum})`);
|
| 1194 |
+
if (current) current.classList.add("current");
|
| 1195 |
+
}
|
| 1196 |
+
|
| 1197 |
+
function drawLinkToFlow() {
|
| 1198 |
+
const flowEl = $("#flow");
|
| 1199 |
+
const svg = $("#linkSvg");
|
| 1200 |
+
const path = $("#linkPath");
|
| 1201 |
+
|
| 1202 |
+
// Hide arrow if flow is hidden or no current step
|
| 1203 |
+
const current = document.querySelector(`.step:nth-of-type(${state.step})`);
|
| 1204 |
+
const flowHidden = flowEl.classList.contains("hidden");
|
| 1205 |
+
if (!current || flowHidden) {
|
| 1206 |
+
if (path) path.setAttribute("d", "");
|
| 1207 |
+
return;
|
| 1208 |
+
}
|
| 1209 |
+
|
| 1210 |
+
// Dimensions
|
| 1211 |
+
const sRect = current.getBoundingClientRect();
|
| 1212 |
+
const fRect = flowEl.getBoundingClientRect();
|
| 1213 |
+
|
| 1214 |
+
// Start: bottom-center of current step card
|
| 1215 |
+
const x1 = sRect.left + sRect.width / 2;
|
| 1216 |
+
const y1 = sRect.bottom + 4; // a tiny gap below the step
|
| 1217 |
+
|
| 1218 |
+
// End: top-center of the flow bar
|
| 1219 |
+
const x2 = fRect.left + fRect.width / 2;
|
| 1220 |
+
const y2 = fRect.top - 6; // a tiny gap above the bar
|
| 1221 |
+
|
| 1222 |
+
// Control points for a smooth bent curve
|
| 1223 |
+
const midY = (y1 + y2) / 2;
|
| 1224 |
+
const c1x = x1;
|
| 1225 |
+
const c1y = midY;
|
| 1226 |
+
const c2x = x2;
|
| 1227 |
+
const c2y = midY;
|
| 1228 |
+
|
| 1229 |
+
// Resize SVG viewBox to viewport and draw
|
| 1230 |
+
svg.setAttribute("viewBox", `0 0 ${window.innerWidth} ${window.innerHeight}`);
|
| 1231 |
+
path.setAttribute("d", `M ${x1} ${y1} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`);
|
| 1232 |
+
|
| 1233 |
+
// Optional: animate the path slightly on change (simple stroke-dash trick)
|
| 1234 |
+
path.style.transition = "d 0.2s ease";
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
/* ------------ Boot ------------ */
|
| 1238 |
+
// Keep arrow aligned on resize/scroll
|
| 1239 |
+
window.addEventListener("resize", drawLinkToFlow, { passive: true });
|
| 1240 |
+
window.addEventListener("scroll", drawLinkToFlow, { passive: true });
|
| 1241 |
load();
|
| 1242 |
renderPurposes();
|
| 1243 |
if (state.purpose && state.purpose !== "anon") {
|