xref: /webtrees/resources/views/modules/place-hierarchy/map.phtml (revision b2e5f20ea75b7378d8949eac2d8ec43fb9552b6f)
1<?php
2
3declare(strict_types=1);
4
5use Fisharebest\Webtrees\View;
6
7/**
8 * @var array<mixed> $data
9 * @var object       $leaflet_config
10 */
11
12?>
13
14<div class="row gchart wt-place-hierarchy-wrapper">
15    <div id="wt-map" class="col-sm-9 wt-ajax-load wt-map wt-place-hierarchy-map" dir="ltr"></div>
16    <ul class="col-sm-3 wt-place-hierarchy-sidebar wt-page-options-value list-unstyled px-md-1"></ul>
17</div>
18
19<?php View::push('javascript') ?>
20<script>
21  'use strict';
22
23  (function () {
24    const config = <?= json_encode($leaflet_config, JSON_THROW_ON_ERROR) ?>;
25
26    let map = null;
27    const sidebar = document.querySelector('.wt-place-hierarchy-sidebar');
28
29    const scrollOptions = {
30      behavior: "smooth",
31      block: "start",
32      inline: "start"
33    };
34
35    // Map components
36    let markers = L.markerClusterGroup({
37      showCoverageOnHover: false,
38    });
39
40    /**
41     * Passed to resetControl to
42     * perform necessary reset actions on map
43     *
44     * @param {Event} event
45     */
46    let resetCallback = function (event) {
47      event.preventDefault();
48      map.flyToBounds(markers.getBounds(), { padding: [50, 30], maxZoom: 15 });
49      sidebar.firstElementChild.scrollIntoView(scrollOptions);
50    };
51
52    /**
53     * @private
54     */
55    let _drawMap = function () {
56      map = webtrees.buildLeafletJsMap('wt-map', config, resetCallback);
57    };
58
59    /**
60     * @private
61     */
62    let _buildMapData = function () {
63      let data = <?= json_encode($data['markers'], JSON_THROW_ON_ERROR) ?>;
64
65      let geoJsonLayer = L.geoJson(data, {
66        pointToLayer: function (feature, latlng) {
67          return new L.Marker(latlng, {
68            icon: L.BeautifyIcon.icon({
69              icon: 'bullseye fas',
70              borderColor: 'transparent',
71              backgroundColor: '#1e90ff',
72              iconShape: 'marker',
73              textColor: 'white',
74            }),
75            title: feature.properties.tooltip,
76            id: feature.id,
77          })
78            .on('popupopen', function (e) {
79              let item = document.querySelector('.mapped[data-wt-feature-id="' + e.target.feature.id + '"]');
80              item.classList.add('messagebox');
81              item.scrollIntoView(scrollOptions);
82            })
83            .on('popupclose', function () {
84              sidebar.querySelectorAll('.mapped').forEach(e => e.classList.remove('messagebox'));
85              sidebar.firstElementChild.scrollIntoView(scrollOptions);
86            });
87        },
88        onEachFeature: function (feature, layer) {
89          layer.bindPopup(feature.properties.popup);
90        },
91      });
92
93      if (data.features.length > 0) {
94        markers.addLayer(geoJsonLayer);
95        map.addLayer(markers);
96      }
97
98      map.fitBounds(<?= json_encode($data['bounds'], JSON_THROW_ON_ERROR) ?>, { padding: [50, 30] });
99      sidebar.innerHTML = <?= json_encode($data['sidebar'], JSON_THROW_ON_ERROR) ?>;
100   };
101
102    window.onload = function() {
103      // Activate marker popup when sidebar entry clicked
104      sidebar.querySelectorAll('.mapped').forEach((element) => {
105        var eventId = parseInt(element.dataset.wtFeatureId);
106
107        element.addEventListener('click', () => {
108          // first close any existing
109          map.closePopup();
110          //find the marker corresponding to the clicked event
111          let mkrLayer = markers.getLayers().filter(function (v) {
112            return v.feature !== undefined && v.feature.id === eventId;
113          });
114
115          let mkr = mkrLayer.pop();
116
117          markers.zoomToShowLayer(mkr, function (e) {
118            mkr.openPopup();
119          });
120
121          return false;
122        });
123
124        // stop clicking on a person also opening the popup
125        element.querySelectorAll('a').forEach((el) => {
126          el.addEventListener('click', (e) => {
127            e.stopPropagation();
128          });
129        });
130      });
131    }
132
133    _drawMap();
134    _buildMapData();
135  })();
136</script>
137<?php View::endpush() ?>
138