Cambiar una sola instancia de icono

Traer de vuelta esta publicación antigua: Changing a single instance of an icon

Necesito cambiar instancias individuales de iconos en todo el sitio. No quiero usar replaceIcon() y anular todas las instancias de ese icono. Veo que en el pasado hicieron esto posible para áreas específicas del sitio, o eso es lo que parece, pero no en todo el sitio.

Por ejemplo, quiero cambiar UN icono de engranaje en la búsqueda para reflejar mejor lo que sucede cuando el usuario hace clic en él. No quiero cambiar todos los iconos de engranaje.

¡Gracias!

1 me gusta

Echa un vistazo a esta PR, podrías hacer algo similar solo con CSS:

3 Me gusta

Esto parece improvisado, ¿cómo funciona cuando navegas con la tecla Tab/solo con el teclado?

2 Me gusta

¡cuando uno está en el desierto y aparentemente no hay agua, los trucos pueden ser muy útiles!

2 Me gusta

Por desgracia, no estamos en el desierto… estamos pagando para usar una plataforma para alojar un sitio público.

2 Me gusta

¿Qué icono es este específicamente (dónde aparece)? Si parece que hay un caso razonable en el que otros podrían encontrarlo útil, podemos crear un alias para él para permitir que se reemplace por separado.

2 Me gusta

Aquí tienes un ejemplo en el que el engranaje no refleja la acción del botón de abrir la página de búsqueda expandida:

1 me gusta

Usamos un icono de “sliders” aquí por defecto (y es la única aparición de este icono en Discourse por defecto) — así que sería seguro usar replaceIcon() en este caso

image

Dado que no tenemos una API para reemplazar iconos individuales, la mejor manera en que podemos soportar esto hoy es evaluando solicitudes para agregar nuevos alias que agrupen casos de uso similares.

Así, por ejemplo, usamos d-liked como alias para heart para que replaceIcon() se pueda usar contra d-liked para cambiarlo en el contexto de “like”, en lugar de reemplazar cada aparición del icono de corazón en toda la aplicación.

Sería bueno reemplazar cualquier ocurrencia individual, esa no es una situación inusual en los temas — esperemos que algún día tengamos una API para eso.

¡Genial! El equipo de Discourse cambió el nuestro al engranaje de configuración, ¡así que lo arreglé! Gracias :pray:

2 Me gusta

Okay, ahora entiendo, ¿cuál es el veredicto sobre esto? Es triste saber que no está disponible hasta una actualización de la API, así que tendré que usar el método que he hecho por el momento.

@awesomerobot Aquí tienes un código actualizado de lo que he hecho para cambiar el icono de crear tema, que es un + sin cambio global a todos los +

<script type="text/discourse-plugin" version="0.8">
    api.onPageChange(() => {
        const button = document.querySelector("#create-topic");

        if (button) {
            // Ocultar temporalmente el botón mientras reemplazamos el icono
            button.style.display = "none";

            // Buscar el SVG dentro del botón
            const oldIcon = button.querySelector("svg");
            if (oldIcon) {
                // Eliminar el icono existente (SVG)
                oldIcon.remove();
            }

            // Crear un nuevo SVG para el icono de Font Awesome (fa-feather-pointed)
            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>';

            // Insertar el nuevo icono dentro del botón
            button.insertBefore(newIcon, button.firstChild);

            // Mostrar el botón de nuevo después de reemplazar el icono
            button.style.display = "block";
        }
    });
</script>

Tengo que refinarlo un poco más para múltiples cambios de icono

