Compare commits
3 Commits
bd9155c13b
...
d50645b216
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d50645b216 | ||
|
|
c9fc5a2fd4 | ||
|
|
1a83c5f01c |
@@ -165,9 +165,9 @@ class MySeaVisitContextTest(TestCase):
|
|||||||
self.assertIn("sea-deck-stack--gravity", html)
|
self.assertIn("sea-deck-stack--gravity", html)
|
||||||
self.assertIn("sea-deck-stack--levity", html)
|
self.assertIn("sea-deck-stack--levity", html)
|
||||||
# FLIP is the disabled (×) state; never an enabled FLIP for a visitor.
|
# FLIP is the disabled (×) state; never an enabled FLIP for a visitor.
|
||||||
self.assertIn("sea-stack-ok btn-disabled", html)
|
self.assertIn("sea-stack-flip btn-disabled", html)
|
||||||
self.assertNotIn(
|
self.assertNotIn(
|
||||||
'class="btn btn-reveal sea-stack-ok" type="button">FLIP', html)
|
'class="btn btn-reveal sea-stack-flip" type="button">FLIP', html)
|
||||||
|
|
||||||
def test_stack_keys_on_owner_deck_not_viewer_deck(self):
|
def test_stack_keys_on_owner_deck_not_viewer_deck(self):
|
||||||
# Viewer has no deck, owner has one → the stack still renders (it's the
|
# Viewer has no deck, owner has one → the stack still renders (it's the
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ class MySeaCardDrawTest(FunctionalTest):
|
|||||||
)
|
)
|
||||||
stack.click()
|
stack.click()
|
||||||
flip = self.wait_for(
|
flip = self.wait_for(
|
||||||
lambda: stack.find_element(By.CSS_SELECTOR, ".sea-stack-ok")
|
lambda: stack.find_element(By.CSS_SELECTOR, ".sea-stack-flip")
|
||||||
)
|
)
|
||||||
self.wait_for(lambda: self.assertTrue(flip.is_displayed()))
|
self.wait_for(lambda: self.assertTrue(flip.is_displayed()))
|
||||||
flip.click()
|
flip.click()
|
||||||
|
|||||||
@@ -299,9 +299,18 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", stack)
|
self.browser.execute_script("arguments[0].click()", stack)
|
||||||
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
))
|
))
|
||||||
self.assertTrue(ok_btn.is_displayed())
|
# Choosing the spread scroll-snaps the felt onto two pages; the deck
|
||||||
|
# stacks sit on a different page than the post-OK scroll lands on in
|
||||||
|
# headless (the rAF scroll-to-cross is dropped, see
|
||||||
|
# [[feedback-headless-delayed-scroll-dropped]]), leaving the revealed OK
|
||||||
|
# btn off the visible page. Pull it into view so is_displayed reflects
|
||||||
|
# the `--active` reveal, not the scroll position.
|
||||||
|
self.browser.execute_script(
|
||||||
|
"arguments[0].scrollIntoView({block: 'center'})", ok_btn
|
||||||
|
)
|
||||||
|
self.wait_for(lambda: self.assertTrue(ok_btn.is_displayed()))
|
||||||
|
|
||||||
def test_clicking_elsewhere_hides_ok_btn(self):
|
def test_clicking_elsewhere_hides_ok_btn(self):
|
||||||
"""Clicking outside a focused stack dismisses the OK btn."""
|
"""Clicking outside a focused stack dismisses the OK btn."""
|
||||||
@@ -311,14 +320,14 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", stack)
|
self.browser.execute_script("arguments[0].click()", stack)
|
||||||
self.wait_for(lambda: self.browser.find_element(
|
self.wait_for(lambda: self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
).is_displayed())
|
).is_displayed())
|
||||||
# Click the sea cards column (not a stack)
|
# Click the sea cards column (not a stack)
|
||||||
col = self.browser.find_element(By.CSS_SELECTOR, ".sea-cards-col")
|
col = self.browser.find_element(By.CSS_SELECTOR, ".sea-cards-col")
|
||||||
self.browser.execute_script("arguments[0].click()", col)
|
self.browser.execute_script("arguments[0].click()", col)
|
||||||
self.wait_for(lambda: not any(
|
self.wait_for(lambda: not any(
|
||||||
el.is_displayed()
|
el.is_displayed()
|
||||||
for el in self.browser.find_elements(By.CSS_SELECTOR, ".sea-stack-ok")
|
for el in self.browser.find_elements(By.CSS_SELECTOR, ".sea-stack-flip")
|
||||||
))
|
))
|
||||||
|
|
||||||
# ── Card draw ─────────────────────────────────────────────────────── #
|
# ── Card draw ─────────────────────────────────────────────────────── #
|
||||||
@@ -331,7 +340,7 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", stack)
|
self.browser.execute_script("arguments[0].click()", stack)
|
||||||
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", ok_btn)
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
||||||
self.wait_for(lambda: self.browser.find_element(
|
self.wait_for(lambda: self.browser.find_element(
|
||||||
@@ -352,7 +361,7 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", stack)
|
self.browser.execute_script("arguments[0].click()", stack)
|
||||||
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", ok_btn)
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
||||||
|
|
||||||
@@ -361,7 +370,7 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
self.wait_for(lambda: self.assertIn(
|
self.wait_for(lambda: self.assertIn(
|
||||||
"btn-disabled",
|
"btn-disabled",
|
||||||
self.browser.find_element(
|
self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
).get_attribute("class"),
|
).get_attribute("class"),
|
||||||
))
|
))
|
||||||
|
|
||||||
@@ -374,7 +383,7 @@ class PickSeaDealTest(ChannelsFunctionalTest):
|
|||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", stack)
|
self.browser.execute_script("arguments[0].click()", stack)
|
||||||
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
||||||
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-flip"
|
||||||
))
|
))
|
||||||
self.browser.execute_script("arguments[0].click()", ok_btn)
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
||||||
self.wait_for(lambda: self.browser.find_element(
|
self.wait_for(lambda: self.browser.find_element(
|
||||||
|
|||||||
@@ -2046,7 +2046,7 @@ $_sea-card-glow: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3), 0 0 0.4rem rgba(0,
|
|||||||
z-index: 1; // sits above the name label
|
z-index: 1; // sits above the name label
|
||||||
}
|
}
|
||||||
|
|
||||||
.sea-stack-ok {
|
.sea-stack-flip {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -2064,9 +2064,9 @@ $_sea-card-glow: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3), 0 0 0.4rem rgba(0,
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
.sea-deck-stack:hover .sea-stack-ok,
|
.sea-deck-stack:hover .sea-stack-flip,
|
||||||
.sea-deck-stack:focus-within .sea-stack-ok,
|
.sea-deck-stack:focus-within .sea-stack-flip,
|
||||||
.sea-deck-stack--active .sea-stack-ok { opacity: 1; }
|
.sea-deck-stack--active .sea-stack-flip { opacity: 1; }
|
||||||
// Interactivity is gated on the PERSIST state (`--active`), NOT hover. Hover is
|
// Interactivity is gated on the PERSIST state (`--active`), NOT hover. Hover is
|
||||||
// a purely VISUAL preview — same as the spectator's disabled FLIP, whose "hover
|
// a purely VISUAL preview — same as the spectator's disabled FLIP, whose "hover
|
||||||
// effect" is just the reveal. Keeping the FLIP click-through on hover preserves
|
// effect" is just the reveal. Keeping the FLIP click-through on hover preserves
|
||||||
@@ -2074,7 +2074,7 @@ $_sea-card-glow: 0 0 0.5rem 0.5rem rgba(var(--ninUser), 0.3), 0 0 0.4rem rgba(0,
|
|||||||
// Were it clickable on hover, a single stack-click would land on the centred
|
// Were it clickable on hover, a single stack-click would land on the centred
|
||||||
// FLIP itself + deal early. The spectator's FLIP stays `.btn-disabled` (read-
|
// FLIP itself + deal early. The spectator's FLIP stays `.btn-disabled` (read-
|
||||||
// only) so it never becomes interactive even while persisted.
|
// only) so it never becomes interactive even while persisted.
|
||||||
.sea-deck-stack--active .sea-stack-ok:not(.btn-disabled) { pointer-events: auto; }
|
.sea-deck-stack--active .sea-stack-flip:not(.btn-disabled) { pointer-events: auto; }
|
||||||
|
|
||||||
.sea-deck-stack { gap: 0; } // remove gap so name slides under the face
|
.sea-deck-stack { gap: 0; } // remove gap so name slides under the face
|
||||||
|
|
||||||
@@ -2354,10 +2354,15 @@ $_sea-title-els: '.fan-card-name, .sig-qualifier-above, .sig-qualifier-below, .f
|
|||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sea-stage--gravity .sea-stage-card.is-flipped-to-back {
|
// The 0.3 fill only earns its keep where a monodeck is CLONED into a two-toned
|
||||||
|
// gravity/levity dubbodeck (Sea Select — `.sea-stage--dubbo`, flagged by
|
||||||
|
// _sea_overlay.html) and the two stacks must read apart. my_sea / my_sea_visit
|
||||||
|
// draw from a single monodeck that is never cloned, so their flipped back stays
|
||||||
|
// un-tinted (no `--dubbo` flag → only the empty, fill-less base `::after` above).
|
||||||
|
.sea-stage--dubbo.sea-stage--gravity .sea-stage-card.is-flipped-to-back {
|
||||||
&::after { background: rgba(var(--quiUser), 0.3); }
|
&::after { background: rgba(var(--quiUser), 0.3); }
|
||||||
}
|
}
|
||||||
.sea-stage--levity .sea-stage-card.is-flipped-to-back {
|
.sea-stage--dubbo.sea-stage--levity .sea-stage-card.is-flipped-to-back {
|
||||||
&::after { background: rgba(var(--terUser), 0.3); }
|
&::after { background: rgba(var(--terUser), 0.3); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -954,7 +954,7 @@ body.page-gameboard {
|
|||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FLIP reveal (hover-fade + click-persist) is now the SHARED `.sea-stack-ok`
|
// FLIP reveal (hover-fade + click-persist) is now the SHARED `.sea-stack-flip`
|
||||||
// behaviour in _card-deck.scss — unified with the owner picker per user-spec
|
// behaviour in _card-deck.scss — unified with the owner picker per user-spec
|
||||||
// 2026-05-30, so the visitor's prior hover-only override is gone. The
|
// 2026-05-30, so the visitor's prior hover-only override is gone. The
|
||||||
// spectator's click-persist (`.sea-deck-stack--active`) is wired in
|
// spectator's click-persist (`.sea-deck-stack--active`) is wired in
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
<span class="sea-stacks-label">DECKS</span>
|
<span class="sea-stacks-label">DECKS</span>
|
||||||
<div class="sea-deck-stack sea-deck-stack--gravity">
|
<div class="sea-deck-stack sea-deck-stack--gravity">
|
||||||
<div class="sea-stack-face">
|
<div class="sea-stack-face">
|
||||||
<button class="btn btn-reveal sea-stack-ok{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
<button class="btn btn-reveal sea-stack-flip{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="sea-stack-name">Gravity</span>
|
<span class="sea-stack-name">Gravity</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sea-deck-stack sea-deck-stack--levity">
|
<div class="sea-deck-stack sea-deck-stack--levity">
|
||||||
<div class="sea-stack-face">
|
<div class="sea-stack-face">
|
||||||
<button class="btn btn-reveal sea-stack-ok{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
<button class="btn btn-reveal sea-stack-flip{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="sea-stack-name">Levity</span>
|
<span class="sea-stack-name">Levity</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
{% if deck.has_card_images %}
|
{% if deck.has_card_images %}
|
||||||
<img class="sea-stack-face-img" src="{{ deck.back_image_url }}" alt="">
|
<img class="sea-stack-face-img" src="{{ deck.back_image_url }}" alt="">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button class="btn btn-reveal sea-stack-ok{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
<button class="btn btn-reveal sea-stack-flip{% if flip_disabled %} btn-disabled{% endif %}" type="button">{% if flip_disabled %}×{% else %}FLIP{% endif %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -131,13 +131,13 @@ via epic:sea_save. `?seat` threads the CARTE-selected seat onto the action URLs.
|
|||||||
<span class="sea-stacks-label">DECKS</span>
|
<span class="sea-stacks-label">DECKS</span>
|
||||||
<div class="sea-deck-stack sea-deck-stack--gravity">
|
<div class="sea-deck-stack sea-deck-stack--gravity">
|
||||||
<div class="sea-stack-face">
|
<div class="sea-stack-face">
|
||||||
<button class="btn btn-reveal sea-stack-ok{% if hand_complete %} btn-disabled{% endif %}" type="button">{% if hand_complete %}×{% else %}FLIP{% endif %}</button>
|
<button class="btn btn-reveal sea-stack-flip{% if hand_complete %} btn-disabled{% endif %}" type="button">{% if hand_complete %}×{% else %}FLIP{% endif %}</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="sea-stack-name">Gravity</span>
|
<span class="sea-stack-name">Gravity</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sea-deck-stack sea-deck-stack--levity">
|
<div class="sea-deck-stack sea-deck-stack--levity">
|
||||||
<div class="sea-stack-face">
|
<div class="sea-stack-face">
|
||||||
<button class="btn btn-reveal sea-stack-ok{% if hand_complete %} btn-disabled{% endif %}" type="button">{% if hand_complete %}×{% else %}FLIP{% endif %}</button>
|
<button class="btn btn-reveal sea-stack-flip{% if hand_complete %} btn-disabled{% endif %}" type="button">{% if hand_complete %}×{% else %}FLIP{% endif %}</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="sea-stack-name">Levity</span>
|
<span class="sea-stack-name">Levity</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,7 +146,10 @@ via epic:sea_save. `?seat` threads the CARTE-selected seat onto the action URLs.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Sea stage — portaled big-card viewer (position:fixed, escapes the snap). #}
|
{# Sea stage — portaled big-card viewer (position:fixed, escapes the snap). #}
|
||||||
{% include "apps/gameboard/_partials/_sea_stage.html" %}
|
{# `sea_stage_dubbo` flags the gameroom's cloned two-toned dubbodeck so the #}
|
||||||
|
{# FLIP-back polarity tint applies HERE only (my_sea/visit draw a monodeck #}
|
||||||
|
{# that is never cloned — no tint there). See _card-deck.scss. #}
|
||||||
|
{% include "apps/gameboard/_partials/_sea_stage.html" with sea_stage_dubbo=True %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -258,7 +261,7 @@ via epic:sea_save. `?seat` threads the CARTE-selected seat onto the action URLs.
|
|||||||
}
|
}
|
||||||
function _setComplete(on, live) {
|
function _setComplete(on, live) {
|
||||||
_locked = on;
|
_locked = on;
|
||||||
overlay.querySelectorAll('.sea-deck-stack .sea-stack-ok').forEach(function (btn) {
|
overlay.querySelectorAll('.sea-deck-stack .sea-stack-flip').forEach(function (btn) {
|
||||||
btn.classList.toggle('btn-disabled', on);
|
btn.classList.toggle('btn-disabled', on);
|
||||||
btn.innerHTML = on ? '×' : 'FLIP';
|
btn.innerHTML = on ? '×' : 'FLIP';
|
||||||
});
|
});
|
||||||
@@ -337,7 +340,7 @@ via epic:sea_save. `?seat` threads the CARTE-selected seat onto the action URLs.
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (_activeStack === stack) _hideOk(); else _showOk(stack);
|
if (_activeStack === stack) _hideOk(); else _showOk(stack);
|
||||||
});
|
});
|
||||||
var ok = stack.querySelector('.sea-stack-ok');
|
var ok = stack.querySelector('.sea-stack-flip');
|
||||||
if (ok) {
|
if (ok) {
|
||||||
ok.addEventListener('click', function (e) {
|
ok.addEventListener('click', function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
{# #}
|
{# #}
|
||||||
{# Shared by the gameroom SEA SELECT phase and the my-sea picker — same #}
|
{# Shared by the gameroom SEA SELECT phase and the my-sea picker — same #}
|
||||||
{# HTML, same SeaDeal module bindings; only the parent overlay differs. #}
|
{# HTML, same SeaDeal module bindings; only the parent overlay differs. #}
|
||||||
<div class="sea-stage" id="id_sea_stage" style="display:none">
|
<div class="sea-stage{% if sea_stage_dubbo %} sea-stage--dubbo{% endif %}" id="id_sea_stage" style="display:none">
|
||||||
<div class="sea-stage-backdrop"></div>
|
<div class="sea-stage-backdrop"></div>
|
||||||
<div class="sea-stage-content">
|
<div class="sea-stage-content">
|
||||||
<div class="sig-stage-card sea-stage-card">
|
<div class="sig-stage-card sea-stage-card">
|
||||||
|
|||||||
@@ -439,7 +439,7 @@
|
|||||||
// draw, NOT at completion).
|
// draw, NOT at completion).
|
||||||
_locked = on;
|
_locked = on;
|
||||||
picker.classList.toggle('my-sea-picker--locked', on);
|
picker.classList.toggle('my-sea-picker--locked', on);
|
||||||
picker.querySelectorAll('.sea-deck-stack .sea-stack-ok').forEach(function (btn) {
|
picker.querySelectorAll('.sea-deck-stack .sea-stack-flip').forEach(function (btn) {
|
||||||
btn.classList.toggle('btn-disabled', on);
|
btn.classList.toggle('btn-disabled', on);
|
||||||
// Mirrors DEL btn convention: disabled label is × (the
|
// Mirrors DEL btn convention: disabled label is × (the
|
||||||
// template's initial render does the same), active is
|
// template's initial render does the same), active is
|
||||||
@@ -477,7 +477,7 @@
|
|||||||
if (_activeStack === stack) _hideOk();
|
if (_activeStack === stack) _hideOk();
|
||||||
else _showOk(stack);
|
else _showOk(stack);
|
||||||
});
|
});
|
||||||
var ok = stack.querySelector('.sea-stack-ok');
|
var ok = stack.querySelector('.sea-stack-flip');
|
||||||
if (ok) {
|
if (ok) {
|
||||||
ok.addEventListener('click', function (e) {
|
ok.addEventListener('click', function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
Reference in New Issue
Block a user