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