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/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..6b27f69 100644 --- a/src/map.js +++ b/src/map.js @@ -204,6 +204,108 @@ const emit = ($item, item) => { scrollWheelZoom: false, }) + /* + ** (Ctrl or ⌘) + Scroll to Zoom map + */ + const mapContainer = map.getContainer() + const zoomOverlay = document.createElement('div') + zoomOverlay.textContent = 'Use (Ctrl or ⌘) + Scroll\nto 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', + textAlign: 'center', + whiteSpace: 'pre-line', + 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) { + // Check for Control (Windows/Linux) OR Command (Mac) + if (event.ctrlKey || event.metaKey) { + // 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) + } + }) + + // 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: { + 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',