// hover_panel.js
// Floating holographic superpower panel. Rendered as a DOM overlay anchored to
// the hero's screen-projected head position (rises ~60cm above the head in world
// space). Name in hero color (monospace), SUPERPOWERS header, 4 lines that type
// out sequentially (30ms/char) then pulse, each with an animated SVG icon.
import { buildIcon } from './superpower_icons.js';
export class HoverPanel {
constructor(container, reduced) {
this.container = container;
this.reduced = reduced;
this.el = document.createElement('div');
this.el.className = 'hover-panel';
this.el.setAttribute('role', 'tooltip');
this.el.setAttribute('aria-hidden', 'true');
container.appendChild(this.el);
this.activeId = null;
this._timers = [];
}
_clearTimers() { this._timers.forEach(clearTimeout); this._timers = []; }
show(hero) {
if (this.activeId === hero.id) return;
this.activeId = hero.id;
this._clearTimers();
const c = hero.color;
this.el.style.setProperty('--hc', c);
this.el.innerHTML = `
${hero.name}
${hero.role}
SUPERPOWERS
`;
const list = this.el.querySelector('.hp-list');
hero.powers.forEach((p, i) => {
const li = document.createElement('li');
li.className = 'hp-line';
const icon = buildIcon(p.icon, c, this.reduced);
const txt = document.createElement('span');
txt.className = 'hp-text';
li.appendChild(icon);
li.appendChild(txt);
list.appendChild(li);
if (this.reduced) {
txt.textContent = p.label;
li.classList.add('hp-shown');
} else {
// sequential type-out: 30ms/char, staggered per line
const startDelay = i * 520;
this._timers.push(setTimeout(() => this._type(li, txt, p.label), startDelay));
}
});
this.el.classList.add('hp-visible');
this.el.setAttribute('aria-hidden', 'false');
}
_type(li, txt, label) {
li.classList.add('hp-shown');
let n = 0;
const step = () => {
txt.textContent = label.slice(0, n);
n++;
if (n <= label.length) {
this._timers.push(setTimeout(step, 30));
} else {
li.classList.add('hp-pulse'); // pulse once typed
}
};
step();
}
hide() {
this.activeId = null;
this._clearTimers();
this.el.classList.remove('hp-visible');
this.el.setAttribute('aria-hidden', 'true');
}
// Position the panel at a screen-space point (px), centered horizontally above it.
place(x, y) {
this.el.style.left = x + 'px';
this.el.style.top = y + 'px';
}
}