mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:31:09 -05:00
- Implemented a new HTML template for the Road Trip Planner. - Integrated Leaflet.js for interactive mapping and routing. - Added functionality for searching and selecting parks to include in a trip. - Enabled drag-and-drop reordering of selected parks. - Included trip optimization and route calculation features. - Created a summary display for trip statistics. - Added functionality to save trips and manage saved trips. - Enhanced UI with responsive design and dark mode support.
196 lines
7.7 KiB
HTML
196 lines
7.7 KiB
HTML
<!-- Reusable Map Container Component -->
|
|
<div class="relative">
|
|
<div id="{{ map_id|default:'map-container' }}"
|
|
class="map-container {% if map_classes %}{{ map_classes }}{% endif %}"
|
|
style="{% if map_height %}height: {{ map_height }};{% endif %}">
|
|
</div>
|
|
|
|
<!-- Map Loading Indicator -->
|
|
<div id="{{ map_id|default:'map-container' }}-loading"
|
|
class="htmx-indicator absolute inset-0 flex items-center justify-center bg-gray-100 dark:bg-gray-800 rounded-lg">
|
|
<div class="text-center">
|
|
<div class="w-8 h-8 mx-auto mb-4 border-4 border-blue-500 rounded-full border-t-transparent animate-spin"></div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">
|
|
{{ loading_text|default:"Loading map data..." }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Map Controls Overlay -->
|
|
{% if show_controls %}
|
|
<div class="absolute top-4 right-4 z-10 space-y-2">
|
|
{% if show_fullscreen %}
|
|
<button id="{{ map_id|default:'map-container' }}-fullscreen"
|
|
class="p-2 bg-white dark:bg-gray-800 rounded-lg shadow-md hover:shadow-lg transition-shadow"
|
|
title="Toggle Fullscreen">
|
|
<i class="fas fa-expand text-gray-600 dark:text-gray-400"></i>
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if show_layers %}
|
|
<button id="{{ map_id|default:'map-container' }}-layers"
|
|
class="p-2 bg-white dark:bg-gray-800 rounded-lg shadow-md hover:shadow-lg transition-shadow"
|
|
title="Map Layers">
|
|
<i class="fas fa-layer-group text-gray-600 dark:text-gray-400"></i>
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if show_locate %}
|
|
<button id="{{ map_id|default:'map-container' }}-locate"
|
|
class="p-2 bg-white dark:bg-gray-800 rounded-lg shadow-md hover:shadow-lg transition-shadow"
|
|
title="Find My Location">
|
|
<i class="fas fa-crosshairs text-gray-600 dark:text-gray-400"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Map Legend -->
|
|
{% if show_legend %}
|
|
<div class="absolute bottom-4 left-4 z-10">
|
|
<div class="p-3 bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
|
<h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-2">Legend</h4>
|
|
<div class="space-y-1 text-xs">
|
|
{% if legend_items %}
|
|
{% for item in legend_items %}
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 mr-2 rounded-full" style="background-color: {{ item.color }};"></div>
|
|
<span class="text-gray-700 dark:text-gray-300">{{ item.label }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 mr-2 rounded-full bg-green-500"></div>
|
|
<span class="text-gray-700 dark:text-gray-300">Operating Parks</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 mr-2 rounded-full bg-blue-500"></div>
|
|
<span class="text-gray-700 dark:text-gray-300">Rides</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 mr-2 rounded-full bg-purple-500"></div>
|
|
<span class="text-gray-700 dark:text-gray-300">Companies</span>
|
|
</div>
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 mr-2 rounded-full bg-red-500"></div>
|
|
<span class="text-gray-700 dark:text-gray-300">Closed/Demolished</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Map Container Styles -->
|
|
<style>
|
|
.map-container {
|
|
height: {{ map_height|default:'60vh' }};
|
|
min-height: {{ min_height|default:'400px' }};
|
|
border-radius: 0.5rem;
|
|
overflow: hidden;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.map-container.fullscreen {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 9999;
|
|
border-radius: 0;
|
|
height: 100vh !important;
|
|
min-height: 100vh !important;
|
|
}
|
|
|
|
.map-container.fullscreen + .absolute {
|
|
z-index: 10000;
|
|
}
|
|
|
|
/* Dark mode adjustments */
|
|
.dark .map-container {
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
|
|
}
|
|
</style>
|
|
|
|
<!-- Map Container JavaScript -->
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const mapId = '{{ map_id|default:"map-container" }}';
|
|
const mapContainer = document.getElementById(mapId);
|
|
|
|
{% if show_fullscreen %}
|
|
// Fullscreen toggle
|
|
const fullscreenBtn = document.getElementById(mapId + '-fullscreen');
|
|
if (fullscreenBtn) {
|
|
fullscreenBtn.addEventListener('click', function() {
|
|
const icon = this.querySelector('i');
|
|
|
|
if (mapContainer.classList.contains('fullscreen')) {
|
|
mapContainer.classList.remove('fullscreen');
|
|
icon.className = 'fas fa-expand text-gray-600 dark:text-gray-400';
|
|
this.title = 'Toggle Fullscreen';
|
|
} else {
|
|
mapContainer.classList.add('fullscreen');
|
|
icon.className = 'fas fa-compress text-gray-600 dark:text-gray-400';
|
|
this.title = 'Exit Fullscreen';
|
|
}
|
|
|
|
// Trigger map resize if map instance exists
|
|
if (window[mapId + 'Instance']) {
|
|
setTimeout(() => {
|
|
window[mapId + 'Instance'].invalidateSize();
|
|
}, 100);
|
|
}
|
|
});
|
|
}
|
|
{% endif %}
|
|
|
|
{% if show_locate %}
|
|
// Geolocation
|
|
const locateBtn = document.getElementById(mapId + '-locate');
|
|
if (locateBtn && navigator.geolocation) {
|
|
locateBtn.addEventListener('click', function() {
|
|
const icon = this.querySelector('i');
|
|
icon.className = 'fas fa-spinner fa-spin text-gray-600 dark:text-gray-400';
|
|
|
|
navigator.geolocation.getCurrentPosition(
|
|
function(position) {
|
|
icon.className = 'fas fa-crosshairs text-gray-600 dark:text-gray-400';
|
|
|
|
// Trigger custom event with user location
|
|
const event = new CustomEvent('userLocationFound', {
|
|
detail: {
|
|
lat: position.coords.latitude,
|
|
lng: position.coords.longitude,
|
|
accuracy: position.coords.accuracy
|
|
}
|
|
});
|
|
mapContainer.dispatchEvent(event);
|
|
},
|
|
function(error) {
|
|
icon.className = 'fas fa-crosshairs text-red-500';
|
|
console.error('Geolocation error:', error);
|
|
|
|
// Reset icon after delay
|
|
setTimeout(() => {
|
|
icon.className = 'fas fa-crosshairs text-gray-600 dark:text-gray-400';
|
|
}, 2000);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
{% endif %}
|
|
|
|
// Escape key handler for fullscreen
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape' && mapContainer.classList.contains('fullscreen')) {
|
|
const fullscreenBtn = document.getElementById(mapId + '-fullscreen');
|
|
if (fullscreenBtn) {
|
|
fullscreenBtn.click();
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script> |