Compare commits
2 Commits
b74f8e1bb1
...
96bb05a4ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96bb05a4ba | ||
|
|
4e07fcf38b |
@@ -205,7 +205,12 @@ var RoleSelect = (function () {
|
|||||||
|
|
||||||
var _reload = function () { window.location.reload(); };
|
var _reload = function () { window.location.reload(); };
|
||||||
|
|
||||||
function handleRolesRevealed() {
|
function handleAllRolesFilled() {
|
||||||
|
var wrap = document.getElementById('id_pick_sigs_wrap');
|
||||||
|
if (wrap) wrap.style.display = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSigSelectStarted() {
|
||||||
_reload();
|
_reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +296,8 @@ var RoleSelect = (function () {
|
|||||||
|
|
||||||
window.addEventListener("room:role_select_start", init);
|
window.addEventListener("room:role_select_start", init);
|
||||||
window.addEventListener("room:turn_changed", handleTurnChanged);
|
window.addEventListener("room:turn_changed", handleTurnChanged);
|
||||||
window.addEventListener("room:roles_revealed", handleRolesRevealed);
|
window.addEventListener("room:all_roles_filled", handleAllRolesFilled);
|
||||||
|
window.addEventListener("room:sig_select_started", handleSigSelectStarted);
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", init);
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
var Tray = (function () {
|
var Tray = (function () {
|
||||||
var _open = false;
|
var _open = false;
|
||||||
|
// Fallback timeout (ms) after close() in placeCard in case transitionend
|
||||||
|
// never fires (e.g. CSS transitions disabled). Zeroed by reset() for tests.
|
||||||
|
var _closeTransitionMs = 600;
|
||||||
var _dragStartX = null;
|
var _dragStartX = null;
|
||||||
var _dragStartY = null;
|
var _dragStartY = null;
|
||||||
var _dragStartLeft = null;
|
var _dragStartLeft = null;
|
||||||
@@ -235,7 +238,9 @@ var Tray = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// placeCard(roleCode, onComplete) — mark the first tray cell with the role,
|
// placeCard(roleCode, onComplete) — mark the first tray cell with the role,
|
||||||
// open the tray, arc-in the cell, then force-close. Calls onComplete after.
|
// open the tray, arc-in the cell, then animated-close. Calls onComplete after
|
||||||
|
// the close slide finishes (transitionend), with a fallback timeout in case
|
||||||
|
// CSS transitions are disabled (e.g. test environments).
|
||||||
// The grid always contains exactly 8 .tray-cell elements (from the template);
|
// The grid always contains exactly 8 .tray-cell elements (from the template);
|
||||||
// the first one receives .tray-role-card and data-role instead of a new element
|
// the first one receives .tray-role-card and data-role instead of a new element
|
||||||
// being inserted, so the cell count never changes.
|
// being inserted, so the cell count never changes.
|
||||||
@@ -250,8 +255,22 @@ var Tray = (function () {
|
|||||||
|
|
||||||
open();
|
open();
|
||||||
_arcIn(firstCell, function () {
|
_arcIn(firstCell, function () {
|
||||||
forceClose();
|
close();
|
||||||
if (onComplete) onComplete();
|
if (!onComplete) return;
|
||||||
|
if (!_wrap) { onComplete(); return; }
|
||||||
|
var propName = _isLandscape() ? 'top' : 'left';
|
||||||
|
var done = false;
|
||||||
|
function finish() {
|
||||||
|
if (done) return;
|
||||||
|
done = true;
|
||||||
|
if (_wrap) _wrap.removeEventListener('transitionend', onCloseEnd);
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
function onCloseEnd(e) {
|
||||||
|
if (e.propertyName === propName) finish();
|
||||||
|
}
|
||||||
|
_wrap.addEventListener('transitionend', onCloseEnd);
|
||||||
|
setTimeout(finish, _closeTransitionMs);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +444,7 @@ var Tray = (function () {
|
|||||||
// reset() — restores module state; used by Jasmine afterEach
|
// reset() — restores module state; used by Jasmine afterEach
|
||||||
function reset() {
|
function reset() {
|
||||||
_open = false;
|
_open = false;
|
||||||
|
_closeTransitionMs = 0;
|
||||||
_dragStartX = null;
|
_dragStartX = null;
|
||||||
_dragStartY = null;
|
_dragStartY = null;
|
||||||
_dragStartLeft = null;
|
_dragStartLeft = null;
|
||||||
|
|||||||
@@ -127,10 +127,30 @@ describe("RoleSelect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
// room:roles_revealed event //
|
// room:all_roles_filled event //
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
describe("room:roles_revealed event", () => {
|
describe("room:all_roles_filled event", () => {
|
||||||
|
let pickSigsWrap;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pickSigsWrap = document.createElement("div");
|
||||||
|
pickSigsWrap.id = "id_pick_sigs_wrap";
|
||||||
|
pickSigsWrap.style.display = "none";
|
||||||
|
testDiv.appendChild(pickSigsWrap);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows #id_pick_sigs_wrap", () => {
|
||||||
|
window.dispatchEvent(new CustomEvent("room:all_roles_filled", { detail: {} }));
|
||||||
|
expect(pickSigsWrap.style.display).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// room:sig_select_started event //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
describe("room:sig_select_started event", () => {
|
||||||
let reloadCalled;
|
let reloadCalled;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -143,7 +163,7 @@ describe("RoleSelect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("triggers a page reload", () => {
|
it("triggers a page reload", () => {
|
||||||
window.dispatchEvent(new CustomEvent("room:roles_revealed", { detail: {} }));
|
window.dispatchEvent(new CustomEvent("room:sig_select_started", { detail: {} }));
|
||||||
expect(reloadCalled).toBe(true);
|
expect(reloadCalled).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ describe("Tray", () => {
|
|||||||
expect(firstCell.classList.contains("arc-in")).toBe(true);
|
expect(firstCell.classList.contains("arc-in")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes .arc-in and force-closes after animationend", () => {
|
it("removes .arc-in and closes after animationend", () => {
|
||||||
Tray.placeCard("PC", null);
|
Tray.placeCard("PC", null);
|
||||||
expect(Tray.isOpen()).toBe(true);
|
expect(Tray.isOpen()).toBe(true);
|
||||||
firstCell.dispatchEvent(new Event("animationend"));
|
firstCell.dispatchEvent(new Event("animationend"));
|
||||||
@@ -497,6 +497,10 @@ describe("Tray", () => {
|
|||||||
let called = false;
|
let called = false;
|
||||||
Tray.placeCard("PC", () => { called = true; });
|
Tray.placeCard("PC", () => { called = true; });
|
||||||
firstCell.dispatchEvent(new Event("animationend"));
|
firstCell.dispatchEvent(new Event("animationend"));
|
||||||
|
// Simulate the close transition completing (portrait: 'left' property)
|
||||||
|
const te = new Event("transitionend");
|
||||||
|
te.propertyName = "left";
|
||||||
|
wrap.dispatchEvent(te);
|
||||||
expect(called).toBe(true);
|
expect(called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -473,6 +473,16 @@ html:has(.gate-backdrop) .position-strip .gate-slot button { pointer-events: aut
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Outside .room-table-scene so it isn't scaled by scaleTable().
|
||||||
|
// Positioned absolute so it floats over the hex without affecting flex layout.
|
||||||
|
#id_pick_sigs_wrap {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
.table-center {
|
.table-center {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ $handle-r: 1rem;
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
transition: left 1.0s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: left 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
&.tray-dragging { transition: none; }
|
&.tray-dragging { transition: none; }
|
||||||
&.wobble { animation: tray-wobble .45s ease; }
|
&.wobble { animation: tray-wobble .45s ease; }
|
||||||
&.snap { animation: tray-snap 1.0s ease; }
|
&.snap { animation: tray-snap 0.30s ease; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#id_tray_handle {
|
#id_tray_handle {
|
||||||
@@ -222,11 +222,11 @@ $handle-r: 1rem;
|
|||||||
right: $sidebar-w;
|
right: $sidebar-w;
|
||||||
top: auto; // JS controls style.top for the Y-axis slide
|
top: auto; // JS controls style.top for the Y-axis slide
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
transition: top 1.0s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: top 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
&.tray-dragging { transition: none; }
|
&.tray-dragging { transition: none; }
|
||||||
&.wobble { animation: tray-wobble-landscape 0.45s ease; }
|
&.wobble { animation: tray-wobble-landscape 0.45s ease; }
|
||||||
&.snap { animation: tray-snap-landscape 1.0s ease; }
|
&.snap { animation: tray-snap-landscape 0.30s ease; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,10 +127,30 @@ describe("RoleSelect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
// room:roles_revealed event //
|
// room:all_roles_filled event //
|
||||||
// ------------------------------------------------------------------ //
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
describe("room:roles_revealed event", () => {
|
describe("room:all_roles_filled event", () => {
|
||||||
|
let pickSigsWrap;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
pickSigsWrap = document.createElement("div");
|
||||||
|
pickSigsWrap.id = "id_pick_sigs_wrap";
|
||||||
|
pickSigsWrap.style.display = "none";
|
||||||
|
testDiv.appendChild(pickSigsWrap);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows #id_pick_sigs_wrap", () => {
|
||||||
|
window.dispatchEvent(new CustomEvent("room:all_roles_filled", { detail: {} }));
|
||||||
|
expect(pickSigsWrap.style.display).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
// room:sig_select_started event //
|
||||||
|
// ------------------------------------------------------------------ //
|
||||||
|
|
||||||
|
describe("room:sig_select_started event", () => {
|
||||||
let reloadCalled;
|
let reloadCalled;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -143,7 +163,7 @@ describe("RoleSelect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("triggers a page reload", () => {
|
it("triggers a page reload", () => {
|
||||||
window.dispatchEvent(new CustomEvent("room:roles_revealed", { detail: {} }));
|
window.dispatchEvent(new CustomEvent("room:sig_select_started", { detail: {} }));
|
||||||
expect(reloadCalled).toBe(true);
|
expect(reloadCalled).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -173,7 +193,6 @@ describe("RoleSelect", () => {
|
|||||||
|
|
||||||
trayWrap = document.createElement("div");
|
trayWrap = document.createElement("div");
|
||||||
trayWrap.id = "id_tray_wrap";
|
trayWrap.id = "id_tray_wrap";
|
||||||
// Simulate server-side class during ROLE_SELECT
|
|
||||||
trayWrap.className = "role-select-phase";
|
trayWrap.className = "role-select-phase";
|
||||||
testDiv.appendChild(trayWrap);
|
testDiv.appendChild(trayWrap);
|
||||||
});
|
});
|
||||||
@@ -186,6 +205,13 @@ describe("RoleSelect", () => {
|
|||||||
expect(Tray.forceClose).toHaveBeenCalled();
|
expect(Tray.forceClose).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("clears .active from all seats on turn change", () => {
|
||||||
|
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||||
|
detail: { active_slot: 2 }
|
||||||
|
}));
|
||||||
|
expect(testDiv.querySelector(".table-seat.active")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("re-adds role-select-phase to tray wrap on turn change", () => {
|
it("re-adds role-select-phase to tray wrap on turn change", () => {
|
||||||
trayWrap.classList.remove("role-select-phase"); // simulate it was shown
|
trayWrap.classList.remove("role-select-phase"); // simulate it was shown
|
||||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||||
@@ -194,13 +220,6 @@ describe("RoleSelect", () => {
|
|||||||
expect(trayWrap.classList.contains("role-select-phase")).toBe(true);
|
expect(trayWrap.classList.contains("role-select-phase")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clears .active from all seats on turn change", () => {
|
|
||||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
|
||||||
detail: { active_slot: 2 }
|
|
||||||
}));
|
|
||||||
expect(testDiv.querySelector(".table-seat.active")).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("removes .active from the previously active seat", () => {
|
it("removes .active from the previously active seat", () => {
|
||||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||||
detail: { active_slot: 2 }
|
detail: { active_slot: 2 }
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ describe("Tray", () => {
|
|||||||
expect(firstCell.classList.contains("arc-in")).toBe(true);
|
expect(firstCell.classList.contains("arc-in")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes .arc-in and force-closes after animationend", () => {
|
it("removes .arc-in and closes after animationend", () => {
|
||||||
Tray.placeCard("PC", null);
|
Tray.placeCard("PC", null);
|
||||||
expect(Tray.isOpen()).toBe(true);
|
expect(Tray.isOpen()).toBe(true);
|
||||||
firstCell.dispatchEvent(new Event("animationend"));
|
firstCell.dispatchEvent(new Event("animationend"));
|
||||||
@@ -497,6 +497,10 @@ describe("Tray", () => {
|
|||||||
let called = false;
|
let called = false;
|
||||||
Tray.placeCard("PC", () => { called = true; });
|
Tray.placeCard("PC", () => { called = true; });
|
||||||
firstCell.dispatchEvent(new Event("animationend"));
|
firstCell.dispatchEvent(new Event("animationend"));
|
||||||
|
// Simulate the close transition completing (portrait: 'left' property)
|
||||||
|
const te = new Event("transitionend");
|
||||||
|
te.propertyName = "left";
|
||||||
|
wrap.dispatchEvent(te);
|
||||||
expect(called).toBe(true);
|
expect(called).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,20 @@
|
|||||||
{% if room.table_status %}data-select-role-url="{% url 'epic:select_role' room.id %}"{% endif %}>
|
{% if room.table_status %}data-select-role-url="{% url 'epic:select_role' room.id %}"{% endif %}>
|
||||||
<div class="room-shell">
|
<div class="room-shell">
|
||||||
<div id="id_game_table" class="room-table">
|
<div id="id_game_table" class="room-table">
|
||||||
|
{% if room.table_status == "ROLE_SELECT" %}
|
||||||
|
<div id="id_pick_sigs_wrap"{% if starter_roles|length < 6 %} style="display:none"{% endif %}>
|
||||||
|
<form method="POST" action="{% url 'epic:pick_sigs' room.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-primary btn-xl">PICK<br>SIGS</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<div class="room-table-scene">
|
<div class="room-table-scene">
|
||||||
<div class="table-hex-border">
|
<div class="table-hex-border">
|
||||||
<div class="table-hex">
|
<div class="table-hex">
|
||||||
<div class="table-center">
|
<div class="table-center">
|
||||||
{% if room.table_status == "ROLE_SELECT" %}
|
{% if room.table_status == "ROLE_SELECT" %}
|
||||||
{% if starter_roles|length == 6 %}
|
{% if card_stack_state %}
|
||||||
<form method="POST" action="{% url 'epic:pick_sigs' room.id %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button type="submit" class="pick-sigs-btn btn btn-primary btn-xl">PICK SIGS</button>
|
|
||||||
</form>
|
|
||||||
{% elif card_stack_state %}
|
|
||||||
<div class="card-stack" data-state="{{ card_stack_state }}"
|
<div class="card-stack" data-state="{{ card_stack_state }}"
|
||||||
data-starter-roles="{{ starter_roles|join:',' }}"
|
data-starter-roles="{{ starter_roles|join:',' }}"
|
||||||
data-user-slots="{{ user_slots|join:',' }}"
|
data-user-slots="{{ user_slots|join:',' }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user