Mudanças entre as edições de "Widget:Conquistas"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
m
Linha 1 037: Linha 1 037:
         }
         }


         /* Popover ancorado no chip — drop-down dos itens escondidos. */
         /* Popover ancorado no chip via JS vive em document.body com
         .gla-item-reward .reward-overflow-popover {
          position:fixed pra escapar o overflow:hidden de cards com ribbon
             position: absolute;
          (e qualquer outro contexto de clipping). Tooltips dos itens
            top: calc(100% + 6px);
          dentro saem livremente. */
            right: 0;
         .reward-overflow-popover {
             position: fixed;
             display: flex;
             display: flex;
             flex-wrap: wrap;
             flex-wrap: wrap;
             justify-content: flex-end;
             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.15),
             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: 10;
             z-index: 9999;
             max-width: 268px;
             max-width: 268px;
             animation: gla-popover-in 0.18s ease;
             animation: gla-popover-in 0.18s ease;
         }
         }


         .gla-item-reward .reward-overflow-popover[hidden] {
         .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 search = currentSearch.toLowerCase();
 
                // 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 titleEl = card.querySelector(".gla-item-title");
                     var matchSearch = true;
                    var descEl = card.querySelector(".gla-item-desc");
                    if (tokens.length > 0) {
                    var titleText = titleEl ? titleEl.textContent : "";
                        var hay = getHaystack(card);
                    var descText = descEl ? descEl.textContent : "";
                        for (var i = 0; i < tokens.length; i++) {
                    var matchSearch = !search ||
                            if (hay.indexOf(tokens[i]) === -1) { matchSearch = false; break; }
                        titleText.toLowerCase().indexOf(search) >= 0 ||
                        }
                        descText.toLowerCase().indexOf(search) >= 0;
                    }


                     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".
             root.addEventListener("click", function (e) {
             // O Lua emite <span role="button"> (MediaWiki não aceita <button>
                var toggle = e.target.closest(".gla-item-spoiler-toggle");
            // em wikitext), por isso aceitamos click + Enter/Space pra
                if (!toggle) return;
            // preservar semântica de botão acessível.
                if (e.target.closest(".item-wrapper")) return;
            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;
                e.preventDefault();
                 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);


                 reward.appendChild(popover);
                 // 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() {

Edição das 00h36min de 21 de maio de 2026