From: Tom Hughes Date: Tue, 15 Apr 2025 17:04:58 +0000 (+0100) Subject: Merge remote-tracking branch 'upstream/pull/5924' X-Git-Tag: live~46 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/ed41e83d710a6ac71b4797f3ea7baf5e2b2a51cb?hp=f61b14bf2d42a8ebbcaa35f5032000ea07ed4b7e Merge remote-tracking branch 'upstream/pull/5924' --- diff --git a/app/assets/javascripts/index/history-changesets-layer.js b/app/assets/javascripts/index/history-changesets-layer.js index 37a458737..eafa2f715 100644 --- a/app/assets/javascripts/index/history-changesets-layer.js +++ b/app/assets/javascripts/index/history-changesets-layer.js @@ -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) { diff --git a/app/assets/javascripts/index/history.js b/app/assets/javascripts/index/history.js index 289081ca1..8a3ba28ae 100644 --- a/app/assets/javascripts/index/history.js +++ b/app/assets/javascripts/index/history.js @@ -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($("
")) + .after($("
")); + if (location.pathname === "/history") { setPaginationMapHashes(); } diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 2cfd5eba7..de8611e9d 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -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;