diff --git a/src/mapml.css b/src/mapml.css index e098d13db..3f747e39d 100644 --- a/src/mapml.css +++ b/src/mapml.css @@ -258,7 +258,8 @@ background-color: #fff; cursor: default; position: absolute; - width: 160px; + width: fit-content; + display: inline-block; z-index: 10001; } @@ -272,6 +273,7 @@ border-bottom: 1px solid transparent; cursor: default; width: 100%; + display: block; } .mapml-contextmenu button.mapml-contextmenu-item.over { @@ -288,6 +290,7 @@ .mapml-contextmenu.mapml-submenu { width: 80px; margin-bottom: -2rem; + width: fit-content; } @supports (contain: layout) { diff --git a/src/mapml/handlers/ContextMenu.js b/src/mapml/handlers/ContextMenu.js index c3fa3cac4..9ad2868b4 100644 --- a/src/mapml/handlers/ContextMenu.js +++ b/src/mapml/handlers/ContextMenu.js @@ -119,22 +119,22 @@ export var ContextMenu = L.Handler.extend({ for (let i = 0; i < 6; i++) { this._items[i].el = this._createItem(this._container, this._items[i]); } - + this._coordMenu = L.DomUtil.create("div", "mapml-contextmenu mapml-submenu", this._container); this._coordMenu.id = "mapml-copy-submenu"; this._coordMenu.setAttribute('hidden', ''); - + this._clickEvent = null; - + for(let i =0;i mapSize.x) { + const menuWidth = this._container.offsetWidth, + menuHeight = this._container.offsetHeight, + submenuWidth = menu.offsetWidth; + if (click.containerPoint.x + menuWidth + submenuWidth > mapSize.x) { menu.style.left = 'auto'; - menu.style.right = 160 + 'px'; + menu.style.right = menuWidth + 'px'; } else { - menu.style.left = 160 + 'px'; + menu.style.left = menuWidth + 'px'; menu.style.right = 'auto'; } - if (click.containerPoint.y + 160 > mapSize.y) { + // height difference between the main contextmenu and submenu + const heightDiff = 73; + if (click.containerPoint.y + menuHeight + heightDiff > mapSize.y) { menu.style.top = 'auto'; - menu.style.bottom = 20 + 'px'; + // to make submenu show completely when clicking at the bottom of the map + menu.style.bottom = 32 + 'px'; } else { menu.style.top = 100 + 'px'; menu.style.bottom = 'auto'; @@ -665,8 +674,9 @@ export var ContextMenu = L.Handler.extend({ }, _hideCoordMenu: function(e){ - if(e.srcElement.parentElement.classList.contains("mapml-submenu") || - e.srcElement.innerText === (M.options.locale.cmCopyCoords + " (C)"))return; + if(!e.relatedTarget || !e.relatedTarget.parentElement || + e.relatedTarget.parentElement.classList.contains("mapml-submenu") || + e.relatedTarget.classList.contains("mapml-submenu"))return; let menu = this._coordMenu, copyEl = this._items[5].el.el; copyEl.setAttribute("aria-expanded","false"); menu.setAttribute('hidden', ''); diff --git a/test/e2e/core/mapContextMenu.test.js b/test/e2e/core/mapContextMenu.test.js index d849f486f..fa88f8463 100644 --- a/test/e2e/core/mapContextMenu.test.js +++ b/test/e2e/core/mapContextMenu.test.js @@ -305,4 +305,37 @@ test.describe("Playwright Map Context Menu Tests", () => { ); expect(layerCount).toEqual(5); }); + + test("Context menu, click at margin and move mouse out when submenu is visible", async () => { + // click at the right-bottom margin of map + await page.mouse.wheel(0, 200); + await page.waitForTimeout(200); + await page.click("body > map", { + button: 'right', + position: {x: 495, y: 580} + }); + const contextMenu = await page.locator('div > div.mapml-contextmenu').first(); + expect(await contextMenu.isVisible()).toBeTruthy(); + const mapSize = await page.$eval( + "body > map", + (map) => { return {x: map.width, y: map.height} } + ); + const contextMenuSize = await page.$eval( + "div > div.mapml-contextmenu", + (menu) => { + return { + x: menu.offsetWidth + menu.getBoundingClientRect().left, + y: menu.offsetHeight + menu.getBoundingClientRect().top + } + } + ); + expect(contextMenuSize.x <= mapSize.x && contextMenuSize.y <= mapSize.y).toBeTruthy(); + + // move the mouse from "copy" to another button in the main contextmenu + await contextMenu.hover(); + const submenu = await page.locator('div > div#mapml-copy-submenu').first(); + expect(await submenu.isVisible()).toBeTruthy(); + await page.hover("div > div.mapml-contextmenu > button:nth-child(5)"); + expect(await submenu.isHidden()).toBeTruthy(); + }); }); \ No newline at end of file