Возможность добавить пользовательский раздел ссылок в боковую панель

Мы хотим предоставить пользователям возможность добавлять настраиваемый раздел ссылок в боковую панель.

На первом этапе реализации этой функции мы планируем доработать существующую страницу настроек боковой панели и позволить пользователю добавить один настраиваемый раздел для таких ссылок.

Мы считаем, что это может работать следующим образом:

  1. Флажок «Показать настраиваемый раздел».
    Если флажок установлен, Настраиваемый раздел отображается выше Раздела категорий, и для сохранения изменений необходимо указать валидное имя и хотя бы одну валидную ссылку.
  2. Поле ввода для имени настраиваемого раздела (по умолчанию: «Мои ссылки», не может быть пустым).
  3. Кнопка «Добавить ссылку». При нажатии открывается диалоговое окно для добавления ссылки.
  4. Добавленные ссылки отображаются с кнопкой для их удаления.

Диалоговое окно «Добавить ссылку»:

  1. Содержит два текстовых поля: «Имя» и «URL».
  2. Имя не может быть пустым, может содержать эмодзи (которые должны отображаться в боковой панели).
  3. URL должен вести на страницу внутри того же сайта Discourse*.
  4. Кнопка «Сохранить» добавляет ссылку, «Отмена» отменяет действие.

При наличии настроенного и сохранённого настраиваемого раздела он будет отображаться в боковой панели выше раздела «Категории».

* Почему?

  1. Мы хотим иметь возможность «подсвечивать»/выделять жирным шрифтом ссылку в боковой панели, когда пользователь находится на соответствующей странице.
  2. Мы хотим хранить часть «путь», чтобы переименования сайта работали корректно.

Мы считаем, что добавление ссылок уже сейчас будет полезно, так как это позволит пользователям легко получать доступ к таким ресурсам, как «Открыть баги боковой панели», а также даст возможность использовать будущие улучшения списков тем, например, улучшенные фильтры. Пока у элемента есть URL, его можно добавить в боковую панель.

Мы понимаем, что пользователи захотят добавлять несколько разделов и изменять их порядок, но на начальном этапе это останется за пределами текущей задачи.

16 лайков

Один вопрос по этому поводу, @mcwumbly. Похоже, это стратегия, позволяющая пользователю добавлять личные ссылки в боковую панель. Позволит ли такой подход владельцам сайтов добавлять ссылки, которые будут видеть все пользователи?

Спойлер — я думаю, что да. :slight_smile:

5 лайков

Да, я думаю, что это определённо можно расширить для реализации чего-то подобного, и я понимаю, что для некоторых сайтов это была бы более полезная функция, чем та, что ориентирована на пользователей.

8 лайков

Мне очень нравится эта идея. :+1:

Всего несколько вопросов:

  1. Будет ли ограничение на количество пользовательских ссылок, которые может добавить пользователь?

  2. Будет ли текст ссылок (в боковой панели) обрезан, как на следующем макете? (Меня это устраивает).
    И если да, сможет ли пользователь прочитать их текст полностью во всплывающей подсказке (при наведении курсора мыши на ссылки)?

Отличный вопрос. Нам, вероятно, нужно установить некоторые ограничения, но мы пока не обсуждали это подробно. Какие ограничения вы хотели бы видеть (или избежать)?

1 лайк

Сначала я думал, что 10 ссылок будет достаточно, но, похоже, другим участникам форума понадобится больше. Может быть, 20. Поэтому я попробую с 20, подожду отзывов пользователей и посмотрю, будут ли они просить больше (но мне кажется, что 20 — уже хорошее число).

1 лайк

Чтобы сделать эту функцию еще более удобной для пользователя, было бы здорово добавить возможность перетаскивания ссылок (любой ссылки с текущей страницы Discourse) прямо в боковую панель.

Указатель (между ссылками, которые ранее были добавлены в блок “Мои ссылки”), показывал бы, куда именно будет размещена ссылка.

Макет (щелкните правой кнопкой мыши > “открыть изображение в новой вкладке”, чтобы увидеть анимацию в полном размере):

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="Tous les sujets" 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"/>
      `

  $(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;
          // Получаем 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, поэтому приношу извинения за любой неоптимальный или некорректный код. По крайней мере, на моём сайте он, похоже, работает как задумано.

Просто вставьте этот код во вкладку «Заголовок» компонента темы и при необходимости настройте:

<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>
5 лайков

@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-раскладки для возможности изменения порядка элементов */
  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 часа. Новые ответы больше не принимаются.