From 3a1aa55a89e355b45b200506f0d733ea3a3bd618 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Sun, 27 Sep 2026 08:52:50 +0000 Subject: [PATCH] UPG Neighbor: a neighbor's dot and its card point at each other A neighbor dot showed a pointer and did nothing on click. Clicking a dot now picks and scrolls to its card; clicking a card (or Enter on it) flies to the dot and rings it, so the map and the brief agree on which neighbor you are looking at. --- plugin-designs/upg-neighbor/src/mobilize.vue | 31 +++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/plugin-designs/upg-neighbor/src/mobilize.vue b/plugin-designs/upg-neighbor/src/mobilize.vue index 06416540b..84a8f8682 100644 --- a/plugin-designs/upg-neighbor/src/mobilize.vue +++ b/plugin-designs/upg-neighbor/src/mobilize.vue @@ -50,6 +50,23 @@ const byLevel = computed(() => [1, 2, 3].map((e) => ({ e, list: neighbors.value. const believersNear = computed(() => neighbors.value.reduce((a, n) => a + n.g.believers, 0)) function select(g) { if (g && !g.reached) { nbState.pgid = g.pgid; query.value = '' } } + +// The neighbor in focus: a dot click picks its card, a card click flies to its dot. Both ways, so the +// map and the brief never disagree about which neighbor you are looking at. +const activeId = ref(null) +watch(() => nbState.pgid, () => { activeId.value = null }) +function focusNeighbor(n, fly = true) { + activeId.value = n.g.pgid + applyActive() + const m = map.value + if (fly && m) { try { m.stop(); m.easeTo({ center: [n.g.lng, n.g.lat], zoom: Math.max(m.getZoom(), 5), duration: 700 }) } catch (_) {} } + const card = mapContainer.value?.getRootNode?.()?.querySelector?.(`[data-nb="${n.g.pgid}"]`) + card?.scrollIntoView?.({ block: 'nearest', behavior: 'smooth' }) +} +function applyActive() { + const m = map.value + if (m?.getLayer?.('nb-a')) m.setFilter('nb-a', ['==', ['get', 'id'], activeId.value || '']) +} function clearSelection() { nbState.pgid = null } // ─── Search / default list ─────────────────────────────────────────────────── @@ -115,9 +132,12 @@ function ensureLayers(m) { if (!m.getLayer('nb-n')) m.addLayer({ id: 'nb-n', type: 'circle', source: SRC_N, paint: { 'circle-color': ['get', 'color'], 'circle-radius': ['match', ['get', 'e'], 1, 7, 2, 6, 5], 'circle-stroke-width': 1.5, 'circle-stroke-color': '#fff' } }) + if (!m.getLayer('nb-a')) m.addLayer({ id: 'nb-a', type: 'circle', source: SRC_N, filter: ['==', ['get', 'id'], ''], paint: { + 'circle-color': 'rgba(0,0,0,0)', 'circle-radius': 11, 'circle-stroke-width': 3, 'circle-stroke-color': SELECTED } }) if (!m.getLayer('nb-s')) m.addLayer({ id: 'nb-s', type: 'circle', source: SRC_S, paint: { 'circle-color': UNREACHED, 'circle-radius': 10, 'circle-stroke-width': 3, 'circle-stroke-color': SELECTED } }) applyDim(m) + applyActive() } function applyDim(m) { if (m.getLayer('nb-u')) m.setPaintProperty('nb-u', 'circle-opacity', selected.value ? 0.18 : 0.55) @@ -162,6 +182,12 @@ onMounted(async () => { const g = data.value?.unreached.find((u) => u.pgid === id) if (g) select(g) }) + // A neighbor dot had a pointer cursor and no click handler; it now picks its card. + m.on('click', 'nb-n', (ev) => { + const id = ev.features?.[0]?.properties?.id + const n = neighbors.value.find((x) => x.g.pgid === id) + if (n) focusNeighbor(n, false) + }) for (const id of ['nb-u', 'nb-n']) { m.on('mouseenter', id, () => { m.getCanvas().style.cursor = 'pointer' }) m.on('mouseleave', id, () => { m.getCanvas().style.cursor = '' }) @@ -207,6 +233,9 @@ useShadowStyles(` .mb-level p { margin:0 0 6px; color:#6b7280; font-size:12px; line-height:1.35; } .mb-dot { width:11px; height:11px; border-radius:50%; flex:0 0 auto; } .mb-card { border:1px solid #e5e7eb; border-radius:8px; padding:8px 10px; margin-bottom:6px; font-size:12px; line-height:1.35; } + .mb-card { cursor:pointer; } + .mb-card:hover, .mb-card:focus-visible { border-color:#9fb3d9; } + .mb-card.on { border-color:${SELECTED}; box-shadow:0 0 0 2px ${SELECTED}55; } .mb-card b { font-size:13px; } .mb-card .muted { color:#6b7280; } .mb-same { font:700 10px/1 inherit; color:#7a3e00; background:#fdeedd; border-radius:999px; padding:3px 6px; margin-left:4px; } @@ -285,7 +314,7 @@ useShadowStyles(`

{{ $t(E_TITLE[lv.e]) }}

{{ $t(E_LONG[lv.e]) }}

-
+
{{ n.g.name }}{{ $t('neighbor.mob.samePeople') }}
{{ n.g.country }} · {{ n.g.langName }}
{{ $t('neighbor.mob.cardStats', { b: fmtNum(n.g.believers), km: n.km.toLocaleString() }) }}