The Driver named eight filters -- people, region, block, country, language, religion, prayer, engagement -- and the rail rendered seven. engagement_status was already fully built: a label and getter, the Status section in the tree, and the Engagement column in the table. It had simply never been registered in the dropdown list, so the one piece missing was a single entry. Adding it exposed the constraint that goes with the count. The dropdowns are a fixed 168px on a 200px pitch, so eight need about 1428px. At a 1440 viewport that just fits; at 1280 the row reported scrollWidth 1414 against clientWidth 1252 -- a horizontal scrollbar, which is exactly what the Driver asked not to see. The row now wraps rather than scrolls. Wrapping is the right lever because the fixed 168px width is itself a Driver decision: a flexible width was what made the dropdowns jump as a selection changed. The justify-content pair is kept, not simplified -- its own comment records that `safe` degrades space-evenly to flex-start rather than pushing the first item to an offset a scroller can never reach. That hazard belonged to the scroller and costs nothing now, so it stays. Verified on a settled tree at three widths. 1440, 1280 and 1024 all render eight filters across two rows at identical offsets, with the row reporting no horizontal overflow and every filter fully inside the viewport. |
||
|---|---|---|
| .. | ||
| 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.