Fix map loading and tab navigation v0.4.1

This commit is contained in:
dmo committed 2026-07-23 09:50:45 +02:00
1 parent e290bd1fff
commit 9b7d302064
9 files changed
+148 -47

No files matched your search

+49 -24
View File
@@ -63,6 +63,7 @@ const elements = {
addressSearchButton: $('#address-search-button'),
addressResults: $('#address-results'),
locationMap: $('#location-map'),
mapStatus: $('#map-status'),
latitude: $('#latitude-input'),
longitude: $('#longitude-input'),
locationName: $('#location-name-input'),
@@ -609,7 +610,13 @@ function activeTabIndex() {
return $$('.tab').findIndex((tab) => tab.classList.contains('active'));
}
function activateTab(tabName, focusTab = false) {
function focusFirstControl(tabName) {
const panel = document.querySelector(`.tab-panel[data-panel="${tabName}"]`);
const first = panel?.querySelector('input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])');
first?.focus();
}
function activateTab(tabName, focusMode = 'none') {
const tabs = $$('.tab');
const target = tabs.find((tab) => tab.dataset.tab === tabName);
if (!target) return;
@@ -620,22 +627,28 @@ function activateTab(tabName, focusTab = false) {
item.tabIndex = active ? 0 : -1;
});
$$('.tab-panel').forEach((panel) => panel.classList.toggle('active', panel.dataset.panel === tabName));
if (focusTab) target.focus();
if (tabName === 'location') {
initialiseMap();
window.setTimeout(() => {
state.map?.invalidateSize();
syncMapFromFields(false);
if (focusMode === 'field') focusFirstControl(tabName);
else if (focusMode === 'tab') target.focus();
}, 0);
} else if (focusMode === 'field') {
window.setTimeout(() => focusFirstControl(tabName), 0);
} else if (focusMode === 'tab') {
target.focus();
}
}
function cycleTabs(delta) {
function cycleTabs(delta, focusMode = 'field') {
const tabs = $$('.tab');
if (!tabs.length) return;
const current = Math.max(0, activeTabIndex());
const next = (current + delta + tabs.length) % tabs.length;
activateTab(tabs[next].dataset.tab, true);
activateTab(tabs[next].dataset.tab, focusMode);
}
function markLocationDirty() {
@@ -643,21 +656,39 @@ function markLocationDirty() {
captureCurrentValues();
}
function setMapStatus(message, isError = false) {
if (!elements.mapStatus) return;
elements.mapStatus.textContent = message;
elements.mapStatus.classList.toggle('error', isError);
}
function initialiseMap() {
if (state.map || typeof window.L === 'undefined' || !elements.locationMap) return;
if (state.map || !elements.locationMap) return;
if (typeof window.L === 'undefined') {
setMapStatus('Map library failed to load. Rebuild the container and hard-refresh the page.', true);
return;
}
const config = state.mapConfig || {};
state.map = L.map(elements.locationMap, { zoomControl: true }).setView(
[Number(config.defaultMapLat ?? 64.5), Number(config.defaultMapLon ?? 11.0)],
Number(config.defaultMapZoom ?? 5),
);
L.tileLayer(config.mapTileUrl || 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: config.mapAttribution || '© OpenStreetMap contributors',
}).addTo(state.map);
state.map.on('click', (event) => {
setLocation(event.latlng.lat, event.latlng.lng, '', true, false);
});
syncMapFromFields(false);
try {
state.map = L.map(elements.locationMap, { zoomControl: true }).setView(
[Number(config.defaultMapLat ?? 64.5), Number(config.defaultMapLon ?? 11.0)],
Number(config.defaultMapZoom ?? 5),
);
const layer = L.tileLayer(config.mapTileUrl || '/api/map/tiles/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: config.mapAttribution || '© OpenStreetMap contributors',
}).addTo(state.map);
layer.on('loading', () => setMapStatus('Loading map…'));
layer.on('load', () => setMapStatus('Click the map to place a pin.'));
layer.on('tileerror', () => setMapStatus('Map tiles could not be loaded. Address search and manual coordinates still work.', true));
state.map.on('click', (event) => {
setLocation(event.latlng.lat, event.latlng.lng, '', true, false);
});
syncMapFromFields(false);
} catch (error) {
state.map = null;
setMapStatus(error.message || 'The map could not be initialized.', true);
}
}
function setMarker(latitude, longitude, center = false) {
@@ -786,12 +817,6 @@ for (const radio of $$('input[name="precision"]')) radio.addEventListener('chang
for (const tab of $$('.tab')) {
tab.addEventListener('click', () => activateTab(tab.dataset.tab));
tab.addEventListener('keydown', (event) => {
if (event.key === 'ArrowLeft') { event.preventDefault(); cycleTabs(-1); }
if (event.key === 'ArrowRight') { event.preventDefault(); cycleTabs(1); }
if (event.key === 'Home') { event.preventDefault(); activateTab($$('.tab')[0].dataset.tab, true); }
if (event.key === 'End') { const tabs = $$('.tab'); event.preventDefault(); activateTab(tabs[tabs.length - 1].dataset.tab, true); }
});
}
activateTab('date');
@@ -804,7 +829,7 @@ window.addEventListener('keydown', (event) => {
const typing = ['INPUT', 'TEXTAREA', 'SELECT'].includes(tag);
if (event.key === 'Tab' && event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey && document.activeElement?.closest('.right-panel')) {
event.preventDefault(); cycleTabs(-1); return;
event.preventDefault(); event.stopPropagation(); cycleTabs(-1, 'field'); return;
}
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 's') {
event.preventDefault(); savePhoto(false); return;