サイドバーにリンクのカスタムセクションを追加する機能

サイドバーにカスタムリンクセクションをユーザーが追加できるようにしたいと考えています。

この機能の最初の段階として、既存のサイドバー設定ページを拡張し、ユーザーがこれらのリンク用のカスタムセクションを1つ追加できるようにする予定です。

以下のような動作になるかと考えています。

  1. 「カスタムセクションを表示」のチェックボックス。
    チェックすると、「カテゴリセクション」の上に「カスタムセクション」が表示され、「変更を保存」するには有効な名前と少なくとも1つの有効なリンクが必要です。
  2. カスタムセクションの名前を入力するテキストボックス(デフォルト:「マイリンク」、空白不可)
  3. 「リンクを追加」ボタン。クリックすると、リンクを追加するためのダイアログが表示されます。
  4. 追加されたリンクには削除ボタンが表示されます。

リンク追加ダイアログ

  1. リンク追加ダイアログには、「名前」と「URL」の2つのテキストボックスがあります。
  2. 名前は空白にできず、絵文字(サイドバーでレンダリングされるべき)を含めることができます。
  3. URLは、同じDiscourseサイト内のリンクである必要があります*
  4. 保存するとリンクが追加され、キャンセルすると破棄されます。

有効なカスタムセクションが設定され保存されると、サイドバーには「カテゴリ」の上にそのセクションが表示されます。

* なぜですか?

  1. 対応するページにいるときに、サイドバーのリンクを「強調表示」/「太字」にできるようにしたいと考えています。
  2. サイト名の変更が期待どおりに機能するように、「パス」部分を保存したいと考えています。

現時点でリンクを追加することは、「サイドバーのバグをオープン」のようなものに簡単にアクセスできるようになり、トピックリストの改善されたトピックリストフィルターのような将来の改善を活用できるようになるため、価値があると考えています。URLがあれば、人々はそれをサイドバーに追加できます。

複数のセクションを追加したり並べ替えたりできるようにしたいと考えていますが、初期段階ではスコープ外とします。

「いいね!」 16

これについて1つ質問があります、@mcwumbly。これは、ユーザーがサイドバーに個人的なリンクを追加できるようにするための戦略のように見えます。このアプローチにより、サイトオーナーがすべてのユーザーが表示するリンクを追加できるようになりますか?

ネタバレ - そうなるべきだと思います。 :slight_smile:

「いいね!」 5

はい、確かにそのような機能に拡張できると思います。また、ユーザー機能よりも一部のサイトにとってより便利な機能になることも理解しています。

「いいね!」 8

このアイデアは本当に気に入りました。:+1:

いくつか質問があります。

  1. ユーザーが追加できるカスタムリンクの数に制限はありますか?

  2. リンクの末尾(サイドバー内)は、次のモックアップのように切り捨てられますか?(私はそれで構いません)。
    もしそうなら、ユーザーはツールチップでリンクのテキストをフルレングスで読むことができますか?(マウスカーソルがリンクにホバーした場合)

良い質問ですね。おそらく何らかの制限を設ける必要があるでしょうが、まだ詳細については議論していません。どのような制限を期待しますか(または避けたいですか)?

「いいね!」 1

最初は10個のリンクで十分だと思っていましたが、フォーラムの他のメンバーはもっと欲しがるかもしれません。おそらく20個でしょう。そのため、20個で試してみて、ユーザーからのフィードバックを待って、さらに要求があるか確認します(ただし、20個でもすでに良い数だと思います)。

「いいね!」 1

この機能の「使いやすさ」をさらに向上させるために、ユーザーが(現在のDiscourseページの任意のリンクを)サイドバーに直接ドラッグアンドドロップできるようにすると、非常に便利だと思います。

以前に「マイリンク」DIVに追加されたリンクの間にあるポインターが、リンクが配置される場所を示します。

モックアップ(アニメーションをフルサイズで表示するには、右クリックして「新しいタブで画像を開く」を選択してください):

anim01

ドラッグアンドドロップの実装には、以下のページが役立つかもしれません。

「いいね!」 7

サイトのデフォルトリンクを設定し、ユーザーがそれを解除できるようにしたいです。そうすれば、現在使用しているカスタムヘッダーリンクよりも確実に良くなると思います。

「いいね!」 3

カスタムリンクの追加に賛成です。元のドロップダウンメニューにいくつかのカスタムリンクを追加していました。Discourse自体が「More」オプションの下に「Birthdays」や「Docs」を持っているのと同様に、それらを元に戻せると良いでしょう。

「いいね!」 4

皆さん、こんにちは!

