Move position of vaadin-menu-bar-overlay on opening

Hello everyone,

I am trying to get the vaadin-menu-bar-overlay open in a different position other than the default below the clicked MenuItem. I found this snippet in the vaadin cookbook How do I create vertically oriented MenuBar - Vaadin Cookbook, but this does not work and I can’t figure it out why:

menuBar.getElement().executeJs(“this._subMenu.addEventListener(‘opened-changed’, function(e) {”
+ “const rootMenu = e.target;” //
+ “const button = rootMenu._context.target;” //
+ “if(!button) return;” //
+ “const rect = button.getBoundingClientRect();” //
+ “rootMenu.__x = rect.right - 200;” //
+ “rootMenu.__y = rect.top;” //
+ “rootMenu.__alignOverlayPosition();” //
+ “});”);

I would appreciate any help. Thanks in advance.

You say that the snippet does not work, can you supply more details? is it throwing an exception? is there an error on the browser’s console log? … BTW, which Vaadin version are you using?

My apologies! I am using Vaadin 24. This snippet does not throw any JS exception, it just does not move the submenu overlay as i would expect. Here is the code I am using to create the MenuBar:

MenuBar menuBar = new MenuBar();
MenuItem mainButton = menuBar.addItem(mainIcon);

Icon secondaryIcon = FontAwesome.Solid.CARET_DOWN.create();
MenuItem openSubmenuButton = menuBar.addItem(secondaryIcon);

Icon secondaryActionIcon = icon.create();
Icon commentIcon = FontAwesome.Solid.COMMENT.create();

MenuItem secondaryButton = openSubmenuButton.getSubMenu().addItem(commentIcon);

menuBar.getElement().executeJs(“this._subMenu.addEventListener(‘opened-changed’, function(e) {”
+ “const rootMenu = e.target;” //
+ “const button = rootMenu._context.target;” //
+ “if(!button) return;” //
+ "console.log('TEST');" // EDIT
+ “const rect = button.getBoundingClientRect();” //
+ “rootMenu.__x = rect.right - 200;” //
+ “rootMenu.__y = rect.top;” //
+ “rootMenu.__alignOverlayPosition();” //
+ “});”);

What i want to achieve is that the Submenu opens aligned below the MenuItem “mainButton” and not the default behavior aligned below MenuItem “openSubmenuButton”. I tried to move the “rootMenu” position 200 pixels to the left of his “click source” just to see if this works, but nothing happens. And I don’t know if this is important or not, but the MenuBar is already embedded in another Vaadin modal dialog. Thanks in advance!

EDIT: The “opened-changed” event fires. I added a console.log() to the JS snippet and I can see it in the browser console.

I tested the cookbook recipe, and I cannot see any difference if I comment the @CssImport and the menuBar.getElement().executeJs() statements.
Anyway I investigated a little bit more and I was able to implement what (I think) you want with the following:

    menuBar.getElement().executeJs("this._subMenu.addEventListener('opened-changed', function(e) {" +
            "const rootMenu = e.target;" +
            "const button = rootMenu._context.target;" +
            "if(!button) return;" +
            "const rect = button.getBoundingClientRect();" +
            "setTimeout(() => {" +
                "console.log(rect.top + 10);" +
                "rootMenu._overlayElement.style.top = rect.top + 10 + 'px';" +
                "rootMenu._overlayElement.style.left = rect.left + 10 + 'px';" +
            "},10);" +
            "});");

Can you take a look?

2 Likes

Thank you Martin, this setTimeout method and targeting the _overlayElement did the trick and this is what I tried to achieve. Unfortunately this moves the overlay after it gets rendered and the menu “jumps”. I would be glad if there was a solution where i can set the coordinates before the initial rendering but i guess this is good enough for now. Thank you again for your effort!

Edit: I solved the “jumping” with an css animation of the opacity of the overlay like this:

vaadin-menu-bar-overlay.multi-button[closing],
vaadin-menu-bar-overlay.multi-button[opening] {
animation: fadein .14s;
}

@keyframes fadein {
0% { opacity: 0; }
66% { opacity: 0; }
100% { opacity: 1; }
}

It is not the most elegant way, but it works like a charm. Thanks again @mmlopez for your solution!

1 Like