Ajoute un résumé modal à la fin du cube
This commit is contained in:
165
app.js
165
app.js
@@ -661,6 +661,22 @@ function initCubePage() {
|
|||||||
closeHelpButton: document.querySelector("#closeCubeHelpButton"),
|
closeHelpButton: document.querySelector("#closeCubeHelpButton"),
|
||||||
helpModal: document.querySelector("#cubeHelpModal"),
|
helpModal: document.querySelector("#cubeHelpModal"),
|
||||||
helpStatus: document.querySelector("#cubeHelpStatus"),
|
helpStatus: document.querySelector("#cubeHelpStatus"),
|
||||||
|
resultModal: document.querySelector("#cubeResultModal"),
|
||||||
|
closeResultButton: document.querySelector("#closeCubeResultButton"),
|
||||||
|
resultModalTitle: document.querySelector("#cubeResultModalTitle"),
|
||||||
|
resultSummary: document.querySelector("#cubeResultSummary"),
|
||||||
|
resultWinner: document.querySelector("#cubeResultWinner"),
|
||||||
|
resultOutcome: document.querySelector("#cubeResultOutcome"),
|
||||||
|
resultWhiteName: document.querySelector("#cubeResultWhiteName"),
|
||||||
|
resultBlackName: document.querySelector("#cubeResultBlackName"),
|
||||||
|
resultWhiteTime: document.querySelector("#cubeResultWhiteTime"),
|
||||||
|
resultBlackTime: document.querySelector("#cubeResultBlackTime"),
|
||||||
|
resultWhiteDetail: document.querySelector("#cubeResultWhiteDetail"),
|
||||||
|
resultBlackDetail: document.querySelector("#cubeResultBlackDetail"),
|
||||||
|
resultWhiteClock: document.querySelector("#cubeResultWhiteClock"),
|
||||||
|
resultBlackClock: document.querySelector("#cubeResultBlackClock"),
|
||||||
|
resultActionButton: document.querySelector("#cubeResultActionButton"),
|
||||||
|
resultDismissButton: document.querySelector("#cubeResultDismissButton"),
|
||||||
replayCubeButton: document.querySelector("#replayCubeButton"),
|
replayCubeButton: document.querySelector("#replayCubeButton"),
|
||||||
resetButton: document.querySelector("#cubeResetButton"),
|
resetButton: document.querySelector("#cubeResetButton"),
|
||||||
};
|
};
|
||||||
@@ -669,9 +685,12 @@ function initCubePage() {
|
|||||||
black: createCubeHoldIntent(),
|
black: createCubeHoldIntent(),
|
||||||
};
|
};
|
||||||
let cubeHoldAnimationFrameId = null;
|
let cubeHoldAnimationFrameId = null;
|
||||||
|
let resultModalKey = null;
|
||||||
|
|
||||||
const openModal = () => toggleModal(refs.helpModal, true);
|
const openModal = () => toggleModal(refs.helpModal, true);
|
||||||
const closeModal = () => toggleModal(refs.helpModal, false);
|
const closeModal = () => toggleModal(refs.helpModal, false);
|
||||||
|
const openResultModal = () => toggleModal(refs.resultModal, true);
|
||||||
|
const closeResultModal = () => toggleModal(refs.resultModal, false);
|
||||||
|
|
||||||
refs.openHelpButton?.addEventListener("click", openModal);
|
refs.openHelpButton?.addEventListener("click", openModal);
|
||||||
refs.closeHelpButton?.addEventListener("click", closeModal);
|
refs.closeHelpButton?.addEventListener("click", closeModal);
|
||||||
@@ -681,6 +700,14 @@ function initCubePage() {
|
|||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
refs.closeResultButton?.addEventListener("click", closeResultModal);
|
||||||
|
refs.resultDismissButton?.addEventListener("click", closeResultModal);
|
||||||
|
refs.resultModal?.addEventListener("click", (event) => {
|
||||||
|
const target = event.target;
|
||||||
|
if (target instanceof HTMLElement && target.dataset.closeCubeResultModal === "true") {
|
||||||
|
closeResultModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
bindCubeButton("white", refs.whiteButton);
|
bindCubeButton("white", refs.whiteButton);
|
||||||
bindCubeButton("black", refs.blackButton);
|
bindCubeButton("black", refs.blackButton);
|
||||||
@@ -697,27 +724,47 @@ function initCubePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (match.cube.times.white !== null && match.cube.times.black !== null) {
|
if (match.cube.times.white !== null && match.cube.times.black !== null) {
|
||||||
if (match.config.mode === "twice" && match.cube.times.white === match.cube.times.black) {
|
openResultModal();
|
||||||
replayCubePhase(match);
|
|
||||||
dirty = true;
|
|
||||||
render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
applyCubeOutcome(match);
|
|
||||||
dirty = true;
|
|
||||||
persistMatch();
|
|
||||||
navigateTo("chrono.html");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
refs.resultActionButton?.addEventListener("click", () => {
|
||||||
|
if (!match || match.result || match.phase !== "cube") {
|
||||||
|
closeResultModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.cube.times.white === null || match.cube.times.black === null) {
|
||||||
|
closeResultModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match.config.mode === "twice" && match.cube.times.white === match.cube.times.black) {
|
||||||
|
closeResultModal();
|
||||||
|
resultModalKey = null;
|
||||||
|
replayCubePhase(match);
|
||||||
|
dirty = true;
|
||||||
|
render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeResultModal();
|
||||||
|
applyCubeOutcome(match);
|
||||||
|
dirty = true;
|
||||||
|
persistMatch();
|
||||||
|
navigateTo("chrono.html");
|
||||||
|
});
|
||||||
|
|
||||||
refs.replayCubeButton?.addEventListener("click", () => {
|
refs.replayCubeButton?.addEventListener("click", () => {
|
||||||
|
closeResultModal();
|
||||||
|
resultModalKey = null;
|
||||||
replayCubePhase(match);
|
replayCubePhase(match);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
refs.resetButton?.addEventListener("click", () => {
|
refs.resetButton?.addEventListener("click", () => {
|
||||||
|
closeResultModal();
|
||||||
clearMatch();
|
clearMatch();
|
||||||
replaceTo(SETUP_PAGE);
|
replaceTo(SETUP_PAGE);
|
||||||
});
|
});
|
||||||
@@ -975,6 +1022,97 @@ function initCubePage() {
|
|||||||
hint.textContent = "Maintenez la grande zone 2 secondes, puis relachez pour lancer votre chrono.";
|
hint.textContent = "Maintenez la grande zone 2 secondes, puis relachez pour lancer votre chrono.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCubeResultState() {
|
||||||
|
if (!match || match.phase !== "cube") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const white = match.cube.times.white;
|
||||||
|
const black = match.cube.times.black;
|
||||||
|
if (white === null || black === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const whiteName = playerName(match, "white");
|
||||||
|
const blackName = playerName(match, "black");
|
||||||
|
|
||||||
|
if (match.config.mode === "time") {
|
||||||
|
const preview = getTimeAdjustmentPreview(match, white, black);
|
||||||
|
if (!preview) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: `time:${match.blockNumber}:${match.cube.round}:${white}:${black}`,
|
||||||
|
title: "Résumé du cube",
|
||||||
|
winner: preview.winner ? playerName(match, preview.winner) : "Egalite",
|
||||||
|
outcome:
|
||||||
|
preview.blockType === "minus" ? "Bloc - a appliquer" : "Bloc + a appliquer",
|
||||||
|
summary:
|
||||||
|
"Validez ce résumé pour appliquer les impacts chrono puis revenir à la page chrono.",
|
||||||
|
actionLabel: "Appliquer et ouvrir la page chrono",
|
||||||
|
whiteName,
|
||||||
|
blackName,
|
||||||
|
whiteTime: `Temps cube ${formatStopwatch(white)}`,
|
||||||
|
blackTime: `Temps cube ${formatStopwatch(black)}`,
|
||||||
|
whiteDetail: `Impact chrono ${formatSignedStopwatch(preview.whiteDelta)}`,
|
||||||
|
blackDetail: `Impact chrono ${formatSignedStopwatch(preview.blackDelta)}`,
|
||||||
|
whiteClock: `Chrono apres ${formatSignedClock(preview.whiteAfter)}`,
|
||||||
|
blackClock: `Chrono apres ${formatSignedClock(preview.blackAfter)}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const winner = white < black ? "white" : black < white ? "black" : null;
|
||||||
|
const isTie = winner === null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: `twice:${match.blockNumber}:${match.cube.round}:${white}:${black}`,
|
||||||
|
title: isTie ? "Egalite parfaite" : "Résumé du cube",
|
||||||
|
winner: winner ? playerName(match, winner) : "Egalite parfaite",
|
||||||
|
outcome: isTie ? "Rejouer la phase cube" : `${playerName(match, winner)} ouvrira la partie suivante`,
|
||||||
|
summary: isTie
|
||||||
|
? "Le règlement Twice impose de rejouer immédiatement la phase cube."
|
||||||
|
: "Validez ce résultat pour préparer la partie suivante.",
|
||||||
|
actionLabel: isTie ? "Rejouer la phase cube" : "Appliquer et ouvrir la page chrono",
|
||||||
|
whiteName,
|
||||||
|
blackName,
|
||||||
|
whiteTime: `Temps cube ${formatStopwatch(white)}`,
|
||||||
|
blackTime: `Temps cube ${formatStopwatch(black)}`,
|
||||||
|
whiteDetail: winner === "white" ? "Gagne la phase cube" : isTie ? "Egalite parfaite" : "Ne gagne pas la phase cube",
|
||||||
|
blackDetail: winner === "black" ? "Gagne la phase cube" : isTie ? "Egalite parfaite" : "Ne gagne pas la phase cube",
|
||||||
|
whiteClock: "Aucun impact chrono en mode Twice",
|
||||||
|
blackClock: "Aucun impact chrono en mode Twice",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderResultModal() {
|
||||||
|
const resultState = getCubeResultState();
|
||||||
|
if (!resultState) {
|
||||||
|
closeResultModal();
|
||||||
|
resultModalKey = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refs.resultModalTitle.textContent = resultState.title;
|
||||||
|
refs.resultSummary.textContent = resultState.summary;
|
||||||
|
refs.resultWinner.textContent = resultState.winner;
|
||||||
|
refs.resultOutcome.textContent = resultState.outcome;
|
||||||
|
refs.resultWhiteName.textContent = resultState.whiteName;
|
||||||
|
refs.resultBlackName.textContent = resultState.blackName;
|
||||||
|
refs.resultWhiteTime.textContent = resultState.whiteTime;
|
||||||
|
refs.resultBlackTime.textContent = resultState.blackTime;
|
||||||
|
refs.resultWhiteDetail.textContent = resultState.whiteDetail;
|
||||||
|
refs.resultBlackDetail.textContent = resultState.blackDetail;
|
||||||
|
refs.resultWhiteClock.textContent = resultState.whiteClock;
|
||||||
|
refs.resultBlackClock.textContent = resultState.blackClock;
|
||||||
|
refs.resultActionButton.textContent = resultState.actionLabel;
|
||||||
|
|
||||||
|
if (resultState.key !== resultModalKey) {
|
||||||
|
resultModalKey = resultState.key;
|
||||||
|
openResultModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const blockHeading = formatBlockHeading(match, match.blockNumber);
|
const blockHeading = formatBlockHeading(match, match.blockNumber);
|
||||||
const timePreview =
|
const timePreview =
|
||||||
@@ -1009,7 +1147,7 @@ function initCubePage() {
|
|||||||
refs.spineLabel.textContent = "Reglement";
|
refs.spineLabel.textContent = "Reglement";
|
||||||
refs.spineHeadline.textContent = "Rejouer la phase cube";
|
refs.spineHeadline.textContent = "Rejouer la phase cube";
|
||||||
refs.spineText.textContent = "Le mode Twice impose de relancer immediatement la phase cube en cas d'egalite parfaite.";
|
refs.spineText.textContent = "Le mode Twice impose de relancer immediatement la phase cube en cas d'egalite parfaite.";
|
||||||
refs.primaryButton.textContent = "Rejouer la phase cube";
|
refs.primaryButton.textContent = "Voir le résumé du cube";
|
||||||
refs.helpStatus.textContent = refs.spineText.textContent;
|
refs.helpStatus.textContent = refs.spineText.textContent;
|
||||||
} else if (match.cube.times.white !== null && match.cube.times.black !== null) {
|
} else if (match.cube.times.white !== null && match.cube.times.black !== null) {
|
||||||
if (timePreview) {
|
if (timePreview) {
|
||||||
@@ -1030,7 +1168,7 @@ function initCubePage() {
|
|||||||
refs.spineHeadline.textContent = "Ouvrir la page chrono";
|
refs.spineHeadline.textContent = "Ouvrir la page chrono";
|
||||||
refs.spineText.textContent = "Appliquer le resultat du cube pour preparer la partie suivante.";
|
refs.spineText.textContent = "Appliquer le resultat du cube pour preparer la partie suivante.";
|
||||||
}
|
}
|
||||||
refs.primaryButton.textContent = "Appliquer et ouvrir la page chrono";
|
refs.primaryButton.textContent = "Voir le résumé du cube";
|
||||||
refs.helpStatus.textContent = refs.spineText.textContent;
|
refs.helpStatus.textContent = refs.spineText.textContent;
|
||||||
} else if (match.cube.running) {
|
} else if (match.cube.running) {
|
||||||
refs.centerLabel.textContent = "Etat";
|
refs.centerLabel.textContent = "Etat";
|
||||||
@@ -1060,6 +1198,7 @@ function initCubePage() {
|
|||||||
|
|
||||||
renderCubeZone("black");
|
renderCubeZone("black");
|
||||||
renderCubeZone("white");
|
renderCubeZone("white");
|
||||||
|
renderResultModal();
|
||||||
refs.primaryButton.disabled =
|
refs.primaryButton.disabled =
|
||||||
!match.result &&
|
!match.result &&
|
||||||
!(
|
!(
|
||||||
|
|||||||
54
cube.html
54
cube.html
@@ -135,6 +135,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="modal hidden" id="cubeResultModal" aria-hidden="true">
|
||||||
|
<div class="modal-backdrop" data-close-cube-result-modal="true"></div>
|
||||||
|
<div class="modal-card result-modal-card">
|
||||||
|
<div class="modal-head">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Fin de phase cube</p>
|
||||||
|
<h2 id="cubeResultModalTitle">Résumé du cube</h2>
|
||||||
|
</div>
|
||||||
|
<button class="button ghost small" id="closeCubeResultButton" type="button">
|
||||||
|
Fermer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="section-copy" id="cubeResultSummary"></p>
|
||||||
|
|
||||||
|
<div class="cube-result-overview">
|
||||||
|
<article class="result-pill-card">
|
||||||
|
<span>Vainqueur cube</span>
|
||||||
|
<strong id="cubeResultWinner">--</strong>
|
||||||
|
</article>
|
||||||
|
<article class="result-pill-card">
|
||||||
|
<span>Suite</span>
|
||||||
|
<strong id="cubeResultOutcome">--</strong>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cube-result-player-grid">
|
||||||
|
<article class="cube-result-player-card">
|
||||||
|
<span class="seat-tag light-seat">Blanc</span>
|
||||||
|
<strong id="cubeResultWhiteName">Blanc</strong>
|
||||||
|
<span id="cubeResultWhiteTime">Temps cube --</span>
|
||||||
|
<span id="cubeResultWhiteDetail">Résultat --</span>
|
||||||
|
<span id="cubeResultWhiteClock">Chrono après --</span>
|
||||||
|
</article>
|
||||||
|
<article class="cube-result-player-card">
|
||||||
|
<span class="seat-tag dark-seat">Noir</span>
|
||||||
|
<strong id="cubeResultBlackName">Noir</strong>
|
||||||
|
<span id="cubeResultBlackTime">Temps cube --</span>
|
||||||
|
<span id="cubeResultBlackDetail">Résultat --</span>
|
||||||
|
<span id="cubeResultBlackClock">Chrono après --</span>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="button primary" id="cubeResultActionButton" type="button">
|
||||||
|
Appliquer et ouvrir la page chrono
|
||||||
|
</button>
|
||||||
|
<button class="button ghost" id="cubeResultDismissButton" type="button">
|
||||||
|
Revenir à la phase cube
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<script type="module" src="app.js"></script>
|
<script type="module" src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
46
styles.css
46
styles.css
@@ -888,6 +888,47 @@ body[data-page="cube"] .zone-button.cube-hold-ready::after {
|
|||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result-modal-card {
|
||||||
|
width: min(880px, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cube-result-overview {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-pill-card,
|
||||||
|
.cube-result-player-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.35rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 22px;
|
||||||
|
border: 1px solid var(--panel-border);
|
||||||
|
background: var(--panel-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-pill-card span,
|
||||||
|
.cube-result-player-card span {
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-pill-card strong {
|
||||||
|
font-size: clamp(1.3rem, 3vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cube-result-player-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cube-result-player-card strong {
|
||||||
|
font-size: clamp(1.15rem, 2.8vw, 1.8rem);
|
||||||
|
}
|
||||||
|
|
||||||
.rules-shell {
|
.rules-shell {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: min(1220px, calc(100% - 2rem));
|
width: min(1220px, calc(100% - 2rem));
|
||||||
@@ -1219,6 +1260,11 @@ body[data-page="cube"] .zone-button.cube-hold-ready::after {
|
|||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cube-result-overview,
|
||||||
|
.cube-result-player-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.phase-shell {
|
.phase-shell {
|
||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
padding: 0.55rem 0 0.55rem;
|
padding: 0.55rem 0 0.55rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user