Forráskód Böngészése

Add track hover highlight and sidebar resize

- MapView.highlightTrack(id)/unhighlightTrack(): thickens the hovered
  polyline (weight 3→6, opacity 0.8→1) and brings it to front
- browser.js and local.js bind mouseenter/mouseleave on track list items
  to call MapView.highlightTrack/unhighlightTrack
- Sidebar resize handle: 5px draggable divider between sidebar and map;
  uses Pointer Events API with setPointerCapture for reliable drag;
  sidebar.resizing disables CSS transition during drag to avoid lag;
  clamps width between 180px and 600px; invalidates Leaflet map on release

No backend restart required (frontend-only change).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
k4be 10 órája
szülő
commit
0489cf8479

+ 24 - 0
gpx-vis-frontend/css/style.css

@@ -271,6 +271,30 @@ form button[type="submit"]:hover {
   border-right: none;
 }
 
+#sidebar.resizing {
+  transition: none;
+}
+
+/* ===== Sidebar Resize Handle ===== */
+#sidebar-resize-handle {
+  width: 5px;
+  flex-shrink: 0;
+  cursor: col-resize;
+  background: transparent;
+  position: relative;
+  z-index: 10;
+  transition: background 0.15s;
+}
+
+#sidebar-resize-handle:hover,
+#sidebar-resize-handle.dragging {
+  background: var(--color-blue);
+}
+
+#sidebar.collapsed + #sidebar-resize-handle {
+  display: none;
+}
+
 /* ===== Sidebar Tabs ===== */
 #sidebar-tabs {
   display: flex;

+ 3 - 0
gpx-vis-frontend/index.html

@@ -73,6 +73,9 @@
         </div>
       </div>
 
+      <!-- Sidebar resize handle -->
+      <div id="sidebar-resize-handle"></div>
+
       <!-- Map -->
       <div id="map-container">
         <div id="map"></div>

+ 41 - 0
gpx-vis-frontend/js/app.js

@@ -1,3 +1,42 @@
+// ===== Sidebar resize =====
+
+function setupSidebarResize() {
+  const handle = document.getElementById('sidebar-resize-handle');
+  const sidebar = document.getElementById('sidebar');
+  if (!handle || !sidebar) return;
+
+  const MIN_WIDTH = 180;
+  const MAX_WIDTH = 600;
+
+  handle.addEventListener('pointerdown', (e) => {
+    e.preventDefault();
+    handle.classList.add('dragging');
+    handle.setPointerCapture(e.pointerId);
+
+    const startX = e.clientX;
+    const startWidth = sidebar.offsetWidth;
+
+    sidebar.classList.add('resizing');
+
+    function onMove(ev) {
+      const newWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, startWidth + ev.clientX - startX));
+      sidebar.style.width = newWidth + 'px';
+    }
+
+    function onUp() {
+      sidebar.classList.remove('resizing');
+      handle.classList.remove('dragging');
+      handle.removeEventListener('pointermove', onMove);
+      handle.removeEventListener('pointerup', onUp);
+      const m = MapView.getMap();
+      if (m) m.invalidateSize();
+    }
+
+    handle.addEventListener('pointermove', onMove);
+    handle.addEventListener('pointerup', onUp);
+  });
+}
+
 // ===== Utility functions (global scope) =====
 
 function escHtml(s) {
@@ -232,6 +271,7 @@ function initGuestMode() {
   MapView.init();
   Elevation.init();
   LocalViewer.init();
+  setupSidebarResize();
 }
 
 // ===== Main Init =====
@@ -339,6 +379,7 @@ async function main() {
 
   // Init browser
   await Browser.init();
+  setupSidebarResize();
 
   // Restore open track from hash
   const params = MapView.getHashParams();

+ 3 - 1
gpx-vis-frontend/js/browser.js

@@ -186,12 +186,14 @@ const Browser = (() => {
       });
     });
 
-    // Track click → open on map
+    // Track click → open on map; hover → highlight on map
     container.querySelectorAll('.track-item').forEach(el => {
       el.addEventListener('click', (e) => {
         if (e.target.closest('.item-actions')) return;
         openTrack(parseInt(el.dataset.id));
       });
+      el.addEventListener('mouseenter', () => MapView.highlightTrack(parseInt(el.dataset.id)));
+      el.addEventListener('mouseleave', () => MapView.unhighlightTrack());
     });
 
     container.querySelectorAll('.view-track-btn').forEach(btn => {

+ 2 - 0
gpx-vis-frontend/js/local.js

@@ -147,6 +147,8 @@ const LocalViewer = (() => {
         if (e.target.closest('.item-actions')) return;
         toggleTrack(el.dataset.id);
       });
+      el.addEventListener('mouseenter', () => MapView.highlightTrack(el.dataset.id));
+      el.addEventListener('mouseleave', () => MapView.unhighlightTrack());
     });
     list.querySelectorAll('.local-remove-btn').forEach(btn => {
       btn.addEventListener('click', (e) => {

+ 22 - 0
gpx-vis-frontend/js/map.js

@@ -180,6 +180,27 @@ const MapView = (() => {
   function hasTrack(trackId)     { return !!trackLayers[trackId]; }
   function getTrackPoints(trackId) { return trackLayers[trackId]?.points || null; }
 
+  // ===== Track highlight (sidebar hover) =====
+
+  let highlightedTrackId = null;
+
+  function highlightTrack(trackId) {
+    if (String(highlightedTrackId) === String(trackId)) return;
+    unhighlightTrack();
+    const tl = trackLayers[trackId];
+    if (!tl) return;
+    tl.layer.setStyle({ weight: 6, opacity: 1 });
+    tl.layer.bringToFront();
+    highlightedTrackId = trackId;
+  }
+
+  function unhighlightTrack() {
+    if (highlightedTrackId === null) return;
+    const tl = trackLayers[highlightedTrackId];
+    if (tl) tl.layer.setStyle({ weight: 3, opacity: 0.8 });
+    highlightedTrackId = null;
+  }
+
   // ===== Hover =====
 
   function onMapMouseMove(e) {
@@ -309,6 +330,7 @@ const MapView = (() => {
     init, addTrack, removeTrack, clearTracks,
     fitTrack, fitAll, hasTrack, getTrackPoints,
     showHoverMarker, hideHoverMarker,
+    highlightTrack, unhighlightTrack,
     saveToHash, restoreFromHash, getHashParams,
     setCurrentTrack, getMap
   };