커스텀 섹션의 이모지

사이드바의 카테고리 섹션에 이모지를 사용하고 있지만, 공개된 사용자 지정 섹션도 하나 있습니다. 카테고리 섹션에 비해 이 섹션이 너무 “단조로워” 보이지 않도록 동일한 컬러 아이콘을 사용하고 싶습니다.

이것이 가능한가요?

2개의 좋아요

ChatGPT와 Claude의 도움으로 작동하게 만들었고, 매우 커스터마이징할 수 있게 되었습니다:

해 보고 싶으시다면, 새로운 컴포넌트를 생성하고 CSS 탭에 다음 내용을 추가하세요:

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

제 경우 1rem이 아주 잘 작동합니다. 포럼/커뮤니티에 맞게 조정해 주세요.

그런 다음 JS 탭에서:

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

export default apiInitializer("0.11.1", (api) => {
  // Map section names to IDs
  const sectionIds = {
    "community": 1,
    "tiago": 2,
    "personal-section": 3,
    // Add more sections here
  };
  
  // Map of [sectionId, itemName] to emoji names
  const iconReplacements = {
    // Community section (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",
    
    // Tiago section (ID: 2)
    "2,Journal": "notebook_with_decorative_cover",
    "2,Music": "musical_note",
    "2,About": "bust_in_silhouette",
    
    // Personal section (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(',');
        
        // Only process if this replacement is for the current section
        if (parseInt(keyId) === sectionId) {
          const url = emojiUrlFor(emojiName);
          
          // Try multiple selectors to catch both hamburger menu and fixed sidebar
          const selectors = [
            // Fixed sidebar selector
            `div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // Hamburger menu selector (more specific)
            `.menu-panel div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // Generic fallback
            `[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) {
              // Check if it's already replaced to avoid unnecessary DOM manipulation
              if (!prefix.querySelector('.prefix-emoji')) {
                prefix.innerHTML = `
                  <img src="${url}"
                       title="${emojiName}"
                       alt="${emojiName}"
                       class="emoji prefix-emoji">
                `;
              }
              break; // Exit loop once we find and replace the icon
            }
          }
        }
      });
    });
  }

  // Run on page change
  api.onPageChange(replaceIcons);
  
  // Also run with delay to catch dynamically loaded content
  api.onPageChange(() => {
    setTimeout(replaceIcons, 100);
    setTimeout(replaceIcons, 500);
  });

  // Enhanced MutationObserver to catch sidebar changes
  const observer = new MutationObserver((mutations) => {
    let shouldReplace = false;
    
    mutations.forEach((mutation) => {
      // Watch for added nodes (original functionality)
      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;
          }
        }
      });
      
      // Watch for attribute changes on sidebar sections (collapse/expand)
      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;
        }
      }
      
      // Watch for childList changes in sidebar sections
      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);
    }
  });

  // Start observing with enhanced options
  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true, // Watch for attribute changes
    attributeFilter: ['class', 'style', 'aria-expanded'], // Common attributes that change on collapse/expand
  });

  // Additional event listeners for common sidebar interactions
  document.addEventListener('click', (event) => {
    // Check if click was on a sidebar section header or toggle button
    if (event.target.closest('.sidebar-section-header') ||
        event.target.closest('[data-section-name]') ||
        event.target.matches('.sidebar-section-toggle')) {
      setTimeout(replaceIcons, 100);
    }
  });
});

이름으로 새로운 커스텀 섹션을 쉽게 추가할 수 있도록 만들었습니다. id를 부여한 후 각 섹션의 매핑을 생성하는 방식입니다.

그 이유는 예를 들어 Journal이라는 제목의 섹션이 2개 있을 수 있지만, 각각에 다른 이모지를 사용하고 싶을 수 있기 때문입니다.

이 기능을 위한 컴포넌트나 플러그인이 있다는 말을 하는 사람이 분명 있을 거라고 확신합니다. :wink: 하지만 제 방식대로 모든 것이 작동할 때까지 ChatGPT와 Claude를 오가며 작업하는 것도 여전히 재미있었습니다.

다른 사용자분들에게 도움이 되기를 바랍니다.

조정하거나 개선할 수 있는 부분이 보인다면, 공유해 주세요 :raising_hands:

4개의 좋아요

JS 코드를 방금 업데이트했는데, 모바일에서 여전히 기본 아이콘이 표시되고 있는 것을 발견했기 때문입니다. 이제 모바일과 데스크톱 모두에서 예상대로 정상 작동합니다.

또 다른 수정 사항: 섹션을 접었다가 다시 펼치면 이모지가 사라지고 원래 아이콘으로 대체되었습니다. 지금은 정상적으로 작동하며, JS 코드를 다시 업데이트했습니다.

방법을 찾아주셔서 감사합니다 – 이제 카테고리에서 이모지를 지원하므로, 이는 핵심 기능으로 매우 자연스럽게 어울릴 것 같습니다.

3개의 좋아요

동의합니다. 언젠가 네이티브 기능이 되었으면 좋겠어요. 그때까지 이 커스텀 컴포넌트가 제 역할을 잘하고 있어요 :slight_smile:

2개의 좋아요

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