Modifier une seule instance d'icône

Ramener cette ancienne publication : Changing a single instance of an icon

Je dois changer des instances uniques d’icônes sur le site. Je ne veux pas utiliser replaceIcon() et remplacer toutes les instances de cette icône. J’ai vu que par le passé, cela était possible pour des zones spécifiques du site, ou du moins c’est ce que j’ai compris, mais pas sur l’ensemble du site.

Par exemple, je veux changer UNE des icônes en forme de roue dentée dans la recherche pour mieux refléter ce qui se passe lorsque l’utilisateur clique dessus. Je ne veux pas changer toutes les icônes en forme de roue dentée.

Merci !

1 « J'aime »

Jetez un œil à cette PR, vous pourriez être en mesure de faire quelque chose de similaire avec juste du CSS :

3 « J'aime »

Cela semble artisanal, comment cela se passe-t-il lorsque vous naviguez avec la touche Tab ou uniquement avec le clavier ?

2 « J'aime »

quand on est dans le désert et qu’il n’y a apparemment pas d’eau, les astuces peuvent être très utiles !

2 « J'aime »

Hélas, nous ne sommes pas dans le désert… nous payons pour utiliser une plateforme afin d’héberger un site public.

2 « J'aime »

Quelle icône est-ce spécifiquement (où apparaît-elle) ? S’il semble qu’il y ait un cas raisonnable où d’autres pourraient la trouver utile, nous pouvons créer un alias pour elle afin de permettre son remplacement séparément.

2 « J'aime »

Voici un exemple où le rouage ne reflète pas l’action du bouton d’ouverture de la page de recherche étendue :

1 « J'aime »

Nous utilisons par défaut une icône « sliders » ici (et c’est la seule occurrence de cette icône dans Discourse par défaut) — il serait donc sûr d’utiliser replaceIcon() dans ce cas

image

Comme nous n’avons pas d’API pour remplacer des icônes individuelles, la meilleure façon de le prendre en charge aujourd’hui est d’évaluer les demandes d’ajout de nouveaux alias qui regroupent des cas d’utilisation similaires.

Ainsi, par exemple, nous utilisons d-liked comme alias pour heart afin que replaceIcon() puisse être utilisé sur d-liked pour le modifier dans le contexte « like », plutôt que de remplacer chaque occurrence de l’icône « heart » dans toute l’application.

Ce serait bien de remplacer toute occurrence unique cependant, ce n’est pas une situation inhabituelle dans les thèmes — espérons qu’un jour nous aurons une API pour cela.

Génial, il semble que l’équipe de Discourse ait changé le nôtre en l’icône d’engrenage, donc je l’ai corrigé ! Merci :pray:

2 « J'aime »

D’accord, je crois comprendre maintenant quel est le verdict. C’est dommage d’apprendre que ce n’est pas disponible avant une mise à jour de l’API, je vais donc utiliser la méthode que j’ai employée pour le moment.

@awesomerobot Voici un code mis à jour de ce que j’ai fait pour changer l’icône de création de sujet qui est un + sans changement global de tous les +

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

        if (button) {
            // Masquer temporairement le bouton pendant le remplacement de l'icône
            button.style.display = "none";

            // Rechercher le SVG à l'intérieur du bouton
            const oldIcon = button.querySelector("svg");
            if (oldIcon) {
                // Supprimer l'icône existante (SVG)
                oldIcon.remove();
            }

            // Créer un nouveau SVG pour l'icône 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>';

            // Insérer la nouvelle icône à l'intérieur du bouton
            button.insertBefore(newIcon, button.firstChild);

            // Afficher à nouveau le bouton après le remplacement de l'icône
            button.style.display = "block";
        }
    });
</script>

Il faut encore l’affiner un peu plus pour des changements d’icônes multiples

<script type="text/discourse-plugin" version="0.8">
    api.onPageChange(() => {
        // Liste des sélecteurs de boutons et du contenu SVG personnalisé correspondant
        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) {
                // Masquer temporairement le bouton pendant le remplacement de l'icône
                button.style.display = "none";

                // Rechercher le SVG existant à l'intérieur du bouton
                const oldIcon = button.querySelector("svg");
                if (oldIcon) {
                    // Supprimer l'icône existante (SVG)
                    oldIcon.remove();
                }

                // Créer un nouvel élément SVG et définir son contenu
                const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                newIcon.innerHTML = icon.newSVG; // Définir le contenu SVG personnalisé

                // Insérer la nouvelle icône SVG à l'intérieur du bouton
                button.insertBefore(newIcon, button.firstChild);

                // Afficher à nouveau le bouton après le remplacement de l'icône
                button.style.display = "block";
            }
        });
    });
</script>

Vous pouvez également utiliser iconHTML au lieu de ajouter l’icône manuellement.
Quelque chose comme ceci devrait fonctionner :

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 « J'aime »

Cela pourrait finir par être un peu instable.

Je crois que api.onPageChange se déclenche lors du changement de route.

C’est bien avant le rendu.

Par conséquent, il y a un risque que les éléments ne soient pas rendus avant que vous n’essayiez de les trouver et de les remplacer.

Vous pourriez avoir de la chance, mais ce n’est pas garanti. D’où l’idée d’awesomerobot de suggérer qu’il serait bien d’avoir une API dédiée…

(@Don, ta solution semble avoir un problème similaire, je pense)

2 « J'aime »

Oui, tu as raison ! Ce n’est pas très stable. Peut-être que ce serait plus stable avec requestAnimationFrame ou MutationObserver ? :thinking: mais oui, le mieux serait une API dédiée comme tu l’as mentionné.

2 « J'aime »

Merci d’avoir signalé cela — vous avez tout à fait raison. L’utilisation de api.onPageChange seul peut être peu fiable car elle se déclenche lors du changement de route, et non après le rendu du DOM. Cela signifie qu’il y a un risque de condition de concurrence où le bouton #create-topic pourrait ne pas encore exister lorsque le script tente d’y accéder.

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

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

            if (button) {
                // Déconnecter l'observateur après avoir trouvé l'élément
                observer.disconnect();

                // Cacher temporairement le bouton pendant que nous remplaçons l'icône
                button.style.display = "none";

                // Supprimer l'ancienne icône si elle existe
                const oldIcon = button.querySelector("svg");
                if (oldIcon) oldIcon.remove();

                // Créer et insérer la nouvelle icône SVG 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);

                // Réafficher le bouton
                button.style.display = "block";
            }
        };

        // Observer les changements dans le DOM et essayer d'améliorer le bouton lorsqu'il est ajouté
        const observer = new MutationObserver(() => tryEnhanceButton());
        observer.observe(document.body, { childList: true, subtree: true });

        // Essayer immédiatement également au cas où il serait déjà présent
        tryEnhanceButton();
    });

@Don J’ai inclus MutationObserver pour m’assurer que le script ne s’exécute qu’une fois que l’élément cible existe réellement. Il se déconnecte après avoir accompli sa tâche pour éviter une surcharge inutile. Il tente toujours une vérification immédiate au cas où l’élément existerait déjà.

Je ne parviens pas à récupérer le SVG que je souhaite du système d’icônes Discourse, bien que je l’aie enregistré dans l’ensemble d’icônes SVG d’administration, car il semble qu’il soit dans Font Awesome mais pas dans Discourse.

1 « J'aime »