vaadin / web-components

A set of high-quality standards based web components for enterprise web applications. Part of Vaadin 20+
https://vaadin.com/docs/latest/components
467 stars 83 forks source link

[menu-bar] There is no HTML API for adding items #925

Open heruan opened 5 years ago

heruan commented 5 years ago

As an HTML developer, I would expect to add items to the menu-bar as simple as:

<vaadin-menu-bar>
  <vaadin-menu-bar-item>Simple item</vaadin-menu-bar-item>
  <vaadin-menu-bar-item>
    <iron-icon icon="lumo-clock"></iron-icon>
    <span>Item with icon</span>
  </vaadin-menu-bar-item>
  <vaadin-menu-bar-item>
    <span>Item with submenu</span>
    <vaadin-menu-bar-submenu slot="submenu">
      <vaadin-menu-bar-item>Submenu item</vaadin-menu-bar-item>
    </vaadin-menu-bar-submenu>
  </vaadin-menu-bar-item>
</vaadin-menu-bar>

Instead, I need to use JavaScript, hooking to an event, query for the element, use strings for components or define them with functions:

customElements.whenDefined('vaadin-menu-bar').then(function() {
  const menu = document.querySelector('vaadin-menu-bar');
  menu.items = [
    {component: 'b', text: 'Home'},
    {
      component: makeIcon('vaadin:user', 'Profile'),
      children: [
        {text: 'Edit Profile'},
        {component: 'hr'},
        {text: 'Privacy Settings'},
        {text: 'Terms of Service'}
      ]
    },
    {
      component: makeIcon('lumo:bell'),
      children: [{text: 'Notifications'}, {text: 'Mark as read'}]
    }
  ];
  function makeIcon(img, txt) {
    const item = window.document.createElement('vaadin-context-menu-item');
    const icon = window.document.createElement('iron-icon');
    icon.setAttribute('icon', img);
    item.appendChild(icon);
    if (txt) {
      item.appendChild(window.document.createTextNode(txt));
    }
    return item;
  }
});

I'm sure there's a rationale behind, but I'd like to know if's final or if there are plans to simplify this šŸ™‚

Haprog commented 5 years ago

I agree it would be very nice to have a way to configure the menu bar declaratively with HTML instead of needing this kind of JS.

I think the main reason why this only has the JS API now is that for components where we provide this kind of HTML API we try to keep it so we will not directly move or modify the HTML declared by the user because it's a bit intrusive and styling of the user provided content can become unintuitive.

So we have the option of using <slot> elements to place the content in shadow DOM, or requiring the use of <template> by the user and then we just clone that template contents into where we need it (still trying not to modify the user provided HTML after cloning I think).

So here the problem is that such a complex nested structure would not be possible to implement simply with <slot> and it would become not nice if you had to define each sub level as a separate <template> due to the fact that vaadin-menu-bar internally uses vaadin-context-menu to render the overlays.

I think the only option to make this happen nicely (with the above constraints) would be to kind of reimplement the behaviour of vaadin-context-menu within menu bar so that user provided nested DOM can be used as is and positioning and displaying of the (sub)menus would be done with CSS only.

Or maybe it would be possible to update vaadin-context-menu so that it can be given this kind of nested menu DOM. I'm not sure.

One option I've been thinking about would be to just use the user provided HTML as a piece of configuration data to generate the items property but not directly display the user provided HTML. The drawbacks would be that the user can't directly style the HTML he's defining and we would need some non-standard way of defining event listeners on the items (like click actions).

I would very much like to see this problem solved but I'm not sure what would technically be the most elegant and least intrusive solution. I think it might be redesigning the API and implementation of vaadin-context-menu but it would need a bit of investigation to check how this would all work together.

heruan commented 5 years ago

Thank you @Haprog for the detailed response! In my opinion, the most elegant and least intrusive solution would be to update vaadin-context-menu so that submenus can be declared with Web Components. I wouldn't consider "intrusive" rewriting a component in the name of FFS šŸ™‚