新しいサイドバーにリンクを追加する方法について質問があります。

この辺りにはコネクタがないようです…

これを追加する最も効果的な方法はどのようなものでしょうか?


編集

私の質問はここに移動されました… なぜかはわかりませんが、私は本当に同じことを尋ねています。

いずれにせよ、これを解決するために行ったことは次のとおりです。これはきれいな解決策ではありません。Discourseのテンプレートシステムと統合する方法があれば、はるかに良いでしょう。

どうぞ:

<script>
    let logo = "" // SVGパスは不要なので削除しました。
    const div = document.createElement('div') // 追加するリンクを作成します
    div.className = 'sidebar-section-link-wrapper' // 関連するクラスを追加します
    div.innerHTML = `
          <a title="すべてのトピック" href="https://www.latranchee.com" id="ember12" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
            <span class="sidebar-section-link-prefix icon">
              <svg viewBox="0 0 100 100" class="logoIcon" xmlns="http://www.w3.org/2000/svg">${logo}</svg>
            </span>
            <span class="sidebar-section-link-content-text"> ホーム </span>
          </a>
    ` // リンクを埋め込みます

    $( document ).ready(() => { // Emberが処理を完了するのを待つために必要です
        // デスクトップでナビゲーションを追加します
        let desktop = document.getElementsByClassName('sidebar-section-content')[0];
        if(desktop) desktop.prepend(div)

        // モバイルでナビゲーションを追加します
        let hamburger = document.getElementById('toggle-hamburger-menu').addEventListener("click", addMobileNav);
        function addMobileNav () {
            setTimeout(function(){ // ナビゲーションがロードされるのを待つように強制します
                document.getElementsByClassName('sidebar-section-content')[0].prepend(div);
            }, 0);
        }
    })
</script>

デスクトップでの結果…

そしてモバイルでの結果:

もっと良い方法ができるまで、これで我慢するしかありません!

編集 #2

ナビゲーションがオブジェクトの配列からロードされるようにコードを整理しました。

