Cambiare un'istanza singola di icona

Riporta questo vecchio post: Changing a single instance of an icon

Ho bisogno di cambiare singole istanze di icone in tutto il sito. Non voglio usare replaceIcon() e sovrascrivere tutte le istanze di quell’icona. Vedo che in passato hanno reso possibile questo per aree specifiche del sito, o almeno così sembra, ma non per tutto il sito.

Ad esempio, voglio cambiare UNA delle icone a forma di ingranaggio nella ricerca per riflettere meglio ciò che accade quando l’utente ci clicca sopra. Non voglio cambiare tutte le icone a forma di ingranaggio.

Grazie!

1 Mi Piace

Dai un’occhiata a questo PR, potresti essere in grado di fare qualcosa di simile solo con CSS:

3 Mi Piace

Sembra una soluzione grezza, come si comporta quando si naviga con il tasto Tab/solo da tastiera?

2 Mi Piace

quando si è nel deserto e apparentemente non c’è acqua, gli hack possono tornare molto utili!

2 Mi Piace

Ahimè, non siamo nel deserto… stiamo pagando per usare una piattaforma per ospitare un sito pubblico.

2 Mi Piace

Quale icona è questa nello specifico (dove appare)? se sembra che ci sia un caso ragionevole in cui altri potrebbero trovarla utile, possiamo creare un alias per essa per consentirne la sostituzione separatamente.

2 Mi Piace

Ecco un esempio in cui l’ingranaggio non riflette l’azione del pulsante di apertura della pagina di ricerca espansa:

1 Mi Piace

Usiamo un’icona “sliders” qui per impostazione predefinita (ed è l’unica occorrenza di questa icona in Discourse per impostazione predefinita) — quindi sarebbe sicuro usare replaceIcon() in questo caso

image

Poiché non disponiamo di un’API per sostituire singole icone, il modo migliore per supportare questo oggi è valutare le richieste di aggiunta di nuovi alias che raggruppano casi d’uso simili.

Quindi, ad esempio, usiamo d-liked come alias per heart in modo che replaceIcon() possa essere utilizzato su d-liked per cambiarlo nel contesto “mi piace”, anziché sostituire ogni occorrenza dell’icona del cuore in tutta l’app.

Sarebbe bello sostituire qualsiasi occorrenza singola, che non è una situazione insolita nei temi — speriamo che un giorno avremo un’API per questo.

Fantastico, sembra che il team di Discourse abbia cambiato il nostro in quello dell’ingranaggio delle impostazioni, quindi l’ho sistemato! Grazie :pray:

2 Mi Piace

Va bene, ora capisco qual è il verdetto. Peccato sapere che non è disponibile fino a un aggiornamento dell’API, quindi per ora dovrò usare il metodo che ho adottato.

@awesomerobot Ecco un codice aggiornato di ciò che ho fatto per cambiare l’icona di creazione argomento che è un + senza modifiche globali a tutti i +

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

        if (button) {
            // Nascondi temporaneamente il pulsante mentre sostituiamo l'icona
            button.style.display = "none";

            // Cerca l'SVG all'interno del pulsante
            const oldIcon = button.querySelector("svg");
            if (oldIcon) {
                // Rimuovi l'icona esistente (SVG)
                oldIcon.remove();
            }

            // Crea un nuovo SVG per l'icona 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>';

            // Inserisci la nuova icona all'interno del pulsante
            button.insertBefore(newIcon, button.firstChild);

            // Mostra di nuovo il pulsante dopo la sostituzione dell'icona
            button.style.display = "block";
        }
    });
</script>

Devo perfezionarlo un po’ di più per modifiche multiple di icone

<script type="text/discourse-plugin" version="0.8">
    api.onPageChange(() => {
        // Elenco di selettori di pulsanti e relativi contenuti SVG personalizzati
        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) {
                // Nascondi temporaneamente il pulsante mentre sostituiamo l'icona
                button.style.display = "none";

                // Cerca l'SVG esistente all'interno del pulsante
                const oldIcon = button.querySelector("svg");
                if (oldIcon) {
                    // Rimuovi l'icona esistente (SVG)
                    oldIcon.remove();
                }

                // Crea un nuovo elemento SVG e imposta il suo contenuto
                const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                newIcon.innerHTML = icon.newSVG; // Imposta il contenuto SVG personalizzato

                // Inserisci la nuova icona SVG all'interno del pulsante
                button.insertBefore(newIcon, button.firstChild);

                // Mostra di nuovo il pulsante dopo la sostituzione dell'icona
                button.style.display = "block";
            }
        });
    });
</script>

Puoi anche usare iconHTML invece di aggiungere manualmente l’icona.
Qualcosa del genere dovrebbe funzionare:

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 Mi Piace

Questo potrebbe finire un po’ instabile.

Credo che api.onPageChange venga attivato al cambio di rotta.

Questo avviene molto prima del rendering.

Pertanto c’è il rischio che gli elementi non siano ancora stati renderizzati prima che tu cerchi di trovarli e modificarli.

Potresti avere fortuna, ma non è garantito. Dunque awesomerobot ha suggerito che sarebbe bello se ci fosse un’api dedicata …

(@Don, la tua soluzione ha un problema simile, credo)

2 Mi Piace

Sì, hai ragione! Non è molto stabile. Forse può essere più stabile con requestAnimationFrame o MutationObserver? :thinking: ma sì, la cosa migliore sarebbe un’API dedicata come hai menzionato.

2 Mi Piace

Grazie per avermelo fatto notare, hai assolutamente ragione. Usare solo api.onPageChange può essere inaffidabile poiché si attiva al cambio di rotta, non dopo il rendering del DOM. Ciò significa che c’è il rischio di una race condition in cui il pulsante #create-topic potrebbe non esistere ancora quando lo script tenta di accedervi.

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

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

            if (button) {
                // Disconnette l'observer dopo aver trovato l'elemento
                observer.disconnect();

                // Nasconde temporaneamente il pulsante mentre sostituiamo l'icona
                button.style.display = "none";

                // Rimuove la vecchia icona se esiste
                const oldIcon = button.querySelector("svg");
                if (oldIcon) oldIcon.remove();

                // Crea e inserisce la nuova icona SVG di 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 di nuovo il pulsante
                button.style.display = "block";
            }
        };

        // Osserva le modifiche al DOM e tenta di migliorare il pulsante quando viene aggiunto
        const observer = new MutationObserver(() => tryEnhanceButton());
        observer.observe(document.body, { childList: true, subtree: true });

        // Tenta immediatamente anche nel caso in cui sia già presente
        tryEnhanceButton();
    });

@Don Ho incluso MutationObserver per garantire che lo script venga eseguito solo una volta che l’elemento di destinazione esiste effettivamente, e si disconnette dopo aver svolto il suo compito per evitare overhead non necessari. Tenta comunque un controllo immediato nel caso in cui l’elemento esista già.

Non riesco a recuperare l’SVG che desidero dal sistema di icone di Discourse, anche se l’ho registrato come sottoinsieme di icone SVG per l’amministratore, poiché sembra essere in fontawesome ma non in discourse.

1 Mi Piace