File size: 3,067 Bytes
a7ffb22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Comets on screen: the crossing comet (or, with reduced motion, one still button), the catch, and the card with the fact.
import { api } from "./api.js";
import { esc } from "./math.js";
import { cometCard, cometSvg } from "./sky.js";

const reducedMotion = () => window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;

/** A small dialog with a comet's card and fact (or a hint for one not caught yet). Closed by its button or Escape; focus returns. */
export function showCard(comet, { heading = "שביט" } = {}) {
  const back = document.activeElement;
  const box = document.createElement("div");
  box.className = "comet-dialog";
  box.innerHTML = `<div class="scrim" data-close></div><div class="sheet" role="dialog" aria-modal="true" aria-labelledby="comet-h">
    <h2 id="comet-h">${esc(heading)}</h2>
    ${comet.caught === false ? `<div class="comet-card"><span class="sym">?</span><b>עוד לא נתפס</b></div>
      <p>שביטים חולפים לפעמים על המסך בזמן תרגול. מי שלוחץ עליהם בזמן תופס אותם.</p>`
      : `${cometCard(comet)}<p>${esc(comet.fact)}</p>`}
    <p><button type="button" class="primary" data-close>סגירה</button></p></div>`;
  document.body.append(box);
  const close = () => { box.remove(); document.removeEventListener("keydown", onKey); back?.focus?.(); };
  const onKey = event => { if (event.key === "Escape") close(); };
  box.addEventListener("click", event => { if (event.target.closest("[data-close]")) close(); });
  document.addEventListener("keydown", onKey);
  box.querySelector("button").focus();
  return close;
}

/** A comet is due: it crosses `host` for about 4 seconds, or, with reduced motion, one still button stays until the next question.
 *  A tap catches it (the server picks which one) and shows the card. `onDone` runs when it is caught or gone. */
export function cross({ feedback, onDone = () => {} } = {}) {
  document.getElementById("comet")?.remove();
  const button = document.createElement("button");
  button.type = "button";
  button.id = "comet";
  const still = reducedMotion();
  button.className = still ? "comet still" : "comet";
  button.setAttribute("aria-label", "שביט! לחצו כדי לתפוס אותו");
  button.innerHTML = still ? "☄ שביט!" : cometSvg();
  (still ? feedback : document.body).append(button);            // a still button sits under the feedback; a moving comet crosses the whole screen
  const gone = () => { if (button.isConnected) { button.remove(); onDone(false); } };
  button.addEventListener("animationend", gone);
  button.addEventListener("click", async () => {
    button.remove();
    try {
      const { comet } = await api("/api/comets/catch", { method: "POST" });
      showCard({ ...comet, caught: true }, { heading: "תפסתם שביט נדיר!" });
      onDone(true);
    } catch { onDone(false); }
  });
  return button;
}

/** Take a crossing comet away (the sheet closed or the person left). */
export const clear = () => document.getElementById("comet")?.remove();