From 002b256ce146a4ec0daa15a911a86621a02ec598 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Wed, 30 Sep 2026 05:54:22 +0000 Subject: [PATCH] Cities with Internet and Gospel Resources get the edge scroll strips on their map Both maps build their map with new gl.Map rather than through useMapInstance, so nothing ever wired the shared scroll strips for them: on a phone there was no scroll escape at the map's edges, where every other map has one. They now render the two strips beside the map and hand them to useEdgeScrollAffordance, UPG-100's pattern, flagged per embed by edgeScrollAffordance (default on). The strips sit under everything but the canvas, so the licence credit stays on top of them. Rebuilds cities-internet and gospel-resources, with their pages and the two manifests. --- .../cities-internet/connectivity-map.vue | 30 +++++++++- .../gospel-resources/resources-map.vue | 30 +++++++++- .../cities-internet/cities-internet.js | 58 +++++++++---------- .../connectivity-list/index.html | 2 +- .../connectivity-map/index.html | 2 +- .../cities-internet/data-tables/index.html | 2 +- public/dx-maps/doxa-maps-spa/manifest.json | 26 ++++----- .../gospel-resources/charts/index.html | 2 +- .../gospel-resources/data-tables/index.html | 2 +- .../gospel-dashboard/index.html | 2 +- .../gospel-resources/gospel-resources.js | 44 +++++++------- .../resources-list/index.html | 2 +- .../gospel-resources/resources-map/index.html | 2 +- public/dx-maps/manifest.json | 26 ++++----- 14 files changed, 141 insertions(+), 89 deletions(-) diff --git a/plugin-designs/cities-internet/connectivity-map.vue b/plugin-designs/cities-internet/connectivity-map.vue index d206886c8..bda32f012 100644 --- a/plugin-designs/cities-internet/connectivity-map.vue +++ b/plugin-designs/cities-internet/connectivity-map.vue @@ -50,6 +50,7 @@ import { addMapAttribution } from '@map/composables/useMapAttribution' // computed off profile-config with no theme-toggle handler to wire it to. import MapToolbar from '@map/components/map-controls/MapToolbar.vue' import MapInfoButton from '@map/components/map-controls/MapInfoButton.vue' +import { useEdgeScrollAffordance } from '@map/composables/useEdgeScrollAffordance.js' import ZoomInButton from '@map/components/map-controls/ZoomInButton.vue' import ZoomOutButton from '@map/components/map-controls/ZoomOutButton.vue' import LocationButton from '@map/components/map-controls/LocationButton.vue' @@ -71,6 +72,16 @@ import LegendDesktop from '@map/components/legends/LegendDesktop.vue' const LegendMobile = retryableAsyncComponent(() => import('@map/components/legends/LegendMobile.vue')) const profileConfig = inject('profileConfig', ref(null)) +// EDGE SCROLL AFFORDANCE: the gray side strips that say the page scrolls PAST the map (UPG-100's pattern). +// This map builds its map RAW with new gl.Map, not through useMapInstance, so nothing wired the strips +// for it and a phone had no scroll escape at the map's edges. Flagged per embed, because it is a +// promise: <… edge-scroll-affordance="false"> or config.edgeScrollAffordance switches it off for a map +// with nothing beneath it. Defaults on, as UPG-100's does. +const EDGE_SCROLL_AFFORDANCE = computed(() => { + const v = profileConfig.value?.edgeScrollAffordance + return v === undefined || v === null ? true : !(v === false || v === 'false') +}) +let detachEdges = null const isDark = computed(() => profileConfig.value?.isDark === true) // The token through the library's channel (documentation/reference-universal-props.md): the // profile-config tk, then ProfileLoader's injected mapboxToken (which carries the page-wide shared @@ -112,8 +123,9 @@ watch(err, (e) => { if (e) announceInfoPending(false) }) // top band, which on a phone is this legend's. MapToolbar's phone rule puts the column at top:72px, // inside that band, so the pointer lay over the legend's title and buttons. The column now starts 8px // below the FOLDED legend's measured bottom, whatever its height in each language. Measured only while -// folded, and before the toolbar exists, so opening the legend never moves the controls (MapInfo places -// its pointer when the control appears and would not follow a later move). Desktop is unchanged. +// folded, and before the toolbar exists, so opening the legend never moves the controls under the reader's +// thumb (MapInfo would re-place its pointer on its 1.5 s timer, but the controls should not jump). Desktop +// is unchanged. const legendEl = ref(null) const toolbarTop = ref(0) let legendRO = null @@ -449,6 +461,7 @@ onMounted(async () => { announceInfoPending(true) window.addEventListener('dxmaps:mapinfo-host', reannounceInfoPending) watchLegend() + detachEdges = useEdgeScrollAffordance(mapEl.value?.parentElement || null, { enabled: EDGE_SCROLL_AFFORDANCE.value }) try { await ensureEngineScript(ciProvider) } catch (_) { /* fall through to the notice below */ } // THE ENGINE THE PAGE ASKED FOR, OR NONE. This read `engineGlobal(ciProvider) || window.mapboxgl`: // engineGlobal already answers for Mapbox, so the fallback only fired when MapLibre was asked for and @@ -527,6 +540,8 @@ onMounted(async () => { onBeforeUnmount(() => { if (typeof window !== 'undefined') window.removeEventListener('dxmaps:mapinfo-host', reannounceInfoPending) try { legendRO?.disconnect() } catch (_) {} + try { detachEdges?.() } catch (_) {} + detachEdges = null if (!mapRef.value) announceInfoPending(false) if (typeof window !== 'undefined') window.removeEventListener('resize', syncNarrow) // ★ RELEASE THE TEST HOOKS. Each pins whatever it closes over — a map instance pins its @@ -582,6 +597,11 @@ useShadowStyles( // LOGICAL, not physical: the control rail sits at inset-inline-end, so in Arabic it moves to the // LEFT; a legend pinned left:8px then sat under the zoom buttons (measured in the ar gallery tab). '@media(max-width:699px){.gm .map-toolbar.map-toolbar{top:var(--gm-toolbar-top,72px)}}' + + // The strips sit at z-index 1: above the canvas, so a vertical drag on them scrolls the page, and UNDER + // everything else, including the engine's corner controls (z 2), so the licence credit stays on top. + '.map-scroll-edge{position:absolute;top:0;bottom:0;width:40px;z-index:1;touch-action:pan-y;pointer-events:auto;background:transparent}' + + '.map-scroll-edge--left{left:0}' + + '.map-scroll-edge--right{right:0}' + '.gm-legend{position:absolute;top:8px;inset-inline-start:8px;width:var(--map-legend-width,380px);' + 'max-width:calc(100% - 16px);box-sizing:border-box;z-index:4;padding:8px 10px;' + 'border-radius:10px;background:rgba(255,255,255,.94);color:#0f1419;' + @@ -646,6 +666,12 @@ useShadowStyles(