diff --git a/plugin-designs/upg-neighbor/src/campaigns.vue b/plugin-designs/upg-neighbor/src/campaigns.vue
index 9c6212730..9ad3a8112 100644
--- a/plugin-designs/upg-neighbor/src/campaigns.vue
+++ b/plugin-designs/upg-neighbor/src/campaigns.vue
@@ -8,7 +8,7 @@
* by the unreached population a campaign could reach; selecting one shows where to run it, whom it
* reaches, and what content already exists in the language. Data: nearData.js.
*/
-import { inject, ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
+import { inject, ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { useMapInstance } from '@map/composables/useMapInstance.js'
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { setProjectionOn } from '@map/composables/useMapProvider.js'
@@ -125,7 +125,7 @@ function frame() {
// The panel covers the left 440px on wide screens, the bottom 55% on a phone: frame what you can see.
// In a right-to-left locale the panel sits on the right, so the wide padding moves with it.
const rtl = !!mapContainer.value && getComputedStyle(mapContainer.value).direction === 'rtl'
- const padding = w > 760 ? { top: 60, bottom: 60, left: rtl ? 60 : 440, right: rtl ? 440 : 60 } : { top: 40, bottom: Math.round(h * 0.55) + 20, left: 30, right: 30 }
+ const padding = w > 760 ? { top: 60, bottom: 60, left: rtl ? 60 : 440, right: rtl ? 440 : 60 } : { top: 40, bottom: (nbState.sheetDown ? 56 : Math.round(h * 0.55)) + 20, left: 30, right: 30 }
try {
m.stop()
m.fitBounds([[Math.min(...lngs), Math.min(...lats)], [Math.max(...lngs), Math.max(...lats)]], { padding, maxZoom: 5.5, duration: 900 })
@@ -133,6 +133,8 @@ function frame() {
}
watch(() => [nbState.lang, data.value], () => { repaint(); frame() })
watch(list, () => repaint()) // the bubbles follow the search and the believers filter
+// PHONE: the same handle as Mobilize; the list can be lowered to a bar and raised again.
+function toggleSheet() { nbState.sheetDown = !nbState.sheetDown; nextTick(() => frame()) }
onMounted(async () => {
loadNearData(profileConfig).then((d) => { data.value = d; repaint(); frame() })
@@ -217,7 +219,12 @@ useShadowStyles(`
.cp-note { padding:14px; color:#6b7280; font-size:13px; line-height:1.45; }
.cp-hover { position:absolute; z-index:25; pointer-events:none; background:rgba(0,27,68,.92); color:#fff; font-size:12px; line-height:1.3; padding:5px 8px; border-radius:6px; max-width:260px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cp-maperr { position:absolute; inset-inline-end:12px; top:12px; z-index:15; background:#fff; border:1px solid #d9dee6; border-radius:8px; padding:8px 12px; font-size:13px; color:#6b7280; }
- @media (max-width:760px) { .cp-panel { top:auto; inset-inline:6px; bottom:6px; width:auto; height:55%; } }
+ .cp-handle { display:none; align-items:center; justify-content:center; gap:8px; width:100%; min-height:40px; border:0; border-bottom:1px solid #eef0f4;
+ background:#fff; color:${NAVY}; font:600 12px/1 inherit; cursor:pointer; flex:0 0 auto; }
+ .cp-handle::before { content:''; width:36px; height:4px; border-radius:2px; background:#c9d1dd; }
+ .cp-handle:focus-visible { outline:2px solid ${NAVY}; outline-offset:-2px; }
+ @media (max-width:760px) { .cp-panel { top:auto; inset-inline:6px; bottom:6px; width:auto; height:55%; }
+ .cp-panel.down { height:56px; } .cp-panel.down > :not(.cp-handle) { display:none; } .cp-handle { display:flex; } }
`, 'upgn-campaigns')
@@ -227,7 +234,8 @@ useShadowStyles(`
{{ mapError }}
{{ hover.text }}
-