-
- {% svgIcon 'search.svg' %}
+
+
+
+ {% svgIcon 'search.svg' %}
+
+
+
+
+
-
+ {% include '../../src/_includes/country-selector.njk' %}
+
+
+
+
-
Enter an address to geocode or click on the map to reverse geocode.
-
+
{% endblock %}
diff --git a/samples/localities-geocode/index.ts b/samples/localities-geocode/index.ts
index c2ab6083..7ee8976c 100644
--- a/samples/localities-geocode/index.ts
+++ b/samples/localities-geocode/index.ts
@@ -1,24 +1,40 @@
// [START woosmap_localities_geocode]
// Initialize and add the map
+import LocalitiesGeocodeRequest = woosmap.map.localities.LocalitiesGeocodeRequest;
+
let map: woosmap.map.Map;
let marker: woosmap.map.Marker;
let infoWindow: woosmap.map.InfoWindow;
+let pr_marker: woosmap.map.Marker;
+let pr_infoWindow: HTMLElement | null;
let localitiesService: woosmap.map.LocalitiesService;
+let suggestionsList: HTMLUListElement;
+let prSuggestionsList: HTMLUListElement;
const request: woosmap.map.localities.LocalitiesGeocodeRequest = {};
+const componentsRestriction: woosmap.map.localities.LocalitiesComponentRestrictions =
+ { country: [] };
function initMap() {
map = new window.woosmap.map.Map(
document.getElementById("map") as HTMLElement,
{
- center: { lat: 51.50940214, lng: -0.133012 },
+ center: { lat: 43.77751768293183, lng: 11.2553439740075 },
zoom: 12,
},
);
infoWindow = new woosmap.map.InfoWindow({});
+ pr_infoWindow = document.getElementById("instructions")
localitiesService = new woosmap.map.LocalitiesService();
+ suggestionsList = document.getElementById(
+ "suggestions-list",
+ ) as HTMLUListElement;
+ prSuggestionsList = document.getElementById(
+ "pr-suggestions-list",
+ ) as HTMLUListElement;
map.addListener("click", (e) => {
handleGeocode(e.latlng);
});
+ manageCountrySelector();
}
const inputElement = document.getElementById(
@@ -49,9 +65,88 @@ clearSearchBtn.addEventListener("click", () => {
marker.setMap(null);
infoWindow.close();
}
+ if (pr_marker) {
+ pr_marker.setMap(null);
+ }
inputElement.focus();
});
+function displaySuggestions(
+ localitiesPredictions: woosmap.map.localities.LocalitiesDetailsSummary[],
+ container: HTMLUListElement = suggestionsList
+) {
+ if (inputElement && container) {
+ container.innerHTML = "";
+ if (localitiesPredictions.length > 0) {
+ localitiesPredictions.forEach((locality) => {
+ const li = document.createElement("li");
+ li.innerHTML = locality["description"] ?? "";
+ container.appendChild(li);
+ });
+ container.style.display = "block";
+ clearSearchBtn.style.display = "block";
+ } else {
+ container.style.display = "none";
+ }
+ }
+}
+
+function buildQueryString(params: object) {
+ const queryStringParts = [];
+
+ for (const key in params) {
+ if (params[key]) {
+ const value = params[key];
+ queryStringParts.push(
+ `${encodeURIComponent(key)}=${encodeURIComponent(value)}` as never,
+ );
+ }
+ }
+ return queryStringParts.join("&");
+}
+
+function getSecondaryUrl():string {
+ let secondary_target = document.getElementById("secondary-target") as HTMLInputElement
+ if (secondary_target && secondary_target.value) {
+ return `https://develop-api.woosmap.com/${secondary_target.value}`
+ }
+ return "https://develop-api.woosmap.com"
+}
+
+const pr_reverse_geocode = async (request:LocalitiesGeocodeRequest): Promise
=> {
+ let params = {
+ "key": "woos-b2f35903-92d8-3a95-9b35-dd503c752a51"
+ }
+ if (request.latLng) {
+ params["latlng"] = `${request.latLng.lat},${request.latLng.lng}`;
+ }
+ if (request.address) {
+ params["address"] = request.address;
+ }
+ if(request.components) {
+ params["components"] = (request.components.country as string[])
+ .map((country) => `country:${country}`)
+ .join("|");
+ }
+ if (request.list_sub_buildings) {
+ params["list_sub_buildings"] = "1";
+ }
+ // let list_sub_buildings: HTMLInputElement= document.getElementById("list-sub-buildings") as HTMLInputElement;
+ // if (list_sub_buildings && list_sub_buildings.checked) {
+ // console.log("list sub buildings")
+ // params["list_sub_buildings"] = "1";
+ // }
+ try {
+ const response = await fetch(
+ `${getSecondaryUrl()}/localities/geocode?${buildQueryString(params)}`
+ );
+ return await response.json();
+ } catch (error) {
+ console.error("Error fetching PR:", error);
+ throw error;
+ }
+};
+
function handleGeocode(latlng: woosmap.map.LatLngLiteral | null) {
if (latlng) {
request.latLng = latlng;
@@ -60,12 +155,20 @@ function handleGeocode(latlng: woosmap.map.LatLngLiteral | null) {
request.address = inputElement.value;
delete request.latLng;
}
+ request.components = componentsRestriction
+ let list_sub_buildings: HTMLInputElement= document.getElementById("list-sub-buildings") as HTMLInputElement;
+ request.list_sub_buildings = (list_sub_buildings && list_sub_buildings.checked);
+ displaySuggestions([], suggestionsList)
+ displaySuggestions([], prSuggestionsList)
if (request.latLng || request.address) {
localitiesService
.geocode(request)
.then((localities) => displayLocality(localities.results[0]))
.catch((error) => console.error("Error geocoding localities:", error));
+ pr_reverse_geocode(request).then((localities:woosmap.map.localities.LocalitiesGeocodeResponse) => displayPRLocality(localities.results[0]))
+ .catch((error) => console.error("Error geocoding localities:", error));
+
}
}
@@ -92,7 +195,40 @@ function displayLocality(
infoWindow.setContent(`${locality.formatted_address}`);
infoWindow.open(map, marker);
map.setCenter(locality.geometry.location);
- map.setZoom(14);
+ if (map.getZoom() < 14) {
+ map.setZoom(14);
+ }
+ if (locality.sub_buildings){
+ displaySuggestions(locality.sub_buildings, suggestionsList)
+ }
+ }
+}
+
+function displayPRLocality(
+ locality: woosmap.map.localities.LocalitiesGeocodeResult | null,
+) {
+ if (pr_marker) {
+ pr_marker.setMap(null);
+ }
+
+ if (locality?.geometry) {
+ pr_marker = new woosmap.map.Marker({
+ position: locality.geometry.location,
+ icon: {
+ url: "https://images.woosmap.com/marker-red.png",
+ scaledSize: {
+ height: 50,
+ width: 32,
+ },
+ },
+ });
+ pr_marker.setMap(map);
+ if (pr_infoWindow) {
+ pr_infoWindow.textContent = `[PR says] ${locality.formatted_address} (red marker)`;
+ }
+ if (locality.sub_buildings){
+ displaySuggestions(locality.sub_buildings, prSuggestionsList)
+ }
}
}
@@ -104,4 +240,101 @@ declare global {
window.initMap = initMap;
// [END woosmap_localities_geocode]
+function manageCountrySelector() {
+ const countryElements = document.querySelectorAll(".country");
+ countryElements.forEach((countryElement: Element) => {
+ countryElement.addEventListener("click", () => {
+ toggleCountry(countryElement);
+ });
+ if (countryElement.classList.contains("active")) {
+ const countryCode = (countryElement as HTMLElement).dataset
+ .countrycode as string;
+ componentsRestriction.country = [
+ ...(componentsRestriction.country as string[]),
+ countryCode,
+ ];
+ }
+ });
+
+ const dropdownButtons = document.querySelectorAll(
+ ".dropdown .dropdown-button",
+ );
+ dropdownButtons.forEach((button: Element) =>
+ button.addEventListener("click", toggleDropdown),
+ );
+
+ // Hide dropdowns when clicking outside
+ const dropdowns = document.querySelectorAll(".dropdown");
+ document.addEventListener("click", (event: Event) => {
+ dropdowns.forEach((dropdown: Element) => {
+ if (!dropdown.contains(event.target as Node)) {
+ hideDropdown(dropdown);
+ }
+ });
+ });
+}
+
+function toggleDropdown(event: Event) {
+ event.stopPropagation();
+ const dropdown = (event.target as Element).closest(".dropdown");
+ if (dropdown) {
+ if (dropdown.classList.contains("active")) {
+ hideDropdown(dropdown);
+ } else {
+ showDropdown(dropdown);
+ }
+ }
+}
+
+function hideDropdown(dropdown: Element) {
+ const dropdownContent = dropdown.querySelector(
+ ".dropdown-content",
+ ) as HTMLElement;
+ dropdownContent?.classList.remove("visible");
+ dropdown.classList.remove("active");
+}
+
+function showDropdown(dropdown: Element) {
+ const dropdownContent = dropdown.querySelector(
+ ".dropdown-content",
+ ) as HTMLElement;
+ dropdownContent?.classList.add("visible");
+ dropdown.classList.add("active");
+}
+
+function toggleCountry(country: Element) {
+ const isActive = country.classList.toggle("active");
+ const countryCode = (country as HTMLElement).dataset.countrycode;
+
+ if (countryCode) {
+ if (isActive) {
+ componentsRestriction.country = [
+ ...(componentsRestriction.country as string[]),
+ countryCode,
+ ];
+ } else {
+ componentsRestriction.country = (
+ componentsRestriction.country as string[]
+ ).filter((code) => code !== countryCode);
+ }
+ updateCountrySelectorText();
+ }
+}
+
+function updateCountrySelectorText() {
+ const dropdownText = document.querySelector(
+ ".dropdown-button span",
+ ) as HTMLElement;
+ const inputPlaceholder = document.querySelector("#autocomplete-input") as HTMLInputElement;
+ if (componentsRestriction.country.length > 0) {
+ inputElement.readOnly = false;
+ dropdownText.innerHTML = `Selected countries: ${(componentsRestriction.country as string[]).join(", ")}`;
+ inputPlaceholder.placeholder = `Search for a place in ${(componentsRestriction.country as string[]).join(" or ")}...`
+ } else {
+ dropdownText.textContent = "Select countries";
+ inputElement.readOnly = true;
+ inputPlaceholder.placeholder = "Select at least one country to proceed."
+ }
+}
+
export {};
diff --git a/samples/localities-geocode/style.scss b/samples/localities-geocode/style.scss
index 0a3a943c..05f0c232 100644
--- a/samples/localities-geocode/style.scss
+++ b/samples/localities-geocode/style.scss
@@ -2,13 +2,25 @@
/* [START woosmap_localities_geocode] */
@include meta.load-css("../../shared/scss/_default.scss");
-@include meta.load-css("../../shared/scss/_autocomplete_input.scss");
+/* @include meta.load-css("../../shared/scss/_autocomplete_input.scss");*/
+@include meta.load-css("../../shared/scss/_dropdown.scss");
@import "../../shared/scss/mixins.scss";
+@include meta.load-css("../../shared/scss/_pr_test.scss");
#app {
height: 100%;
}
-
+#map{
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+.search-container {
+ top: 10px;
+ left: 10px;
+}
.btn {
@include button($background: #3d5afe, $display: inline, $position: relative, $height: 38px);
}
@@ -16,7 +28,21 @@
.btnText {
@include actionText($color: #fff);
}
-
+.checkbox-container{
+ margin: 0 5px;
+ border-radius: 10px;
+ border-color: transparent;
+ height: 42px;
+ padding: 0 8px;
+ align-content: center;
+ font-size: 14px;
+ font-weight: 400;
+ background: #f9fbfd;
+ box-shadow: 0 2px 4px #0003, 0 -1px #00000005;
+}
+#countrySelector{
+ margin: 0 5px;
+}
#instructions {
max-width: 300px;
font-size: 17px;
diff --git a/samples/localities-search/index.njk b/samples/localities-search/index.njk
index d25cc684..d13af8dd 100644
--- a/samples/localities-search/index.njk
+++ b/samples/localities-search/index.njk
@@ -11,9 +11,7 @@
-
+
{% svgIcon 'search.svg' %}
{% svgIcon 'clear.svg' %}
-
+
+
+
+
+
+ {% include '../../src/_includes/country-selector.njk' %}
+
+
- {% include '../../src/_includes/country-selector.njk' %}
{% endblock %}
diff --git a/samples/localities-search/index.ts b/samples/localities-search/index.ts
index e7dea6fc..4d931c9f 100644
--- a/samples/localities-search/index.ts
+++ b/samples/localities-search/index.ts
@@ -4,6 +4,7 @@ let marker: woosmap.map.Marker;
let infoWindow: woosmap.map.InfoWindow;
let localitiesService: woosmap.map.LocalitiesService;
let debouncedLocalitiesSearch: (...args: any[]) => Promise;
+let debouncedPRSearch: (...args: any[]) => Promise;
let input: string;
let detailsHTML: HTMLElement;
let detailsResultContainer: HTMLElement;
@@ -36,6 +37,7 @@ function initMap(): void {
localitiesService = new window.woosmap.map.LocalitiesService();
debouncedLocalitiesSearch = debouncePromise(fetchLocalitiesSearch, 0);
+ debouncedPRSearch = debouncePromise(fetchPRSearch, 0);
manageCountrySelector();
}
@@ -59,6 +61,34 @@ https://api.woosmap.com/localities/search?types=point_of_interest|locality|admin
}
};
+function getSecondaryUrl():string {
+ let secondary_target = document.getElementById("secondary-target") as HTMLInputElement
+ if (secondary_target && secondary_target.value) {
+ return `https://develop-api.woosmap.com/${secondary_target.value}`
+ }
+ return "https://develop-api.woosmap.com"
+}
+
+const fetchPRSearch = async (input: any): Promise => {
+ const center = map.getCenter();
+ const radius = map.getZoom() > 10 ? (map.getZoom() > 14 ? "1000" : "10000") : "100000";
+ const componentsArgs: string = (componentsRestriction.country as string[])
+ .map((country) => `country:${country}`)
+ .join("|");
+
+
+ try {
+ const response = await fetch(
+ `${getSecondaryUrl()}/localities/search?types=point_of_interest|locality|admin_level|postal_code|address&input=${encodeURIComponent(input)}&location=${center.lat()},${center.lng()}&radius=${radius}&key=woos-b2f35903-92d8-3a95-9b35-dd503c752a51&components=${componentsArgs}`
+ );
+ return await response.json();
+ } catch (error) {
+ console.error("Error fetching PR:", error);
+ throw error;
+ }
+};
+
+
function fillDetailsResult(detailsResult: any) {
const details: string[] = [];
detailsHTML.innerHTML = "";
@@ -105,6 +135,9 @@ const inputElement = document.getElementById(
const suggestionsList = document.getElementById(
"suggestions-list"
) as HTMLUListElement;
+const prSuggestionsList = document.getElementById(
+ "pr-suggestions-list"
+) as HTMLUListElement;
const clearSearchBtn = document.getElementsByClassName(
"clear-searchButton"
)[0] as HTMLButtonElement;
@@ -145,6 +178,11 @@ function handleAutocomplete(): void {
.catch((error) =>
console.error("Error autocomplete localities:", error)
);
+ debouncedPRSearch(input)
+ .then((results) => displaySuggestions(results, prSuggestionsList))
+ .catch((error) =>
+ console.error("Error autocomplete localities:", error)
+ );
} else {
suggestionsList.style.display = "none";
clearSearchBtn.style.display = "none";
@@ -190,9 +228,12 @@ function displayResult(result: woosmap.map.localities.LocalitiesDetailsResult) {
}
}
-function displaySuggestions(localitiesPredictions: any) {
+function displaySuggestions(localitiesPredictions: any, container:any=null) {
+ if (container == null) {
+ container=suggestionsList
+ }
if (inputElement && suggestionsList) {
- suggestionsList.innerHTML = "";
+ container.innerHTML = "";
if (localitiesPredictions.results.length > 0 && input) {
localitiesPredictions.results.forEach((result) => {
const li = document.createElement("li");
@@ -204,10 +245,10 @@ function displaySuggestions(localitiesPredictions: any) {
desc.className = "localities-search-description";
li.addEventListener("click", () => {
inputElement.value = result.title ?? "";
- suggestionsList.style.display = "none";
+ container.style.display = "none";
handleDetails(result.public_id);
});
- suggestionsList.appendChild(li);
+ container.appendChild(li);
li.appendChild(title);
title.appendChild(desc);
if (result.categories) {
@@ -222,10 +263,10 @@ function displaySuggestions(localitiesPredictions: any) {
title.appendChild(type);
}
});
- suggestionsList.style.display = "block";
+ container.style.display = "block";
clearSearchBtn.style.display = "block";
} else {
- suggestionsList.style.display = "none";
+ container.style.display = "none";
}
}
}
@@ -242,7 +283,6 @@ document.addEventListener("click", (event) => {
});
// [START woosmap_localities_search_debounce_promise]
-let PRESERVE_COMMENT_ABOVE; // force tsc to maintain the comment above eslint-disable-line
type DebouncePromiseFunction = (
...args: Args
@@ -281,7 +321,6 @@ function debouncePromise(
}
// [END woosmap_localities_search_debounce_promise] */
-PRESERVE_COMMENT_ABOVE; // force tsc to maintain the comment above eslint-disable-line
declare global {
interface Window {
diff --git a/samples/localities-search/style.scss b/samples/localities-search/style.scss
index 729e9f05..b0b373cf 100644
--- a/samples/localities-search/style.scss
+++ b/samples/localities-search/style.scss
@@ -6,6 +6,7 @@
@include meta.load-css("../../shared/scss/_autocomplete_list.scss");
@include meta.load-css("../../shared/scss/_dropdown.scss");
@import "../../shared/scss/mixins.scss";
+@include meta.load-css("../../shared/scss/_pr_test.scss");
#app {
height: 100%;
@@ -16,12 +17,21 @@ body {
overflow-y: hidden;
}
-#countrySelector {
+#map{
position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+#countrySelector {
+ left: 10px;
+ top: 0;
+}
+.search-container {
top: 10px;
- left: 335px;
+ left: 10px;
}
-
.country {
.country-name {
display: flex;
@@ -92,6 +102,43 @@ body {
align-self: flex-end;
}
+.suggestions-list {
+ border-radius: 12px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(0, 0, 0, 0.02);
+ box-sizing: border-box;
+ position: absolute;
+ max-width: 320px;
+ width: 100%;
+ top: 100%;
+ left: 0;
+ z-index: 1;
+ list-style: none;
+ max-height: 80vh;
+ margin: 5px 0 0;
+ padding: 0;
+ display: none;
+ overflow-y: auto;
+ background-color: #fff;
+}
+
+#pr-suggestions-list.suggestions-list {
+ left:325px;
+}
+
+.suggestions-list.visible {
+ display: block;
+}
+
+.suggestions-list li {
+ padding: 12px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+.suggestions-list li:hover {
+ background-color: #f2f2f2;
+}
+
.detailsResult {
display: none;
@@ -101,6 +148,7 @@ body {
border-radius: 6px;
max-width: 240px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(0, 0, 0, 0.02);
+
z-index: 1;
overflow: hidden;
diff --git a/shared/scss/_autocomplete_list.scss b/shared/scss/_autocomplete_list.scss
index 6246f6eb..07668489 100644
--- a/shared/scss/_autocomplete_list.scss
+++ b/shared/scss/_autocomplete_list.scss
@@ -30,3 +30,4 @@
#suggestions-list li:hover {
background-color: #f2f2f2;
}
+
diff --git a/shared/scss/_pr_test.scss b/shared/scss/_pr_test.scss
new file mode 100644
index 00000000..3120c9b1
--- /dev/null
+++ b/shared/scss/_pr_test.scss
@@ -0,0 +1,111 @@
+.search-container {
+ display: flex;
+ position: relative;
+ min-height: 45px;
+}
+#autocomplete-container, .autocomplete-container {
+ display: flex;
+ position: initial!important;
+ margin-right:5px;
+ z-index: 1;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(0, 0, 0, 0.02);
+ background: #fff;
+ border-radius: 12px;
+ padding: 0 12px;
+ max-width: 320px;
+ width: 100%;
+ height: 42px;
+ border: none;
+ box-sizing: border-box;
+ align-items: center;
+ cursor: text;
+ font-size: 15px;
+
+ .search-icon, .clear-icon {
+ color: inherit;
+ flex-shrink: 0;
+ height: 16px;
+ width: 16px;
+ }
+
+ .clear-icon {
+ transform: scale(1.3);
+ }
+}
+
+#secondary-target.autocomplete-input{
+ margin-left: 5px;
+ border-radius: 10px;
+ border-color: transparent;
+ height: 38px;
+ padding: 0 8px;
+ width: 40px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(0, 0, 0, 0.02);
+}
+
+#autocomplete-input {
+ box-sizing: border-box;
+ padding: 0;
+ height: 40px;
+ line-height: 24px;
+ vertical-align: top;
+ transition-property: color;
+ transition-duration: .3s;
+ width: 100%;
+ text-overflow: ellipsis;
+ background: transparent;
+ border-radius: 0;
+ border: 0;
+ margin: 0 8px;
+ outline: 0;
+ overflow: visible;
+ appearance: textfield;
+ font-size: 100%;
+}
+#pr-suggestions-list.suggestions-list {
+ left:325px;
+}
+
+.clear-searchButton {
+ display: none;
+ height: 18px;
+ width: 22px;
+ background: none;
+ border: none;
+ vertical-align: middle;
+ pointer-events: all;
+ cursor: pointer;
+}
+
+.suggestions-list {
+ border-radius: 12px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 -1px 0px rgba(0, 0, 0, 0.02);
+ box-sizing: border-box;
+ position: absolute;
+ max-width: 320px;
+ width: 100%;
+ top: 100%;
+ left: 0;
+ z-index: 1;
+ list-style: none;
+ max-height: 300px;
+ margin: 5px 0 0;
+ padding: 0;
+ display: none;
+ overflow-y: auto;
+ background-color: #fff;
+
+ &.visible {
+ display: block;
+ }
+}
+
+.suggestions-list li {
+ padding: 12px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+.suggestions-list li:hover {
+ background-color: #f2f2f2;
+}
\ No newline at end of file