Merge main into go-int/work
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions

This commit is contained in:
Doxa Maps 2026-09-27 09:20:47 +00:00
commit 7a7ca67bd2
10 changed files with 76 additions and 43 deletions

View file

@ -50,6 +50,24 @@ 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
// padding: centred in the part of the map you can see, above the phone sheet
if (fly && m) { try { m.stop(); m.easeTo({ center: [n.g.lng, n.g.lat], zoom: Math.max(m.getZoom(), 5), padding: framePadding(), 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 +133,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 +183,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 +234,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 +315,7 @@ useShadowStyles(`
<section v-for="lv in byLevel" :key="lv.e" class="mb-level">
<h3><span class="mb-dot" :style="{ background: E_COLORS[lv.e] }"></span>{{ $t(E_TITLE[lv.e]) }}</h3>
<p>{{ $t(E_LONG[lv.e]) }}</p>
<div v-for="n in lv.list" :key="n.g.pgid" class="mb-card">
<div v-for="n in lv.list" :key="n.g.pgid" class="mb-card" :class="{ on: activeId === n.g.pgid }" :data-nb="n.g.pgid" role="button" tabindex="0" @click="focusNeighbor(n)" @keydown.enter="focusNeighbor(n)">
<b>{{ n.g.name }}</b><span v-if="n.same" class="mb-same">{{ $t('neighbor.mob.samePeople') }}</span><br>
{{ n.g.country }} · {{ n.g.langName }}<br>
<span class="muted">{{ $t('neighbor.mob.cardStats', { b: fmtNum(n.g.believers), km: n.km.toLocaleString() }) }}</span>

View file

@ -1261,7 +1261,7 @@
"bundle": "upg-neighbor",
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"output": "dx-maps/upg-neighbor/upg-neighbor.js",
"integrity": "sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3",
"integrity": "sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX",
"elements": [
"upg-neighbor"
],
@ -1279,7 +1279,7 @@
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"embed": {
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
},
"instances": [
{

View file

@ -1261,7 +1261,7 @@
"bundle": "upg-neighbor",
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"output": "dx-maps/upg-neighbor/upg-neighbor.js",
"integrity": "sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3",
"integrity": "sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX",
"elements": [
"upg-neighbor"
],
@ -1279,7 +1279,7 @@
"scriptUrl": "./upg-neighbor/upg-neighbor.js",
"embed": {
"iframe": "<iframe src=\"./upg-neighbor/upg-neighbor/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
"script": "<!-- Mapbox engine, fetched early. Keep it only with a real Mapbox token in \"tk\": without one (or with \"provider\":\"maplibre\") the map draws with MapLibre and this line must go -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./upg-neighbor/upg-neighbor.js\" integrity=\"sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX\" crossorigin=\"anonymous\"></script>\n<upg-neighbor profile-config='{\"profile\":\"upg-neighbor\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></upg-neighbor>"
},
"instances": [
{

File diff suppressed because one or more lines are too long

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -36,7 +36,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <upg-neighbor> custom element. -->
<script src="../../upg-neighbor.js" integrity="sha384-X6YaUyV+9g7wppNaQ+YWgER7pRrk8y4egSZypQF8+2lVYRith+nPvkpiI77Oh6p3" crossorigin="anonymous"></script>
<script src="../../upg-neighbor.js" integrity="sha384-Fhs5F/t9ml4X4R/M7bgME3Ghl0KO3IRMef2UmS3aDlgWIL7WPKngDCWZqfC+jWnX" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: