class WoMap extends HTMLElement {
#map = null;
#mounted = false;
static get observedAttributes() { return ['lat', 'lng', 'site-name', 'access-notes', 'wo-number']; }
connectedCallback() {
this.style.display = 'block';
// Light DOM — Leaflet needs real DOM, not shadow root
if (!this.querySelector('.wo-map-root')) this.#build();
}
attributeChangedCallback() {
if (this.#mounted) this.#update();
}
#build() {
const lat = parseFloat(this.getAttribute('lat'));
const lng = parseFloat(this.getAttribute('lng'));
const siteName = this.getAttribute('site-name') || 'Work Site';
const accessNotes = this.getAttribute('access-notes') || '';
const woNumber = this.getAttribute('wo-number') || '';
this.innerHTML = `
${lat && lng ? `
` : `
No location set for this work order
`}
${accessNotes ? `
${this.#esc(accessNotes)}
` : ''}
`;
if (window.lucide) lucide.createIcons({ root: this });
if (lat && lng) {
this.#initMap(lat, lng, siteName, woNumber);
this.#setDirectionsLink(lat, lng);
}
this.#mounted = true;
}
#initMap(lat, lng, siteName, woNumber) {
if (typeof L === 'undefined') {
// Leaflet not yet loaded — retry once it fires
window.addEventListener('load', () => this.#initMap(lat, lng, siteName, woNumber), { once: true });
return;
}
const mapId = `leaflet-map-${this.getAttribute('wo-id') || 'map'}`;
const container = this.querySelector(`#${mapId}`);
if (!container) return;
// Prevent double-init
if (container._leaflet_id) return;
this.#map = L.map(container, { zoomControl: true }).setView([lat, lng], 16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19,
}).addTo(this.#map);
const icon = L.divIcon({
className: '',
html: `
${this.#esc(woNumber || '📍')}
`,
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32],
});
L.marker([lat, lng], { icon })
.addTo(this.#map)
.bindPopup(`${this.#esc(siteName)}
${lat.toFixed(5)}, ${lng.toFixed(5)}`)
.openPopup();
}
#setDirectionsLink(lat, lng) {
const btn = this.querySelector('#directions-btn');
if (!btn) return;
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
btn.href = isIOS
? `maps://maps.apple.com/?daddr=${lat},${lng}&dirflg=d`
: `https://maps.google.com/maps?daddr=${lat},${lng}`;
}
#update() {
if (this.#map) {
this.#map.remove();
this.#map = null;
}
this.#mounted = false;
this.#build();
}
#esc(s) { return (s || '').replace(/&/g,'&').replace(//g,'>'); }
}
customElements.define('wo-map', WoMap);