CAST SKY cascade: felt eases out → glow → DRAW SEA eases in; burger handoff; reload-into-open — TDD
Post-save choreography + the polish asks from this session.
- Post-save cascade (_sky_overlay.html): after SAVE the gamer lingers on the
wheel ~3s, then the felt eases OUT (fade, .sky-page--cascade-out) to reveal the
table-hex; the burger fires its --priTk glow + #id_sky_btn goes active (both
ride the cascade now, not the save instant); 3s later the DRAW SEA btn eases IN
and the sea overlay is injected so it's live with NO reload.
- Hex phase-stack (room.html + _room.scss): CAST SKY + DRAW SEA share one grid
cell (.hex-phase-stack) so they cross-fade in place (.hex-phase-btn--out); the
server seeds --out on the inactive one, the cascade swaps them. A confirmed
reload lands DRAW SEA visible / CAST SKY out, same as the cascade end.
- Seamless sea injection: _injectSeaOverlay fetches sea_partial (the URL already
existed, unused) + re-creates its <script>s so the overlay's own init (openSea
+ SeaDeal.reinit) runs — DRAW SEA opens with no reload. (Bridge until Sea Select
is itself hollowed into a felt.)
- Burger → Sky-btn handoff (burger-btn.js + _burger.scss): _pulseGlow generalized;
once saved, the next burger-OPEN pulses #id_sky_btn --priTk ("now click me to
reopen"). Jasmine specs added (BurgerSpec).
- #id_text_btn disabled while the felt is up (openSky/closeSky) — its swipe
machine would otherwise half-load the scroll-locked reelhouse.
- Reload-into-open (sig-select.js + _sky_overlay.html): the SIG_SELECT→SKY_SELECT
CAST SKY click crosses a server-render boundary (felt/phase-stack/sea-inject
only exist in SKY_SELECT), so it still reloads — but drops a sessionStorage
flag first so the felt OPENS on arrival. Kills the old click→reload→click-again
double-take (the "first click reloads" report). DRAW SEA can inject in-place
(stays within SKY_SELECT); CAST SKY can't, so this is the seamless equivalent.
Tests: BurgerSpec handoff specs + full Jasmine green; 32 render ITs + 930
epic+gameboard green. Cascade timing live-verified by the user.
[[feedback-scss-import-order-specificity]]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -166,8 +166,26 @@
|
||||
|
||||
// ── Open / Close ──────────────────────────────────────────────────────────
|
||||
|
||||
// While the felt is up the burger Text sub-btn must be inert: its swipe
|
||||
// machine (room-views.js) would otherwise drive DOWN into the reelhouse — but
|
||||
// the aperture is scroll-locked, so the page half-loads a carousel view it
|
||||
// can't actually reach (confusing UX). We toggle #id_text_btn OFF on open and
|
||||
// restore its server-set baseline on close.
|
||||
let _textBtnWasActive = false;
|
||||
function _disableTextBtn() {
|
||||
const tb = document.getElementById('id_text_btn');
|
||||
if (!tb) return;
|
||||
_textBtnWasActive = tb.classList.contains('active');
|
||||
tb.classList.remove('active');
|
||||
}
|
||||
function _restoreTextBtn() {
|
||||
const tb = document.getElementById('id_text_btn');
|
||||
if (tb && _textBtnWasActive) tb.classList.add('active');
|
||||
}
|
||||
|
||||
function openSky() {
|
||||
document.documentElement.classList.add('sky-open');
|
||||
_disableTextBtn();
|
||||
// Re-sync the room gear to the sky NVM pane (room-views.js owns the gear
|
||||
// pane-swap; sky-open toggles outside any scroll/view event it watches).
|
||||
if (window.RoomViews && window.RoomViews.syncGear) window.RoomViews.syncGear();
|
||||
@@ -181,6 +199,7 @@
|
||||
|
||||
function closeSky() {
|
||||
document.documentElement.classList.remove('sky-open');
|
||||
_restoreTextBtn();
|
||||
hideSuggestions();
|
||||
if (window.RoomViews && window.RoomViews.syncGear) window.RoomViews.syncGear();
|
||||
}
|
||||
@@ -424,21 +443,86 @@
|
||||
SkyWheel.draw(svgEl, _lastChartData);
|
||||
}
|
||||
_ensureDelBtn();
|
||||
// The sky now lives in the burger fan: light its Sky sub-btn so it's a live
|
||||
// reopen affordance immediately (server also renders it active after any
|
||||
// reload). No-op if the burger isn't on this surface.
|
||||
const skyBtn = document.getElementById('id_sky_btn');
|
||||
if (skyBtn) skyBtn.classList.add('active');
|
||||
if (!wasSaved) {
|
||||
// First reveal: pin to the form section so the wheel slides in from above
|
||||
// (the form "shunts down") rather than hard-cutting into place.
|
||||
// (the form "shunts down"), then begin the post-save cascade. #id_sky_btn
|
||||
// activation + the burger glow ride the cascade (not the save instant) so
|
||||
// they land WITH the table-hex reveal — see _cascadeFeltOut.
|
||||
const formCol = overlay.querySelector('.sky-form-col');
|
||||
if (formCol) overlay.scrollTop = formCol.offsetTop;
|
||||
// Cue the burger thrice (--priTk) — "your sky lives here now". One-shot
|
||||
// per save; the burger-btn.js load pulse covers the post-reload hex.
|
||||
if (window.pulseSkyGlow) window.pulseSkyGlow();
|
||||
_scrollApertureToTop();
|
||||
_startSaveCascade();
|
||||
} else {
|
||||
// Re-assert (WS re-fire / reopen-from-saved): keep the reopen affordance
|
||||
// live; no cascade (the table-hex already advanced on the first save).
|
||||
const skyBtn = document.getElementById('id_sky_btn');
|
||||
if (skyBtn) skyBtn.classList.add('active');
|
||||
_scrollApertureToTop();
|
||||
}
|
||||
_scrollApertureToTop();
|
||||
}
|
||||
|
||||
// ── Post-save cascade ───────────────────────────────────────────────────────
|
||||
// After SAVE the gamer lingers on the freshly-drawn wheel ~3s; THEN the felt
|
||||
// eases OUT to reveal the table-hex, the burger glow fires (your sky now lives
|
||||
// in the burger fan), and 3s later the DRAW SEA btn eases IN — with the sea
|
||||
// overlay injected so it's live with no reload. Each beat is its own timer.
|
||||
const _CASCADE_LINGER = 3000, _FELT_FADE = 500, _SEA_DELAY = 3000;
|
||||
|
||||
function _startSaveCascade() {
|
||||
setTimeout(_cascadeFeltOut, _CASCADE_LINGER);
|
||||
}
|
||||
|
||||
function _cascadeFeltOut() {
|
||||
overlay.classList.add('sky-page--cascade-out'); // CSS fades the felt out
|
||||
setTimeout(() => {
|
||||
document.documentElement.classList.remove('sky-open');
|
||||
overlay.classList.remove('sky-page--cascade-out');
|
||||
_restoreTextBtn();
|
||||
if (window.RoomViews && window.RoomViews.syncGear) window.RoomViews.syncGear();
|
||||
// CAST SKY is stale → ease it out; light the burger reopen affordance +
|
||||
// fire the glow, all concurrent with the table-hex reveal.
|
||||
const skyPhaseBtn = document.getElementById('id_pick_sky_btn');
|
||||
if (skyPhaseBtn) skyPhaseBtn.classList.add('hex-phase-btn--out');
|
||||
const skyBtn = document.getElementById('id_sky_btn');
|
||||
if (skyBtn) skyBtn.classList.add('active');
|
||||
if (window.pulseSkyGlow) window.pulseSkyGlow();
|
||||
setTimeout(_cascadeDrawSeaIn, _SEA_DELAY);
|
||||
}, _FELT_FADE);
|
||||
}
|
||||
|
||||
function _cascadeDrawSeaIn() {
|
||||
const seaPhaseBtn = document.getElementById('id_pick_sea_btn');
|
||||
if (seaPhaseBtn) seaPhaseBtn.classList.remove('hex-phase-btn--out'); // eases in
|
||||
_injectSeaOverlay();
|
||||
}
|
||||
|
||||
// Fetch + inject the DRAW SEA overlay so it's live without a reload. The sea
|
||||
// partial carries its own inline <script> (openSea binding on #id_pick_sea_btn
|
||||
// + SeaDeal.reinit) — innerHTML won't execute it, so each <script> is re-
|
||||
// created. Idempotent + best-effort (a gear NVM / refresh recovers via the
|
||||
// server-rendered confirmed overlay).
|
||||
function _injectSeaOverlay() {
|
||||
const url = overlay.dataset.seaPartialUrl;
|
||||
const container = document.getElementById('id_sea_inject');
|
||||
if (!url || !container || container.dataset.injected) return;
|
||||
fetch(url, { credentials: 'same-origin' })
|
||||
.then((r) => { if (!r.ok) throw new Error(r.status); return r.text(); })
|
||||
.then((html) => {
|
||||
container.dataset.injected = '1';
|
||||
const tpl = document.createElement('template');
|
||||
tpl.innerHTML = html;
|
||||
Array.prototype.slice.call(tpl.content.childNodes).forEach((node) => {
|
||||
if (node.nodeName === 'SCRIPT') {
|
||||
const s = document.createElement('script');
|
||||
if (node.src) s.src = node.src;
|
||||
else s.textContent = node.textContent;
|
||||
container.appendChild(s);
|
||||
} else {
|
||||
container.appendChild(node);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => { /* transient — server render recovers on next load */ });
|
||||
}
|
||||
|
||||
// Ease the felt's scroll back to the wheel page (top) after a save —
|
||||
@@ -566,6 +650,17 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Reload-into-open: the SIG_SELECT → SKY_SELECT transition (sig-select.js's
|
||||
// CAST SKY click) drops a sessionStorage flag then reloads here, so the felt
|
||||
// OPENS on arrival — one click yields the form instead of the old click→reload
|
||||
// →click-again. Read once + clear so a later manual refresh doesn't re-open.
|
||||
try {
|
||||
if (sessionStorage.getItem('sky-autoopen') === '1') {
|
||||
sessionStorage.removeItem('sky-autoopen');
|
||||
openSky();
|
||||
}
|
||||
} catch (_) { /* sessionStorage unavailable — non-fatal */ }
|
||||
|
||||
// ── STUB: lock the form once character creation completes ───────────────────
|
||||
// Character creation ends after DRAW SEA → the Voronoi map (roadmap step 21),
|
||||
// which isn't built yet. When it lands the server will flag the finished state
|
||||
|
||||
Reference in New Issue
Block a user