<script type="text/discourse-plugin" version="0.8">
    api.onPageChange(() => {
        // Lista de selectores de botones y contenido SVG personalizado correspondiente
        const iconsToReplace = [
            { selector: "#create-topic", newSVG: `
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
                    <path d="M12 2L15 5H13V9H11V5H9L12 2ZM3 12L5 10V13H9V15H5V18L3 16V12ZM21 12L19 10V13H15V15H19V18L21 16V12Z" />
                </svg>
            ` },
            { selector: "#some-other-button", newSVG: `
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
                    <path d="M21 3H3C2.44772 3 2 3.44772 2 4V20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20V4C22 3.44772 21.5523 3 21 3ZM20 19H4V5H20V19Z" />
                </svg>
            ` },
            { selector: "#another-button", newSVG: `
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
                    <path d="M12 2L8 6H10V10H14V6H16L12 2ZM12 12L8 16H10V20H14V16H16L12 12Z" />
                </svg>
            ` }
        ];

        iconsToReplace.forEach(icon => {
            const button = document.querySelector(icon.selector);

            if (button) {
                // Ocultar temporalmente el botón mientras reemplazamos el icono
                button.style.display = "none";

                // Buscar el SVG existente dentro del botón
                const oldIcon = button.querySelector("svg");
                if (oldIcon) {
                    // Eliminar el icono existente (SVG)
                    oldIcon.remove();
                }

                // Crear un nuevo elemento SVG y establecer su contenido
                const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                newIcon.innerHTML = icon.newSVG; // Establecer el contenido SVG personalizado

                // Insertar el nuevo icono SVG dentro del botón
                button.insertBefore(newIcon, button.firstChild);

                // Mostrar el botón de nuevo después de reemplazar el icono
                button.style.display = "block";
            }
        });
    });
</script>

También puedes usar iconHTML en lugar de agregar manualmente el icono.
Algo así debería 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 me gusta

Esto podría terminar siendo un poco inestable.

Creo que api.onPageChange se está activando en el cambio de ruta.

Eso es mucho antes de renderizar.

Por lo tanto, existe el riesgo de que los elementos no se hayan renderizado antes de que intentes encontrarlos y sobrescribirlos.

Podrías tener suerte, pero no está garantizado. Por eso awesomerobot mencionó que sería bueno si hubiera una API dedicada…

(@Don, tu solución tiene un problema similar, creo)

2 Me gusta

¡Sí, tienes razón! Esto no es muy estable. ¿Quizás podría ser más estable con requestAnimationFrame o MutationObserver? :thinking: pero sí, lo mejor sería una API dedicada como mencionaste.

2 Me gusta

Gracias por señalarlo; tienes toda la razón. Usar solo api.onPageChange puede ser poco fiable, ya que se activa al cambiar de ruta, no después de que el DOM se haya renderizado. Esto significa que existe el riesgo de una condición de carrera en la que el botón #create-topic podría no existir todavía cuando el script intente acceder a él.

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

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

            if (button) {
                // Desconectar el observador después de encontrar el elemento
                observer.disconnect();

                // Ocultar temporalmente el botón mientras reemplazamos el icono
                button.style.display = "none";

                // Eliminar el icono antiguo si existe
                const oldIcon = button.querySelector("svg");
                if (oldIcon) oldIcon.remove();

                // Crear e insertar el nuevo icono SVG de 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);

                // Volver a mostrar el botón
                button.style.display = "block";
            }
        };

        // Observar los cambios en el DOM e intentar mejorar el botón cuando se añada
        const observer = new MutationObserver(() => tryEnhanceButton());
        observer.observe(document.body, { childList: true, subtree: true });

        // Intentar inmediatamente también en caso de que ya esté allí
        tryEnhanceButton();
    });

@Don He incluido MutationObserver para asegurar que el script solo se ejecute una vez que el elemento objetivo exista realmente, y se desconecta después de hacer su trabajo para evitar sobrecarga innecesaria. Aún así, intenta una comprobación inmediata en caso de que el elemento ya exista.

No puedo obtener el SVG que quiero del sistema de iconos de Discourse, aunque lo registré en el subconjunto de iconos SVG de administrador, ya que parece estar en fontawesome pero no en discourse.

1 me gusta