doxa-maps/library
Doxa Maps 948a00c7da Two maps placed their share menu correctly because they were named in a string literal
DRV1650-SHARE-MENU-MOBILE-OFFSCREEN. On mobile the share/embed menu opens off-screen to
the right on Cities with Internet and Gospel Resources, while Simple Research and Prayer
Progress are correct.

THE SPLIT IS NOT A PLUGIN DIFFERENCE. MapControlMenu resolved the area it places inside
with closest('.dsm-map-area, .rm-map-area, .dm-map-area, [data-map-area]') -- three
hardcoded plugin class names. doxa-simple-map and research-map are on that list. Everyone
else resolved `|| null`, and place() answers a null area by returning an EMPTY STYLE,
which leaves .mcm{position:absolute} at its STATIC position: below-and-right of a button
already at the top right. Off the page.

So the two maps that worked worked because they were named in a string in another
component, and the bug was decided by a literal rather than by geometry. It is also the
SAME failure the 2026-09-22 dashboard fix documents twenty lines below -- not "a wrong
side was chosen" but "nothing chose anything" -- returning through a different door.

MEASURED: cities-internet, gospel-resources, key-cities and upg-neighbor carry no
map-area class and no data-map-area, so FOUR plugins were unanchored. The Driver reported
two. A fix aimed at the two he named would have left the other two, and a gate written
around them would have passed.

The named area is still preferred, because it is a real map's walls and a menu must not
outgrow them. When a plugin declares none, the anchor falls back to the toolbar's own
offsetParent -- the containing block this menu is absolutely positioned against, so
placing against it is correct by definition rather than by registration. A plugin that
adopts a class later simply gets the better anchor; nothing has to be registered anywhere
for a menu to be on screen.

check-map-menu-anchor asserts the PROPERTY rather than the list: that the resolution
cannot produce null from a real toolbar, that place() still guards a genuinely absent
one, and that a declared map area is still preferred over the fallback. Negative-tested:
restoring the allowlist-only resolution turns it red.

NEEDS EYES: this is rendered geometry and I work headless. What I can prove is that the
anchor is no longer decided by a name; whether the menu now sits correctly at 390px on
all four is rega's to measure.
2026-09-28 16:53:12 +00:00
..
api The people-group list is a universal prop: the same maps can show any project's list 2026-09-26 05:16:02 +00:00
colors The legends mirror now, and their labels never became their values 2026-09-26 22:06:58 +00:00
components Two maps placed their share menu correctly because they were named in a string literal 2026-09-28 16:53:12 +00:00
composables A composite embedded bare dropped tokenUrl, and passed because a ProfileLoader sat above it 2026-09-28 15:52:47 +00:00
constants Every runtime CDN script and stylesheet is an exact version with an integrity hash 2026-09-26 05:50:24 +00:00
data Country names in the reader's language, for all 242 2026-09-27 04:14:31 +00:00
i18n Author legend.noList, which existed in no catalogue at all 2026-09-28 15:52:47 +00:00
layouts DataTable + StackLayout: physical left/right -> logical, where the rule mirrors 2026-09-28 09:48:45 +00:00
lists List names speak the reader's language: listLabel and listDefinition 2026-09-26 08:39:52 +00:00
maps Two readers of one private field, and nothing was holding them together 2026-09-28 15:52:46 +00:00
packet One scatter implementation, and the gate that could have caught the bar 2026-09-28 15:01:19 +00:00
packets Number({value:"1"}) is NaN, so the whole reachedness sentence vanished 2026-09-28 15:52:47 +00:00
scripts The language tree's layout becomes library's; its gospel classification does not 2026-09-28 15:52:47 +00:00
stores Make the map's clustering picker real: four options, four different maps 2026-09-25 16:16:46 +00:00
styles Fiord: the MapLibre dark that actually looks like the map it replaces 2026-09-24 02:26:14 +00:00
utils A relative share link works when you click it, which is why nobody caught it 2026-09-28 16:50:59 +00:00
vendor/cldr Country names in the reader's language, for all 242 2026-09-27 04:14:31 +00:00
index.js Move internal files out of the repo; fix five dead links; a fuller delegation diagram 2026-09-25 05:14:10 +00:00
ProfileLoader.vue ?lang= on the page turns every embedded map's direction and data language, not only its text 2026-09-27 06:42:54 +00:00
README.md Complete app-profiles->plugin-designs sweep across comments/docs; build green; layouts kept at root (load-bearing) 2026-09-17 18:18:19 +00:00

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.