mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-05-15 11:36:20 +02:00
UX: country hover highlight + instant loading feedback on click
- Add transparent interactive fill layer for country hover detection - Hover shows subtle highlight + pointer cursor so user knows it's clickable - Show loading modal immediately on click (before geocode completes) - Dismiss modal if geocode fails (ocean click etc)
This commit is contained in:
@@ -404,8 +404,13 @@ export class App {
|
||||
this.countryIntelModal = new CountryIntelModal();
|
||||
|
||||
this.map.onCountryClicked(async (lat, lon) => {
|
||||
this.countryIntelModal!.showLoading();
|
||||
|
||||
const geo = await reverseGeocode(lat, lon);
|
||||
if (!geo) return;
|
||||
if (!geo) {
|
||||
this.countryIntelModal!.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const scores = calculateCII();
|
||||
const score = scores.find((s) => s.code === geo.code) ?? null;
|
||||
|
||||
@@ -89,6 +89,24 @@ export class CountryIntelModal {
|
||||
`;
|
||||
}
|
||||
|
||||
public showLoading(): void {
|
||||
this.currentCode = '__loading__';
|
||||
this.headerEl.innerHTML = `
|
||||
<span class="country-flag">🌍</span>
|
||||
<span class="country-name">Identifying country...</span>
|
||||
`;
|
||||
this.contentEl.innerHTML = `
|
||||
<div class="intel-brief-section">
|
||||
<div class="intel-brief-loading">
|
||||
<div class="intel-skeleton"></div>
|
||||
<div class="intel-skeleton short"></div>
|
||||
<span class="intel-loading-text">Locating region...</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
this.overlay.classList.add('active');
|
||||
}
|
||||
|
||||
public show(country: string, code: string, score: CountryScore | null, signals?: ActiveSignals): void {
|
||||
this.currentCode = code;
|
||||
const flag = this.countryFlag(code);
|
||||
|
||||
@@ -2585,6 +2585,25 @@ export class DeckGLMap {
|
||||
type: 'geojson',
|
||||
data: geojson,
|
||||
});
|
||||
this.maplibreMap.addLayer({
|
||||
id: 'country-interactive',
|
||||
type: 'fill',
|
||||
source: 'country-boundaries',
|
||||
paint: {
|
||||
'fill-color': '#3b82f6',
|
||||
'fill-opacity': 0,
|
||||
},
|
||||
});
|
||||
this.maplibreMap.addLayer({
|
||||
id: 'country-hover-fill',
|
||||
type: 'fill',
|
||||
source: 'country-boundaries',
|
||||
paint: {
|
||||
'fill-color': '#3b82f6',
|
||||
'fill-opacity': 0.06,
|
||||
},
|
||||
filter: ['==', ['get', 'ISO3166-1-Alpha-2'], ''],
|
||||
});
|
||||
this.maplibreMap.addLayer({
|
||||
id: 'country-highlight-fill',
|
||||
type: 'fill',
|
||||
@@ -2606,11 +2625,43 @@ export class DeckGLMap {
|
||||
},
|
||||
filter: ['==', ['get', 'ISO3166-1-Alpha-2'], ''],
|
||||
});
|
||||
|
||||
this.setupCountryHover();
|
||||
console.log('[DeckGLMap] Country boundaries loaded');
|
||||
})
|
||||
.catch((err) => console.warn('[DeckGLMap] Failed to load country boundaries:', err));
|
||||
}
|
||||
|
||||
private setupCountryHover(): void {
|
||||
if (!this.maplibreMap) return;
|
||||
const map = this.maplibreMap;
|
||||
let hoveredCode: string | null = null;
|
||||
|
||||
map.on('mousemove', (e) => {
|
||||
if (!this.onCountryClick) return;
|
||||
const features = map.queryRenderedFeatures(e.point, { layers: ['country-interactive'] });
|
||||
const code = features?.[0]?.properties?.['ISO3166-1-Alpha-2'] as string | undefined;
|
||||
|
||||
if (code && code !== hoveredCode) {
|
||||
hoveredCode = code;
|
||||
map.setFilter('country-hover-fill', ['==', ['get', 'ISO3166-1-Alpha-2'], code]);
|
||||
map.getCanvas().style.cursor = 'pointer';
|
||||
} else if (!code && hoveredCode) {
|
||||
hoveredCode = null;
|
||||
map.setFilter('country-hover-fill', ['==', ['get', 'ISO3166-1-Alpha-2'], '']);
|
||||
map.getCanvas().style.cursor = '';
|
||||
}
|
||||
});
|
||||
|
||||
map.on('mouseout', () => {
|
||||
if (hoveredCode) {
|
||||
hoveredCode = null;
|
||||
map.setFilter('country-hover-fill', ['==', ['get', 'ISO3166-1-Alpha-2'], '']);
|
||||
map.getCanvas().style.cursor = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public highlightCountry(code: string): void {
|
||||
if (!this.maplibreMap || !this.countryGeoJsonLoaded) return;
|
||||
// Update MapLibre filter to highlight this country
|
||||
|
||||
Reference in New Issue
Block a user