diff --git a/samples/localities-geocode/index.njk b/samples/localities-geocode/index.njk
index de919713..917d06e4 100644
--- a/samples/localities-geocode/index.njk
+++ b/samples/localities-geocode/index.njk
@@ -14,9 +14,30 @@
-
- Enter an address to geocode or click on the map to reverse geocode.
-
+
diff --git a/samples/localities-geocode/index.ts b/samples/localities-geocode/index.ts
index c2ab6083..1a1c4f0a 100644
--- a/samples/localities-geocode/index.ts
+++ b/samples/localities-geocode/index.ts
@@ -49,6 +49,7 @@ clearSearchBtn.addEventListener("click", () => {
marker.setMap(null);
infoWindow.close();
}
+ clearResponse();
inputElement.focus();
});
@@ -92,10 +93,124 @@ function displayLocality(
infoWindow.setContent(`${locality.formatted_address}`);
infoWindow.open(map, marker);
map.setCenter(locality.geometry.location);
- map.setZoom(14);
+ renderResponse(locality);
}
}
+function escapeHtml(value: string): string {
+ return value
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+function formatNameValue(value: string | string[]): string {
+ return Array.isArray(value) ? value.join(", ") : value;
+}
+
+function renderResponse(
+ locality: woosmap.map.localities.LocalitiesGeocodeResult,
+) {
+ const panel = document.getElementById("response-panel");
+ const summaryEl = panel?.querySelector(".summary") as HTMLElement | null;
+ const componentsEl = panel?.querySelector(
+ ".address-components",
+ ) as HTMLElement | null;
+ const rawEl = document.getElementById("raw-json");
+ if (!panel || !summaryEl || !componentsEl || !rawEl) return;
+
+ panel.dataset.empty = "false";
+
+ const summaryRows: string[] = [];
+ summaryRows.push(
+ `Formatted address:${escapeHtml(locality.formatted_address)}
`,
+ );
+ if (locality.types && locality.types.length) {
+ summaryRows.push(
+ `Types:${escapeHtml(locality.types.join(", "))}
`,
+ );
+ }
+ if (locality.public_id) {
+ summaryRows.push(
+ `Public ID:${escapeHtml(locality.public_id)}
`,
+ );
+ }
+ if (typeof locality.distance === "number") {
+ summaryRows.push(
+ `Distance (m):${locality.distance}
`,
+ );
+ }
+ if (locality.geometry) {
+ const { geometry } = locality;
+ if (geometry.location_type) {
+ summaryRows.push(
+ `Location type:${escapeHtml(geometry.location_type)}
`,
+ );
+ }
+ summaryRows.push(
+ `Latitude:${geometry.location.lat}
`,
+ `Longitude:${geometry.location.lng}
`,
+ );
+ if (geometry.viewport) {
+ const { viewport } = geometry;
+ summaryRows.push(
+ `Viewport NE:${viewport.northeast.lat}, ${viewport.northeast.lng}
`,
+ `Viewport SW:${viewport.southwest.lat}, ${viewport.southwest.lng}
`,
+ );
+ }
+ }
+ summaryEl.innerHTML = `Summary
${summaryRows.join("")}`;
+
+ if (locality.address_components && locality.address_components.length) {
+ const rows = locality.address_components
+ .map((compo) => {
+ const label = escapeHtml((compo.types[0] ?? "").replace(/_/g, " "));
+ const longName = escapeHtml(formatNameValue(compo.long_name));
+ return `${label}:${longName}
`;
+ })
+ .join("");
+ componentsEl.innerHTML = `Address components
${rows}`;
+ } else {
+ componentsEl.innerHTML = "";
+ }
+
+ rawEl.textContent = JSON.stringify(locality, null, 2);
+}
+
+const copyBtn = document.getElementById("copy-raw-json");
+if (copyBtn) {
+ copyBtn.addEventListener("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const rawEl = document.getElementById("raw-json");
+ const text = rawEl?.textContent ?? "";
+ if (!text) return;
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ copyBtn.classList.add("copied");
+ window.setTimeout(() => copyBtn.classList.remove("copied"), 1500);
+ })
+ .catch((err) => console.error("Failed to copy raw JSON:", err));
+ });
+}
+
+function clearResponse() {
+ const panel = document.getElementById("response-panel");
+ if (!panel) return;
+ panel.dataset.empty = "true";
+ const summaryEl = panel.querySelector(".summary") as HTMLElement | null;
+ const componentsEl = panel.querySelector(
+ ".address-components",
+ ) as HTMLElement | null;
+ const rawEl = document.getElementById("raw-json");
+ if (summaryEl) summaryEl.innerHTML = "";
+ if (componentsEl) componentsEl.innerHTML = "";
+ if (rawEl) rawEl.textContent = "";
+}
+
declare global {
interface Window {
initMap: () => void;
diff --git a/samples/localities-geocode/style.scss b/samples/localities-geocode/style.scss
index 881873ab..3f6bbfcf 100644
--- a/samples/localities-geocode/style.scss
+++ b/samples/localities-geocode/style.scss
@@ -17,11 +17,108 @@
@include actionText($color: #fff);
}
-#instructions {
- max-width: 300px;
- font-size: 17px;
- padding: 10px;
- @include map-panel($bottom: 50px, $left: 0);
+#response-panel {
+ @include map-panel($bottom: 25px, $right: 10px);
+ width: 360px;
+ max-width: calc(100vw - 40px);
+ max-height: calc(40vh - 25px);
+ overflow-y: auto;
+ padding: 0;
+ font-size: 13px;
+
+ .panel-header {
+ padding: 12px 14px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ }
+
+ .panel-title {
+ font-weight: 700;
+ font-size: 14px;
+ margin-bottom: 4px;
+ }
+
+ .panel-hint {
+ color: rgba(0, 0, 0, 0.6);
+ font-size: 12px;
+ }
+
+ .section-title {
+ color: rgba(0, 0, 0, 0.5);
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ padding: 14px 12px 8px 12px;
+ }
+
+ .summary,
+ .address-components {
+ background-color: #fff;
+ }
+
+ .address-components {
+ background-color: rgba(0, 0, 0, 0.03);
+ }
+
+ .option-detail {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0 12px 8px 12px;
+ margin: 0;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+
+ &-label {
+ color: rgba(0, 0, 0, 0.5);
+ margin-right: 4px;
+ flex-shrink: 0;
+ }
+ }
+
+ .bold {
+ font-weight: 700;
+ }
+
+ .raw-json-details {
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
+
+ summary {
+ cursor: pointer;
+ padding: 10px 14px;
+ font-weight: 600;
+ user-select: none;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ }
+
+ .copy-btn {
+ @include copy-button;
+ }
+
+ pre {
+ margin: 0 10px 10px 10px;
+ padding: 10px;
+ border: 1px solid #e0e0e0;
+ background-color: #fafafa;
+ border-radius: 6px;
+ white-space: pre-wrap;
+ overflow-x: auto;
+ font-size: 12px;
+ font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console",
+ "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono",
+ "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier,
+ monospace;
+ }
+ }
+
+ &[data-empty="true"] {
+ .summary,
+ .address-components,
+ .raw-json-details {
+ display: none;
+ }
+ }
}
/* [END woosmap_localities_geocode] */
diff --git a/shared/scss/_mixins.scss b/shared/scss/_mixins.scss
index 58287c22..108d3fba 100644
--- a/shared/scss/_mixins.scss
+++ b/shared/scss/_mixins.scss
@@ -75,6 +75,45 @@
}
}
+@mixin copy-button(
+ $color: rgba(0, 0, 0, 0.5),
+ $hover-color: #3d5afe,
+ $success-color: #2e7d32,
+ $padding: 2px 4px
+) {
+ background: transparent;
+ border: 0;
+ padding: $padding;
+ cursor: pointer;
+ color: $color;
+ display: inline-flex;
+ align-items: center;
+ transition: color 0.2s ease-in-out;
+
+ &:hover,
+ &:focus {
+ color: $hover-color;
+ outline: none;
+ }
+
+ .icon-check {
+ display: none;
+ color: $success-color;
+ }
+
+ &.copied {
+ color: $success-color;
+
+ .icon-copy {
+ display: none;
+ }
+
+ .icon-check {
+ display: inline-block;
+ }
+ }
+}
+
@mixin progress($width: 70px, $height:70px, $borderColor: #f3f3f3, $borderTopColor: #444444) {
position: fixed;
top: 0;