在侧边栏添加自定义链接部分的功能

我们希望允许用户在其侧边栏中添加自定义链接部分。

在此功能的第一个版本中,我们计划在现有的侧边栏偏好设置页面的基础上进行构建,并允许用户为这些链接添加一个自定义部分。

我们认为它的工作方式可能如下:

  1. “显示自定义部分”复选框。
    如果选中,则“自定义部分”显示在“类别部分”上方,并且值必须具有有效的名称和至少一个有效的链接才能“保存更改”。
  2. 自定义部分名称文本框(默认值:“我的链接”,不能为空)。
  3. “添加链接”按钮。单击时,显示添加链接的对话框。
  4. 添加的链接显示一个删除按钮。

添加链接对话框

  1. 添加链接对话框有两个文本框:名称、URL。
  2. 名称不能为空,可以包含表情符号(应在侧边栏中呈现)。
  3. URL 必须是同一 Discourse 站点的链接*。
  4. 保存将添加链接,取消将丢弃它。

当配置并保存了有效的自定义部分后,侧边栏将在“类别”上方显示该部分。

* 为什么?

  1. 我们希望在用户处于相应页面时,“突出显示”/“加粗”侧边栏中的链接。
  2. 我们希望存储“路径”部分,以便站点重命名按预期工作。

我们认为今天添加链接将很有价值,因为它将允许人们轻松访问诸如[“打开侧边栏错误”](Topics tagged sidebar URL,人们就可以将它们添加到他们的侧边栏。

我们认为用户将希望能够添加多个部分并重新排序它们,但我们最初将把这些排除在范围之外。

16 个赞

关于这一点有一个问题,@mcwumbly。这看起来是一种允许用户向侧边栏添加个人链接的策略。这种方法是否允许网站所有者添加所有用户都能看到的链接?

剧透 - 我认为应该是这样。:slight_smile:

5 个赞

是的,我认为肯定可以扩展来实现这样的功能,而且我明白这对于某些网站来说比用户功能更有用。

8 个赞

我真的很喜欢这个主意。:+1:

只是有几个问题:

  1. 用户可以添加的自定义链接数量是否有限制?

  2. 侧边栏中的链接末尾是否会像下面的模型图一样被截断?(我对此没意见)。
    如果会,用户是否可以在工具提示中(当鼠标光标悬停在链接上时)阅读其完整文本?

问得好。我们可能需要一些限制,但这并不是我们详细讨论过的事情。您希望看到(或避免)什么样的限制?

1 个赞

起初,我认为 10 个链接就足够了,但我猜其他论坛成员会想要更多。也许 20 个。因此,我会尝试 20 个,然后等待用户的反馈,看看他们是否要求更多(但我认为 20 个已经是一个不错的数字了)。

1 个赞

为了扩展此功能的“用户友好性”,让用户能够直接将链接(当前 Discourse 页面上的任何链接)拖放到侧边栏中,将会非常棒。

一个指示器(位于先前添加到“我的链接” DIV 中的链接之间)将指示链接将被放置的位置。

模型图 (右键单击 > “在新标签页中打开图片”以全尺寸查看动画):

anim01

也许,以下页面有助于实现拖放功能?

7 个赞

我希望能够为我的网站设置默认链接,用户可以将其关闭。在我看来,这会让它比我现在使用的自定义标题链接好得多。

3 个赞

+1 支持添加自定义链接。我们之前在原始下拉菜单中添加了一些自定义链接。如果能像 Discourse 本身在“更多”选项下添加“生日”和“文档”一样,将它们添加回来就好了。

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"> Accueil </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"> Accueil </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"> Camp d'entraînement </span>
              </button>
          </div>
          <div class="sidebar-section-content" id="customNavigation"></div>
      `

  $(document).ready(function () {
    // 创建链接
    const links = [
      { title: "Accueil", src: "https://www.latranchee.com", svg: logo, viewbox: "0 0 100 100" },
      { title: "Formations", src: "https://www.latranchee.com/formations", svg: mdiSchool, viewbox: "2 -2 16 16" },
      { title: "Blogue", 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。侧边栏能有更多自定义选项,比如自定义链接或者顶部能有更多按钮就更好了!

感谢您考虑这一点,我认为这对我的论坛用户来说是一个非常重要的功能。用户通常很难发现即使是稍微隐藏的信息,我需要添加一些醒目的链接,例如“联系管理员”和“论坛规则”。我不在乎它们是否直接放在“社区”标题下,但它们绝对不会在“更多”菜单下被发现。此外,能够拥有内部和外部链接的灵活性也很重要,外部链接在自定义汉堡菜单中目前是无效的:

1 个赞

这是一个对某人有帮助的另一个示例,很大程度上基于上述示例。此代码不添加一个全新的部分,而是将其他链接添加到“社区”部分中的“更多”面板底部(但在页脚的“常见问题解答”和“关于”链接之前)。它支持 FontAwesome 图标(假设它们已在站点设置中添加)和外部链接。它处理侧边栏关闭和重新打开,以及/或社区部分折叠和重新展开的边缘情况。它在桌面和移动设备上均可运行。

我不是 JavaScript 专家,因此对于任何不佳或非最优的代码表示歉意。至少在我的网站上,它似乎按预期工作。

只需将此代码放入主题组件的“Header”选项卡中,然后根据需要进行自定义:

<script>
const links = [
    // 如果图标未正确显示,可能需要在站点设置中添加 FontAwesome 图标
    { title: "我的账户", src: "/my/billing/subscriptions", icon: "file-invoice-dollar" },
    { title: "用户目录", src: "/u?asc=true&cards=yes&order=username&period=all", icon: "address-book" },
    { title: "文档", src: "/docs", icon: "book-reader" },
    { title: "外部网站", 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])
        addHeaderListener()
        addMoreListener()
    }

    function addToggleListener(toggleEl) {
        if (toggleEl) {
            toggleEl.addEventListener("click", function () {
                // 等待片刻让侧边栏加载
                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 () {
                // 等待片刻让部分展开
                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() {
        // 等待片刻直到导航加载完成
        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)
    }
})
</script>
4 个赞

@Ryan_Hyer 很好!你找到了在汉堡包切换事件后显示(或继续显示)项目的方法,这正是我在这里遇到的问题:

你的代码也非常有序和整洁。多亏了你,我才能把它改造成我想要的样子,显示在“Community”菜单中,而不是隐藏在“More”下面:

Head:

<script>

const links = [
    // 如果字体图标未正确显示,可能需要在站点设置中添加它们
    { 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 小时后自动关闭。不再允许回复。