From 5d67a3969d6d2d0608fa6cdee89c1c48253e0b32 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Mon, 28 Sep 2026 03:58:06 +0000 Subject: [PATCH] PPLR maps: the legend and the instance store are the library's, not drifted copies pplr carried private copies of SemanticTreeLegend and usePplrInstance that had drifted. The instance copy lacked activeTabTouched, so the legend tab snapped back to its default after a pick; the legend copy spoke English accessibility labels in every locale. Both are deleted and the views import the library versions. --- .../src/components/FlatLegend.vue | 2 +- .../src/components/MapboxGeocoder.vue | 2 +- .../src/components/PinDetailCard.vue | 2 +- .../src/components/SemanticTreeLegend.vue | 971 ------------------ .../src/composables/usePplrInstance.js | 93 -- .../src/views/pplr-language-families.vue | 4 +- .../src/views/pplr-religion.vue | 2 +- .../src/views/pplr-rop-tree.vue | 4 +- .../src/views/pplr-translations.vue | 2 +- 9 files changed, 9 insertions(+), 1073 deletions(-) delete mode 100644 plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue delete mode 100644 plugin-designs/pplr-data-maps/src/composables/usePplrInstance.js diff --git a/plugin-designs/pplr-data-maps/src/components/FlatLegend.vue b/plugin-designs/pplr-data-maps/src/components/FlatLegend.vue index 1c3dc1e3e..5a8a66ee5 100644 --- a/plugin-designs/pplr-data-maps/src/components/FlatLegend.vue +++ b/plugin-designs/pplr-data-maps/src/components/FlatLegend.vue @@ -19,7 +19,7 @@ * and writes selection back to `instance.selection.value`. */ import { ref, computed } from 'vue' -import { useInstance } from '../composables/usePplrInstance.js' +import { useInstance } from '@map/composables/usePplrInstance.js' import { usePplrT } from '../composables/usePplrT.js' const props = defineProps({ diff --git a/plugin-designs/pplr-data-maps/src/components/MapboxGeocoder.vue b/plugin-designs/pplr-data-maps/src/components/MapboxGeocoder.vue index 48bc15fd2..96100266e 100644 --- a/plugin-designs/pplr-data-maps/src/components/MapboxGeocoder.vue +++ b/plugin-designs/pplr-data-maps/src/components/MapboxGeocoder.vue @@ -23,7 +23,7 @@ import { ensureGeocoder, GEOCODER } from '../utils/pplrData.js' import { escHtml } from '../utils/pplrMap.js' import { resolveMapProvider } from '@map/composables/useMapProvider.js' import { ensureEngineScript, engineGlobal } from '@map/composables/useMapInstance.js' -import { useInstance } from '../composables/usePplrInstance.js' +import { useInstance } from '@map/composables/usePplrInstance.js' const props = defineProps({ /** Pre-flattened searchable items: [{id, label, kind, depth, filter, color?, count?, pop?, ...}] */ diff --git a/plugin-designs/pplr-data-maps/src/components/PinDetailCard.vue b/plugin-designs/pplr-data-maps/src/components/PinDetailCard.vue index 106907fb7..c30409c65 100644 --- a/plugin-designs/pplr-data-maps/src/components/PinDetailCard.vue +++ b/plugin-designs/pplr-data-maps/src/components/PinDetailCard.vue @@ -16,7 +16,7 @@ * (X button or Esc) clears instance.pinDetail and the legend reappears. */ import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue' -import { useInstance } from '../composables/usePplrInstance.js' +import { useInstance } from '@map/composables/usePplrInstance.js' import { LOCALES, LOCALE_NAMES } from '../utils/pplrMap.js' import { loadNames } from '../utils/pplrData.js' import { usePplrT } from '../composables/usePplrT.js' diff --git a/plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue b/plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue deleted file mode 100644 index 32fb1440a..000000000 --- a/plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue +++ /dev/null @@ -1,971 +0,0 @@ - - - - - diff --git a/plugin-designs/pplr-data-maps/src/composables/usePplrInstance.js b/plugin-designs/pplr-data-maps/src/composables/usePplrInstance.js deleted file mode 100644 index b291a3378..000000000 --- a/plugin-designs/pplr-data-maps/src/composables/usePplrInstance.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * usePplrInstance.js — per-map "instance store" for PPLR profiles. - * - * Implements the Mediator Pattern (per QA-feedback Round 8 architecture closeout): - * each map cluster (one map + one geocoder + one legend) gets its own scoped store. - * Components never call each other directly — they read/write the store and watch its fields. - * - * Bidirectional sync between geocoder and legend lives here: - * - Selecting a row in the legend → store.selection set → geocoder watches and clears its text - * - Picking a result in the geocoder → store.selection set + store.activeTab synced → legend reacts - * - X on either side → store.selection cleared → both clear - * - * Each profile creates one instance via createPplrInstance() and provides it. - * Children (legend, geocoder) consume it via useInstance(). - */ - -import { ref, provide, inject } from 'vue' - -const KEY = 'pplrInstance' - -/** - * Create a new instance store for one map. - * @param {string} instanceId Stable id used for debugging/keying. - * @returns {object} reactive store - */ -export function createPplrInstance(instanceId) { - return { - instanceId, - - // ── Selection (drives map filter + legend highlight) ──────────────────── - /** Currently selected tree node, or null. Shape: { id, label, kind, depth, filter, color, ...extras } */ - selection: ref(null), - - /** Active legend tab index (0-based — but Tab 1 = Generation 1 in user-facing labels) */ - activeTab: ref(0), - - /** Search-mode flag: true when geocoder text ≥ 2 chars */ - isSearching: ref(false), - - // ── Geocoder text (read by legend so it can dim itself, etc.) ──────────── - geocoderText: ref(''), - - // ── Pin-detail panel (clicked pin replaces legend with detail) ────────── - /** Currently clicked pin properties, or null */ - pinDetail: ref(null), - - // ── Chrome theme — driven by the active basemap (light-v11 → 'light', - // anything else → 'dark'). Legend/geocoder/detail-card read this and - // apply [data-theme] CSS overrides so chrome matches the map underneath. - theme: ref('dark'), - } -} - -/** - * Provide the instance to descendants. - * Call this in the profile (parent) component. - */ -export function provideInstance(instance) { - provide(KEY, instance) -} - -/** - * Inject the instance in a child component. - * Returns null if no instance was provided (component used standalone). - */ -export function useInstance() { - return inject(KEY, null) -} - -/** - * Convenience: select a node, sync legend tab to its depth. - */ -export function selectNode(instance, node) { - if (!instance) return - if (!node) { - instance.selection.value = null - return - } - instance.selection.value = node - if (typeof node.depth === 'number') { - instance.activeTab.value = node.depth - } -} - -/** - * Convenience: clear all (selection + geocoder text + pin detail). - */ -export function clearAll(instance) { - if (!instance) return - instance.selection.value = null - instance.geocoderText.value = '' - instance.pinDetail.value = null -} diff --git a/plugin-designs/pplr-data-maps/src/views/pplr-language-families.vue b/plugin-designs/pplr-data-maps/src/views/pplr-language-families.vue index c3c5bb646..e4e537ea2 100644 --- a/plugin-designs/pplr-data-maps/src/views/pplr-language-families.vue +++ b/plugin-designs/pplr-data-maps/src/views/pplr-language-families.vue @@ -18,11 +18,11 @@ import { resolveNamedStyle } from '@map/styles/registry.js' import { resolveMapProvider } from '@map/composables/useMapProvider.js' import { useMapInstance } from '@map/composables/useMapInstance.js' import { addCountryPolygons, POPUP_CSS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick, escHtml } from '../utils/pplrMap.js' -import SemanticTreeLegend from '../components/SemanticTreeLegend.vue' +import SemanticTreeLegend from '@map/components/legends/SemanticTreeLegend.vue' import MapboxGeocoder from '../components/MapboxGeocoder.vue' import PinDetailCard from '../components/PinDetailCard.vue' import LoadingGlobe from '../components/LoadingGlobe.vue' -import { createPplrInstance, provideInstance } from '../composables/usePplrInstance.js' +import { createPplrInstance, provideInstance } from '@map/composables/usePplrInstance.js' const pplrInstance = createPplrInstance('language-families') provideInstance(pplrInstance) diff --git a/plugin-designs/pplr-data-maps/src/views/pplr-religion.vue b/plugin-designs/pplr-data-maps/src/views/pplr-religion.vue index f4312242d..e9a6a4959 100644 --- a/plugin-designs/pplr-data-maps/src/views/pplr-religion.vue +++ b/plugin-designs/pplr-data-maps/src/views/pplr-religion.vue @@ -19,7 +19,7 @@ import MapboxGeocoder from '../components/MapboxGeocoder.vue' import PinDetailCard from '../components/PinDetailCard.vue' import FlatLegend from '../components/FlatLegend.vue' import LoadingGlobe from '../components/LoadingGlobe.vue' -import { createPplrInstance, provideInstance } from '../composables/usePplrInstance.js' +import { createPplrInstance, provideInstance } from '@map/composables/usePplrInstance.js' const pplrInstance = createPplrInstance('religion') provideInstance(pplrInstance) diff --git a/plugin-designs/pplr-data-maps/src/views/pplr-rop-tree.vue b/plugin-designs/pplr-data-maps/src/views/pplr-rop-tree.vue index 8b1b503b1..863900033 100644 --- a/plugin-designs/pplr-data-maps/src/views/pplr-rop-tree.vue +++ b/plugin-designs/pplr-data-maps/src/views/pplr-rop-tree.vue @@ -14,11 +14,11 @@ import { resolveNamedStyle } from '@map/styles/registry.js' import { resolveMapProvider } from '@map/composables/useMapProvider.js' import { useMapInstance } from '@map/composables/useMapInstance.js' import { addCountryPolygons, setCountryPolygonTheme, POPUP_CSS, COUNTRY_FS_URL, COUNTRY_FS_FIELDS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick, escHtml } from '../utils/pplrMap.js' -import SemanticTreeLegend from '../components/SemanticTreeLegend.vue' +import SemanticTreeLegend from '@map/components/legends/SemanticTreeLegend.vue' import MapboxGeocoder from '../components/MapboxGeocoder.vue' import PinDetailCard from '../components/PinDetailCard.vue' import LoadingGlobe from '../components/LoadingGlobe.vue' -import { createPplrInstance, provideInstance } from '../composables/usePplrInstance.js' +import { createPplrInstance, provideInstance } from '@map/composables/usePplrInstance.js' // ── Per-map instance store (Mediator pattern: ties legend, geocoder, map together) ── const pplrInstance = createPplrInstance('rop-tree') diff --git a/plugin-designs/pplr-data-maps/src/views/pplr-translations.vue b/plugin-designs/pplr-data-maps/src/views/pplr-translations.vue index 2a0a7869b..b9492017b 100644 --- a/plugin-designs/pplr-data-maps/src/views/pplr-translations.vue +++ b/plugin-designs/pplr-data-maps/src/views/pplr-translations.vue @@ -14,7 +14,7 @@ import { useMapInstance } from '@map/composables/useMapInstance.js' import { addCountryPolygons, POPUP_CSS, FullscreenToggleControl, makePinIndicator, bindFuzzyPinClick } from '../utils/pplrMap.js' import PinDetailCard from '../components/PinDetailCard.vue' import LoadingGlobe from '../components/LoadingGlobe.vue' -import { createPplrInstance, provideInstance } from '../composables/usePplrInstance.js' +import { createPplrInstance, provideInstance } from '@map/composables/usePplrInstance.js' const pplrInstance = createPplrInstance('translations') provideInstance(pplrInstance)