From ab5b4c95dd97bfe4fb961f300df04bf0636d8e82 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Tue, 19 May 2026 00:15:13 -0400 Subject: [PATCH] =?UTF-8?q?My=20Sign=20picker:=20hover-preview,=20click-lo?= =?UTF-8?q?ck,=20NVM-unlock=20+=20polarity=20SCSS=20port=20+=20bigger=20st?= =?UTF-8?q?age=20=E2=80=94=20Sprint=204a-cont=20iteration=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-driven polish on iteration 1: separate hover-preview from click-lock semantics (room sig-select pattern), add NVM to unlock, port the room's `.sig-overlay[data-polarity]` polarity-themed CSS to also target `.my-sign-page[data-polarity]`, bump the stage card width so it occupies a bigger slice of the viewport ; **state machine** (inline JS in my_sign.html): three discrete states — (a) idle: stage frame visible but empty (stage card hidden via display:none, stat block hidden via `.sig-stage--frozen` absence, FLIP btn hidden via CSS); (b) hover: hovered .sig-card populates the stage card (preview); mouseleave clears it; mouseover-mouseout sequence guards against transient gaps when moving between adjacent thumbnails (relatedTarget closest('.sig-card') check); (c) locked: click on any grid card freezes the stage — populates content, adds `.sig-stage--frozen` to .sig-stage (which surfaces .sig-stat-block + .my-sign-flip-btn via CSS), enables SAVE SIGN, reveals NVM. Subsequent hovers ignored while locked. NVM click reverts to idle (clears content, hides stat-block + FLIP, disables SAVE, hides NVM) ; **new template** elements: NVM ` + {# Picker JS — click .sig-card to pick + populate stage preview via #} @@ -114,11 +114,13 @@ (function () { var grid = document.querySelector('.my-sign-deck-grid'); if (!grid) return; + var pageEl = document.querySelector('.my-sign-page'); var stage = document.querySelector('.my-sign-stage'); var stageCard = stage.querySelector('.sig-stage-card'); var statBlock = stage.querySelector('.sig-stat-block'); var cardIdInput = document.getElementById('id_save_sign_card_id'); var saveBtn = document.getElementById('id_save_sign_btn'); + var nvmBtn = document.getElementById('id_nvm_sign_btn'); var spinBtn = stage.querySelector('.spin-btn'); var flipBtn = stage.querySelector('.my-sign-flip-btn'); var fyiBtn = stage.querySelector('.fyi-btn'); @@ -126,9 +128,11 @@ var fyiPrev = stage.querySelector('.fyi-prev'); var fyiNext = stage.querySelector('.fyi-next'); var revInput = document.getElementById('id_save_sign_reversed'); - var _currentCard = null; - var _fyiData = []; - var _fyiIdx = 0; + var _currentCard = null; + var _focusedCardEl = null; + var _locked = false; // true after click; cleared by NVM + var _fyiData = []; + var _fyiIdx = 0; // Default polarity = gravity (significator_reversed=False); // FLIP toggles to levity (significator_reversed=True). Mirrors the @@ -138,13 +142,61 @@ return revInput.value === '1' ? 'levity' : 'gravity'; } + function _populateStage(cardEl) { + _focusedCardEl = cardEl; + _currentCard = StageCard.fromDataset(cardEl); + StageCard.populateCard(stageCard, _currentCard, _polarity()); + StageCard.populateKeywords(statBlock, + _currentCard.keywords_upright, _currentCard.keywords_reversed); + _fyiData = StageCard.buildInfoData(_currentCard); + _fyiIdx = 0; + if (fyiPanel) StageCard.renderFyi(fyiPanel, _fyiData, _fyiIdx); + stageCard.style.display = ''; + } + + function _clearStage() { + stageCard.style.display = 'none'; + _focusedCardEl = null; + _currentCard = null; + } + + function _lock(cardEl) { + _locked = true; + _populateStage(cardEl); + grid.querySelectorAll('.sig-card.sig-focused').forEach(function (c) { + if (c !== cardEl) c.classList.remove('sig-focused'); + }); + cardEl.classList.add('sig-focused'); + cardIdInput.value = cardEl.dataset.cardId; + saveBtn.removeAttribute('disabled'); + if (nvmBtn) nvmBtn.style.display = ''; + stage.classList.add('sig-stage--frozen'); + statBlock.classList.remove('fyi-open'); + } + + function _unlock() { + _locked = false; + stage.classList.remove('sig-stage--frozen'); + statBlock.classList.remove('fyi-open'); + grid.querySelectorAll('.sig-card.sig-focused').forEach(function (c) { + c.classList.remove('sig-focused'); + }); + _clearStage(); + cardIdInput.value = ''; + saveBtn.setAttribute('disabled', 'disabled'); + if (nvmBtn) nvmBtn.style.display = 'none'; + } + // Horizontal-perspective FLIP animation lifted from // apps/gameboard/static/apps/gameboard/game-kit.js `_flipActive`. // 500ms Y-axis rotation; mid-animation (offset 0.5) the populator // swaps polarity content so the user sees the new face from the // start of the second half-rotation. Preserves the SPIN orientation // (.stage-card--reversed) through the flip by including the spin - // rotate(180deg) in both keyframes. + // rotate(180deg) in both keyframes. data-polarity moved to the page + // wrapper so descendant .sig-card + .sig-stage-card both pick up + // the polarity-themed CSS rules ([[feedback-applet-vs-page]] / port + // of `.sig-overlay[data-polarity]` to `.my-sign-page[data-polarity]`). function _flipPolarityAnimated() { if (!stageCard || stageCard.dataset.flipping) return; stageCard.dataset.flipping = '1'; @@ -158,11 +210,9 @@ { transform: mid, offset: 0.5 }, { transform: rest }, ], { duration: 500, easing: 'ease' }); - // Swap polarity at the edge-on midpoint so the new face shows - // through the second half of the rotation. setTimeout(function () { revInput.value = revInput.value === '1' ? '0' : '1'; - stage.setAttribute('data-polarity', _polarity()); + pageEl.setAttribute('data-polarity', _polarity()); if (flipBtn) flipBtn.classList.toggle( 'is-reversed', revInput.value === '1'); if (_currentCard && window.StageCard) { @@ -182,28 +232,32 @@ if (spinBtn) spinBtn.classList.toggle('is-reversed', on); } - function _selectCard(cardEl) { - grid.querySelectorAll('.sig-card.sig-focused').forEach(function (c) { - c.classList.remove('sig-focused'); - }); - cardEl.classList.add('sig-focused'); - cardIdInput.value = cardEl.dataset.cardId; - saveBtn.removeAttribute('disabled'); - if (!window.StageCard) return; - _currentCard = StageCard.fromDataset(cardEl); - StageCard.populateCard(stageCard, _currentCard, _polarity()); - StageCard.populateKeywords(statBlock, - _currentCard.keywords_upright, _currentCard.keywords_reversed); - _fyiData = StageCard.buildInfoData(_currentCard); - _fyiIdx = 0; - if (fyiPanel) StageCard.renderFyi(fyiPanel, _fyiData, _fyiIdx); - statBlock.classList.remove('fyi-open'); - } - + // Hover preview — only when stage isn't locked. mouseenter shows + // the stage card; mouseleave hides it. Click locks the state + + // surfaces the stat block + FLIP btn (via .sig-stage--frozen). + grid.addEventListener('mouseover', function (e) { + if (_locked) return; + var cardEl = e.target.closest('.sig-card'); + if (!cardEl || !window.StageCard) return; + _populateStage(cardEl); + }); + grid.addEventListener('mouseout', function (e) { + if (_locked) return; + var cardEl = e.target.closest('.sig-card'); + if (!cardEl) return; + var nextCard = e.relatedTarget && e.relatedTarget.closest + && e.relatedTarget.closest('.sig-card'); + if (nextCard) return; // moving between cards — let mouseover update + _clearStage(); + }); grid.addEventListener('click', function (e) { var cardEl = e.target.closest('.sig-card'); - if (cardEl) _selectCard(cardEl); + if (!cardEl || !window.StageCard) return; + _lock(cardEl); }); + if (nvmBtn) { + nvmBtn.addEventListener('click', function () { _unlock(); }); + } if (flipBtn) { flipBtn.addEventListener('click', function () { if (!_currentCard) return; // need a selected card to flip @@ -241,14 +295,14 @@ }); } - // On-load: if user has a saved sig, pre-select its grid card so - // the stage shows the persisted choice rather than an empty frame. - // `data-current-card-id` is set on .my-sign-page when significator is set. - var pageEl = document.querySelector('.my-sign-page'); + // On-load: if user has a saved sig, lock that card so the stage + // + stat block + FLIP btn appear w. the persisted choice from + // the start. Otherwise the stage stays empty until first hover. var savedId = pageEl && pageEl.dataset.currentCardId; if (savedId) { - var savedCardEl = grid.querySelector('.sig-card[data-card-id="' + savedId + '"]'); - if (savedCardEl) _selectCard(savedCardEl); + var savedCardEl = grid.querySelector( + '.sig-card[data-card-id="' + savedId + '"]'); + if (savedCardEl) _lock(savedCardEl); } }());