xref: /webtrees/resources/views/modules/places/tab.phtml (revision 46ee52088e7429650f7ae3b1b0b4fe2c9d275437)
1<?php
2
3use Fisharebest\Webtrees\I18N;
4
5/**
6 * @var array<mixed> $data
7 * @var object       $leaflet_config
8 */
9
10?>
11
12<div class="py-4">
13    <div class="row gchart osm-wrapper">
14        <div id="osm-map" class="col-sm-9 wt-ajax-load osm-user-map" dir="ltr"></div>
15        <ul class="col-sm-3 osm-sidebar wt-page-options-value list-unstyled px-md-1"></ul>
16    </div>
17</div>
18
19<style>
20    .osm-wrapper, .osm-user-map {
21        height: 70vh
22    }
23
24    .osm-sidebar {
25        height: 100%;
26        overflow-y: auto;
27        font-size: small;
28    }
29</style>
30
31<script>
32    "use strict";
33
34    window.WT_OSM = (function() {
35        const config = <?= json_encode($leaflet_config) ?>;
36
37        let map       = null;
38        let sidebar   = $('.osm-sidebar');
39
40        // Map components
41        let markers = L.markerClusterGroup({
42            showCoverageOnHover: false,
43        });
44
45        let resetControl = L.Control.extend({
46            options: {
47                position: "topleft",
48            },
49            onAdd: function(map) {
50                let container = L.DomUtil.create("div", "leaflet-bar leaflet-control leaflet-control-custom");
51                container.onclick = function() {
52                    map.flyToBounds(markers.getBounds(), {padding: [50, 30], maxZoom: 15});
53                    sidebar.scrollTo(sidebar.children(":first"));
54
55                    return false;
56                };
57                let reset    = config.i18n.reset;
58                let anchor   = L.DomUtil.create('a', 'leaflet-control-reset', container);
59                anchor.setAttribute('aria-label', reset);
60                anchor.href  = '#';
61                anchor.title = reset;
62                anchor.role  = 'button';
63                let image    = L.DomUtil.create('i', 'fas fa-redo', anchor);
64                image.alt    = reset;
65
66                return container;
67            },
68        });
69
70        /**
71         *
72         * @private
73         */
74        let _drawMap = function() {
75            map = webtrees.buildLeafletJsMap('osm-map', config)
76                .addControl(new resetControl());
77        };
78
79        /**
80         *
81         * @private
82         */
83        let _buildMapData = function() {
84            let sidebar_content = "";
85            let data            = <?= json_encode($data) ?>;
86
87            if (data.features.length === 0) {
88                map.fitWorld();
89                sidebar_content += '<div class="bg-info text-white text-center">' + <?= json_encode(I18N::translate('Nothing to show')) ?> + '</div>';
90            } else {
91                let geoJsonLayer = L.geoJson(data, {
92                    pointToLayer: function(feature, latlng) {
93                        return new L.Marker(latlng, {
94                            icon: L.BeautifyIcon.icon({
95                                icon           : feature.properties.icon["name"],
96                                borderColor    : "transparent",
97                                backgroundColor: feature.properties.icon["color"],
98                                iconShape      : "marker",
99                                textColor      : "white",
100                            }),
101                            title: feature.properties.tooltip,
102                            alt  : feature.properties.tooltip,
103                            id   : feature.id,
104                        })
105                        .on("popupopen", function(e) {
106                            let item = sidebar.children(".gchart[data-id=" + e.target.feature.id + "]");
107                            item.addClass("messagebox");
108                            sidebar.scrollTo(item);
109                        })
110                        .on("popupclose", function() {
111                            sidebar.children(".gchart")
112                                .removeClass("messagebox");
113                        });
114                    },
115                    onEachFeature: function(feature, layer) {
116                        layer.bindPopup(feature.properties.summary);
117                        sidebar_content += `<li class="gchart px-md-2" data-id=${feature.id}>${feature.properties.summary}</li>`;
118                    },
119                });
120                markers.addLayer(geoJsonLayer);
121                map.addLayer(markers);
122                map.fitBounds(markers.getBounds(), {padding: [50, 30], maxZoom: 15});
123            }
124            sidebar.append(sidebar_content);
125        };
126
127        /**
128         * @param   elem
129         * @returns {$}
130         */
131        $.fn.scrollTo = function(elem) {
132            let _this = $(this);
133            _this.animate({
134                scrollTop: elem.offset().top - _this.offset().top + _this.scrollTop(),
135            });
136            return this;
137        };
138
139        // Activate marker popup when sidebar entry clicked
140        $(function() {
141            sidebar
142            // open marker popup if sidebar event is clicked
143            .on('click', '.gchart', function(e) {
144                // first close any existing
145                map.closePopup();
146                let eventId = $(this).data('id');
147                //find the marker corresponding to the clicked event
148                let mkrLayer = markers.getLayers().filter(function(v) {
149                    return typeof(v.feature) !== 'undefined' && v.feature.id === eventId;
150                });
151                let mkr = mkrLayer.pop();
152                // Unfortunately zoomToShowLayer zooms to maxZoom
153                // when all marker in a cluster have exactly the
154                // same co-ordinates
155                markers.zoomToShowLayer(mkr, function(e) {
156                    mkr.openPopup();
157                });
158
159                return false;
160            })
161            .on('click', 'a', function(e) { // stop click on a person also opening the popup
162                e.stopPropagation();
163            });
164        });
165
166        _drawMap();
167        _buildMapData();
168
169        return "Leaflet map interface for webtrees-2";
170    })();
171</script>
172