fix(map): group collocated user markers
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { groupMapLocations } from "@/lib/user-location-map";
|
||||
import type { UserMapLocation } from "./user-world-map";
|
||||
|
||||
export function MapViewToggle({ locations, children }: { locations: UserMapLocation[]; children: ReactNode }) {
|
||||
@@ -40,33 +41,76 @@ function InteractiveMap({ locations }: { locations: UserMapLocation[] }) {
|
||||
}).addTo(map);
|
||||
|
||||
const bounds: [number, number][] = [];
|
||||
for (const user of locations) {
|
||||
const marker = leaflet.circleMarker([user.latitude, user.longitude], {
|
||||
radius: 8,
|
||||
color: "#eee8d8",
|
||||
weight: 3,
|
||||
fillColor: "#a32f1b",
|
||||
fillOpacity: 1,
|
||||
}).addTo(map);
|
||||
for (const group of groupMapLocations(locations)) {
|
||||
const firstUser = group.locations[0]!;
|
||||
const isGrouped = group.count > 1;
|
||||
const marker = isGrouped
|
||||
? leaflet.marker([group.latitude, group.longitude], {
|
||||
icon: leaflet.divIcon({
|
||||
className: "map-user-cluster",
|
||||
html: `<span aria-hidden="true">${group.count}</span>`,
|
||||
iconAnchor: [18, 18],
|
||||
iconSize: [36, 36],
|
||||
}),
|
||||
keyboard: true,
|
||||
}).addTo(map)
|
||||
: leaflet.circleMarker([group.latitude, group.longitude], {
|
||||
radius: 8,
|
||||
color: "#eee8d8",
|
||||
weight: 3,
|
||||
fillColor: "#a32f1b",
|
||||
fillOpacity: 1,
|
||||
}).addTo(map);
|
||||
const tooltip = document.createElement("span");
|
||||
tooltip.textContent = `${user.nickname} · ${user.location}`;
|
||||
tooltip.textContent = isGrouped
|
||||
? `${group.count} users · ${group.nicknames.join(" · ")}`
|
||||
: `${firstUser.nickname} · ${firstUser.location}`;
|
||||
marker.bindTooltip(tooltip, { direction: "top" });
|
||||
const userPath = `/admin/users/${user.userId}`;
|
||||
marker.on("click", () => window.location.assign(userPath));
|
||||
|
||||
if (isGrouped) {
|
||||
const popup = document.createElement("div");
|
||||
const heading = document.createElement("strong");
|
||||
heading.textContent = `${group.count} users near ${firstUser.location}`;
|
||||
popup.append(heading);
|
||||
const list = document.createElement("ul");
|
||||
for (const user of group.locations) {
|
||||
const item = document.createElement("li");
|
||||
const link = document.createElement("a");
|
||||
link.href = `/admin/users/${user.userId}`;
|
||||
link.textContent = user.nickname;
|
||||
item.append(link);
|
||||
list.append(item);
|
||||
}
|
||||
popup.append(list);
|
||||
marker.bindPopup(popup);
|
||||
} else {
|
||||
marker.on("click", () => window.location.assign(`/admin/users/${firstUser.userId}`));
|
||||
}
|
||||
|
||||
const element = marker.getElement();
|
||||
element?.setAttribute("aria-label", `${user.nickname}, ${user.location}`);
|
||||
element?.setAttribute("role", "link");
|
||||
const label = isGrouped
|
||||
? `${group.count} users near ${firstUser.location}: ${group.nicknames.join(", ")}`
|
||||
: `${firstUser.nickname}, ${firstUser.location}`;
|
||||
element?.setAttribute("aria-label", label);
|
||||
element?.setAttribute("role", isGrouped ? "button" : "link");
|
||||
element?.setAttribute("tabindex", "0");
|
||||
if (isGrouped) {
|
||||
element?.setAttribute("aria-haspopup", "dialog");
|
||||
element?.setAttribute("aria-expanded", "false");
|
||||
marker.on("popupopen", () => element?.setAttribute("aria-expanded", "true"));
|
||||
marker.on("popupclose", () => element?.setAttribute("aria-expanded", "false"));
|
||||
}
|
||||
element?.addEventListener("focus", () => marker.openTooltip());
|
||||
element?.addEventListener("blur", () => marker.closeTooltip());
|
||||
element?.addEventListener("keydown", (event) => {
|
||||
const keyboardEvent = event as KeyboardEvent;
|
||||
if (keyboardEvent.key === "Enter" || keyboardEvent.key === " ") {
|
||||
keyboardEvent.preventDefault();
|
||||
window.location.assign(userPath);
|
||||
if (isGrouped) marker.openPopup();
|
||||
else window.location.assign(`/admin/users/${firstUser.userId}`);
|
||||
}
|
||||
});
|
||||
bounds.push([user.latitude, user.longitude]);
|
||||
bounds.push([group.latitude, group.longitude]);
|
||||
}
|
||||
if (bounds.length) map.fitBounds(bounds, { padding: [40, 40], maxZoom: 6 });
|
||||
cleanup = () => map.remove();
|
||||
|
||||
Reference in New Issue
Block a user