fix(gps): клик по точке открывает попап с названием (делегирование только по data-gps-action); спайдерфай кластеров в одной координате на max zoom

This commit is contained in:
2026-09-23 08:49:15 +03:00
parent 27a14ec513
commit 6c1cea39b4
4 changed files with 16 additions and 10 deletions

View File

@@ -84,7 +84,8 @@ export function OsmMap({
iconCreateFunction: (cluster) =>
L.divIcon({ html: gpsClusterHtml(cluster.getChildCount()), className: '', iconSize: [0, 0] }),
zoomToBoundsOnClick: false,
spiderfyOnMaxZoom: false,
// Точки в одной координате («3») на max zoom раскрываются «паучком»
spiderfyOnMaxZoom: true,
showCoverageOnHover: false,
}).addTo(map);
clusterLayerRef.current = clusterLayer;
@@ -113,9 +114,11 @@ export function OsmMap({
// Делегированный клик по пункту списка кластера в popup
// (контент popup рендерится внутри контейнера карты)
map.getContainer().addEventListener('click', (ev) => {
const el = (ev.target as HTMLElement | null)?.closest?.('[data-gps-marker-id]') as HTMLElement | null;
const el = (ev.target as HTMLElement | null)?.closest?.('[data-gps-action]') as HTMLElement | null;
if (!el) return;
const marker = markersByIdRef.current.get(el.dataset.gpsMarkerId || '');
const action = el.dataset.gpsAction || '';
if (!action.startsWith('open:')) return;
const marker = markersByIdRef.current.get(action.slice(5));
if (marker?.onClick) {
marker.onClick();
map.closePopup();

View File

@@ -170,9 +170,11 @@ export function Yandex21Map({
const container = containerRef.current;
if (!mapReady || !container) return;
const handler = (ev: MouseEvent) => {
const el = (ev.target as HTMLElement | null)?.closest?.('[data-gps-marker-id]') as HTMLElement | null;
const el = (ev.target as HTMLElement | null)?.closest?.('[data-gps-action]') as HTMLElement | null;
if (!el) return;
const marker = markersByIdRef.current.get(el.dataset.gpsMarkerId || '');
const action = el.dataset.gpsAction || '';
if (!action.startsWith('open:')) return;
const marker = markersByIdRef.current.get(action.slice(5));
if (marker?.onClick) {
marker.onClick();
mapRef.current?.balloon.close();

View File

@@ -94,9 +94,10 @@ export function gpsMarkerHtml(marker: GpsMapMarker): string {
}
}
// data-gps-marker-id: делегированный обработчик контейнера карты ловит клики
// по любому маркеру (надёжнее нативных событий placemark/divIcon)
return `<div data-gps-marker-id="${escapeHtml(String(marker.id))}" style="display:flex;flex-direction:column;align-items:center;width:${layout.width}px;user-select:none;cursor:pointer;${opacity}">${body}${label}</div>`;
// data-* атрибута на корне НЕТ намеренно: делегированный обработчик ловит
// только кнопки с data-gps-action (список кластера, «Открыть задачу» в попапе),
// иначе клик по точке сразу вызывал onClick и глушил открытие попапа
return `<div style="display:flex;flex-direction:column;align-items:center;width:${layout.width}px;user-select:none;cursor:pointer;${opacity}">${body}${label}</div>`;
}
/**
@@ -119,7 +120,7 @@ export function gpsClusterHtml(count: number | string): string {
export function gpsClusterListHtml(markers: GpsMapMarker[]): string {
const rows = markers
.map(
(m) => `<button type="button" data-gps-marker-id="${escapeHtml(String(m.id))}" style="display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border:0;background:none;cursor:pointer;font-size:12px;text-align:left;border-radius:4px;">
(m) => `<button type="button" data-gps-action="open:${escapeHtml(String(m.id))}" style="display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border:0;background:none;cursor:pointer;font-size:12px;text-align:left;border-radius:4px;">
<span style="width:10px;height:10px;border-radius:50%;background:${m.color || DEFAULT_COLOR};border:1px solid #fff;box-shadow:0 0 1px rgba(0,0,0,0.4);flex-shrink:0;"></span>
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(m.clusterLabel || m.label || 'Объект')}</span>
</button>`

View File

@@ -236,7 +236,7 @@ export function GpsMapTab({ onShowRoute }: { onShowRoute: (assetId: number) => v
const st = shortTitle(t.title);
// Попап по клику: название (две строки) + кнопка «Открыть задачу».
// Работает и при выключенных подписях — название видно по клику.
const popup = `<div style="max-width:220px;"><b>${esc(st.line1)}</b>${st.line2 ? `<div style="color:#52525b;font-size:11px;">${esc(st.line2)}</div>` : ''}<button type="button" data-gps-marker-id="task-${t.id}" style="margin-top:6px;font-size:11px;color:#2563eb;background:none;border:0;cursor:pointer;padding:0;">Открыть задачу</button></div>`;
const popup = `<div style="max-width:220px;"><b>${esc(st.line1)}</b>${st.line2 ? `<div style="color:#52525b;font-size:11px;">${esc(st.line2)}</div>` : ''}<button type="button" data-gps-action="open:task-${t.id}" style="margin-top:6px;font-size:11px;color:#2563eb;background:none;border:0;cursor:pointer;padding:0;">Открыть задачу</button></div>`;
markers.push({
id: `task-${t.id}`,
lat: geo.lat,