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.scrollTo(sidebar.children(":first")); 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