added date selection and filter popup
This commit is contained in:
1 parent
739a1eee28
commit
36ed3880e8
16 files changed
+741
-180
No files matched your search
@@ -30,6 +30,63 @@
|
||||
statusEl.classList.toggle('is-error', !!isError);
|
||||
}
|
||||
|
||||
// German number formatting for what the panel shows (decimal comma, dot
|
||||
// as thousands separator) — mirrors GermanFormatExtension::distance() on
|
||||
// the server so a distance reads the same on a card and in the panel.
|
||||
var DE_INT = new Intl.NumberFormat('de-DE', { maximumFractionDigits: 0 });
|
||||
var DE_ONE_DECIMAL = new Intl.NumberFormat('de-DE', { maximumFractionDigits: 1 });
|
||||
|
||||
function formatKm(km) {
|
||||
return km < 10 ? DE_ONE_DECIMAL.format(km) : DE_INT.format(km);
|
||||
}
|
||||
|
||||
// Day-range filtering for the maps — the same "event overlaps [from, to]"
|
||||
// rule as EventRepository::filterByDateRange on the server, applied to the
|
||||
// Y-m-d prefixes the serializer ships, so it's a plain string comparison.
|
||||
function eventInDateRange(ev, range) {
|
||||
if (!range.from && !range.to) return true;
|
||||
if (!ev.start) return false; // undated events can't match a date window
|
||||
var end = ev.end || ev.start;
|
||||
return (!range.to || ev.start <= range.to) && (!range.from || end >= range.from);
|
||||
}
|
||||
|
||||
// Binds the shared #dateFrom/#dateTo/#dateClear controls (German-format
|
||||
// pickers, remembered range — see dates.js) and returns the live range
|
||||
// object ({from, to}, either may be null) the caller filters by; onChange
|
||||
// runs after every user edit. `initial` seeds it (a range from the URL or
|
||||
// from storage) without firing onChange.
|
||||
function initDateRange(initial, onChange) {
|
||||
var fromEl = document.getElementById('dateFrom');
|
||||
var toEl = document.getElementById('dateTo');
|
||||
var clearEl = document.getElementById('dateClear');
|
||||
var range = { from: null, to: null };
|
||||
var setInputs = function () {};
|
||||
|
||||
function apply(from, to) {
|
||||
var normalized = window.VindnDates.normalize(from, to);
|
||||
range.from = normalized.from;
|
||||
range.to = normalized.to;
|
||||
setInputs(range.from, range.to);
|
||||
if (clearEl) clearEl.hidden = !(range.from || range.to);
|
||||
}
|
||||
|
||||
function edited(from, to) {
|
||||
apply(from, to);
|
||||
window.VindnDates.save(range.from, range.to);
|
||||
onChange();
|
||||
}
|
||||
|
||||
if (fromEl && toEl) setInputs = window.VindnDates.bindInputs(fromEl, toEl, edited);
|
||||
if (clearEl) clearEl.addEventListener('click', function () { edited(null, null); });
|
||||
|
||||
apply(initial && initial.from, initial && initial.to);
|
||||
// A range that arrived via the URL should be remembered too, so the
|
||||
// other pages pick it up like they would one picked here.
|
||||
if (range.from || range.to) window.VindnDates.save(range.from, range.to);
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
// Leaflet.awesome-markers only ships a fixed palette of named colors (no
|
||||
// arbitrary hex/hsl) — curated to the ones that read well against the
|
||||
// grayscale-filtered basemap. Approximate hex values below are only used
|
||||
@@ -350,6 +407,23 @@
|
||||
|
||||
if (limit && map) fitToMarkers(map, markers);
|
||||
|
||||
var dates = initDateRange(window.VindnDates.load(), onDatesChange);
|
||||
|
||||
// Candidate pool for the date window; in limit mode ("nearest N") the
|
||||
// N nearest are picked *from this*, so a narrow window still shows up
|
||||
// to N events instead of N nearest-overall minus the ones outside it.
|
||||
function poolEvents() {
|
||||
return allEvents.filter(function (ev) { return eventInDateRange(ev, dates); });
|
||||
}
|
||||
|
||||
function onDatesChange() {
|
||||
if (limit) {
|
||||
rebuildMarkers(nearest(state.userLoc || defaultCenter, poolEvents(), limit));
|
||||
if (map) fitToMarkers(map, markers);
|
||||
}
|
||||
renderList();
|
||||
}
|
||||
|
||||
var legendEl = document.getElementById('legend');
|
||||
if (legendEl) {
|
||||
var seenCats = [];
|
||||
@@ -398,7 +472,7 @@
|
||||
return list;
|
||||
}
|
||||
|
||||
var visible = allEvents.filter(function (ev) { return state.cat === 'all' || ev.category === state.cat; });
|
||||
var visible = allEvents.filter(function (ev) { return (state.cat === 'all' || ev.category === state.cat) && eventInDateRange(ev, dates); });
|
||||
if (state.userLoc) {
|
||||
visible = visible.map(function (ev) { ev._dist = distanceKm(state.userLoc[0], state.userLoc[1], ev.lat, ev.lon); return ev; })
|
||||
.sort(function (a, b) { return a._dist - b._dist; });
|
||||
@@ -419,7 +493,7 @@
|
||||
var list = currentList();
|
||||
|
||||
var countEl = document.getElementById('resultCount');
|
||||
if (countEl) countEl.textContent = list.length + (list.length === 1 ? ' event' : ' events');
|
||||
if (countEl) countEl.textContent = DE_INT.format(list.length) + (list.length === 1 ? ' event' : ' events');
|
||||
|
||||
var panelEl = document.getElementById('panelList');
|
||||
if (!panelEl) return;
|
||||
@@ -427,7 +501,7 @@
|
||||
panelEl.innerHTML = list.map(function (ev) {
|
||||
var distHtml = '';
|
||||
if (ev._dist != null) {
|
||||
var distLabel = ev._dist < 1 ? Math.round(ev._dist * 1000) : ev._dist.toFixed(1);
|
||||
var distLabel = ev._dist < 1 ? Math.round(ev._dist * 1000) : formatKm(ev._dist);
|
||||
var distUnit = ev._dist < 1 ? 'm' : 'km';
|
||||
distHtml = '<div class="dist">' + distLabel + '<span class="u">' + distUnit + '</span></div>';
|
||||
}
|
||||
@@ -480,7 +554,7 @@
|
||||
window.VindnLocation.save(lat, lon, placeName);
|
||||
setLocationInputValue(placeName);
|
||||
|
||||
if (limit) rebuildMarkers(nearest([lat, lon], allEvents.slice(), limit));
|
||||
if (limit) rebuildMarkers(nearest([lat, lon], poolEvents(), limit));
|
||||
|
||||
var statusEl = document.getElementById('locateStatus');
|
||||
setLocateStatus(statusEl, statusText, false);
|
||||
@@ -548,7 +622,10 @@
|
||||
}).catch(function () {});
|
||||
}
|
||||
|
||||
renderList();
|
||||
// The first pins/list were built before the date range was known — a
|
||||
// remembered range has to reshape them (onDatesChange also renders).
|
||||
if (dates.from || dates.to) onDatesChange();
|
||||
else renderList();
|
||||
|
||||
return { map: map, markers: markers };
|
||||
}
|
||||
@@ -576,6 +653,14 @@
|
||||
var containerId = opts.containerId || 'mapCanvas';
|
||||
|
||||
var state = { source: originalCenter.slice(), rangeKm: opts.rangeKm || 25, cat: 'all' };
|
||||
// A from/to in the URL (shared link, or this page's own reload) wins
|
||||
// over the remembered range, the same way an explicit lat/lon does.
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var urlHasDates = urlParams.has('from') || urlParams.has('to');
|
||||
var dates = initDateRange(
|
||||
urlHasDates ? { from: urlParams.get('from'), to: urlParams.get('to') } : window.VindnDates.load(),
|
||||
function () { render(); }
|
||||
);
|
||||
// getCurrentPosition() can take a few seconds; if the user searches or
|
||||
// clicks the map before it resolves, its result must not overwrite
|
||||
// whatever they deliberately chose in the meantime.
|
||||
@@ -625,6 +710,7 @@
|
||||
function inRange() {
|
||||
return allEvents
|
||||
.filter(function (ev) { return state.cat === 'all' || ev.category === state.cat; })
|
||||
.filter(function (ev) { return eventInDateRange(ev, dates); })
|
||||
.map(function (ev) { ev._dist = distanceKm(state.source[0], state.source[1], ev.lat, ev.lon); return ev; })
|
||||
.filter(function (ev) { return ev._dist <= state.rangeKm; })
|
||||
.sort(function (a, b) { return a._dist - b._dist; });
|
||||
@@ -636,6 +722,10 @@
|
||||
url.searchParams.set('lat', state.source[0].toFixed(5));
|
||||
url.searchParams.set('lon', state.source[1].toFixed(5));
|
||||
url.searchParams.set('range', state.rangeKm);
|
||||
['from', 'to'].forEach(function (key) {
|
||||
if (dates[key]) url.searchParams.set(key, dates[key]);
|
||||
else url.searchParams.delete(key);
|
||||
});
|
||||
window.history.replaceState(null, '', url.toString());
|
||||
}
|
||||
|
||||
@@ -658,12 +748,12 @@
|
||||
setLocateStatus(statusEl, 'Showing events within ' + state.rangeKm + ' km', false);
|
||||
|
||||
var countEl = document.getElementById('resultCount');
|
||||
if (countEl) countEl.textContent = visible.length + (visible.length === 1 ? ' event' : ' events');
|
||||
if (countEl) countEl.textContent = DE_INT.format(visible.length) + (visible.length === 1 ? ' event' : ' events');
|
||||
|
||||
var panelEl = document.getElementById('panelList');
|
||||
if (panelEl) {
|
||||
panelEl.innerHTML = visible.map(function (ev) {
|
||||
var distLabel = ev._dist < 1 ? Math.round(ev._dist * 1000) : ev._dist.toFixed(1);
|
||||
var distLabel = ev._dist < 1 ? Math.round(ev._dist * 1000) : formatKm(ev._dist);
|
||||
var distUnit = ev._dist < 1 ? 'm' : 'km';
|
||||
return (
|
||||
'<div class="map-row" data-id="' + ev.id + '" tabindex="0" role="button" aria-label="' + ev.title + '">' +
|
||||
|
||||
Reference in new issue
Block a user