PICK SKY: natal wheel planet tooltips + FT modernisation
- natus-wheel.js: per-planet <g> group with data-planet/sign/degree/retrograde attrs; mouseover/mouseout on group (pointer-events:none on child text/℞ so the whole apparatus triggers hover); tooltip uses .tt-title/.tt-description; in-sign degree via _inSignDeg() (ecliptic % 30); D3 switched from CDN to local d3.min.js - _natus.scss: .nw-planet--hover glow; #id_natus_tooltip position:fixed z-200 - _natus_overlay.html: tooltip div uses .tt; local d3.min.js script tag - T3/T4/T5 converted from Selenium execute_script to Jasmine unit tests (NatusWheelSpec.js) — NatusWheel was never defined in headless GeckoDriver; SpecRunner.html updated to load D3 + natus-wheel.js - test_pick_sky.py: NatusWheelTooltipTest removed (replaced by Jasmine) - test_component_cards_tarot / test_trinket_carte_blanche: equip assertions updated from legacy .equip-deck-btn/.equip-trinket-btn mini-tooltip pattern to current DON|DOFF (.btn-equip in main portal); mini-portal text assertions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1030,6 +1030,9 @@ def natus_preview(request, room_id):
|
||||
return HttpResponse(status=502)
|
||||
|
||||
data = resp.json()
|
||||
# PySwiss uses "Earth"; the wheel and SCSS use "Stone".
|
||||
if 'elements' in data and 'Earth' in data['elements']:
|
||||
data['elements']['Stone'] = data['elements'].pop('Earth')
|
||||
data['distinctions'] = _compute_distinctions(data['planets'], data['houses'])
|
||||
data['timezone'] = tz_str
|
||||
return JsonResponse(data)
|
||||
|
||||
2
src/apps/gameboard/static/apps/gameboard/d3.min.js
vendored
Normal file
2
src/apps/gameboard/static/apps/gameboard/d3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -98,6 +98,11 @@ const NatusWheel = (() => {
|
||||
return v || fallback;
|
||||
}
|
||||
|
||||
/** Ecliptic longitude → degrees within the sign (0–29.999…). */
|
||||
function _inSignDeg(ecliptic) {
|
||||
return ((ecliptic % 360) + 360) % 360 % 30;
|
||||
}
|
||||
|
||||
function _layout(svgEl) {
|
||||
const rect = svgEl.getBoundingClientRect();
|
||||
const size = Math.min(rect.width || 400, rect.height || 400);
|
||||
@@ -264,16 +269,48 @@ const NatusWheel = (() => {
|
||||
const finalA = _toAngle(pdata.degree, asc);
|
||||
const el = PLANET_ELEMENTS[name] || '';
|
||||
|
||||
// Per-planet group — data attrs + hover events live here so the
|
||||
// symbol text and ℞ indicator don't block mouse events on the circle.
|
||||
const planetEl = planetGroup.append('g')
|
||||
.attr('class', 'nw-planet-group')
|
||||
.attr('data-planet', name)
|
||||
.attr('data-sign', pdata.sign)
|
||||
.attr('data-degree', pdata.degree.toFixed(1))
|
||||
.attr('data-retrograde', pdata.retrograde ? 'true' : 'false')
|
||||
.on('mouseover', function (event) {
|
||||
d3.select(this).classed('nw-planet--hover', true);
|
||||
const tooltip = document.getElementById('id_natus_tooltip');
|
||||
if (!tooltip) return;
|
||||
const sym = PLANET_SYMBOLS[name] || name[0];
|
||||
const signData = SIGNS.find(s => s.name === pdata.sign) || {};
|
||||
const signSym = signData.symbol || '';
|
||||
const inDeg = _inSignDeg(pdata.degree).toFixed(1);
|
||||
const rx = pdata.retrograde ? ' ℞' : '';
|
||||
tooltip.innerHTML =
|
||||
`<div class="tt-title">${name} (${sym})</div>` +
|
||||
`<div class="tt-description">${inDeg}° ${pdata.sign} ${signSym}${rx}</div>`;
|
||||
tooltip.style.left = (event.clientX + 14) + 'px';
|
||||
tooltip.style.top = (event.clientY - 10) + 'px';
|
||||
tooltip.style.display = 'block';
|
||||
})
|
||||
.on('mouseout', function (event) {
|
||||
// Ignore mouseout when moving between children of this group
|
||||
if (planetEl.node().contains(event.relatedTarget)) return;
|
||||
d3.select(this).classed('nw-planet--hover', false);
|
||||
const tooltip = document.getElementById('id_natus_tooltip');
|
||||
if (tooltip) tooltip.style.display = 'none';
|
||||
});
|
||||
|
||||
// Circle behind symbol
|
||||
const circleBase = pdata.retrograde ? 'nw-planet-circle--rx' : 'nw-planet-circle';
|
||||
const circle = planetGroup.append('circle')
|
||||
const circle = planetEl.append('circle')
|
||||
.attr('cx', _cx + R.planetR * Math.cos(ascAngle))
|
||||
.attr('cy', _cy + R.planetR * Math.sin(ascAngle))
|
||||
.attr('r', _r * 0.05)
|
||||
.attr('class', el ? `${circleBase} nw-planet--${el}` : circleBase);
|
||||
|
||||
// Symbol
|
||||
const label = planetGroup.append('text')
|
||||
// Symbol — pointer-events:none so hover is handled by the group
|
||||
const label = planetEl.append('text')
|
||||
.attr('x', _cx + R.planetR * Math.cos(ascAngle))
|
||||
.attr('y', _cy + R.planetR * Math.sin(ascAngle))
|
||||
.attr('text-anchor', 'middle')
|
||||
@@ -281,17 +318,20 @@ const NatusWheel = (() => {
|
||||
.attr('dy', '0.1em')
|
||||
.attr('font-size', `${_r * 0.09}px`)
|
||||
.attr('class', el ? `nw-planet-label nw-planet-label--${el}` : 'nw-planet-label')
|
||||
.attr('pointer-events', 'none')
|
||||
.text(PLANET_SYMBOLS[name] || name[0]);
|
||||
|
||||
// Retrograde indicator
|
||||
// Retrograde indicator — also pointer-events:none
|
||||
let rxLabel = null;
|
||||
if (pdata.retrograde) {
|
||||
planetGroup.append('text')
|
||||
rxLabel = planetEl.append('text')
|
||||
.attr('x', _cx + (R.planetR + _r * 0.055) * Math.cos(ascAngle))
|
||||
.attr('y', _cy + (R.planetR + _r * 0.055) * Math.sin(ascAngle))
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('font-size', `${_r * 0.040}px`)
|
||||
.attr('class', 'nw-rx')
|
||||
.attr('pointer-events', 'none')
|
||||
.text('℞');
|
||||
}
|
||||
|
||||
@@ -311,10 +351,8 @@ const NatusWheel = (() => {
|
||||
.attrTween('x', () => t => _cx + R.planetR * Math.cos(interpAngle(t)))
|
||||
.attrTween('y', () => t => _cy + R.planetR * Math.sin(interpAngle(t)));
|
||||
|
||||
// Retrograde ℞ — move together with planet
|
||||
if (pdata.retrograde) {
|
||||
planetGroup.select('.nw-rx:last-child')
|
||||
.transition(transition())
|
||||
if (rxLabel) {
|
||||
rxLabel.transition(transition())
|
||||
.attrTween('x', () => t => _cx + (R.planetR + _r * 0.055) * Math.cos(interpAngle(t)))
|
||||
.attrTween('y', () => t => _cy + (R.planetR + _r * 0.055) * Math.sin(interpAngle(t)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user