Emojis en la Sección Personalizada

Estoy usando emojis en la sección de Categorías en la barra lateral, pero también tengo una sección personalizada que es pública. Quería tener los mismos iconos coloridos en esa sección para que no se vea tan “insípida” en comparación con la sección de Categorías.

¿Es esto posible?

2 Me gusta

Con la ayuda de ChatGPT y Claude, pude hacerlo funcionar y ser muy personalizable:

Si quieres hacerlo, crea un nuevo componente y añade esto a la pestaña CSS:

.sidebar-section-link-prefix .emoji.prefix-emoji {
  width: 1rem !important;
  height: 1rem !important;
}

En mi caso particular, 1rem funciona muy bien. Ajústalo a tu foro/comunidad.

Luego, en la pestaña JS:

import { apiInitializer } from "discourse/lib/api";
import { emojiUrlFor } from "discourse/lib/text";

export default apiInitializer("0.11.1", (api) => {
  // Mapear nombres de sección a IDs
  const sectionIds = {
    "community": 1,
    "tiago": 2,
    "personal-section": 3,
    // Añadir más secciones aquí
  };
  
  // Mapa de [sectionId, itemName] a nombres de emoji
  const iconReplacements = {
    // Sección Comunidad (ID: 1)
    "1,admin": "wrench",
    "1,review": "triangular_flag_on_post",
    "1,everything": "books",
    "1,my-posts": "writing_hand",
    "1,my-messages": "envelope_with_arrow",
    
    // Sección Tiago (ID: 2)
    "2,Journal": "notebook_with_decorative_cover",
    "2,Music": "musical_note",
    "2,About": "bust_in_silhouette",
    
    // Sección Personal (ID: 3)
    "3,Backups": "floppy_disk",
    "3,Scheduled": "clock3",
    "3,Staff": "lock",
    "3,Components": "electric_plug",
  };

  function replaceIcons() {
    Object.entries(sectionIds).forEach(([sectionName, sectionId]) => {
      Object.entries(iconReplacements).forEach(([key, emojiName]) => {
        const [keyId, itemName] = key.split(',');
        
        // Procesar solo si este reemplazo es para la sección actual
        if (parseInt(keyId) === sectionId) {
          const url = emojiUrlFor(emojiName);
          
          // Intentar múltiples selectores para capturar tanto el menú hamburguesa como la barra lateral fija
          const selectors = [
            // Selector de barra lateral fija
            `div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // Selector de menú hamburguesa (más específico)
            `.menu-panel div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // Fallback genérico
            `[data-section-name="${sectionName}"] [data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`
          ];
          
          for (const selector of selectors) {
            const prefix = document.querySelector(selector);
            
            if (prefix && url) {
              // Comprobar si ya ha sido reemplazado para evitar manipulación innecesaria del DOM
              if (!prefix.querySelector('.prefix-emoji')) {
                prefix.innerHTML = `
                  <img src="${url}"
                       title="${emojiName}"
                       alt="${emojiName}"
                       class="emoji prefix-emoji">
                `;
              }
              break; // Salir del bucle una vez que encontremos y reemplacemos el icono
            }
          }
        }
      });
    });
  }

  // Ejecutar al cambiar de página
  api.onPageChange(replaceIcons);
  
  // También ejecutar con un retraso para capturar contenido cargado dinámicamente
  api.onPageChange(() => {
    setTimeout(replaceIcons, 100);
    setTimeout(replaceIcons, 500);
  });

  // MutationObserver mejorado para capturar cambios en la barra lateral
  const observer = new MutationObserver((mutations) => {
    let shouldReplace = false;
    
    mutations.forEach((mutation) => {
      // Observar nodos añadidos (funcionalidad original)
      mutation.addedNodes.forEach((node) => {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.classList?.contains('menu-panel') || 
              node.querySelector?.('.sidebar-sections') ||
              node.classList?.contains('sidebar-sections') ||
              node.querySelector?.('[data-section-name]')) {
            shouldReplace = true;
          }
        }
      });
      
      // Observar cambios de atributos en secciones de la barra lateral (colapsar/expandir)
      if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
        const target = mutation.target;
        if (target.matches('[data-section-name]') || 
            target.closest('[data-section-name]') ||
            target.matches('.sidebar-section') ||
            target.closest('.sidebar-section')) {
          shouldReplace = true;
        }
      }
      
      // Observar cambios en childList en secciones de la barra lateral
      if (mutation.type === 'childList' && mutation.target.nodeType === Node.ELEMENT_NODE) {
        const target = mutation.target;
        if (target.matches('[data-section-name]') || 
            target.closest('[data-section-name]') ||
            target.querySelector('[data-section-name]')) {
          shouldReplace = true;
        }
      }
    });
    
    if (shouldReplace) {
      setTimeout(replaceIcons, 50);
    }
  });

  // Iniciar la observación con opciones mejoradas
  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true, // Observar cambios de atributos
    attributeFilter: ['class', 'style', 'aria-expanded'], // Atributos comunes que cambian al colapsar/expandir
  });

  // Escuchadores de eventos adicionales para interacciones comunes de la barra lateral
  document.addEventListener('click', (event) => {
    // Comprobar si el clic fue en un encabezado de sección de la barra lateral o un botón de alternancia
    if (event.target.closest('.sidebar-section-header') ||
        event.target.closest('[data-section-name]') ||
        event.target.matches('.sidebar-section-toggle')) {
      setTimeout(replaceIcons, 100);
    }
  });
});

Lo hice de manera que pueda añadir fácilmente nuevas secciones personalizadas por nombre, asignándole un id y luego creando el mapeo de cada sección.
La razón de esto es que puedo tener 2 secciones con el título Journal, por ejemplo, pero tal vez quiero 2 emojis diferentes para cada una.

Estoy bastante seguro de que alguien dirá que hay un componente o plugin para esto :wink: pero aun así fue divertido ir y venir con ChatGPT y Claude hasta que todo funcionó como quería.

Espero que esto ayude a otros usuarios.

Si ves algo que se pueda ajustar/mejorar, por favor compártelo :raising_hands:

3 Me gusta

Acabo de actualizar el código JS, porque noté que en el móvil todavía se mostraban los iconos predeterminados. Todo funciona como se esperaba tanto en el móvil como en el escritorio.

Otra edición: cuando colapsaba y expandía la sección, los emojis desaparecían y eran reemplazados por los iconos originales. Ahora funciona y actualicé el código JS nuevamente.

Me alegra que hayas encontrado una manera de hacer esto; creo que encajaría de forma bastante natural en una función principal, ahora que admitimos emojis para las categorías.

3 Me gusta

Estoy de acuerdo. Espero que esto se convierta en una función nativa eventualmente. Hasta que eso suceda, este componente personalizado está haciendo su trabajo :slight_smile:

2 Me gusta

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.