Move to using web components for custom components

They're modular, which is really nice.
This commit is contained in:
Eli Ribble 2026-01-14 18:21:56 +00:00
parent b91718cd7c
commit 6fd0ed8711
No known key found for this signature in database
9 changed files with 473 additions and 280 deletions

20
htmlpage/static.go Normal file
View file

@ -0,0 +1,20 @@
package htmlpage
import (
"embed"
"net/http"
"github.com/go-chi/chi/v5"
)
//go:embed static/*
var EmbeddedStaticFS embed.FS
var localFS http.Dir
func AddStaticRoute(r chi.Router, path string) {
if localFS == "" {
localFS = http.Dir("./htmlpage/static")
}
FileServer(r, "/static", localFS, EmbeddedStaticFS, "static")
}

View file

@ -0,0 +1,127 @@
class AddressDisplay extends HTMLElement {
constructor() {
super();
// Create a shadow DOM
this.attachShadow({mode: "open" });
// Initial render
this.render();
// Element references
this._locationDisplay = this.shadowRoot.querySelector(".location-display");
this._streetAddress = this.shadowRoot.querySelector(".street-address");
this._postCode = this.shadowRoot.querySelector(".post-code");
this._district = this.shadowRoot.querySelector(".district");
this._region = this.shadowRoot.querySelector(".region");
this._country = this.shadowRoot.querySelector(".country");
}
// Initial render of component
render() {
this.shadowRoot.innerHTML = `
<style>
@import url('/static/vendor/css/bootstrap.min.css');
.detail-label {
font-size: 0.8rem;
text-transform: uppercase;
color: #6c757d;
margin-bottom: 2px;
font-weight: 600;
}
.detail-value {
font-weight: 500;
}
.main-address {
font-weight: 500;
}
.place-info {
font-size: 0.85rem;
color: #6c757d;
margin-top: 2px;
}
.suggestions-container {
position: absolute;
width: 100%;
max-height: 300px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.suggestion-item {
cursor: pointer;
padding: 10px 12px;
border-bottom: 1px solid #f0f0f0;
}
.suggestion-item:hover {
background-color: #f8f9fa;
}
</style>
<div class="location-display" class="mt-4 d-none">
<h5 class="mb-3">Location Details</h5>
<div class="location-card p-3 mb-3">
<div class="location-detail">
<div class="detail-label">Street Address</div>
<div class="street-address detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Post Code</div>
<div class="post-code detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">District/Place</div>
<div class="district detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Region/State</div>
<div class="region detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Country</div>
<div class="country detail-value">-</div>
</div>
</div>
</div>
`;
}
// Public methods
show(location) {
console.log("Showing location", location);
// Extract context data from properties
const props = location.properties;
const context = props.context || {};
// Populate structured fields
// Street Address - combine address, street, housenumber if available
let addressStr = '';
if (context.address) addressStr += context.address.address_number;
if (context.street) {
if (addressStr) addressStr += ' ';
addressStr += context.street.name;
}
if (addressStr === '') {
addressStr = props.name || props.full_address || '-';
}
this._streetAddress.textContent = addressStr;
// Post Code
this._postCode.textContent = context.postcode.name || '-';
// District (could be district, locality, or place)
this._district.textContent = context.district.name || context.place.name || context.locality.name || '-';
// Region (state, province, etc.)
this._region.textContent = context.region.name || '-';
// Country
this._country.textContent = context.country.name || '-';
}
}
customElements.define('address-display', AddressDisplay);

View file

