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