postnummer-app/frontend/src/main.ts
2023-02-08 14:09:00 +01:00

173 lines
5.5 KiB
TypeScript

import { Throttler } from "./Throttler";
import { Coordinate, Position, convertPixelsToCoordinate, convertCoordinateToPixels } from "./coordinates";
import { Size } from "./size";
type ZipCodeReverseResponse = {
nr: number | null;
navn: string;
visueltcenter: number[];
};
async function fetchZipCode({
longitude,
latitude,
}: Coordinate): Promise<ZipCodeReverseResponse> {
return fetch(
`https://api.dataforsyningen.dk/postnumre/reverse?x=${longitude}&y=${latitude}`,
)
.then((request) => request.json())
.then((data) => {
let zipCode = parseInt(data.nr);
return {
...data,
nr: isNaN(zipCode) ? null : zipCode,
} as ZipCodeReverseResponse;
})
.catch(() => null as never);
}
function displayMousePosition(element: HTMLParagraphElement, mouse: Position) {
element.innerHTML = `Mouse position: <code>(${mouse.x}px, ${mouse.y}px)</code>`;
}
function displayCoords(element: HTMLParagraphElement, coords: Coordinate) {
const longitude = coords.longitude.toFixed(3);
const latitude = coords.latitude.toFixed(3);
element.innerHTML = `Coords: <code>${longitude}, ${latitude}</code>`;
}
function displayZipCode(
element: HTMLParagraphElement,
zipCode: number | null,
name: string | null,
center: Coordinate | null,
) {
element.innerHTML =
zipCode === null
? `Postnummer ikke fundet`
: `Postnummer: <code>${zipCode}</code>, ${name}`;
if (center == null) return;
const dot = document.getElementById("dot")!;
if (!zipCode) {
dot.style.display = "none";
return;
}
const mapImg = document.getElementById("map")!;
const mapSize: Size = {
width: mapImg.clientWidth,
height: mapImg.clientHeight,
};
const position = convertCoordinateToPixels(center, mapSize);
const rect = document.getElementById("map")!.getBoundingClientRect();
dot.style.display = "block";
dot.style.left = (position.x + rect.left) + "px";
dot.style.top = (position.y + rect.top) + "px";
}
function setupMap(
mousePositionElement: HTMLParagraphElement,
coordsElement: HTMLParagraphElement,
zipCodeElement: HTMLParagraphElement,
) {
const mapImg = document.querySelector<HTMLImageElement>("#map")!;
const fetcher = new Throttler(500);
mapImg.onmousemove = async (event: MouseEvent) => {
const mousePosition: Position = { x: event.offsetX, y: event.offsetY };
displayMousePosition(mousePositionElement, mousePosition);
const mapSize: Size = {
width: mapImg.clientWidth,
height: mapImg.clientHeight,
};
const coords = convertPixelsToCoordinate(mousePosition, mapSize);
displayCoords(coordsElement, coords);
fetcher.call(async () => {
const response = await fetchZipCode(coords);
displayZipCode(
zipCodeElement,
response.nr,
response.navn,
response.visueltcenter ? { longitude: response.visueltcenter[0], latitude: response.visueltcenter[1] } : null,
);
});
};
mapImg.onmouseup = async (event: MouseEvent) => {
const mousePosition: Position = { x: event.offsetX, y: event.offsetY };
displayMousePosition(mousePositionElement, mousePosition);
const mapSize: Size = {
width: mapImg.clientWidth,
height: mapImg.clientHeight,
};
const coords = convertPixelsToCoordinate(mousePosition, mapSize);
displayCoords(coordsElement, coords);
fetcher.call(async () => {
const response = await fetchZipCode(coords);
displayZipCode(
zipCodeElement,
response.nr,
response.navn,
response.visueltcenter ? { longitude: response.visueltcenter[0], latitude: response.visueltcenter[1] } : null,
);
});
}
mapImg.onmouseleave = (_event: MouseEvent) => {
document.getElementById("dot")!.style.display = "none";
[mousePositionElement, coordsElement, zipCodeElement].forEach(
(e) => (e.innerHTML = ""),
);
};
}
function setupSearchBar(zipCodeElement: HTMLParagraphElement) {
const searchBar =
document.querySelector<HTMLFormElement>("#search-bar")!;
const searchInput =
document.querySelector<HTMLInputElement>("#search-input")!;
// Prevent typing letters
searchBar.onkeypress = event => {
event.key !== "Enter" || !isNaN(parseInt(event.key));
}
searchBar.addEventListener("submit", async (event: Event) => {
event.preventDefault();
const inputValue = searchInput.value;
if (!/^\d+$/.test(inputValue)) return;
const data = await (
await fetch(
`https://api.dataforsyningen.dk/postnumre?nr=${inputValue}`,
)
).json();
displayZipCode(
zipCodeElement,
data.length ? parseInt(data[0]["nr"]) : null,
data.length ? data[0]["navn"] : null,
data.length ? { longitude: data[0]["visueltcenter"][0], latitude: data[0]["visueltcenter"][1] } : null,
);
});
}
function main() {
const [mousePositionElement, coordsElement, zipCodeElement] = [
"#mouse-position",
"#coords",
"#zip-code",
].map((id) => document.querySelector<HTMLParagraphElement>(id)!);
setupSearchBar(zipCodeElement);
setupMap(mousePositionElement, coordsElement, zipCodeElement);
}
main();