@ -0,0 +1,226 @@
class AddressInput extends HTMLElement {
constructor() {
super();
// Create a shadow DOM
this.attachShadow({mode: "open" });
// Initial render
this.render();
// Element references
this._input = this.shadowRoot.querySelector('input');
this._suggestions = this.shadowRoot.querySelector('.suggestions-container');
// Bind methods
this._handleInput = this._handleInput.bind(this);
// Debounce timer
this._debounceTimer = null;
// The suggestion data
this._suggestionData = null;
}
// Lifecycle: when element is added to the DOM
connectedCallback() {
this._input.addEventListener("input", this._handleInput);
}
// Lifecycle: when element is removed from the DOM
disconnectedCallback() {
this._input.removeEventListener('input', this._handleInput);
}
// Lifecycle: watch these attributes for changes
static get observedAttributes() {
return ['placeholder', 'api-key'];
}
// Lifecycle: respond to attribute changes
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'placeholder' && this._input) {
this._input.placeholder = newValue;
}
if (name === 'api-key') {
this._apiKey = newValue;
}
}
// Properties API
get value() {
return this._input ? this._input.value : '';
}
set value(val) {
if (this._input) {
this._input.value = val;
}
}
// Private methods
_handleInput(event) {
const searchText = event.target.value.trim();
// Clear previous timer
clearTimeout(this._debounceTimer);
// Clear suggestions if input is less than 3 characters
if (searchText.length < 3) {
this._suggestions.innerHTML = '';
return;
}
// Debounce API calls (wait 300ms after typing stops)
this._debounceTimer = setTimeout(() => {
this._fetchAddressSuggestions(searchText)
.then(response => {
this._renderSuggestions(response.features);
});
}, 300);
}
async _fetchAddressSuggestions(text) {
try {
const url = `https://api.mapbox.com/search/geocode/v6/forward?q=${encodeURIComponent(text)}&access_token=${this._apiKey}`;
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching geocoding suggestions:', error);
}
}
_renderSuggestions(suggestions) {
console.log("Rendering suggestions", suggestions);
this._suggestions.innerHTML = suggestions.map((item, index) => {
if (item.properties.place_formatted != "") {
return `
<div class="suggestion-item list-group-item"
data-index="${index}"
data-lat="${item.geometry.coordinates[1]}"
data-lng="${item.geometry.coordinates[0]}">
<div class="main-address">${item.properties.name || item.properties.full_address}</div>
<div class="place-info">${item.properties.place_formatted}</div>
</div>`
} else {
return `
<div class="suggestion-item list-group-item"
data-index="${index}"
data-lat="${item.coordinates.lat}"
data-lng="${item.coordinates.lng}">
<div class="main-address">${item.properties.name || item.properties.full_address}</div>
<div class="place-info">${item.properties.place_formatted}</div>
</div>`
}
}).join('');
// Add click listeners to suggestions
this.shadowRoot.querySelectorAll('.suggestion-item').forEach(el => {
el.addEventListener('click', e => {
const index = parseInt(el.dataset.index);
const suggestion = suggestions[index];
this.value = suggestion.properties.full_address;
this._suggestions.innerHTML = '';
// Dispatch custom event
this.dispatchEvent(new CustomEvent('address-selected', {
bubbles: true,
composed: true, // Allows event to cross shadow DOM boundary
detail: {
location: suggestion
}
}));
});
});
}
// Initial render of component
render() {
const placeholder = this.getAttribute('placeholder') || 'Enter address';
this.shadowRoot.innerHTML = `
<style>
@import url('/static/vendor/css/bootstrap.min.css');
.detail-label {
font-size: 0.8rem;
text-transform: uppercase;
color: #6c757d;
margin-bottom: 2px;
font-weight: 600;
}
.detail-value {
font-weight: 500;
}
.main-address {
font-weight: 500;
}
.place-info {
font-size: 0.85rem;
color: #6c757d;
margin-top: 2px;
}
.suggestions-container {
position: absolute;
width: 100%;
max-height: 300px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.suggestion-item {
cursor: pointer;
padding: 10px 12px;
border-bottom: 1px solid #f0f0f0;
}
.suggestion-item:hover {
background-color: #f8f9fa;
}
</style>
<label for="addressInput" class="form-label">Enter address</label>
<input type="text" class="form-control" id="address" name="address" placeholder="${placeholder}">
<div id="suggestions" class="suggestions-container list-group"></div>
`;
}
// Public methods
clear() {
if (this._input) {
this._input.value = '';
this._suggestions.innerHTML = '';
}
}
}
customElements.define('address-input', AddressInput);
function setLocationInputs(suggestion) {
let address = document.getElementById('address');
let country = document.getElementById('address-country');
let latitude = document.getElementById('latitude');
let longitude = document.getElementById('longitude');
let latlngAccuracyType = document.getElementById('latlng-accuracy-type');
let postcode = document.getElementById('address-postcode');
let place = document.getElementById('address-place');
let region = document.getElementById('address-region');
let street = document.getElementById('address-street');
// Extract context data from properties
const props = suggestion.properties;
const context = props.context || {};
// Populate structured fields
address.value = props.full_address;
country.value = context.country.name;
latitude.value = props.coordinates.latitude;
longitude.value = props.coordinates.longitude;
latlngAccuracyType.value = props.coordinates.accuracy;
postcode.value = context.postcode.name;
place.value = context.place.name;
region.value = context.region.name;
street.value = context.country.name;
}