<script>
  let rss = `<path d="M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M7.5,15A1.5,1.5 0 0,0 6,16.5A1.5,1.5 0 0,0 7.5,18A1.5,1.5 0 0,0 9,16.5A1.5,1.5 0 0,0 7.5,15M6,10V12A6,6 0 0,1 12,18H14A8,8 0 0,0 6,10M6,6V8A10,10 0 0,1 16,18H18A12,12 0 0,0 6,6Z"></path>`
  let mdiSchool = `<path d="M12,3L1,9L12,15L21,10.09V17H23V9M5,13.18V17.18L12,21L19,17.18V13.18L12,17L5,13.18Z"></path>`
  let logo = `<g xmlns="http://www.w3.org/2000/svg" fill="#0e1e2b">
    <path d="M77.4 196.2 c-8 -6.9 -11.4 -16.9 -11.4 -33.2 0.1 -20.9 2.4 -30.2 6.7 -27.4 2.9 1.8 4.3 9.4 5.4 28.9 0.9 16.5 1.4 19.8 3.6 25 1.3 3.3 2.5 6.7 2.6 7.5 0.3 2.6 -3.6 2.1 -6.9 -0.8z"/>
    <path d="M2.9 167.3 c-5.1 -6.1 11.2 -24.2 21.9 -24.3 2.6 0 2.7 1.1 0.6 5.5 -1.4 3 -12.6 13.5 -14.3 13.5 -0.4 0 -1.4 1.2 -2.1 2.6 -2 3.6 -4.5 4.8 -6.1 2.7z"/>
    <path d="M87.4 160.5 c-1.9 -1.9 -2.4 -3.4 -2.2 -5.8 0.4 -4.5 3.2 -4.7 7.4 -0.6 3.9 3.8 4.1 4.9 1.6 7.2 -2.5 2.3 -3.9 2.1 -6.8 -0.8z"/>
    <path d="M126.5 158.9 c-6 -3 -10 -7.4 -17 -18.6 -7.6 -12.2 -8.2 -13.8 -5.8 -15.2 2.8 -1.5 7.3 1.8 21.3 15.4 6.9 6.8 13.5 12.6 14.7 12.9 2.7 0.8 3.9 4.2 2.3 6.1 -1.8 2.2 -10.7 1.8 -15.5 -0.6z"/>
    <path d="M36.2 156.7 c-1.5 -1.8 -1.8 -7.8 -0.4 -10.3 1.9 -3.6 3.8 -4.7 6.1 -3.4 1.7 0.9 2.1 2 2.1 6.1 0 2.8 -0.5 5.9 -1 7 -1.2 2.2 -5.2 2.5 -6.8 0.6z"/>
    <path d="M121.5 116 c-0.8 -2.5 1.1 -5.1 3 -4.4 2 0.8 2.7 3.4 1.4 5 -1.7 2 -3.7 1.7 -4.4 -0.6z"/>
    <path d="M4.3 113 c-2.6 -1.1 -2.8 -1.9 -1 -4.4 1.5 -2 7.5 -2.9 9.5 -1.4 1.8 1.5 1.5 4.6 -0.7 5.8 -2.3 1.2 -4.8 1.2 -7.8 0z"/>
    <path d="M32.7 82 c-6.3 -4 -10.3 -9.9 -13.2 -19.4 -2.5 -8.8 -4 -24 -2.7 -29.5 0.9 -4.2 3.4 -6.1 6 -4.5 1.7 1.1 1.9 2 4.6 17.9 1.8 10.8 7.3 22.5 14.1 30.3 2.8 3.1 4.5 5.8 4.1 6.7 -1 2.6 -7.7 1.8 -12.9 -1.5z"/>
    <path d="M133.6 80.4 c-2.1 -2.1 -2 -2.9 0.6 -5.4 2.8 -2.6 6.6 -3.6 8.6 -2.3 2.5 1.5 2.4 2.9 -0.3 6.2 -2.9 3.4 -6.4 4 -8.9 1.5z"/>
    <path d="M93.3 73.3 c-2 -0.8 -1.6 -2.8 2.6 -11.1 7.1 -14.1 10.8 -19.4 22.7 -31.9 9.8 -10.5 12.1 -12.4 14.3 -12.1 1.5 0.2 2.7 1 2.9 2 0.6 3.2 -18 29.9 -29.4 42.1 -6 6.5 -11.1 11.7 -11.4 11.6 -0.3 0 -1.1 -0.3 -1.7 -0.6z"/>
    <path d="M61.7 56.2 c-2.7 -3 -3.3 -7.9 -1.2 -10.2 1 -1.1 2.4 -2 3.1 -2 2.1 0 4.4 4.2 4.4 8 0 5.6 -3.1 7.7 -6.3 4.2z"/>
    <path d="M96.2 18.8 c-4.2 -4.2 4 -19.3 8.8 -16.3 1.8 1.1 1 6.9 -1.7 12.2 -2.6 5.3 -4.7 6.5 -7.1 4.1z"/>
    </g>`

  const div = document.createElement("div")
  div.className = "sidebar-section-link-wrapper"
  div.innerHTML = `
            <a href="https://www.latranchee.com" class="sidebar-section-link sidebar-section-link-everything sidebar-row">
              <span class="sidebar-section-link-prefix icon">
                <svg viewBox="0 0 100 100" class="logoIcon" xmlns="http://www.w3.org/2000/svg">${logo}</svg>
              </span>
              <span class="sidebar-section-link-content-text"> ホーム </span>
            </a>
      `

  const customHeader = document.createElement("div")
  customHeader.className = "sidebar-section-wrapper sidebar-section-community"
  customHeader.innerHTML = `
            <div class="sidebar-section-header-wrapper sidebar-row">
              <button id="ember11" class="sidebar-section-header sidebar-section-header-collapsable btn-flat btn no-text" type="button">
                <span class="sidebar-section-header-text"> トレーニングキャンプ </span>
              </button>
          </div>
          <div class="sidebar-section-content" id="customNavigation"></div>
      `

  $(document).ready(function () {
    // リンクを作成します
    const links = [
      { title: "ホーム", src: "https://www.latranchee.com", svg: logo, viewbox: "0 0 100 100" },
      { title: "トレーニング", src: "https://www.latranchee.com/formations", svg: mdiSchool, viewbox: "2 -2 16 16" },
      { title: "ブログ", src: "https://www.latranchee.com/blogue", viewbox: "1 -3 16 16", svg: rss },
    ]

    // モバイル
    let hamburger = document.getElementById("toggle-hamburger-menu")
    if (hamburger) {
      hamburger.addEventListener("click", addCustomLinks)
    } else {
      addCustomLinks()
    }

    let bool = false;
    function addCustomLinks() {
      setTimeout(function () {
        // ナビゲーションがロードされるのを待つように強制します
        const sidebar = document.getElementsByClassName("sidebar-sections")[0]
        if (sidebar) {
          sidebar.prepend(customHeader)
          if (bool) return;
          // customNav IDを取得します
          const customNavigation = document.getElementById("customNavigation")
          if (customNavigation) {
            links.filter(function (link) {
              let linkDiv = document.createElement("div")
              linkDiv.className = "sidebar-section-link-wrapper"
              linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                        <span class="sidebar-section-link-prefix icon" id="link_${link.title}"></span>
                        <span class="sidebar-section-link-content-text"> ${link.title} </span>
                    </a>
                  `
              customNavigation.append(linkDiv)
              let linkIcon = document.getElementById("link_" + link.title)
              if (linkIcon && link.svg) {
                linkIcon.innerHTML = `<svg viewBox="${link.viewbox}" class="logoIcon" xmlns="http://www.w3.org/2000/svg"> ${link.svg}</svg>`
              }
            })
          }
        }
        bool = true
      }, 0)

    }
  })
</script>

誰かの役に立つことを願っています!

「いいね!」 2

こちらからも賛成です。サイドバーのカスタマイズ性、例えばカスタムリンクや、さらに多くのボタンをトップに追加できれば、さらに良くなるでしょう!

これを検討していただきありがとうございます。フォーラムのユーザーベースにとって、これはかなり重要な機能だと思います。ユーザーは、少しでも隠された情報を見つけるのが苦手なため、「管理者に連絡」や「フォーラムのルール」などの目立つリンクを追加する必要があります。コミュニティの見出しのすぐ下に配置しても構いませんが、_その他_メニューの下では絶対に見つけられません。また、内部リンクと外部リンクの両方に対応できる柔軟性も重要です。外部リンクは、カスタムハンバーガーメニューでは現在壊れています。

「いいね!」 1

これは、上記の例に大きく基づいた、誰かに役立つと思われる別の例です。このコードはまったく新しいセクションを追加するのではなく、コミュニティセクションの「その他」パネルの下部(フッターのFAQと「会社概要」リンクの前)に追加のリンクを追加します。FontAwesomeアイコン(サイト設定で追加されていると仮定)と外部リンクをサポートします。サイドバーが閉じたり開いたり、コミュニティセクションが折りたたまれたり展開されたりするエッジケースを処理します。デスクトップとモバイルで動作します。

私はJavaScriptの専門家ではないので、悪いコードや最適でないコードについてはお詫びします。少なくとも私のサイトでは、意図したとおりに動作しているようです。

このコードをテーマコンポーネントのヘッダータブに貼り付けて、必要に応じてカスタマイズしてください。

$(document).ready(function () {
    if (document.getElementById("toggle-hamburger-menu")) {
        // We're in mobile view
        addToggleListener(document.getElementById("toggle-hamburger-menu"))
    } else {
        // We're in desktop view
        addToggleListener(document.getElementsByClassName("btn-sidebar-toggle")[0])
        addHeaderListener()
        addMoreListener()
    }

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // Wait a bit for the sidebar to load
                setTimeout(function() {
                    let sidebar = document.getElementsByClassName("sidebar-section-header").length
                    if (sidebar) {
                        addHeaderListener()
                        addMoreListener()
                    }
                }, 100)
            })
        }
    }

    function addHeaderListener() {
        let communityHeader = document.getElementsByClassName("sidebar-section-header")[0]
        if (communityHeader) {
            communityHeader.addEventListener("click", function () {
                // Wait a bit for the section to expand
                setTimeout(function() {
                    let communitySection = document.getElementById("sidebar-section-content-community")
                    if (communitySection) {
                        addMoreListener()
                    }
                }, 100)
            })
        }
    }
    
    function addMoreListener() {
        let buttonMore = document.getElementsByClassName("sidebar-more-section-links-details")[0]
        if (buttonMore) {
            buttonMore.addEventListener("click", addCustomLinks)
        }
    }
    
    function addCustomLinks() {
        // Wait a bit until navigation has been loaded
        setTimeout(function () {
            const parentEl = document.getElementsByClassName("sidebar-more-section-links-details-content-main")[0]
            let linksAlreadyAdded = document.getElementsByClassName("sidebar-section-custom-link").length
        
            if (parentEl && !linksAlreadyAdded) {
                links.filter(function (link) {
                    let linkDiv = document.createElement("li")
                    let linkTitleTrim = link.title.replace(/\s+/g, '')
                    linkDiv.className = "sidebar-section-link-wrapper sidebar-section-custom-link"
                    linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                            <span class="sidebar-section-link-prefix icon" id="link_${linkTitleTrim}"></span>
                            <span class="sidebar-section-link-content-text"> ${link.title} </span>
                        </a>
                      `
                    parentEl.append(linkDiv)
                    
                    let linkIcon = document.getElementById("link_" + linkTitleTrim)
                    if (linkIcon && link.icon) {
                        linkIcon.innerHTML = `<svg viewBox="0 0 640 512" class="fa d-icon svg-icon prefix-icon svg-string d-icon-${link.icon}" xmlns="http://www.w3.org/2000/svg">
                                <use xlink:href="#${link.icon}"></use>
                            </svg>
                        `
                    }
                })
            }
        }, 100)
    }
})

const links = [
    // FontAwesome icons may need to be added in the site settings if they don't correctly appear
    { title: "My Account", src: "/my/billing/subscriptions", icon: "file-invoice-dollar" },
    { title: "User Directory", src: "/u?asc=true&cards=yes&order=username&period=all", icon: "address-book" },
    { title: "Docs", src: "/docs", icon: "book-reader" },
    { title: "External Site", src: "https://google.com/", icon: "globe" }
]
「いいね!」 4

@Ryan_Hyer 素晴らしいですね!ハンバーガーメニューのトグルイベント後にアイテムを表示させる(または表示し続ける)方法を見つけられたのですね。これは私がここで直面していた問題でした。

コードも非常に整理されていてきれいです。おかげで、「詳細」の下に隠れることなく、コミュニティメニューに表示したいものを表示させるように改造することができました。

ヘッダー:

<script>

const links = [
    // FontAwesome アイコンは、正しく表示されない場合はサイト設定に追加する必要があるかもしれません
    { title: "User Directory", src: "/u?asc=true&cards=yes&order=username&period=all", icon: "address-book" },
    { title: "Docs", src: "/docs", icon: "book-reader" },
    { title: "External Site", src: "https://google.com/", icon: "globe" }
]

$(document).ready(function () {
    if (document.getElementById("toggle-hamburger-menu")) {
        // モバイルビューです
        addToggleListener(document.getElementById("toggle-hamburger-menu"))
    } else {
        // デスクトップビューです
        addToggleListener(document.getElementsByClassName("btn-sidebar-toggle")[0])
        addCustomLinks()
    }

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // サイドバーがロードされるまで少し待ちます
                setTimeout(function() {
                    let sidebar = document.getElementsByClassName("sidebar-section-header").length
                    if (sidebar) {
                        addCustomLinks()
                    }
                }, 100)
            })
        }
    }
    
    function addCustomLinks() {
        // ナビゲーションがロードされるまで少し待ちます
        setTimeout(function () {
            const parentEl = document.getElementsByClassName("sidebar-section-content")[0]
            let linksAlreadyAdded = document.getElementsByClassName("sidebar-section-custom-link").length
        
            if (parentEl && !linksAlreadyAdded) {
                links.filter(function (link) {
                    let linkDiv = document.createElement("li")
                    let linkTitleTrim = link.title.replace(/\s+/g, '')
                    linkDiv.className = "sidebar-section-link-wrapper sidebar-section-custom-link"
                    linkDiv.innerHTML = `<a href="${link.src}" class="sidebar-section-link sidebar-section-link-everything sidebar-row ember-view">
                            <span class="sidebar-section-link-prefix icon" id="link_${linkTitleTrim}"></span>
                            <span class="sidebar-section-link-content-text"> ${link.title} </span>
                        </a>
                      `
                    parentEl.append(linkDiv)
                    
                    let linkIcon = document.getElementById("link_" + linkTitleTrim)
                    if (linkIcon && link.icon) {
                        linkIcon.innerHTML = `<svg viewBox="0 0 640 512" class="fa d-icon svg-icon prefix-icon svg-string d-icon-${link.icon}" xmlns="http://www.w3.org/2000/svg">
                                <use xlink:href="#${link.icon}"></use>
                            </svg>
                        `
                    }
                })
            }
        }, 100)
    }
})
</script>

そして、補完的なCSS:

.sidebar-section-content {
  display: flex; /* 要素を並べ替えるためにフレックスレイアウトを設定します */
  flex-direction: column;
  .sidebar-more-section-links-details {
    order: +1;
  }
}

.sidebar-wrapper li a.sidebar-section-link-about {
    display: none;
}

.sidebar-wrapper li a.sidebar-section-link-faq {
    display: none;
}

.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-about {
    display: none;
}

.sidebar-more-section-links-details-content-secondary .sidebar-section-link.sidebar-section-link-faq {
    display: none;
}
「いいね!」 2

ちょうど公開されたこのトピックに注目していただきたく、お知らせします。

フィードバックはそちらのトピックにお寄せください!

「いいね!」 6

このトピックは42時間後に自動的に閉じられました。返信はもうできません。