A pin on the bounded set opens its people group too

The same interaction as the centered set and as GiTree, so a reader who learns
one chart has learned all three: click a group, its drawer opens and the camera
frames it; reduced motion jumps instead of easing. The centre dish is not a
people group, so it is excluded by tier rather than by id — an id test would
have to know the dish's private name and would break the day it changed.

This chart takes no band layout: its groups are inside or outside one boundary,
not on rings, so bandFill has nothing to spread. Only the interaction is shared.
This commit is contained in:
Doxa Maps 2026-09-29 00:54:13 +00:00
parent 3a06772a27
commit 4e00ec4aa1

View file

@ -22,7 +22,7 @@ import { useShadowStyles } from '@map/composables/useShadowStyles.js'
import { labelFont } from '@map/maps/label-font.js'
import { whiteboardProjection } from '@map/maps/whiteboard-projection.js'
import { addTieredPointLayers } from '@map/charts/whiteboard/tiered-points.js'
import { createBoardMap } from '../boardEngine.js'
import { createBoardMap, reducedMotion } from '../boardEngine.js'
import { useBoardState } from '../useBoard.js'
import { useGiT } from '../useGiT.js'
import { layoutBoundedSet } from '@map/components/charts/boundedSet.js'
@ -53,6 +53,24 @@ const data = computed(() => toFeatureCollection(features.value, project).collect
const dishDegrees = computed(() => dishRadiusDegrees(R, project))
/**
* Open a people group and frame it — the same interaction as the centered set and as GiTree, so a
* reader who learns one chart has learned all three. See GiCenteredSetMap.openAndFrame for why the
* camera treats a single dot as a degenerate frame rather than calling fitBounds on it.
*/
function openAndFrame(slug, feature) {
try { board.openGroup?.(slug) } catch (_) { /* the drawer is the ask; the camera is the flourish */ }
const map = mapRef.value
const c = feature?.geometry?.coordinates
if (!map || !Array.isArray(c) || c.length < 2) return
try {
const duration = reducedMotion() ? 0 : 700
const zoom = Math.max(map.getZoom(), 5.2)
if (duration) map.easeTo({ center: c, zoom, duration, essential: true })
else map.jumpTo({ center: c, zoom })
} catch (_) { /* a degenerate frame never breaks the board */ }
}
onMounted(async () => {
try {
const made = await createBoardMap(el.value, board.profileConfig?.value || {},
@ -84,6 +102,9 @@ onMounted(async () => {
stroke: '#3b463d', // a fill would imply the inside is a region
},
font: labelFont,
// A pin opens its people group, the same as the centered set. The centre dish is not a
// group, so it is filtered out by tier rather than by id.
onClick: (id, f) => { if (f?.properties?.tier === TIERS.GROUP && id) openAndFrame(id, f) },
}))
}
// ★ CALLED UNCONDITIONALLY, AND THE GUARD THAT USED TO BE HERE IS WHY THIS WAS BROKEN TWICE.