Mudanças entre as edições de "Widget:Conquistas"
Ir para navegação
Ir para pesquisar
m |
m |
||
| Linha 1 037: | Linha 1 037: | ||
} | } | ||
/* Popover ancorado no chip — | /* Popover ancorado no chip via JS — vive em document.body com | ||
position:fixed pra escapar o overflow:hidden de cards com ribbon | |||
position: | (e qualquer outro contexto de clipping). Tooltips dos itens | ||
dentro saem livremente. */ | |||
.reward-overflow-popover { | |||
position: fixed; | |||
display: flex; | display: flex; | ||
flex-wrap: wrap; | flex-wrap: wrap; | ||
justify-content: flex- | justify-content: flex-start; | ||
gap: 4px; | gap: 4px; | ||
padding: 8px; | padding: 8px; | ||
background: var(--gla-paper); | background: var(--gla-paper, #ffffff); | ||
border: 1px solid var(--gla-rule); | border: 1px solid var(--gla-rule, #e2e8f0); | ||
border-radius: 10px; | border-radius: 10px; | ||
box-shadow: 0 6px 18px -4px rgba(15, 23, 42, 0. | box-shadow: 0 6px 18px -4px rgba(15, 23, 42, 0.18), | ||
0 2px 6px -2px rgba(15, 23, 42, 0.08); | 0 2px 6px -2px rgba(15, 23, 42, 0.08); | ||
z-index: | z-index: 9999; | ||
max-width: 268px; | max-width: 268px; | ||
animation: gla-popover-in 0.18s ease; | animation: gla-popover-in 0.18s ease; | ||
} | } | ||
.reward-overflow-popover[hidden] { | |||
display: none; | display: none; | ||
} | } | ||
| Linha 1 205: | Linha 1 206: | ||
} | } | ||
} | } | ||
} | |||
// Normalização pra busca: lowercase + remove acentos (NFD divide | |||
// base+diacrítico, regex remove os diacríticos). Assim "voce" | |||
// acha "você", "missao" acha "missão", etc. | |||
function normalize(s) { | |||
return (s || "") | |||
.toLowerCase() | |||
.normalize("NFD") | |||
.replace(/[̀-ͯ]/g, ""); | |||
} | |||
// Cacheia o "haystack" (title + desc normalizados) no próprio card | |||
// pra não recalcular a cada keystroke. Recomputa só se o conteúdo | |||
// do card mudar (não acontece na vida do widget — é estático). | |||
function getHaystack(card) { | |||
var cached = card.__glaHaystack; | |||
if (cached) return cached; | |||
var titleEl = card.querySelector(".gla-item-title"); | |||
var descEl = card.querySelector(".gla-item-desc"); | |||
var titleText = titleEl ? titleEl.textContent : ""; | |||
var descText = descEl ? descEl.textContent : ""; | |||
cached = normalize(titleText + " " + descText); | |||
card.__glaHaystack = cached; | |||
return cached; | |||
} | } | ||
| Linha 1 210: | Linha 1 236: | ||
var panel = root.querySelector('.gla-conquistas-panel[data-tab-content="' + currentTab + '"]'); | var panel = root.querySelector('.gla-conquistas-panel[data-tab-content="' + currentTab + '"]'); | ||
if (!panel) return; | if (!panel) return; | ||
var | |||
// Token-based search: divide a query em palavras e exige que | |||
// TODAS apareçam no haystack (title + desc), em qualquer | |||
// ordem. "voce grand" → tokens ["voce", "grand"] → casa com | |||
// "Você entrou na Grand Line pela primeira vez". Acentos e | |||
// case são ignorados via normalize(). | |||
var tokens = normalize(currentSearch).split(/\s+/).filter(Boolean); | |||
panel.querySelectorAll(".gla-item").forEach(function (card) { | panel.querySelectorAll(".gla-item").forEach(function (card) { | ||
var | var matchSearch = true; | ||
if (tokens.length > 0) { | |||
var hay = getHaystack(card); | |||
for (var i = 0; i < tokens.length; i++) { | |||
if (hay.indexOf(tokens[i]) === -1) { matchSearch = false; break; } | |||
} | |||
} | |||
var hidden = card.getAttribute("data-hidden") === "true"; | var hidden = card.getAttribute("data-hidden") === "true"; | ||
| Linha 1 255: | Linha 1 288: | ||
}); | }); | ||
// Spoiler — abre/fecha só no botão "Spoiler" | // Spoiler — abre/fecha só no botão "Spoiler". | ||
// O Lua emite <span role="button"> (MediaWiki não aceita <button> | |||
// em wikitext), por isso aceitamos click + Enter/Space pra | |||
// preservar semântica de botão acessível. | |||
function toggleSpoiler(toggle) { | |||
var card = toggle.closest(".gla-item.has-spoiler"); | var card = toggle.closest(".gla-item.has-spoiler"); | ||
if (!card) return; | if (!card) return; | ||
var isOpen = card.classList.contains("is-open"); | var isOpen = card.classList.contains("is-open"); | ||
root.querySelectorAll(".gla-item.is-open").forEach(function (c) { | root.querySelectorAll(".gla-item.is-open").forEach(function (c) { | ||
| Linha 1 275: | Linha 1 306: | ||
toggle.setAttribute("aria-expanded", "true"); | toggle.setAttribute("aria-expanded", "true"); | ||
} | } | ||
} | |||
root.addEventListener("click", function (e) { | |||
var toggle = e.target.closest(".gla-item-spoiler-toggle"); | |||
if (!toggle) return; | |||
if (e.target.closest(".item-wrapper")) return; | |||
e.preventDefault(); | |||
toggleSpoiler(toggle); | |||
}); | |||
root.addEventListener("keydown", function (e) { | |||
if (e.key !== "Enter" && e.key !== " ") return; | |||
var toggle = e.target.closest(".gla-item-spoiler-toggle"); | |||
if (!toggle) return; | |||
e.preventDefault(); | |||
toggleSpoiler(toggle); | |||
}); | }); | ||
| Linha 1 385: | Linha 1 432: | ||
(function () { | (function () { | ||
var MAX_VISIBLE = 4; | var MAX_VISIBLE = 4; | ||
// Container portal: vive direto em document.body, fora de | |||
// qualquer .gla-item / .gla-item-reward. Necessário pra que | |||
// cards com overflow:hidden (ex.: variante "ribbon") não | |||
// recortem o popover nem os tooltips dos itens dentro dele. | |||
var portal = null; | |||
function ensurePortal() { | |||
if (portal && portal.isConnected) return portal; | |||
portal = document.querySelector(".gla-conquistas-portal"); | |||
if (!portal) { | |||
portal = document.createElement("div"); | |||
portal.className = "gla-conquistas-portal"; | |||
portal.style.position = "absolute"; | |||
portal.style.top = "0"; | |||
portal.style.left = "0"; | |||
portal.style.width = "0"; | |||
portal.style.height = "0"; | |||
portal.style.pointerEvents = "none"; | |||
portal.style.zIndex = "9998"; | |||
document.body.appendChild(portal); | |||
} | |||
return portal; | |||
} | |||
function positionPopover(popover, chip) { | |||
var rect = chip.getBoundingClientRect(); | |||
var pw = popover.offsetWidth || 240; | |||
var vpW = window.innerWidth || document.documentElement.clientWidth; | |||
// Alinha pela direita do chip; clampa pra não vazar viewport. | |||
var left = rect.right - pw; | |||
if (left < 8) left = 8; | |||
if (left + pw > vpW - 8) left = vpW - 8 - pw; | |||
popover.style.left = left + "px"; | |||
popover.style.top = (rect.bottom + 6) + "px"; | |||
popover.style.pointerEvents = "auto"; | |||
} | |||
function processOne(reward) { | function processOne(reward) { | ||
| Linha 1 416: | Linha 1 499: | ||
line.appendChild(chip); | line.appendChild(chip); | ||
// Anexa o popover ao portal global (não ao card) — escapa | |||
// overflow:hidden de qualquer ancestral. | |||
ensurePortal().appendChild(popover); | |||
chip.addEventListener("click", function (e) { | chip.addEventListener("click", function (e) { | ||
| Linha 1 424: | Linha 1 509: | ||
if (!open) { | if (!open) { | ||
popover.hidden = false; | popover.hidden = false; | ||
// Render primeiro (offsetWidth precisa do layout), | |||
// depois posiciona. | |||
positionPopover(popover, chip); | |||
chip.setAttribute("aria-expanded", "true"); | chip.setAttribute("aria-expanded", "true"); | ||
chip.__glaPopover = popover; | |||
} | } | ||
}); | }); | ||
| Linha 1 434: | Linha 1 523: | ||
document.querySelectorAll(".reward-overflow-popover").forEach(function (p) { | document.querySelectorAll(".reward-overflow-popover").forEach(function (p) { | ||
p.hidden = true; | p.hidden = true; | ||
p.style.pointerEvents = "none"; | |||
}); | }); | ||
document.querySelectorAll(".reward-more-chip").forEach(function (c) { | document.querySelectorAll(".reward-more-chip").forEach(function (c) { | ||
| Linha 1 439: | Linha 1 529: | ||
}); | }); | ||
} | } | ||
// Reposiciona qualquer popover aberto em scroll/resize. | |||
function repositionOpen() { | |||
document.querySelectorAll(".reward-more-chip[aria-expanded=\"true\"]").forEach(function (chip) { | |||
var pop = chip.__glaPopover; | |||
if (pop && !pop.hidden) positionPopover(pop, chip); | |||
}); | |||
} | |||
window.addEventListener("resize", repositionOpen); | |||
window.addEventListener("scroll", repositionOpen, true); | |||
// Click fora do chip e do popover fecha tudo. | |||
document.addEventListener("click", function (e) { | |||
if (e.target.closest(".reward-more-chip")) return; | |||
if (e.target.closest(".reward-overflow-popover")) return; | |||
closeAll(); | |||
}); | |||
document.addEventListener("keydown", function (e) { | |||
if (e.key === "Escape") closeAll(); | |||
}); | |||
function processAll() { | function processAll() { | ||