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