]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5924'
authorTom Hughes <tom@compton.nu>
Tue, 15 Apr 2025 17:04:58 +0000 (18:04 +0100)
committerTom Hughes <tom@compton.nu>
Tue, 15 Apr 2025 17:04:58 +0000 (18:04 +0100)
app/assets/javascripts/index/history-changesets-layer.js
app/assets/javascripts/index/history.js
app/assets/stylesheets/common.scss

index 37a45873777913cff5172789c743be9396fdc7d0..eafa2f71502bdeb63612a4488ae032bdfb753aec 100644 (file)
@@ -1,15 +1,47 @@
 OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
-  _changesets: [],
+  _changesets: new Map,
+
+  _getChangesetStyle: function ({ isHighlighted, sidebarRelativePosition }) {
+    let className;
+
+    if (sidebarRelativePosition > 0) {
+      className = "changeset-above-sidebar-viewport";
+    } else if (sidebarRelativePosition < 0) {
+      className = "changeset-below-sidebar-viewport";
+    } else {
+      className = "changeset-in-sidebar-viewport";
+    }
+    if (isHighlighted) {
+      className += " changeset-highlighted";
+    }
+
+    return {
+      weight: isHighlighted ? 3 : 2,
+      color: "var(--changeset-border-color)",
+      fillColor: "var(--changeset-fill-color)",
+      fillOpacity: isHighlighted ? 0.3 : 0,
+      className
+    };
+  },
+
+  _updateChangesetStyle: function (changeset) {
+    const rect = this.getLayer(changeset.id);
+    if (!rect) return;
+
+    const style = this._getChangesetStyle(changeset);
+    rect.setStyle(style);
+    // setStyle doesn't update css classes: https://github.com/leaflet/leaflet/issues/2662
+    rect._path.classList.value = style.className;
+    rect._path.classList.add("leaflet-interactive");
+  },
 
   updateChangesets: function (map, changesets) {
-    this._changesets = changesets;
+    this._changesets = new Map(changesets.map(changeset => [changeset.id, changeset]));
     this.updateChangesetShapes(map);
   },
 
   updateChangesetShapes: function (map) {
-    this.clearLayers();
-
-    for (const changeset of this._changesets) {
+    for (const changeset of this._changesets.values()) {
       const bottomLeft = map.project(L.latLng(changeset.bbox.minlat, changeset.bbox.minlon)),
             topRight = map.project(L.latLng(changeset.bbox.maxlat, changeset.bbox.maxlon)),
             width = topRight.x - bottomLeft.x,
@@ -30,24 +62,14 @@ OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
                                         map.unproject(topRight));
     }
 
-    this._changesets.sort(function (a, b) {
-      return b.bounds.getSize() - a.bounds.getSize();
-    });
-
     this.updateChangesetLocations(map);
-
-    for (const changeset of this._changesets) {
-      const rect = L.rectangle(changeset.bounds,
-                               { weight: 2, color: "#FF9500", opacity: 1, fillColor: "#FFFFAF", fillOpacity: 0 });
-      rect.id = changeset.id;
-      rect.addTo(this);
-    }
+    this.reorderChangesets();
   },
 
   updateChangesetLocations: function (map) {
     const mapCenterLng = map.getCenter().lng;
 
-    for (const changeset of this._changesets) {
+    for (const changeset of this._changesets.values()) {
       const changesetSouthWest = changeset.bounds.getSouthWest();
       const changesetNorthEast = changeset.bounds.getNorthEast();
       const changesetCenterLng = (changesetSouthWest.lng + changesetNorthEast.lng) / 2;
@@ -62,12 +84,40 @@ OSM.HistoryChangesetsLayer = L.FeatureGroup.extend({
     }
   },
 
-  highlightChangeset: function (id) {
-    this.getLayer(id)?.setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 });
+  reorderChangesets: function () {
+    const changesetEntries = [...this._changesets];
+    changesetEntries.sort(([, a], [, b]) => {
+      const aInViewport = !a.sidebarRelativePosition;
+      const bInViewport = !b.sidebarRelativePosition;
+      if (aInViewport !== bInViewport) return aInViewport - bInViewport;
+      return b.bounds.getSize() - a.bounds.getSize();
+    });
+    this._changesets = new Map(changesetEntries);
+
+    this.clearLayers();
+
+    for (const changeset of this._changesets.values()) {
+      delete changeset.isHighlighted;
+      const rect = L.rectangle(changeset.bounds, this._getChangesetStyle(changeset));
+      rect.id = changeset.id;
+      rect.addTo(this);
+    }
   },
 
-  unHighlightChangeset: function (id) {
-    this.getLayer(id)?.setStyle({ fillOpacity: 0, color: "#FF9500", weight: 2 });
+  toggleChangesetHighlight: function (id, state) {
+    const changeset = this._changesets.get(id);
+    if (!changeset) return;
+
+    changeset.isHighlighted = state;
+    this._updateChangesetStyle(changeset);
+  },
+
+  setChangesetSidebarRelativePosition: function (id, state) {
+    const changeset = this._changesets.get(id);
+    if (!changeset) return;
+
+    changeset.sidebarRelativePosition = state;
+    this._updateChangesetStyle(changeset);
   },
 
   getLayerId: function (layer) {
index 289081ca1a9c2feee28951e10c213532ed1f4871..8a3ba28ae56646ad6cdc05a4517a59c9c28e37ac 100644 (file)
@@ -7,18 +7,18 @@ OSM.History = function (map) {
   $("#sidebar_content")
     .on("click", ".changeset_more a", loadMoreChangesets)
     .on("mouseover", "[data-changeset]", function () {
-      highlightChangeset($(this).data("changeset").id);
+      toggleChangesetHighlight($(this).data("changeset").id, true);
     })
     .on("mouseout", "[data-changeset]", function () {
-      unHighlightChangeset($(this).data("changeset").id);
+      toggleChangesetHighlight($(this).data("changeset").id, false);
     });
 
   const changesetsLayer = new OSM.HistoryChangesetsLayer()
     .on("mouseover", function (e) {
-      highlightChangeset(e.layer.id);
+      toggleChangesetHighlight(e.layer.id, true);
     })
     .on("mouseout", function (e) {
-      unHighlightChangeset(e.layer.id);
+      toggleChangesetHighlight(e.layer.id, false);
     })
     .on("click", function (e) {
       clickChangeset(e.layer.id, e.originalEvent);
@@ -37,24 +37,27 @@ OSM.History = function (map) {
     disableChangesetIntersectionObserver();
     if (!window.IntersectionObserver) return;
 
-    let ignoreIntersectionEvents = true;
+    let keepInitialLocation = true;
 
     changesetIntersectionObserver = new IntersectionObserver((entries) => {
-      if (ignoreIntersectionEvents) {
-        ignoreIntersectionEvents = false;
-        return;
-      }
-
       let closestTargetToTop,
           closestDistanceToTop = Infinity,
           closestTargetToBottom,
           closestDistanceToBottom = Infinity;
 
       for (const entry of entries) {
-        if (entry.isIntersecting) continue;
+        const id = $(entry.target).data("changeset")?.id;
+
+        if (entry.isIntersecting) {
+          if (id) changesetsLayer.setChangesetSidebarRelativePosition(id, 0);
+          continue;
+        }
 
         const distanceToTop = entry.rootBounds.top - entry.boundingClientRect.bottom;
         const distanceToBottom = entry.boundingClientRect.top - entry.rootBounds.bottom;
+
+        if (id) changesetsLayer.setChangesetSidebarRelativePosition(id, distanceToTop >= 0 ? 1 : -1);
+
         if (distanceToTop >= 0 && distanceToTop < closestDistanceToTop) {
           closestDistanceToTop = distanceToTop;
           closestTargetToTop = entry.target;
@@ -65,6 +68,13 @@ OSM.History = function (map) {
         }
       }
 
+      changesetsLayer.reorderChangesets();
+
+      if (keepInitialLocation) {
+        keepInitialLocation = false;
+        return;
+      }
+
       if (closestTargetToTop && closestDistanceToTop < closestDistanceToBottom) {
         const id = $(closestTargetToTop).data("changeset")?.id;
         if (id) {
@@ -83,14 +93,9 @@ OSM.History = function (map) {
     });
   }
 
-  function highlightChangeset(id) {
-    changesetsLayer.highlightChangeset(id);
-    $("#changeset_" + id).addClass("selected");
-  }
-
-  function unHighlightChangeset(id) {
-    changesetsLayer.unHighlightChangeset(id);
-    $("#changeset_" + id).removeClass("selected");
+  function toggleChangesetHighlight(id, state) {
+    changesetsLayer.toggleChangesetHighlight(id, state);
+    $("#changeset_" + id).toggleClass("selected", state);
   }
 
   function clickChangeset(id, e) {
@@ -100,6 +105,10 @@ OSM.History = function (map) {
   function displayFirstChangesets(html) {
     $("#sidebar_content .changesets").html(html);
 
+    $("#sidebar_content .changesets ol")
+      .before($("<div class='changeset-color-hint-bar opacity-75 sticky-top changeset-above-sidebar-viewport'>"))
+      .after($("<div class='changeset-color-hint-bar opacity-75 sticky-bottom changeset-below-sidebar-viewport'>"));
+
     if (location.pathname === "/history") {
       setPaginationMapHashes();
     }
index 2cfd5eba732348b338be53952e627319efc59f5c..de8611e9d7bad595d8101aa888b092076f3a7949 100644 (file)
@@ -601,7 +601,28 @@ tr.turn {
 
 /* Rules for the history sidebar */
 
+.changeset-above-sidebar-viewport {
+  --changeset-border-color: #CC7755;
+  --changeset-fill-color: #888888;
+}
+.changeset-in-sidebar-viewport {
+  --changeset-border-color: #FF9500;
+  &.changeset-highlighted {
+    --changeset-border-color: #FF6600;
+  }
+  --changeset-fill-color: #FFFFAF;
+}
+.changeset-below-sidebar-viewport {
+  --changeset-border-color: #8888AA;
+  --changeset-fill-color: #888888;
+}
+
 #sidebar .changesets {
+  .changeset-color-hint-bar {
+    height: 2px;
+    background: var(--changeset-border-color);
+  }
+
   li {
     &.selected {
       @extend :hover;