From 455fa8ab7dbf3d95c2e2dec23e0d882812e2694c Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Thu, 1 Oct 2026 05:55:55 +0000 Subject: [PATCH] One rule for countries too large for paper: countrySheet.js's LARGE_SHEETS; whole-India list, packet and cards on a desktop CORRECTS the previous commit's "no prayer list, packet or cards for whole India": the current direction is that whole India's poster, list, packet and cards (about a thousand pages) are offered DESKTOP-ONLY, and a phone shows the existing poster.phoneLimit sentence instead of failing. countrySheet.js now exports LARGE_SHEETS ({ IND: 60x96, desktopOnly }) and largeSheet(item), and makeCountrySheet itself prints such a country in its large format (a packet page stays a page) with its "Administrative Areas" cell from the shared state table ("36 states and union territories"). So every caller gets the same sheet: the Regions list's India no longer has to disagree with Country Posters (regions-list's own lines follow, from regions). The wall drops its India-only code and follows the same rule. --- library/packet/countrySheet.js | 27 ++++++++++++++-- .../country-posters/country-poster-wall.vue | 31 +++++++------------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/library/packet/countrySheet.js b/library/packet/countrySheet.js index 93ccceeba..98b2aa00a 100644 --- a/library/packet/countrySheet.js +++ b/library/packet/countrySheet.js @@ -72,6 +72,27 @@ export function seriesFor(countries, item) { return { totalGroups: tier.reduce((a, x) => a + (x.count || 0), 0), totalLists: tier.length, index } } +/** + * ★ COUNTRIES TOO LARGE FOR ANY PAPER SHEET print as ONE LARGE side-lane sheet instead (poster-render's 60x96 / + * 66x66 / 36x60: every group named beside the map, crossing-free leaders). ONE source of truth for every caller (the + * Country Posters wall, the Regions list), so one country never gets two answers. `desktopOnly`: its poster, list, + * packet and cards are offered on a desktop or laptop only (a phone cannot draw a sheet that big). + * The data (country-posters.json) still records why no PAPER sheet fits; this table is what prints instead. + */ +export const LARGE_SHEETS = { IND: { format: '60x96', desktopOnly: true } } +export function largeSheet(item) { return (item && LARGE_SHEETS[String(item.iso3 || '').toUpperCase()]) || null } + +// The facts row's "Administrative Areas" cell for a large-sheet country whose divisions we know (India: its states and +// union territories, counted from the shared state table). English, like the rest of the sheet. +async function adminAreasFor(iso3, base) { + if (iso3 !== 'IND') return undefined + try { + const { loadIndiaStates } = await import('@map/data/indiaStates.js') + const n = ((await loadIndiaStates(base)).states || []).length + return n ? `${n} states and union territories` : undefined + } catch (_) { return undefined } // the cell falls back to the country's name +} + export function makeCountrySheet({ records, assetsBase, countries = null }) { return async function countrySheet(item, format) { const iso3 = item && item.iso3 @@ -84,14 +105,16 @@ export function makeCountrySheet({ records, assetsBase, countries = null }) { records({ type: 'country', values: [iso3] }), ]) if (!rows || !rows.length) return '' // nothing to pin: fall through rather than draw an empty country + const big = largeSheet(item) + const adminAreas = item.adminAreas || (big ? await adminAreasFor(String(iso3).toUpperCase(), assetsBase()) : undefined) return generateCountrySheet({ // region travels: countryZone colours the sheet from it, and a country arriving without one // silently falls back to the old flat teal for that poster alone. - country: { id: item.id, iso3, label: item.label, count: item.count, region: item.region, adminAreas: item.adminAreas }, + country: { id: item.id, iso3, label: item.label, count: item.count, region: item.region, adminAreas }, records: rows, assetsBase: assetsBase(), ISO3_NAME, - format, + format: big && format !== 'packet' ? big.format : format, // a packet's page 1 stays a page series: seriesFor(countries, item), }) } diff --git a/plugin-designs/country-posters/country-poster-wall.vue b/plugin-designs/country-posters/country-poster-wall.vue index 1685db618..fdd1336be 100644 --- a/plugin-designs/country-posters/country-poster-wall.vue +++ b/plugin-designs/country-posters/country-poster-wall.vue @@ -37,7 +37,7 @@ */ import { computed, inject, ref, watch } from 'vue' import PosterWall from '@map/components/poster/PosterWall.vue' -import { useOptionalT, useOptionalI18n, pickPluralForm, fillParams } from '@map/composables/useOptionalT.js' +import { useOptionalT, useOptionalI18n } from '@map/composables/useOptionalT.js' // A country's name in the reader's language (Intl, joined from our ISO-3 codes). import { countryName } from '@map/utils/countryName.js' import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js' @@ -48,7 +48,8 @@ import { getRuntimeLocale, detectLocale } from '@map/i18n/index.js' // library/. Generated by library/scripts/posters/emit-countries.mjs, which now writes there. import data from '@map/data/country-posters.json' import { resolveAssetsBase } from '@map/utils/embedBase.js' -import { makeCountrySheet } from '@map/packet/countrySheet.js' +import { makeCountrySheet, largeSheet } from '@map/packet/countrySheet.js' +import { isPhonePrint } from '@map/composables/printInPlace.js' // India's states and union territories: the shared table (fetched when the wall shows them) and their sheet. import { loadIndiaStates } from '@map/data/indiaStates.js' import { makeIndiaStateSheet, statesWithPosters } from './src/indiaStateSheet.js' @@ -149,10 +150,12 @@ const recordsFor = createPeopleGroupRecords({ lang: () => getRuntimeLocale() || detectLocale(), profileConfig, }) -// ★ THE WHOLE-INDIA POSTER IS THE POSTER ONLY: no prayer list, packet or cards (855 groups would make a book), so its -// card's rows are empty. Its sheet still draws every group: makeCountrySheet fetches them itself. -const isWholeIndia = (c) => !c.indiaState && String(c.iso3 || '').toUpperCase() === 'IND' -const countryRecords = (c) => (isWholeIndia(c) ? Promise.resolve([]) : c.indiaState +// ★ A LARGE-SHEET COUNTRY (India: one 5x8 sheet naming all its groups; countrySheet.js's LARGE_SHEETS) is DESKTOP-ONLY: +// its poster, list, packet and cards (about a thousand pages) are offered on a desktop or laptop, and on a phone the +// card says so instead of failing. Same rule as the Regions list, from the same table. +const ON_PHONE = isPhonePrint() +const phoneBlocked = (c) => !c.indiaState && ON_PHONE && Boolean(largeSheet(c)?.desktopOnly) +const countryRecords = (c) => (phoneBlocked(c) ? Promise.resolve([]) : c.indiaState ? (c.slugs.length ? recordsFor({ type: 'pg_slugs', values: c.slugs }) : Promise.resolve([])) : recordsFor({ type: 'country', values: [c.iso3] })) // ★ GENERATED AT THE CLICK, through the ONE shared path (@map/packet/countrySheet.js, which the Regions @@ -168,19 +171,7 @@ const sheetForRecord = makeCountrySheet({ assetsBase: () => assetsBase.value, countries: data.countries || [], }) -// ★ INDIA PRINTS AS ONE LARGE SIDE-LANE SHEET (60x96: every group named beside the map, one leader per cluster), -// whatever paper the wall asks for, until the shared size picker also offers 66x66 and 36x60. Its facts row names -// the true count of its divisions, in English like the rest of the sheet, from the same plural key as the wall head. -const INDIA_FORMAT = '60x96' -const indiaAdminAreas = () => { - const n = (indiaStates.value || []).length - return n ? fillParams(pickPluralForm(tStatic('countryPosters.indiaItemCount', 'en') || '', n, 'en'), { n }) : undefined -} -const countryOnlySheet = (card, format) => { - const rec = byId.get(card.id) || card - if (isWholeIndia(rec) && format !== 'packet') return sheetForRecord({ ...rec, adminAreas: indiaAdminAreas() }, INDIA_FORMAT) - return sheetForRecord(rec, format) -} +const countryOnlySheet = (card, format) => sheetForRecord(byId.get(card.id) || card, format) // A state's sheet through its own ONE path (src/indiaStateSheet.js), in India's hue and the indiaState series. const stateSheet = makeIndiaStateSheet({ records: recordsFor, assetsBase: () => assetsBase.value, states: () => indiaStates.value, region: INDIA_REGION, @@ -238,7 +229,7 @@ const collapsedBy = (sec) => sec.total < 60 // unsupportedReason is an English note (and a flag), not display text. The card already states the // count above it, so the sentence needs no number and no plural agreement. // A requested country with no poster at all says so in regions-list.vue's words (the regions catalogue). -const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : c.unsupportedReason && !isWholeIndia(c) ? t('messages.countryPosterTooMany') : null) +const unsupported = (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : c.noPoster ? tRegions('No poster has been generated for this country yet.') : phoneBlocked(c) ? t('poster.phoneLimit') : c.unsupportedReason && !largeSheet(c) ? t('messages.countryPosterTooMany') : null) // ★ THROUGH THE CATALOGUE, like the UPG-100 wall: the heading, the tag, the intro and the counts read // in the page's language. The intro keeps its one emphasis as three plain keys (A, the emphasis, B)