mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 05:31:10 -05:00
190 lines
6.0 KiB
PHP
190 lines
6.0 KiB
PHP
@push('styles')
|
|
<style>
|
|
/* Ensure map container and its elements stay below other UI elements */
|
|
.leaflet-pane,
|
|
.leaflet-tile,
|
|
.leaflet-marker-icon,
|
|
.leaflet-marker-shadow,
|
|
.leaflet-tile-container,
|
|
.leaflet-pane > svg,
|
|
.leaflet-pane > canvas,
|
|
.leaflet-zoom-box,
|
|
.leaflet-image-layer,
|
|
.leaflet-layer {
|
|
z-index: 1 !important;
|
|
}
|
|
.leaflet-control {
|
|
z-index: 2 !important;
|
|
}
|
|
|
|
/* Custom marker cluster styling */
|
|
.marker-cluster {
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
border-radius: 50%;
|
|
text-align: center;
|
|
color: #333;
|
|
border: 2px solid rgba(0, 120, 255, 0.5);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.marker-cluster div {
|
|
width: 30px;
|
|
height: 30px;
|
|
margin-left: 5px;
|
|
margin-top: 5px;
|
|
padding-top: 5px;
|
|
background-color: rgba(0, 120, 255, 0.6);
|
|
border-radius: 50%;
|
|
}
|
|
|
|
/* Custom info window styling */
|
|
.location-info-window {
|
|
padding: 10px;
|
|
background: white;
|
|
border-radius: 6px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
max-width: 300px;
|
|
}
|
|
|
|
.location-info-window h3 {
|
|
margin-top: 0;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
<div class="location-display-component">
|
|
<div wire:ignore class="relative mb-4" style="z-index: 1;">
|
|
<div id="locationMap" class="h-[400px] w-full rounded-lg border border-gray-300 dark:border-gray-600"></div>
|
|
|
|
@if($showInfoWindow && $activeMarker)
|
|
<div class="location-info-window absolute top-4 right-4 z-10">
|
|
<div class="flex justify-between items-start">
|
|
<h3 class="text-lg font-semibold">{{ $activeMarker['name'] ?? 'Location' }}</h3>
|
|
<button wire:click="closeInfoWindow" class="text-gray-500 hover:text-gray-700">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="mt-2">
|
|
@if(isset($activeMarker['address']))
|
|
<p class="text-sm text-gray-600">
|
|
{{ $activeMarker['address']['street'] ?? '' }}
|
|
{{ $activeMarker['address']['city'] ?? '' }}
|
|
{{ $activeMarker['address']['state'] ?? '' }}
|
|
{{ $activeMarker['address']['country'] ?? '' }}
|
|
</p>
|
|
@endif
|
|
@if(isset($activeMarker['description']))
|
|
<p class="mt-2 text-sm">{{ $activeMarker['description'] }}</p>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
@push('scripts')
|
|
<script>
|
|
document.addEventListener('livewire:initialized', function () {
|
|
const map = L.map('locationMap');
|
|
let markerClusterGroup;
|
|
let markers = {};
|
|
|
|
// Initialize map with OSM tiles
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap contributors'
|
|
}).addTo(map);
|
|
|
|
// Initialize marker cluster group
|
|
markerClusterGroup = L.markerClusterGroup({
|
|
maxClusterRadius: @entangle('clusterRadius'),
|
|
spiderfyOnMaxZoom: true,
|
|
showCoverageOnHover: false,
|
|
zoomToBoundsOnClick: true,
|
|
removeOutsideVisibleBounds: true,
|
|
iconCreateFunction: function(cluster) {
|
|
const count = cluster.getChildCount();
|
|
return L.divIcon({
|
|
html: `<div><span>${count}</span></div>`,
|
|
className: 'marker-cluster',
|
|
iconSize: L.point(40, 40)
|
|
});
|
|
}
|
|
});
|
|
|
|
map.addLayer(markerClusterGroup);
|
|
|
|
// Listen for marker updates from Livewire
|
|
@this.on('updateMarkers', (markerData) => {
|
|
// Clear existing markers
|
|
markerClusterGroup.clearLayers();
|
|
markers = {};
|
|
|
|
// Add new markers
|
|
markerData.forEach(marker => {
|
|
const leafletMarker = L.marker([marker.lat, marker.lng], {
|
|
icon: getMarkerIcon(marker.category)
|
|
});
|
|
|
|
leafletMarker.on('click', () => {
|
|
@this.markerClicked(marker.id);
|
|
});
|
|
|
|
markers[marker.id] = leafletMarker;
|
|
markerClusterGroup.addLayer(leafletMarker);
|
|
});
|
|
|
|
// Fit bounds if available
|
|
if (@this.bounds) {
|
|
map.fitBounds([
|
|
[@this.bounds.south, @this.bounds.west],
|
|
[@this.bounds.north, @this.bounds.east]
|
|
]);
|
|
}
|
|
});
|
|
|
|
// Handle cluster clicks
|
|
markerClusterGroup.on('clusterclick', (e) => {
|
|
const clusterMarkers = e.layer.getAllChildMarkers().map(marker => {
|
|
const markerId = Object.keys(markers).find(key => markers[key] === marker);
|
|
return { id: markerId, lat: marker.getLatLng().lat, lng: marker.getLatLng().lng };
|
|
});
|
|
@this.clusterClicked(clusterMarkers);
|
|
});
|
|
|
|
// Handle map bounds changes
|
|
map.on('moveend', () => {
|
|
const bounds = map.getBounds();
|
|
@this.boundsChanged({
|
|
north: bounds.getNorth(),
|
|
south: bounds.getSouth(),
|
|
east: bounds.getEast(),
|
|
west: bounds.getWest()
|
|
});
|
|
});
|
|
|
|
// Initialize markers
|
|
@this.getMarkers().then(markerData => {
|
|
@this.emit('updateMarkers', markerData);
|
|
});
|
|
|
|
// Helper function to get marker icon based on category
|
|
function getMarkerIcon(category) {
|
|
// Customize icons based on category
|
|
return L.divIcon({
|
|
className: `marker-icon marker-${category || 'default'}`,
|
|
html: `<div class="w-8 h-8 rounded-full bg-blue-500 border-2 border-white shadow-lg flex items-center justify-center">
|
|
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/>
|
|
</svg>
|
|
</div>`,
|
|
iconSize: [32, 32],
|
|
iconAnchor: [16, 32]
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
@endpush |