View file

@ -8,6 +8,4 @@ async function geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat) {
return; return;
} }
const match = data.features[0]; const match = data.features[0];
displaySelectedLocation(match);
setLocationInputs(match);
} }

View file

@ -13,7 +13,7 @@ var embeddedFiles embed.FS
//go:embed static/* //go:embed static/*
var EmbeddedStaticFS embed.FS var EmbeddedStaticFS embed.FS
var components = [...]string{"footer", "location-geocode", "location-geocode-header", "photo-upload", "photo-upload-header"} var components = [...]string{"footer", "photo-upload", "photo-upload-header"}
func buildTemplate(files ...string) *htmlpage.BuiltTemplate { func buildTemplate(files ...string) *htmlpage.BuiltTemplate {
subdir := "public-report" subdir := "public-report"

View file

@ -1,206 +0,0 @@
{{define "location-geocode-header"}}
<style>
.detail-label {
font-size: 0.8rem;
text-transform: uppercase;
color: #6c757d;
margin-bottom: 2px;
font-weight: 600;
}
.detail-value {
font-weight: 500;
}
.main-address {
font-weight: 500;
}
.place-info {
font-size: 0.85rem;
color: #6c757d;
margin-top: 2px;
}
.suggestions-container {
position: absolute;
width: 100%;
max-height: 300px;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.suggestion-item {
cursor: pointer;
padding: 10px 12px;
border-bottom: 1px solid #f0f0f0;
}
.suggestion-item:hover {
background-color: #f8f9fa;
}
</style>
<script>
// Display suggestions in the dropdown
function displaySuggestions(suggestions) {
const suggestionsContainer = document.getElementById('suggestions');
// Clear previous suggestions
suggestionsContainer.innerHTML = '';
if (suggestions.length === 0) {
suggestionsContainer.classList.add('d-none');
return;
}
// Create and append suggestion items
suggestions.forEach(suggestion => {
const item = document.createElement('div');
item.className = 'suggestion-item list-group-item';
// Create structure for the main address and place info
const mainAddressDiv = document.createElement('div');
mainAddressDiv.className = 'main-address';
mainAddressDiv.textContent = suggestion.properties.name || suggestion.properties.full_address;
item.appendChild(mainAddressDiv);
if (suggestion.properties.place_formatted) {
const placeInfoDiv = document.createElement('div');
placeInfoDiv.className = 'place-info';
placeInfoDiv.textContent = suggestion.properties.place_formatted || '';
item.appendChild(placeInfoDiv);
}
// Handle click on a suggestion
item.addEventListener('click', function() {
// Hide the suggestions container
setLocationInputs(suggestion);
suggestionsContainer.classList.add('d-none');
displaySelectedLocation(suggestion);
const locationSelected = new CustomEvent("locationselected", {
detail: {
coordinates: suggestion.geometry.coordinates,
},
});
suggestionsContainer.dispatchEvent(locationSelected);
});
suggestionsContainer.appendChild(item);
});
// Show the suggestions container
suggestionsContainer.classList.remove('d-none');
}
// Fetch suggestions from Mapbox API
async function fetchGeocodingSuggestions(query) {
// Replace with your actual Mapbox access token
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
try {
const url = `https://api.mapbox.com/search/geocode/v6/forward?q=${encodeURIComponent(query)}&access_token=${MAPBOX_ACCESS_TOKEN}`;
const response = await fetch(url);
const data = await response.json();
displaySuggestions(data.features || []);
} catch (error) {
console.error('Error fetching geocoding suggestions:', error);
}
}
function displaySelectedLocation(suggestion) {
const locationDisplayContainer = document.getElementById('locationDisplayContainer');
// Show location display container
locationDisplayContainer.classList.remove('d-none');
// Extract context data from properties
const props = suggestion.properties;
const context = props.context || {};
// Populate structured fields
// Street Address - combine address, street, housenumber if available
let addressStr = '';
if (context.address) addressStr += context.address.address_number;
if (context.street) {
if (addressStr) addressStr += ' ';
addressStr += context.street.name;
}
if (addressStr === '') {
addressStr = props.name || props.full_address || '-';
}
streetAddress.textContent = addressStr;
// Post Code
postCode.textContent = context.postcode.name || '-';
// District (could be district, locality, or place)
district.textContent = context.district.name || context.place.name || context.locality.name || '-';
// Region (state, province, etc.)
region.textContent = context.region.name || '-';
// Country
country.textContent = context.country.name || '-';
}
function onAddressInput() {
const suggestionsContainer = document.getElementById('suggestions');
let debounceTimer;
const query = this.value.trim();
// Clear previous timer
clearTimeout(debounceTimer);
// Clear suggestions if input is less than 3 characters
if (query.length < 3) {
suggestionsContainer.classList.add('d-none');
suggestionsContainer.innerHTML = '';
return;
}
// Debounce API calls (wait 300ms after typing stops)
debounceTimer = setTimeout(() => {
fetchGeocodingSuggestions(query);
}, 300);
}
function setLocationInputs(suggestion) {
let address = document.getElementById('address');
let country = document.getElementById('address-country');
let latitude = document.getElementById('latitude');
let longitude = document.getElementById('longitude');
let latlngAccuracyType = document.getElementById('latlng-accuracy-type');
let postcode = document.getElementById('address-postcode');
let place = document.getElementById('address-place');
let region = document.getElementById('address-region');
let street = document.getElementById('address-street');
// Extract context data from properties
const props = suggestion.properties;
const context = props.context || {};
// Populate structured fields
address.value = props.full_address;
country.value = context.country.name;
latitude.value = props.coordinates.latitude;
longitude.value = props.coordinates.longitude;
latlngAccuracyType.value = props.coordinates.accuracy;
postcode.value = context.postcode.name;
place.value = context.place.name;
region.value = context.region.name;
street.value = context.country.name;
}
document.addEventListener('DOMContentLoaded', function() {
const address = document.getElementById('address');
const suggestionsContainer = document.getElementById('suggestions');
const locationDetails = document.getElementById('locationDetails');
// Listen for input changes
address.addEventListener('input', onAddressInput);
// Close suggestions when clicking outside
document.addEventListener('click', function(event) {
if (!address.contains(event.target) && !suggestionsContainer.contains(event.target)) {
suggestionsContainer.classList.add('d-none');
}
});
});
</script>
{{end}}

View file

@ -1,52 +0,0 @@
{{define "location-geocode"}}
<!-- Hidden fields for location data -->
<input type="hidden" id="address-country" name="address-country"/>
<input type="hidden" id="address-postcode" name="address-postcode"/>
<input type="hidden" id="address-place" name="address-place"/>
<input type="hidden" id="address-region" name="address-region"/>
<input type="hidden" id="address-street" name="address-street"/>
<input type="hidden" id="latitude" name="latitude"/>
<input type="hidden" id="longitude" name="longitude"/>
<input type="hidden" id="latlng-accuracy-type" name="latlng-accuracy-type"/>
<input type="hidden" id="latlng-accuracy-value" name="latlng-accuracy-value"/>
<div class="col-md-6">
<div class="mb-3 position-relative">
<label for="addressInput" class="form-label">Enter address</label>
<input type="text" class="form-control" id="address" name="address"
placeholder="Start typing an address (min 3 characters)">
<div id="suggestions" class="suggestions-container list-group d-none"></div>
</div>
<div class="mt-3">
<!-- Structured Location Display -->
<div id="locationDisplayContainer" class="mt-4 d-none">
<h5 class="mb-3">Location Details</h5>
<div class="location-card p-3 mb-3">
<div class="location-detail">
<div class="detail-label">Street Address</div>
<div id="streetAddress" class="detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Post Code</div>
<div id="postCode" class="detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">District/Place</div>
<div id="district" class="detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Region/State</div>
<div id="region" class="detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Country</div>
<div id="country" class="detail-value">-</div>
</div>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -2,9 +2,10 @@
{{define "title"}}Green Pool{{end}} {{define "title"}}Green Pool{{end}}
{{define "extraheader"}} {{define "extraheader"}}
{{template "location-geocode-header" .}}
<script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script> <script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' /> <link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
<script src="/static/js/address-display.js"></script>
<script src="/static/js/address-suggestion.js"></script>
<script src="/static/js/geocode.js"></script> <script src="/static/js/geocode.js"></script>
<script src="/static/js/location.js"></script> <script src="/static/js/location.js"></script>
<script src="/static/js/map.js"></script> <script src="/static/js/map.js"></script>
@ -163,6 +164,31 @@ function onMapMarkerDragEnd(marker) {
geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat); geocodeReverse(MAPBOX_ACCESS_TOKEN, lngLat);
} }
function setLocationInputs(location) {
let country = document.getElementById('address-country');
let latitude = document.getElementById('latitude');
let longitude = document.getElementById('longitude');
let latlngAccuracyType = document.getElementById('latlng-accuracy-type');
let postcode = document.getElementById('address-postcode');
let place = document.getElementById('address-place');
let region = document.getElementById('address-region');
let street = document.getElementById('address-street');
// Extract context data from properties
const props = location.properties;
const context = props.context || {};
// Populate structured fields
country.value = context.country.name;
latitude.value = props.coordinates.latitude;
longitude.value = props.coordinates.longitude;
latlngAccuracyType.value = props.coordinates.accuracy;
postcode.value = context.postcode.name;
place.value = context.place.name;
region.value = context.region.name;
street.value = context.country.name;
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Elements // Elements
const photoInput = document.getElementById('photos'); const photoInput = document.getElementById('photos');
@ -224,9 +250,21 @@ document.addEventListener('DOMContentLoaded', function() {
onMapMarkerDragEnd(e.detail.marker); onMapMarkerDragEnd(e.detail.marker);
}); });
const suggestionsContainer = document.getElementById('suggestions'); const addressDisplay = document.querySelector("address-display");
suggestionsContainer.addEventListener("locationselected", (e) => { const addressInput = document.querySelector("address-input");
mapSetMarker(e.detail.coordinates); const mapComponent = document.querySelector("map-component");
addressInput.addEventListener("address-selected", (event) => {
const l = event.detail.location;
console.log("Address selected", l);
// Center map on selected address
mapSetMarker(l.geometry.coordinates);
mapJumpTo({
center: l.geometry.coordinates,
zoom: 14,
});
addressDisplay.show(l);
setLocationInputs(l);
}); });
}); });
function displaySelectedCoordinates(lngLat) { function displaySelectedCoordinates(lngLat) {
@ -284,18 +322,26 @@ function displaySelectedCoordinates(lngLat) {
<p class="mb-3">Please provide the location of the potential mosquito breeding source. We may be able to extract this information from your photos if they contain location data.</p> <p class="mb-3">Please provide the location of the potential mosquito breeding source. We may be able to extract this information from your photos if they contain location data.</p>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-12"> <!-- Hidden fields for location data -->
{{template "location-geocode"}} <input type="hidden" id="address-country" name="address-country"/>
<input type="hidden" id="address-postcode" name="address-postcode"/>
<input type="hidden" id="address-place" name="address-place"/>
<input type="hidden" id="address-region" name="address-region"/>
<input type="hidden" id="address-street" name="address-street"/>
<input type="hidden" id="latitude" name="latitude"/>
<input type="hidden" id="longitude" name="longitude"/>
<input type="hidden" id="latlng-accuracy-type" name="latlng-accuracy-type"/>
<input type="hidden" id="latlng-accuracy-value" name="latlng-accuracy-value"/>
<div class="col-md-6">
<div class="mb-3 position-relative">
<address-input
placeholder="Start typing an address (min 3 characters)"
api-key="{{ .MapboxToken }}">
</address-input>
</div>
</div> </div>
<div class="col-md-12 d-none" id="gps-display"> <div class="col-md-6">
<div class="location-detail"> <address-display></address-display>
<div class="detail-label">Longitude</div>
<div id="longitude" class="detail-value">-</div>
</div>
<div class="location-detail">
<div class="detail-label">Latitude</div>
<div id="latitude" class="detail-value">-</div>
</div>
</div> </div>
</div> </div>

View file

@ -4,6 +4,8 @@
{{define "extraheader"}} {{define "extraheader"}}
<script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script> <script src='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' /> <link href='https://api.mapbox.com/mapbox-gl-js/v3.17.0-beta.1/mapbox-gl.css' rel='stylesheet' />
<script src="/static/js/address-display.js"></script>
<script src="/static/js/address-suggestion.js"></script>
<script src="/static/js/geocode.js"></script> <script src="/static/js/geocode.js"></script>
<script src="/static/js/location.js"></script> <script src="/static/js/location.js"></script>
<script src="/static/js/map.js"></script> <script src="/static/js/map.js"></script>
@ -11,7 +13,7 @@
const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}'; const MAPBOX_ACCESS_TOKEN = '{{.MapboxToken}}';
function setDistrictColors(map) { function setDistrictColors(map) {
const features = map.querySourceFeatures('tegola-mosquito', {sourceLayer: 'district'}); const features = map.querySourceFeatures('tegola-mosquito', {sourceLayer: 'district'});
console.log("features", features); //console.log("features", features);
const regionIds = [...new Set(features.map(f => f.properties.regionid))]; const regionIds = [...new Set(features.map(f => f.properties.regionid))];
map.setPaintProperty('districts', 'fill-opacity', 0.4); map.setPaintProperty('districts', 'fill-opacity', 0.4);
if (regionIds.length > 0) { if (regionIds.length > 0) {
@ -26,11 +28,11 @@ function setDistrictColors(map) {
matchExpression.push(id, colorScale[id]); matchExpression.push(id, colorScale[id]);
}); });
matchExpression.push('#cccccc'); // Default color matchExpression.push('#cccccc'); // Default color
console.log("using district coloring", matchExpression); //console.log("using district coloring", matchExpression);
map.setPaintProperty('districts', 'fill-color', matchExpression); map.setPaintProperty('districts', 'fill-color', matchExpression);
} else { } else {
map.setPaintProperty('districts', 'fill-color', 'rgb(250, 100, 100)'); map.setPaintProperty('districts', 'fill-color', 'rgb(250, 100, 100)');
console.log("using fallback district coloring"); //console.log("using fallback district coloring");
} }
} }
function onLoad() { function onLoad() {
@ -88,7 +90,6 @@ function onLoad() {
} }
}); });
map.on('sourcedata', (e) => { map.on('sourcedata', (e) => {
console.log("source loaded", e);
if (e.sourceId == 'tegola-mosquito' && e.isSourceLoaded) { if (e.sourceId == 'tegola-mosquito' && e.isSourceLoaded) {
setDistrictColors(map) setDistrictColors(map)
} }
@ -98,6 +99,26 @@ function onLoad() {
map.addControl(new mapboxgl.NavigationControl()); map.addControl(new mapboxgl.NavigationControl());
console.log("Map init done."); console.log("Map init done.");
/*const address = document.getElementById('address');
const suggestionsContainer = document.getElementById('suggestions');
addressSuggestionEnable(address, suggestionsContainer);*/
const addressDisplay = document.querySelector("address-display");
const addressInput = document.querySelector("address-input");
const mapComponent = document.querySelector("map-component");
addressInput.addEventListener("address-selected", (event) => {
const l = event.detail.location;
// Center map on selected address
//mapComponent.jumpTo(coordinates);
// Add marker for selected address
/*mapComponent.addMarker(coordinates, {
title: event.detail.address
});*/
addressDisplay.show(l);
});
} }
document.addEventListener("DOMContentLoaded", onLoad); document.addEventListener("DOMContentLoaded", onLoad);
</script> </script>
@ -176,10 +197,23 @@ body {
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="container my-4"> <div class="container my-4">
<div class="card-body p-0">
<div class="mb-3 position-relative">
<address-input
placeholder="Start typing an address (min 3 characters)"
api-key="{{ .MapboxToken }}">
</address-input>
</div>
<div class="mt-3">
</div>
</div>
<div class="card-body p-0"> <div class="card-body p-0">
<div class="map-container" id="map-container"> <div class="map-container" id="map-container">
<div id="map"></div> <div id="map"></div>
</div> </div>
</div> </div>
<div class="card-body p-0">
<address-display></address-display>
</div>
</div> </div>
{{end}} {{end}}