إعادة استخدام وظيفة قائمة هامبرغر في 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…

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.

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>
        

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;
    },
  });

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. لكنني أعرف كيفية نسخ ولصق الكود؛)

ما أحاول فعله هو إضافة تسجيل الدخول إلى غرفة دردشة داخل قائمة منسدلة. لذا، أريد أيقونة “دردشة”، وفي القائمة المنسدلة ستكون هناك حقل إدخال وزر إرسال، بالإضافة إلى بعض نصوص المساعدة / رابط إلى صفحة المساعدة.

هل يمكنني تكييف الكود الخاص بك، فبدلاً من أيقونة “الأدوات” أضع أيقونة “دردشة”، وكيف أفعل ذلك؟
وثانيًا، كيف يمكنني إدراج كود HTML بدلاً من عبارة “مرحبًا بالعالم”. (بشكل أساسي، كيف أدخل HTML هناك؟)

شكرًا جزيلاً مقدّمًا!

بالنسبة لمنشور Queth، هل من الممكن إدراج رابط إضافي مباشرةً قبل قسم الأسئلة الشائعة (FAQ) في القائمة المنسدلة؟

يمكن أن يعمل هذا المكون. كل ما يتعين فعله هو نقل الرابط المخصص مباشرةً قبل قسم الأسئلة الشائعة (FAQ).
https://meta.discourse.org/t/custom-hamburger-menu-links/87644/41

هاها، بالصدفة أنا أبحث عن نفس الشيء تمامًا. أعتقد أن أفضل طريقة هي إعادة فتح عنصر القائمة الجانبية (hamburger-menu) وتجاوز طريقة html.

سأقوم بإضافة عنصر الـ widget الخاص بي في بداية قائمة الـ FAQ باستخدام دالة unshift. لم أتحقق بعد من عمله، لكن هذه هي خطتي :smiley:
يبدو أنه يعمل هنا

  api.reopenWidget("hamburger-menu", {

      html() {
        let conti = this.panelContents()
        conti.unshift(h('div',[h('span','blabla description: '),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
        });
      },
  });

أضفت قائمة منسدلة كمثال.
تحياتي

مرحبًا، كيف يمكنني وضع هذا مباشرة بعد أيقونة البحث؟ وكيف يمكنني إضافة HTML مخصص داخل panelContents؟