Rings that zoom apart, as a flag, so either answer to row 130 is one line at the call site. DEFAULT IS 'fit' — this changes nothing today. 'fit' keeps every pin at least minSep apart at the chart's own scale and reports what will not fit. That is the right behaviour for a PRINTED sheet, where there is no zoom and a dot a reader cannot resolve is a dot that is not there. 'fill-all' places all of them. Separation is in map units, so it grows with zoom: measured on the shipped table, the worst ring is ring 1 (1,190 groups in the smallest circle) separating to 8px on screen at z=4.45, and ring 8 (1,932) at z=3.50. The GROUP label tier already starts at 5.2, so every pin is clickable before the names appear. At the start zoom the band reads as a dense arc of colour rather than countable dots, which is the intended appearance — and the reason a caller in this mode must caption the true total rather than a count that changes with zoom. The row count had to follow the POPULATION here, not the pin size. Keeping the 'fit' row count would have packed 1,932 pins into the two rows a band allows at full size, and they would have sat almost on top of each other along the arc — the single-line defect again, one level down. Ring 8 uses 14 rows in fill-all against 2 in fit. Real table: fit places 1,851 of 7,296, fill-all places 7,296 of 7,296, both with zero pins outside their band. Six new assertions, and the row-growth arm is poison-tested: making fill-all reuse the fit row count fails it at "2 rows vs 2". |
||
|---|---|---|
| .. | ||
| api | ||
| charts/whiteboard | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| dashboards | ||
| data | ||
| elements | ||
| i18n | ||
| layouts | ||
| lists | ||
| maps | ||
| packet | ||
| packets | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| vendor/cldr | ||
| 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.