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