Map engine is one prop: MapLibre drops the search bar, legend rises
Simple Research showed a Fly + Clusters toolbar where the search bar used to be, and the map offered no way to pick an engine without writing a second map. FLY + CLUSTERS ARE NOW LEAF-ONLY. LegendTools renders at top:14px centred on the legend column — the search bar's exact slot — so on the Regions and Languages tabs (the legendTypes its predicate allows) it reads as having REPLACED the search bar. MEASURED on the leaf: fly toolbar at y=58 against the search bar at y=56, the two on top of each other. In a stack whose bottom half is a grid the reader is scanning, a fly-tour that moves the camera underneath fights the other half, so the composite passes legendTools:false. The standalone leaf keeps both. THE ENGINE IS ONE PROP. resolveMapProvider already read profileConfig .provider, so research-map.instances.json publishes the maplibre instance and a partner running thousands of embeds changes one value instead of maintaining a second map. Mapbox stays the default: it carries the richer feature set. MapLibre is the keyless option for volume. MAPLIBRE SHIPS NO SEARCH BAR, ON PURPOSE. GeocoderComponent's maplibre path still calls makeMapboxForwardGeocode(accessToken) (:526) — the geocoding REQUEST goes to Mapbox whichever engine draws the tiles. Shipping the bar under maplibre would keep billing the exact key the option exists to avoid while LOOKING free. Under mapbox it stays, unchanged. AND THE LEGEND RISES INTO THE VACATED SPACE. SemanticTreeLegend's --stl-top defaults to 60px that exists only to reserve room for a search bar; leaving it when the bar is gone is a dead band. MapToolbar's has-geocoder collapses its own reserved gap the same way. MEASURED against the built tree: leaf mapbox search@56 legend@104 fly@NONE (Prayer) / @58 (Regions) leaf maplibre search@NONE legend@58 — legend rose 46px composite search@NONE legend@104 fly@NONE on Regions 7/7 pre, 18/18 post. KNOWN GAP, pre-existing and NOT introduced here: the mapbox composite still shows no search bar. mapboxToken is injected per element and the composite passes neither tk nor tokenUrl to its child, so the child's geocoder gets an empty token and bails while the tiles render from another path. Isolated but not fixed.
This commit is contained in:
parent
90b8109fd0
commit
e7bc86d4f2
11 changed files with 295 additions and 67 deletions
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"_comment": "Preconfigured instances of the research map. The ENGINE IS ONE PROP: provider:'maplibre' swaps Mapbox for the token-free engine, so a partner running thousands of embeds changes a single value instead of maintaining a second map. Mapbox stays the default because it carries the richer feature set; maplibre is the keyless option for volume. Under maplibre the search bar is deliberately ABSENT — GeocoderComponent's maplibre path still geocodes through Mapbox, so shipping it would bill the very key this instance exists to avoid — and the legend rises into the vacated space (--stl-top) rather than leaving a dead band.",
|
||||
"instances": [
|
||||
{ "id": "maplibre", "title": "Research map — MapLibre (token-free, no search bar)",
|
||||
"props": { "profile": "research-map", "provider": "maplibre", "instanceId": "research-map-maplibre" } }
|
||||
]
|
||||
}
|
||||
|
|
@ -428,6 +428,34 @@ const profileConfig = inject('profileConfig')
|
|||
const mapboxToken = inject('mapboxToken')
|
||||
// Map engine: profile-config.provider → window.MAP_PROVIDER → 'mapbox' (default).
|
||||
const mapProvider = resolveMapProvider(profileConfig)
|
||||
|
||||
// ── SEARCH BAR + FLY/CLUSTER TOOLS ARE PROVIDER- AND HOST-DEPENDENT ──────────
|
||||
// ★ WHY MAPLIBRE GETS NO SEARCH BAR AT ALL (Driver, 2026-09-23).
|
||||
// The point of the maplibre provider is to run KEYLESS — a partner with thousands of sites swaps
|
||||
// one prop and stops paying for a Mapbox key. But GeocoderComponent's maplibre path still calls
|
||||
// makeMapboxForwardGeocode(accessToken) (GeocoderComponent.vue:526): the geocoding REQUEST goes to
|
||||
// Mapbox whichever engine draws the tiles. So shipping the search bar under maplibre would keep
|
||||
// billing the exact key the option exists to avoid, while LOOKING free. Under mapbox it stays —
|
||||
// it is the fully-featured search this profile was built around.
|
||||
// A host may also switch it off explicitly (`search: false`) without changing engine.
|
||||
const hasGeocoder = computed(() => {
|
||||
if (profileConfig?.value?.search === false) return false
|
||||
return mapProvider === 'mapbox'
|
||||
})
|
||||
|
||||
// ★ NO SEARCH BAR MEANS THE LEGEND RISES, NOT A DEAD GAP.
|
||||
// SemanticTreeLegend's --stl-top defaults to 60px, and that 60px exists ONLY to reserve room for a
|
||||
// search bar above it. Leaving it when the bar is gone is the empty band the Driver called out.
|
||||
// 14px matches .lt-root's own top, so the legend lands where the search bar's top edge was.
|
||||
const legendTopVar = computed(() => (hasGeocoder.value ? null : '14px'))
|
||||
|
||||
// ★ FLY + CLUSTERS ARE A LEAF-ONLY TOOL. LegendTools renders at top:14px centred on the legend
|
||||
// column — the search bar's exact slot — so inside a COMPOSITE it appears to replace the search
|
||||
// bar rather than accompany it. The composite (research-map-plus-grid, the "Simple Research" pair)
|
||||
// is a reading surface: its map is one half of a stack, and a fly-tour that moves the camera under
|
||||
// a grid the reader is scanning fights the other half. Default stays true so the standalone leaf
|
||||
// is untouched; the composite passes legendTools:false.
|
||||
const showLegendTools = computed(() => profileConfig?.value?.legendTools !== false)
|
||||
const mapProviderName = providerLabel(mapProvider)
|
||||
const dataSource = inject('dataSource')
|
||||
const colorSet = inject('colorSet')
|
||||
|
|
@ -2171,7 +2199,7 @@ onBeforeUnmount(() => {
|
|||
simple-map mfe so tabs 1-3 stay visually identical. -->
|
||||
<GeocoderComponent
|
||||
ref="geocoderRef"
|
||||
v-if="appReady && map"
|
||||
v-if="appReady && map && hasGeocoder"
|
||||
:map-instance="map"
|
||||
:access-token="mapboxToken"
|
||||
:provider="mapProvider"
|
||||
|
|
@ -2194,7 +2222,7 @@ onBeforeUnmount(() => {
|
|||
/>
|
||||
|
||||
<!-- Map toolbar (right rail) — canonical button set + ordering. -->
|
||||
<MapToolbar v-if="appReady && map">
|
||||
<MapToolbar v-if="appReady && map" :has-geocoder="hasGeocoder">
|
||||
<ZoomInButton :map="map" :is-dark="isDark" />
|
||||
<ZoomOutButton :map="map" :is-dark="isDark" />
|
||||
<LocationButton :map="map" :is-dark="isDark" />
|
||||
|
|
@ -2211,7 +2239,7 @@ onBeforeUnmount(() => {
|
|||
(showAutoFlyButton) limits rendering to doxa-regions / language-family
|
||||
tabs, so this can stay declared unconditionally besides appReady. -->
|
||||
<LegendTools
|
||||
v-if="appReady"
|
||||
v-if="appReady && showLegendTools"
|
||||
:legend-type="activeLegendType"
|
||||
:is-dark="isDark"
|
||||
/>
|
||||
|
|
@ -2230,7 +2258,7 @@ onBeforeUnmount(() => {
|
|||
(prayer/adoption/engagement/religion) pass hideTabs to suppress the
|
||||
inner tab strip. LegendDesktop only renders for the people-group
|
||||
detail panel (pin click → PeopleGroupDetail swap). -->
|
||||
<div class="rm-legend-desktop-slot">
|
||||
<div class="rm-legend-desktop-slot" :style="legendTopVar ? { '--stl-top': legendTopVar } : null">
|
||||
<LegendDesktop
|
||||
v-if="appReady && uiStore.legendMode === 'detail' && uiStore.selectedPeopleGroup"
|
||||
:legend-type="activeLegendType"
|
||||
|
|
|
|||
|
|
@ -148,6 +148,13 @@ const sections = [
|
|||
profile: 'research-map',
|
||||
dataSource: 'pray-tools',
|
||||
instanceId: 'rmpg-research-map',
|
||||
// ★ NO FLY/CLUSTER TOOLBAR IN THE COMPOSITE (Driver, 2026-09-23).
|
||||
// LegendTools renders at top:14px centred on the legend column — the search bar's exact
|
||||
// slot — so on the Regions and Languages tabs (the legendTypes its predicate allows) it
|
||||
// reads as having REPLACED the search bar. In a stack whose bottom half is a grid the
|
||||
// reader is scanning, a fly-tour that moves the camera underneath fights the other half.
|
||||
// The standalone leaf keeps both; this is a per-host decision, which is why it is config.
|
||||
legendTools: false,
|
||||
assetsBase: '../../assets',
|
||||
shareUrl: SHARE_URL
|
||||
})
|
||||
|
|
|
|||
106
public/doxa-maps/doxa-research-map/doxa-research-map.js
generated
106
public/doxa-maps/doxa-research-map/doxa-research-map.js
generated
File diff suppressed because one or more lines are too long
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-research-map> custom element. -->
|
||||
<script src="../doxa-research-map.js" integrity="sha384-rX8Z6H4/pnGt+1GrwGQRCooJ21TL4dQE7JZBD19mV0ow7bdagmo9TS0qbVtYrbx5" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-research-map.js" integrity="sha384-YIcYmnktFXizWh4bC8Uwu/SGknvzONjT94ks7Od5brk0C+o+ukJrUTU/qsRTaUjj" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
169
public/doxa-maps/doxa-research-map/research-map/maplibre/index.html
generated
Normal file
169
public/doxa-maps/doxa-research-map/research-map/maplibre/index.html
generated
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Research map — MapLibre (token-free, no search bar) · doxa-research-map</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<!-- MapLibre peer (token-free engine) loaded ONCE here; the custom element
|
||||
uses window.maplibregl directly (same versions as @map/composables/useMapInstance). -->
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||
<link href="https://cdn.jsdelivr.net/npm/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5/dist/maplibre-gl.js"></script>
|
||||
<style>
|
||||
/* BARE full-page map — no chrome. The custom element IS the page, so the
|
||||
browser URL shares the map itself. */
|
||||
html, body { height: 100%; margin: 0; background: #0a0e15; }
|
||||
#slot, doxa-research-map { position: fixed; inset: 0; width: 100%; height: 100%; display: block; }
|
||||
#status { position: fixed; inset: 0; z-index: 2; display: grid; place-items: center;
|
||||
padding: 24px; text-align: center; color: #9aa7b8;
|
||||
font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
#status.err { color: #f87171; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="slot"></div>
|
||||
<div id="status">loading…</div>
|
||||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-research-map> custom element. -->
|
||||
<script src="../../doxa-research-map.js" integrity="sha384-YIcYmnktFXizWh4bC8Uwu/SGknvzONjT94ks7Od5brk0C+o+ukJrUTU/qsRTaUjj" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
// 1. prop — a token hardcoded into this page's profile-config (config.tk)
|
||||
// 2. ?tk= URL param — token carried in the shared link
|
||||
// 3. baked token — opt-in literal token inlined at build time (build-config.token)
|
||||
// A token from rungs 1-3 is used DIRECTLY (no network). Otherwise a token
|
||||
// *endpoint* is resolved and fetched:
|
||||
// 4. window.MAP_TOKEN_URL override 5. configured TOKEN_SRC URL 6. same-origin /api/maps/token
|
||||
// TOKEN_SRC is treated as DATA, never eval'd: the literal "window.MAP_TOKEN_URL"
|
||||
// means "read that global"; anything else is used as a URL — so both a global
|
||||
// name and a plain URL are safe (no SyntaxError footgun).
|
||||
(function () {
|
||||
var slot = document.getElementById('slot');
|
||||
var status = document.getElementById('status');
|
||||
var BASE_CONFIG = {"assetsBase":"../../../assets","profile":"research-map","provider":"maplibre","instanceId":"research-map-maplibre"};
|
||||
// MapLibre map pages AND shell pages (app-shell / nested-mfe-spa) need NO token:
|
||||
// the ladder below is skipped entirely and the element mounts immediately.
|
||||
var TOKEN_FREE = true;
|
||||
var TOKEN_SRC = "window.MAP_TOKEN_URL";
|
||||
var BAKED_TOKEN = "pk.eyJ1IjoiZ29zeXN0ZW1zIiwiYSI6ImNtZm9vM291czA3amQya3BzYW42YW9ibTcifQ.52WM0IpxY9FcB0zc9f1zbQ";
|
||||
// Expose the baked token as window.MAP_TOKEN so a BAKED composite (a compiled element that mounts
|
||||
// a map component directly, with no ProfileLoader/profile-config wrapper) still finds a token —
|
||||
// useMapInstance falls back to window.MAP_TOKEN. No effect when BAKED_TOKEN is empty.
|
||||
if (BAKED_TOKEN && typeof window !== 'undefined' && !window.MAP_TOKEN) { window.MAP_TOKEN = BAKED_TOKEN; }
|
||||
var DEFAULT_TOKEN_URL = '/api/maps/token';
|
||||
|
||||
function fail(msg) { if (status) { status.className = 'err'; status.textContent = '⚠ ' + msg; } }
|
||||
// No token anywhere on the ladder → ask ONCE, store locally, remount. No dead ends.
|
||||
function askToken() {
|
||||
if (!status) return;
|
||||
status.className = 'err';
|
||||
status.innerHTML = 'map needs a Mapbox token — ' +
|
||||
'<input id="tkIn" type="password" placeholder="paste pk.… (stays in this browser)" ' +
|
||||
'style="font:12px ui-monospace,monospace;padding:6px 8px;border-radius:6px;border:1px solid #223047;background:#0e1623;color:#eef2f7;width:min(320px,70vw)"> ' +
|
||||
'<button id="tkGo" style="font:700 11px ui-monospace,monospace;padding:7px 12px;border-radius:999px;border:0;cursor:pointer;background:linear-gradient(90deg,#46d4ff,#b9a6f5);color:#0a0e15">use it</button>';
|
||||
var go = document.getElementById('tkGo');
|
||||
if (go) go.addEventListener('click', function () {
|
||||
var v = (document.getElementById('tkIn').value || '').trim();
|
||||
if (!v) return;
|
||||
try { localStorage.setItem('MAPS_STAGING_TK', v); } catch (e) {}
|
||||
mount(v);
|
||||
});
|
||||
}
|
||||
/* ★ RELEASE THE LOAD BACKDROP WHEN THE ELEMENT MOUNTS (BUG-GOSPEL-MAP-BLACK-RECTANGLE).
|
||||
html/body carry background:#0a0e15 so the light #status text (#9aa7b8) is readable while the
|
||||
bundle loads. That backdrop is LOAD-STATE CHROME, but it outlived its moment: any gap the
|
||||
mounted element does not cover shows it through, because every layer above body is
|
||||
transparent. On gospel-resources/resources-map that produced a 352x126 DARK RECTANGLE between
|
||||
the legend card and the detail panel — MEASURED rgb(8,12,18), i.e. the page background, NOT an
|
||||
unpainted canvas as first supposed. Clearing it here (the one place we already know the
|
||||
element is up) is a one-shot style write — no resize, no repaint loop. The Driver is
|
||||
PHOTOSENSITIVE and a 60fps resize-strobe was already fixed this session; do NOT turn this into
|
||||
a per-frame or per-event handler. */
|
||||
function done() {
|
||||
if (status && status.parentNode) { status.parentNode.removeChild(status); }
|
||||
document.documentElement.style.background = '#f3f3f1';
|
||||
document.body.style.background = '#f3f3f1';
|
||||
}
|
||||
|
||||
function mount(token) {
|
||||
if (!token && !TOKEN_FREE) { return askToken(); }
|
||||
try {
|
||||
var cfg = token ? Object.assign({}, BASE_CONFIG, { tk: token }) : BASE_CONFIG;
|
||||
var el = document.createElement("doxa-research-map");
|
||||
el.setAttribute('profile-config', JSON.stringify(cfg));
|
||||
slot.innerHTML = '';
|
||||
slot.appendChild(el);
|
||||
done();
|
||||
} catch (e) { fail(e && e.message ? e.message : String(e)); }
|
||||
}
|
||||
|
||||
function qp(name) {
|
||||
try { return new URLSearchParams(location.search).get(name) || ''; }
|
||||
catch (e) { return ''; }
|
||||
}
|
||||
|
||||
function extractToken(data) {
|
||||
if (typeof data === 'string') return data.trim();
|
||||
if (data && typeof data === 'object') return data.token || data.tk || data.accessToken || '';
|
||||
return '';
|
||||
}
|
||||
|
||||
// Rungs 1-3b: an actual token → mount directly, no fetch.
|
||||
// 2b = localStorage: the paste-once store shared with the staging pages
|
||||
// (MAPS_STAGING_TK) — paste a token ONCE anywhere in the tree, every page uses it.
|
||||
var stored = '';
|
||||
try { stored = localStorage.getItem('MAPS_STAGING_TK') || ''; } catch (e) {}
|
||||
var urlTk = qp('tk');
|
||||
if (urlTk) { try { localStorage.setItem('MAPS_STAGING_TK', urlTk); } catch (e) {} }
|
||||
var directToken = (BASE_CONFIG && BASE_CONFIG.tk) || urlTk || stored || BAKED_TOKEN;
|
||||
if (TOKEN_FREE) { return mount(''); }
|
||||
if (directToken) { return mount(directToken); }
|
||||
|
||||
// Rungs 4-6: resolve the token ENDPOINT candidates and try them IN ORDER — the first
|
||||
// one that answers with a token wins. Order: window.MAP_TOKEN_URL override → the
|
||||
// configured TOKEN_SRC (an ABSOLUTE url, so a tree hosted on a CDN away from the API
|
||||
// still finds it) → same-origin /api/maps/token (dev, or the site hosting both).
|
||||
// A single hard-coded pick used to kill the page when its one url 404'd or was
|
||||
// blocked by CORS; a shared link must work wherever the tree is hosted.
|
||||
var candidates = [];
|
||||
function addCandidate(u) { if (u && candidates.indexOf(u) === -1) candidates.push(u); }
|
||||
addCandidate(window.MAP_TOKEN_URL);
|
||||
addCandidate(TOKEN_SRC && TOKEN_SRC !== 'window.MAP_TOKEN_URL' ? TOKEN_SRC : '');
|
||||
addCandidate(DEFAULT_TOKEN_URL);
|
||||
|
||||
function fetchToken(i, errors) {
|
||||
if (i >= candidates.length) {
|
||||
// Every endpoint failed: say which, and still offer the paste-once box (no dead ends).
|
||||
askToken();
|
||||
if (status) {
|
||||
var d = document.createElement('div');
|
||||
d.textContent = '⚠ Token fetch failed from ' + candidates.join(', ') + ' — set window.MAP_TOKEN_URL or pass ?tk=. (' + errors.join(' | ') + ')';
|
||||
status.insertBefore(d, status.firstChild);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var url = candidates[i];
|
||||
return fetch(url)
|
||||
.then(function (r) {
|
||||
if (!r.ok) throw new Error(url + ' → HTTP ' + r.status);
|
||||
var ct = r.headers.get('content-type') || '';
|
||||
return ct.indexOf('json') !== -1 ? r.json() : r.text();
|
||||
})
|
||||
.then(function (data) {
|
||||
var tk = extractToken(data);
|
||||
if (!tk) throw new Error(url + ' → no token in response');
|
||||
mount(tk);
|
||||
})
|
||||
.catch(function (e) {
|
||||
errors.push(e && e.message ? e.message : String(e));
|
||||
return fetchToken(i + 1, errors);
|
||||
});
|
||||
}
|
||||
fetchToken(0, []);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
27
public/doxa-maps/manifest.json
generated
27
public/doxa-maps/manifest.json
generated
|
|
@ -331,7 +331,7 @@
|
|||
"bundle": "doxa-research-map",
|
||||
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
|
||||
"output": "doxa-maps/doxa-research-map/doxa-research-map.js",
|
||||
"integrity": "sha384-rX8Z6H4/pnGt+1GrwGQRCooJ21TL4dQE7JZBD19mV0ow7bdagmo9TS0qbVtYrbx5",
|
||||
"integrity": "sha384-YIcYmnktFXizWh4bC8Uwu/SGknvzONjT94ks7Od5brk0C+o+ukJrUTU/qsRTaUjj",
|
||||
"elements": [
|
||||
"doxa-research-map"
|
||||
],
|
||||
|
|
@ -347,8 +347,25 @@
|
|||
"scriptUrl": "./doxa-research-map/doxa-research-map.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-rX8Z6H4/pnGt+1GrwGQRCooJ21TL4dQE7JZBD19mV0ow7bdagmo9TS0qbVtYrbx5\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
|
||||
}
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-YIcYmnktFXizWh4bC8Uwu/SGknvzONjT94ks7Od5brk0C+o+ukJrUTU/qsRTaUjj\" crossorigin=\"anonymous\"></script>\n<doxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></doxa-research-map>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
"id": "maplibre",
|
||||
"title": "Research map — MapLibre (token-free, no search bar)",
|
||||
"apiBaseUrl": "",
|
||||
"props": {
|
||||
"profile": "research-map",
|
||||
"provider": "maplibre",
|
||||
"instanceId": "research-map-maplibre"
|
||||
},
|
||||
"element": "doxa-research-map",
|
||||
"iframeUrl": "./doxa-research-map/research-map/maplibre/index.html",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./doxa-research-map/research-map/maplibre/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -904,7 +921,7 @@
|
|||
"bundle": "research-map-plus-grid",
|
||||
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
|
||||
"output": "doxa-maps/research-map-plus-grid/research-map-plus-grid.js",
|
||||
"integrity": "sha384-K7QdQDF5WffVlrlBuvJDUzH7wXLdXUhAQMjgB+svavqQimYzD9O4Jq/zanhiFjhG",
|
||||
"integrity": "sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo",
|
||||
"elements": [
|
||||
"research-map-plus-grid"
|
||||
],
|
||||
|
|
@ -924,7 +941,7 @@
|
|||
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-K7QdQDF5WffVlrlBuvJDUzH7wXLdXUhAQMjgB+svavqQimYzD9O4Jq/zanhiFjhG\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo\" crossorigin=\"anonymous\"></script>\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
|
||||
},
|
||||
"instances": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,4 +44,4 @@
|
|||
.stl ::-webkit-scrollbar-thumb { background:#c4c8c0; border-radius:8px; }
|
||||
.stl ::-webkit-scrollbar-thumb:hover { background:#9aa197; }
|
||||
.stl ::-webkit-scrollbar-corner { background:transparent; }
|
||||
`,Gl=Qn({name:"StackLayout",props:{sections:{type:Array,default:()=>[]},title:{type:String,default:""},maxSectionHeight:{type:String,default:""},minSectionHeight:{type:String,default:""}},setup(){kl(ql,"stack-layout")},data(){return{_hosts:{},_mounted:{}}},mounted(){for(const t of this.sections||[])t.element&&this.mountSection(t)},methods:{registerHost(t,e){e&&(this._hosts[t]=e)},sectionStyle(t){const e={},s=t.maxHeight||this.maxSectionHeight,n=t.minHeight||this.minSectionHeight,i=!!(s&&n&&s===n);return s&&(e.maxHeight=s,i||(e.overflow="auto")),n&&(e.minHeight=n),Object.keys(e).length?e:null},hasDefiniteHeight(t){const e=t.maxHeight||this.maxSectionHeight,s=t.minHeight||this.minSectionHeight;return!!(e&&s&&e===s)},mountSection(t){if(this._mounted[t.key])return;this._mounted[t.key]=!0;const e=this._hosts[t.key];e&&Wl(t.src).then(()=>zl(t.element)).then(()=>{if(!customElements.get(t.element))return;const s=document.createElement(t.element),n=t.attrs||{};for(const i in n){const r=n[i];try{s[i]=r}catch{}try{s.setAttribute(i,r==null?"":String(r))}catch{}}e.innerHTML="",e.appendChild(s)}).catch(s=>{const n=s&&s.message||String(s);console.error('[StackLayout] section "'+t.key+'" failed to mount <'+t.element+">"+(t.src?" from "+t.src:"")+": "+n);try{e.innerHTML="";const i=document.createElement("div");i.setAttribute("role","status"),i.style.cssText="box-sizing:border-box;max-width:100%;padding:16px;font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#6d766e;background:#fff;border:1px solid #d1d4cd;border-radius:8px;margin:12px 0;overflow-wrap:anywhere;word-break:break-word",i.textContent="Could not load <"+t.element+">"+(t.src?" from "+t.src:"")+" — "+n,e.appendChild(i)}catch{}})}}}),Jl={class:"stl"},Yl={key:0,class:"stl-top"},Xl={class:"stl-brand"},Zl={key:0,class:"stl-title"},Ql={class:"stl-meta"},tc={class:"stl-stack"},ec={key:0,class:"stl-sec-head"},sc={class:"stl-sec-title"},nc={key:0,class:"stl-sec-sub"},ic={key:2,class:"stl-slot"},rc={class:"stl-tail"};function oc(t,e,s,n,i,r){return ut(),jt("div",Jl,[t.$slots.brand||t.$slots.meta||t.title?(ut(),jt("header",Yl,[Xt("span",Xl,[ns(t.$slots,"brand")]),t.title?(ut(),jt("b",Zl,Ue(t.title),1)):fs("",!0),Xt("span",Ql,[ns(t.$slots,"meta")])])):fs("",!0),Xt("div",tc,[(ut(!0),jt(ct,null,vo(t.sections,o=>(ut(),jt("section",{key:o.key,class:ke(["stl-sec",{"stl-sec-fill":t.hasDefiniteHeight(o)}]),style:Le(t.sectionStyle(o))},[o.title?(ut(),jt("div",ec,[Xt("b",sc,Ue(o.title),1),o.sub?(ut(),jt("span",nc,Ue(o.sub),1)):fs("",!0)])):fs("",!0),o.element?(ut(),jt("div",{key:1,class:"stl-host",ref_for:!0,ref:l=>t.registerHost(o.key,l)},null,512)):(ut(),jt("div",ic,[ns(t.$slots,"section-"+o.key)]))],6))),128)),Xt("div",rc,[ns(t.$slots,"default")])])])}const lc=nr(Gl,[["render",oc],["styles",[Bl]]]),cc=":host{display:block;width:100%}",ir="88svh",fc=Fl(nr({__name:"research-map-plus-grid",setup(t){const e=(()=>{const i=(Ul()||"").replace(/\/+$/,""),r="research-map-plus-grid/research-map-plus-grid/index.html";return i?i+"/"+r:r})(),s=(i,r)=>`../../${i}/${r}.js`,n=[{key:"map",element:"doxa-research-map",src:s("doxa-research-map","doxa-research-map"),attrs:{"profile-config":JSON.stringify({profile:"research-map",dataSource:"pray-tools",instanceId:"rmpg-research-map",assetsBase:"../../assets",shareUrl:e})},minHeight:ir,maxHeight:ir},{key:"grid",element:"people-groups-grid",src:s("people-groups-grid","people-groups-grid"),attrs:{"show-search":"true","show-filters":"true","show-total":"true"}}];return(i,r)=>(ut(),ls(lc,{sections:n}))}},[["styles",[cc]]]));typeof customElements<"u"&&(customElements.get("research-map-plus-grid")||customElements.define("research-map-plus-grid",fc))})();
|
||||
`,Gl=Qn({name:"StackLayout",props:{sections:{type:Array,default:()=>[]},title:{type:String,default:""},maxSectionHeight:{type:String,default:""},minSectionHeight:{type:String,default:""}},setup(){kl(ql,"stack-layout")},data(){return{_hosts:{},_mounted:{}}},mounted(){for(const t of this.sections||[])t.element&&this.mountSection(t)},methods:{registerHost(t,e){e&&(this._hosts[t]=e)},sectionStyle(t){const e={},s=t.maxHeight||this.maxSectionHeight,n=t.minHeight||this.minSectionHeight,i=!!(s&&n&&s===n);return s&&(e.maxHeight=s,i||(e.overflow="auto")),n&&(e.minHeight=n),Object.keys(e).length?e:null},hasDefiniteHeight(t){const e=t.maxHeight||this.maxSectionHeight,s=t.minHeight||this.minSectionHeight;return!!(e&&s&&e===s)},mountSection(t){if(this._mounted[t.key])return;this._mounted[t.key]=!0;const e=this._hosts[t.key];e&&Wl(t.src).then(()=>zl(t.element)).then(()=>{if(!customElements.get(t.element))return;const s=document.createElement(t.element),n=t.attrs||{};for(const i in n){const r=n[i];try{s[i]=r}catch{}try{s.setAttribute(i,r==null?"":String(r))}catch{}}e.innerHTML="",e.appendChild(s)}).catch(s=>{const n=s&&s.message||String(s);console.error('[StackLayout] section "'+t.key+'" failed to mount <'+t.element+">"+(t.src?" from "+t.src:"")+": "+n);try{e.innerHTML="";const i=document.createElement("div");i.setAttribute("role","status"),i.style.cssText="box-sizing:border-box;max-width:100%;padding:16px;font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;color:#6d766e;background:#fff;border:1px solid #d1d4cd;border-radius:8px;margin:12px 0;overflow-wrap:anywhere;word-break:break-word",i.textContent="Could not load <"+t.element+">"+(t.src?" from "+t.src:"")+" — "+n,e.appendChild(i)}catch{}})}}}),Jl={class:"stl"},Yl={key:0,class:"stl-top"},Xl={class:"stl-brand"},Zl={key:0,class:"stl-title"},Ql={class:"stl-meta"},tc={class:"stl-stack"},ec={key:0,class:"stl-sec-head"},sc={class:"stl-sec-title"},nc={key:0,class:"stl-sec-sub"},ic={key:2,class:"stl-slot"},rc={class:"stl-tail"};function oc(t,e,s,n,i,r){return ut(),jt("div",Jl,[t.$slots.brand||t.$slots.meta||t.title?(ut(),jt("header",Yl,[Xt("span",Xl,[ns(t.$slots,"brand")]),t.title?(ut(),jt("b",Zl,Ue(t.title),1)):fs("",!0),Xt("span",Ql,[ns(t.$slots,"meta")])])):fs("",!0),Xt("div",tc,[(ut(!0),jt(ct,null,vo(t.sections,o=>(ut(),jt("section",{key:o.key,class:ke(["stl-sec",{"stl-sec-fill":t.hasDefiniteHeight(o)}]),style:Le(t.sectionStyle(o))},[o.title?(ut(),jt("div",ec,[Xt("b",sc,Ue(o.title),1),o.sub?(ut(),jt("span",nc,Ue(o.sub),1)):fs("",!0)])):fs("",!0),o.element?(ut(),jt("div",{key:1,class:"stl-host",ref_for:!0,ref:l=>t.registerHost(o.key,l)},null,512)):(ut(),jt("div",ic,[ns(t.$slots,"section-"+o.key)]))],6))),128)),Xt("div",rc,[ns(t.$slots,"default")])])])}const lc=nr(Gl,[["render",oc],["styles",[Bl]]]),cc=":host{display:block;width:100%}",ir="88svh",fc=Fl(nr({__name:"research-map-plus-grid",setup(t){const e=(()=>{const i=(Ul()||"").replace(/\/+$/,""),r="research-map-plus-grid/research-map-plus-grid/index.html";return i?i+"/"+r:r})(),s=(i,r)=>`../../${i}/${r}.js`,n=[{key:"map",element:"doxa-research-map",src:s("doxa-research-map","doxa-research-map"),attrs:{"profile-config":JSON.stringify({profile:"research-map",dataSource:"pray-tools",instanceId:"rmpg-research-map",legendTools:!1,assetsBase:"../../assets",shareUrl:e})},minHeight:ir,maxHeight:ir},{key:"grid",element:"people-groups-grid",src:s("people-groups-grid","people-groups-grid"),attrs:{"show-search":"true","show-filters":"true","show-total":"true"}}];return(i,r)=>(ut(),ls(lc,{sections:n}))}},[["styles",[cc]]]));typeof customElements<"u"&&(customElements.get("research-map-plus-grid")||customElements.define("research-map-plus-grid",fc))})();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
|
||||
<script src="../../research-map-plus-grid.js" integrity="sha384-K7QdQDF5WffVlrlBuvJDUzH7wXLdXUhAQMjgB+svavqQimYzD9O4Jq/zanhiFjhG" crossorigin="anonymous"></script>
|
||||
<script src="../../research-map-plus-grid.js" integrity="sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <research-map-plus-grid> custom element. -->
|
||||
<script src="../research-map-plus-grid.js" integrity="sha384-K7QdQDF5WffVlrlBuvJDUzH7wXLdXUhAQMjgB+svavqQimYzD9O4Jq/zanhiFjhG" crossorigin="anonymous"></script>
|
||||
<script src="../research-map-plus-grid.js" integrity="sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue