Gospel Resources and Cities with Internet: one compact licence credit at the bottom end corner, folded after 3 s

This commit is contained in:
Doxa Maps 2026-09-30 09:32:08 +00:00
parent f065ee504e
commit 0f5f991dae
2 changed files with 85 additions and 15 deletions

View file

@ -93,6 +93,34 @@ const mapboxToken = computed(() => profileConfig.value?.tk || injectedTk?.value
const assetsBase = computed(() => resolveAssetsBase(profileConfig.value))
const mapEl = ref(null)
// ★ THE LICENCE CREDIT, COMPACT (plugin-designer 5.8, the bottom-of-map standard). The engine's own
// AttributionControl, with geoBoundaries as custom attribution (its licence asks for the name and a link back).
// On a narrow map the engine draws it compact, EXPANDED at first; CREDIT_FOLD_MS after it first shows,
// foldCredit folds it to its (i) the way the engine's own drag does (it drops the -compact-show class), and a
// tap on the (i) opens it again. The style may still be loading when the map is built, so it looks every half
// second, for up to 30 s, and stops once the credit is there and folded, or is full width (not compact).
const GB_CREDIT = '<a href="https://www.geoboundaries.org" target="_blank" rel="noopener">geoBoundaries</a>'
const CREDIT_FOLD_MS = 3000
let creditTimer = 0
function foldCredit(triesLeft = 60) {
creditTimer = 0
const box = mapEl.value
if (!box) return
const shown = box.querySelector('.maplibregl-ctrl-attrib.maplibregl-compact-show, .mapboxgl-ctrl-attrib.mapboxgl-compact-show')
if (shown) {
creditTimer = setTimeout(() => { creditTimer = 0; shown.classList.remove('maplibregl-compact-show', 'mapboxgl-compact-show') }, CREDIT_FOLD_MS)
return
}
const credit = box.querySelector('.maplibregl-ctrl-attrib, .mapboxgl-ctrl-attrib')
if (credit && !/attrib-empty/.test(credit.className)) return
if (triesLeft > 0) creditTimer = setTimeout(() => foldCredit(triesLeft - 1), 500)
}
function addCompactCredit(map, gl) {
if (typeof gl?.AttributionControl !== 'function') return false
try { map.addControl(new gl.AttributionControl({ customAttribution: GB_CREDIT }), 'bottom-right') } catch (_) { return false }
foldCredit()
return true
}
const mapRef = shallowRef(null)
const rows = ref([])
const selected = ref(null)
@ -517,7 +545,10 @@ onMounted(async () => {
// NO attribution at all. It SHADES geoBoundaries polygons, so the geoBoundaries
// credit + link back is required, and gbOpen is mixed-licence with several
// countries carrying a real attribution requirement (India CC BY 2.5 IN, etc).
addMapAttribution(map, { boundaries: true })
// ★ NOW ONE CREDIT, THE ENGINE'S OWN COMPACT CONTROL (plugin-designer 5.8): the basemap credit comes from the
// style's own sources and geoBoundaries rides in it as custom attribution, with its link back, so every term
// above is still met. An engine without the control keeps the shared credit line.
if (!addCompactCredit(map, gl)) addMapAttribution(map, { boundaries: true })
map.once('load', async () => {
// The BASEMAP's place names in the reader's language (Mapbox setLanguage / MapLibre name:<lang>),
// the library's own helper. useMapInstance applies it for every map built through it; this map is
@ -538,6 +569,7 @@ onMounted(async () => {
} catch (e) { err.value = String(e.message || e); loading.value = false }
})
onBeforeUnmount(() => {
if (creditTimer) { clearTimeout(creditTimer); creditTimer = 0 }
if (typeof window !== 'undefined') window.removeEventListener('dxmaps:mapinfo-host', reannounceInfoPending)
try { legendRO?.disconnect() } catch (_) {}
try { detachEdges?.() } catch (_) {}
@ -583,11 +615,15 @@ useShadowStyles(
':host{display:block;position:relative;width:100%;height:100%;min-height:420px}' +
'.gm{position:absolute;inset:0;overflow:hidden}' +
'.gm-map{position:absolute;inset:0}' +
// THE GALLERY'S ROUND ☰ sits at a phone's bottom corner (bottom-left; bottom-right in Arabic) and publishes
// --map-fab-clearance while it shows. The engine's bottom corners, where this map's licence credit (and a
// Mapbox wordmark) live, lift by it: the rule every LegendMobile map already has. Unset anywhere else, so
// desktop and a map without the button are unchanged. It covered the credit's first/last ~30-40px before.
'.mapboxgl-ctrl-bottom-left,.mapboxgl-ctrl-bottom-right,.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right{transform:translateY(calc(-1 * var(--map-fab-clearance,0px)))}' +
// THE CREDIT CORNER (plugin-designer 5.8; this map has no footer sheet, its legend panel is at the top). The
// engine's compact credit sits at the bottom END corner, opposite the gallery's round ☰ (bottom-right;
// bottom-left in Arabic), and is NOT lifted by --map-fab-clearance: lifted, it floated up the page. Where the
// round button can show, the corner sits above the scroll-edge strips so a tap on its (i) lands, and the
// expanded credit stops 64px short of the far edge, clear of the button.
'@media(max-width:767.98px){' +
'.gm .maplibregl-ctrl-bottom-right,.gm .mapboxgl-ctrl-bottom-right{inset-inline-end:0 !important;inset-inline-start:auto !important;max-width:calc(100% - 64px);z-index:3}' +
'.gm .maplibregl-ctrl-bottom-right .maplibregl-ctrl,.gm .mapboxgl-ctrl-bottom-right .mapboxgl-ctrl{margin:0 0 10px;margin-inline-end:10px}' +
'}' +
// LEGEND: a BOUNDED, left-anchored panel — NOT a full-width strip.
// It used to be `left:8px;right:8px`, which is a strip spanning the whole map
// (MEASURED 1264px of a 1280px map, 98.75%). That collided with the floating

View file

@ -136,6 +136,34 @@ const grProvider = resolveMapProvider(profileConfig)
const assetsBase = computed(() => resolveAssetsBase(profileConfig.value))
const mapEl = ref(null)
// ★ THE LICENCE CREDIT, COMPACT (plugin-designer 5.8, the bottom-of-map standard). The engine's own
// AttributionControl, with geoBoundaries as custom attribution (its licence asks for the name and a link back).
// On a narrow map the engine draws it compact, EXPANDED at first; CREDIT_FOLD_MS after it first shows,
// foldCredit folds it to its (i) the way the engine's own drag does (it drops the -compact-show class), and a
// tap on the (i) opens it again. The style may still be loading when the map is built, so it looks every half
// second, for up to 30 s, and stops once the credit is there and folded, or is full width (not compact).
const GB_CREDIT = '<a href="https://www.geoboundaries.org" target="_blank" rel="noopener">geoBoundaries</a>'
const CREDIT_FOLD_MS = 3000
let creditTimer = 0
function foldCredit(triesLeft = 60) {
creditTimer = 0
const box = mapEl.value
if (!box) return
const shown = box.querySelector('.maplibregl-ctrl-attrib.maplibregl-compact-show, .mapboxgl-ctrl-attrib.mapboxgl-compact-show')
if (shown) {
creditTimer = setTimeout(() => { creditTimer = 0; shown.classList.remove('maplibregl-compact-show', 'mapboxgl-compact-show') }, CREDIT_FOLD_MS)
return
}
const credit = box.querySelector('.maplibregl-ctrl-attrib, .mapboxgl-ctrl-attrib')
if (credit && !/attrib-empty/.test(credit.className)) return
if (triesLeft > 0) creditTimer = setTimeout(() => foldCredit(triesLeft - 1), 500)
}
function addCompactCredit(map, gl) {
if (typeof gl?.AttributionControl !== 'function') return false
try { map.addControl(new gl.AttributionControl({ customAttribution: GB_CREDIT }), 'bottom-right') } catch (_) { return false }
foldCredit()
return true
}
const mapRef = shallowRef(null)
const rows = ref([])
const selected = ref(null)
@ -1828,10 +1856,11 @@ onMounted(async () => {
* against https://tiles.openfreemap.org/planet before writing this.
*/
const isMaplibre = grProvider === 'maplibre'
addMapAttribution(map, { boundaries: true, basemap: !isMaplibre })
if (isMaplibre && typeof gl.AttributionControl === 'function') {
try { map.addControl(new gl.AttributionControl({ compact: false })) } catch (_) { /* credit is not worth breaking the map for */ }
}
// ★ NOW ONE CREDIT: the engine's own control on BOTH engines, compact on a narrow map (plugin-designer 5.8),
// with geoBoundaries as custom attribution. It still reads the basemap wording from the style, as above; under
// Mapbox the style names Mapbox and OpenStreetMap. An engine without the control keeps the shared line, with
// the basemap credit it can name.
if (!addCompactCredit(map, gl)) addMapAttribution(map, { boundaries: true, basemap: !isMaplibre })
/**
* ═══ 'load' MAY HAVE ALREADY FIRED ════════════════════════════════════════════════════
*
@ -1862,6 +1891,7 @@ onMounted(async () => {
} catch (e) { err.value = String(e.message || e) }
})
onBeforeUnmount(() => {
if (creditTimer) { clearTimeout(creditTimer); creditTimer = 0 }
if (typeof window !== 'undefined') window.removeEventListener('dxmaps:mapinfo-host', reannounceInfoPending)
try { legendRO?.disconnect() } catch (_) {}
try { detachEdges?.() } catch (_) {}
@ -2012,11 +2042,15 @@ useShadowStyles(
// 2,107 pins to queryRenderedFeatures, because that API does not care whether anything
// is visible. The size is stated outright now so no engine stylesheet can take it.
'.gm-map{position:absolute;inset:0;width:100%;height:100%}' +
// THE GALLERY'S ROUND ☰ sits at a phone's bottom corner (bottom-left; bottom-right in Arabic) and publishes
// --map-fab-clearance while it shows. The engine's bottom corners, where this map's licence credit (and a
// Mapbox wordmark) live, lift by it: the rule every LegendMobile map already has. Unset anywhere else, so
// desktop and a map without the button are unchanged. It covered the credit's first/last ~30-40px before.
'.mapboxgl-ctrl-bottom-left,.mapboxgl-ctrl-bottom-right,.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right{transform:translateY(calc(-1 * var(--map-fab-clearance,0px)))}' +
// THE CREDIT CORNER (plugin-designer 5.8; this map has no footer sheet, its legend panel is at the top). The
// engine's compact credit sits at the bottom END corner, opposite the gallery's round ☰ (bottom-right;
// bottom-left in Arabic), and is NOT lifted by --map-fab-clearance: lifted, it floated up the page. Where the
// round button can show, the corner sits above the scroll-edge strips so a tap on its (i) lands, and the
// expanded credit stops 64px short of the far edge, clear of the button.
'@media(max-width:767.98px){' +
'.gm .maplibregl-ctrl-bottom-right,.gm .mapboxgl-ctrl-bottom-right{inset-inline-end:0 !important;inset-inline-start:auto !important;max-width:calc(100% - 64px);z-index:3}' +
'.gm .maplibregl-ctrl-bottom-right .maplibregl-ctrl,.gm .mapboxgl-ctrl-bottom-right .mapboxgl-ctrl{margin:0 0 10px;margin-inline-end:10px}' +
'}' +
// LEGEND: a BOUNDED, left-anchored panel — NOT a full-width strip.
// It used to be `left:8px;right:8px`, which is a strip spanning the whole map
// (MEASURED 1264px of a 1280px map, 98.75%). That collided with the floating