feat(contact-page): implement contact page with dynamic content and map integration

This commit is contained in:
2026-07-24 09:41:09 -03:00
parent 656e41d189
commit fa0e9de886
5 changed files with 522 additions and 10 deletions

View File

@@ -11,7 +11,31 @@ import {
SimpleChanges,
ViewChild,
} from '@angular/core';
import type { LayerGroup, Map as LeafletMap, LatLngBounds, LatLngTuple } from 'leaflet';
import type { LayerGroup, Map as LeafletMap, LatLngBounds, LatLngTuple, TileLayer } from 'leaflet';
export type MapStyle = 'minimal-light' | 'standard';
const MAP_LAYERS: Record<
MapStyle,
{
url: string;
attribution: string;
maxZoom: number;
}
> = {
'minimal-light': {
url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>',
maxZoom: 20,
},
standard: {
url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
maxZoom: 19,
},
};
export interface MapLocation {
id: string | number;
@@ -32,12 +56,15 @@ export class LocationMapComponent implements AfterViewInit, OnChanges, OnDestroy
@Input() fallbackCenter: LatLngTuple = [-32.94682, -60.63932];
@Input() fallbackZoom = 13;
@Input() maxFitZoom = 15;
@Input() mapStyle: MapStyle = 'minimal-light';
@Input() markerColor = '#24a9e0';
@ViewChild('mapContainer', { static: true })
private readonly mapContainer!: ElementRef<HTMLDivElement>;
private leaflet: typeof import('leaflet') | null = null;
private map: LeafletMap | null = null;
private baseLayer: TileLayer | null = null;
private markerLayer: LayerGroup | null = null;
constructor(
@@ -52,7 +79,11 @@ export class LocationMapComponent implements AfterViewInit, OnChanges, OnDestroy
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['locations'] && this.map) {
if (changes['mapStyle'] && this.map) {
this.renderBaseLayer();
}
if ((changes['locations'] || changes['markerColor']) && this.map) {
this.renderLocations();
}
}
@@ -60,6 +91,7 @@ export class LocationMapComponent implements AfterViewInit, OnChanges, OnDestroy
ngOnDestroy(): void {
this.map?.remove();
this.map = null;
this.baseLayer = null;
this.markerLayer = null;
}
@@ -88,17 +120,30 @@ export class LocationMapComponent implements AfterViewInit, OnChanges, OnDestroy
scrollWheelZoom: false,
});
leaflet
.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '&copy; OpenStreetMap contributors',
})
.addTo(this.map);
this.renderBaseLayer();
this.markerLayer = leaflet.layerGroup().addTo(this.map);
this.renderLocations();
}
private renderBaseLayer(): void {
if (!this.leaflet || !this.map) {
return;
}
if (this.baseLayer) {
this.map.removeLayer(this.baseLayer);
}
const layer = MAP_LAYERS[this.mapStyle];
this.baseLayer = this.leaflet
.tileLayer(layer.url, {
maxZoom: layer.maxZoom,
attribution: layer.attribution,
crossOrigin: true,
})
.addTo(this.map);
}
private renderLocations(): void {
if (!this.leaflet || !this.map || !this.markerLayer) {
return;
@@ -152,7 +197,7 @@ export class LocationMapComponent implements AfterViewInit, OnChanges, OnDestroy
radius: 8,
color: '#ffffff',
weight: 3,
fillColor: '#24a9e0',
fillColor: this.markerColor,
fillOpacity: 1,
})
.bindTooltip(tooltip)