From 825c7370b8b5130fe7eaa98a99e586bc89c1f8a9 Mon Sep 17 00:00:00 2001 From: serversdown Date: Fri, 15 May 2026 06:34:19 +0000 Subject: [PATCH] feat(project-overview): hover location card to highlight its map pin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverse direction of the existing pin→card flash on the project overview map. Hovering a location card now enlarges + reddens the matching pin on the map and opens its tooltip. Mouse-out reverts. Why hover instead of click: clicking the card title navigates to the location detail page, so any flash effect would never be visible. Hover is the right interaction here. Event delegation on document means cards that appear after htmx swaps (e.g. after a reorder, remove/restore, or assign-modal close) still get the behavior without rewiring. Co-Authored-By: Claude Opus 4.7 --- .../partials/projects/project_dashboard.html | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/templates/partials/projects/project_dashboard.html b/templates/partials/projects/project_dashboard.html index 5b859e2..d8daf49 100644 --- a/templates/partials/projects/project_dashboard.html +++ b/templates/partials/projects/project_dashboard.html @@ -161,7 +161,9 @@ maxZoom: 18, }).addTo(map); - const markers = []; + // Marker store keyed by location id so card-click can find + flash + // the matching pin (bidirectional highlight). + const markersById = {}; const bounds = []; withCoords.forEach(loc => { const marker = L.circleMarker(loc.latlon, { @@ -174,10 +176,53 @@ }).addTo(map); marker.bindTooltip(loc.name, { direction: 'top', offset: [0, -6] }); marker.on('click', () => _flashLocationCard(loc.id)); - markers.push(marker); + markersById[loc.id] = marker; bounds.push(loc.latlon); }); + // Wire up the reverse direction: hovering a card highlights its + // pin on the map. Hover-based instead of click-based because + // clicking the card navigates to the location detail page, so the + // flash would never be visible. Event delegation so cards that + // appear later (htmx swap or reorder) still work. + let _hoverPinId = null; + document.addEventListener('mouseover', (e) => { + const card = e.target.closest('.location-card'); + if (!card) return; + const locId = card.dataset.locationId; + if (locId === _hoverPinId) return; + if (_hoverPinId) _unhighlightPin(_hoverPinId); + _highlightPin(locId); + _hoverPinId = locId; + }); + document.addEventListener('mouseout', (e) => { + const card = e.target.closest('.location-card'); + if (!card) return; + // Only un-highlight if the mouse actually left the card, + // not just moved to a child element. + const related = e.relatedTarget && e.relatedTarget.closest('.location-card'); + if (related === card) return; + if (_hoverPinId) { + _unhighlightPin(_hoverPinId); + _hoverPinId = null; + } + }); + + function _highlightPin(locId) { + const marker = markersById[locId]; + if (!marker) return; + marker.setStyle({ radius: 12, fillColor: '#dc2626', weight: 3 }); + marker.openTooltip(); + marker.bringToFront(); + } + + function _unhighlightPin(locId) { + const marker = markersById[locId]; + if (!marker) return; + marker.setStyle({ radius: 8, fillColor: '#f48b1c', weight: 2 }); + marker.closeTooltip(); + } + if (bounds.length === 1) { map.setView(bounds[0], 14); } else {