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