Second plugin in the MapLibre rollout, copying doxa-research-map's pattern rather than inventing a second one: hasGeocoder gates the search bar on provider, MapToolbar collapses its own reserved gap, and the legend rises into the vacated space. The geocoder stays Mapbox-only by decision, not omission: its maplibre path still calls makeMapboxForwardGeocode(accessToken), so the search REQUEST goes to Mapbox whichever engine draws the tiles — a keyless map with a search bar is not keyless and would bill the very key the option exists to avoid. ★ RAISING THE LEGEND COLLIDED WITH THE FLY/CLUSTERS TOOLBAR, and only the screenshot showed it. LegendTools is absolutely positioned at top:14px, centred on the legend column — the SEARCH BAR's slot. With the bar gone and the legend raised to 14, the toolbar printed straight across the legend's "WAGF REGIONS" header. Every number was green while the panel was unusable. A collision is worse than the gap this work exists to close. So LegendTools now EXPORTS showsFlyTools(legendType), and hosts ask it instead of re-typing its condition — two copies of one rule is how they drift. A host with no search bar stops at 56px when the toolbar renders and rises to 14px when it does not. doxa-research-map picked up the same fix. MEASURED on the built tree, both engines: country-posters mapbox search@12 legend@60 (unchanged) country-posters maplibre search@NONE legend@56 (no gap, no overlap) doxa-research-map maplibre search@NONE legend@58 doxa-simple-map needed NOTHING and was not touched: FEATURES.geocoder has been false on both engines since 2026-05-04 and its legend already sits flush at 10px. Recorded as a no-op so the next pass does not "fix" it. 22/22 bundles, 7/7 pre, 18/18 post, browser sweep of five StackLayout consumers and all seven studio dashboards. |
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| maps | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| index.js | ||
| ProfileLoader.vue | ||
| README.md | ||
library/ (@map) — the shared map template
The other half of the architecture (see ../plugin-designs/README.md):
every map in plugin-designs/<your-map>/ composes the reusable, parameterized pieces that live here.
Fix something here and every map benefits on the next build; break something here and every map
breaks — read components/WARNING.md before touching shared pieces.
| Folder | What lives there |
|---|---|
ProfileLoader.vue |
The root of every bundle: parses profile-config, loads the profile, provides the stores + config |
components/ |
Shared UI — map, legends, controls, popup (PeopleGroupDetail), tables, charts, hints (README) |
composables/ |
Shared logic — map engine (useMapInstance), provider switch, shadow-DOM styles, theming |
stores/ |
Per-instance Pinia stores (ui / map / data) — provided by ProfileLoader, injected by children |
api/ |
Data-source registry (sources.json) + fetch layer — the JAMstack runtime wiring |
colors/ |
Color sets maps opt into via profile-config.colorSet |
i18n/ |
The shared string catalogs (all maps speak the same languages) |
data/ |
Small shared datasets baked at build time (big served data lives in ../data/) |
constants/ |
Shared constants |
scripts/ |
Maintainer pipelines (posters, research) — not part of any bundle |
The rule of thumb (same as WARNING.md): custom code in your app-profile, reusable code here — and things only move from the first to the second once they've earned it.