mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
217 lines
8 KiB
JavaScript
217 lines
8 KiB
JavaScript
import L from 'leaflet';
|
|
import { applyThemeToPanel } from './theme_utils';
|
|
|
|
/**
|
|
* Custom Leaflet control for managing Places layer visibility and filtering
|
|
*/
|
|
export function createPlacesControl(placesManager, tags, userTheme = 'dark') {
|
|
return L.Control.extend({
|
|
options: {
|
|
position: 'topright'
|
|
},
|
|
|
|
onAdd: function(map) {
|
|
this.placesManager = placesManager;
|
|
this.tags = tags || [];
|
|
this.userTheme = userTheme;
|
|
this.activeFilters = new Set(); // Track which tags are active
|
|
this.showUntagged = false;
|
|
this.placesEnabled = false;
|
|
|
|
// Create main container
|
|
const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-places');
|
|
|
|
// Prevent map interactions when clicking the control
|
|
L.DomEvent.disableClickPropagation(container);
|
|
L.DomEvent.disableScrollPropagation(container);
|
|
|
|
// Create toggle button
|
|
this.button = L.DomUtil.create('a', 'leaflet-control-places-button', container);
|
|
this.button.href = '#';
|
|
this.button.title = 'Places Layer';
|
|
this.button.innerHTML = '📍';
|
|
this.button.style.fontSize = '20px';
|
|
this.button.style.width = '34px';
|
|
this.button.style.height = '34px';
|
|
this.button.style.lineHeight = '30px';
|
|
this.button.style.textAlign = 'center';
|
|
this.button.style.textDecoration = 'none';
|
|
|
|
// Create panel (hidden by default)
|
|
this.panel = L.DomUtil.create('div', 'leaflet-control-places-panel', container);
|
|
this.panel.style.display = 'none';
|
|
this.panel.style.marginTop = '5px';
|
|
this.panel.style.minWidth = '200px';
|
|
this.panel.style.maxWidth = '280px';
|
|
this.panel.style.maxHeight = '400px';
|
|
this.panel.style.overflowY = 'auto';
|
|
this.panel.style.padding = '10px';
|
|
this.panel.style.borderRadius = '4px';
|
|
this.panel.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
|
|
|
|
// Apply theme to panel
|
|
applyThemeToPanel(this.panel, this.userTheme);
|
|
|
|
// Build panel content
|
|
this.buildPanelContent();
|
|
|
|
// Toggle panel on button click
|
|
L.DomEvent.on(this.button, 'click', (e) => {
|
|
L.DomEvent.preventDefault(e);
|
|
this.togglePanel();
|
|
});
|
|
|
|
return container;
|
|
},
|
|
|
|
buildPanelContent: function() {
|
|
const html = `
|
|
<div style="margin-bottom: 10px; font-weight: bold; font-size: 14px; border-bottom: 1px solid rgba(128,128,128,0.3); padding-bottom: 8px;">
|
|
📍 Places Layer
|
|
</div>
|
|
|
|
<!-- All Places Toggle -->
|
|
<label style="display: flex; align-items: center; padding: 6px; cursor: pointer; border-radius: 4px; margin-bottom: 4px;"
|
|
class="places-control-item"
|
|
onmouseover="this.style.backgroundColor='rgba(128,128,128,0.2)'"
|
|
onmouseout="this.style.backgroundColor='transparent'">
|
|
<input type="checkbox"
|
|
data-filter="all"
|
|
style="margin-right: 8px; cursor: pointer;"
|
|
${this.placesEnabled ? 'checked' : ''}>
|
|
<span style="font-weight: bold;">Show All Places</span>
|
|
</label>
|
|
|
|
<!-- Untagged Places Toggle -->
|
|
<label style="display: flex; align-items: center; padding: 6px; cursor: pointer; border-radius: 4px; margin-bottom: 8px;"
|
|
class="places-control-item"
|
|
onmouseover="this.style.backgroundColor='rgba(128,128,128,0.2)'"
|
|
onmouseout="this.style.backgroundColor='transparent'">
|
|
<input type="checkbox"
|
|
data-filter="untagged"
|
|
style="margin-right: 8px; cursor: pointer;"
|
|
${this.showUntagged ? 'checked' : ''}>
|
|
<span>Untagged Places</span>
|
|
</label>
|
|
|
|
${this.tags.length > 0 ? `
|
|
<div style="border-top: 1px solid rgba(128,128,128,0.3); padding-top: 8px; margin-top: 8px;">
|
|
<div style="font-size: 12px; font-weight: bold; margin-bottom: 6px; opacity: 0.7;">
|
|
FILTER BY TAG
|
|
</div>
|
|
<div style="max-height: 250px; overflow-y: auto; margin-right: -5px; padding-right: 5px;">
|
|
${this.tags.map(tag => `
|
|
<label style="display: flex; align-items: center; padding: 6px; cursor: pointer; border-radius: 4px; margin-bottom: 2px;"
|
|
class="places-control-item"
|
|
onmouseover="this.style.backgroundColor='rgba(128,128,128,0.2)'"
|
|
onmouseout="this.style.backgroundColor='transparent'">
|
|
<input type="checkbox"
|
|
data-filter="tag"
|
|
data-tag-id="${tag.id}"
|
|
style="margin-right: 8px; cursor: pointer;"
|
|
${this.activeFilters.has(tag.id) ? 'checked' : ''}>
|
|
<span style="font-size: 18px; margin-right: 6px;">${tag.icon || '📍'}</span>
|
|
<span style="flex: 1;">#${this.escapeHtml(tag.name)}</span>
|
|
${tag.color ? `<span style="width: 12px; height: 12px; border-radius: 50%; background-color: ${tag.color}; margin-left: 4px;"></span>` : ''}
|
|
</label>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
` : '<div style="font-size: 12px; opacity: 0.6; padding: 8px; text-align: center;">No tags created yet</div>'}
|
|
`;
|
|
|
|
this.panel.innerHTML = html;
|
|
|
|
// Add event listeners to checkboxes
|
|
const checkboxes = this.panel.querySelectorAll('input[type="checkbox"]');
|
|
checkboxes.forEach(cb => {
|
|
L.DomEvent.on(cb, 'change', (e) => {
|
|
this.handleFilterChange(e.target);
|
|
});
|
|
});
|
|
},
|
|
|
|
handleFilterChange: function(checkbox) {
|
|
const filterType = checkbox.dataset.filter;
|
|
|
|
if (filterType === 'all') {
|
|
this.placesEnabled = checkbox.checked;
|
|
|
|
if (checkbox.checked) {
|
|
// Show places layer
|
|
this.placesManager.placesLayer.addTo(this.placesManager.map);
|
|
this.applyCurrentFilters();
|
|
} else {
|
|
// Hide places layer
|
|
this.placesManager.map.removeLayer(this.placesManager.placesLayer);
|
|
// Uncheck all other filters
|
|
this.activeFilters.clear();
|
|
this.showUntagged = false;
|
|
this.buildPanelContent();
|
|
}
|
|
} else if (filterType === 'untagged') {
|
|
this.showUntagged = checkbox.checked;
|
|
this.applyCurrentFilters();
|
|
} else if (filterType === 'tag') {
|
|
const tagId = parseInt(checkbox.dataset.tagId);
|
|
|
|
if (checkbox.checked) {
|
|
this.activeFilters.add(tagId);
|
|
} else {
|
|
this.activeFilters.delete(tagId);
|
|
}
|
|
|
|
this.applyCurrentFilters();
|
|
}
|
|
|
|
// Update button appearance
|
|
this.updateButtonState();
|
|
},
|
|
|
|
applyCurrentFilters: function() {
|
|
if (!this.placesEnabled) return;
|
|
|
|
// If no specific filters, show all places
|
|
if (this.activeFilters.size === 0 && !this.showUntagged) {
|
|
this.placesManager.filterByTags(null);
|
|
} else {
|
|
// Build filter criteria
|
|
const tagIds = Array.from(this.activeFilters);
|
|
|
|
// For now, just filter by tags
|
|
// TODO: Add support for untagged filter in PlacesManager
|
|
if (tagIds.length > 0) {
|
|
this.placesManager.filterByTags(tagIds);
|
|
} else if (this.showUntagged) {
|
|
// Show only untagged places
|
|
this.placesManager.filterByTags([]);
|
|
}
|
|
}
|
|
},
|
|
|
|
updateButtonState: function() {
|
|
if (this.placesEnabled) {
|
|
this.button.style.backgroundColor = '#4CAF50';
|
|
this.button.style.color = 'white';
|
|
} else {
|
|
this.button.style.backgroundColor = '';
|
|
this.button.style.color = '';
|
|
}
|
|
},
|
|
|
|
togglePanel: function() {
|
|
if (this.panel.style.display === 'none') {
|
|
this.panel.style.display = 'block';
|
|
} else {
|
|
this.panel.style.display = 'none';
|
|
}
|
|
},
|
|
|
|
escapeHtml: function(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
});
|
|
}
|