doxa-maps/library/composables
Doxa Maps 3daa602741 One pin click should call the click handler once, not twice
The pin click handler was registered on two layers that always overlap: the
visible pins and the invisible hitbox drawn over them. A layer-scoped click
fires once per registered layer with a feature under the point, and every
visible pin has a hitbox twin at its own point -- same source, larger radius,
added alongside it so taps are easier to land. So every pin click ran the
handler twice, 3ms apart. Measured on the leaf at 1440, on two different pins,
both reporting both layers at the click point; exactly twice, never more.

That already shipped as a visible bug once: the second call overwrote the
legend state the first had saved, so closing a people-group detail reopened a
legend the reader had deliberately collapsed. The store is now idempotent, so
that particular symptom cannot return, but the duplicate call was the cause and
is still here -- anything that counts, toggles, appends or reports from a pin
click is wrong by a factor of two, and nothing in the code warns the next
author.

Bind the click to the hitbox only. It covers every click the visible layer
would catch: both layers are created together by addLanguageFamilyLayer, from
the same source, and the two places that filter the hitbox mirror the pin
filter rather than narrowing it, so the two layers always carry the same
features. The handler reads only properties and geometry, which are identical
between them. Cursor handlers stay on both layers -- mouseenter and mouseleave
are idempotent, so they were never part of this.

Checked before relying on it: exactly one addLayer creates each of the two
layers, both inside addLanguageFamilyLayer as consecutive statements with no
branch between them; no flag gates the hitbox; no removeLayer anywhere touches
either; and the only layout writes on the pin layer set circle-sort-key. The
invariant is now stated at the binding, because it is not visible from the call
site: a pins layer added without a hitbox would get no clicks.

Also fixes a dead trap in the same file. The people-groups-pins branch called
the handler with no argument, so it fell back to the language-family pins and
bound the wrong layer. Nothing creates that layer today -- the id occurs only
in this file -- so the branch never runs, but whoever first adds it would have
got a duplicate binding, or no click handler at all if the language-family
layer were absent.
2026-09-29 12:44:25 +00:00
..
legends Move four identical bodies into the library 2026-09-29 09:19:04 +00:00
poster The poster's legend was painted over the map, unless you ask otherwise 2026-09-29 00:59:14 +00:00
.gitkeep doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
host-token-url.js A composite embedded bare dropped tokenUrl, and passed because a ProfileLoader sat above it 2026-09-28 15:52:47 +00:00
joinBase.js The country link went to the people group; four links broke on a missing slash 2026-09-28 21:59:29 +00:00
printInPlace.js A print frame is kept until it has printed: its 60 s clock starts at load, not at creation 2026-09-29 08:11:49 +00:00
retryableAsyncComponent.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useAvatarGenerator.js Keep the prayer yellow to the prayer palette; other palettes keep their orange 2026-09-16 21:21:07 -05:00
useCardSurface.js The prayer-progress cards become shared components too 2026-09-27 04:14:31 +00:00
useCountryOutline.js Comments state their findings instead of citing files outside the repo 2026-09-29 03:46:37 +00:00
useCsvExport.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useDashboardData.js The research dashboard asked the API for English, then translated half of it 2026-09-28 22:25:31 +00:00
useDashboardLayout.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useDashboardSearch.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useDocumentFont.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useEdgeScrollAffordance.js doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
useFacetAggregation.js Kit 4c-1: the counting half, and the bucket that said "Other" in every locale 2026-09-27 04:14:13 +00:00
useGeocoderSearch.js Brand-neutral shared VerticalTabsLayout with lazy nested-element hosting; fix cap-5 prayer tab leaf path; neutralize library identifiers; add Forgejo CI build-gate workflow 2026-09-19 17:24:30 +00:00
useHostedHelp.js A map's own ? can ask its host for help: HelpButton and useHostedHelp send doxa:help 2026-09-29 02:35:35 +00:00
useHostTokenUrl.js A composite embedded bare dropped tokenUrl, and passed because a ProfileLoader sat above it 2026-09-28 15:52:47 +00:00
useMapAttribution.js Escape the two popup URLs and the packet photo URL 2026-09-29 05:26:27 +00:00
useMapClustering.js useMapClustering: no console.log in shipped bundles 2026-09-28 02:01:47 +00:00
useMapData.js The prayer map's legends see the pins it draws 2026-09-27 02:17:35 +00:00
useMapEvents.js One pin click should call the click handler once, not twice 2026-09-29 12:44:25 +00:00
useMapFly.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useMapInstance.js The engine rule reads as it is everywhere it is written: auto is MapLibre, token or not 2026-09-29 11:04:22 +00:00
useMapLayers.js The interactive maps fetch the web-sized boundaries 2026-09-27 03:25:59 +00:00
useMapProvider.js The engine rule reads as it is everywhere it is written: auto is MapLibre, token or not 2026-09-29 11:04:22 +00:00
useMapTheme.js Research map: religion joins prayer, adoption and engagement on the night look; only people, regions and languages are monochrome day 2026-09-16 21:01:37 -05:00
useOptionalT.js A host without i18n picks the same plural form an installed i18n would, in every language 2026-09-29 05:26:26 +00:00
usePosterPrints.js Serve the packet text at print time instead of bundling all 11 languages 2026-09-29 08:33:44 +00:00
usePplrInstance.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
usePrayedLocally.js Public builds carry zero platform surface + copy-embed on staging 2026-09-16 05:13:44 +00:00
usePrayerStatistics.js doxa-maps — portable bundler for embeddable JAMstack maps 2026-09-10 01:53:07 +00:00
useScreenGate.js A desktop-only composite blocks on a phone too: the phone rule is a composable its root can call 2026-09-29 09:18:55 +00:00
useSearch.js Religion families follow the reader; the value they filter on does not 2026-09-27 01:57:23 +00:00
useSelectedPin.js engineFor(map): build markers, popups and poster maps with the map's OWN engine 2026-09-25 18:38:17 +00:00
useSelectionBus.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useSelectionCamera.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useSentence.js Prove the sentence helper against a real t(), instead of claiming it 2026-09-29 05:26:25 +00:00
useShadowStyles.js Every shadow root honours prefers-reduced-motion 2026-09-27 05:05:04 +00:00
useShareableView.js A shared link reads a list separated by , or |, so partner links already published keep working 2026-09-27 19:10:08 +00:00
useSharedMapToken.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
useTextHighlight.js Two plugins built the same search highlighter; now there is one 2026-09-27 01:16:50 +00:00