Privacy: neutral comments in Country Posters (no quotes, date tags or agent names)
Comments only; behaviour unchanged. Each rewritten line keeps its technical fact and drops the attribution: - design-note and brief quotations restated as plain design statements (country-poster-wall.vue, index.js, countries-map.vue); - "(qa: <date> …)" tags and "user feedback / user reported" attributions removed (countries-map.vue); - fleet agent names replaced by what was measured (countries-map.vue, country-posters-mfe.vue); - series.json's India-state note: the approval date is gone; the wording stays the editable default.
This commit is contained in:
parent
01c390a929
commit
202a0244e2
5 changed files with 29 additions and 47 deletions
|
|
@ -160,8 +160,8 @@
|
|||
"A state poster is a country poster one level down: the Country Posters wall prints one for each of India's",
|
||||
"states and union territories that has people groups listed (34 of 36; data/people-groups/india-states.json).",
|
||||
"The country series' text cannot be reused: it would call Gujarāt's poster one of the ~170 'Country Maps',",
|
||||
"one sheet for each country. The term is 'state and union territory', never 'province'. Approved as the",
|
||||
"default wording 2026-09-30 and shown as editable; a change is a strings-only follow-up."
|
||||
"one sheet for each country. The term is 'state and union territory', never 'province'. This is the default",
|
||||
"wording and stays editable; a change is a strings-only follow-up."
|
||||
],
|
||||
"strings": {
|
||||
"map_page_kicker": "PRAY FOR THIS STATE",
|
||||
|
|
|
|||
|
|
@ -20,11 +20,8 @@
|
|||
* region polygons, the region colors, the scroll-edge accessibility strips, the
|
||||
* geocoder, the clustering, the detail panel and the 3-tier tree for free.
|
||||
*
|
||||
* > design note 2026-08-01: "we want to reuse all of the region polygons, all of the
|
||||
* > region colors, the same exact legend, the same exact region colors, the same
|
||||
* > exact code for the region's application profile. But we parameterize it
|
||||
* > slightly to make the default tab that opens up as the country's tab. That's
|
||||
* > the only real change."
|
||||
* It reuses the region polygons, the region colours, the same legend and the same code as the regions
|
||||
* application profile, parameterized only so the default tab is the countries tab.
|
||||
*
|
||||
* THE THREE DIFFERENCES (search "COUNTRIES-MAP DIFFERENCE" to find them):
|
||||
* 1. The WAGF Regions tab is RELABELED "Countries" — same doxaRegion strategy,
|
||||
|
|
@ -200,7 +197,7 @@ useShadowStyles(`
|
|||
selector the column got no position or width at all under MapLibre. */
|
||||
.mapboxgl-ctrl-top-left, .maplibregl-ctrl-top-left { top:12px!important;inset-inline-start:8px!important;width:380px!important;max-width:380px!important;z-index:1200!important; }
|
||||
.mapboxgl-ctrl-top-left .mapboxgl-ctrl, .maplibregl-ctrl-top-left .maplibregl-ctrl { margin:0!important;float:none!important; }
|
||||
/* Pill geometry + PPLR-matched focus ring (qa: 2026-05-02). border-radius
|
||||
/* Pill geometry + PPLR-matched focus ring. border-radius
|
||||
999px matches PPLR's .pgc-input-wrap; focus-within ring uses PPLR's
|
||||
accent rgba(59,70,61,0.18) so the search bar reads as one column with
|
||||
the SemanticTreeLegend below it. Input font-size:13px / placeholder
|
||||
|
|
@ -276,8 +273,8 @@ useShadowStyles(`
|
|||
/* isolation:isolate forces .rm-root to form its own stacking context so
|
||||
internal z-indexes (geocoder 1200, legend 1000, toolbar 10) compare only
|
||||
against each other and do NOT leak out to compete with page-level
|
||||
elements like the site nav / hamburger menu (qa: 2026-05-04 user
|
||||
reported the geocoder pill rendering above the page menu). Without
|
||||
elements like the site nav / hamburger menu (the geocoder pill once
|
||||
rendered above the page menu). Without
|
||||
this, the host element doesn't establish a stacking context and the
|
||||
internal 1200 wins against any page elements with z-index < 1200. */
|
||||
.rm-root { position:absolute;inset:0;display:flex;flex-direction:column;overflow:hidden;background:#ffffff;color:#1f2937;isolation:isolate; }
|
||||
|
|
@ -299,7 +296,7 @@ useShadowStyles(`
|
|||
top-left corner than the standard 16px gutter; end side stays 16px.
|
||||
Mobile @media block below resets both sides back to 16px since the
|
||||
mobile slot uses a smaller radius and the existing 16px is correct
|
||||
there (qa: 2026-05-04 user feedback — desktop only). Active-tab
|
||||
there (desktop only). Active-tab
|
||||
scrollIntoView still uses 16px scroll-padding which is fine — the
|
||||
tab is already visible, just nudged by the static padding. */
|
||||
padding-inline-start:36px;
|
||||
|
|
@ -465,14 +462,8 @@ const FEATURES = {
|
|||
// (a colorStrategy + a legend type + its own tree). The country app needs exactly
|
||||
// ONE of them — the WAGF Regions profile — and none of the other six.
|
||||
//
|
||||
// > design note 2026-08-01: "I don't need every single tab from the research map for
|
||||
// > the countries app. The country's application only needs the region's
|
||||
// > application profile… the research map has nested application profiles, and not
|
||||
// > all of them are needed. The only one we need is the regions tab application
|
||||
// > profile. The countries map should not display any tabs. It should just open by
|
||||
// > default to the regions tab reused, with the slight change to the application
|
||||
// > profile. That slight change is that the legend opens by default to the
|
||||
// > countries [tier]."
|
||||
// The countries app needs only the regions application profile, not every research-map tab: no tab bar,
|
||||
// the regions tab reused, its legend opening on the countries tier.
|
||||
//
|
||||
// So ALL_TABS holds that ONE profile, and showTabBar is false — a tab bar with a
|
||||
// single tab is chrome that does nothing. Everything else about the profile is
|
||||
|
|
@ -832,16 +823,13 @@ const {
|
|||
// Default the SemanticTreeLegend internal tab per outer-tab (legend type):
|
||||
// • language-families → 1 (Language) — users know "Language" better than "Language Family"
|
||||
// • affinity-blocks → 0 (Bloc) — users know affinity blocks better than clusters
|
||||
// (qa: 2026-05-08 user feedback)
|
||||
// Reset on every outer-tab change so switching between Languages and Affinity
|
||||
// Blocks lands on the right inner tab regardless of where the previous tab left it.
|
||||
// COUNTRIES-MAP DIFFERENCE #3 — boot the legend on the COUNTRY tier.
|
||||
// REGIONS_TABS = [region(0), block(1), country(2)], so 2 is the Country tier.
|
||||
// research-map boots doxa-regions on Region(0); this profile lands users on the
|
||||
// country-level view they came for.
|
||||
// design note 2026-08-01: "when you open the map it doesn't open to the regions inside
|
||||
// of the semantic tree legend, but it opens to the country's semantic level in the
|
||||
// legend… by default, when you open the map, it opens on the countries."
|
||||
// The map opens on the COUNTRY level of the semantic-tree legend, not on its regions.
|
||||
pplrInstance.activeTab.value = 2
|
||||
function _defaultInnerTabForLegendType(t) {
|
||||
if (t === 'doxa-regions') return 2 // Country — the countries-map default tier
|
||||
|
|
@ -939,7 +927,7 @@ const legendTitle = computed(() => {
|
|||
// @select to the same handler as the desktop instance — without this,
|
||||
// mobile row-clicks updated only instance.selection (shared via inject)
|
||||
// and never reached mapStore.selectFamily → applyDimFilter, so the
|
||||
// dim-on-select / restore-on-deselect path never ran (qa: 2026-05-03 iter-18).
|
||||
// dim-on-select / restore-on-deselect path never ran.
|
||||
provide('onSemanticTreeSelect', (node) => onSemanticTreeSelect(node))
|
||||
// The SAME export handler the desktop legend gets via @export, reaching the mobile sheet the
|
||||
// same way @select does. Without it <LegendMobile>'s embedded SemanticTreeLegend rendered with
|
||||
|
|
@ -959,7 +947,7 @@ const EXPERIMENTAL_EXPORTS = EXPORT_FEATURE_ENABLED && experimentalExportsOn(pro
|
|||
provide('legendExportCards', EXPERIMENTAL_EXPORTS)
|
||||
// Resolves the active tab's strategy → color for a given feature, used by
|
||||
// useSelectedPin.js so the selection-pin matches the underlying dot color
|
||||
// (qa: 2026-05-06 user feedback). research-mfe tab strategyKeys already
|
||||
// research-mfe tab strategyKeys already
|
||||
// match COLOR_MODES values directly, so no mapping needed.
|
||||
provide('getActivePinColor', (properties) => {
|
||||
const strat = getColorStrategy(activeTab.value?.colorStrategy ?? 'languageFamily')
|
||||
|
|
@ -1103,7 +1091,7 @@ function onSemanticTreeSelect(node) {
|
|||
}
|
||||
// ── Pin opacity: ONE writer (the research-map pattern, applyPinOpacity) ─────────
|
||||
// matchExpr dims to it (matched 1, others 0); null clears the dim. Two things it does that the 14 raw
|
||||
// writes it replaces did not (upg100's drift classification; rega's profile on research-map):
|
||||
// writes it replaces did not (a drift classification and a performance profile on research-map):
|
||||
// · READ BEFORE WRITE. One family click reached onSemanticTreeSelect AND the selectedFamily watcher ->
|
||||
// applyDimFilter, rewriting the same value 4+ times, each a per-feature re-upload. Compared as text
|
||||
// against the LIVE value (never a remembered one, which would go stale on a style reload).
|
||||
|
|
@ -1222,7 +1210,7 @@ async function openPacket(variant = 'full') {
|
|||
import('@map/composables/poster/useMapPoster.js'),
|
||||
])
|
||||
|
||||
// ★ THE PACKET IS THE SELECTION, not the first 50 loaded groups (rega: "Asia" printed Sudan).
|
||||
// ★ THE PACKET IS THE SELECTION, not the first 50 loaded groups (a packet for "Asia" once printed Sudan).
|
||||
// library/maps/pin-selection.js reads the pins as DRAWN (their properties are derived: filled-in
|
||||
// language family, canonical region keys), so a row filter would pick a plausible WRONG subset.
|
||||
// This map has no dim expression, so the pins layer's own filter is the whole selection.
|
||||
|
|
@ -1358,8 +1346,8 @@ const isDark = computed(() => uiStore.isDarkTheme)
|
|||
const isDarkBasemap = computed(() =>
|
||||
THEME_TOGGLE ? uiStore.isDarkTheme : FIXED_BASEMAP.lightPreset === 'night')
|
||||
|
||||
// Theme-aware data-pin outline (qa: 2026-05-06 user feedback — Joshua Project
|
||||
// uses a thin near-black outline that's barely visible; reverse-engineered).
|
||||
// Theme-aware data-pin outline (Joshua Project uses a thin near-black outline
|
||||
// that's barely visible; reverse-engineered).
|
||||
// In dark mode the outline goes white so pins stay visible against dark
|
||||
// basemaps. Boot style is light-v11, so the initial circle-stroke-color
|
||||
// in addLanguageFamilyLayer is already 'rgba(0,0,0,0.45)' — this watcher
|
||||
|
|
@ -1411,7 +1399,7 @@ async function ensureRegionsLoaded() {
|
|||
mapLayers.addRegionsLayer?.(regionsData, 'doxa-regions')
|
||||
// PRE-WARM the shader variant here, while a large GeoJSON is loading anyway. highlightCountryByIso
|
||||
// turns these three paints DATA-DRIVEN on a click; the first time that costs a one-off shader
|
||||
// compile (rega on research-map: 5.5 s MapLibre / 11.1 s Mapbox under SwiftShader). Painting each
|
||||
// compile (measured on research-map: 5.5 s MapLibre / 11.1 s Mapbox under SwiftShader). Painting each
|
||||
// current value through a case with two identical branches keeps the look and moves the compile
|
||||
// off the gesture. All three, or the other two still compile on the click (simpleresearch).
|
||||
// "Already data-driven" means the value READS THE FEATURE somewhere, not merely that it is an
|
||||
|
|
@ -1504,7 +1492,7 @@ function switchTab(tabId) {
|
|||
// LegendRows emits a window event when a row is clicked; we fly the map there.
|
||||
// ─── "Show on map" FROM THE POSTER WALL ──────────────────────────────────────────────────────────
|
||||
// ★ THIS LINK DID NOT EXIST. country-posters-mfe's onShowOnMap() switched to the Map view and
|
||||
// returned, so the camera stayed at world view and nothing was selected -- measured by rega. The
|
||||
// returned, so the camera stayed at world view and nothing was selected -- as measured. The
|
||||
// shell now records the requested country as a `select-country` ATTRIBUTE on this pane element and
|
||||
// also announces it by event; both arrive here.
|
||||
//
|
||||
|
|
@ -1518,7 +1506,7 @@ let _selectCountryObserver = null
|
|||
|
||||
/** The ONE apply path. Returns true when the request was satisfied. */
|
||||
function applySelectCountry(countryId) {
|
||||
// ★ DESELECT RESTORES THE VIEW. rega measured that a second press left the camera on India:
|
||||
// ★ DESELECT RESTORES THE VIEW. a second press measurably left the camera on India:
|
||||
// this branch used to clear the flag and return, so toggling OFF did nothing by construction.
|
||||
// PosterWall's button IS a toggle (it tracks lastShown and sends listId: null on the second
|
||||
// press), so a control the user can turn on but not off reads as broken even when the first
|
||||
|
|
@ -2082,7 +2070,7 @@ function applyContainerScale(width) {
|
|||
el.style.setProperty('--map-font-md', '13px')
|
||||
el.style.setProperty('--map-font-sm', '11px')
|
||||
// 380px matches SemanticTreeLegend's .stl-panel + the geocoder pill
|
||||
// (qa: 2026-05-02 — pin-detail card was 400px and looked WIDER than the
|
||||
// (the pin-detail card was 400px and looked WIDER than the
|
||||
// language-family legend below it; unify to 380px so the swap reads
|
||||
// as same-card different-content).
|
||||
el.style.setProperty('--map-legend-width', '380px')
|
||||
|
|
@ -2108,7 +2096,7 @@ onMounted(async () => {
|
|||
// any code branching on isMobile (LegendDesktop's internal LegendMobile
|
||||
// mount, mobile-only feature gates, etc.) runs the desktop path.
|
||||
// The CSS @media path on .rm-legend-mobile-slot still works either way,
|
||||
// but isMobile-driven branches don't. (qa: 2026-05-02 mobile audit.)
|
||||
// but isMobile-driven branches don't.
|
||||
uiStore.init?.()
|
||||
// COUNTRIES-MAP DIFFERENCE #2 (boot half) — research-map forces tabs[0] (Prayer)
|
||||
// here on mount, which silently overrode the ref's initial value. THIS is the
|
||||
|
|
|
|||
|
|
@ -15,11 +15,8 @@
|
|||
* rendering lives in the shared @map/components/poster/PosterWall.vue — this
|
||||
* profile supplies only its data and its taxonomy.
|
||||
*
|
||||
* > design note 2026-08-01: "the poster wall should be semantically organized by the
|
||||
* > regions… there's going to be way too many countries, so we'll need to be able
|
||||
* > to collapse the regions and see how many countries there are in each of the
|
||||
* > regions, because people don't want to just look at, like, scroll through
|
||||
* > hundreds of countries."
|
||||
* The wall is organized by REGION, and each region collapses and shows how many countries it holds, so a
|
||||
* reader never has to scroll through hundreds of countries.
|
||||
*
|
||||
* WHY COLLAPSE IS NOT A NICETY — measured from the live data:
|
||||
* Asia 28 countries / 1309 groups
|
||||
|
|
@ -30,8 +27,7 @@
|
|||
* collapsing, the wall is a scroll of mostly single-entry cards.
|
||||
*
|
||||
* SORTING IS THE ANSWER TO SPARSE SHEETS, not a template variant.
|
||||
* > Brief: "if it has a one-people-group on it, we still want a one-people-group
|
||||
* > map generated… those get sorted to the bottom of the list."
|
||||
* A country with a single people group still gets its own map; it simply sorts to the bottom.
|
||||
* The shared wall always sorts sections AND cards by descending count, so the
|
||||
* dense countries lead and the long tail sinks. There is no floor.
|
||||
*/
|
||||
|
|
@ -205,7 +201,7 @@ const groupBy = (c) => {
|
|||
const collapsedBy = (sec) => sec.total < 60
|
||||
|
||||
// The not-yet reason travels WITH the record, so the exact number the wall prints
|
||||
// can never drift from the data. Brief: "it should say the exact number in our data."
|
||||
// can never drift from the data: the wall states the exact number the data holds.
|
||||
// The reason is shown on the card and in the print HUD, so it speaks the reader's language: the data's
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ function paneElement(pane) {
|
|||
// `inert` on every pane except the active one: the same contract as upg-100-mfe (4642e78d). The panes
|
||||
// are stacked in one scroll-snap column, so the hidden pane is still full-size geometry; inert tells
|
||||
// PosterWall (67e9ede5) the wall is out of view so it forgets its shown card, and a second
|
||||
// "Show on map" after Map -> back SHOWS the country again instead of toggling it off (rega row 79).
|
||||
// "Show on map" after Map -> back SHOWS the country again instead of toggling it off.
|
||||
// inert, not display:none: hiding would tear down the live map.
|
||||
function markActivePane(activeId) {
|
||||
if (!column || !activeId) return
|
||||
|
|
@ -170,7 +170,7 @@ function onShowOnMap(e) {
|
|||
if (d.instanceId && d.instanceId !== instanceId.value) return
|
||||
go('map')
|
||||
// ★ THE FORWARDING THAT WAS NEVER WRITTEN. This handler used to end at go('map'), so the tab
|
||||
// changed and the map was never told anything at all -- rega measured exactly that: "the camera
|
||||
// changed and the map was never told anything at all -- measured exactly: "the camera
|
||||
// stays at world view, nothing selected". It is not a timing bug and no amount of waiting fixes
|
||||
// it; the link simply did not exist.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -20,9 +20,7 @@ import { defineMapElement } from '@map/elements/defineMapElement.js'
|
|||
* country-poster-wall.vue the poster grid, grouped by region, collapsible
|
||||
*
|
||||
* WHY A SEPARATE BUNDLE rather than two more profiles in my-upg-100-list:
|
||||
* > design note 2026-08-01: "I don't mind creating multiple application profiles, one
|
||||
* > for each application. I do mind that we really need to make sure every
|
||||
* > component that we're [re]usable is added to the library."
|
||||
* Several application profiles are fine; every REUSABLE component belongs in the library.
|
||||
* PROFILES duplicate; COMPONENTS get promoted to library/ and parameterized. The
|
||||
* wall here is @map/components/poster/PosterWall.vue — the same component the
|
||||
* 100-list renders — differing only in data, taxonomy and copy.
|
||||
|
|
|
|||
Loading…
Reference in a new issue