From 0c0488cc79f363a2aaebac14d9edca76faf492fc Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 22 Jan 2024 01:58:37 +0100 Subject: Add ability to grab and move popovers. --- main.js | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- style.css | 18 +++++++++++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/main.js b/main.js index 4ed491f..051ef5f 100644 --- a/main.js +++ b/main.js @@ -807,9 +807,54 @@ map.addLayer(new VectorTileLayer({ const header = document.createElement('div'); header.classList.add('d-flex'); - const pageNode = document.createElement('div'); - pageNode.classList.add('flex-grow-1', 'pe-4'); - header.appendChild(pageNode); + const headerGrabbingArea = document.createElement('div'); + headerGrabbingArea.classList.add('flex-grow-1', 'grabbing-area', 'pe-2', 'me-2'); + header.appendChild(headerGrabbingArea); + + headerGrabbingArea.onmousedown = function(event) { + if (event.button != 0) { + return; + } + const popoverTip = Popover.getInstance(popup).tip; + pageNode.classList.add('grabbing-area-grabbed'); + + if (!popoverTip.classList.contains('popover-detached')) { + /* detach popover tip */ + popoverTip.classList.add('popover-detached'); + const rect = popoverTip.getBoundingClientRect(); + const maxHeight = document.getElementById('map').getBoundingClientRect().height - 1 - rect.top - + popoverTip.getElementsByClassName('popover-header')[0].getBoundingClientRect().height; + + const style = popoverTip.style; + style.display = 'none'; /* avoid reflows between the following assignments */ + style.position = 'absolute'; + style.transform = ''; + style.inset = `${rect.top}px auto auto ${rect.left}px`; + style.display = ''; + } + + let clientX = event.clientX, clientY = event.clientY; + document.onmousemove = function(event) { + const offsetX = clientX - event.clientX; + const offsetY = clientY - event.clientY; + clientX = event.clientX; + clientY = event.clientY; + popoverTip.style.top = (popoverTip.offsetTop - offsetY).toString() + 'px'; + popoverTip.style.left = (popoverTip.offsetLeft - offsetX).toString() + 'px'; + }; + + document.onmouseup = function(event) { + if (event.button != 0) { + return; + } + pageNode.classList.remove('grabbing-area-grabbed'); + document.onmousemove = null; + document.onmouseup = null; + }; + }; + + const pageNode = document.createElement('h6'); + headerGrabbingArea.appendChild(pageNode); const pageNum = document.createElement('span'); const pageCount = document.createElement('span'); @@ -891,10 +936,10 @@ map.addLayer(new VectorTileLayer({ return; } - /* unclear how many feature we'll find, render prev/next buttons invisible for now */ - pageNode.classList.add('invisible'); - btnPrev.classList.add('invisible', 'disabled'); - btnNext.classList.add('invisible', 'disabled'); + /* unclear how many feature we'll find, don't render prev/next buttons for now */ + pageNode.classList.add('d-none'); + btnPrev.classList.add('d-none', 'disabled'); + btnNext.classList.add('d-none', 'disabled'); map.forEachFeatureAtPixel(event.pixel, function(feature, layer) { const properties = feature.getProperties(); @@ -955,10 +1000,10 @@ map.addLayer(new VectorTileLayer({ pageCount.innerHTML = features.length.toString(); if (features.length == 2) { - /* there are ≥2 features, make prev/pre buttons visible */ - btnNext.classList.remove('invisible', 'disabled'); - btnPrev.classList.remove('invisible'); - pageNode.classList.remove('invisible'); + /* there are ≥2 features, render prev/pre buttons */ + btnNext.classList.remove('d-none', 'disabled'); + btnPrev.classList.remove('d-none'); + pageNode.classList.remove('d-none'); } if (popover === null || popover.tip === null) { diff --git a/style.css b/style.css index 3377f44..4b78cee 100644 --- a/style.css +++ b/style.css @@ -348,6 +348,9 @@ html, body { padding: 0.1rem 0.3rem; } +.popover-header h6 { + margin: 0; +} /* inspired from bootstrap's .btn-close */ .popover-header .popover-button { box-sizing: content-box; @@ -384,3 +387,18 @@ html, body { .popover-header .popover-button-close { --popover-button-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath transform='scale(1.3332) translate(-2 -2)' d='M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z'/%3e%3c/svg%3e"); } + +.popover .grabbing-area { + cursor: move; /* fallback if grab cursor is unsupported */ + cursor: grab; + cursor: -moz-grab; + cursor: -webkit-grab; +} +.popover .grabbing-area.grabbing-area-grabbed { + cursor: grabbing; + cursor: -moz-grabbing; + cursor: -webkit-grabbing; +} +.popover.popover-detached > .popover-arrow { + display: none; +} -- cgit v1.2.3