Merge origin/development into exp-shepherd/pinhint-s3 at the landing turn
# Conflicts: # public/dx-maps/cities-internet/cities-internet.js # public/dx-maps/cities-internet/connectivity-list/index.html # public/dx-maps/cities-internet/connectivity-map/index.html # public/dx-maps/cities-internet/data-tables/index.html # public/dx-maps/doxa-maps-spa/manifest.json # public/dx-maps/gospel-resources/charts/index.html # public/dx-maps/gospel-resources/data-tables/index.html # public/dx-maps/gospel-resources/gospel-dashboard/index.html # public/dx-maps/gospel-resources/gospel-resources.js # public/dx-maps/gospel-resources/resources-list/index.html # public/dx-maps/gospel-resources/resources-map/index.html # public/dx-maps/manifest.json # public/dx-maps/my-upg-100-list/my-upg-100-list.js # public/dx-maps/my-upg-100-list/poster-wall/index.html # public/dx-maps/my-upg-100-list/upg-100-list/index.html # public/dx-maps/my-upg-100-list/upg-100-mfe/index.html # public/dx-maps/regions-dashboard/region-dashboards/index.html # public/dx-maps/regions-dashboard/regions-dashboard.js # public/dx-maps/regions-dashboard/regions-list/index.html # public/dx-maps/regions-dashboard/regions-mfe/index.html
This commit is contained in:
commit
1d32aa1a97
27 changed files with 129 additions and 92 deletions
|
|
@ -166,6 +166,27 @@ for (const fn of ['onLegendExport', 'makeSelectionPacket', 'makeSelectionPoster'
|
|||
}
|
||||
}
|
||||
|
||||
// ── 4. A TAPPED FEATURE RESOLVES AT ITS REAL LEVEL, FROM ITS REAL PROPERTIES ──────────────────────
|
||||
// Sections 1-2 start from hand-made KEYS ('adm0:IND'), so they could not see the resolver itself
|
||||
// misfile a country: once the world ADM0 boundaries carried a shapeName, "shapeName → ADM1" turned
|
||||
// every tapped country into a province (key 'adm1:Sudan', count null, 0 UUPG), and the Prayer List
|
||||
// refused it (reported from a phone, 2026-09-30). These are the properties the map ACTUALLY hands the
|
||||
// click handler (read off the live tapestry), so a resolver that keys on the wrong field fails here.
|
||||
{
|
||||
const { resolveTerritory, COUNTRY_COUNTS, ADM1_COUNTS } = await import(resolve(ROOT, 'plugin-designs/my-upg-100-list/data/territory-counts.js'))
|
||||
const country = resolveTerritory({ shapeGroup: 'SDN', shapeName: 'Sudan', iso3: 'SDN', zoneKey: 'sudan', zoneLabel: 'Sudan' })
|
||||
ok(country && country.kind === 'adm0' && country.key === 'adm0:SDN',
|
||||
`a tapped COUNTRY (shapeName + iso3, no listId) resolved as ${JSON.stringify(country)}, not adm0:SDN — every country tap would count 0 UUPG`)
|
||||
ok(country && country.count === COUNTRY_COUNTS.SDN && country.count > 0,
|
||||
`a tapped country's count is ${country && country.count}, not its COUNTRY_COUNTS entry (${COUNTRY_COUNTS.SDN})`)
|
||||
const state = resolveTerritory({ iso3: 'IND', shapeGroup: 'IND', listId: 'asia-india-madhya-pradesh', shapeName: 'Madhya Pradesh', zoneKey: 'asia-india-madhya-pradesh' })
|
||||
ok(state && state.kind === 'adm1' && state.key === 'adm1:Madhya Pradesh' && state.count === ADM1_COUNTS['Madhya Pradesh'],
|
||||
`a tapped India STATE (listId + shapeName + iso3) resolved as ${JSON.stringify(state)}, not adm1 with its ADM1_COUNTS entry`)
|
||||
const nepal = resolveTerritory({ shapeGroup: 'NPL', shapeName: 'Nepal', iso3: 'NPL', zoneKey: 'asia-india-uttarakhand' })
|
||||
ok(nepal && nepal.kind === 'adm0' && nepal.key === 'adm0:NPL',
|
||||
`a country drawn inside an Asia zone (zone.adm0, no listId) resolved as ${JSON.stringify(nepal)}, not adm0:NPL`)
|
||||
}
|
||||
|
||||
if (fails.length) {
|
||||
console.error('SELECTION-KINDS: FAIL')
|
||||
for (const f of fails) console.error(` ✗ ${f}`)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
sit NEXT TO the circle rather than on top of it (12px inset + 44px button + 8px gap). A
|
||||
consumer that hardcoded 64px would silently overlap the day the circle resizes; reading it
|
||||
means the circle's geometry is stated once, here, by the component that owns it. -->
|
||||
<div class="vtl" :class="{ 'nav-open': navOpen, 'rail-collapsed': railCollapsed, 'no-header': !header, 'has-bar': barShown }"
|
||||
<div class="vtl" :class="{ 'nav-open': navOpen, 'rail-collapsed': railCollapsed, 'no-header': !header }"
|
||||
:style="fabShown ? { '--map-fab-clearance': 'calc(56px + env(safe-area-inset-bottom))', '--map-fab-beside': 'calc(64px + env(safe-area-inset-left))' } : null">
|
||||
<header class="vtl-top" v-if="header">
|
||||
<button class="vtl-menu" type="button" :aria-expanded="String(navOpen)" @click="navOpen = !navOpen"
|
||||
|
|
@ -123,18 +123,11 @@
|
|||
|
||||
Rendered ONLY when narrow AND headerless, so no existing consumer can see a change: every
|
||||
other case is desktop (rail always visible) or header-on (the toggle above already works).
|
||||
★ THE PHONE BAR REPLACED THE FLOATING CIRCLE (mobile pass, 2026-09-30). The circle floated over
|
||||
whatever each map put in its bottom-left corner: Simple Research's search box, and the licence
|
||||
credit on Cities and Gospel Resources (measured at 390, en and ar). No placement of a floating
|
||||
button is free on every map, so on a phone the ☰ lives in a 44px bar ABOVE the pane, with the
|
||||
open view's name beside it, and covers nothing. It stays while the drawer is open: its ☰
|
||||
closes the drawer again. The button keeps the vtl-menu class, which is how
|
||||
check:nav-reachable-mobile finds the rail's opener. -->
|
||||
<div v-if="barShown" class="vtl-bar">
|
||||
<button class="vtl-menu vtl-bar-menu" type="button" :aria-expanded="String(navOpen)"
|
||||
@click="navOpen = !navOpen" :aria-label="label('openViews')">☰</button>
|
||||
<span class="vtl-bar-title">{{ activeLabel }}</span>
|
||||
</div>
|
||||
It hides itself while the drawer is open — tapping a tab closes the drawer anyway, and a
|
||||
floating button over an open drawer would just cover a row. -->
|
||||
<button v-if="fabShown"
|
||||
class="vtl-fab" type="button" :aria-expanded="String(navOpen)"
|
||||
@click="navOpen = true" :aria-label="label('openViews')">☰</button>
|
||||
|
||||
<!-- Beside the circle, driving the shell's own scroller. Reads --map-fab-beside, which this
|
||||
layout publishes only while the circle is up, so with no circle it falls back to the
|
||||
|
|
@ -322,11 +315,6 @@ const VTL_SHADOW_CSS = `
|
|||
content to size it and collapses to 0 (the 1fr row sits empty) → the same 0-height black screen.
|
||||
With no header there is only one region, so declare a single 1fr row and the body fills. */
|
||||
.vtl.no-header { grid-template-rows:1fr; }
|
||||
.vtl.has-bar { grid-template-rows:auto 1fr; }
|
||||
.vtl-bar { display:flex; align-items:center; gap:10px; box-sizing:border-box; height:44px; padding:0 10px; border-bottom:1px solid var(--vtl-border); background:#ffffff; }
|
||||
.vtl-bar .vtl-bar-menu { display:inline-flex; align-items:center; justify-content:center; flex:none; width:36px; height:36px; padding:0; font:18px/1 system-ui; }
|
||||
.vtl-bar .vtl-bar-menu:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:2px; }
|
||||
.vtl-bar-title { flex:1; min-width:0; font-size:15px; font-weight:600; color:#3b463d; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
/* MATERIAL APP BAR = 64px desktop / 56px mobile (2026-09-21), was 51px from 11px padding.
|
||||
Stated as an explicit min-height rather than left to padding arithmetic, so the number a
|
||||
reader sees is the number that renders. min-height keeps a wrapping title from clipping.
|
||||
|
|
@ -637,17 +625,7 @@ export default defineComponent({
|
|||
* has a symptom a gate can see, so the fix is to make disagreement impossible rather than to
|
||||
* test for it.
|
||||
*/
|
||||
// The circle no longer renders (the phone bar replaced it), so it reserves nothing: every consumer
|
||||
// that reads --map-fab-clearance / --map-fab-beside falls back to its plain inset.
|
||||
fabShown() { return false },
|
||||
// The phone bar: narrow and headerless, like the circle was, and it stays while the drawer is open
|
||||
// (its ☰ closes the drawer again).
|
||||
barShown() { return this.narrow && !this.header },
|
||||
// The open view's name for the bar: the row whose key is `active`, else the layout title.
|
||||
activeLabel() {
|
||||
for (const g of this.groups || []) for (const r of (g.rows || [])) if (r.key === this.active) return r.label
|
||||
return this.title || ''
|
||||
},
|
||||
fabShown() { return this.narrow && !this.header && !this.navOpen },
|
||||
/**
|
||||
* The element the back-to-top arrow scrolls. .vtl-host is the shell's scroller
|
||||
* (overflow-y:auto) and there is one PER TAB, so this follows `active` rather than binding a
|
||||
|
|
@ -903,11 +881,6 @@ export default defineComponent({
|
|||
content to size it and collapses to 0 (the 1fr row sits empty) → the same 0-height black screen.
|
||||
With no header there is only one region, so declare a single 1fr row and the body fills. */
|
||||
.vtl.no-header { grid-template-rows:1fr; }
|
||||
.vtl.has-bar { grid-template-rows:auto 1fr; }
|
||||
.vtl-bar { display:flex; align-items:center; gap:10px; box-sizing:border-box; height:44px; padding:0 10px; border-bottom:1px solid var(--vtl-border); background:#ffffff; }
|
||||
.vtl-bar .vtl-bar-menu { display:inline-flex; align-items:center; justify-content:center; flex:none; width:36px; height:36px; padding:0; font:18px/1 system-ui; }
|
||||
.vtl-bar .vtl-bar-menu:focus-visible { outline:2px solid var(--vtl-accent); outline-offset:2px; }
|
||||
.vtl-bar-title { flex:1; min-width:0; font-size:15px; font-weight:600; color:#3b463d; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
/* MATERIAL APP BAR = 64px desktop / 56px mobile (2026-09-21), was 51px from 11px padding.
|
||||
Stated as an explicit min-height rather than left to padding arithmetic, so the number a
|
||||
reader sees is the number that renders. min-height keeps a wrapping title from clipping.
|
||||
|
|
|
|||
|
|
@ -568,6 +568,11 @@ 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)))}' +
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -1991,6 +1991,11 @@ 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)))}' +
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -128,10 +128,18 @@ export function buildCountryNameMap(records) {
|
|||
* resolveTerritory(props, opts) -> { key, name, count, kind } | null
|
||||
*
|
||||
* Given a clicked tapestry feature's `properties`, return a stable identity for
|
||||
* batch-selection. Resolution order mirrors zone-geometry.js stamp():
|
||||
* 1. ADM1: props.shapeName present → name = shapeName, count = ADM1_COUNTS.
|
||||
* 2. ADM0: props.iso3 present → name = nameMap[iso3] (live label, else
|
||||
* ISO3_NAME fallback, else the raw iso3), count = COUNTRY_COUNTS[iso3].
|
||||
* batch-selection. The LEVEL comes from where zone-geometry.js drew the feature from:
|
||||
* 1. ADM1: an India state / China province. zone-geometry takes those ONLY from the
|
||||
* gb-IND/CHN-ADM1 pools, and only when props.listId === the zone's id, so every
|
||||
* ADM1 feature carries listId → name = shapeName, count = ADM1_COUNTS. (A legacy
|
||||
* feature with a shapeName and no iso3 is also ADM1.)
|
||||
* 2. ADM0: a country, props.iso3 → name = nameMap[iso3] (live label, else ISO3_NAME
|
||||
* fallback, else the raw iso3), count = COUNTRY_COUNTS[iso3].
|
||||
* ★ NOT "shapeName present → ADM1". The world ADM0 boundaries now carry a shapeName too
|
||||
* (Sudan: { shapeGroup:'SDN', shapeName:'Sudan', iso3:'SDN' }), so that rule filed EVERY
|
||||
* tapped country as a province: key 'adm1:Sudan', count null, a HUD total of 0 UUPG, and a
|
||||
* Prayer List that refused the country the reader had just selected ("Select your own
|
||||
* territory always finds 0 people groups", reported from a phone, 2026-09-30).
|
||||
*
|
||||
* `key` is the de-dup / toggle identity ('adm1:<shapeName>' or 'adm0:<iso3>') so
|
||||
* a country and a same-named admin never collide, and re-clicking toggles cleanly.
|
||||
|
|
@ -145,8 +153,8 @@ export function resolveTerritory(props, opts = {}) {
|
|||
if (!props || typeof props !== 'object') return null;
|
||||
const nameMap = opts.nameMap || ISO3_NAME;
|
||||
|
||||
// (1) ADM1 — India state / China province
|
||||
if (props.shapeName) {
|
||||
// (1) ADM1 — India state / China province (listId marks the ADM1 pools; see above)
|
||||
if (props.shapeName && (props.listId || !props.iso3)) {
|
||||
const name = String(props.shapeName);
|
||||
const count = Object.prototype.hasOwnProperty.call(ADM1_COUNTS, name)
|
||||
? ADM1_COUNTS[name] : null;
|
||||
|
|
|
|||
|
|
@ -286,5 +286,12 @@ const wallTag = computed(() => t('messages.upg100WallTag'))
|
|||
:hide-prayer-list-when-soon="true"
|
||||
download-prefix="doxa-upg-100"
|
||||
:columns-control="true"
|
||||
:min-card-px="150"
|
||||
/>
|
||||
<!-- min-card-px 150: "Posters per row: 2" now SHOWS two on a phone. The shared clamp (210px) forced one at
|
||||
390 and silently overrode the select, which read 2 (2026-09-30: work like the country posters,
|
||||
which opted in first). A two-up card is ~162px, so each thumbnail is ~114px and its poster title is
|
||||
too small to read; two-up was chosen over one-up with that trade shown side by side. The action
|
||||
labels wrap (PosterWall's .pfs > .pbtn), so none clip. -->
|
||||
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1060,7 +1060,7 @@ onMounted(() => {
|
|||
</svg>
|
||||
{{ filtersExpanded ? (t.hide_filters || 'Hide Filters') : (t.show_filters || 'Show Filters') }}
|
||||
<span v-if="activeFilterCount" class="filters__toggle-count">{{ activeFilterCount }}</span>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="filters__toggle-chevron" width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path d="M2 4l4 4 4-4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -1268,6 +1268,11 @@ onMounted(() => {
|
|||
gap: 10px; align-items: center; flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
width: 100%; max-width: 600px; margin-inline: auto;
|
||||
/* ★ BESIDE THE GALLERY'S ROUND ☰. On a phone the gallery draws a 44px circle at the bottom-left
|
||||
(bottom-right in ar), and at rest this row sits right where it lands, so the circle covered the
|
||||
start of the search box. The layout publishes --map-fab-beside only while the circle shows. It is
|
||||
unset (0) on desktop, on a standalone page, and wherever there is no circle. */
|
||||
padding-inline-start: var(--map-fab-beside, 0px); box-sizing: border-box;
|
||||
}
|
||||
.pgg-search--repel > :first-child { flex: 1 1 320px; min-width: 0; }
|
||||
.pgg-exact { flex: none; }
|
||||
|
|
@ -1289,7 +1294,12 @@ onMounted(() => {
|
|||
background: var(--dx-accent, #92b195); color: #fff; border-radius: 999px;
|
||||
min-width: 18px; padding: 0 5px; font-size: 0.75rem; line-height: 18px; text-align: center;
|
||||
}
|
||||
.filters__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
|
||||
/* Only the CHEVRON turns over when the filters are open. The rule used to match every svg in the
|
||||
button, so the filter icon beside the label flipped upside down too (its longest line at the
|
||||
bottom), which read as a broken icon. Measured on a phone, where the grid opens with its filters
|
||||
expanded. */
|
||||
.filters__toggle-chevron { flex: none; }
|
||||
.filters__toggle[aria-expanded="true"] .filters__toggle-chevron { transform: rotate(180deg); }
|
||||
.filters__panel { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; align-items: flex-start; }
|
||||
.filter-toggle {
|
||||
padding: 6px 12px; cursor: pointer; font-size: 0.875rem; color: inherit; background: var(--card);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,12 @@
|
|||
<path d="M12 16v5" />
|
||||
</svg>
|
||||
</span>
|
||||
<!-- ON A PHONE the layout hides the header's #meta slot (its 56px bar has no room for it), and the
|
||||
"Hide header" button in #meta went with it, so a phone could not collapse the header at all. This
|
||||
copy shows only at phone width, pinned to the header's end corner; the one in #meta serves wider
|
||||
screens. The floating "Show header" button below brings the header back, as on a desktop. -->
|
||||
<button type="button" class="ps-hide-header-phone" :title="tr('hideHeader')" :aria-label="tr('hideHeader')"
|
||||
@click="setHeaderHidden(true)"><span aria-hidden="true">⌃</span></button>
|
||||
</template>
|
||||
|
||||
<!-- #meta: the bundle/element count + an i18n locale toggle. The studio previews LOCALISED
|
||||
|
|
@ -248,6 +254,8 @@ const PS_SHADOW_CSS = `
|
|||
.ps-token { position:relative; display:inline-flex; }
|
||||
.ps-show-header { position:fixed; top:8px; inset-inline-end:8px; z-index:2147483000; display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border:1px solid #d1d4cd; border-radius:999px; background:rgba(255,255,255,.94); color:#3b463d; font:600 12px system-ui, sans-serif; cursor:pointer; box-shadow:0 4px 14px rgba(0,0,0,.18); }
|
||||
.ps-show-header:hover, .ps-show-header:focus-visible { background:#ffffff; outline:2px solid #92b195; }
|
||||
.ps-hide-header-phone { display:none; }
|
||||
@media (max-width:760px) { .ps-hide-header-phone { display:inline-flex; position:fixed; top:6px; inset-inline-end:8px; z-index:4; align-items:center; justify-content:center; width:44px; height:44px; padding:0; border:1px solid #d1d4cd; border-radius:var(--vtl-radius,8px); background:#d1d4cd; color:#3b463d; font:18px/1 system-ui, sans-serif; cursor:pointer; } }
|
||||
.ps-token-dot { width:8px; height:8px; border-radius:50%; background:#b8bdb4; }
|
||||
.ps-token-dot.is-yours, .ps-token-dot.is-url { background:#92b195; }
|
||||
.ps-token-dot.is-built { background:#c9b36a; }
|
||||
|
|
@ -1274,6 +1282,8 @@ export default defineComponent({
|
|||
.ps-token { position:relative; display:inline-flex; }
|
||||
.ps-show-header { position:fixed; top:8px; inset-inline-end:8px; z-index:2147483000; display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border:1px solid #d1d4cd; border-radius:999px; background:rgba(255,255,255,.94); color:#3b463d; font:600 12px system-ui, sans-serif; cursor:pointer; box-shadow:0 4px 14px rgba(0,0,0,.18); }
|
||||
.ps-show-header:hover, .ps-show-header:focus-visible { background:#ffffff; outline:2px solid #92b195; }
|
||||
.ps-hide-header-phone { display:none; }
|
||||
@media (max-width:760px) { .ps-hide-header-phone { display:inline-flex; position:fixed; top:6px; inset-inline-end:8px; z-index:4; align-items:center; justify-content:center; width:44px; height:44px; padding:0; border:1px solid #d1d4cd; border-radius:var(--vtl-radius,8px); background:#d1d4cd; color:#3b463d; font:18px/1 system-ui, sans-serif; cursor:pointer; } }
|
||||
.ps-token-dot { width:8px; height:8px; border-radius:50%; background:#b8bdb4; }
|
||||
.ps-token-dot.is-yours, .ps-token-dot.is-url { background:#92b195; }
|
||||
.ps-token-dot.is-built { background:#c9b36a; }
|
||||
|
|
|
|||
|
|
@ -299,13 +299,19 @@ const countryRecords = createPeopleGroupRecords({
|
|||
// Page 1 of a country packet, and the country poster, drawn at the click. Nothing is stored: no
|
||||
// country sheet ships, and the packet's default lift would ask for posters/country-<iso3>.html and
|
||||
// 404 on all 175.
|
||||
const countrySheet = makeCountrySheet({
|
||||
const sheetForRecord = makeCountrySheet({
|
||||
records: countryRecords,
|
||||
assetsBase: () => posterAssetsBase.value,
|
||||
// The same list the Country Posters wall computes its series from, so this tab's printed
|
||||
// "map N of M" agrees with the wall's print and with the country's thumbnail.
|
||||
countries: posterData.countries || [],
|
||||
})
|
||||
// ★ THE POSTER IS ENGLISH BY RECORDED DECISION (series.json english_only_note), so the generator gets
|
||||
// the DATA's own record, never the card: the card's label is localised (localisedPoster below), and
|
||||
// handing it over printed "Pray for السودان" beside an English "Countries: Sudan" in Arabic. The
|
||||
// Country Posters wall resolves the record by id the same way (country-poster-wall.vue, byId).
|
||||
const POSTER_BY_ID = new Map((posterData.countries || []).map(c => [c.id, c]))
|
||||
const countrySheet = (card, format) => sheetForRecord(POSTER_BY_ID.get(card && card.id) || card, format)
|
||||
const openPosters = ref(new Set())
|
||||
|
||||
function togglePosters(region) {
|
||||
|
|
@ -330,7 +336,7 @@ const POSTER_BY_ISO3 = new Map((posterData.countries || []).map(c => [String(c.i
|
|||
// countries, and countryName.js records that swap as a product decision rather than a refactor.
|
||||
// · THE SHEET GENERATOR STILL GETS THE ORIGINAL RECORD. The poster itself is English by recorded
|
||||
// decision, so only the card's label is localised — postersFor's result feeds the wall, while
|
||||
// countrySheet resolves its own country entry from the same json by iso3.
|
||||
// countrySheet (above) swaps the card back for the json's own record, by id, before generating.
|
||||
// The i18n locale REF is read, not a snapshot, so a runtime language switch renames the cards.
|
||||
function localisedPoster(rec) {
|
||||
const locale = i18nLocale.value || 'en'
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
|
||||
<script src="../../big-picture.js" integrity="sha384-xh/EgGOlFUWh08WY72oWpOpUp03Kga++2Zpo4FWdF/i/v5tJ8cso5wm91+oDBda2" crossorigin="anonymous"></script>
|
||||
<script src="../../big-picture.js" integrity="sha384-ey0YOb/+Xq79FiqQ6Vn8ttxou+LVfk9lbuo7SFJqziF5y0xz8mWoSoCjPFFVeasF" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
|
||||
<script src="../../big-picture.js" integrity="sha384-xh/EgGOlFUWh08WY72oWpOpUp03Kga++2Zpo4FWdF/i/v5tJ8cso5wm91+oDBda2" crossorigin="anonymous"></script>
|
||||
<script src="../../big-picture.js" integrity="sha384-ey0YOb/+Xq79FiqQ6Vn8ttxou+LVfk9lbuo7SFJqziF5y0xz8mWoSoCjPFFVeasF" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <bp-big-picture> custom element. -->
|
||||
<script src="../big-picture.js" integrity="sha384-xh/EgGOlFUWh08WY72oWpOpUp03Kga++2Zpo4FWdF/i/v5tJ8cso5wm91+oDBda2" crossorigin="anonymous"></script>
|
||||
<script src="../big-picture.js" integrity="sha384-ey0YOb/+Xq79FiqQ6Vn8ttxou+LVfk9lbuo7SFJqziF5y0xz8mWoSoCjPFFVeasF" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -81,7 +81,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
|
||||
<script src="../../dashboard-plus-grid.js" integrity="sha384-rvFw5xBM+g8q05USlrDCVt6AeljaunK0mjZqiXtvud8sNiMpRB/HfustwC4hM2rs" crossorigin="anonymous"></script>
|
||||
<script src="../../dashboard-plus-grid.js" integrity="sha384-ARIaqnumXNGEKtV9VV6ht1yuaTZvjNdhZXO7/vVNmIsl1mPTENQWlAsoabtuLtEs" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <dashboard-plus-grid> custom element. -->
|
||||
<script src="../dashboard-plus-grid.js" integrity="sha384-rvFw5xBM+g8q05USlrDCVt6AeljaunK0mjZqiXtvud8sNiMpRB/HfustwC4hM2rs" crossorigin="anonymous"></script>
|
||||
<script src="../dashboard-plus-grid.js" integrity="sha384-ARIaqnumXNGEKtV9VV6ht1yuaTZvjNdhZXO7/vVNmIsl1mPTENQWlAsoabtuLtEs" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
|
||||
<script src="../../doxa-maps-spa.js" integrity="sha384-oYB02v1OWlb2erOc10jYt+QZEZl294usemur9EmNAorpr0TO+jqJq7b/C9wPK90X" crossorigin="anonymous"></script>
|
||||
<script src="../../doxa-maps-spa.js" integrity="sha384-pO1Mvg+IAD0qNekZT6pZ5SkUxHbA+Pe0vioUgXCQEh7uMLrSaZxvO9JLeF0hY2yt" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <doxa-maps-spa> custom element. -->
|
||||
<script src="../doxa-maps-spa.js" integrity="sha384-oYB02v1OWlb2erOc10jYt+QZEZl294usemur9EmNAorpr0TO+jqJq7b/C9wPK90X" crossorigin="anonymous"></script>
|
||||
<script src="../doxa-maps-spa.js" integrity="sha384-pO1Mvg+IAD0qNekZT6pZ5SkUxHbA+Pe0vioUgXCQEh7uMLrSaZxvO9JLeF0hY2yt" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<plugin-studio tree-base="."></plugin-studio>
|
||||
<script src="./plugin-studio/plugin-studio.js" integrity="sha384-kGBt3dtJspjQbJfUABkZFSuDmST/LFuzTzflC6QlhF18nkBpMXpVcnGaEgo7uVZV" crossorigin="anonymous"></script>
|
||||
<script src="./plugin-studio/plugin-studio.js" integrity="sha384-tS2hZUYwpbq3Z5J9WuL0Ic8gfxHFO5fxsHoO2uMhZa7g/cYOpZ6fzXY1Tx9j/QUK" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
|
||||
<script src="../../people-groups-grid.js" integrity="sha384-hc+/n5HqZ8gYQ1UTrwADtSwUuk14EoIP51j36lml+BaCKA3jsbGJeiuqLrbjxelj" crossorigin="anonymous"></script>
|
||||
<script src="../../people-groups-grid.js" integrity="sha384-1QWhUNF3YqSd47EbeIR3yKl/WZ6lqnsfa0h4cNdilDdYS05iJMlrhtNi8fgWfs8X" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
|
||||
<script src="../people-groups-grid.js" integrity="sha384-hc+/n5HqZ8gYQ1UTrwADtSwUuk14EoIP51j36lml+BaCKA3jsbGJeiuqLrbjxelj" crossorigin="anonymous"></script>
|
||||
<script src="../people-groups-grid.js" integrity="sha384-1QWhUNF3YqSd47EbeIR3yKl/WZ6lqnsfa0h4cNdilDdYS05iJMlrhtNi8fgWfs8X" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <people-groups-grid> custom element. -->
|
||||
<script src="../../people-groups-grid.js" integrity="sha384-hc+/n5HqZ8gYQ1UTrwADtSwUuk14EoIP51j36lml+BaCKA3jsbGJeiuqLrbjxelj" crossorigin="anonymous"></script>
|
||||
<script src="../../people-groups-grid.js" integrity="sha384-1QWhUNF3YqSd47EbeIR3yKl/WZ6lqnsfa0h4cNdilDdYS05iJMlrhtNi8fgWfs8X" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <plugin-studio> custom element. -->
|
||||
<script src="../plugin-studio.js" integrity="sha384-kGBt3dtJspjQbJfUABkZFSuDmST/LFuzTzflC6QlhF18nkBpMXpVcnGaEgo7uVZV" crossorigin="anonymous"></script>
|
||||
<script src="../plugin-studio.js" integrity="sha384-tS2hZUYwpbq3Z5J9WuL0Ic8gfxHFO5fxsHoO2uMhZa7g/cYOpZ6fzXY1Tx9j/QUK" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <plugin-studio> custom element. -->
|
||||
<script src="../../plugin-studio.js" integrity="sha384-kGBt3dtJspjQbJfUABkZFSuDmST/LFuzTzflC6QlhF18nkBpMXpVcnGaEgo7uVZV" crossorigin="anonymous"></script>
|
||||
<script src="../../plugin-studio.js" integrity="sha384-tS2hZUYwpbq3Z5J9WuL0Ic8gfxHFO5fxsHoO2uMhZa7g/cYOpZ6fzXY1Tx9j/QUK" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue