Klima: Achievement-Tap-Tooltip (Touch hat keinen Hover)

Tap auf eine Erfolgs-Badge öffnet einen kleinen Tooltip mit Titel +
Beschreibung. Verschwindet nach 4 s oder beim nächsten Tap außerhalb.
Position bevorzugt unter der Badge, mit Fallback nach oben falls unten
kein Platz.
This commit is contained in:
2026-05-02 21:19:48 +02:00
parent 80c487d463
commit f6ff4122e2
2 changed files with 111 additions and 0 deletions
+56
View File
@@ -587,6 +587,23 @@
}
}
/* Achievement-Tooltip: title-Attribute öffnen sich auf Touch nicht; daher
ein eigenes Tap-Popup mit Titel + Beschreibung. */
.kw-ach-tooltip {
position: fixed;
z-index: 1500;
max-width: 240px;
background: var(--ggs-fjord-dark);
color: var(--ggs-white);
padding: 8px 12px;
border-radius: var(--ggs-radius-sm);
font-size: 13px;
line-height: 1.4;
box-shadow: var(--ggs-shadow-md);
pointer-events: none;
animation: ggs-fade-in 0.15s var(--ggs-ease) both;
}
@keyframes kw-build-slidein {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
@@ -1728,11 +1745,50 @@ function renderAchievements(force) {
if (!has) classes.push('kw-ach-locked');
el.className = classes.join(' ');
el.title = a.title + (has ? ' — ' + a.desc : ' (noch nicht)');
el.dataset.achTitle = a.title;
el.dataset.achDesc = has ? a.desc : 'Noch nicht freigeschaltet.';
el.textContent = has ? a.icon : '?';
host.appendChild(el);
});
}
/* Achievement-Tap-Tooltip — title greift auf Touch nicht. Tap auf eine
Badge zeigt einen kleinen schwarzen Tooltip mit Titel + Beschreibung,
verschwindet nach 4 s oder beim nächsten Tap außerhalb. */
let _achTooltip = null;
let _achTooltipTimer = 0;
function _hideAchTooltip() {
if (_achTooltipTimer) { clearTimeout(_achTooltipTimer); _achTooltipTimer = 0; }
if (_achTooltip) { _achTooltip.remove(); _achTooltip = null; }
}
document.addEventListener('click', (e) => {
if (_achTooltip && !_achTooltip.contains(e.target) && !e.target.closest('.ggs-badge-earned')) {
_hideAchTooltip();
}
const badge = e.target.closest('.ggs-badge-earned');
if (!badge) return;
const title = badge.dataset.achTitle || badge.title || '';
const desc = badge.dataset.achDesc || '';
if (!title) return;
_hideAchTooltip();
const t = document.createElement('div');
t.className = 'kw-ach-tooltip';
t.innerHTML = '<strong>' + title.replace(/[<>&]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])) + '</strong>'
+ (desc ? '<br>' + desc.replace(/[<>&]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])) : '');
document.body.appendChild(t);
_achTooltip = t;
const rect = badge.getBoundingClientRect();
const tw = t.offsetWidth;
const th = t.offsetHeight;
const left = Math.max(8, Math.min(rect.left + rect.width / 2 - tw / 2, window.innerWidth - tw - 8));
// Bevorzugt unter der Badge; falls dort kein Platz, nach oben.
let top = rect.bottom + 8;
if (top + th > window.innerHeight - 8) top = Math.max(8, rect.top - th - 8);
t.style.left = left + 'px';
t.style.top = top + 'px';
_achTooltipTimer = setTimeout(_hideAchTooltip, 4000);
});
/* ============================================================
UI RENDERING
============================================================ */
+55
View File
@@ -715,6 +715,23 @@
}
}
/* Achievement-Tooltip: title-Attribute öffnen sich auf Touch nicht; daher
ein eigenes Tap-Popup mit Titel + Beschreibung. */
.kw-ach-tooltip {
position: fixed;
z-index: 1500;
max-width: 240px;
background: var(--ggs-fjord-dark);
color: var(--ggs-white);
padding: 8px 12px;
border-radius: var(--ggs-radius-sm);
font-size: 13px;
line-height: 1.4;
box-shadow: var(--ggs-shadow-md);
pointer-events: none;
animation: ggs-fade-in 0.15s var(--ggs-ease) both;
}
@keyframes kw-build-slidein {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
@@ -2036,11 +2053,49 @@ function renderAchievements(force) {
if (!has) classes.push('kw-ach-locked');
el.className = classes.join(' ');
el.title = a.title + (has ? ' — ' + a.desc : ' (noch nicht)');
el.dataset.achTitle = a.title;
el.dataset.achDesc = has ? a.desc : 'Noch nicht freigeschaltet.';
el.textContent = has ? a.icon : '?';
host.appendChild(el);
});
}
/* Achievement-Tap-Tooltip — title greift auf Touch nicht. Tap auf eine
Badge zeigt einen kleinen schwarzen Tooltip mit Titel + Beschreibung,
verschwindet nach 4 s oder beim nächsten Tap außerhalb. */
let _achTooltip = null;
let _achTooltipTimer = 0;
function _hideAchTooltip() {
if (_achTooltipTimer) { clearTimeout(_achTooltipTimer); _achTooltipTimer = 0; }
if (_achTooltip) { _achTooltip.remove(); _achTooltip = null; }
}
document.addEventListener('click', (e) => {
if (_achTooltip && !_achTooltip.contains(e.target) && !e.target.closest('.ggs-badge-earned')) {
_hideAchTooltip();
}
const badge = e.target.closest('.ggs-badge-earned');
if (!badge) return;
const title = badge.dataset.achTitle || badge.title || '';
const desc = badge.dataset.achDesc || '';
if (!title) return;
_hideAchTooltip();
const t = document.createElement('div');
t.className = 'kw-ach-tooltip';
t.innerHTML = '<strong>' + title.replace(/[<>&]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])) + '</strong>'
+ (desc ? '<br>' + desc.replace(/[<>&]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])) : '');
document.body.appendChild(t);
_achTooltip = t;
const rect = badge.getBoundingClientRect();
const tw = t.offsetWidth;
const th = t.offsetHeight;
const left = Math.max(8, Math.min(rect.left + rect.width / 2 - tw / 2, window.innerWidth - tw - 8));
let top = rect.bottom + 8;
if (top + th > window.innerHeight - 8) top = Math.max(8, rect.top - th - 8);
t.style.left = left + 'px';
t.style.top = top + 'px';
_achTooltipTimer = setTimeout(_hideAchTooltip, 4000);
});
/* ============================================================
UI RENDERING
============================================================ */