feat: split map view into 50/50 map + bounds-filtered photo grid
On map view the body container now shows the Leaflet map on one half and a photo grid on the other half. The grid renders only photos whose GPS falls inside the map's current bounds, and re-renders on moveend/zoomend so panning and zooming filters the visible photo set live. Stacks vertically on narrow viewports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b4e801e17f
commit
96d9a6971c
|
|
@ -135,6 +135,29 @@
|
|||
flex: 1; overflow-y: auto; padding: 0 20px 20px;
|
||||
max-width: 1440px; width: 100%; margin: 0 auto;
|
||||
}
|
||||
#live-search-panel .ls-body.ls-split {
|
||||
display: flex; flex-direction: row; gap: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#live-search-panel .ls-body.ls-split > .ls-map-wrap {
|
||||
flex: 0 0 50%; height: 100%; min-height: 0;
|
||||
}
|
||||
#live-search-panel .ls-body.ls-split > .ls-grid {
|
||||
flex: 1 1 50%; overflow-y: auto; align-content: start;
|
||||
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#live-search-panel .ls-body.ls-split {
|
||||
flex-direction: column;
|
||||
}
|
||||
#live-search-panel .ls-body.ls-split > .ls-map-wrap,
|
||||
#live-search-panel .ls-body.ls-split > .ls-grid {
|
||||
flex: 1 1 50%;
|
||||
}
|
||||
#live-search-panel .ls-body.ls-split > .ls-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
#live-search-panel .ls-grid {
|
||||
display: grid;
|
||||
|
|
@ -235,14 +258,17 @@
|
|||
const bodyEl = panel.querySelector('.ls-body');
|
||||
|
||||
if (view === 'grid') {
|
||||
bodyEl.classList.remove('ls-split');
|
||||
gridEl.style.display = '';
|
||||
mapWrap.style.display = 'none';
|
||||
bodyEl.style.overflowY = 'auto';
|
||||
renderGridItems(currentItems);
|
||||
} else {
|
||||
gridEl.style.display = 'none';
|
||||
// Map view = 50/50 split: map on one side, bounds-filtered photos on the other
|
||||
bodyEl.classList.add('ls-split');
|
||||
gridEl.style.display = '';
|
||||
mapWrap.style.display = '';
|
||||
bodyEl.style.overflowY = 'hidden';
|
||||
bodyEl.style.overflowY = '';
|
||||
showMap(currentItems);
|
||||
}
|
||||
}
|
||||
|
|
@ -267,9 +293,10 @@
|
|||
const mapWrap = panel ? panel.querySelector('.ls-map-wrap') : null;
|
||||
const bodyEl = panel ? panel.querySelector('.ls-body') : null;
|
||||
if (gridEl && mapWrap && bodyEl) {
|
||||
gridEl.style.display = 'none';
|
||||
bodyEl.classList.add('ls-split');
|
||||
gridEl.style.display = '';
|
||||
mapWrap.style.display = '';
|
||||
bodyEl.style.overflowY = 'hidden';
|
||||
bodyEl.style.overflowY = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -545,6 +572,28 @@
|
|||
// Fix map sizing (Leaflet needs this after dynamic show)
|
||||
setTimeout(() => mapInstance.invalidateSize(), 100);
|
||||
|
||||
// Bounds-filtered grid on the other half of the split. Updates live
|
||||
// as the user pans/zooms so "photos taken on that part of the map"
|
||||
// stay in sync with the visible viewport.
|
||||
const updateGridFromBounds = () => {
|
||||
if (currentView !== 'map') return;
|
||||
const b = mapInstance.getBounds();
|
||||
const visible = geoItems.filter(i =>
|
||||
b.contains([i.exifInfo.latitude, i.exifInfo.longitude])
|
||||
);
|
||||
renderGridItems(visible);
|
||||
const statusEl = panel.querySelector('.ls-status');
|
||||
if (statusEl) {
|
||||
const totalGeo = geoItems.length;
|
||||
statusEl.textContent =
|
||||
visible.length + ' of ' + totalGeo + ' shown in view';
|
||||
}
|
||||
};
|
||||
mapInstance.on('moveend', updateGridFromBounds);
|
||||
mapInstance.on('zoomend', updateGridFromBounds);
|
||||
// Initial population once bounds settle
|
||||
setTimeout(updateGridFromBounds, 150);
|
||||
|
||||
// Add custom label style
|
||||
if (!document.getElementById('ls-marker-styles')) {
|
||||
const s = document.createElement('style');
|
||||
|
|
|
|||
Loading…
Reference in New Issue