カスタムセクションの絵文字

サイドバーの「カテゴリ」セクションで絵文字を使用していますが、公開されているカスタムセクションもあります。「カテゴリ」セクションと比較して「味気なく」ならないように、そのセクションにも同じカラフルなアイコンを付けたいと考えています。

これは可能でしょうか?

「いいね!」 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) => {
  // セクション名を ID にマッピング
  const sectionIds = {
    "community": 1,
    "tiago": 2,
    "personal-section": 3,
    // ここにさらにセクションを追加
  };
  
  // [sectionId, itemName] から絵文字名へのマッピング
  const iconReplacements = {
    // コミュニティセクション (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 セクション (ID: 2)
    "2,Journal": "notebook_with_decorative_cover",
    "2,Music": "musical_note",
    "2,About": "bust_in_silhouette",
    
    // 個人セクション (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(',');
        
        // 現在のセクションの置換のみを処理
        if (parseInt(keyId) === sectionId) {
          const url = emojiUrlFor(emojiName);
          
          // ハンバーガーメニューと固定サイドバーの両方をキャッチするために複数のセレクターを試す
          const selectors = [
            // 固定サイドバーセレクター
            `div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // ハンバーガーメニューセレクター (より具体的)
            `.menu-panel div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
            // 一般的なフォールバック
            `[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) {
              // すでに置換されているかどうかを確認して、不要な DOM 操作を避ける
              if (!prefix.querySelector('.prefix-emoji')) {
                prefix.innerHTML = `
                  <img src="${url}"
                       title="${emojiName}"
                       alt="${emojiName}"
                       class="emoji prefix-emoji">
                `;
              }
              break; // アイコンを見つけて置換したらループを抜ける
            }
          }
        }
      });
    });
  }

  // ページ変更時に実行
  api.onPageChange(replaceIcons);
  
  // 動的に読み込まれるコンテンツをキャッチするために遅延実行も行う
  api.onPageChange(() => {
    setTimeout(replaceIcons, 100);
    setTimeout(replaceIcons, 500);
  });

  // サイドバーの変更をキャッチするための拡張 MutationObserver
  const observer = new MutationObserver((mutations) => {
    let shouldReplace = false;
    
    mutations.forEach((mutation) => {
      // 追加されたノードを監視 (元の機能)
      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;
          }
        }
      });
      
      // サイドバーセクションの属性変更を監視 (折りたたみ/展開)
      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;
        }
      }
      
      // サイドバーセクションの childList 変更を監視
      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);
    }
  });

  // 拡張オプションで監視を開始
  observer.observe(document.body, {
    childList: true,
    subtree: true,
    attributes: true, // 属性変更を監視
    attributeFilter: ['class', 'style', 'aria-expanded'], // 折りたたみ/展開時に変更される一般的な属性
  });

  // 一般的なサイドバーインタラクションのための追加イベントリスナー
  document.addEventListener('click', (event) => {
    // クリックがサイドバーセクションヘッダーまたはトグルボタンであったかどうかを確認
    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 つあっても、それぞれに異なる絵文字を割り当てたい場合があるからです。

これにはコンポーネントやプラグインがあると言う人がいることは確かですが、ChatGPT や Claude とやり取りして、すべてが希望どおりに機能するようになるのは楽しかったです。

他のユーザーの役に立つことを願っています。

調整/改善できる点があれば、ぜひ共有してください :raising_hands:

「いいね!」 3

JSコードを更新しました。モバイルではデフォルトのアイコンが表示されたままでした。モバイルとデスクトップの両方で期待どおりに動作しています。

別の編集:セクションを折りたたんで展開すると、絵文字が消えて元のアイコンに置き換わっていました。今は動作しており、JavaScriptコードを再度更新しました。

これを実現する方法を見つけられて嬉しいです。カテゴリの絵文字をサポートするようになった今、これはコア機能として非常に自然に適合すると思います。

「いいね!」 3

同意します。最終的にはネイティブ機能になることを願っています。それまでは、このカスタムコンポーネントがその役割を果たしています :slight_smile:

「いいね!」 2

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