From 46810de5acca46d8efe81a1b06de008dfe7cef2b Mon Sep 17 00:00:00 2001 From: Robert Best Date: Fri, 22 May 2026 11:41:40 -0400 Subject: [PATCH 1/5] Some UX enhancements to maps plugin Lets you use Ctrl + Scroll wheel to zoom in/out of map based on location of pointer. Also adds a text overlay when scrolling over the map, and not holding Ctrl, to let you know this feature exists. Adds a Zoom to bounds control to the top left of the map. Fixes build command in package.json to also be compatible with Windows machines. --- package.json | 2 +- src/map.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f844393..ac30820 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ ], "scripts": { "clean": "rm client/map.js client/map.js.map", - "build": "npm run clean;npm run test; node --no-warnings scripts/build-client.js", + "build": "npm run clean && npm run test && node --no-warnings scripts/build-client.js", "test": "node --test", "update-authors": "node scripts/update-authors.cjs" }, diff --git a/src/map.js b/src/map.js index 801b736..0fd5fe8 100644 --- a/src/map.js +++ b/src/map.js @@ -204,6 +204,97 @@ const emit = ($item, item) => { scrollWheelZoom: false, }) + /* + ** Ctrl + Scroll Zoom UX Enhancement: + */ + + const mapContainer = map.getContainer() + + const zoomOverlay = document.createElement('div') + zoomOverlay.innerText = 'Use Ctrl + Scroll to zoom the map' + + Object.assign(zoomOverlay.style, { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'rgba(0, 0, 0, 0.5)', + color: 'white', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontSize: '22px', + fontFamily: 'sans-serif', + zIndex: '9999', // Ensures it sits above all Leaflet map tiles + opacity: '0', // Start hidden + transition: 'opacity 0.3s ease', + pointerEvents: 'none', // CRITICAL: Allows scroll events to pass through the overlay! + }) + + mapContainer.appendChild(zoomOverlay) + + let overlayTimeout + + mapContainer.addEventListener('wheel', function (event) { + if (event.ctrlKey) { + // THE USER IS HOLDING CTRL (Zoom the map) + event.preventDefault() + + // Hide the overlay instantly if they press Ctrl + zoomOverlay.style.opacity = '0' + + const mouseLatLng = map.mouseEventToLatLng(event) + let currentZoom = map.getZoom() + let newZoom = event.deltaY < 0 ? currentZoom + 1 : currentZoom - 1 + + map.setZoomAround(mouseLatLng, newZoom) + } else { + // THE USER IS SCROLLING NORMALLY (Show overlay) + + // Make the overlay visible + zoomOverlay.style.opacity = '1' + + // Clear any existing timer so it doesn't fade out while they are still scrolling + clearTimeout(overlayTimeout) + + // Set a timer to fade it out after 1.5 seconds of inactivity + overlayTimeout = setTimeout(() => { + zoomOverlay.style.opacity = '0' + }, 1500) + } + }) + + // Define a new Leaflet "Fit to Bounds" Control + const FitBoundsControl = L.Control.extend({ + options: { + position: 'topleft', // Places it right below the native +/- buttons + }, + + onAdd: function (map) { + const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom') + + container.innerHTML = + '' + + container.onclick = function (event) { + event.preventDefault() + event.stopPropagation() + + const bounds = new L.LatLngBounds(boundary.map(p => [p.lat, p.lon])) + map.fitBounds(bounds.pad(0.3)) + } + + container.ondblclick = function (event) { + event.stopPropagation() + } + + return container + }, + }) + + map.addControl(new FitBoundsControl()) + const update = () => { wiki.pageHandler.put($item.parents('.page:first'), { type: 'edit', From 1a4de46b9afc274bb2ea85e10012943ed96baf6b Mon Sep 17 00:00:00 2001 From: Robert Best Date: Fri, 22 May 2026 15:01:36 -0400 Subject: [PATCH 2/5] Update map.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the scroll modified key to be more cross-platform compatible, and suggest ⌘+scroll for apple devices. --- src/map.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/map.js b/src/map.js index 0fd5fe8..847e217 100644 --- a/src/map.js +++ b/src/map.js @@ -207,11 +207,13 @@ const emit = ($item, item) => { /* ** Ctrl + Scroll Zoom UX Enhancement: */ - const mapContainer = map.getContainer() - const zoomOverlay = document.createElement('div') - zoomOverlay.innerText = 'Use Ctrl + Scroll to zoom the map' + + // Let Leaflet determine if the user is on an Apple ecosystem device + const isMac = L.Browser.mac + const modifierKey = isMac ? '⌘' : 'Ctrl' + zoomOverlay.innerText = `Use ${modifierKey} + Scroll to zoom the map` Object.assign(zoomOverlay.style, { position: 'absolute', @@ -237,8 +239,9 @@ const emit = ($item, item) => { let overlayTimeout mapContainer.addEventListener('wheel', function (event) { - if (event.ctrlKey) { - // THE USER IS HOLDING CTRL (Zoom the map) + if (event.ctrlKey || event.metaKey) { + // Check for Control (Windows/Linux) OR Command (Mac) + // THE USER IS HOLDING CTRL/⌘ (Zoom the map) event.preventDefault() // Hide the overlay instantly if they press Ctrl From 832656c64f6dcd0e73b5b712a43ff866d9ae48c2 Mon Sep 17 00:00:00 2001 From: Robert Best Date: Sat, 23 May 2026 10:06:47 -0400 Subject: [PATCH 3/5] small changes to overlay text Don't try and determine the user's device type, just display a more universal overlay. --- src/map.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/map.js b/src/map.js index 847e217..21bb641 100644 --- a/src/map.js +++ b/src/map.js @@ -205,15 +205,11 @@ const emit = ($item, item) => { }) /* - ** Ctrl + Scroll Zoom UX Enhancement: + ** (Ctrl or ⌘) + Scroll to Zoom map */ const mapContainer = map.getContainer() const zoomOverlay = document.createElement('div') - - // Let Leaflet determine if the user is on an Apple ecosystem device - const isMac = L.Browser.mac - const modifierKey = isMac ? '⌘' : 'Ctrl' - zoomOverlay.innerText = `Use ${modifierKey} + Scroll to zoom the map` + zoomOverlay.innerText = 'Use (Ctrl or ⌘) + Scroll to zoom the map' Object.assign(zoomOverlay.style, { position: 'absolute', @@ -239,8 +235,8 @@ const emit = ($item, item) => { let overlayTimeout mapContainer.addEventListener('wheel', function (event) { + // Check for Control (Windows/Linux) OR Command (Mac) if (event.ctrlKey || event.metaKey) { - // Check for Control (Windows/Linux) OR Command (Mac) // THE USER IS HOLDING CTRL/⌘ (Zoom the map) event.preventDefault() From 8f8aefb23b3c013ddeab0229cb8b98ca020485fa Mon Sep 17 00:00:00 2001 From: Robert Best Date: Sat, 23 May 2026 13:30:36 -0400 Subject: [PATCH 4/5] Change overlay text again --- src/map.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/map.js b/src/map.js index 21bb641..1ea3eb4 100644 --- a/src/map.js +++ b/src/map.js @@ -209,7 +209,7 @@ const emit = ($item, item) => { */ const mapContainer = map.getContainer() const zoomOverlay = document.createElement('div') - zoomOverlay.innerText = 'Use (Ctrl or ⌘) + Scroll to zoom the map' + zoomOverlay.textContent = 'Use (Ctrl or ⌘) + Scroll\nto zoom the map' Object.assign(zoomOverlay.style, { position: 'absolute', @@ -222,6 +222,8 @@ const emit = ($item, item) => { display: 'flex', alignItems: 'center', justifyContent: 'center', + textAlign: 'center', + whiteSpace: 'pre-line', fontSize: '22px', fontFamily: 'sans-serif', zIndex: '9999', // Ensures it sits above all Leaflet map tiles From 283453e37d0091642cec716300a7266ad926e687 Mon Sep 17 00:00:00 2001 From: Robert Best Date: Sat, 23 May 2026 23:05:28 -0400 Subject: [PATCH 5/5] Minor UX changes * Hide the overlay if the user starts interacting with the map * Prevent a strange outline appearing around the map if the user holds down a modifier key like shift or ctrl. --- client/map.css | 4 ++++ src/map.js | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/client/map.css b/client/map.css index dbb3ab4..0af1216 100644 --- a/client/map.css +++ b/client/map.css @@ -22,3 +22,7 @@ #mapediting{ background: #eae8d7; } + +.leaflet-container:focus { + outline: none !important; +} \ No newline at end of file diff --git a/src/map.js b/src/map.js index 1ea3eb4..6b27f69 100644 --- a/src/map.js +++ b/src/map.js @@ -266,6 +266,16 @@ const emit = ($item, item) => { } }) + // Helper function to hide the overlay smoothly + function hideZoomOverlay() { + zoomOverlay.style.opacity = '0' + clearTimeout(overlayTimeout) + } + map.on('dragstart', hideZoomOverlay) + map.on('mousedown', hideZoomOverlay) + map.on('zoomstart', hideZoomOverlay) + map.on('movestart', hideZoomOverlay) + // Define a new Leaflet "Fit to Bounds" Control const FitBoundsControl = L.Control.extend({ options: {