]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/leaflet.layers.js
Merge remote-tracking branch 'upstream/pull/5201'
[rails.git] / app / assets / javascripts / leaflet.layers.js
1 L.OSM.layers = function (options) {
2   const control = L.OSM.sidebarPane(options, "layers", "javascripts.map.layers.title", "javascripts.map.layers.header");
3
4   control.onAddPane = function (map, button, $ui, toggle) {
5     const layers = options.layers;
6
7     const baseSection = $("<div>")
8       .attr("class", "base-layers d-grid gap-3 p-3 border-bottom border-secondary-subtle")
9       .appendTo($ui);
10
11     layers.forEach(function (layer, i) {
12       const id = "map-ui-layer-" + i;
13
14       const buttonContainer = $("<div class='position-relative'>")
15         .appendTo(baseSection);
16
17       const mapContainer = $("<div class='position-absolute top-0 start-0 bottom-0 end-0 z-0 bg-body-secondary'>")
18         .appendTo(buttonContainer);
19
20       const input = $("<input type='radio' class='btn-check' name='layer'>")
21         .prop("id", id)
22         .prop("checked", map.hasLayer(layer))
23         .appendTo(buttonContainer);
24
25       const item = $("<label class='btn btn-outline-primary border-4 rounded-3 bg-transparent position-absolute p-0 h-100 w-100 overflow-hidden'>")
26         .prop("for", id)
27         .append($("<span class='badge position-absolute top-0 start-0 rounded-top-0 rounded-start-0 py-1 px-2 bg-body bg-opacity-75 text-body text-wrap text-start fs-6 lh-base'>").append(layer.options.name))
28         .appendTo(buttonContainer);
29
30       map.whenReady(function () {
31         const miniMap = L.map(mapContainer[0], { attributionControl: false, zoomControl: false, keyboard: false })
32           .addLayer(new layer.constructor(layer.options));
33
34         miniMap.dragging.disable();
35         miniMap.touchZoom.disable();
36         miniMap.doubleClickZoom.disable();
37         miniMap.scrollWheelZoom.disable();
38
39         $ui
40           .on("show", shown)
41           .on("hide", hide);
42
43         function shown() {
44           miniMap.invalidateSize();
45           setView({ animate: false });
46           map.on("moveend", moved);
47         }
48
49         function hide() {
50           map.off("moveend", moved);
51         }
52
53         function moved() {
54           setView();
55         }
56
57         function setView(options) {
58           miniMap.setView(map.getCenter(), Math.max(map.getZoom() - 2, 0), options);
59         }
60       });
61
62       input.on("click", function () {
63         for (const other of layers) {
64           if (other !== layer) {
65             map.removeLayer(other);
66           }
67         }
68         map.addLayer(layer);
69       });
70
71       item.on("dblclick", toggle);
72
73       map.on("baselayerchange", function () {
74         input.prop("checked", map.hasLayer(layer));
75       });
76     });
77
78     if (OSM.STATUS !== "api_offline" && OSM.STATUS !== "database_offline") {
79       const overlaySection = $("<div>")
80         .attr("class", "overlay-layers p-3")
81         .appendTo($ui);
82
83       $("<p>")
84         .text(OSM.i18n.t("javascripts.map.layers.overlays"))
85         .attr("class", "text-body-secondary small mb-2")
86         .appendTo(overlaySection);
87
88       const addOverlay = function (layer, name, maxArea) {
89         const item = $("<div>")
90           .attr("class", "form-check")
91           .appendTo(overlaySection);
92
93         if (name === "notes" || name === "data") {
94           item
95             .attr("title", OSM.i18n.t("javascripts.site.map_" + name + "_zoom_in_tooltip"))
96             .tooltip("disable");
97         }
98
99         const label = $("<label>")
100           .attr("class", "form-check-label")
101           .attr("id", `label-layers-${name}`)
102           .appendTo(item);
103
104         let checked = map.hasLayer(layer);
105
106         const input = $("<input>")
107           .attr("type", "checkbox")
108           .attr("class", "form-check-input")
109           .prop("checked", checked)
110           .appendTo(label);
111
112         label.append(" ", OSM.i18n.t("javascripts.map.layers." + name));
113
114         input.on("change", function () {
115           checked = input.is(":checked");
116           if (layer.cancelLoading) {
117             layer.cancelLoading();
118           }
119
120           if (checked) {
121             map.addLayer(layer);
122           } else {
123             map.removeLayer(layer);
124             $(`#layers-${name}-loading`).remove();
125           }
126         });
127
128         map.on("overlayadd overlayremove", function () {
129           input.prop("checked", map.hasLayer(layer));
130         });
131
132         map.on("zoomend", function () {
133           const disabled = map.getBounds().getSize() >= maxArea;
134           $(input).prop("disabled", disabled);
135
136           if (disabled && $(input).is(":checked")) {
137             $(input).prop("checked", false)
138               .trigger("change");
139             checked = true;
140           } else if (!disabled && !$(input).is(":checked") && checked) {
141             $(input).prop("checked", true)
142               .trigger("change");
143           }
144
145           $(item)
146             .toggleClass("disabled", disabled)
147             .tooltip(disabled ? "enable" : "disable");
148         });
149       };
150
151       addOverlay(map.noteLayer, "notes", OSM.MAX_NOTE_REQUEST_AREA);
152       addOverlay(map.dataLayer, "data", OSM.MAX_REQUEST_AREA);
153       addOverlay(map.gpsLayer, "gps", Number.POSITIVE_INFINITY);
154     }
155   };
156
157   return control;
158 };