Resume the clock on leaving the score sheet, and split the readout in three
Closing the score sheet left the clock paused, so glancing at the score or nudging a clock cost a tap on play. It now picks the turn back up on the way out. openScore() records whether it was the thing that paused the clock, so a pause the players set themselves survives. After a score change there is nothing to resume — newGame() has already moved to IDLE — and resumeNow() only acts on PAUSE, so that case needs no special handling. The scrim behaves the same, since dismissing discards the staged edits and leaves the turn live. togglePause() had the resume arithmetic inline; it splits out as resumeNow() to match pauseNow(), leaving the play button and the sheet on one path. The score line becomes three labelled lines: You: 2 out of 5 Them: 1 Moves: 7 .head is a two-column grid with a max-content label column, so the values line up whatever the system font renders — no hand-tuned em width to drift between iOS and Android. Each line is a display:contents wrapper, which keeps one element per line to hide when a one-point match drops the score. The gap above Moves rides on that row's two cells, since a display:contents row can't carry a margin, and a :not([hidden]) guard drops it when there is no score line above to separate from. Each half reads from its own side of the board, so both players see their own score on the You line. st.score is already indexed by side, so the mirroring costs nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+54
-30
@@ -75,25 +75,30 @@
|
||||
.panel--top .inner{padding:14px 20px max(14px,env(safe-area-inset-top))}
|
||||
.panel--bottom .inner{padding:14px 20px max(14px,env(safe-area-inset-bottom))}
|
||||
|
||||
/* the label column sizes itself to the widest label, so the three values
|
||||
line up whatever the system font turns out to be */
|
||||
.head{
|
||||
display:flex;flex-direction:column;align-items:flex-start;
|
||||
gap:3px;
|
||||
display:grid;
|
||||
grid-template-columns:max-content max-content;
|
||||
justify-content:start;
|
||||
gap:3px 8px;
|
||||
order:-1;
|
||||
}
|
||||
.score[hidden]{display:none}
|
||||
.score,.moves{
|
||||
font-size:clamp(13px,3.7vw,20px);
|
||||
font-weight:700;
|
||||
letter-spacing:.01em;
|
||||
color:var(--idle-mute);
|
||||
white-space:nowrap;
|
||||
}
|
||||
.panel[data-state="active"] .score,
|
||||
.panel[data-state="active"] .moves,
|
||||
.panel[data-state="flagged"] .score,
|
||||
.panel[data-state="flagged"] .moves{color:var(--accent-mute)}
|
||||
.panel[data-state="flagged"] .score,
|
||||
.panel[data-state="flagged"] .moves{color:rgba(255,255,255,.55)}
|
||||
.line{display:contents}
|
||||
.line[hidden]{display:none}
|
||||
/* set the moves line apart from the score — a display:contents row can't take
|
||||
a margin itself, so both its cells carry it and the row shifts down evenly.
|
||||
The guard drops the space when there's no score line above to separate from. */
|
||||
.line:not([hidden]) + .line--moves > *{margin-top:7px}
|
||||
|
||||
.panel[data-state="active"] .head,
|
||||
.panel[data-state="flagged"] .head{color:var(--accent-mute)}
|
||||
.panel[data-state="flagged"] .head{color:rgba(255,255,255,.55)}
|
||||
|
||||
.stack{
|
||||
flex:1;
|
||||
@@ -313,8 +318,9 @@
|
||||
<section class="panel panel--top" data-side="0" data-state="idle">
|
||||
<div class="inner">
|
||||
<div class="head">
|
||||
<div class="score" hidden>Score: <span data-score>0 - 0</span>, out of <span data-target>1</span></div>
|
||||
<div class="moves">Moves: <span data-moves>0</span></div>
|
||||
<span class="line" hidden><span>You:</span><span><span data-you>0</span> out of <span data-target>1</span></span></span>
|
||||
<span class="line" hidden><span>Them:</span><span data-them>0</span></span>
|
||||
<span class="line line--moves"><span>Moves:</span><span data-moves>0</span></span>
|
||||
</div>
|
||||
<div class="stack">
|
||||
<div class="time" data-time>3:00</div>
|
||||
@@ -373,8 +379,9 @@
|
||||
<section class="panel panel--bottom" data-side="1" data-state="idle">
|
||||
<div class="inner">
|
||||
<div class="head">
|
||||
<div class="score" hidden>Score: <span data-score>0 - 0</span>, out of <span data-target>1</span></div>
|
||||
<div class="moves">Moves: <span data-moves>0</span></div>
|
||||
<span class="line" hidden><span>You:</span><span><span data-you>0</span> out of <span data-target>1</span></span></span>
|
||||
<span class="line" hidden><span>Them:</span><span data-them>0</span></span>
|
||||
<span class="line line--moves"><span>Moves:</span><span data-moves>0</span></span>
|
||||
</div>
|
||||
<div class="stack">
|
||||
<div class="time" data-time>3:00</div>
|
||||
@@ -571,9 +578,10 @@
|
||||
root: p,
|
||||
time: p.querySelector("[data-time]"),
|
||||
moves: p.querySelector("[data-moves]"),
|
||||
score: p.querySelector("[data-score]"),
|
||||
you: p.querySelector("[data-you]"),
|
||||
them: p.querySelector("[data-them]"),
|
||||
target: p.querySelector("[data-target]"),
|
||||
scoreLine: p.querySelector(".score"),
|
||||
scoreLines: [].slice.call(p.querySelectorAll(".line:not(.line--moves)")),
|
||||
delay: p.querySelector("[data-delay]"),
|
||||
fill: p.querySelector("[data-fill]"),
|
||||
dnum: p.querySelector("[data-dnum]")
|
||||
@@ -691,7 +699,8 @@
|
||||
|
||||
el[i].time.textContent = fmt(main);
|
||||
el[i].moves.textContent = st.moves[i];
|
||||
el[i].score.textContent = st.score[i] + " - " + st.score[1 - i];
|
||||
el[i].you.textContent = st.score[i]; // each panel is "You" to its own player
|
||||
el[i].them.textContent = st.score[1 - i];
|
||||
el[i].target.textContent = cfg.points;
|
||||
|
||||
var showDelay = live && cfg.delay > 0 && dLeft > 0;
|
||||
@@ -753,18 +762,19 @@
|
||||
saveGame();
|
||||
}
|
||||
|
||||
function togglePause(){
|
||||
if(st.phase === RUN){
|
||||
pauseNow();
|
||||
return;
|
||||
}else if(st.phase === PAUSE){
|
||||
st.turnAt = performance.now() - st.held;
|
||||
st.phase = RUN;
|
||||
}else return;
|
||||
function resumeNow(){
|
||||
if(st.phase !== PAUSE) return;
|
||||
st.turnAt = performance.now() - st.held; // pick the turn up where it stopped
|
||||
st.phase = RUN;
|
||||
paintPlay();
|
||||
saveGame();
|
||||
}
|
||||
|
||||
function togglePause(){
|
||||
if(st.phase === RUN) pauseNow();
|
||||
else resumeNow();
|
||||
}
|
||||
|
||||
/* SVG elements aren't HTMLElements, so they have no .hidden property —
|
||||
setting it only makes a JS expando. The attribute has to be set by hand. */
|
||||
function showIcon(btn, name, on){
|
||||
@@ -902,7 +912,7 @@
|
||||
if(many) node.removeAttribute("hidden"); else node.setAttribute("hidden", "");
|
||||
}
|
||||
show(btnScore);
|
||||
el.forEach(function(e){ show(e.scoreLine); });
|
||||
el.forEach(function(e){ e.scoreLines.forEach(show); });
|
||||
}
|
||||
|
||||
function clock(ms){ // m:ss, for copy rather than the big readout
|
||||
@@ -1003,12 +1013,23 @@
|
||||
document.getElementById("done").addEventListener("click", closeSheet);
|
||||
// tapping outside the score sheet discards the staged edits rather than
|
||||
// committing them — a stray tap must never restart a game
|
||||
scrim.addEventListener("click", closeSheet);
|
||||
scrim.addEventListener("click", function(){
|
||||
if(openNode === scoreSheet) closeScore(); else closeSheet();
|
||||
});
|
||||
|
||||
/* ============ score sheet ============ */
|
||||
// Edits are staged here and only reach `st` on Done, so opening the sheet,
|
||||
// fiddling and tapping away changes nothing.
|
||||
var edit = null, before = null;
|
||||
var edit = null, before = null, resumeOnClose = false;
|
||||
|
||||
// leaving the sheet with the turn still live carries straight on. After a score
|
||||
// change there is no turn to resume — newGame() has already moved us to IDLE,
|
||||
// which the PAUSE check below quietly skips.
|
||||
function closeScore(){
|
||||
if(resumeOnClose) resumeNow();
|
||||
resumeOnClose = false;
|
||||
closeSheet();
|
||||
}
|
||||
|
||||
function paintScore(){
|
||||
document.getElementById("v-you").textContent = edit.score[1];
|
||||
@@ -1021,6 +1042,9 @@
|
||||
}
|
||||
|
||||
function openScore(){
|
||||
// only the sheet's own pause gets undone on the way out; a pause the players
|
||||
// set themselves survives
|
||||
resumeOnClose = (st.phase === RUN);
|
||||
pauseNow();
|
||||
disarm();
|
||||
// seed from what the panels are showing, not from the banked reserve — for
|
||||
@@ -1074,7 +1098,7 @@
|
||||
document.getElementById("score").addEventListener("click", openScore);
|
||||
document.getElementById("score-done").addEventListener("click", function(){
|
||||
applyScore();
|
||||
closeSheet();
|
||||
closeScore();
|
||||
});
|
||||
|
||||
btnReset.addEventListener("click", armReset);
|
||||
|
||||
Reference in New Issue
Block a user