From 0f5f991dae2ea077a67111c3943c407fa6d6a32f Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Wed, 30 Sep 2026 09:32:08 +0000 Subject: [PATCH] Gospel Resources and Cities with Internet: one compact licence credit at the bottom end corner, folded after 3 s --- .../cities-internet/connectivity-map.vue | 48 ++++++++++++++--- .../gospel-resources/resources-map.vue | 52 +++++++++++++++---- 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/plugin-designs/cities-internet/connectivity-map.vue b/plugin-designs/cities-internet/connectivity-map.vue index 9fed464f1..007f969b5 100644 --- a/plugin-designs/cities-internet/connectivity-map.vue +++ b/plugin-designs/cities-internet/connectivity-map.vue @@ -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 = 'geoBoundaries' +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:), // 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 diff --git a/plugin-designs/gospel-resources/resources-map.vue b/plugin-designs/gospel-resources/resources-map.vue index 784e88a47..bec6b55a6 100644 --- a/plugin-designs/gospel-resources/resources-map.vue +++ b/plugin-designs/gospel-resources/resources-map.vue @@ -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 = 'geoBoundaries' +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