复用 Discourse 汉堡菜单功能

Hello Discourse Team!

I am using the discourse CLI Discourse Theme CLI (console app to help you build themes) and the plugin API A new versioned API for client side plugins to help me create my theme.

I have added a custom header in the header.html file. On mobile, I was hoping to create a hamburger menu that functions like discourse’s hamburger


But has completely different menu contents.

I could create the hamburger and menu with my knowledge of css/html/js. But I was hoping for a way to use the functionality that discourse has already implemented (opening, closing, etc…).

Any suggestions on how to achieve this?

  • I thought maybe I would be able to extend the widget for the current navigation, change the settings, then attach it to the html I have written for my nav. But the connectors seemed to be predefined, and I wasnt sure how to connect my widget to my html. This might not be the right direction at all either…
2 个赞

Okay, so I think I have a better understanding on what I am trying to do.

I would like to create a connector where I can connect to one of the elements that I have added to the page.

I’m guessing this isn’t possible???

The plugin API (https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/lib/plugin-api.js.es6#L11) has api.registerConnectorClass but I cannot seem to figure out how to connect this to my custom element.

The Brand Header might give you some ideas about how to approach this. You can find its code here: GitHub - discourse/discourse-brand-header: Brand header theme component for Discourse. It only displays a hamburger menu in mobile mode. You can get the mobile view on a desktop by appending
?mobile_view=1 to your forum’s URL.

10 个赞

This is slightly rough but should get you 99% of the way there when it comes to having an additional menu item with a dropdown panel. Calling this one a pizza menu :pizza:. Add this to your header.html file.

<script type="text/discourse-plugin" version="0.8">
        
api.createWidget('pizza-menu', {
  tagName: 'div.pizza-panel',

  panelContents() {
    return "hello world";
  },

  html() {
    return this.attach('menu-panel', {
      contents: () => this.panelContents()
    });
  },

  clickOutside() {
    this.sendWidgetAction('togglePizza');
  }
});
    
api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  let contents = [];
    contents.push(helper.attach('header-dropdown', {
      title: 'pizza-menu',
      icon: 'cutlery',
      active: headerState.pizzaVisible,
      iconId: 'toggle-pizza-menu',
      action: 'togglePizza',
    }));
    if (headerState.pizzaVisible) {
            contents.push(helper.attach('pizza-menu'));
    }
    return contents;
});

api.attachWidgetAction('header', 'togglePizza', function() {
  this.state.pizzaVisible = !this.state.pizzaVisible;
});

</script>
        

20 个赞

Yes! This is very close!

My only concern was the connect part:

What if I wanted to add the pizza menu to a custom HTML element (defined in common/head_tag.html)?

Ah ok in that case you’re looking at something more like what @simon mentioned in the brand header theme component, I was attaching the action to the existing header widget… the brand header adds it to a new one. The new widget here w/ toggleHamburger:

 api.createWidget('brand-header', {
    tagName: 'header.b-header.clearfix',
    buildKey: () => `header`,
  
    defaultState() {
      let states =  {
        hamburgerVisible: false
      };
  
      return states;
    },
  
    toggleHamburger() {
      this.state.hamburgerVisible = !this.state.hamburgerVisible;
    },

and then the action is added to an HTML element in another widget here

api.createWidget('brand-header-icons', {
    tagName: 'ul.icons.clearfix',
  
    buildAttributes() {
      return { role: 'navigation' };
    },
  
    html(attrs) {
      const hamburger = this.attach('header-dropdown', {
                              title: 'hamburger_brand_menu',
                              icon: 'bars',
                              iconId: 'toggle-hamburger-brand-menu',
                              active: attrs.hamburgerVisible,
                              action: 'toggleHamburger'
                            });
      const icons = [hamburger];
      return icons;
    },
  });
8 个赞

Thank you for this code. Can you point me in the right direction on filling out the panelContents? I see some indication, but not sure how it all links together.

你好!我知道这是一个旧话题,但这几乎就是我正在寻找或尝试做的事情。因此我在此发帖(而不是创建新话题)。请理解,我并非开发者,不了解插件的工作原理,也不会编写 JavaScript,但我懂得如何复制粘贴代码。:wink:

我试图在下拉菜单中添加一个聊天室的登录功能。也就是说,我想要一个“聊天”图标,下拉菜单中包含一个输入框、一个提交按钮,以及一些帮助文本或指向帮助页面的链接。

我能否修改您的代码,将“餐具”图标替换为“聊天”图标?具体该如何操作?
其次,我该如何插入 HTML 代码,而不是显示“你好,世界”这句话?(基本上就是如何在那里输入 HTML?)

非常感谢您的帮助!

1 个赞

关于 Queth 的帖子,是否可以在下拉面板中 FAQ 之前插入一个额外的链接?

这个组件应该可行。只需将自定义链接移到 FAQ 之前即可。
https://meta.discourse.org/t/custom-hamburger-menu-links/87644/41

2 个赞

哈哈,真巧,我也在看完全相同的内容。我认为最好的方法是重新打开汉堡菜单组件并覆盖 html 方法。

我会在 FAQ 项目之前 unshift 我的组件。我还没测试是否可行,但这将是我的思路 :smiley:
看起来在这里是有效的

  api.reopenWidget("hamburger-menu", {

      html() {
        let conti = this.panelContents()
        conti.unshift(h('div',[h('span','描述:'),this.attach('widget-dropdown',
  {id: "from", translatedLabel: "bla",onChange: "changeaction",
  content: [
      { id: 1, label: "foo.bar" },
      { id: 2, translatedLabel: "FooBar" },
      { id: 3, label: "foo.baz", icon: "times" },
      { id: 4, html: "<b>foo</b>" },
    ],
    options: {bodyClass: "lang-drop-body"}
  })]))
        return this.attach("menu-panel", {
          contents: () => conti,
          maxWidth: this.settings.maxWidth
        });
      },
  });

例如,我添加了一个下拉菜单。
干杯

4 个赞

你好,我该如何将其放在搜索图标右侧?又该如何在 panelContents 中添加自定义 HTML?

1 个赞