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