The ⓘ panel shows each map's strategy: an intro, then the strategy, with Examples | Strategy tabs where a map has examples
MapInfo gains a `strategyKey` prop: a catalogue key prefix whose .bottom is the bottom line and .p1, .p2 … the points, read in order until the first missing one (the catalogue decides how many). The popup shows the intro (the lead), then: - a map with examples AND a strategy gets two tabs, Examples first, then Strategy (44px targets, reset to Examples on every open); - a map with only a strategy goes straight to it, under a Strategy heading; - a map with neither is unchanged. The takeaway is not shown beside a strategy, which supersedes it. Labels come from mapInfo.tabExamples and mapInfo.tabStrategy. Prayer Progress's own info (for embeds) names its strategy key too.
This commit is contained in:
parent
63a64d1bf9
commit
9b39577830
2 changed files with 57 additions and 1 deletions
|
|
@ -65,6 +65,14 @@ const props = defineProps({
|
|||
takeaway: { type: String, default: '' },
|
||||
/** A caveat under the examples (e.g. which countries are not supported yet). Key or string. */
|
||||
noteKey: { type: String, default: '' },
|
||||
/**
|
||||
* The map's STRATEGY, as a catalogue key PREFIX (e.g. 'mapInfo.tab.upg-100.strategy'): <prefix>.bottom is
|
||||
* the bottom line, <prefix>.p1, .p2 … the points, read in order until the first one that is missing. So
|
||||
* the number of points is the catalogue's business, never a count kept here. With a strategy the popup
|
||||
* shows the intro (the lead), then the strategy; a map that also has examples gets two tabs, EXAMPLES
|
||||
* first. The takeaway is not shown beside a strategy, which supersedes it.
|
||||
*/
|
||||
strategyKey: { type: String, default: '' },
|
||||
note: { type: String, default: '' },
|
||||
/** The artefacts, in display order. See the header for the four kinds. */
|
||||
examples: { type: Array, default: () => [] },
|
||||
|
|
@ -106,6 +114,25 @@ function lang() {
|
|||
}
|
||||
const t = useOptionalT('', { fallback: (q) => tStatic(q, lang()) })
|
||||
const text = (key, str) => (key ? t(key) : str)
|
||||
// The strategy, read from the catalogue in the reader's language (tStatic falls back to English per key).
|
||||
// lang() reads `opens`, so the lines are re-read on every open. A point is a non-empty string; the first
|
||||
// missing one ends the list.
|
||||
const MAX_POINTS = 8
|
||||
const strategyBottom = () => (props.strategyKey ? tStatic(props.strategyKey + '.bottom', lang()) || '' : '')
|
||||
function strategyPoints() {
|
||||
const out = []
|
||||
if (!props.strategyKey) return out
|
||||
for (let i = 1; i <= MAX_POINTS; i++) {
|
||||
const v = tStatic(props.strategyKey + '.p' + i, lang())
|
||||
if (!v) break
|
||||
out.push(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
const hasStrategy = () => !!strategyBottom()
|
||||
// Two tabs only where there is something on both sides: examples AND a strategy.
|
||||
const hasTabs = () => hasStrategy() && (props.examples || []).length > 0
|
||||
const view = ref('examples')
|
||||
const num = (n) => { try { return new Intl.NumberFormat(lang()).format(n) } catch { return String(n) } }
|
||||
const isRtl = () => lang() === 'ar'
|
||||
|
||||
|
|
@ -162,6 +189,7 @@ const failed = reactive({})
|
|||
async function show() {
|
||||
opens.value++
|
||||
open.value = true
|
||||
view.value = 'examples'
|
||||
// A page-load pointer is done for this load once the popup has been opened (in memory only).
|
||||
if (pageLoad()) pointerDoneThisLoad.add(props.storageKey)
|
||||
await nextTick()
|
||||
|
|
@ -415,6 +443,16 @@ useShadowStyles(`
|
|||
.mi-takeaway{ margin:0 0 14px; color:#3b4256; }
|
||||
.mi-takeaway b{ color:#1b1f2a; }
|
||||
.mi-ex{ margin:18px 0 0; }
|
||||
.mi-tabs{ display:flex; gap:4px; margin:14px 0 0; border-bottom:1px solid #e6e8ee; }
|
||||
.mi-tab{ min-height:44px; padding:0 16px; border:none; background:none; color:#3b4256; font:inherit; font-weight:600;
|
||||
cursor:pointer; border-bottom:3px solid transparent; margin-bottom:-1px; }
|
||||
.mi-tab[aria-selected="true"]{ color:#1b1f2a; border-bottom-color:#3b463d; }
|
||||
.mi-tab:focus-visible{ outline:3px solid #2563eb; outline-offset:-3px; }
|
||||
.mi-strategy{ margin:16px 0 0; }
|
||||
.mi-strategy h3{ margin:0 0 6px; }
|
||||
.mi-bottom{ margin:0 0 8px; font-weight:600; color:#1b1f2a; }
|
||||
.mi-points{ margin:0; padding-inline-start:20px; }
|
||||
.mi-points li{ margin:0 0 6px; }
|
||||
.mi-ex h3{ margin:0 0 8px; font-size:15px; text-transform:uppercase; letter-spacing:.04em; color:#556; }
|
||||
.mi-img{ display:block; width:100%; height:auto; border-radius:8px; background:#f4f5f8;
|
||||
box-shadow:0 1px 4px rgba(0,0,0,.15); }
|
||||
|
|
@ -547,7 +585,22 @@ useShadowStyles(`
|
|||
|
||||
<div class="mi-body">
|
||||
<p v-if="leadKey || lead" class="mi-lead">{{ text(leadKey, lead) }}</p>
|
||||
<p v-if="takeawayKey || takeaway" class="mi-takeaway"><b>{{ t('mapInfo.takeaway') }}</b> {{ text(takeawayKey, takeaway) }}</p>
|
||||
<p v-if="(takeawayKey || takeaway) && !hasStrategy()" class="mi-takeaway"><b>{{ t('mapInfo.takeaway') }}</b> {{ text(takeawayKey, takeaway) }}</p>
|
||||
|
||||
<div v-if="hasTabs()" class="mi-tabs" role="tablist">
|
||||
<button type="button" role="tab" class="mi-tab" :aria-selected="String(view === 'examples')" @click="view = 'examples'">{{ t('mapInfo.tabExamples') }}</button>
|
||||
<button type="button" role="tab" class="mi-tab" :aria-selected="String(view === 'strategy')" @click="view = 'strategy'">{{ t('mapInfo.tabStrategy') }}</button>
|
||||
</div>
|
||||
|
||||
<section v-if="hasStrategy() && (!hasTabs() || view === 'strategy')" class="mi-strategy">
|
||||
<h3 v-if="!hasTabs()">{{ t('mapInfo.tabStrategy') }}</h3>
|
||||
<p class="mi-bottom">{{ strategyBottom() }}</p>
|
||||
<ul v-if="strategyPoints().length" class="mi-points">
|
||||
<li v-for="(p, k) in strategyPoints()" :key="k">{{ p }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<template v-if="!hasTabs() || view === 'examples'">
|
||||
|
||||
<section v-for="(ex, i) in examples" v-show="!failed[i]" :key="i" class="mi-ex">
|
||||
<h3>{{ titleOf(ex) }}</h3>
|
||||
|
|
@ -614,6 +667,8 @@ useShadowStyles(`
|
|||
</template>
|
||||
</section>
|
||||
|
||||
</template>
|
||||
|
||||
<p v-if="noteKey || note" class="mi-note">{{ text(noteKey, note) }}</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const PRAYER = {
|
|||
// The approximate-location caveat, kept here where a reader who asks will always find it, whether or not
|
||||
// the map shows its own pin note. The same catalogue string that note uses.
|
||||
noteKey: 'pins.approximate',
|
||||
strategyKey: 'mapInfo.tab.prayer-map.strategy',
|
||||
examples: [],
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue