Alterar uma única instância de ícone

Traga este post antigo de volta: Changing a single instance of an icon

Preciso alterar instâncias únicas de ícones em todo o site. Não quero usar replaceIcon() e sobrescrever todas as instâncias desse ícone. Vejo que no passado eles tornaram isso possível para áreas específicas do site, ou pelo menos é o que parece, mas não em todo o site.

Por exemplo, quero mudar UM dos ícones de engrenagem na pesquisa para refletir melhor o que acontece quando o usuário clica nele. Não quero mudar todos os ícones de engrenagem.

Obrigado!

1 curtida

Dê uma olhada neste PR, você talvez consiga fazer algo semelhante apenas com CSS:

3 curtidas

Isso parece improvisado, como funciona quando você navega com Tab/apenas pelo teclado?

2 curtidas

quando se está no deserto e aparentemente não há água, truques podem ser muito úteis!

2 curtidas

Infelizmente, não estamos no deserto… estamos pagando para usar uma plataforma para hospedar um site público.

2 curtidas

Qual ícone é este especificamente (onde ele aparece)? Se parecer que há um caso razoável em que outros possam achar útil, podemos criar um alias para ele para permitir que seja substituído separadamente.

2 curtidas

Aqui está um exemplo em que a engrenagem não reflete a ação do botão de abrir a página de pesquisa expandida:

1 curtida

Usamos um ícone de “sliders” aqui por padrão (e é a única ocorrência deste ícone no Discourse por padrão) — então seria seguro usar replaceIcon() neste caso

image

Como não temos uma API para substituir ícones individuais, a melhor maneira de dar suporte a isso hoje é avaliando solicitações para adicionar novos aliases que agrupem casos de uso semelhantes.

Assim, por exemplo, usamos d-liked como um alias para heart para que replaceIcon() possa ser usado contra d-liked para alterá-lo no contexto de “like”, em vez de substituir todas as ocorrências do ícone de coração em todo o aplicativo.

Seria bom substituir qualquer ocorrência única, isso não é uma situação incomum em temas — esperamos que um dia tenhamos uma API para isso.

incrível, parece que a equipe do discourse mudou o nosso para o ícone de engrenagem, então eu o corrigi! Obrigado :pray:

2 curtidas

Ok, agora eu entendi qual é o veredito sobre isso. É triste saber que não está disponível até uma atualização da API, então terei que usar o método que fiz por enquanto.

{

Você também pode usar iconHTML em vez de adicionar manualmente o ícone.
Algo assim deve funcionar:

import { apiInitializer } from "discourse/lib/api";
import { iconHTML } from "discourse/lib/icon-library";

export default apiInitializer((api) => {
  api.onPageChange(() => {
    const btnIcon = document.querySelector("#create-topic .d-icon");
    if (btnIcon) {
      btnIcon.outerHTML = iconHTML("plus");
    }
  });
});
1 curtida

Isso pode acabar sendo um pouco instável.

Acredito que api.onPageChange esteja sendo acionado na mudança de rota.

Isso acontece bem antes da renderização.

Portanto, há o risco de os elementos não serem renderizados antes de você tentar encontrá-los e sobrescrevê-los.

Você pode ter sorte, mas não é garantido. Por isso, o awesomerobot sugeriu que seria bom se existisse uma API dedicada…

(@Don, sua solução tem um problema semelhante, acredito)

2 curtidas

Sim, você está certo! Isso não é muito estável. Talvez possa ser mais estável com requestAnimationFrame ou MutationObserver? :thinking: mas sim, o melhor seria uma API dedicada como você mencionou.

2 curtidas

Obrigado por apontar isso — você está absolutamente certo. Usar apenas api.onPageChange pode ser não confiável, pois ele é acionado na mudança de rota, não após a renderização do DOM. Isso significa que há um risco de condição de corrida em que o botão #create-topic pode ainda não existir quando o script tentar acessá-lo.

 api.onPageChange(() => {
        const targetSelector = "#create-topic";

        const tryEnhanceButton = () => {
            const button = document.querySelector(targetSelector);

            if (button) {
                // Desconecta o observador após encontrar o elemento
                observer.disconnect();

                // Oculta temporariamente o botão enquanto substituímos o ícone
                button.style.display = "none";

                // Remove o ícone antigo, se existir
                const oldIcon = button.querySelector("svg");
                if (oldIcon) oldIcon.remove();

                // Cria e insere o novo ícone SVG do Font Awesome
                const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                newIcon.setAttribute("class", "fa d-icon d-icon-feather-pointed svg-icon svg-string");
                newIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
                newIcon.innerHTML = '<use href="#feather-pointed"></use>';

                button.insertBefore(newIcon, button.firstChild);

                // Mostra o botão novamente
                button.style.display = "block";
            }
        };

        // Observa as mudanças no DOM e tenta aprimorar o botão quando ele for adicionado
        const observer = new MutationObserver(() => tryEnhanceButton());
        observer.observe(document.body, { childList: true, subtree: true });

        // Tenta imediatamente também, caso já esteja lá
        tryEnhanceButton();
    });

@Don Incluí o MutationObserver para garantir que o script só seja executado quando o elemento de destino realmente existir, ele se desconecta após cumprir sua função para evitar sobrecarga desnecessária. Ele ainda tenta uma verificação imediata caso o elemento já exista.

Não consigo buscar o SVG que desejo do sistema de ícones do Discourse, embora eu o tenha registrado como um subconjunto de ícones SVG de administrador, pois parece que ele está no fontawesome, mas não no discourse.

1 curtida