Add collapsible footer to map controls and update location search icon

This commit is contained in:
Eugene Burmakin 2025-10-14 17:30:53 +02:00
parent e72b2d9182
commit 6da1019bf3
8 changed files with 73 additions and 30 deletions

File diff suppressed because one or more lines are too long

View file

@ -218,6 +218,7 @@
/* Fix bottom controls being cut off */
.leaflet-bottom {
padding-bottom: 10px !important;
transition: padding-bottom 0.3s ease;
}
.leaflet-bottom.leaflet-left {

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>

After

Width:  |  Height:  |  Size: 295 B

View file

@ -112,7 +112,7 @@ export default class extends BaseController {
this.map = L.map(this.containerTarget).setView([this.center[0], this.center[1]], 14);
// Add scale control
L.control.scale({
this.scaleControl = L.control.scale({
position: 'bottomright',
imperial: this.distanceUnit === 'mi',
metric: this.distanceUnit === 'km',
@ -765,17 +765,23 @@ export default class extends BaseController {
onAdd: function(map) {
const container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
const button = L.DomUtil.create('button', 'map-info-toggle-button', container);
button.innerHTML = ''; // Info emoji
button.title = 'Toggle stats visibility';
// Lucide info icon
button.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-info">
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 16v-4"></path>
<path d="M12 8h.01"></path>
</svg>
`;
button.title = 'Toggle footer visibility';
// Style the button with theme-aware styling
applyThemeToButton(button, controller.userTheme);
button.style.width = '34px';
button.style.height = '34px';
button.style.fontSize = '20px';
button.style.display = 'block';
button.style.lineHeight = '34px';
button.style.textAlign = 'center';
button.style.display = 'flex';
button.style.alignItems = 'center';
button.style.justifyContent = 'center';
button.style.cursor = 'pointer';
button.style.border = 'none';
button.style.borderRadius = '4px';
@ -783,9 +789,9 @@ export default class extends BaseController {
// Disable map interactions when clicking the button
L.DomEvent.disableClickPropagation(container);
// Toggle stats visibility on button click
// Toggle footer visibility on button click
L.DomEvent.on(button, 'click', () => {
controller.toggleStatsVisibility();
controller.toggleFooterVisibility();
});
return container;
@ -796,18 +802,52 @@ export default class extends BaseController {
this.map.addControl(new InfoToggleControl());
}
toggleStatsVisibility() {
if (!this.statsControl) return;
toggleFooterVisibility() {
// Toggle the page footer
const footer = document.getElementById('map-footer');
if (!footer) return;
// Get the stats control element
const statsElement = this.map.getContainer().querySelector('.leaflet-control-stats');
if (!statsElement) return;
const isCurrentlyHidden = footer.classList.contains('hidden');
// Toggle visibility
if (statsElement.style.display === 'none') {
statsElement.style.display = 'inline-block';
// Toggle Tailwind's hidden class
footer.classList.toggle('hidden');
// Adjust bottom controls position based on footer visibility
if (isCurrentlyHidden) {
// Footer is being shown - move controls up
setTimeout(() => {
const footerHeight = footer.offsetHeight;
// Add extra 20px margin above footer
this.adjustBottomControls(footerHeight + 20);
}, 10); // Small delay to ensure footer is rendered
} else {
statsElement.style.display = 'none';
// Footer is being hidden - reset controls position
this.adjustBottomControls(10); // Back to default padding
}
// Add click event to close footer when clicking on it (only add once)
if (!footer.dataset.clickHandlerAdded) {
footer.addEventListener('click', (e) => {
// Only close if clicking the footer itself, not its contents
if (e.target === footer) {
footer.classList.add('hidden');
this.adjustBottomControls(10); // Reset controls position
}
});
footer.dataset.clickHandlerAdded = 'true';
}
}
adjustBottomControls(paddingBottom) {
// Adjust all bottom Leaflet controls
const bottomLeftControls = this.map.getContainer().querySelector('.leaflet-bottom.leaflet-left');
const bottomRightControls = this.map.getContainer().querySelector('.leaflet-bottom.leaflet-right');
if (bottomLeftControls) {
bottomLeftControls.style.setProperty('padding-bottom', `${paddingBottom}px`, 'important');
}
if (bottomRightControls) {
bottomRightControls.style.setProperty('padding-bottom', `${paddingBottom}px`, 'important');
}
}

View file

@ -99,13 +99,6 @@ export default class extends BaseController {
console.log('📊 After fitBounds overlay display:', afterFitBoundsElement?.style.display || 'default');
}
console.log('🎯 Public sharing: using manual hexagon loading');
console.log('🔍 Debug values:');
console.log(' dataBounds:', dataBounds);
console.log(' point_count:', dataBounds?.point_count);
console.log(' hexagonsAvailableValue:', this.hexagonsAvailableValue);
console.log(' hexagonsAvailableValue type:', typeof this.hexagonsAvailableValue);
// Load hexagons only if they are pre-calculated and data exists
if (dataBounds && dataBounds.point_count > 0 && this.hexagonsAvailableValue) {
await this.loadStaticHexagons();
@ -140,7 +133,7 @@ export default class extends BaseController {
// Ensure loading overlay is visible and disable map interaction
const loadingElement = document.getElementById('map-loading');
console.log('🔍 Loading element found:', !!loadingElement);
if (loadingElement) {
loadingElement.style.display = 'flex';
loadingElement.style.visibility = 'visible';

View file

@ -24,7 +24,7 @@ class LocationSearch {
const SearchToggleControl = L.Control.extend({
onAdd: function(map) {
const button = L.DomUtil.create('button', 'location-search-toggle');
button.innerHTML = '🔍';
button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search-icon lucide-search"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>';
// Style the button with theme-aware styling
applyThemeToButton(button, this.userTheme);
button.style.width = '48px';
@ -33,6 +33,9 @@ class LocationSearch {
button.style.padding = '0';
button.style.fontSize = '18px';
button.style.marginTop = '10px'; // Space below settings button
button.style.display = 'flex';
button.style.alignItems = 'center';
button.style.justifyContent = 'center';
button.title = 'Search locations';
button.id = 'location-search-toggle';
return button;

View file

@ -42,6 +42,11 @@
<%= yield %>
</div>
<!-- Fixed Footer (hidden by default) -->
<div id='map-footer' class='fixed bottom-0 left-0 right-0 z-30 hidden'>
<%= render 'shared/legal_footer' %>
</div>
<%= render 'map/onboarding_modal' %>
</body>
</html>

View file

@ -1,4 +1,4 @@
<div class="navbar bg-base-100">
<div class="navbar bg-base-100 h-16">
<div class="navbar-start">
<div class="dropdown">
<label tabindex="0" class="btn btn-ghost lg:hidden">