From fa4115b714586ecfb8aebcde0303c1f3f8288761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Wed, 21 Jan 2026 19:38:55 +0700 Subject: [PATCH] Pan map on mobile when pin obscured by sidebar --- app/components/map.gjs | 37 +++++++++++++++++++++++++++++++++++++ app/styles/app.css | 20 ++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/app/components/map.gjs b/app/components/map.gjs index 4f787bf..9c84d49 100644 --- a/app/components/map.gjs +++ b/app/components/map.gjs @@ -359,6 +359,8 @@ export default class MapComponent extends Component { // Force reflow void this.selectedPinElement.offsetWidth; this.selectedPinElement.classList.add('active'); + + this.panIfObscured(coords); } else { this.selectedPinElement.classList.remove('active'); // Hide it effectively by moving it away or just relying on display:none in CSS @@ -366,6 +368,41 @@ export default class MapComponent extends Component { } }); + panIfObscured(coords) { + if (!this.mapInstance) return; + + const size = this.mapInstance.getSize(); + // Check if mobile (width <= 768px matches CSS) + if (size[0] > 768) return; + + const pixel = this.mapInstance.getPixelFromCoordinate(coords); + const height = size[1]; + + // Sidebar covers the bottom 50% + const splitPoint = height / 2; + + // If the pin is in the bottom half (y > splitPoint), it is obscured + if (pixel[1] > splitPoint) { + // Target position: Center of top half = height * 0.25 + const targetY = height * 0.25; + const deltaY = pixel[1] - targetY; + + const view = this.mapInstance.getView(); + const center = view.getCenter(); + const resolution = view.getResolution(); + + // Move the map center SOUTH (decrease Y) to move the pin UP (decrease pixel Y) + const deltaMapUnits = deltaY * resolution; + const newCenter = [center[0], center[1] - deltaMapUnits]; + + view.animate({ + center: newCenter, + duration: 500, + easing: (t) => t * (2 - t) // Ease-out + }); + } + } + // Re-fetch bookmarks when the version changes (triggered by parent action or service) updateBookmarks = modifier(() => { // Depend on the tracked storage.savedPlaces to automatically update when they change diff --git a/app/styles/app.css b/app/styles/app.css index c5a9eee..14b9b48 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -312,3 +312,23 @@ span.icon { opacity: 1; } } + +@media (max-width: 768px) { + .sidebar { + top: auto; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 50vh; + box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); + border-top-left-radius: 16px; + border-top-right-radius: 16px; + } + + .sidebar-content { + overflow-y: auto; + /* Ensure content doesn't get hidden behind bottom safe areas on mobile */ + padding-bottom: env(safe-area-inset-bottom, 20px); + } +}