mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:11:09 -05:00
Add park and ride card components with advanced search functionality
- Implemented park card component with image, status badge, favorite button, and quick stats overlay. - Developed ride card component featuring thrill level badge, status badge, favorite button, and detailed stats. - Created advanced search page with filters for parks and rides, including location, type, status, and thrill level. - Added dynamic quick search functionality with results display. - Enhanced user experience with JavaScript for filter toggling, range slider updates, and view switching. - Included custom CSS for improved styling of checkboxes and search results layout.
This commit is contained in:
725
static/js/backup/htmx-maps.js
Normal file
725
static/js/backup/htmx-maps.js
Normal file
@@ -0,0 +1,725 @@
|
||||
/**
|
||||
* ThrillWiki HTMX Maps Integration - Dynamic Map Updates via HTMX
|
||||
*
|
||||
* This module handles HTMX events for map updates, manages loading states
|
||||
* during API calls, updates map content based on HTMX responses, and provides
|
||||
* error handling for failed requests
|
||||
*/
|
||||
|
||||
class HTMXMapIntegration {
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
mapInstance: null,
|
||||
filterInstance: null,
|
||||
defaultTarget: '#map-container',
|
||||
loadingClass: 'htmx-loading',
|
||||
errorClass: 'htmx-error',
|
||||
successClass: 'htmx-success',
|
||||
loadingTimeout: 30000, // 30 seconds
|
||||
retryAttempts: 3,
|
||||
retryDelay: 1000,
|
||||
...options
|
||||
};
|
||||
|
||||
this.loadingElements = new Set();
|
||||
this.activeRequests = new Map();
|
||||
this.requestQueue = [];
|
||||
this.retryCount = new Map();
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize HTMX integration
|
||||
*/
|
||||
init() {
|
||||
if (typeof htmx === 'undefined') {
|
||||
console.warn('HTMX not found, map integration disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupEventHandlers();
|
||||
this.setupCustomEvents();
|
||||
this.setupErrorHandling();
|
||||
this.enhanceExistingElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup HTMX event handlers
|
||||
*/
|
||||
setupEventHandlers() {
|
||||
// Before request - show loading states
|
||||
document.addEventListener('htmx:beforeRequest', (e) => {
|
||||
this.handleBeforeRequest(e);
|
||||
});
|
||||
|
||||
// After request - handle response and update maps
|
||||
document.addEventListener('htmx:afterRequest', (e) => {
|
||||
this.handleAfterRequest(e);
|
||||
});
|
||||
|
||||
// Response error - handle failed requests
|
||||
document.addEventListener('htmx:responseError', (e) => {
|
||||
this.handleResponseError(e);
|
||||
});
|
||||
|
||||
// Send error - handle network errors
|
||||
document.addEventListener('htmx:sendError', (e) => {
|
||||
this.handleSendError(e);
|
||||
});
|
||||
|
||||
// Timeout - handle request timeouts
|
||||
document.addEventListener('htmx:timeout', (e) => {
|
||||
this.handleTimeout(e);
|
||||
});
|
||||
|
||||
// Before swap - prepare for content updates
|
||||
document.addEventListener('htmx:beforeSwap', (e) => {
|
||||
this.handleBeforeSwap(e);
|
||||
});
|
||||
|
||||
// After swap - update maps with new content
|
||||
document.addEventListener('htmx:afterSwap', (e) => {
|
||||
this.handleAfterSwap(e);
|
||||
});
|
||||
|
||||
// Config request - modify requests before sending
|
||||
document.addEventListener('htmx:configRequest', (e) => {
|
||||
this.handleConfigRequest(e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup custom map-specific events
|
||||
*/
|
||||
setupCustomEvents() {
|
||||
// Custom event for map data updates
|
||||
document.addEventListener('map:dataUpdate', (e) => {
|
||||
this.handleMapDataUpdate(e);
|
||||
});
|
||||
|
||||
// Custom event for filter changes
|
||||
document.addEventListener('filter:changed', (e) => {
|
||||
this.handleFilterChange(e);
|
||||
});
|
||||
|
||||
// Custom event for search updates
|
||||
document.addEventListener('search:results', (e) => {
|
||||
this.handleSearchResults(e);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup global error handling
|
||||
*/
|
||||
setupErrorHandling() {
|
||||
// Global error handler
|
||||
window.addEventListener('error', (e) => {
|
||||
if (e.filename && e.filename.includes('htmx')) {
|
||||
console.error('HTMX error:', e.error);
|
||||
this.showErrorMessage('An error occurred while updating the map');
|
||||
}
|
||||
});
|
||||
|
||||
// Unhandled promise rejection handler
|
||||
window.addEventListener('unhandledrejection', (e) => {
|
||||
if (e.reason && e.reason.toString().includes('htmx')) {
|
||||
console.error('HTMX promise rejection:', e.reason);
|
||||
this.showErrorMessage('Failed to complete map request');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhance existing elements with HTMX map functionality
|
||||
*/
|
||||
enhanceExistingElements() {
|
||||
// Add map-specific attributes to filter forms
|
||||
const filterForms = document.querySelectorAll('[data-map-filter]');
|
||||
filterForms.forEach(form => {
|
||||
if (!form.hasAttribute('hx-get')) {
|
||||
form.setAttribute('hx-get', form.getAttribute('data-map-filter'));
|
||||
form.setAttribute('hx-trigger', 'change, submit');
|
||||
form.setAttribute('hx-target', '#map-container');
|
||||
form.setAttribute('hx-swap', 'none');
|
||||
}
|
||||
});
|
||||
|
||||
// Add map update attributes to search inputs
|
||||
const searchInputs = document.querySelectorAll('[data-map-search]');
|
||||
searchInputs.forEach(input => {
|
||||
if (!input.hasAttribute('hx-get')) {
|
||||
input.setAttribute('hx-get', input.getAttribute('data-map-search'));
|
||||
input.setAttribute('hx-trigger', 'input changed delay:500ms');
|
||||
input.setAttribute('hx-target', '#search-results');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle before request event
|
||||
*/
|
||||
handleBeforeRequest(e) {
|
||||
const element = e.target;
|
||||
const requestId = this.generateRequestId();
|
||||
|
||||
// Store request information
|
||||
this.activeRequests.set(requestId, {
|
||||
element: element,
|
||||
startTime: Date.now(),
|
||||
url: e.detail.requestConfig.path
|
||||
});
|
||||
|
||||
// Show loading state
|
||||
this.showLoadingState(element, true);
|
||||
|
||||
// Add request ID to detail for tracking
|
||||
e.detail.requestId = requestId;
|
||||
|
||||
// Set timeout
|
||||
setTimeout(() => {
|
||||
if (this.activeRequests.has(requestId)) {
|
||||
this.handleTimeout({ detail: { requestId } });
|
||||
}
|
||||
}, this.options.loadingTimeout);
|
||||
|
||||
console.log('HTMX request started:', e.detail.requestConfig.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle after request event
|
||||
*/
|
||||
handleAfterRequest(e) {
|
||||
const requestId = e.detail.requestId;
|
||||
const request = this.activeRequests.get(requestId);
|
||||
|
||||
if (request) {
|
||||
const duration = Date.now() - request.startTime;
|
||||
console.log(`HTMX request completed in ${duration}ms:`, request.url);
|
||||
|
||||
this.activeRequests.delete(requestId);
|
||||
this.showLoadingState(request.element, false);
|
||||
}
|
||||
|
||||
if (e.detail.successful) {
|
||||
this.handleSuccessfulResponse(e);
|
||||
} else {
|
||||
this.handleFailedResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle successful response
|
||||
*/
|
||||
handleSuccessfulResponse(e) {
|
||||
const element = e.target;
|
||||
|
||||
// Add success class temporarily
|
||||
element.classList.add(this.options.successClass);
|
||||
setTimeout(() => {
|
||||
element.classList.remove(this.options.successClass);
|
||||
}, 2000);
|
||||
|
||||
// Reset retry count
|
||||
this.retryCount.delete(element);
|
||||
|
||||
// Check if this is a map-related request
|
||||
if (this.isMapRequest(e)) {
|
||||
this.updateMapFromResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle failed response
|
||||
*/
|
||||
handleFailedResponse(e) {
|
||||
const element = e.target;
|
||||
|
||||
// Add error class
|
||||
element.classList.add(this.options.errorClass);
|
||||
setTimeout(() => {
|
||||
element.classList.remove(this.options.errorClass);
|
||||
}, 5000);
|
||||
|
||||
// Check if we should retry
|
||||
if (this.shouldRetry(element)) {
|
||||
this.scheduleRetry(element, e.detail);
|
||||
} else {
|
||||
this.showErrorMessage('Failed to update map data');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle response error
|
||||
*/
|
||||
handleResponseError(e) {
|
||||
console.error('HTMX response error:', e.detail);
|
||||
|
||||
const element = e.target;
|
||||
const status = e.detail.xhr.status;
|
||||
|
||||
let message = 'An error occurred while updating the map';
|
||||
|
||||
switch (status) {
|
||||
case 400:
|
||||
message = 'Invalid request parameters';
|
||||
break;
|
||||
case 401:
|
||||
message = 'Authentication required';
|
||||
break;
|
||||
case 403:
|
||||
message = 'Access denied';
|
||||
break;
|
||||
case 404:
|
||||
message = 'Map data not found';
|
||||
break;
|
||||
case 429:
|
||||
message = 'Too many requests. Please wait a moment.';
|
||||
break;
|
||||
case 500:
|
||||
message = 'Server error. Please try again later.';
|
||||
break;
|
||||
}
|
||||
|
||||
this.showErrorMessage(message);
|
||||
this.showLoadingState(element, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle send error
|
||||
*/
|
||||
handleSendError(e) {
|
||||
console.error('HTMX send error:', e.detail);
|
||||
this.showErrorMessage('Network error. Please check your connection.');
|
||||
this.showLoadingState(e.target, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle timeout
|
||||
*/
|
||||
handleTimeout(e) {
|
||||
console.warn('HTMX request timeout');
|
||||
|
||||
if (e.detail.requestId) {
|
||||
const request = this.activeRequests.get(e.detail.requestId);
|
||||
if (request) {
|
||||
this.showLoadingState(request.element, false);
|
||||
this.activeRequests.delete(e.detail.requestId);
|
||||
}
|
||||
}
|
||||
|
||||
this.showErrorMessage('Request timed out. Please try again.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle before swap
|
||||
*/
|
||||
handleBeforeSwap(e) {
|
||||
// Prepare map for content update
|
||||
if (this.isMapRequest(e)) {
|
||||
console.log('Preparing map for content swap');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle after swap
|
||||
*/
|
||||
handleAfterSwap(e) {
|
||||
// Re-initialize any new HTMX elements
|
||||
this.enhanceExistingElements();
|
||||
|
||||
// Update maps if needed
|
||||
if (this.isMapRequest(e)) {
|
||||
this.reinitializeMapComponents();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle config request
|
||||
*/
|
||||
handleConfigRequest(e) {
|
||||
const config = e.detail;
|
||||
|
||||
// Add CSRF token if available
|
||||
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]');
|
||||
if (csrfToken && (config.verb === 'post' || config.verb === 'put' || config.verb === 'patch')) {
|
||||
config.headers['X-CSRFToken'] = csrfToken.value;
|
||||
}
|
||||
|
||||
// Add map-specific headers
|
||||
if (this.isMapRequest(e)) {
|
||||
config.headers['X-Map-Request'] = 'true';
|
||||
|
||||
// Add current map bounds if available
|
||||
if (this.options.mapInstance) {
|
||||
const bounds = this.options.mapInstance.getBounds();
|
||||
if (bounds) {
|
||||
config.headers['X-Map-Bounds'] = JSON.stringify({
|
||||
north: bounds.getNorth(),
|
||||
south: bounds.getSouth(),
|
||||
east: bounds.getEast(),
|
||||
west: bounds.getWest(),
|
||||
zoom: this.options.mapInstance.getZoom()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle map data updates
|
||||
*/
|
||||
handleMapDataUpdate(e) {
|
||||
if (this.options.mapInstance) {
|
||||
const data = e.detail;
|
||||
this.options.mapInstance.updateMarkers(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle filter changes
|
||||
*/
|
||||
handleFilterChange(e) {
|
||||
if (this.options.filterInstance) {
|
||||
const filters = e.detail;
|
||||
|
||||
// Trigger HTMX request for filter update
|
||||
const filterForm = document.getElementById('map-filters');
|
||||
if (filterForm && filterForm.hasAttribute('hx-get')) {
|
||||
htmx.trigger(filterForm, 'change');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle search results
|
||||
*/
|
||||
handleSearchResults(e) {
|
||||
const results = e.detail;
|
||||
|
||||
// Update map with search results if applicable
|
||||
if (results.locations && this.options.mapInstance) {
|
||||
this.options.mapInstance.updateMarkers({ locations: results.locations });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/hide loading state
|
||||
*/
|
||||
showLoadingState(element, show) {
|
||||
if (show) {
|
||||
element.classList.add(this.options.loadingClass);
|
||||
this.loadingElements.add(element);
|
||||
|
||||
// Show loading indicators
|
||||
const indicators = element.querySelectorAll('.htmx-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
indicator.style.display = 'block';
|
||||
});
|
||||
|
||||
// Disable form elements
|
||||
const inputs = element.querySelectorAll('input, button, select');
|
||||
inputs.forEach(input => {
|
||||
input.disabled = true;
|
||||
});
|
||||
} else {
|
||||
element.classList.remove(this.options.loadingClass);
|
||||
this.loadingElements.delete(element);
|
||||
|
||||
// Hide loading indicators
|
||||
const indicators = element.querySelectorAll('.htmx-indicator');
|
||||
indicators.forEach(indicator => {
|
||||
indicator.style.display = 'none';
|
||||
});
|
||||
|
||||
// Re-enable form elements
|
||||
const inputs = element.querySelectorAll('input, button, select');
|
||||
inputs.forEach(input => {
|
||||
input.disabled = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if request is map-related
|
||||
*/
|
||||
isMapRequest(e) {
|
||||
const element = e.target;
|
||||
const url = e.detail.requestConfig ? e.detail.requestConfig.path : '';
|
||||
|
||||
return element.hasAttribute('data-map-filter') ||
|
||||
element.hasAttribute('data-map-search') ||
|
||||
element.closest('[data-map-target]') ||
|
||||
url.includes('/api/map/') ||
|
||||
url.includes('/maps/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update map from HTMX response
|
||||
*/
|
||||
updateMapFromResponse(e) {
|
||||
if (!this.options.mapInstance) return;
|
||||
|
||||
try {
|
||||
// Try to extract map data from response
|
||||
const responseText = e.detail.xhr.responseText;
|
||||
|
||||
// If response is JSON, update map directly
|
||||
try {
|
||||
const data = JSON.parse(responseText);
|
||||
if (data.status === 'success' && data.data) {
|
||||
this.options.mapInstance.updateMarkers(data.data);
|
||||
}
|
||||
} catch (jsonError) {
|
||||
// If not JSON, look for data attributes in HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = responseText;
|
||||
|
||||
const mapData = tempDiv.querySelector('[data-map-data]');
|
||||
if (mapData) {
|
||||
const data = JSON.parse(mapData.getAttribute('data-map-data'));
|
||||
this.options.mapInstance.updateMarkers(data);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update map from response:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element should be retried
|
||||
*/
|
||||
shouldRetry(element) {
|
||||
const retryCount = this.retryCount.get(element) || 0;
|
||||
return retryCount < this.options.retryAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule retry for failed request
|
||||
*/
|
||||
scheduleRetry(element, detail) {
|
||||
const retryCount = (this.retryCount.get(element) || 0) + 1;
|
||||
this.retryCount.set(element, retryCount);
|
||||
|
||||
const delay = this.options.retryDelay * Math.pow(2, retryCount - 1); // Exponential backoff
|
||||
|
||||
setTimeout(() => {
|
||||
console.log(`Retrying HTMX request (attempt ${retryCount})`);
|
||||
htmx.trigger(element, 'retry');
|
||||
}, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message to user
|
||||
*/
|
||||
showErrorMessage(message) {
|
||||
// Create or update error message element
|
||||
let errorEl = document.getElementById('htmx-error-message');
|
||||
|
||||
if (!errorEl) {
|
||||
errorEl = document.createElement('div');
|
||||
errorEl.id = 'htmx-error-message';
|
||||
errorEl.className = 'htmx-error-message';
|
||||
|
||||
// Insert at top of page
|
||||
document.body.insertBefore(errorEl, document.body.firstChild);
|
||||
}
|
||||
|
||||
errorEl.innerHTML = `
|
||||
<div class="error-content">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
<span>${message}</span>
|
||||
<button onclick="this.parentElement.parentElement.remove()" class="error-close">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
errorEl.style.display = 'block';
|
||||
|
||||
// Auto-hide after 10 seconds
|
||||
setTimeout(() => {
|
||||
if (errorEl.parentNode) {
|
||||
errorEl.remove();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize map components after content swap
|
||||
*/
|
||||
reinitializeMapComponents() {
|
||||
// Reinitialize filter components
|
||||
if (this.options.filterInstance) {
|
||||
this.options.filterInstance.init();
|
||||
}
|
||||
|
||||
// Reinitialize any new map containers
|
||||
const newMapContainers = document.querySelectorAll('[data-map="auto"]:not([data-initialized])');
|
||||
newMapContainers.forEach(container => {
|
||||
container.setAttribute('data-initialized', 'true');
|
||||
// Initialize new map instance if needed
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique request ID
|
||||
*/
|
||||
generateRequestId() {
|
||||
return `htmx-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to map instance
|
||||
*/
|
||||
connectToMap(mapInstance) {
|
||||
this.options.mapInstance = mapInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to filter instance
|
||||
*/
|
||||
connectToFilter(filterInstance) {
|
||||
this.options.filterInstance = filterInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active request count
|
||||
*/
|
||||
getActiveRequestCount() {
|
||||
return this.activeRequests.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all active requests
|
||||
*/
|
||||
cancelAllRequests() {
|
||||
this.activeRequests.forEach((request, id) => {
|
||||
this.showLoadingState(request.element, false);
|
||||
});
|
||||
this.activeRequests.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loading elements
|
||||
*/
|
||||
getLoadingElements() {
|
||||
return Array.from(this.loadingElements);
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-initialize HTMX integration
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
window.htmxMapIntegration = new HTMXMapIntegration();
|
||||
|
||||
// Connect to existing instances
|
||||
if (window.thrillwikiMap) {
|
||||
window.htmxMapIntegration.connectToMap(window.thrillwikiMap);
|
||||
}
|
||||
|
||||
if (window.mapFilters) {
|
||||
window.htmxMapIntegration.connectToFilter(window.mapFilters);
|
||||
}
|
||||
});
|
||||
|
||||
// Add styles for HTMX integration
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (document.getElementById('htmx-map-styles')) return;
|
||||
|
||||
const styles = `
|
||||
<style id="htmx-map-styles">
|
||||
.htmx-loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.htmx-error {
|
||||
border-color: #EF4444;
|
||||
background-color: #FEE2E2;
|
||||
}
|
||||
|
||||
.htmx-success {
|
||||
border-color: #10B981;
|
||||
background-color: #D1FAE5;
|
||||
}
|
||||
|
||||
.htmx-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.htmx-error-message {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 10000;
|
||||
max-width: 400px;
|
||||
background: #FEE2E2;
|
||||
border: 1px solid #FCA5A5;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
||||
animation: slideInRight 0.3s ease;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
color: #991B1B;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.error-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #991B1B;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.error-close:hover {
|
||||
color: #7F1D1D;
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode styles */
|
||||
.dark .htmx-error-message {
|
||||
background: #7F1D1D;
|
||||
border-color: #991B1B;
|
||||
}
|
||||
|
||||
.dark .error-content {
|
||||
color: #FCA5A5;
|
||||
}
|
||||
|
||||
.dark .error-close {
|
||||
color: #FCA5A5;
|
||||
}
|
||||
|
||||
.dark .error-close:hover {
|
||||
color: #F87171;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
document.head.insertAdjacentHTML('beforeend', styles);
|
||||
});
|
||||
|
||||
// Export for use in other modules
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = HTMXMapIntegration;
|
||||
} else {
|
||||
window.HTMXMapIntegration = HTMXMapIntegration;
|
||||
}
|
||||
Reference in New Issue
Block a user