bharat-choropleth 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/index.js +64 -25
- package/dist/index.js.map +1 -1
- package/dist/style.css +8 -2
- package/package.json +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 - 2026-09-13
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Drilling in no longer loses keyboard focus. Stepping back out had an obvious
|
|
8
|
+
target — the region just left — and going in had none, so focus fell to
|
|
9
|
+
`<body>` on every drill-down, dropping a keyboard user at the top of the
|
|
10
|
+
document and telling a screen reader nothing about where they now were. Focus
|
|
11
|
+
now lands on the first region of the level entered; on the message when that
|
|
12
|
+
level holds nothing; and back on the district itself when an optimistic drill
|
|
13
|
+
turns out to be a leaf, which had unmounted the region under the cursor.
|
|
14
|
+
- Changing the state now tells the host that the sub-district level was dropped.
|
|
15
|
+
The component clears that level itself and fired no
|
|
16
|
+
`onSubDistrictDrillDownChange`, so anything mirroring the level kept pointing
|
|
17
|
+
at a district of the state just left — the wrong level, reported against the
|
|
18
|
+
wrong map. There is no prior district to hand back, because it belonged to the
|
|
19
|
+
state that is gone, so the callback receives `(null, undefined)`.
|
|
20
|
+
- A host that stops controlling a prop now keeps what is on screen. The
|
|
21
|
+
uncontrolled slot held whatever it contained before control began, often many
|
|
22
|
+
interactions stale, and that was what came back.
|
|
23
|
+
- Two rows naming the same region warn instead of resolving in silence. That
|
|
24
|
+
silence is how a mis-shaped query becomes a believed wrong number: a state
|
|
25
|
+
showing one of its districts' totals looks exactly like a state showing its
|
|
26
|
+
own. The last value still wins — changing that would move numbers under
|
|
27
|
+
existing callers — and the warning quotes the spelling the caller wrote.
|
|
28
|
+
- Legend swatches size from the legend row rather than the viewport. `vw`
|
|
29
|
+
measures the browser window, so in any embed narrower than the page the
|
|
30
|
+
swatches pinned to their maximum and overflowed the map they belong to.
|
|
31
|
+
|
|
3
32
|
## 0.3.0 - 2026-09-05
|
|
4
33
|
|
|
5
34
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/IndiaChoropleth.tsx
|
|
2
2
|
import { geoMercator, geoPath } from "d3-geo";
|
|
3
|
-
import { useCallback as useCallback2, useEffect, useId, useLayoutEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
3
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useId, useLayoutEffect, useMemo, useRef, useState as useState2 } from "react";
|
|
4
4
|
|
|
5
5
|
// src/geometry.ts
|
|
6
6
|
import { feature as topoFeature } from "topojson-client";
|
|
@@ -220,10 +220,13 @@ function ringsToPath(rings) {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// src/useControllableState.ts
|
|
223
|
-
import { useCallback, useState } from "react";
|
|
223
|
+
import { useCallback, useEffect, useState } from "react";
|
|
224
224
|
function useControllableState(controlled, initial) {
|
|
225
225
|
const [uncontrolled, setUncontrolled] = useState(initial);
|
|
226
226
|
const value = controlled === void 0 ? uncontrolled : controlled;
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
if (controlled !== void 0) setUncontrolled(controlled);
|
|
229
|
+
}, [controlled]);
|
|
227
230
|
const setValue = useCallback((next) => {
|
|
228
231
|
if (controlled === void 0) setUncontrolled(next);
|
|
229
232
|
}, [controlled]);
|
|
@@ -340,7 +343,7 @@ function useStableLoaderWarning(propName, loader, levelId) {
|
|
|
340
343
|
changedAt: [],
|
|
341
344
|
warned: false
|
|
342
345
|
});
|
|
343
|
-
|
|
346
|
+
useEffect2(() => {
|
|
344
347
|
const state = seen.current;
|
|
345
348
|
if (loader === state.loader) return;
|
|
346
349
|
state.loader = loader;
|
|
@@ -407,7 +410,7 @@ function IndiaChoropleth({
|
|
|
407
410
|
const [loadedSubDistricts, setLoadedSubDistricts] = useState2(null);
|
|
408
411
|
const [leafDistrictIds, setLeafDistrictIds] = useState2(() => /* @__PURE__ */ new Set());
|
|
409
412
|
const priorSubDistrictLoader = useRef(loadSubDistricts);
|
|
410
|
-
|
|
413
|
+
useEffect2(() => {
|
|
411
414
|
if (priorSubDistrictLoader.current === loadSubDistricts) return;
|
|
412
415
|
priorSubDistrictLoader.current = loadSubDistricts;
|
|
413
416
|
setLeafDistrictIds(/* @__PURE__ */ new Set());
|
|
@@ -419,6 +422,8 @@ function IndiaChoropleth({
|
|
|
419
422
|
const [subLoadError, setSubLoadError] = useState2(null);
|
|
420
423
|
const pathRefs = useRef({});
|
|
421
424
|
const restoreFocusId = useRef(null);
|
|
425
|
+
const focusFirstFromLevel = useRef(null);
|
|
426
|
+
const emptyLevelRef = useRef(null);
|
|
422
427
|
const stateCollection = useMemo(() => asFeatureCollection(states.geometry), [states.geometry]);
|
|
423
428
|
const referenceCollection = useMemo(
|
|
424
429
|
() => referenceOverlay ? asFeatureCollection(referenceOverlay.geometry) : null,
|
|
@@ -505,7 +510,7 @@ function IndiaChoropleth({
|
|
|
505
510
|
const [activeBucket, setActiveBucket] = useState2(null);
|
|
506
511
|
const filterBucket = activeBucket !== null && activeBucket < buckets.length ? activeBucket : null;
|
|
507
512
|
const colorScaleKey = typeof colorScale === "function" ? "function" : colorScale.join(",");
|
|
508
|
-
|
|
513
|
+
useEffect2(() => {
|
|
509
514
|
setActiveBucket(null);
|
|
510
515
|
}, [activeDrillDownId, activeSubDrillDownId, colorScaleKey, level]);
|
|
511
516
|
const highlighted = useMemo(
|
|
@@ -513,7 +518,7 @@ function IndiaChoropleth({
|
|
|
513
518
|
[filterBucket, legendColors.length, max, min, regions]
|
|
514
519
|
);
|
|
515
520
|
const isDimmed = (id) => highlighted !== null && !highlighted.has(id);
|
|
516
|
-
|
|
521
|
+
useEffect2(() => {
|
|
517
522
|
let cancelled = false;
|
|
518
523
|
if (!activeDrillDownId || !loadDistricts) {
|
|
519
524
|
setLoadedDistricts(null);
|
|
@@ -537,7 +542,7 @@ function IndiaChoropleth({
|
|
|
537
542
|
cancelled = true;
|
|
538
543
|
};
|
|
539
544
|
}, [activeDrillDownId, loadDistricts, stateCollection, states.getId]);
|
|
540
|
-
|
|
545
|
+
useEffect2(() => {
|
|
541
546
|
let cancelled = false;
|
|
542
547
|
if (!activeDrillDownId || !loadDistrictReferenceOverlay) {
|
|
543
548
|
setLoadedDistrictReferenceOverlay(null);
|
|
@@ -555,7 +560,7 @@ function IndiaChoropleth({
|
|
|
555
560
|
cancelled = true;
|
|
556
561
|
};
|
|
557
562
|
}, [activeDrillDownId, loadDistrictReferenceOverlay, stateCollection, states.getId]);
|
|
558
|
-
|
|
563
|
+
useEffect2(() => {
|
|
559
564
|
let cancelled = false;
|
|
560
565
|
if (!activeSubDrillDownId || !loadSubDistricts) {
|
|
561
566
|
setLoadedSubDistricts(null);
|
|
@@ -573,6 +578,8 @@ function IndiaChoropleth({
|
|
|
573
578
|
if (!loaded) {
|
|
574
579
|
setLeafDistrictIds((known) => known.has(activeSubDrillDownId) ? known : new Set(known).add(activeSubDrillDownId));
|
|
575
580
|
setActiveSubDrillDownId(null);
|
|
581
|
+
restoreFocusId.current = sourceDistrict.id;
|
|
582
|
+
focusFirstFromLevel.current = null;
|
|
576
583
|
setActiveSelectedId(sourceDistrict.id);
|
|
577
584
|
onSubDistrictDrillDownChange?.(null, sourceDistrict);
|
|
578
585
|
return;
|
|
@@ -588,19 +595,37 @@ function IndiaChoropleth({
|
|
|
588
595
|
};
|
|
589
596
|
}, [activeDrillDownId, activeSubDrillDownId, districtCollection, districtLayer?.getId, loadSubDistricts]);
|
|
590
597
|
const priorDrillDownId = useRef(activeDrillDownId);
|
|
591
|
-
|
|
598
|
+
const activeSubDrillDownIdRef = useRef(activeSubDrillDownId);
|
|
599
|
+
activeSubDrillDownIdRef.current = activeSubDrillDownId;
|
|
600
|
+
useEffect2(() => {
|
|
592
601
|
if (priorDrillDownId.current === activeDrillDownId) return;
|
|
593
602
|
priorDrillDownId.current = activeDrillDownId;
|
|
603
|
+
if (activeSubDrillDownIdRef.current === null) return;
|
|
594
604
|
setActiveSubDrillDownId(null);
|
|
605
|
+
onSubDistrictDrillDownChange?.(null, void 0);
|
|
595
606
|
}, [activeDrillDownId]);
|
|
596
|
-
|
|
607
|
+
useEffect2(() => {
|
|
597
608
|
const regionId = restoreFocusId.current;
|
|
598
|
-
if (
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
609
|
+
if (regionId) {
|
|
610
|
+
if (!regions.some((region) => region.id === regionId)) return;
|
|
611
|
+
restoreFocusId.current = null;
|
|
612
|
+
focusFirstFromLevel.current = null;
|
|
613
|
+
pathRefs.current[regionId]?.focus();
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
if (!focusFirstFromLevel.current || focusFirstFromLevel.current === level) return;
|
|
617
|
+
const first = regions[0];
|
|
618
|
+
if (first) {
|
|
619
|
+
focusFirstFromLevel.current = null;
|
|
620
|
+
pathRefs.current[first.id]?.focus();
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
if (emptyLevelRef.current) {
|
|
624
|
+
focusFirstFromLevel.current = null;
|
|
625
|
+
emptyLevelRef.current.focus();
|
|
626
|
+
}
|
|
627
|
+
}, [level, regions, loadingState, loadingDistrict]);
|
|
628
|
+
useEffect2(() => {
|
|
604
629
|
if (!inspectedId) return;
|
|
605
630
|
if (!regions.some((region) => region.id === inspectedId)) {
|
|
606
631
|
inspectedIdRef.current = null;
|
|
@@ -620,17 +645,20 @@ function IndiaChoropleth({
|
|
|
620
645
|
onSelectedChange?.(region, level);
|
|
621
646
|
if (level === "state" && loadDistricts) {
|
|
622
647
|
setActiveSelectedId(null);
|
|
648
|
+
focusFirstFromLevel.current = level;
|
|
623
649
|
setActiveDrillDownId(region.id);
|
|
624
650
|
onDrillDownChange?.(region.id, region);
|
|
625
651
|
return;
|
|
626
652
|
}
|
|
627
653
|
if (level === "district" && loadSubDistricts && !leafDistrictIds.has(region.id)) {
|
|
628
654
|
setActiveSelectedId(null);
|
|
655
|
+
focusFirstFromLevel.current = level;
|
|
629
656
|
setActiveSubDrillDownId(region.id);
|
|
630
657
|
onSubDistrictDrillDownChange?.(region.id, region);
|
|
631
658
|
}
|
|
632
659
|
};
|
|
633
660
|
const goBack = () => {
|
|
661
|
+
focusFirstFromLevel.current = null;
|
|
634
662
|
if (level === "subdistrict") {
|
|
635
663
|
const priorDistrict = drilledDistrict ?? void 0;
|
|
636
664
|
setActiveSubDrillDownId(null);
|
|
@@ -651,6 +679,7 @@ function IndiaChoropleth({
|
|
|
651
679
|
onDrillDownChange?.(null, priorState);
|
|
652
680
|
};
|
|
653
681
|
const goToStates = () => {
|
|
682
|
+
focusFirstFromLevel.current = null;
|
|
654
683
|
const priorState = drilledState ?? void 0;
|
|
655
684
|
const wasDrilledDistrict = drilledDistrict ?? void 0;
|
|
656
685
|
setActiveSubDrillDownId(null);
|
|
@@ -769,7 +798,7 @@ function IndiaChoropleth({
|
|
|
769
798
|
const regionCanDrill = (id) => canDrill && !(level === "district" && leafDistrictIds.has(id));
|
|
770
799
|
const visibleReferenceRegions = level === "state" ? referenceRegions : districtReferenceRegions;
|
|
771
800
|
const mergedReferenceIds = useMemo(() => new Set(referenceOverlayMergeIds), [referenceOverlayMergeIds]);
|
|
772
|
-
|
|
801
|
+
useEffect2(() => {
|
|
773
802
|
onInsight?.(insightContext);
|
|
774
803
|
}, [insightContext, onInsight]);
|
|
775
804
|
useLayoutEffect(() => {
|
|
@@ -798,7 +827,7 @@ function IndiaChoropleth({
|
|
|
798
827
|
] }) : null
|
|
799
828
|
] }) }) : null,
|
|
800
829
|
/* @__PURE__ */ jsxs("div", { ref: canvasRef, className: "india-choropleth__canvas", onMouseLeave: interactive ? () => inspect(null) : void 0, children: [
|
|
801
|
-
isSubDrillRequested && (!subDistrictLayer || subLoadError) ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: subLoadError ? "alert" : "status", children: subLoadError ? subLoadError.message : loadingDistrict ? "Loading sub-districts\u2026" : "Sub-district data is unavailable for this district." }) : isDrillRequested && (!districtLayer || loadError) ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: loadError ? "alert" : "status", children: loadError ? loadError.message : loadingState ? "Loading districts\u2026" : "District data is unavailable for this state." }) : regions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: "status", children: level === "subdistrict" ? "No sub-district data is available for this district." : "No district data is available for this state." }) : /* @__PURE__ */ jsxs(
|
|
830
|
+
isSubDrillRequested && (!subDistrictLayer || subLoadError) ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: subLoadError ? "alert" : "status", children: subLoadError ? subLoadError.message : loadingDistrict ? "Loading sub-districts\u2026" : "Sub-district data is unavailable for this district." }) : isDrillRequested && (!districtLayer || loadError) ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: loadError ? "alert" : "status", children: loadError ? loadError.message : loadingState ? "Loading districts\u2026" : "District data is unavailable for this state." }) : regions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "india-choropleth__status", role: "status", tabIndex: -1, ref: emptyLevelRef, children: level === "subdistrict" ? "No sub-district data is available for this district." : "No district data is available for this state." }) : /* @__PURE__ */ jsxs(
|
|
802
831
|
"svg",
|
|
803
832
|
{
|
|
804
833
|
className: `india-choropleth__svg${filterBucket !== null ? " india-choropleth__svg--filtered" : ""}`,
|
|
@@ -976,7 +1005,7 @@ function IndiaChoropleth({
|
|
|
976
1005
|
}
|
|
977
1006
|
|
|
978
1007
|
// src/BharatChoropleth.tsx
|
|
979
|
-
import { useCallback as useCallback3, useEffect as
|
|
1008
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
|
|
980
1009
|
|
|
981
1010
|
// src/data-source.ts
|
|
982
1011
|
var DEFAULT_DATA_BASE_URL = "https://cdn.jsdelivr.net/gh/shashankbudem/bharat-choropleth@v0.3.0/data/generated";
|
|
@@ -1180,17 +1209,19 @@ function BharatChoropleth({
|
|
|
1180
1209
|
if (data) return data.map((row) => [String(row[regionKey] ?? ""), toValue(row[valueKey])]);
|
|
1181
1210
|
return [];
|
|
1182
1211
|
}, [data, regionKey, valueKey, values]);
|
|
1183
|
-
const { valueMap, exactKeys, writtenAs } = useMemo2(() => {
|
|
1212
|
+
const { valueMap, exactKeys, writtenAs, duplicated } = useMemo2(() => {
|
|
1184
1213
|
const valueMap2 = /* @__PURE__ */ new Map();
|
|
1185
1214
|
const exactKeys2 = /* @__PURE__ */ new Map();
|
|
1186
1215
|
const writtenAs2 = /* @__PURE__ */ new Map();
|
|
1216
|
+
const duplicated2 = /* @__PURE__ */ new Map();
|
|
1187
1217
|
for (const [name, value] of entries) {
|
|
1188
1218
|
const key = keyFor(name);
|
|
1219
|
+
if (valueMap2.has(key)) duplicated2.set(key, (duplicated2.get(key) ?? 1) + 1);
|
|
1189
1220
|
valueMap2.set(key, value);
|
|
1190
1221
|
exactKeys2.set(name, key);
|
|
1191
1222
|
if (!writtenAs2.has(key)) writtenAs2.set(key, name);
|
|
1192
1223
|
}
|
|
1193
|
-
return { valueMap: valueMap2, exactKeys: exactKeys2, writtenAs: writtenAs2 };
|
|
1224
|
+
return { valueMap: valueMap2, exactKeys: exactKeys2, writtenAs: writtenAs2, duplicated: duplicated2 };
|
|
1194
1225
|
}, [entries]);
|
|
1195
1226
|
const districtValueMap = useMemo2(() => {
|
|
1196
1227
|
const byState = /* @__PURE__ */ new Map();
|
|
@@ -1226,7 +1257,7 @@ function BharatChoropleth({
|
|
|
1226
1257
|
const [error, setError] = useState3(null);
|
|
1227
1258
|
const onErrorRef = useRef2(onError);
|
|
1228
1259
|
onErrorRef.current = onError;
|
|
1229
|
-
|
|
1260
|
+
useEffect3(() => {
|
|
1230
1261
|
if (isInlineGeometry(source)) return;
|
|
1231
1262
|
const controller = typeof AbortController === "function" ? new AbortController() : null;
|
|
1232
1263
|
let cancelled = false;
|
|
@@ -1259,7 +1290,7 @@ function BharatChoropleth({
|
|
|
1259
1290
|
};
|
|
1260
1291
|
}, [exactKeys, getId, getLabel, resolvedGeometry, valueSignature]);
|
|
1261
1292
|
const warned = useRef2(/* @__PURE__ */ new Set());
|
|
1262
|
-
|
|
1293
|
+
useEffect3(() => {
|
|
1263
1294
|
if (!resolvedGeometry || !statesLayer) return;
|
|
1264
1295
|
const known = new Set(
|
|
1265
1296
|
asFeatureCollection(resolvedGeometry).features.map(
|
|
@@ -1273,7 +1304,15 @@ function BharatChoropleth({
|
|
|
1273
1304
|
`BharatChoropleth: "${writtenAs.get(key) ?? key}" is not a recognized state/UT \u2014 its value is ignored.`
|
|
1274
1305
|
);
|
|
1275
1306
|
}
|
|
1276
|
-
|
|
1307
|
+
for (const [key, count] of duplicated) {
|
|
1308
|
+
const seen = `duplicate:${key}`;
|
|
1309
|
+
if (warned.current.has(seen)) continue;
|
|
1310
|
+
warned.current.add(seen);
|
|
1311
|
+
console.warn(
|
|
1312
|
+
`BharatChoropleth: "${writtenAs.get(key) ?? key}" appears in ${count} rows \u2014 the last one is shown. Aggregate it in the query if that is not what you meant.`
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
}, [duplicated, getId, getLabel, resolvedGeometry, statesLayer, valueSignature, writtenAs]);
|
|
1277
1316
|
const drillDownRef = useRef2(null);
|
|
1278
1317
|
drillDownRef.current ??= {
|
|
1279
1318
|
controller: typeof AbortController === "function" ? new AbortController() : null,
|
|
@@ -1281,7 +1320,7 @@ function BharatChoropleth({
|
|
|
1281
1320
|
subDistricts: /* @__PURE__ */ new Map()
|
|
1282
1321
|
};
|
|
1283
1322
|
const drillDown = drillDownRef.current;
|
|
1284
|
-
|
|
1323
|
+
useEffect3(() => () => drillDownRef.current?.controller?.abort(), []);
|
|
1285
1324
|
const warnedDistricts = useRef2(/* @__PURE__ */ new Set());
|
|
1286
1325
|
const withDistrictValues = useCallback3((layer, stateId) => {
|
|
1287
1326
|
const wanted = districtValuesRef.current.get(stateId);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/IndiaChoropleth.tsx","../src/geometry.ts","../src/legend.ts","../src/tooltip-position.ts","../src/small-regions.ts","../src/useControllableState.ts","../src/BharatChoropleth.tsx","../src/data-source.ts","../src/states.ts"],"sourcesContent":["import { geoMercator, geoPath, type GeoProjection } from \"d3-geo\";\nimport { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type MouseEvent as ReactMouseEvent } from \"react\";\nimport { asFeatureCollection, totalOf } from \"./geometry\";\nimport { legendBucketLabel, legendBuckets, swatchIndexOf } from \"./legend\";\nimport { placeTooltip } from \"./tooltip-position\";\nimport {\n boundsOfRing,\n distanceToParts,\n keepsTrueGeometry,\n labelPointFor,\n enlargeSmallParts,\n largestRingExtent,\n placeOutsideLabel,\n ringsToPath,\n scatteredHitArea,\n type Box,\n type Point,\n} from \"./small-regions\";\nimport type {\n ColorContext,\n ColorScale,\n IndiaChoroplethProps,\n InsightContext,\n MapFeature,\n MapFeatureCollection,\n MapLayer,\n MapLevel,\n MapRegion,\n ReferenceOverlay,\n TooltipContext,\n} from \"./types\";\nimport { useControllableState } from \"./useControllableState\";\n\nconst VIEWBOX = { width: 960, height: 640, padding: 28 };\nconst DEFAULT_COLORS = [\"#d9f1ed\", \"#b9e3dd\", \"#8fd1c8\", \"#5bb9ae\", \"#2f9c90\", \"#147b71\", \"#075b55\"] as const;\nconst DEFAULT_FORMAT = new Intl.NumberFormat(\"en-IN\").format;\n// Space between the region centroid and the tooltip edge. Mirrors the .75rem in style.css.\nconst TOOLTIP_GAP_PX = 12;\n// Small-region handling, in view-box units. Mirrors the DOM and Dart renderers.\nconst SMALL_REGION_EXTENT = 22;\nconst SMALL_REGION_CLICK_RADIUS = 14;\nconst MIN_REGION_MARKER_SIZE = 7;\n\ntype PreparedRegion = MapRegion & {\n path: string;\n /**\n * Hull covering a scattered region's parts and the space between them, drawn\n * invisibly under every outline so hovering the water inside Lakshadweep\n * reaches Lakshadweep. Null for regions that are one part or big enough to\n * point at directly.\n */\n hitPath: string | null;\n centroid: [number, number];\n /** Bounding box of each separate part, for measuring how close a click landed. */\n partBounds: Box[];\n /** Longest side of the largest part — the measure of \"too small to use\". */\n extent: number;\n};\n\n/**\n * Project a feature's rings into view-box coordinates. Needed alongside the SVG\n * path string because the small-region helpers work on coordinates, and a path\n * string cannot be measured per part.\n */\nfunction projectedRings(feature: MapFeature, projection: GeoProjection): Point[][] {\n const geometry = feature.geometry;\n if (!geometry) return [];\n const polygons =\n geometry.type === \"Polygon\" ? [geometry.coordinates]\n : geometry.type === \"MultiPolygon\" ? geometry.coordinates\n : [];\n const rings: Point[][] = [];\n for (const polygon of polygons) {\n for (const ring of polygon) {\n const projected: Point[] = [];\n for (const position of ring) {\n const point = projection(position as [number, number]);\n if (point && Number.isFinite(point[0]) && Number.isFinite(point[1])) projected.push([point[0], point[1]]);\n }\n if (projected.length > 0) rings.push(projected);\n }\n }\n return rings;\n}\ntype PreparedReferenceOverlay = { id: string; label: string; description: string; path: string };\n\nfunction colorFor(value: number | null, region: MapRegion, min: number, max: number, scale: ColorScale): string {\n if (typeof scale === \"function\") {\n const context: ColorContext = { min, max, feature: region.feature, id: region.id };\n return scale(value, context);\n }\n // The same index the legend filters by, deliberately: \"highlight the regions\n // painted in this colour\" has to be true by construction, not by two formulas\n // that happen to agree until one of them is tweaked.\n const index = swatchIndexOf(value, min, max, scale.length);\n return index === null ? \"var(--india-map-empty)\" : scale[index] ?? \"var(--india-map-empty)\";\n}\n\nfunction makeProjection(collection: MapFeatureCollection): GeoProjection {\n return geoMercator().fitExtent(\n [[VIEWBOX.padding, VIEWBOX.padding], [VIEWBOX.width - VIEWBOX.padding, VIEWBOX.height - VIEWBOX.padding]],\n collection,\n );\n}\n\n/**\n * `collection` lets a caller that has already unpacked this layer's geometry\n * hand the features straight in. Preparing a layer is how values reach the\n * screen, so it re-runs on every value change — unpacking the same topology\n * again each time is work whose answer cannot have changed.\n */\nfunction prepareLayer(\n layer: MapLayer,\n projection = makeProjection(asFeatureCollection(layer.geometry)),\n minPartExtent = 0,\n collection: MapFeatureCollection = asFeatureCollection(layer.geometry),\n): PreparedRegion[] {\n const path = geoPath(projection);\n return collection.features.map((feature) => {\n const centroid = path.centroid(feature) as [number, number];\n const bounds = path.bounds(feature);\n const fallbackCentroid: [number, number] = [\n (bounds[0][0] + bounds[1][0]) / 2,\n (bounds[0][1] + bounds[1][1]) / 2,\n ];\n const region: MapRegion = {\n id: layer.getId(feature),\n label: layer.getLabel(feature),\n value: layer.getValue(feature),\n meta: layer.getMeta?.(feature),\n feature,\n };\n // Puducherry and anything else on the keep-true list is drawn as it really\n // is, however small, because there is no room around it to grow into.\n const exaggerate = minPartExtent > 0 && !keepsTrueGeometry(region.id);\n const rings = exaggerate\n ? enlargeSmallParts(projectedRings(feature, projection), minPartExtent)\n : projectedRings(feature, projection);\n const fallback: [number, number] = centroid.every(Number.isFinite) ? centroid : fallbackCentroid;\n const hull = scatteredHitArea(rings, SMALL_REGION_EXTENT);\n // With exaggeration on, the drawn outline has to come from the moved rings\n // rather than d3's path generator, so what is drawn, measured, labelled and\n // clicked are all the same geometry.\n return {\n ...region,\n path: exaggerate ? ringsToPath(rings) : (path(feature) ?? \"\"),\n hitPath: hull ? ringsToPath([hull]) : null,\n // The largest part's centroid, not the whole feature's: averaging across\n // parts puts an island group's label out at sea between its islands.\n centroid: rings.length > 0 ? (labelPointFor(rings, fallback) as [number, number]) : fallback,\n partBounds: rings.map(boundsOfRing),\n extent: largestRingExtent(rings),\n };\n });\n}\n\nfunction prepareReferenceOverlay(overlay: ReferenceOverlay, projection: GeoProjection): PreparedReferenceOverlay[] {\n const path = geoPath(projection);\n return asFeatureCollection(overlay.geometry).features.map((feature) => ({\n id: overlay.getId(feature),\n label: overlay.getLabel(feature),\n description: overlay.getDescription(feature),\n path: path(feature) ?? \"\",\n }));\n}\n\nfunction ordinal(n: number): string {\n const lastTwo = n % 100;\n if (lastTwo >= 11 && lastTwo <= 13) return `${n}th`;\n return `${n}${[\"th\", \"st\", \"nd\", \"rd\"][n % 10] ?? \"th\"}`;\n}\n\nfunction defaultTooltip(context: TooltipContext, formatValue: (value: number) => string) {\n return (\n <>\n <strong>{context.label}</strong>\n {/* Same wording as the region's own aria-label, so the two never disagree. */}\n <b>{context.value === null ? \"No data\" : formatValue(context.value)}</b>\n {context.share !== null ? (\n <>\n {/* A bar makes the share readable at a glance; it repeats the number\n beside it, so it's decorative and hidden from assistive tech. */}\n <span className=\"india-choropleth__tooltip-bar\" aria-hidden=\"true\">\n <span style={{ width: `${Math.max(context.share, 1.5)}%` }} />\n </span>\n <small>\n {[\n `${context.share.toFixed(1)}% of total`,\n ...(context.rank !== null ? [`${ordinal(context.rank)} of ${context.rankedCount}`] : []),\n ].join(\" · \")}\n </small>\n </>\n ) : null}\n </>\n );\n}\n\n/**\n * A data-agnostic, accessible SVG India map renderer. Import `@india-choropleth/react/style.css`\n * once in the host app; data and boundaries intentionally remain separate.\n */\n/**\n * Warns when a lazy loader is being recreated on every render.\n *\n * The loading effects list their loader in their dependencies because a\n * genuinely different loader — a different boundary edition, a different\n * reporting year — must refetch. An inline arrow is also a new function every\n * render, and from in here the two look identical: in both cases the loader is\n * the only dependency that moved.\n *\n * What separates them is *density*. An inline arrow changes on consecutive\n * renders, because every render makes one. A memoized loader whose dependency\n * changed — a dashboard swapping the displayed metric — changes once, then not\n * again until the reader does something, which is many renders later. So the\n * test is three changes inside a short window of renders, not three changes.\n *\n * That distinction is the whole value of the check: this repo's own demo swaps a\n * correctly-memoized loader whenever its metric changes, and a warning that\n * fired on that would be noise, and would train people to ignore it.\n *\n * The cost of the real mistake is invisible — the level silently refetches over\n * the network on every unrelated re-render while rendering perfectly correctly —\n * which is why it needs saying at all.\n */\nconst LOADER_CHURN_CHANGES = 3;\n/** Renders those changes must fall within. Generous, because StrictMode renders twice. */\nconst LOADER_CHURN_WINDOW = 6;\n\nfunction useStableLoaderWarning(propName: string, loader: unknown, levelId: string | null) {\n const renders = useRef(0);\n renders.current += 1;\n const seen = useRef<{ loader: unknown; levelId: string | null; changedAt: number[]; warned: boolean }>({\n loader,\n levelId,\n changedAt: [],\n warned: false,\n });\n useEffect(() => {\n const state = seen.current;\n if (loader === state.loader) return;\n state.loader = loader;\n // A different level is a different question; start counting again.\n if (levelId !== state.levelId) {\n state.levelId = levelId;\n state.changedAt = [];\n return;\n }\n state.changedAt = [...state.changedAt, renders.current].slice(-LOADER_CHURN_CHANGES);\n const [first] = state.changedAt;\n const dense =\n state.changedAt.length === LOADER_CHURN_CHANGES && renders.current - (first ?? 0) <= LOADER_CHURN_WINDOW;\n if (loader && dense && !state.warned) {\n state.warned = true;\n console.warn(\n `IndiaChoropleth: \\`${propName}\\` has been a different function on ${LOADER_CHURN_CHANGES} renders in a row ` +\n \"while the level it loads stayed the same, so that level has been fetched again each time. Wrap it in \" +\n \"`useCallback` or hoist it out of the component — an inline arrow is a new function on every render, and the \" +\n \"renderer cannot tell that apart from a deliberately different loader.\",\n );\n }\n }, [levelId, loader, propName]);\n}\n\nexport function IndiaChoropleth({\n states,\n referenceOverlay,\n loadDistricts,\n loadSubDistricts,\n loadDistrictReferenceOverlay,\n drillDownId,\n defaultDrillDownId = null,\n onDrillDownChange,\n subDistrictDrillDownId,\n defaultSubDistrictDrillDownId = null,\n onSubDistrictDrillDownChange,\n selectedId,\n defaultSelectedId = null,\n onSelectedChange,\n onInspect,\n onInsight,\n onRegionClick,\n onBackgroundClick,\n colorScale = DEFAULT_COLORS,\n formatValue = DEFAULT_FORMAT,\n renderTooltip,\n renderInsights,\n showLegend = true,\n showBreadcrumb = true,\n legendLabels = [\"Lower\", \"Higher\"],\n referenceOverlayLegendLabel = \"Reference context · data unavailable\",\n referenceOverlayMergeIds = [],\n referenceOverlayFill = \"hatch\",\n showRegionValues = false,\n minPartExtent = 0,\n minDistrictPartExtent,\n className,\n ariaLabel = \"Interactive choropleth map\",\n interactive = true,\n}: IndiaChoroplethProps) {\n const tooltipId = useId();\n const canvasRef = useRef<HTMLDivElement | null>(null);\n const tooltipAnchorRef = useRef<HTMLDivElement | null>(null);\n const hatchId = `${useId()}-reference-hatch`;\n const [activeDrillDownId, setActiveDrillDownId] = useControllableState(drillDownId, defaultDrillDownId);\n const [activeSubDrillDownId, setActiveSubDrillDownId] = useControllableState(subDistrictDrillDownId, defaultSubDistrictDrillDownId);\n const [activeSelectedId, setActiveSelectedId] = useControllableState(selectedId, defaultSelectedId);\n const [loadedDistricts, setLoadedDistricts] = useState<{ stateId: string; layer: MapLayer } | null>(null);\n const [loadedSubDistricts, setLoadedSubDistricts] = useState<{ districtId: string; layer: MapLayer } | null>(null);\n /**\n * Districts the loader has already answered `null` for. The renderer cannot know\n * which districts are leaves without asking, so the first activation asks — but\n * after that the region should stop announcing a level it will not open. Held as\n * state rather than a ref so learning it repaints the label, and cleared when the\n * loader changes, since a different source may well have sub-districts for them.\n */\n const [leafDistrictIds, setLeafDistrictIds] = useState<ReadonlySet<string>>(() => new Set());\n const priorSubDistrictLoader = useRef(loadSubDistricts);\n useEffect(() => {\n if (priorSubDistrictLoader.current === loadSubDistricts) return;\n priorSubDistrictLoader.current = loadSubDistricts;\n setLeafDistrictIds(new Set());\n }, [loadSubDistricts]);\n const [loadedDistrictReferenceOverlay, setLoadedDistrictReferenceOverlay] = useState<{ stateId: string; overlay: ReferenceOverlay | null } | null>(null);\n const [loadingState, setLoadingState] = useState<string | null>(null);\n const [loadingDistrict, setLoadingDistrict] = useState<string | null>(null);\n const [loadError, setLoadError] = useState<Error | null>(null);\n const [subLoadError, setSubLoadError] = useState<Error | null>(null);\n const pathRefs = useRef<Record<string, SVGPathElement | null>>({});\n const restoreFocusId = useRef<string | null>(null);\n\n // Keyed on the geometry, not the layer: a caller that repaints by handing over\n // a new `MapLayer` with the same geometry — which is how values change — gets\n // the same decoded features and the same projection back, instead of paying to\n // unpack the topology and refit the projection for numbers that moved.\n const stateCollection = useMemo(() => asFeatureCollection(states.geometry), [states.geometry]);\n const referenceCollection = useMemo(\n () => referenceOverlay ? asFeatureCollection(referenceOverlay.geometry) : null,\n [referenceOverlay],\n );\n const nationalProjection = useMemo(\n () => makeProjection({ type: \"FeatureCollection\", features: [...stateCollection.features, ...(referenceCollection?.features ?? [])] }),\n [referenceCollection, stateCollection],\n );\n const stateRegions = useMemo(\n () => prepareLayer(states, nationalProjection, minPartExtent, stateCollection),\n [minPartExtent, nationalProjection, stateCollection, states],\n );\n const referenceRegions = useMemo(\n () => referenceOverlay ? prepareReferenceOverlay(referenceOverlay, nationalProjection) : [],\n [nationalProjection, referenceOverlay],\n );\n const drilledState = useMemo(\n () => stateRegions.find((region) => region.id === activeDrillDownId) ?? null,\n [activeDrillDownId, stateRegions],\n );\n /**\n * Prepared regions bake in each region's value, so they are a new array\n * whenever any number changes. The lazy-loading effects below need the current\n * region to hand to a loader, but must not re-run just because a value moved:\n * re-running calls the loader again and clears the level while the promise is\n * in flight, so a map whose data updates on a timer would blink its districts\n * away on every tick. They read regions through these refs and depend on the\n * geometry and id accessor instead — the things that actually decide which\n * regions exist and what they are called.\n */\n const stateRegionsRef = useRef(stateRegions);\n stateRegionsRef.current = stateRegions;\n useStableLoaderWarning(\"loadDistricts\", loadDistricts, activeDrillDownId);\n useStableLoaderWarning(\"loadSubDistricts\", loadSubDistricts, activeSubDrillDownId);\n const isDrillRequested = Boolean(drilledState && activeDrillDownId);\n const districtLayer = loadedDistricts?.stateId === activeDrillDownId ? loadedDistricts.layer : null;\n const districtReferenceOverlay = loadedDistrictReferenceOverlay?.stateId === activeDrillDownId\n ? loadedDistrictReferenceOverlay.overlay\n : null;\n const districtCollection = useMemo(\n () => districtLayer ? asFeatureCollection(districtLayer.geometry) : null,\n [districtLayer],\n );\n const districtReferenceCollection = useMemo(\n () => districtReferenceOverlay ? asFeatureCollection(districtReferenceOverlay.geometry) : null,\n [districtReferenceOverlay],\n );\n const districtProjection = useMemo(\n () => districtCollection ? makeProjection({ type: \"FeatureCollection\", features: [...districtCollection.features, ...(districtReferenceCollection?.features ?? [])] }) : null,\n [districtCollection, districtReferenceCollection],\n );\n // Districts are prepared whenever their layer is loaded rather than only while\n // they are the visible level, because the district below them has to be\n // resolvable — by id, for the breadcrumb and for the loader — from one level down.\n const districtRegions = useMemo(\n () => districtLayer && districtProjection\n ? prepareLayer(districtLayer, districtProjection, minDistrictPartExtent ?? minPartExtent, districtCollection ?? undefined)\n : [],\n [districtCollection, districtLayer, districtProjection, minDistrictPartExtent, minPartExtent],\n );\n const drilledDistrict = useMemo(\n () => districtRegions.find((region) => region.id === activeSubDrillDownId) ?? null,\n [activeSubDrillDownId, districtRegions],\n );\n // As above, one level down.\n const districtRegionsRef = useRef(districtRegions);\n districtRegionsRef.current = districtRegions;\n const isSubDrillRequested = Boolean(isDrillRequested && drilledDistrict && activeSubDrillDownId);\n const level: MapLevel = isSubDrillRequested ? \"subdistrict\" : isDrillRequested ? \"district\" : \"state\";\n\n const subDistrictLayer = loadedSubDistricts?.districtId === activeSubDrillDownId ? loadedSubDistricts.layer : null;\n const subDistrictCollection = useMemo(\n () => subDistrictLayer ? asFeatureCollection(subDistrictLayer.geometry) : null,\n [subDistrictLayer],\n );\n const subDistrictProjection = useMemo(\n () => subDistrictCollection ? makeProjection(subDistrictCollection) : null,\n [subDistrictCollection],\n );\n // Sub-districts share the district knob rather than adding a fourth: they are\n // drawn at the same zoom as districts and want the same small-part treatment.\n const subDistrictRegions = useMemo(\n () => subDistrictLayer && subDistrictProjection\n ? prepareLayer(subDistrictLayer, subDistrictProjection, minDistrictPartExtent ?? minPartExtent)\n : [],\n [minDistrictPartExtent, minPartExtent, subDistrictLayer, subDistrictProjection],\n );\n\n const regions = level === \"subdistrict\" ? subDistrictRegions : level === \"district\" ? districtRegions : stateRegions;\n const districtReferenceRegions = useMemo(\n () => level === \"district\" && districtReferenceOverlay && districtProjection ? prepareReferenceOverlay(districtReferenceOverlay, districtProjection) : [],\n [districtProjection, districtReferenceOverlay, level],\n );\n const selected = regions.find((region) => region.id === activeSelectedId) ?? null;\n const [inspectedId, setInspectedId] = useState<string | null>(null);\n const inspectedIdRef = useRef<string | null>(null);\n // Hover/focus only — no fallback to `selected`, so the floating tooltip clears\n // when the pointer/focus leaves instead of sticking on the selected region.\n const inspected = regions.find((region) => region.id === inspectedId) ?? null;\n\n const values = useMemo(() => regions.map((region) => region.value).filter((value): value is number => value !== null), [regions]);\n const total = useMemo(() => totalOf(values), [values]);\n const min = values.length ? Math.min(...values) : 0;\n const max = values.length ? Math.max(...values) : 0;\n\n // The legend doubles as a filter. A function colour scale has no swatches of\n // its own, so the default ramp stands in and the bands still read low to high.\n const legendColors = typeof colorScale === \"function\" ? DEFAULT_COLORS : colorScale;\n const buckets = useMemo(\n () => legendBuckets(legendColors, regions.map((region) => region.value), min, max),\n [legendColors, max, min, regions],\n );\n const [activeBucket, setActiveBucket] = useState<number | null>(null);\n // Clamped here rather than only in the effect below: effects run after paint,\n // so a stale index would dim against the previous level's bands for a frame.\n const filterBucket = activeBucket !== null && activeBucket < buckets.length ? activeBucket : null;\n // Keyed on the ramp's contents, not its identity — a host passing an inline\n // array literal would otherwise clear the filter on every re-render.\n const colorScaleKey = typeof colorScale === \"function\" ? \"function\" : colorScale.join(\",\");\n // Bands come from this level's own min and max, and change with the ramp, so an\n // index picked under one of them means something else under another.\n useEffect(() => { setActiveBucket(null); }, [activeDrillDownId, activeSubDrillDownId, colorScaleKey, level]);\n\n const highlighted = useMemo(\n () => filterBucket === null\n ? null\n : new Set(regions\n .filter((region) => swatchIndexOf(region.value, min, max, legendColors.length) === filterBucket)\n .map((region) => region.id)),\n [filterBucket, legendColors.length, max, min, regions],\n );\n // Dimmed regions stay hoverable: the filter is about where the eye goes, and a\n // region you can see is a region whose number should still be reachable.\n const isDimmed = (id: string) => highlighted !== null && !highlighted.has(id);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeDrillDownId || !loadDistricts) {\n setLoadedDistricts(null);\n setLoadingState(null);\n setLoadError(null);\n return;\n }\n const sourceState = stateRegionsRef.current.find((region) => region.id === activeDrillDownId);\n if (!sourceState) return;\n setLoadingState(activeDrillDownId);\n setLoadError(null);\n // Only blank the level when it is a different one. A reload of the state\n // already showing — a swapped loader, say — should leave its districts up\n // until the replacement lands, rather than flashing \"Loading districts…\"\n // over a map the reader is looking at.\n setLoadedDistricts((current) => (current?.stateId === activeDrillDownId ? current : null));\n loadDistricts(activeDrillDownId, sourceState)\n .then((loaded) => { if (!cancelled) setLoadedDistricts({ stateId: activeDrillDownId, layer: loaded }); })\n .catch((error: unknown) => { if (!cancelled) setLoadError(error instanceof Error ? error : new Error(\"Unable to load districts.\")); })\n .finally(() => { if (!cancelled) setLoadingState(null); });\n return () => { cancelled = true; };\n }, [activeDrillDownId, loadDistricts, stateCollection, states.getId]);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeDrillDownId || !loadDistrictReferenceOverlay) {\n setLoadedDistrictReferenceOverlay(null);\n return;\n }\n const sourceState = stateRegionsRef.current.find((region) => region.id === activeDrillDownId);\n if (!sourceState) return;\n setLoadedDistrictReferenceOverlay(null);\n loadDistrictReferenceOverlay(activeDrillDownId, sourceState)\n .then((overlay) => { if (!cancelled) setLoadedDistrictReferenceOverlay({ stateId: activeDrillDownId, overlay }); })\n // Optional reference context must not prevent a usable district data view.\n .catch(() => { if (!cancelled) setLoadedDistrictReferenceOverlay({ stateId: activeDrillDownId, overlay: null }); });\n return () => { cancelled = true; };\n }, [activeDrillDownId, loadDistrictReferenceOverlay, stateCollection, states.getId]);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeSubDrillDownId || !loadSubDistricts) {\n setLoadedSubDistricts(null);\n setLoadingDistrict(null);\n setSubLoadError(null);\n return;\n }\n const sourceDistrict = districtRegionsRef.current.find((region) => region.id === activeSubDrillDownId);\n // The district layer has not arrived yet, so there is nothing to load from.\n // This effect re-runs once it does.\n if (!sourceDistrict || !activeDrillDownId) return;\n setLoadingDistrict(activeSubDrillDownId);\n setSubLoadError(null);\n // As above, one level down.\n setLoadedSubDistricts((current) => (current?.districtId === activeSubDrillDownId ? current : null));\n loadSubDistricts(activeSubDrillDownId, sourceDistrict, activeDrillDownId)\n .then((loaded) => {\n if (cancelled) return;\n if (!loaded) {\n // This district is a leaf. Step back to the district view and leave it\n // selected, rather than opening a level with nothing in it.\n setLeafDistrictIds((known) => known.has(activeSubDrillDownId) ? known : new Set(known).add(activeSubDrillDownId));\n setActiveSubDrillDownId(null);\n setActiveSelectedId(sourceDistrict.id);\n onSubDistrictDrillDownChange?.(null, sourceDistrict);\n return;\n }\n setLoadedSubDistricts({ districtId: activeSubDrillDownId, layer: loaded });\n })\n .catch((error: unknown) => { if (!cancelled) setSubLoadError(error instanceof Error ? error : new Error(\"Unable to load sub-districts.\")); })\n .finally(() => { if (!cancelled) setLoadingDistrict(null); });\n return () => { cancelled = true; };\n // `onSubDistrictDrillDownChange` and the setters are deliberately not\n // dependencies: a host passing an inline callback would otherwise refetch on\n // every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeDrillDownId, activeSubDrillDownId, districtCollection, districtLayer?.getId, loadSubDistricts]);\n\n // A district id only means something inside the state it came from, so leaving\n // or changing the state drops the level below it. Seeded with the mount-time\n // value so an initial state + district pair survives: this must fire on a\n // change, not on arrival.\n const priorDrillDownId = useRef(activeDrillDownId);\n useEffect(() => {\n if (priorDrillDownId.current === activeDrillDownId) return;\n priorDrillDownId.current = activeDrillDownId;\n setActiveSubDrillDownId(null);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeDrillDownId]);\n\n useEffect(() => {\n const regionId = restoreFocusId.current;\n if (!regionId) return;\n // Focus is restored onto the region that was just stepped out of, so it waits\n // until the level holding that region is the one being drawn.\n if (!regions.some((region) => region.id === regionId)) return;\n restoreFocusId.current = null;\n pathRefs.current[regionId]?.focus();\n }, [level, regions]);\n\n useEffect(() => {\n if (!inspectedId) return;\n if (!regions.some((region) => region.id === inspectedId)) {\n inspectedIdRef.current = null;\n setInspectedId(null);\n }\n }, [inspectedId, regions]);\n\n // Mirrors `inspectedId` synchronously so a single gesture that exits both a region\n // and the canvas doesn't report the clear twice. Deliberately limited to redundant\n // clears: re-inspecting the same region must still notify, because focus restoration\n // after breadcrumb-back re-inspects the region `goBack` already primed.\n const inspect = (region: PreparedRegion | null) => {\n const nextId = region?.id ?? null;\n if (nextId === null && inspectedIdRef.current === null) return;\n inspectedIdRef.current = nextId;\n setInspectedId(nextId);\n onInspect?.(region ?? null, level);\n };\n\n const activate = (region: PreparedRegion) => {\n onRegionClick?.(region, level);\n setActiveSelectedId(region.id);\n onSelectedChange?.(region, level);\n if (level === \"state\" && loadDistricts) {\n setActiveSelectedId(null);\n setActiveDrillDownId(region.id);\n onDrillDownChange?.(region.id, region);\n return;\n }\n // A district is a leaf unless the host offers a level below it. Whether this\n // particular district actually has one is only known once the loader answers,\n // so the drill is entered optimistically and stepped back out if it returns null.\n if (level === \"district\" && loadSubDistricts && !leafDistrictIds.has(region.id)) {\n setActiveSelectedId(null);\n setActiveSubDrillDownId(region.id);\n onSubDistrictDrillDownChange?.(region.id, region);\n }\n };\n\n /** Steps up exactly one level, so the breadcrumb and the back button agree. */\n const goBack = () => {\n if (level === \"subdistrict\") {\n const priorDistrict = drilledDistrict ?? undefined;\n setActiveSubDrillDownId(null);\n setActiveSelectedId(priorDistrict?.id ?? null);\n inspectedIdRef.current = priorDistrict?.id ?? null;\n setInspectedId(priorDistrict?.id ?? null);\n restoreFocusId.current = priorDistrict?.id ?? null;\n onSubDistrictDrillDownChange?.(null, priorDistrict);\n return;\n }\n const priorState = drilledState ?? undefined;\n setActiveDrillDownId(null);\n setActiveSubDrillDownId(null);\n setActiveSelectedId(priorState?.id ?? null);\n inspectedIdRef.current = priorState?.id ?? null;\n setInspectedId(priorState?.id ?? null);\n restoreFocusId.current = priorState?.id ?? null;\n onDrillDownChange?.(null, priorState);\n };\n\n /** Jumps straight to the national map from any level. */\n const goToStates = () => {\n const priorState = drilledState ?? undefined;\n const wasDrilledDistrict = drilledDistrict ?? undefined;\n setActiveSubDrillDownId(null);\n setActiveDrillDownId(null);\n setActiveSelectedId(priorState?.id ?? null);\n inspectedIdRef.current = priorState?.id ?? null;\n setInspectedId(priorState?.id ?? null);\n restoreFocusId.current = priorState?.id ?? null;\n if (wasDrilledDistrict) onSubDistrictDrillDownChange?.(null, wasDrilledDistrict);\n onDrillDownChange?.(null, priorState);\n };\n\n // Shared by the tooltip and the insight panel so the two can never disagree\n // about share or rank — they only differ in which region they describe.\n const toTooltipContext = useCallback((region: PreparedRegion): TooltipContext => {\n const valued = regions.filter((candidate) => candidate.value !== null);\n // Ties share the better rank (\"2nd of 36\" twice, then 4th), which is what a\n // reader expects from a leaderboard and avoids an arbitrary tiebreak.\n const rank = region.value === null\n ? null\n : valued.filter((candidate) => (candidate.value ?? 0) > (region.value ?? 0)).length + 1;\n return {\n ...region,\n level,\n total,\n share: region.value === null || total === 0 ? null : (region.value / total) * 100,\n rank,\n rankedCount: valued.length,\n };\n }, [level, regions, total]);\n\n const tooltipContext = useMemo<TooltipContext | null>(\n () => inspected ? toTooltipContext(inspected) : null,\n [inspected, toTooltipContext],\n );\n // Unlike the tooltip, the host-owned insight panel is meant to stay informative\n // when nothing is hovered, so it still falls back to the selected region.\n const insightSource = inspected ?? selected;\n const insightContext = useMemo<InsightContext | null>(() => insightSource\n ? { ...toTooltipContext(insightSource), selected: insightSource.id === selected?.id }\n : null, [insightSource, selected?.id, toTooltipContext]);\n /**\n * Where each region's value label goes, and whether it needs a leader line.\n * Small regions are moved into clear space beside themselves; everything else\n * keeps its number at its centroid.\n */\n const valuePlacements = useMemo(() => {\n const coversAnother = (region: PreparedRegion, at: Point, halfWidth: number, halfHeight: number) => {\n const probes: Point[] = [\n at,\n [at[0] - halfWidth, at[1] - halfHeight],\n [at[0] + halfWidth, at[1] + halfHeight],\n [at[0] + halfWidth, at[1] - halfHeight],\n [at[0] - halfWidth, at[1] + halfHeight],\n ];\n return regions.some((other) => other.id !== region.id && other.partBounds.some(([minX, minY, maxX, maxY]) =>\n probes.some((probe) => probe[0] >= minX && probe[0] <= maxX && probe[1] >= minY && probe[1] <= maxY)));\n };\n\n return regions.map((region) => {\n const text = region.value === null ? \"—\" : formatValue(region.value);\n // Glyph metrics without measuring the DOM: the stylesheet sets 11px bold,\n // and digits in that face are close enough to half-em wide for placement.\n const halfWidth = Math.max(text.length * 3.1, 3);\n const halfHeight = 5.5;\n const isSmall = region.extent > 0 && region.extent < SMALL_REGION_EXTENT;\n const fitsInside = region.partBounds.some(([minX, minY, maxX, maxY]) =>\n maxX - minX >= halfWidth * 2 && maxY - minY >= halfHeight * 2);\n\n if (!isSmall && fitsInside) return { region, text, at: region.centroid as Point, leader: null };\n\n const outside = isSmall\n ? placeOutsideLabel({\n anchor: region.centroid,\n clearance: Math.max(region.extent, MIN_REGION_MARKER_SIZE) / 2 + 4 + halfWidth,\n halfSize: [halfWidth, halfHeight],\n viewBox: [VIEWBOX.width, VIEWBOX.height],\n centre: [VIEWBOX.width / 2, VIEWBOX.height / 2],\n // Moving a label out only helps if there is open space to move it into.\n // Goa and Puducherry have sea beside them; Delhi is ringed by other\n // states, so its number stays put rather than landing on a neighbour.\n isBlocked: (candidate) => coversAnother(region, candidate, halfWidth, halfHeight),\n })\n : null;\n\n if (!outside) {\n return fitsInside ? { region, text, at: region.centroid as Point, leader: null } : null;\n }\n\n const gap = Math.max(region.extent, MIN_REGION_MARKER_SIZE) / 2 + 1;\n const dx = outside[0] - region.centroid[0];\n const dy = outside[1] - region.centroid[1];\n const length = Math.hypot(dx, dy);\n if (length <= gap + halfWidth) return { region, text, at: outside, leader: null };\n return {\n region,\n text,\n at: outside,\n leader: [\n [region.centroid[0] + (dx / length) * gap, region.centroid[1] + (dy / length) * gap],\n [outside[0] - (dx / length) * (halfWidth + 1.5), outside[1] - (dy / length) * (halfWidth + 1.5)],\n ] as [Point, Point],\n };\n }).filter((placement): placement is NonNullable<typeof placement> => placement !== null);\n }, [formatValue, regions]);\n\n const smallMarkers = useMemo(\n () => regions.filter((region) => region.extent > 0 && region.extent < MIN_REGION_MARKER_SIZE),\n [regions],\n );\n\n /**\n * A click that reaches the svg itself missed every region path. Either it\n * landed near a small one — Goa, Puducherry, the island groups, all awkward to\n * hit — or it is a click on open sea, which clears the selection.\n */\n const handleBackgroundClick = (event: ReactMouseEvent<SVGSVGElement>) => {\n if (event.target !== event.currentTarget) return; // a region handled it\n // An unlaid-out svg (or a test environment that reports zero-size rects) has\n // no usable coordinates, so the proximity step is skipped — but the click is\n // still a click on the background and must clear the selection.\n const rect = event.currentTarget.getBoundingClientRect();\n const point: Point | null = rect.width > 0 && rect.height > 0\n ? (() => {\n // The svg scales its view box to fit while preserving aspect ratio, so\n // the scale is the smaller ratio and the remainder is centring.\n const scale = Math.min(rect.width / VIEWBOX.width, rect.height / VIEWBOX.height);\n return [\n (event.clientX - rect.left - (rect.width - VIEWBOX.width * scale) / 2) / scale,\n (event.clientY - rect.top - (rect.height - VIEWBOX.height * scale) / 2) / scale,\n ] as Point;\n })()\n : null;\n\n let nearest: PreparedRegion | null = null;\n if (point) {\n let nearestDistance = Infinity;\n for (const region of regions) {\n if (region.extent <= 0 || region.extent >= SMALL_REGION_EXTENT) continue;\n const distance = distanceToParts(point, region.partBounds);\n if (distance <= SMALL_REGION_CLICK_RADIUS && distance < nearestDistance) {\n nearestDistance = distance;\n nearest = region;\n }\n }\n }\n if (nearest) { activate(nearest); return; }\n\n onBackgroundClick?.();\n if (activeSelectedId !== null) {\n setActiveSelectedId(null);\n onSelectedChange?.(null, level);\n }\n };\n\n const canDrill = level === \"state\" ? Boolean(loadDistricts) : level === \"district\" ? Boolean(loadSubDistricts) : false;\n const drillActionLabel = level === \"state\" ? \"Activate to view districts.\" : \"Activate to view sub-districts.\";\n const regionCanDrill = (id: string) => canDrill && !(level === \"district\" && leafDistrictIds.has(id));\n const visibleReferenceRegions = level === \"state\" ? referenceRegions : districtReferenceRegions;\n const mergedReferenceIds = useMemo(() => new Set(referenceOverlayMergeIds), [referenceOverlayMergeIds]);\n\n useEffect(() => { onInsight?.(insightContext); }, [insightContext, onInsight]);\n\n // Nudge the anchored tooltip back inside the map. This runs in a layout effect,\n // not an effect, so the un-nudged position never paints — that one frame would be\n // a visible jump on exactly the edge regions this exists to fix. Keyed on the\n // content, not just the region, because the box is sized by what's in it.\n useLayoutEffect(() => {\n const anchor = tooltipAnchorRef.current;\n const canvas = canvasRef.current;\n if (!anchor || !canvas) return;\n\n // Measure the default placement (centered, above), so the correction is\n // computed against a known starting point rather than the last region's.\n anchor.style.removeProperty(\"--india-map-tooltip-dx\");\n anchor.style.removeProperty(\"--india-map-tooltip-dy\");\n\n const tooltipRect = anchor.getBoundingClientRect();\n const boundsRect = canvas.getBoundingClientRect();\n if (tooltipRect.width === 0 || boundsRect.width === 0) return; // not laid out (hidden, or jsdom)\n\n const { dx, side } = placeTooltip(tooltipRect, boundsRect, TOOLTIP_GAP_PX);\n if (dx !== 0) anchor.style.setProperty(\"--india-map-tooltip-dx\", `${Math.round(dx)}px`);\n if (side === \"below\") anchor.style.setProperty(\"--india-map-tooltip-dy\", `${TOOLTIP_GAP_PX}px`);\n }, [tooltipContext?.id, tooltipContext?.value, tooltipContext?.label, renderTooltip]);\n\n return (\n <section className={[\"india-choropleth\", !interactive && \"india-choropleth--static\", className].filter(Boolean).join(\" \")} aria-busy={loadingState || loadingDistrict ? \"true\" : undefined}>\n {showBreadcrumb ? (\n <div className=\"india-choropleth__toolbar\">\n <nav className=\"india-choropleth__breadcrumb\" aria-label=\"Map hierarchy\">\n {drilledState\n ? <button className=\"india-choropleth__back\" type=\"button\" onClick={goToStates}>All states</button>\n : <span>All states</span>}\n {drilledState ? (\n <>\n <span aria-hidden=\"true\">/</span>\n {/* The state is a link back only once there is a level below it to\n come back from; on the district view it is where you already are. */}\n {isSubDrillRequested\n ? <button className=\"india-choropleth__back\" type=\"button\" onClick={goBack}>{drilledState.label}</button>\n : <span aria-current=\"page\">{drilledState.label}</span>}\n </>\n ) : null}\n {isSubDrillRequested && drilledDistrict ? (\n <><span aria-hidden=\"true\">/</span><span aria-current=\"page\">{drilledDistrict.label}</span></>\n ) : null}\n </nav>\n </div>\n ) : null}\n <div ref={canvasRef} className=\"india-choropleth__canvas\" onMouseLeave={interactive ? () => inspect(null) : undefined}>\n {isSubDrillRequested && (!subDistrictLayer || subLoadError) ? (\n <div className=\"india-choropleth__status\" role={subLoadError ? \"alert\" : \"status\"}>\n {subLoadError ? subLoadError.message : loadingDistrict ? \"Loading sub-districts…\" : \"Sub-district data is unavailable for this district.\"}\n </div>\n ) : isDrillRequested && (!districtLayer || loadError) ? (\n <div className=\"india-choropleth__status\" role={loadError ? \"alert\" : \"status\"}>\n {loadError ? loadError.message : loadingState ? \"Loading districts…\" : \"District data is unavailable for this state.\"}\n </div>\n ) : regions.length === 0 ? (\n <div className=\"india-choropleth__status\" role=\"status\">\n {level === \"subdistrict\" ? \"No sub-district data is available for this district.\" : \"No district data is available for this state.\"}\n </div>\n ) : (\n <svg\n className={`india-choropleth__svg${filterBucket !== null ? \" india-choropleth__svg--filtered\" : \"\"}`}\n viewBox={`0 0 ${VIEWBOX.width} ${VIEWBOX.height}`}\n role=\"group\"\n aria-label={ariaLabel}\n onKeyDown={interactive ? (event) => { if (event.key === \"Escape\") { event.preventDefault(); inspect(null); } } : undefined}\n onClick={interactive ? handleBackgroundClick : undefined}\n >\n {visibleReferenceRegions.length > 0 ? (\n <>\n <defs>\n <pattern id={hatchId} width=\"8\" height=\"8\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">\n <rect width=\"8\" height=\"8\" fill=\"var(--india-map-reference-bg)\" />\n <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"8\" stroke=\"var(--india-map-reference-hatch)\" strokeWidth=\"2\" />\n </pattern>\n </defs>\n <g className=\"india-choropleth__reference-fill\" role=\"group\" aria-label=\"Non-statistical reference context.\">\n {visibleReferenceRegions.map((region) => (\n <path\n key={region.id}\n d={region.path}\n fill={referenceOverlayFill === \"solid\" ? \"var(--india-map-reference-bg)\" : `url(#${hatchId})`}\n aria-label={`${region.label}.${region.description ? ` ${region.description}` : \"\"}`}\n role=\"img\"\n />\n ))}\n </g>\n </>\n ) : null}\n {/* Hit areas for scattered regions, first so that every real outline is\n painted on top of them: the hull spanning Lakshadweep's islands is\n open sea, but Puducherry's spans the Tamil Nadu coast, and a hull\n must never take a pointer from a region that is actually there.\n Not focusable — keyboard focus has no coordinates, so the one tab\n stop stays on the region itself. */}\n {interactive ? (\n <g className=\"india-choropleth__hit-areas\" aria-hidden=\"true\">\n {regions.filter((region) => region.hitPath).map((region) => (\n <path\n key={region.id}\n d={region.hitPath!}\n fill=\"none\"\n pointerEvents=\"all\"\n tabIndex={-1}\n onMouseEnter={() => inspect(region)}\n onMouseLeave={() => inspect(null)}\n onClick={() => activate(region)}\n />\n ))}\n </g>\n ) : null}\n {regions.map((region) => {\n const isInspected = region.id === inspected?.id;\n const isSelected = region.id === selected?.id;\n const action = regionCanDrill(region.id) ? drillActionLabel : \"Activate to select.\";\n const textValue = region.value === null ? \"No data\" : formatValue(region.value);\n return (\n <path\n key={region.id}\n className={`india-choropleth__region${isInspected ? \" india-choropleth__region--inspected\" : \"\"}${isSelected ? \" india-choropleth__region--selected\" : \"\"}${mergedReferenceIds.has(region.id) ? \" india-choropleth__region--reference-merged\" : \"\"}${isDimmed(region.id) ? \" india-choropleth__dimmed\" : \"\"}`}\n d={region.path}\n fill={colorFor(region.value, region, min, max, colorScale)}\n tabIndex={interactive ? 0 : -1}\n role={interactive ? \"button\" : undefined}\n aria-label={`${region.label}, ${textValue}. ${action}`}\n aria-pressed={interactive ? isSelected : undefined}\n aria-describedby={isInspected ? tooltipId : undefined}\n ref={(element) => { pathRefs.current[region.id] = element; }}\n onMouseEnter={interactive ? () => inspect(region) : undefined}\n // The canvas is much wider than the drawn map, so leaving a region\n // usually lands on blank canvas rather than leaving the canvas at all.\n // Without this the last-hovered tooltip stays pinned indefinitely.\n // Moving straight to a sibling region dispatches this leave and that\n // region's enter from the same native event, so the tooltip switches\n // in one batch instead of blanking.\n onMouseLeave={interactive ? () => inspect(null) : undefined}\n onFocus={interactive ? () => inspect(region) : undefined}\n // Tabbing to a sibling region re-inspects it synchronously right after\n // this fires, so clearing here only matters when focus leaves the map.\n onBlur={interactive ? () => inspect(null) : undefined}\n onClick={interactive ? () => activate(region) : undefined}\n onKeyDown={interactive ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") { event.preventDefault(); activate(region); }\n } : undefined}\n />\n );\n })}\n {/* A region whose largest part is smaller than the marker is invisible at\n this scale — Puducherry's enclaves are a couple of units — so it gets\n a dot in its own colour instead of nothing at all. The dot takes the\n pointer as well: it is painted over whatever is beneath it, so it is\n what the reader sees and aims at, and the outline it stands in for is\n too small to hover. Focus stays on the region path, its one tab stop. */}\n {smallMarkers.length > 0 ? (\n <g className=\"india-choropleth__small-markers\" aria-hidden=\"true\">\n {smallMarkers.map((region) => (\n <circle\n key={region.id}\n className={isDimmed(region.id) ? \"india-choropleth__dimmed\" : undefined}\n cx={region.centroid[0]}\n cy={region.centroid[1]}\n r={MIN_REGION_MARKER_SIZE / 2}\n fill={colorFor(region.value, region, min, max, colorScale)}\n onMouseEnter={interactive ? () => inspect(region) : undefined}\n onMouseLeave={interactive ? () => inspect(null) : undefined}\n onClick={interactive ? () => activate(region) : undefined}\n />\n ))}\n </g>\n ) : null}\n {showRegionValues ? (\n <>\n <g className=\"india-choropleth__value-leaders\" aria-hidden=\"true\">\n {valuePlacements.filter((p) => p.leader).map((p) => (\n <line\n key={p.region.id}\n className={isDimmed(p.region.id) ? \"india-choropleth__dimmed\" : undefined}\n x1={p.leader![0][0]} y1={p.leader![0][1]} x2={p.leader![1][0]} y2={p.leader![1][1]}\n />\n ))}\n </g>\n <g className={`india-choropleth__region-values${level === \"state\" ? \"\" : \" india-choropleth__region-values--district\"}`} aria-hidden=\"true\">\n {valuePlacements.map((p) => (\n <text\n key={p.region.id}\n className={isDimmed(p.region.id) ? \"india-choropleth__dimmed\" : undefined}\n x={p.at[0]} y={p.at[1]} textAnchor=\"middle\" dominantBaseline=\"central\"\n >\n {p.text}\n </text>\n ))}\n </g>\n </>\n ) : null}\n {visibleReferenceRegions.length > 0 ? (\n <g className=\"india-choropleth__reference-outline\" aria-hidden=\"true\">\n {visibleReferenceRegions.map((region) => <path key={region.id} d={region.path} fill=\"none\" />)}\n </g>\n ) : null}\n {/* Selection is drawn as a ring on top of everything, rather than by recoloring\n the region: on a choropleth the fill *is* the data, so overwriting it made the\n selected region's color stop meaning anything. Hovering a region lifts it 2px,\n so the ring matches that lift when the selected region is also the inspected\n one — otherwise the fill slides out from under its own outline. */}\n {selected ? (\n <g\n className={`india-choropleth__selection${selected.id === inspected?.id ? \" india-choropleth__selection--lifted\" : \"\"}`}\n aria-hidden=\"true\"\n >\n <path className=\"india-choropleth__selection-halo\" d={selected.path} fill=\"none\" />\n <path className=\"india-choropleth__selection-ring\" d={selected.path} fill=\"none\" />\n </g>\n ) : null}\n </svg>\n )}\n {tooltipContext && regions.length > 0 ? (\n <div ref={tooltipAnchorRef} className=\"india-choropleth__tooltip-anchor\" style={{ left: `${(inspected!.centroid[0] / VIEWBOX.width) * 100}%`, top: `${(inspected!.centroid[1] / VIEWBOX.height) * 100}%` }}>\n {/* Deliberately not `role=\"status\"`. As a live region it re-announced the whole\n tooltip on every hover *and* every focus move, and the content is richer now.\n The region aria-label already carries label + value, and `aria-describedby`\n reads this box out on focus — once, on demand. */}\n <div id={tooltipId} className=\"india-choropleth__tooltip\">\n {renderTooltip ? renderTooltip(tooltipContext) : defaultTooltip(tooltipContext, formatValue)}\n </div>\n </div>\n ) : null}\n </div>\n {showLegend ? (\n <div\n className=\"india-choropleth__legend\"\n role=\"group\"\n aria-label={`Color scale: ${legendLabels[0]} to ${legendLabels[1]} values`}\n onKeyDown={interactive ? (event) => { if (event.key === \"Escape\") { event.preventDefault(); setActiveBucket(null); } } : undefined}\n >\n <span>{legendLabels[0]}</span><div className=\"india-choropleth__swatches\" aria-hidden={interactive ? undefined : true}>\n {/* Each swatch filters the map to its own band. An empty band is left\n as a swatch you can still read — silently skipping it would hide\n the fact that the ramp has a gap there — but it does nothing,\n because filtering to nothing just dims the whole map. */}\n {buckets.map((bucket) => interactive ? (\n <button\n key={bucket.index}\n type=\"button\"\n className={`india-choropleth__swatch${filterBucket === bucket.index ? \" india-choropleth__swatch--active\" : \"\"}${filterBucket !== null && filterBucket !== bucket.index ? \" india-choropleth__swatch--muted\" : \"\"}`}\n style={{ backgroundColor: bucket.color }}\n aria-pressed={filterBucket === bucket.index}\n aria-disabled={bucket.matches === 0 ? true : undefined}\n aria-label={legendBucketLabel(bucket, formatValue)}\n // Sighted readers get the same sentence the accessible name carries,\n // which is the only place an empty band explains itself now that it\n // is no longer faded.\n title={legendBucketLabel(bucket, formatValue)}\n onClick={() => {\n if (bucket.matches === 0) return;\n setActiveBucket(filterBucket === bucket.index ? null : bucket.index);\n }}\n />\n ) : (\n <i className=\"india-choropleth__swatch\" key={bucket.index} style={{ backgroundColor: bucket.color }} />\n ))}\n </div><span>{legendLabels[1]}</span>\n {visibleReferenceRegions.length > 0 ? <><i className={`india-choropleth__reference-key${referenceOverlayFill === \"solid\" ? \" india-choropleth__reference-key--solid\" : \"\"}`} aria-hidden=\"true\" /><span>{referenceOverlayLegendLabel}</span></> : null}\n </div>\n ) : null}\n {renderInsights ? <aside className=\"india-choropleth__insights\" aria-live=\"polite\">{renderInsights(insightContext)}</aside> : null}\n </section>\n );\n}\n","import { feature as topoFeature } from \"topojson-client\";\nimport type { GeometryObject } from \"topojson-specification\";\nimport type { GeometrySource, MapFeatureCollection } from \"./types\";\n\nexport function asFeatureCollection(source: GeometrySource): MapFeatureCollection {\n if (\"type\" in source && source.type === \"FeatureCollection\") {\n return source;\n }\n\n const topoSource = source as Exclude<GeometrySource, MapFeatureCollection>;\n\n const object = (typeof topoSource.object === \"string\"\n ? topoSource.topology.objects[topoSource.object]!\n : topoSource.object) as unknown as GeometryObject;\n if (!object) {\n throw new Error(\"The named TopoJSON object does not exist in this topology.\");\n }\n\n const unpacked = topoFeature(topoSource.topology, object);\n return unpacked.type === \"FeatureCollection\"\n ? (unpacked as MapFeatureCollection)\n : { type: \"FeatureCollection\", features: [unpacked] };\n}\n\nexport function totalOf(values: readonly (number | null)[]) {\n return values.reduce<number>((total, value) => total + (value ?? 0), 0);\n}\n","/**\n * The legend, as a filter.\n *\n * Every value on the map is painted from one of the ramp's colours. That makes\n * the legend a ready-made set of value bands, and picking a band is the\n * question a reader of a choropleth actually has: *which regions are the dark\n * ones?* These helpers answer it — which swatch a value belongs to, what each\n * swatch stands for, and how many regions land there.\n *\n * [swatchIndexOf] is the single definition of that mapping: the renderers pick\n * a region's fill with it too, so \"highlight the regions painted in this\n * colour\" is true by construction rather than by two formulas agreeing.\n *\n * Pure functions of values, so the DOM renderer and the React component share\n * one definition of the behaviour.\n */\n\n/** One swatch: the colour, and what the map actually has in it. */\nexport interface LegendBucket {\n index: number;\n color: string;\n /**\n * Lowest and highest value that lands here, or null when nothing does.\n *\n * The band the ramp *nominally* covers is a half-step either side of the\n * swatch's own stop, which lands on numbers like \"24.333 to 31\" that appear\n * nowhere in the data. What a reader wants to know is what picking this\n * swatch will give them, so the range is measured from the regions in it.\n */\n from: number | null;\n to: number | null;\n /** How many regions land here. Zero means the swatch would filter to nothing. */\n matches: number;\n}\n\n/**\n * Which swatch a value is painted from, or null when there is nothing to paint\n * — no value, or no ramp to paint it with.\n *\n * A ramp of one colour, or data with no spread at all, collapses to the top\n * swatch: there is a single band and every value is in it.\n */\nexport function swatchIndexOf(value: number | null, min: number, max: number, count: number): number | null {\n if (value === null || count <= 0) return null;\n if (count === 1 || max === min) return count - 1;\n const index = Math.round(((value - min) / (max - min)) * (count - 1));\n return Math.min(Math.max(index, 0), count - 1);\n}\n\n/**\n * The ramp described swatch by swatch.\n *\n * With a function colour scale the renderers show the default ramp, because a\n * function has no swatches to show. The bands are still the honest reading of\n * \"lower to higher\"; the swatch colour just isn't any region's actual fill.\n */\nexport function legendBuckets(\n colors: readonly string[],\n values: readonly (number | null)[],\n min: number,\n max: number,\n): LegendBucket[] {\n const count = colors.length;\n return colors.map((color, index) => {\n const members = values.filter((value): value is number => swatchIndexOf(value, min, max, count) === index);\n return {\n index,\n color,\n from: members.length ? Math.min(...members) : null,\n to: members.length ? Math.max(...members) : null,\n matches: members.length,\n };\n });\n}\n\n/**\n * What a swatch does, in words — the accessible name for its control, since the\n * colour itself carries the meaning and a screen reader cannot see it.\n */\nexport function legendBucketLabel(bucket: LegendBucket, formatValue: (value: number) => string): string {\n if (bucket.matches === 0 || bucket.from === null || bucket.to === null) return \"No regions in this band\";\n const range = bucket.from === bucket.to\n ? formatValue(bucket.from)\n : `${formatValue(bucket.from)} to ${formatValue(bucket.to)}`;\n return `Highlight ${bucket.matches} ${bucket.matches === 1 ? \"region\" : \"regions\"}, ${range}`;\n}\n\n","/**\n * Edge handling for the centroid-anchored tooltip.\n *\n * The tooltip is anchored to the region's centroid rather than the cursor on\n * purpose: hover and keyboard focus run through the same inspect path, and a\n * focused region has no cursor position to follow. That anchor still has to be\n * nudged so the box never leaves the map — a state at the top of the map (Jammu\n * & Kashmir on a narrow screen) would otherwise render *above* the map, over\n * whatever the host page put there.\n *\n * Kept as a pure function of two rects so both the DOM renderer and the React\n * component can share the same math.\n */\n\nexport interface Rect {\n left: number;\n right: number;\n top: number;\n bottom: number;\n width: number;\n height: number;\n}\n\nexport interface TooltipPlacement {\n /** Horizontal correction in px, applied on top of the centering translate. */\n dx: number;\n /** Which side of the centroid the tooltip sits on. */\n side: \"above\" | \"below\";\n}\n\n/**\n * @param tooltip The tooltip's rect as currently laid out (centered above the centroid).\n * @param bounds The area the tooltip must stay inside — the map canvas.\n * @param gap Space between the centroid and the tooltip edge, in px.\n * @param padding Minimum breathing room between the tooltip and the bounds edge, in px.\n */\nexport function placeTooltip(tooltip: Rect, bounds: Rect, gap: number, padding = 4): TooltipPlacement {\n let dx = 0;\n // A tooltip wider than the space available can't satisfy both edges; favor the\n // left one so the region name (which leads the content) stays readable.\n if (tooltip.right > bounds.right - padding) dx = bounds.right - padding - tooltip.right;\n if (tooltip.left + dx < bounds.left + padding) dx = bounds.left + padding - tooltip.left;\n\n // Flip below only when there genuinely isn't room above. The flipped position\n // is `gap` below the centroid, which is `height + 2 * gap` further down.\n const side: TooltipPlacement[\"side\"] = tooltip.top < bounds.top + padding ? \"below\" : \"above\";\n\n return { dx, side };\n}\n","/**\n * Geometry for regions that are too small to use normally.\n *\n * Goa is a few view-box units across, Puducherry is scattered enclaves, and\n * Lakshadweep's islands are a fraction of a pixel at national scale. Left alone\n * they are invisible, unclickable, and their value labels either don't fit or\n * land on a neighbour. These helpers back the three fixes for that: a marker so\n * they can be seen, a click buffer so they can be hit, and label placement that\n * moves a number into open space beside the region.\n *\n * Pure functions of projected coordinates, so the DOM renderer, the React\n * component and the Dart port can all share one definition of the behaviour.\n */\n\nexport type Point = readonly [number, number];\n\n/** Axis-aligned box as `[minX, minY, maxX, maxY]`. */\nexport type Box = readonly [number, number, number, number];\n\n/**\n * Regions kept at their true size even when they are small enough to qualify\n * for exaggeration.\n *\n * Exaggeration assumes a region has room to grow into. Puducherry does not: it\n * is four coastal enclaves *inside* Tamil Nadu, so growing them to the\n * visibility threshold pushes each one several units into the state around it,\n * and the map ends up showing a Puducherry that is the wrong shape in the wrong\n * place. Lakshadweep's islands grow into open sea, where nothing is displaced,\n * which is the case the feature was built for.\n *\n * Matched on the id containing the name because host data brings its own id\n * scheme; the bundled layer uses `in-cs-34-puducherry`. A district id inside the\n * UT matches too, which is a no-op: drilled into, its districts fill the map and\n * are far past the threshold that would have grown them.\n */\nconst TRUE_GEOMETRY_REGIONS = [\"puducherry\"];\n\nexport function keepsTrueGeometry(id: string): boolean {\n const normalized = id.toLowerCase();\n return TRUE_GEOMETRY_REGIONS.some((name) => normalized.includes(name));\n}\n\nexport function boundsOfRing(ring: readonly Point[]): Box {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [x, y] of ring) {\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n return [minX, minY, maxX, maxY];\n}\n\n/**\n * Longest side of the largest ring's box. This is the measure of \"small\", not\n * the feature's overall bounds: an island group's bounds can be large while\n * every island in it is sub-pixel.\n */\nexport function largestRingExtent(rings: readonly (readonly Point[])[]): number {\n let largest = 0;\n for (const ring of rings) {\n if (ring.length === 0) continue;\n const [minX, minY, maxX, maxY] = boundsOfRing(ring);\n const extent = Math.max(maxX - minX, maxY - minY);\n if (extent > largest) largest = extent;\n }\n return largest;\n}\n\nfunction signedArea(ring: readonly Point[]): number {\n let total = 0;\n for (let i = 0; i < ring.length; i++) {\n const [ax, ay] = ring[i]!;\n const [bx, by] = ring[(i + 1) % ring.length]!;\n total += ax * by - bx * ay;\n }\n return total / 2;\n}\n\n/**\n * Area-weighted centroid of the largest ring.\n *\n * Deliberately not the whole feature's centroid: averaging across parts pulls\n * Andaman & Nicobar's label out to sea between its islands, and Gujarat's off\n * its own coast.\n */\nexport function labelPointFor(rings: readonly (readonly Point[])[], fallback: Point): Point {\n let largest: readonly Point[] | null = null;\n let largestArea = 0;\n for (const ring of rings) {\n if (ring.length < 3) continue;\n const area = Math.abs(signedArea(ring));\n if (area > largestArea) {\n largestArea = area;\n largest = ring;\n }\n }\n if (!largest || largestArea === 0) return fallback;\n\n const area = signedArea(largest);\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < largest.length; i++) {\n const [ax, ay] = largest[i]!;\n const [bx, by] = largest[(i + 1) % largest.length]!;\n const cross = ax * by - bx * ay;\n cx += (ax + bx) * cross;\n cy += (ay + by) * cross;\n }\n const centroid: Point = [cx / (6 * area), cy / (6 * area)];\n return Number.isFinite(centroid[0]) && Number.isFinite(centroid[1]) ? centroid : fallback;\n}\n\n/** Distance from a point to the nearest edge of a box; zero when inside it. */\nexport function distanceToBox(point: Point, box: Box): number {\n const [x, y] = point;\n const [minX, minY, maxX, maxY] = box;\n const dx = Math.max(minX - x, 0) + Math.max(x - maxX, 0);\n const dy = Math.max(minY - y, 0) + Math.max(y - maxY, 0);\n return Math.hypot(dx, dy);\n}\n\n/** Distance to the nearest of a region's parts. */\nexport function distanceToParts(point: Point, parts: readonly Box[]): number {\n let nearest = Infinity;\n for (const part of parts) {\n const distance = distanceToBox(point, part);\n if (distance < nearest) nearest = distance;\n }\n return nearest;\n}\n\n/**\n * Angles to try when placing a small region's label, as turns from the\n * away-from-centre direction: straight out first, then progressively to either\n * side, and back inward only as a last resort.\n *\n * One fixed direction is not enough. Puducherry sits south-east of the map's\n * middle, so the radial direction runs inland into Tamil Nadu while its open\n * water is due east.\n */\nconst LABEL_SEARCH_TURNS = [\n 0,\n Math.PI / 6, -Math.PI / 6,\n Math.PI / 3, -Math.PI / 3,\n Math.PI / 2, -Math.PI / 2,\n (2 * Math.PI) / 3, -(2 * Math.PI) / 3,\n (5 * Math.PI) / 6, -(5 * Math.PI) / 6,\n Math.PI,\n] as const;\n\nexport interface OutsideLabelOptions {\n /** Where the region is, in view-box units. */\n anchor: Point;\n /** How far the label must clear the region itself. */\n clearance: number;\n /** Half the label's width and height, used to keep it inside the view box. */\n halfSize: Point;\n /** The view box the label must stay within, as `[width, height]`. */\n viewBox: Point;\n /** Direction is measured away from this point — normally the view box's middle. */\n centre: Point;\n /** Returns true when a label centred here would cover another region. */\n isBlocked: (candidate: Point) => boolean;\n}\n\n/**\n * Find a clear spot beside a region for its value label, or null when the\n * region is hemmed in on every side (Delhi, ringed by other states) and the\n * label is better left where it was.\n */\nexport function placeOutsideLabel(options: OutsideLabelOptions): Point | null {\n const { anchor, clearance, halfSize, viewBox, centre, isBlocked } = options;\n const dx = anchor[0] - centre[0];\n const dy = anchor[1] - centre[1];\n const base = dx === 0 && dy === 0 ? 0 : Math.atan2(dy, dx);\n\n for (const turn of LABEL_SEARCH_TURNS) {\n const angle = base + turn;\n const candidate: Point = [\n clamp(anchor[0] + Math.cos(angle) * clearance, halfSize[0], viewBox[0] - halfSize[0]),\n clamp(anchor[1] + Math.sin(angle) * clearance, halfSize[1], viewBox[1] - halfSize[1]),\n ];\n if (!isBlocked(candidate)) return candidate;\n }\n return null;\n}\n\nfunction clamp(value: number, lower: number, upper: number): number {\n return Math.min(Math.max(value, lower), upper);\n}\n\n/**\n * Grow a feature whose whole geometry is too small to see, about each part's\n * own centre.\n *\n * Two guards make this safe, and both were learned the hard way:\n *\n * 1. It applies only when the feature's *largest* part is under [minExtent] —\n * that is, the whole region is tiny. West Bengal is a large state whose\n * Sundarbans delta is fourteen small islets; enlarging those individually\n * blew them up into an overlapping mess across the river mouth. A region is\n * either small enough to need help or it is not.\n * 2. Once a feature qualifies, each part grows about its own centre until it is\n * visible. This preserves every part's location and aspect ratio instead of\n * turning a dispersed archipelago into one large, misplaced blob. The cap\n * keeps the tiniest specks from reading as real landmass.\n *\n * Growth is capped by [maxScale] so a speck never reads as a real landmass.\n */\nexport function enlargeSmallParts(\n rings: readonly (readonly Point[])[],\n minExtent: number,\n maxScale = 8,\n): Point[][] {\n const unchanged = () => rings.map((ring) => [...ring]);\n if (minExtent <= 0) return unchanged();\n // Only a feature that is small *as a whole* qualifies.\n const largest = largestRingExtent(rings);\n if (largest <= 0 || largest >= minExtent) return unchanged();\n\n return rings.map((ring) => {\n const [minX, minY, maxX, maxY] = boundsOfRing(ring);\n const extent = Math.max(maxX - minX, maxY - minY);\n const scale = extent <= 0 ? 1 : Math.min(minExtent / extent, maxScale);\n const cx = (minX + maxX) / 2;\n const cy = (minY + maxY) / 2;\n return ring.map(([x, y]) => [cx + (x - cx) * scale, cy + (y - cy) * scale] as Point);\n });\n}\n\n/**\n * Convex hull of a set of points, as one closed ring (Andrew's monotone chain).\n * Returns the input when there is nothing to wrap — fewer than three distinct\n * points, or all of them collinear.\n */\nexport function convexHull(points: readonly Point[]): Point[] {\n const sorted = [...points].sort((a, b) => a[0] - b[0] || a[1] - b[1]);\n const unique: Point[] = [];\n for (const point of sorted) {\n const prior = unique.at(-1);\n if (!prior || prior[0] !== point[0] || prior[1] !== point[1]) unique.push(point);\n }\n if (unique.length < 3) return unique.map((point) => [...point] as Point);\n\n const turn = (o: Point, a: Point, b: Point) =>\n (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);\n const half = (ordered: readonly Point[]) => {\n const chain: Point[] = [];\n for (const point of ordered) {\n while (chain.length >= 2 && turn(chain[chain.length - 2]!, chain[chain.length - 1]!, point) <= 0) chain.pop();\n chain.push(point);\n }\n return chain;\n };\n\n const lower = half(unique);\n const upper = half([...unique].reverse());\n // Each chain repeats the other's first point, so drop both endpoints once.\n const hull = [...lower.slice(0, -1), ...upper.slice(0, -1)];\n return hull.length >= 3 ? hull : unique.map((point) => [...point] as Point);\n}\n\n/**\n * One continuous hit area for a region scattered across separate parts: the\n * convex hull of everything it is drawn as.\n *\n * Lakshadweep is twenty specks in open sea. Even exaggerated they are a poor\n * pointer target, and the water they enclose is how the group reads on the map,\n * so treating that water as part of the region is what a reader expects. The\n * hull is safe to be generous with only because every renderer tests it *after*\n * every real outline has missed, so it can never take a hover from a neighbour\n * it happens to span.\n *\n * Null when the region needs no help: a single part, or already big enough to\n * point at directly ([maxExtent], the same \"too small to use\" threshold the\n * click buffer and value labels work from).\n */\nexport function scatteredHitArea(\n rings: readonly (readonly Point[])[],\n maxExtent: number,\n): Point[] | null {\n if (rings.length < 2 || maxExtent <= 0) return null;\n const extent = largestRingExtent(rings);\n if (extent <= 0 || extent >= maxExtent) return null;\n const hull = convexHull(rings.flat());\n return hull.length >= 3 ? hull : null;\n}\n\n/** An SVG path string for a set of rings, used when exaggeration has moved them. */\nexport function ringsToPath(rings: readonly (readonly Point[])[]): string {\n let d = \"\";\n for (const ring of rings) {\n if (ring.length === 0) continue;\n d += `M${ring[0]![0]},${ring[0]![1]}`;\n for (let i = 1; i < ring.length; i++) d += `L${ring[i]![0]},${ring[i]![1]}`;\n d += \"Z\";\n }\n return d;\n}\n","import { useCallback, useState } from \"react\";\n\nexport function useControllableState<T>(controlled: T | undefined, initial: T) {\n const [uncontrolled, setUncontrolled] = useState<T>(initial);\n const value = controlled === undefined ? uncontrolled : controlled;\n const setValue = useCallback((next: T) => {\n if (controlled === undefined) setUncontrolled(next);\n }, [controlled]);\n return [value, setValue] as const;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { IndiaChoropleth } from \"./IndiaChoropleth\";\nimport { asFeatureCollection } from \"./geometry\";\nimport {\n DEFAULT_DATA_BASE_URL,\n isInlineGeometry,\n loadDistrictTopology,\n loadSubDistrictTopology,\n resolveGeometry,\n statesUrl,\n type GeometryInput,\n} from \"./data-source\";\nimport { normalizeStateKey, resolveState } from \"./states\";\nimport type { GeometrySource, IndiaChoroplethProps, MapFeature, MapLayer, MapRegion } from \"./types\";\n\n/** Reads a feature's stable id. Matches the framework-free facade's default. */\nfunction defaultGetId(feature: MapFeature): string {\n return String(feature.properties?.id ?? feature.properties?.name);\n}\n\n/** Reads a feature's display name. Matches the framework-free facade's default. */\nfunction defaultGetLabel(feature: MapFeature): string {\n return String(feature.properties?.name ?? feature.properties?.id);\n}\n\n/**\n * Canonical storage key for a name. Known states/UTs collapse to their LGD id,\n * so `\"Goa\"`, `\"goa\"`, `\"GOA\"` and `\"in-cs-30-goa\"` are one entry; anything else\n * (custom geometry with its own labels) falls back to its normalized name.\n *\n * Identical to `BharatChoropleth#keyFor` in the framework-free package — both\n * read the same `states.ts`, which a test keeps byte-identical.\n */\nfunction keyFor(name: string): string {\n return resolveState(name)?.id ?? normalizeStateKey(name);\n}\n\n/**\n * Finds a feature's value, trying the most literal match first.\n *\n * Each key the caller wrote maps to the canonical key it addresses, never to a\n * value, so the value map stays the single source of truth and a later write\n * through one spelling is seen through every other.\n *\n * Exact id, then exact label, then each resolved through the state registry.\n * Ordering matters both ways round: an id that the registry does not know still\n * matches when the caller keyed by that id, and a caller who keyed by \"Orissa\"\n * still reaches Odisha. `has` rather than `??` throughout, so a deliberate null\n * reads as \"no data\" instead of falling through to the next candidate.\n */\nfunction lookUp(\n exactKeys: ReadonlyMap<string, string>,\n canonical: ReadonlyMap<string, number | null>,\n id: string,\n label: string,\n): number | null {\n for (const candidate of [\n exactKeys.get(id),\n exactKeys.get(label),\n resolveState(id)?.id,\n resolveState(label)?.id,\n normalizeStateKey(id),\n normalizeStateKey(label),\n ]) {\n if (candidate !== undefined && canonical.has(candidate)) return canonical.get(candidate) ?? null;\n }\n return null;\n}\n\n/** A number, or null for \"no data\". Anything not finite (NaN, Infinity) reads as no data. */\nfunction toValue(input: unknown): number | null {\n return typeof input === \"number\" && Number.isFinite(input) ? input : null;\n}\n\nexport interface BharatChoroplethProps extends Omit<IndiaChoroplethProps, \"states\"> {\n /**\n * Per-state values, keyed by any spelling the state registry accepts: display\n * name, slug, LGD id, former name, or a separator-free form. `Goa`, `goa`,\n * `Tamil Nadu`, `tamilnadu`, `Jammu & Kashmir`, `Orissa` and\n * `in-cs-30-goa` all resolve. Names it does not recognize are ignored with a\n * console warning rather than throwing.\n *\n * States you omit render as \"no data\", exactly as an explicit `null` does.\n *\n * This is the primary API. When both `values` and `data` are given, `values`\n * wins and `data` is ignored — they are not merged.\n */\n values?: Readonly<Record<string, number | null>>;\n /**\n * The same values as a row array, for data that already arrives that way.\n * Read through `regionKey` and `valueKey`. Ignored when `values` is given.\n *\n * ```tsx\n * <BharatChoropleth\n * data={[{ state: \"Telangana\", value: 82 }]}\n * regionKey=\"state\"\n * valueKey=\"value\"\n * />\n * ```\n */\n data?: readonly Readonly<Record<string, unknown>>[];\n /** Field on a `data` row holding the state name. Defaults to `\"region\"`. */\n regionKey?: string;\n /** Field on a `data` row holding the number. Defaults to `\"value\"`. Non-finite values read as \"no data\". */\n valueKey?: string;\n /**\n * Boundary data for the state/UT layer: inline GeoJSON/TopoJSON, a URL string\n * to fetch, or a promise of either. Omit to fetch the prepared current-vintage\n * state bundle from `dataBaseUrl`. The package itself bundles no geometry.\n */\n geometry?: GeometryInput;\n /**\n * District values, nested under the state each district belongs to.\n *\n * ```tsx\n * districtValues={{\n * Telangana: { Hyderabad: 90, \"Ranga Reddy\": 76 },\n * Maharashtra: { Aurangabad: 44 },\n * }}\n * ```\n *\n * The nesting is not decoration. District names repeat across states —\n * Aurangabad, Bilaspur and Hamirpur each name a district in two — and unlike\n * states there is no district registry to resolve a bare name against, so a\n * flat map could not say which one you meant. Under a state it is unambiguous.\n *\n * Outer keys resolve through the state registry, exactly like `values`, and are\n * checked immediately. Inner keys match a district's name, slug or id,\n * case-insensitively; they can only be checked once that state's districts have\n * been fetched, so a typo there is warned about when you first drill into it.\n *\n * Applies to whichever district layer is in use, including one from your own\n * `loadDistricts`: a district listed here takes this value, and any district not\n * listed keeps whatever the layer itself returned.\n *\n * There is no `subDistrictValues`. Three levels of nesting stops reading\n * clearly, and sub-district naming is far less settled than district naming —\n * set those through a custom `loadSubDistricts` instead.\n */\n districtValues?: Readonly<Record<string, Readonly<Record<string, number | null>>>>;\n /** Base URL for the prepared boundary bundles. Point it at your own copy of `data/generated` to self-host. */\n dataBaseUrl?: string;\n /**\n * Click-to-drill-down into districts. Defaults to `true` when the state layer\n * came from `dataBaseUrl` (district files live beside it), `false` when you\n * supplied your own `geometry` — pass `loadDistricts` yourself in that case.\n */\n districts?: boolean;\n /**\n * Click-to-drill-down from a district into its sub-districts (tehsils / taluks /\n * mandals / blocks). Defaults the same way `districts` does. Districts the\n * bundle has no sub-districts for stay leaves rather than erroring.\n */\n subDistricts?: boolean;\n /** Reads a feature's stable id. Defaults to `feature.properties.id`. Memoize a custom one — a new identity re-projects the map. */\n getId?: (feature: MapFeature) => string;\n /** Reads a feature's display name. Defaults to `feature.properties.name`. Memoize a custom one — a new identity re-projects the map. */\n getLabel?: (feature: MapFeature) => string;\n /** Called if boundary data fails to load. Without it the error is logged; either way the message is rendered in place of the map. */\n onError?: (error: Error) => void;\n}\n\n/**\n * The zero-config map: give it numbers keyed by state name, get a choropleth.\n *\n * ```tsx\n * import { BharatChoropleth } from \"bharat-choropleth\";\n * import \"bharat-choropleth/style.css\";\n *\n * <BharatChoropleth values={{ Telangana: 82, Karnataka: 74, Maharashtra: 91 }} />\n * ```\n *\n * Boundary data is fetched (never bundled), so the map shows a placeholder until\n * it lands. Drill-down into districts and sub-districts is on by default when\n * that default data source is in use.\n *\n * This is sugar over {@link IndiaChoropleth}, which remains the full renderer —\n * custom layers, controlled selection and drill-down, reference overlays, custom\n * tooltips. Every one of its props except `states` passes straight through, so\n * reaching for one is a prop, not a rewrite.\n */\nexport function BharatChoropleth({\n values,\n data,\n districtValues,\n regionKey = \"region\",\n valueKey = \"value\",\n geometry,\n dataBaseUrl = DEFAULT_DATA_BASE_URL,\n districts,\n subDistricts,\n getId = defaultGetId,\n getLabel = defaultGetLabel,\n onError,\n ...rest\n}: BharatChoroplethProps) {\n // `values` wins outright when both are given; merging two sources of truth for\n // the same state would make precedence a guess at the call site.\n const entries: readonly (readonly [name: string, value: number | null])[] = useMemo(() => {\n if (values) return Object.entries(values).map(([name, value]) => [name, toValue(value)] as const);\n if (data) return data.map((row) => [String(row[regionKey] ?? \"\"), toValue(row[valueKey])] as const);\n return [];\n }, [data, regionKey, valueKey, values]);\n\n const { valueMap, exactKeys, writtenAs } = useMemo(() => {\n const valueMap = new Map<string, number | null>();\n /**\n * The caller's keys exactly as written, checked before the registry.\n *\n * Not every id belongs to the registry. The historical Census bundle in this\n * repository uses `in-hs-*` ids, which `resolveState` does not know, so a\n * feature keyed on its id would fall through to being keyed on its *label* —\n * and values written against ids would silently never match, leaving a fully\n * populated dataset rendering as \"No data\" everywhere. Keeping the literal\n * keys means id-keyed values work for any geometry, registry or not.\n */\n const exactKeys = new Map<string, string>();\n const writtenAs = new Map<string, string>();\n for (const [name, value] of entries) {\n const key = keyFor(name);\n valueMap.set(key, value);\n exactKeys.set(name, key);\n // Keep the caller's own spelling so a warning quotes what they typed.\n if (!writtenAs.has(key)) writtenAs.set(key, name);\n }\n return { valueMap, exactKeys, writtenAs };\n }, [entries]);\n\n /**\n * `{ Telangana: { Hyderabad: 90 } }` resolved to\n * `{ \"in-cs-36-telangana\" => { \"hyderabad\" => 90 } }`. The outer key goes\n * through the state registry so any spelling of the state works; the inner keys\n * are normalized the same way, which is what makes them match a district's\n * name, slug or id whatever case or separators the caller used.\n */\n const districtValueMap = useMemo(() => {\n const byState = new Map<string, Map<string, number | null>>();\n for (const [stateName, districts] of Object.entries(districtValues ?? {})) {\n const stateKey = keyFor(stateName);\n const inner = byState.get(stateKey) ?? new Map<string, number | null>();\n for (const [districtName, value] of Object.entries(districts ?? {})) {\n inner.set(normalizeStateKey(districtName), toValue(value));\n }\n byState.set(stateKey, inner);\n }\n return byState;\n }, [districtValues]);\n\n const districtSignature = useMemo(\n () =>\n JSON.stringify(\n [...districtValueMap]\n .map(([stateKey, inner]) => [stateKey, [...inner].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))] as const)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n ),\n [districtValueMap],\n );\n\n /**\n * Content hash of the resolved values. Callers write `values={{ Goa: 6 }}`\n * inline, so the object is new on every parent render; keying the layer on it\n * would re-project the whole national map each time. Keying on what the values\n * actually *are* means a re-render that changed nothing costs nothing.\n */\n const valueSignature = useMemo(\n () => JSON.stringify([...valueMap].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),\n [valueMap],\n );\n\n // Read by the drill-down loaders, which must keep a stable identity across\n // value changes: IndiaChoropleth re-runs its district effect when the loader\n // changes, so a loader rebuilt per value would refetch on every update.\n const valuesRef = useRef(valueMap);\n valuesRef.current = valueMap;\n const exactKeysRef = useRef(exactKeys);\n exactKeysRef.current = exactKeys;\n const districtValuesRef = useRef(districtValueMap);\n districtValuesRef.current = districtValueMap;\n\n const source: GeometryInput = geometry ?? statesUrl(dataBaseUrl);\n const inlineGeometry = isInlineGeometry(source) ? source : null;\n const [fetched, setFetched] = useState<GeometrySource | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (isInlineGeometry(source)) return; // already in hand — rendered without a placeholder\n const controller = typeof AbortController === \"function\" ? new AbortController() : null;\n let cancelled = false;\n setFetched(null);\n setError(null);\n resolveGeometry(source, \"states\", controller?.signal)\n .then((loaded) => {\n if (!cancelled) setFetched(loaded);\n })\n .catch((cause: unknown) => {\n if (cancelled || (cause instanceof Error && cause.name === \"AbortError\")) return;\n const failure = cause instanceof Error ? cause : new Error(\"BharatChoropleth: could not load boundary data.\");\n setError(failure);\n if (onErrorRef.current) onErrorRef.current(failure);\n else console.error(failure);\n });\n return () => {\n cancelled = true;\n controller?.abort();\n };\n // A URL string compares by value, so the usual case re-runs only on a real change.\n }, [source]);\n\n const resolvedGeometry = inlineGeometry ?? fetched;\n\n const statesLayer = useMemo<MapLayer | null>(() => {\n if (!resolvedGeometry) return null;\n /**\n * A district can share its parent's name (Lakshadweep), so resolve a\n * feature through the state registry by id first and only then by label,\n * rather than matching a bare slug against an id-keyed map.\n */\n return {\n geometry: resolvedGeometry,\n getId,\n getLabel,\n // `valueMap` is captured deliberately: the memo is keyed on the signature\n // of exactly these values, so the captured map and the key always agree.\n getValue: (feature) => lookUp(exactKeys, valueMap, getId(feature), getLabel(feature)),\n };\n // `valueSignature` is the dependency that stands in for `valueMap`; see above.\n }, [exactKeys, getId, getLabel, resolvedGeometry, valueSignature]);\n\n /**\n * Unknown names cannot be judged until the boundary data has landed and named\n * the real label set, so the warning is deferred rather than guessed at — the\n * same order the framework-free facade warns in. Warning during render would\n * also fire twice under StrictMode.\n */\n const warned = useRef(new Set<string>());\n useEffect(() => {\n if (!resolvedGeometry || !statesLayer) return;\n const known = new Set(\n asFeatureCollection(resolvedGeometry).features.map(\n (feature) =>\n resolveState(getId(feature))?.id ?? resolveState(getLabel(feature))?.id ?? normalizeStateKey(getLabel(feature)),\n ),\n );\n for (const [key, value] of valuesRef.current) {\n if (value === null || known.has(key) || warned.current.has(key)) continue;\n warned.current.add(key);\n console.warn(\n `BharatChoropleth: \"${writtenAs.get(key) ?? key}\" is not a recognized state/UT — its value is ignored.`,\n );\n }\n }, [getId, getLabel, resolvedGeometry, statesLayer, valueSignature, writtenAs]);\n\n // Drill-down geometry is fetched once per id and held for the component's life.\n // IndiaChoropleth re-runs its district effect whenever the state layer changes\n // — which a value update does — so without this every value change refetched.\n interface DrillDownState {\n controller: AbortController | null;\n districts: Map<string, Promise<GeometrySource>>;\n subDistricts: Map<string, Promise<GeometrySource | null>>;\n }\n // Built lazily: `useRef(expr)` evaluates `expr` on every render and discards\n // it, and this component is built to re-render on every data tick.\n const drillDownRef = useRef<DrillDownState | null>(null);\n drillDownRef.current ??= {\n controller: typeof AbortController === \"function\" ? new AbortController() : null,\n districts: new Map(),\n subDistricts: new Map(),\n };\n const drillDown = drillDownRef.current;\n useEffect(() => () => drillDownRef.current?.controller?.abort(), []);\n\n /**\n * Overlays `districtValues` onto a district layer. A district named in the prop\n * takes that value; one that is not keeps whatever the layer returned, so a\n * caller's own `loadDistricts` still supplies everything they did not override.\n *\n * Warns once per state, after that state's districts have arrived — the only\n * point at which an unmatched name is known to be a typo rather than a district\n * that simply has not loaded yet.\n */\n const warnedDistricts = useRef(new Set<string>());\n const withDistrictValues = useCallback((layer: MapLayer, stateId: string): MapLayer => {\n const wanted = districtValuesRef.current.get(stateId);\n if (!wanted || wanted.size === 0) return layer;\n const keysFor = (feature: MapFeature) => [normalizeStateKey(layer.getId(feature)), normalizeStateKey(layer.getLabel(feature))];\n\n if (!warnedDistricts.current.has(stateId)) {\n warnedDistricts.current.add(stateId);\n const present = new Set(asFeatureCollection(layer.geometry).features.flatMap(keysFor));\n for (const [key, value] of wanted) {\n if (value !== null && !present.has(key)) {\n console.warn(`BharatChoropleth: \"${key}\" is not a district of this state — its value is ignored.`);\n }\n }\n }\n\n return {\n ...layer,\n getValue: (feature) => {\n for (const key of keysFor(feature)) {\n // `has` rather than `??`, so an explicit null reads as \"no data\"\n // instead of falling through to the layer's own value.\n if (wanted.has(key)) return wanted.get(key) ?? null;\n }\n return layer.getValue(feature);\n },\n };\n }, []);\n\n const usingDefaultData = geometry === undefined;\n const districtsEnabled = districts ?? usingDefaultData;\n const subDistrictsEnabled = subDistricts ?? usingDefaultData;\n\n const defaultDistrictLoader = useMemo(() => {\n if (!districtsEnabled) return undefined;\n return async (stateId: string): Promise<MapLayer> => {\n const cache = drillDown.districts;\n let pending = cache.get(stateId);\n if (!pending) {\n pending = loadDistrictTopology(dataBaseUrl, stateId, drillDown.controller?.signal);\n // A failed fetch must not be cached, or a retry can never succeed.\n pending.catch(() => cache.delete(stateId));\n cache.set(stateId, pending);\n }\n return withDistrictValues(\n {\n geometry: await pending,\n getId: defaultGetId,\n getLabel: defaultGetLabel,\n getValue: (feature) => lookUp(exactKeysRef.current, valuesRef.current, defaultGetId(feature), defaultGetLabel(feature)),\n },\n stateId,\n );\n };\n // `districtSignature` rebuilds the loader when district values change, which\n // is what makes the map repaint them: the renderer only re-derives a level\n // from a new layer object. The geometry behind it is cached, so this is a\n // repaint, not a refetch.\n }, [dataBaseUrl, districtSignature, districtsEnabled, withDistrictValues]);\n\n const defaultSubDistrictLoader = useMemo(() => {\n if (!subDistrictsEnabled) return undefined;\n return async (districtId: string): Promise<MapLayer | null> => {\n const cache = drillDown.subDistricts;\n let pending = cache.get(districtId);\n if (!pending) {\n pending = loadSubDistrictTopology(dataBaseUrl, districtId, drillDown.controller?.signal);\n pending.catch(() => cache.delete(districtId));\n cache.set(districtId, pending);\n }\n const geometry = await pending;\n // Null means the bundle holds no sub-districts for this district, which\n // leaves it a leaf rather than opening an empty level.\n if (!geometry) return null;\n return {\n geometry,\n getId: defaultGetId,\n getLabel: defaultGetLabel,\n getValue: (feature) => lookUp(exactKeysRef.current, valuesRef.current, defaultGetId(feature), defaultGetLabel(feature)),\n };\n };\n }, [dataBaseUrl, subDistrictsEnabled]);\n\n const callerLoadDistricts = rest.loadDistricts;\n const districtLoader = useMemo(() => {\n if (!callerLoadDistricts) return defaultDistrictLoader;\n return async (stateId: string, state: MapRegion) =>\n withDistrictValues(await callerLoadDistricts(stateId, state), stateId);\n }, [callerLoadDistricts, defaultDistrictLoader, districtSignature, withDistrictValues]);\n\n if (error) {\n return (\n <div className=\"bharat-choropleth__status bharat-choropleth__status--error\" role=\"alert\">\n {error.message}\n </div>\n );\n }\n\n if (!statesLayer) {\n return (\n <div className=\"bharat-choropleth__status\" role=\"status\">\n Loading map…\n </div>\n );\n }\n\n return (\n <IndiaChoropleth\n {...rest}\n states={statesLayer}\n loadDistricts={districtLoader}\n loadSubDistricts={rest.loadSubDistricts ?? defaultSubDistrictLoader}\n />\n );\n}\n","import type { GeometrySource, MapFeatureCollection } from \"./types\";\nimport type { Topology } from \"topojson-specification\";\n\n/**\n * Where the optional prepared boundary bundles are fetched from when the caller\n * doesn't supply their own `geometry`.\n *\n * The files are *fetched*, never bundled — the package still ships no boundary\n * geometry, and each asset keeps its own source's licence and attribution\n * alongside it (see `data/ATTRIBUTION.md`). The default bundle is\n * `datta07/INDIAN-SHAPEFILES` (MIT):\n *\n * > State/UT boundaries derived from datta07/INDIAN-SHAPEFILES (MIT).\n *\n * Self-host by copying `data/generated/` next to your app and passing\n * `dataBaseUrl: \"/maps\"`, which is the right call for offline or air-gapped\n * deployments and avoids a third-party CDN request at runtime.\n *\n * The ref is pinned to an immutable tag on purpose. A branch ref (`@main`) is\n * mutable and cached by jsDelivr for hours, so a data change would silently\n * alter — or break — every consumer's map at a time nobody chose. Bump this\n * deliberately, alongside a release.\n *\n * Bump it *in* the release, and check one asset of every level actually resolves\n * at the new tag. Leaving it behind does not fail loudly: a level whose files\n * the pinned tag predates 404s, and `loadSubDistrictTopology` reads a 404 as\n * \"this district has no sub-districts\", so the whole level just quietly goes\n * missing. That is exactly what 0.2.0 shipped with.\n */\nexport const DEFAULT_DATA_BASE_URL = \"https://cdn.jsdelivr.net/gh/shashankbudem/bharat-choropleth@v0.3.0/data/generated\";\n\nexport const ATTRIBUTION = \"State/UT, district and sub-district boundaries derived from datta07/INDIAN-SHAPEFILES (MIT).\";\n\n/** Anything `geometry` accepts: inline data, a URL to fetch, or a promise of either. */\nexport type GeometryInput = GeometrySource | string | Promise<GeometrySource | Topology | MapFeatureCollection>;\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport function statesUrl(baseUrl: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-states/states.topo.json`;\n}\n\nexport function districtsUrl(baseUrl: string, stateId: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-districts/districts/${stateId}.topo.json`;\n}\n\nexport function subDistrictsUrl(baseUrl: string, districtId: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-subdistricts/subdistricts/${districtId}.topo.json`;\n}\n\nasync function fetchJson(url: string, signal?: AbortSignal): Promise<unknown> {\n if (typeof fetch !== \"function\") {\n throw new Error(\n \"BharatChoropleth: no global fetch is available, so boundary data cannot be downloaded. Pass `geometry` with data you loaded yourself.\",\n );\n }\n const response = await fetch(url, { signal });\n if (!response.ok) {\n throw new Error(\n `BharatChoropleth: failed to load boundary data from ${url} (HTTP ${response.status}). ` +\n \"Set `dataBaseUrl` to your own copy of data/generated, or pass `geometry` directly.\",\n );\n }\n return response.json();\n}\n\n/**\n * Normalize a parsed payload into a `GeometrySource`. Accepts a raw TopoJSON\n * topology (the shape our own bundles have — the named object is picked for the\n * caller), a `{ topology, object }` pair, or a plain GeoJSON FeatureCollection.\n */\nexport function toGeometrySource(payload: unknown, preferredObject: string): GeometrySource {\n if (!payload || typeof payload !== \"object\") {\n throw new Error(\"BharatChoropleth: boundary data must be TopoJSON or a GeoJSON FeatureCollection.\");\n }\n const candidate = payload as Record<string, unknown>;\n\n if (candidate.type === \"FeatureCollection\") return payload as MapFeatureCollection;\n if (\"topology\" in candidate && \"object\" in candidate) return payload as GeometrySource;\n\n if (candidate.type === \"Topology\" && candidate.objects && typeof candidate.objects === \"object\") {\n const objects = candidate.objects as Record<string, unknown>;\n const object = preferredObject in objects ? preferredObject : Object.keys(objects)[0];\n if (!object) throw new Error(\"BharatChoropleth: the TopoJSON topology contains no objects.\");\n return { topology: payload as Topology, object };\n }\n\n throw new Error(\"BharatChoropleth: boundary data must be TopoJSON or a GeoJSON FeatureCollection.\");\n}\n\n/** Resolve whichever form of `geometry` the caller passed into usable geometry. */\nexport async function resolveGeometry(\n input: GeometryInput,\n preferredObject: string,\n signal?: AbortSignal,\n): Promise<GeometrySource> {\n if (typeof input === \"string\") return toGeometrySource(await fetchJson(input, signal), preferredObject);\n if (input instanceof Promise) return toGeometrySource(await input, preferredObject);\n return input;\n}\n\n/** True for geometry that is already in hand, so the map can render synchronously. */\nexport function isInlineGeometry(input: GeometryInput | undefined): input is GeometrySource {\n return typeof input === \"object\" && input !== null && !(input instanceof Promise);\n}\n\nexport async function loadDistrictTopology(\n baseUrl: string,\n stateId: string,\n signal?: AbortSignal,\n): Promise<GeometrySource> {\n return toGeometrySource(await fetchJson(districtsUrl(baseUrl, stateId), signal), \"districts\");\n}\n\n/**\n * Sub-districts for one district, or `null` where the bundle has no file for it.\n *\n * A missing file is the bundle's way of saying a district has no sub-district\n * level — three of the 788 current districts are in that position, and the\n * prepared bundle deliberately ships no asset for them. So a 404 resolves to\n * `null` (the district is a leaf) rather than raising, while any other failure\n * still surfaces as an error the map can report.\n */\nexport async function loadSubDistrictTopology(\n baseUrl: string,\n districtId: string,\n signal?: AbortSignal,\n): Promise<GeometrySource | null> {\n const url = subDistrictsUrl(baseUrl, districtId);\n if (typeof fetch !== \"function\") {\n throw new Error(\n \"BharatChoropleth: no global fetch is available, so boundary data cannot be downloaded. Pass `geometry` with data you loaded yourself.\",\n );\n }\n const response = await fetch(url, { signal });\n if (response.status === 404) return null;\n if (!response.ok) {\n throw new Error(\n `BharatChoropleth: failed to load boundary data from ${url} (HTTP ${response.status}). ` +\n \"Set `dataBaseUrl` to your own copy of data/generated, or pass `geometry` directly.\",\n );\n }\n return toGeometrySource(await response.json(), \"subdistricts\");\n}\n","/**\n * Static registry of the 36 current state/UT identities: id, display name, slug.\n *\n * This is *metadata only* — no coordinates, no boundary geometry. It exists so\n * `map.goa = 6` can be recognized, validated and warned about the instant the\n * script runs, before the (asynchronously fetched) boundary file has landed.\n * Boundary geometry itself still never ships inside this package.\n *\n * Kept in sync with `data/generated/current-2019-states/manifest.json`; ids are\n * LGD-derived and match the `districts/{stateId}.topo.json` filenames.\n */\nexport interface StateIdentity {\n /** LGD-derived stable id, e.g. `in-cs-30-goa`. */\n id: string;\n /** Display name as it appears in the boundary data, e.g. `Jammu & Kashmir`. */\n name: string;\n /** Hyphenated slug as it appears in the boundary data, e.g. `jammu-and-kashmir`. */\n slug: string;\n}\n\nexport const STATES: readonly StateIdentity[] = [\n { id: \"in-cs-01-jammu-and-kashmir\", name: \"Jammu & Kashmir\", slug: \"jammu-and-kashmir\" },\n { id: \"in-cs-02-himachal-pradesh\", name: \"Himachal Pradesh\", slug: \"himachal-pradesh\" },\n { id: \"in-cs-03-punjab\", name: \"Punjab\", slug: \"punjab\" },\n { id: \"in-cs-04-chandigarh\", name: \"Chandigarh\", slug: \"chandigarh\" },\n { id: \"in-cs-05-uttarakhand\", name: \"Uttarakhand\", slug: \"uttarakhand\" },\n { id: \"in-cs-06-haryana\", name: \"Haryana\", slug: \"haryana\" },\n { id: \"in-cs-07-delhi\", name: \"Delhi\", slug: \"delhi\" },\n { id: \"in-cs-08-rajasthan\", name: \"Rajasthan\", slug: \"rajasthan\" },\n { id: \"in-cs-09-uttar-pradesh\", name: \"Uttar Pradesh\", slug: \"uttar-pradesh\" },\n { id: \"in-cs-10-bihar\", name: \"Bihar\", slug: \"bihar\" },\n { id: \"in-cs-11-sikkim\", name: \"Sikkim\", slug: \"sikkim\" },\n { id: \"in-cs-12-arunachal-pradesh\", name: \"Arunachal Pradesh\", slug: \"arunachal-pradesh\" },\n { id: \"in-cs-13-nagaland\", name: \"Nagaland\", slug: \"nagaland\" },\n { id: \"in-cs-14-manipur\", name: \"Manipur\", slug: \"manipur\" },\n { id: \"in-cs-15-mizoram\", name: \"Mizoram\", slug: \"mizoram\" },\n { id: \"in-cs-16-tripura\", name: \"Tripura\", slug: \"tripura\" },\n { id: \"in-cs-17-meghalaya\", name: \"Meghalaya\", slug: \"meghalaya\" },\n { id: \"in-cs-18-assam\", name: \"Assam\", slug: \"assam\" },\n { id: \"in-cs-19-west-bengal\", name: \"West Bengal\", slug: \"west-bengal\" },\n { id: \"in-cs-20-jharkhand\", name: \"Jharkhand\", slug: \"jharkhand\" },\n { id: \"in-cs-21-odisha\", name: \"Odisha\", slug: \"odisha\" },\n { id: \"in-cs-22-chhattisgarh\", name: \"Chhattisgarh\", slug: \"chhattisgarh\" },\n { id: \"in-cs-23-madhya-pradesh\", name: \"Madhya Pradesh\", slug: \"madhya-pradesh\" },\n { id: \"in-cs-24-gujarat\", name: \"Gujarat\", slug: \"gujarat\" },\n { id: \"in-cs-26-dadra-and-nagar-haveli-and-daman-and-diu\", name: \"Dadra and Nagar Haveli and Daman and Diu\", slug: \"dadra-and-nagar-haveli-and-daman-and-diu\" },\n { id: \"in-cs-27-maharashtra\", name: \"Maharashtra\", slug: \"maharashtra\" },\n { id: \"in-cs-28-andhra-pradesh\", name: \"Andhra Pradesh\", slug: \"andhra-pradesh\" },\n { id: \"in-cs-29-karnataka\", name: \"Karnataka\", slug: \"karnataka\" },\n { id: \"in-cs-30-goa\", name: \"Goa\", slug: \"goa\" },\n { id: \"in-cs-31-lakshadweep\", name: \"Lakshadweep\", slug: \"lakshadweep\" },\n { id: \"in-cs-32-kerala\", name: \"Kerala\", slug: \"kerala\" },\n { id: \"in-cs-33-tamil-nadu\", name: \"Tamil Nadu\", slug: \"tamil-nadu\" },\n { id: \"in-cs-34-puducherry\", name: \"Puducherry\", slug: \"puducherry\" },\n { id: \"in-cs-35-andaman-and-nicobar\", name: \"Andaman & Nicobar\", slug: \"andaman-and-nicobar\" },\n { id: \"in-cs-36-telangana\", name: \"Telangana\", slug: \"telangana\" },\n { id: \"in-cs-37-ladakh\", name: \"Ladakh\", slug: \"ladakh\" },\n];\n\n/**\n * Former / colloquial / commonly-typed names, mapped to the canonical slug.\n * `map.orissa = 4` should not be a silent typo for someone who learned the\n * older name — it should just work.\n */\nconst ALIASES: Readonly<Record<string, string>> = {\n orissa: \"odisha\",\n pondicherry: \"puducherry\",\n uttaranchal: \"uttarakhand\",\n \"nct-of-delhi\": \"delhi\",\n \"new-delhi\": \"delhi\",\n \"delhi-nct\": \"delhi\",\n \"jammu-kashmir\": \"jammu-and-kashmir\",\n \"j-and-k\": \"jammu-and-kashmir\",\n jk: \"jammu-and-kashmir\",\n \"andaman-nicobar\": \"andaman-and-nicobar\",\n \"andaman-and-nicobar-islands\": \"andaman-and-nicobar\",\n \"dadra-and-nagar-haveli\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n \"daman-and-diu\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n dnhdd: \"dadra-and-nagar-haveli-and-daman-and-diu\",\n \"nagar-haveli\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n};\n\n/**\n * Canonical key for any user-supplied spelling: lowercase, `&` → `and`, every\n * run of non-alphanumerics → a single `-`. So `Tamil Nadu`, `tamil_nadu`,\n * `TAMIL-NADU` and `tamil nadu` all collapse to `tamil-nadu`.\n */\nexport function normalizeStateKey(input: string): string {\n return input\n .toLowerCase()\n .replace(/&/g, \" and \")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/** Separator-free form, so `tamilnadu` / `uttarpradesh` / `westbengal` also resolve. */\nfunction compact(key: string): string {\n return key.replace(/-/g, \"\");\n}\n\nconst BY_KEY = new Map<string, StateIdentity>();\nconst BY_COMPACT = new Map<string, StateIdentity>();\n\nfor (const state of STATES) {\n for (const key of [state.slug, normalizeStateKey(state.name), state.id]) {\n if (!BY_KEY.has(key)) BY_KEY.set(key, state);\n const compacted = compact(key);\n if (!BY_COMPACT.has(compacted)) BY_COMPACT.set(compacted, state);\n }\n}\nfor (const [alias, slug] of Object.entries(ALIASES)) {\n const state = BY_KEY.get(slug);\n if (!state) continue; // unreachable while ALIASES stays in sync with STATES\n if (!BY_KEY.has(alias)) BY_KEY.set(alias, state);\n const compacted = compact(alias);\n if (!BY_COMPACT.has(compacted)) BY_COMPACT.set(compacted, state);\n}\n\n/**\n * Resolve any spelling of a state/UT — display name, slug, LGD id, underscore\n * form, alias, or separator-free form — to its canonical identity.\n * Returns `undefined` for anything unrecognized, which callers treat as a typo.\n */\nexport function resolveState(input: string): StateIdentity | undefined {\n const key = normalizeStateKey(input);\n return BY_KEY.get(key) ?? BY_COMPACT.get(compact(key));\n}\n"],"mappings":"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","names":["useCallback","useState","area","useState","useCallback","useCallback","useEffect","useMemo","useRef","useState","jsx","useMemo","valueMap","exactKeys","writtenAs","districts","useRef","useState","useEffect","useCallback","geometry"]}
|
|
1
|
+
{"version":3,"sources":["../src/IndiaChoropleth.tsx","../src/geometry.ts","../src/legend.ts","../src/tooltip-position.ts","../src/small-regions.ts","../src/useControllableState.ts","../src/BharatChoropleth.tsx","../src/data-source.ts","../src/states.ts"],"sourcesContent":["import { geoMercator, geoPath, type GeoProjection } from \"d3-geo\";\nimport { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type MouseEvent as ReactMouseEvent } from \"react\";\nimport { asFeatureCollection, totalOf } from \"./geometry\";\nimport { legendBucketLabel, legendBuckets, swatchIndexOf } from \"./legend\";\nimport { placeTooltip } from \"./tooltip-position\";\nimport {\n boundsOfRing,\n distanceToParts,\n keepsTrueGeometry,\n labelPointFor,\n enlargeSmallParts,\n largestRingExtent,\n placeOutsideLabel,\n ringsToPath,\n scatteredHitArea,\n type Box,\n type Point,\n} from \"./small-regions\";\nimport type {\n ColorContext,\n ColorScale,\n IndiaChoroplethProps,\n InsightContext,\n MapFeature,\n MapFeatureCollection,\n MapLayer,\n MapLevel,\n MapRegion,\n ReferenceOverlay,\n TooltipContext,\n} from \"./types\";\nimport { useControllableState } from \"./useControllableState\";\n\nconst VIEWBOX = { width: 960, height: 640, padding: 28 };\nconst DEFAULT_COLORS = [\"#d9f1ed\", \"#b9e3dd\", \"#8fd1c8\", \"#5bb9ae\", \"#2f9c90\", \"#147b71\", \"#075b55\"] as const;\nconst DEFAULT_FORMAT = new Intl.NumberFormat(\"en-IN\").format;\n// Space between the region centroid and the tooltip edge. Mirrors the .75rem in style.css.\nconst TOOLTIP_GAP_PX = 12;\n// Small-region handling, in view-box units. Mirrors the DOM and Dart renderers.\nconst SMALL_REGION_EXTENT = 22;\nconst SMALL_REGION_CLICK_RADIUS = 14;\nconst MIN_REGION_MARKER_SIZE = 7;\n\ntype PreparedRegion = MapRegion & {\n path: string;\n /**\n * Hull covering a scattered region's parts and the space between them, drawn\n * invisibly under every outline so hovering the water inside Lakshadweep\n * reaches Lakshadweep. Null for regions that are one part or big enough to\n * point at directly.\n */\n hitPath: string | null;\n centroid: [number, number];\n /** Bounding box of each separate part, for measuring how close a click landed. */\n partBounds: Box[];\n /** Longest side of the largest part — the measure of \"too small to use\". */\n extent: number;\n};\n\n/**\n * Project a feature's rings into view-box coordinates. Needed alongside the SVG\n * path string because the small-region helpers work on coordinates, and a path\n * string cannot be measured per part.\n */\nfunction projectedRings(feature: MapFeature, projection: GeoProjection): Point[][] {\n const geometry = feature.geometry;\n if (!geometry) return [];\n const polygons =\n geometry.type === \"Polygon\" ? [geometry.coordinates]\n : geometry.type === \"MultiPolygon\" ? geometry.coordinates\n : [];\n const rings: Point[][] = [];\n for (const polygon of polygons) {\n for (const ring of polygon) {\n const projected: Point[] = [];\n for (const position of ring) {\n const point = projection(position as [number, number]);\n if (point && Number.isFinite(point[0]) && Number.isFinite(point[1])) projected.push([point[0], point[1]]);\n }\n if (projected.length > 0) rings.push(projected);\n }\n }\n return rings;\n}\ntype PreparedReferenceOverlay = { id: string; label: string; description: string; path: string };\n\nfunction colorFor(value: number | null, region: MapRegion, min: number, max: number, scale: ColorScale): string {\n if (typeof scale === \"function\") {\n const context: ColorContext = { min, max, feature: region.feature, id: region.id };\n return scale(value, context);\n }\n // The same index the legend filters by, deliberately: \"highlight the regions\n // painted in this colour\" has to be true by construction, not by two formulas\n // that happen to agree until one of them is tweaked.\n const index = swatchIndexOf(value, min, max, scale.length);\n return index === null ? \"var(--india-map-empty)\" : scale[index] ?? \"var(--india-map-empty)\";\n}\n\nfunction makeProjection(collection: MapFeatureCollection): GeoProjection {\n return geoMercator().fitExtent(\n [[VIEWBOX.padding, VIEWBOX.padding], [VIEWBOX.width - VIEWBOX.padding, VIEWBOX.height - VIEWBOX.padding]],\n collection,\n );\n}\n\n/**\n * `collection` lets a caller that has already unpacked this layer's geometry\n * hand the features straight in. Preparing a layer is how values reach the\n * screen, so it re-runs on every value change — unpacking the same topology\n * again each time is work whose answer cannot have changed.\n */\nfunction prepareLayer(\n layer: MapLayer,\n projection = makeProjection(asFeatureCollection(layer.geometry)),\n minPartExtent = 0,\n collection: MapFeatureCollection = asFeatureCollection(layer.geometry),\n): PreparedRegion[] {\n const path = geoPath(projection);\n return collection.features.map((feature) => {\n const centroid = path.centroid(feature) as [number, number];\n const bounds = path.bounds(feature);\n const fallbackCentroid: [number, number] = [\n (bounds[0][0] + bounds[1][0]) / 2,\n (bounds[0][1] + bounds[1][1]) / 2,\n ];\n const region: MapRegion = {\n id: layer.getId(feature),\n label: layer.getLabel(feature),\n value: layer.getValue(feature),\n meta: layer.getMeta?.(feature),\n feature,\n };\n // Puducherry and anything else on the keep-true list is drawn as it really\n // is, however small, because there is no room around it to grow into.\n const exaggerate = minPartExtent > 0 && !keepsTrueGeometry(region.id);\n const rings = exaggerate\n ? enlargeSmallParts(projectedRings(feature, projection), minPartExtent)\n : projectedRings(feature, projection);\n const fallback: [number, number] = centroid.every(Number.isFinite) ? centroid : fallbackCentroid;\n const hull = scatteredHitArea(rings, SMALL_REGION_EXTENT);\n // With exaggeration on, the drawn outline has to come from the moved rings\n // rather than d3's path generator, so what is drawn, measured, labelled and\n // clicked are all the same geometry.\n return {\n ...region,\n path: exaggerate ? ringsToPath(rings) : (path(feature) ?? \"\"),\n hitPath: hull ? ringsToPath([hull]) : null,\n // The largest part's centroid, not the whole feature's: averaging across\n // parts puts an island group's label out at sea between its islands.\n centroid: rings.length > 0 ? (labelPointFor(rings, fallback) as [number, number]) : fallback,\n partBounds: rings.map(boundsOfRing),\n extent: largestRingExtent(rings),\n };\n });\n}\n\nfunction prepareReferenceOverlay(overlay: ReferenceOverlay, projection: GeoProjection): PreparedReferenceOverlay[] {\n const path = geoPath(projection);\n return asFeatureCollection(overlay.geometry).features.map((feature) => ({\n id: overlay.getId(feature),\n label: overlay.getLabel(feature),\n description: overlay.getDescription(feature),\n path: path(feature) ?? \"\",\n }));\n}\n\nfunction ordinal(n: number): string {\n const lastTwo = n % 100;\n if (lastTwo >= 11 && lastTwo <= 13) return `${n}th`;\n return `${n}${[\"th\", \"st\", \"nd\", \"rd\"][n % 10] ?? \"th\"}`;\n}\n\nfunction defaultTooltip(context: TooltipContext, formatValue: (value: number) => string) {\n return (\n <>\n <strong>{context.label}</strong>\n {/* Same wording as the region's own aria-label, so the two never disagree. */}\n <b>{context.value === null ? \"No data\" : formatValue(context.value)}</b>\n {context.share !== null ? (\n <>\n {/* A bar makes the share readable at a glance; it repeats the number\n beside it, so it's decorative and hidden from assistive tech. */}\n <span className=\"india-choropleth__tooltip-bar\" aria-hidden=\"true\">\n <span style={{ width: `${Math.max(context.share, 1.5)}%` }} />\n </span>\n <small>\n {[\n `${context.share.toFixed(1)}% of total`,\n ...(context.rank !== null ? [`${ordinal(context.rank)} of ${context.rankedCount}`] : []),\n ].join(\" · \")}\n </small>\n </>\n ) : null}\n </>\n );\n}\n\n/**\n * A data-agnostic, accessible SVG India map renderer. Import `@india-choropleth/react/style.css`\n * once in the host app; data and boundaries intentionally remain separate.\n */\n/**\n * Warns when a lazy loader is being recreated on every render.\n *\n * The loading effects list their loader in their dependencies because a\n * genuinely different loader — a different boundary edition, a different\n * reporting year — must refetch. An inline arrow is also a new function every\n * render, and from in here the two look identical: in both cases the loader is\n * the only dependency that moved.\n *\n * What separates them is *density*. An inline arrow changes on consecutive\n * renders, because every render makes one. A memoized loader whose dependency\n * changed — a dashboard swapping the displayed metric — changes once, then not\n * again until the reader does something, which is many renders later. So the\n * test is three changes inside a short window of renders, not three changes.\n *\n * That distinction is the whole value of the check: this repo's own demo swaps a\n * correctly-memoized loader whenever its metric changes, and a warning that\n * fired on that would be noise, and would train people to ignore it.\n *\n * The cost of the real mistake is invisible — the level silently refetches over\n * the network on every unrelated re-render while rendering perfectly correctly —\n * which is why it needs saying at all.\n */\nconst LOADER_CHURN_CHANGES = 3;\n/** Renders those changes must fall within. Generous, because StrictMode renders twice. */\nconst LOADER_CHURN_WINDOW = 6;\n\nfunction useStableLoaderWarning(propName: string, loader: unknown, levelId: string | null) {\n const renders = useRef(0);\n renders.current += 1;\n const seen = useRef<{ loader: unknown; levelId: string | null; changedAt: number[]; warned: boolean }>({\n loader,\n levelId,\n changedAt: [],\n warned: false,\n });\n useEffect(() => {\n const state = seen.current;\n if (loader === state.loader) return;\n state.loader = loader;\n // A different level is a different question; start counting again.\n if (levelId !== state.levelId) {\n state.levelId = levelId;\n state.changedAt = [];\n return;\n }\n state.changedAt = [...state.changedAt, renders.current].slice(-LOADER_CHURN_CHANGES);\n const [first] = state.changedAt;\n const dense =\n state.changedAt.length === LOADER_CHURN_CHANGES && renders.current - (first ?? 0) <= LOADER_CHURN_WINDOW;\n if (loader && dense && !state.warned) {\n state.warned = true;\n console.warn(\n `IndiaChoropleth: \\`${propName}\\` has been a different function on ${LOADER_CHURN_CHANGES} renders in a row ` +\n \"while the level it loads stayed the same, so that level has been fetched again each time. Wrap it in \" +\n \"`useCallback` or hoist it out of the component — an inline arrow is a new function on every render, and the \" +\n \"renderer cannot tell that apart from a deliberately different loader.\",\n );\n }\n }, [levelId, loader, propName]);\n}\n\nexport function IndiaChoropleth({\n states,\n referenceOverlay,\n loadDistricts,\n loadSubDistricts,\n loadDistrictReferenceOverlay,\n drillDownId,\n defaultDrillDownId = null,\n onDrillDownChange,\n subDistrictDrillDownId,\n defaultSubDistrictDrillDownId = null,\n onSubDistrictDrillDownChange,\n selectedId,\n defaultSelectedId = null,\n onSelectedChange,\n onInspect,\n onInsight,\n onRegionClick,\n onBackgroundClick,\n colorScale = DEFAULT_COLORS,\n formatValue = DEFAULT_FORMAT,\n renderTooltip,\n renderInsights,\n showLegend = true,\n showBreadcrumb = true,\n legendLabels = [\"Lower\", \"Higher\"],\n referenceOverlayLegendLabel = \"Reference context · data unavailable\",\n referenceOverlayMergeIds = [],\n referenceOverlayFill = \"hatch\",\n showRegionValues = false,\n minPartExtent = 0,\n minDistrictPartExtent,\n className,\n ariaLabel = \"Interactive choropleth map\",\n interactive = true,\n}: IndiaChoroplethProps) {\n const tooltipId = useId();\n const canvasRef = useRef<HTMLDivElement | null>(null);\n const tooltipAnchorRef = useRef<HTMLDivElement | null>(null);\n const hatchId = `${useId()}-reference-hatch`;\n const [activeDrillDownId, setActiveDrillDownId] = useControllableState(drillDownId, defaultDrillDownId);\n const [activeSubDrillDownId, setActiveSubDrillDownId] = useControllableState(subDistrictDrillDownId, defaultSubDistrictDrillDownId);\n const [activeSelectedId, setActiveSelectedId] = useControllableState(selectedId, defaultSelectedId);\n const [loadedDistricts, setLoadedDistricts] = useState<{ stateId: string; layer: MapLayer } | null>(null);\n const [loadedSubDistricts, setLoadedSubDistricts] = useState<{ districtId: string; layer: MapLayer } | null>(null);\n /**\n * Districts the loader has already answered `null` for. The renderer cannot know\n * which districts are leaves without asking, so the first activation asks — but\n * after that the region should stop announcing a level it will not open. Held as\n * state rather than a ref so learning it repaints the label, and cleared when the\n * loader changes, since a different source may well have sub-districts for them.\n */\n const [leafDistrictIds, setLeafDistrictIds] = useState<ReadonlySet<string>>(() => new Set());\n const priorSubDistrictLoader = useRef(loadSubDistricts);\n useEffect(() => {\n if (priorSubDistrictLoader.current === loadSubDistricts) return;\n priorSubDistrictLoader.current = loadSubDistricts;\n setLeafDistrictIds(new Set());\n }, [loadSubDistricts]);\n const [loadedDistrictReferenceOverlay, setLoadedDistrictReferenceOverlay] = useState<{ stateId: string; overlay: ReferenceOverlay | null } | null>(null);\n const [loadingState, setLoadingState] = useState<string | null>(null);\n const [loadingDistrict, setLoadingDistrict] = useState<string | null>(null);\n const [loadError, setLoadError] = useState<Error | null>(null);\n const [subLoadError, setSubLoadError] = useState<Error | null>(null);\n const pathRefs = useRef<Record<string, SVGPathElement | null>>({});\n const restoreFocusId = useRef<string | null>(null);\n /**\n * The level a drill started from, so focus can land on the level it arrives at.\n *\n * Going back has an obvious target — the region stepped out of — and going in\n * does not: the activated region is no longer on screen. Without one, focus\n * fell to <body> on every drill-in, dropping a keyboard user at the top of the\n * document and telling a screen reader nothing about where they now are. It\n * holds the departing level rather than a flag, so a district that turns out\n * to be a leaf and steps straight back out does not move focus at all.\n */\n const focusFirstFromLevel = useRef<string | null>(null);\n /** The \"nothing here\" message, so a drill into an empty level can land on it. */\n const emptyLevelRef = useRef<HTMLDivElement | null>(null);\n\n // Keyed on the geometry, not the layer: a caller that repaints by handing over\n // a new `MapLayer` with the same geometry — which is how values change — gets\n // the same decoded features and the same projection back, instead of paying to\n // unpack the topology and refit the projection for numbers that moved.\n const stateCollection = useMemo(() => asFeatureCollection(states.geometry), [states.geometry]);\n const referenceCollection = useMemo(\n () => referenceOverlay ? asFeatureCollection(referenceOverlay.geometry) : null,\n [referenceOverlay],\n );\n const nationalProjection = useMemo(\n () => makeProjection({ type: \"FeatureCollection\", features: [...stateCollection.features, ...(referenceCollection?.features ?? [])] }),\n [referenceCollection, stateCollection],\n );\n const stateRegions = useMemo(\n () => prepareLayer(states, nationalProjection, minPartExtent, stateCollection),\n [minPartExtent, nationalProjection, stateCollection, states],\n );\n const referenceRegions = useMemo(\n () => referenceOverlay ? prepareReferenceOverlay(referenceOverlay, nationalProjection) : [],\n [nationalProjection, referenceOverlay],\n );\n const drilledState = useMemo(\n () => stateRegions.find((region) => region.id === activeDrillDownId) ?? null,\n [activeDrillDownId, stateRegions],\n );\n /**\n * Prepared regions bake in each region's value, so they are a new array\n * whenever any number changes. The lazy-loading effects below need the current\n * region to hand to a loader, but must not re-run just because a value moved:\n * re-running calls the loader again and clears the level while the promise is\n * in flight, so a map whose data updates on a timer would blink its districts\n * away on every tick. They read regions through these refs and depend on the\n * geometry and id accessor instead — the things that actually decide which\n * regions exist and what they are called.\n */\n const stateRegionsRef = useRef(stateRegions);\n stateRegionsRef.current = stateRegions;\n useStableLoaderWarning(\"loadDistricts\", loadDistricts, activeDrillDownId);\n useStableLoaderWarning(\"loadSubDistricts\", loadSubDistricts, activeSubDrillDownId);\n const isDrillRequested = Boolean(drilledState && activeDrillDownId);\n const districtLayer = loadedDistricts?.stateId === activeDrillDownId ? loadedDistricts.layer : null;\n const districtReferenceOverlay = loadedDistrictReferenceOverlay?.stateId === activeDrillDownId\n ? loadedDistrictReferenceOverlay.overlay\n : null;\n const districtCollection = useMemo(\n () => districtLayer ? asFeatureCollection(districtLayer.geometry) : null,\n [districtLayer],\n );\n const districtReferenceCollection = useMemo(\n () => districtReferenceOverlay ? asFeatureCollection(districtReferenceOverlay.geometry) : null,\n [districtReferenceOverlay],\n );\n const districtProjection = useMemo(\n () => districtCollection ? makeProjection({ type: \"FeatureCollection\", features: [...districtCollection.features, ...(districtReferenceCollection?.features ?? [])] }) : null,\n [districtCollection, districtReferenceCollection],\n );\n // Districts are prepared whenever their layer is loaded rather than only while\n // they are the visible level, because the district below them has to be\n // resolvable — by id, for the breadcrumb and for the loader — from one level down.\n const districtRegions = useMemo(\n () => districtLayer && districtProjection\n ? prepareLayer(districtLayer, districtProjection, minDistrictPartExtent ?? minPartExtent, districtCollection ?? undefined)\n : [],\n [districtCollection, districtLayer, districtProjection, minDistrictPartExtent, minPartExtent],\n );\n const drilledDistrict = useMemo(\n () => districtRegions.find((region) => region.id === activeSubDrillDownId) ?? null,\n [activeSubDrillDownId, districtRegions],\n );\n // As above, one level down.\n const districtRegionsRef = useRef(districtRegions);\n districtRegionsRef.current = districtRegions;\n const isSubDrillRequested = Boolean(isDrillRequested && drilledDistrict && activeSubDrillDownId);\n const level: MapLevel = isSubDrillRequested ? \"subdistrict\" : isDrillRequested ? \"district\" : \"state\";\n\n const subDistrictLayer = loadedSubDistricts?.districtId === activeSubDrillDownId ? loadedSubDistricts.layer : null;\n const subDistrictCollection = useMemo(\n () => subDistrictLayer ? asFeatureCollection(subDistrictLayer.geometry) : null,\n [subDistrictLayer],\n );\n const subDistrictProjection = useMemo(\n () => subDistrictCollection ? makeProjection(subDistrictCollection) : null,\n [subDistrictCollection],\n );\n // Sub-districts share the district knob rather than adding a fourth: they are\n // drawn at the same zoom as districts and want the same small-part treatment.\n const subDistrictRegions = useMemo(\n () => subDistrictLayer && subDistrictProjection\n ? prepareLayer(subDistrictLayer, subDistrictProjection, minDistrictPartExtent ?? minPartExtent)\n : [],\n [minDistrictPartExtent, minPartExtent, subDistrictLayer, subDistrictProjection],\n );\n\n const regions = level === \"subdistrict\" ? subDistrictRegions : level === \"district\" ? districtRegions : stateRegions;\n const districtReferenceRegions = useMemo(\n () => level === \"district\" && districtReferenceOverlay && districtProjection ? prepareReferenceOverlay(districtReferenceOverlay, districtProjection) : [],\n [districtProjection, districtReferenceOverlay, level],\n );\n const selected = regions.find((region) => region.id === activeSelectedId) ?? null;\n const [inspectedId, setInspectedId] = useState<string | null>(null);\n const inspectedIdRef = useRef<string | null>(null);\n // Hover/focus only — no fallback to `selected`, so the floating tooltip clears\n // when the pointer/focus leaves instead of sticking on the selected region.\n const inspected = regions.find((region) => region.id === inspectedId) ?? null;\n\n const values = useMemo(() => regions.map((region) => region.value).filter((value): value is number => value !== null), [regions]);\n const total = useMemo(() => totalOf(values), [values]);\n const min = values.length ? Math.min(...values) : 0;\n const max = values.length ? Math.max(...values) : 0;\n\n // The legend doubles as a filter. A function colour scale has no swatches of\n // its own, so the default ramp stands in and the bands still read low to high.\n const legendColors = typeof colorScale === \"function\" ? DEFAULT_COLORS : colorScale;\n const buckets = useMemo(\n () => legendBuckets(legendColors, regions.map((region) => region.value), min, max),\n [legendColors, max, min, regions],\n );\n const [activeBucket, setActiveBucket] = useState<number | null>(null);\n // Clamped here rather than only in the effect below: effects run after paint,\n // so a stale index would dim against the previous level's bands for a frame.\n const filterBucket = activeBucket !== null && activeBucket < buckets.length ? activeBucket : null;\n // Keyed on the ramp's contents, not its identity — a host passing an inline\n // array literal would otherwise clear the filter on every re-render.\n const colorScaleKey = typeof colorScale === \"function\" ? \"function\" : colorScale.join(\",\");\n // Bands come from this level's own min and max, and change with the ramp, so an\n // index picked under one of them means something else under another.\n useEffect(() => { setActiveBucket(null); }, [activeDrillDownId, activeSubDrillDownId, colorScaleKey, level]);\n\n const highlighted = useMemo(\n () => filterBucket === null\n ? null\n : new Set(regions\n .filter((region) => swatchIndexOf(region.value, min, max, legendColors.length) === filterBucket)\n .map((region) => region.id)),\n [filterBucket, legendColors.length, max, min, regions],\n );\n // Dimmed regions stay hoverable: the filter is about where the eye goes, and a\n // region you can see is a region whose number should still be reachable.\n const isDimmed = (id: string) => highlighted !== null && !highlighted.has(id);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeDrillDownId || !loadDistricts) {\n setLoadedDistricts(null);\n setLoadingState(null);\n setLoadError(null);\n return;\n }\n const sourceState = stateRegionsRef.current.find((region) => region.id === activeDrillDownId);\n if (!sourceState) return;\n setLoadingState(activeDrillDownId);\n setLoadError(null);\n // Only blank the level when it is a different one. A reload of the state\n // already showing — a swapped loader, say — should leave its districts up\n // until the replacement lands, rather than flashing \"Loading districts…\"\n // over a map the reader is looking at.\n setLoadedDistricts((current) => (current?.stateId === activeDrillDownId ? current : null));\n loadDistricts(activeDrillDownId, sourceState)\n .then((loaded) => { if (!cancelled) setLoadedDistricts({ stateId: activeDrillDownId, layer: loaded }); })\n .catch((error: unknown) => { if (!cancelled) setLoadError(error instanceof Error ? error : new Error(\"Unable to load districts.\")); })\n .finally(() => { if (!cancelled) setLoadingState(null); });\n return () => { cancelled = true; };\n }, [activeDrillDownId, loadDistricts, stateCollection, states.getId]);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeDrillDownId || !loadDistrictReferenceOverlay) {\n setLoadedDistrictReferenceOverlay(null);\n return;\n }\n const sourceState = stateRegionsRef.current.find((region) => region.id === activeDrillDownId);\n if (!sourceState) return;\n setLoadedDistrictReferenceOverlay(null);\n loadDistrictReferenceOverlay(activeDrillDownId, sourceState)\n .then((overlay) => { if (!cancelled) setLoadedDistrictReferenceOverlay({ stateId: activeDrillDownId, overlay }); })\n // Optional reference context must not prevent a usable district data view.\n .catch(() => { if (!cancelled) setLoadedDistrictReferenceOverlay({ stateId: activeDrillDownId, overlay: null }); });\n return () => { cancelled = true; };\n }, [activeDrillDownId, loadDistrictReferenceOverlay, stateCollection, states.getId]);\n\n useEffect(() => {\n let cancelled = false;\n if (!activeSubDrillDownId || !loadSubDistricts) {\n setLoadedSubDistricts(null);\n setLoadingDistrict(null);\n setSubLoadError(null);\n return;\n }\n const sourceDistrict = districtRegionsRef.current.find((region) => region.id === activeSubDrillDownId);\n // The district layer has not arrived yet, so there is nothing to load from.\n // This effect re-runs once it does.\n if (!sourceDistrict || !activeDrillDownId) return;\n setLoadingDistrict(activeSubDrillDownId);\n setSubLoadError(null);\n // As above, one level down.\n setLoadedSubDistricts((current) => (current?.districtId === activeSubDrillDownId ? current : null));\n loadSubDistricts(activeSubDrillDownId, sourceDistrict, activeDrillDownId)\n .then((loaded) => {\n if (cancelled) return;\n if (!loaded) {\n // This district is a leaf. Step back to the district view and leave it\n // selected, rather than opening a level with nothing in it.\n setLeafDistrictIds((known) => known.has(activeSubDrillDownId) ? known : new Set(known).add(activeSubDrillDownId));\n setActiveSubDrillDownId(null);\n // The optimistic drill unmounted the district that was activated, so\n // focus has to be put back on it deliberately — a keyboard user who\n // opened a district with nothing under it should not be dropped.\n restoreFocusId.current = sourceDistrict.id;\n focusFirstFromLevel.current = null;\n setActiveSelectedId(sourceDistrict.id);\n onSubDistrictDrillDownChange?.(null, sourceDistrict);\n return;\n }\n setLoadedSubDistricts({ districtId: activeSubDrillDownId, layer: loaded });\n })\n .catch((error: unknown) => { if (!cancelled) setSubLoadError(error instanceof Error ? error : new Error(\"Unable to load sub-districts.\")); })\n .finally(() => { if (!cancelled) setLoadingDistrict(null); });\n return () => { cancelled = true; };\n // `onSubDistrictDrillDownChange` and the setters are deliberately not\n // dependencies: a host passing an inline callback would otherwise refetch on\n // every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeDrillDownId, activeSubDrillDownId, districtCollection, districtLayer?.getId, loadSubDistricts]);\n\n // A district id only means something inside the state it came from, so leaving\n // or changing the state drops the level below it. Seeded with the mount-time\n // value so an initial state + district pair survives: this must fire on a\n // change, not on arrival.\n const priorDrillDownId = useRef(activeDrillDownId);\n // Mirrors the sub level so the effect below can read it without depending on\n // it, which would re-run this on every sub-district drill.\n const activeSubDrillDownIdRef = useRef(activeSubDrillDownId);\n activeSubDrillDownIdRef.current = activeSubDrillDownId;\n useEffect(() => {\n if (priorDrillDownId.current === activeDrillDownId) return;\n priorDrillDownId.current = activeDrillDownId;\n if (activeSubDrillDownIdRef.current === null) return;\n setActiveSubDrillDownId(null);\n // The host is told, because it cannot see this happen. Dropping the level\n // silently left anything mirroring it pointing at a district of the state\n // just left — the wrong level, reported against the wrong map. There is no\n // prior district to hand back: it belonged to the state that is gone.\n onSubDistrictDrillDownChange?.(null, undefined);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeDrillDownId]);\n\n useEffect(() => {\n const regionId = restoreFocusId.current;\n if (regionId) {\n // Focus is restored onto the region that was just stepped out of, so it waits\n // until the level holding that region is the one being drawn.\n if (!regions.some((region) => region.id === regionId)) return;\n restoreFocusId.current = null;\n focusFirstFromLevel.current = null;\n pathRefs.current[regionId]?.focus();\n return;\n }\n // Drilling in: the level has to have actually changed, so an optimistic drill\n // into a leaf that steps back out leaves focus where the user put it.\n if (!focusFirstFromLevel.current || focusFirstFromLevel.current === level) return;\n const first = regions[0];\n if (first) {\n focusFirstFromLevel.current = null;\n pathRefs.current[first.id]?.focus();\n return;\n }\n // A level can arrive with nothing in it — a state whose districts loaded but\n // hold no regions. It still has to take focus, or the drill leaves a keyboard\n // user at the top of the document with only a live region to explain it. The\n // message is the honest target: it says why there is nothing, and the\n // breadcrumb out of here is its neighbour.\n if (emptyLevelRef.current) {\n focusFirstFromLevel.current = null;\n emptyLevelRef.current.focus();\n }\n }, [level, regions, loadingState, loadingDistrict]);\n\n useEffect(() => {\n if (!inspectedId) return;\n if (!regions.some((region) => region.id === inspectedId)) {\n inspectedIdRef.current = null;\n setInspectedId(null);\n }\n }, [inspectedId, regions]);\n\n // Mirrors `inspectedId` synchronously so a single gesture that exits both a region\n // and the canvas doesn't report the clear twice. Deliberately limited to redundant\n // clears: re-inspecting the same region must still notify, because focus restoration\n // after breadcrumb-back re-inspects the region `goBack` already primed.\n const inspect = (region: PreparedRegion | null) => {\n const nextId = region?.id ?? null;\n if (nextId === null && inspectedIdRef.current === null) return;\n inspectedIdRef.current = nextId;\n setInspectedId(nextId);\n onInspect?.(region ?? null, level);\n };\n\n const activate = (region: PreparedRegion) => {\n onRegionClick?.(region, level);\n setActiveSelectedId(region.id);\n onSelectedChange?.(region, level);\n if (level === \"state\" && loadDistricts) {\n setActiveSelectedId(null);\n focusFirstFromLevel.current = level;\n setActiveDrillDownId(region.id);\n onDrillDownChange?.(region.id, region);\n return;\n }\n // A district is a leaf unless the host offers a level below it. Whether this\n // particular district actually has one is only known once the loader answers,\n // so the drill is entered optimistically and stepped back out if it returns null.\n if (level === \"district\" && loadSubDistricts && !leafDistrictIds.has(region.id)) {\n setActiveSelectedId(null);\n focusFirstFromLevel.current = level;\n setActiveSubDrillDownId(region.id);\n onSubDistrictDrillDownChange?.(region.id, region);\n }\n };\n\n /** Steps up exactly one level, so the breadcrumb and the back button agree. */\n const goBack = () => {\n focusFirstFromLevel.current = null;\n if (level === \"subdistrict\") {\n const priorDistrict = drilledDistrict ?? undefined;\n setActiveSubDrillDownId(null);\n setActiveSelectedId(priorDistrict?.id ?? null);\n inspectedIdRef.current = priorDistrict?.id ?? null;\n setInspectedId(priorDistrict?.id ?? null);\n restoreFocusId.current = priorDistrict?.id ?? null;\n onSubDistrictDrillDownChange?.(null, priorDistrict);\n return;\n }\n const priorState = drilledState ?? undefined;\n setActiveDrillDownId(null);\n setActiveSubDrillDownId(null);\n setActiveSelectedId(priorState?.id ?? null);\n inspectedIdRef.current = priorState?.id ?? null;\n setInspectedId(priorState?.id ?? null);\n restoreFocusId.current = priorState?.id ?? null;\n onDrillDownChange?.(null, priorState);\n };\n\n /** Jumps straight to the national map from any level. */\n const goToStates = () => {\n focusFirstFromLevel.current = null;\n const priorState = drilledState ?? undefined;\n const wasDrilledDistrict = drilledDistrict ?? undefined;\n setActiveSubDrillDownId(null);\n setActiveDrillDownId(null);\n setActiveSelectedId(priorState?.id ?? null);\n inspectedIdRef.current = priorState?.id ?? null;\n setInspectedId(priorState?.id ?? null);\n restoreFocusId.current = priorState?.id ?? null;\n if (wasDrilledDistrict) onSubDistrictDrillDownChange?.(null, wasDrilledDistrict);\n onDrillDownChange?.(null, priorState);\n };\n\n // Shared by the tooltip and the insight panel so the two can never disagree\n // about share or rank — they only differ in which region they describe.\n const toTooltipContext = useCallback((region: PreparedRegion): TooltipContext => {\n const valued = regions.filter((candidate) => candidate.value !== null);\n // Ties share the better rank (\"2nd of 36\" twice, then 4th), which is what a\n // reader expects from a leaderboard and avoids an arbitrary tiebreak.\n const rank = region.value === null\n ? null\n : valued.filter((candidate) => (candidate.value ?? 0) > (region.value ?? 0)).length + 1;\n return {\n ...region,\n level,\n total,\n share: region.value === null || total === 0 ? null : (region.value / total) * 100,\n rank,\n rankedCount: valued.length,\n };\n }, [level, regions, total]);\n\n const tooltipContext = useMemo<TooltipContext | null>(\n () => inspected ? toTooltipContext(inspected) : null,\n [inspected, toTooltipContext],\n );\n // Unlike the tooltip, the host-owned insight panel is meant to stay informative\n // when nothing is hovered, so it still falls back to the selected region.\n const insightSource = inspected ?? selected;\n const insightContext = useMemo<InsightContext | null>(() => insightSource\n ? { ...toTooltipContext(insightSource), selected: insightSource.id === selected?.id }\n : null, [insightSource, selected?.id, toTooltipContext]);\n /**\n * Where each region's value label goes, and whether it needs a leader line.\n * Small regions are moved into clear space beside themselves; everything else\n * keeps its number at its centroid.\n */\n const valuePlacements = useMemo(() => {\n const coversAnother = (region: PreparedRegion, at: Point, halfWidth: number, halfHeight: number) => {\n const probes: Point[] = [\n at,\n [at[0] - halfWidth, at[1] - halfHeight],\n [at[0] + halfWidth, at[1] + halfHeight],\n [at[0] + halfWidth, at[1] - halfHeight],\n [at[0] - halfWidth, at[1] + halfHeight],\n ];\n return regions.some((other) => other.id !== region.id && other.partBounds.some(([minX, minY, maxX, maxY]) =>\n probes.some((probe) => probe[0] >= minX && probe[0] <= maxX && probe[1] >= minY && probe[1] <= maxY)));\n };\n\n return regions.map((region) => {\n const text = region.value === null ? \"—\" : formatValue(region.value);\n // Glyph metrics without measuring the DOM: the stylesheet sets 11px bold,\n // and digits in that face are close enough to half-em wide for placement.\n const halfWidth = Math.max(text.length * 3.1, 3);\n const halfHeight = 5.5;\n const isSmall = region.extent > 0 && region.extent < SMALL_REGION_EXTENT;\n const fitsInside = region.partBounds.some(([minX, minY, maxX, maxY]) =>\n maxX - minX >= halfWidth * 2 && maxY - minY >= halfHeight * 2);\n\n if (!isSmall && fitsInside) return { region, text, at: region.centroid as Point, leader: null };\n\n const outside = isSmall\n ? placeOutsideLabel({\n anchor: region.centroid,\n clearance: Math.max(region.extent, MIN_REGION_MARKER_SIZE) / 2 + 4 + halfWidth,\n halfSize: [halfWidth, halfHeight],\n viewBox: [VIEWBOX.width, VIEWBOX.height],\n centre: [VIEWBOX.width / 2, VIEWBOX.height / 2],\n // Moving a label out only helps if there is open space to move it into.\n // Goa and Puducherry have sea beside them; Delhi is ringed by other\n // states, so its number stays put rather than landing on a neighbour.\n isBlocked: (candidate) => coversAnother(region, candidate, halfWidth, halfHeight),\n })\n : null;\n\n if (!outside) {\n return fitsInside ? { region, text, at: region.centroid as Point, leader: null } : null;\n }\n\n const gap = Math.max(region.extent, MIN_REGION_MARKER_SIZE) / 2 + 1;\n const dx = outside[0] - region.centroid[0];\n const dy = outside[1] - region.centroid[1];\n const length = Math.hypot(dx, dy);\n if (length <= gap + halfWidth) return { region, text, at: outside, leader: null };\n return {\n region,\n text,\n at: outside,\n leader: [\n [region.centroid[0] + (dx / length) * gap, region.centroid[1] + (dy / length) * gap],\n [outside[0] - (dx / length) * (halfWidth + 1.5), outside[1] - (dy / length) * (halfWidth + 1.5)],\n ] as [Point, Point],\n };\n }).filter((placement): placement is NonNullable<typeof placement> => placement !== null);\n }, [formatValue, regions]);\n\n const smallMarkers = useMemo(\n () => regions.filter((region) => region.extent > 0 && region.extent < MIN_REGION_MARKER_SIZE),\n [regions],\n );\n\n /**\n * A click that reaches the svg itself missed every region path. Either it\n * landed near a small one — Goa, Puducherry, the island groups, all awkward to\n * hit — or it is a click on open sea, which clears the selection.\n */\n const handleBackgroundClick = (event: ReactMouseEvent<SVGSVGElement>) => {\n if (event.target !== event.currentTarget) return; // a region handled it\n // An unlaid-out svg (or a test environment that reports zero-size rects) has\n // no usable coordinates, so the proximity step is skipped — but the click is\n // still a click on the background and must clear the selection.\n const rect = event.currentTarget.getBoundingClientRect();\n const point: Point | null = rect.width > 0 && rect.height > 0\n ? (() => {\n // The svg scales its view box to fit while preserving aspect ratio, so\n // the scale is the smaller ratio and the remainder is centring.\n const scale = Math.min(rect.width / VIEWBOX.width, rect.height / VIEWBOX.height);\n return [\n (event.clientX - rect.left - (rect.width - VIEWBOX.width * scale) / 2) / scale,\n (event.clientY - rect.top - (rect.height - VIEWBOX.height * scale) / 2) / scale,\n ] as Point;\n })()\n : null;\n\n let nearest: PreparedRegion | null = null;\n if (point) {\n let nearestDistance = Infinity;\n for (const region of regions) {\n if (region.extent <= 0 || region.extent >= SMALL_REGION_EXTENT) continue;\n const distance = distanceToParts(point, region.partBounds);\n if (distance <= SMALL_REGION_CLICK_RADIUS && distance < nearestDistance) {\n nearestDistance = distance;\n nearest = region;\n }\n }\n }\n if (nearest) { activate(nearest); return; }\n\n onBackgroundClick?.();\n if (activeSelectedId !== null) {\n setActiveSelectedId(null);\n onSelectedChange?.(null, level);\n }\n };\n\n const canDrill = level === \"state\" ? Boolean(loadDistricts) : level === \"district\" ? Boolean(loadSubDistricts) : false;\n const drillActionLabel = level === \"state\" ? \"Activate to view districts.\" : \"Activate to view sub-districts.\";\n const regionCanDrill = (id: string) => canDrill && !(level === \"district\" && leafDistrictIds.has(id));\n const visibleReferenceRegions = level === \"state\" ? referenceRegions : districtReferenceRegions;\n const mergedReferenceIds = useMemo(() => new Set(referenceOverlayMergeIds), [referenceOverlayMergeIds]);\n\n useEffect(() => { onInsight?.(insightContext); }, [insightContext, onInsight]);\n\n // Nudge the anchored tooltip back inside the map. This runs in a layout effect,\n // not an effect, so the un-nudged position never paints — that one frame would be\n // a visible jump on exactly the edge regions this exists to fix. Keyed on the\n // content, not just the region, because the box is sized by what's in it.\n useLayoutEffect(() => {\n const anchor = tooltipAnchorRef.current;\n const canvas = canvasRef.current;\n if (!anchor || !canvas) return;\n\n // Measure the default placement (centered, above), so the correction is\n // computed against a known starting point rather than the last region's.\n anchor.style.removeProperty(\"--india-map-tooltip-dx\");\n anchor.style.removeProperty(\"--india-map-tooltip-dy\");\n\n const tooltipRect = anchor.getBoundingClientRect();\n const boundsRect = canvas.getBoundingClientRect();\n if (tooltipRect.width === 0 || boundsRect.width === 0) return; // not laid out (hidden, or jsdom)\n\n const { dx, side } = placeTooltip(tooltipRect, boundsRect, TOOLTIP_GAP_PX);\n if (dx !== 0) anchor.style.setProperty(\"--india-map-tooltip-dx\", `${Math.round(dx)}px`);\n if (side === \"below\") anchor.style.setProperty(\"--india-map-tooltip-dy\", `${TOOLTIP_GAP_PX}px`);\n }, [tooltipContext?.id, tooltipContext?.value, tooltipContext?.label, renderTooltip]);\n\n return (\n <section className={[\"india-choropleth\", !interactive && \"india-choropleth--static\", className].filter(Boolean).join(\" \")} aria-busy={loadingState || loadingDistrict ? \"true\" : undefined}>\n {showBreadcrumb ? (\n <div className=\"india-choropleth__toolbar\">\n <nav className=\"india-choropleth__breadcrumb\" aria-label=\"Map hierarchy\">\n {drilledState\n ? <button className=\"india-choropleth__back\" type=\"button\" onClick={goToStates}>All states</button>\n : <span>All states</span>}\n {drilledState ? (\n <>\n <span aria-hidden=\"true\">/</span>\n {/* The state is a link back only once there is a level below it to\n come back from; on the district view it is where you already are. */}\n {isSubDrillRequested\n ? <button className=\"india-choropleth__back\" type=\"button\" onClick={goBack}>{drilledState.label}</button>\n : <span aria-current=\"page\">{drilledState.label}</span>}\n </>\n ) : null}\n {isSubDrillRequested && drilledDistrict ? (\n <><span aria-hidden=\"true\">/</span><span aria-current=\"page\">{drilledDistrict.label}</span></>\n ) : null}\n </nav>\n </div>\n ) : null}\n <div ref={canvasRef} className=\"india-choropleth__canvas\" onMouseLeave={interactive ? () => inspect(null) : undefined}>\n {isSubDrillRequested && (!subDistrictLayer || subLoadError) ? (\n <div className=\"india-choropleth__status\" role={subLoadError ? \"alert\" : \"status\"}>\n {subLoadError ? subLoadError.message : loadingDistrict ? \"Loading sub-districts…\" : \"Sub-district data is unavailable for this district.\"}\n </div>\n ) : isDrillRequested && (!districtLayer || loadError) ? (\n <div className=\"india-choropleth__status\" role={loadError ? \"alert\" : \"status\"}>\n {loadError ? loadError.message : loadingState ? \"Loading districts…\" : \"District data is unavailable for this state.\"}\n </div>\n ) : regions.length === 0 ? (\n <div className=\"india-choropleth__status\" role=\"status\" tabIndex={-1} ref={emptyLevelRef}>\n {level === \"subdistrict\" ? \"No sub-district data is available for this district.\" : \"No district data is available for this state.\"}\n </div>\n ) : (\n <svg\n className={`india-choropleth__svg${filterBucket !== null ? \" india-choropleth__svg--filtered\" : \"\"}`}\n viewBox={`0 0 ${VIEWBOX.width} ${VIEWBOX.height}`}\n role=\"group\"\n aria-label={ariaLabel}\n onKeyDown={interactive ? (event) => { if (event.key === \"Escape\") { event.preventDefault(); inspect(null); } } : undefined}\n onClick={interactive ? handleBackgroundClick : undefined}\n >\n {visibleReferenceRegions.length > 0 ? (\n <>\n <defs>\n <pattern id={hatchId} width=\"8\" height=\"8\" patternUnits=\"userSpaceOnUse\" patternTransform=\"rotate(45)\">\n <rect width=\"8\" height=\"8\" fill=\"var(--india-map-reference-bg)\" />\n <line x1=\"0\" y1=\"0\" x2=\"0\" y2=\"8\" stroke=\"var(--india-map-reference-hatch)\" strokeWidth=\"2\" />\n </pattern>\n </defs>\n <g className=\"india-choropleth__reference-fill\" role=\"group\" aria-label=\"Non-statistical reference context.\">\n {visibleReferenceRegions.map((region) => (\n <path\n key={region.id}\n d={region.path}\n fill={referenceOverlayFill === \"solid\" ? \"var(--india-map-reference-bg)\" : `url(#${hatchId})`}\n aria-label={`${region.label}.${region.description ? ` ${region.description}` : \"\"}`}\n role=\"img\"\n />\n ))}\n </g>\n </>\n ) : null}\n {/* Hit areas for scattered regions, first so that every real outline is\n painted on top of them: the hull spanning Lakshadweep's islands is\n open sea, but Puducherry's spans the Tamil Nadu coast, and a hull\n must never take a pointer from a region that is actually there.\n Not focusable — keyboard focus has no coordinates, so the one tab\n stop stays on the region itself. */}\n {interactive ? (\n <g className=\"india-choropleth__hit-areas\" aria-hidden=\"true\">\n {regions.filter((region) => region.hitPath).map((region) => (\n <path\n key={region.id}\n d={region.hitPath!}\n fill=\"none\"\n pointerEvents=\"all\"\n tabIndex={-1}\n onMouseEnter={() => inspect(region)}\n onMouseLeave={() => inspect(null)}\n onClick={() => activate(region)}\n />\n ))}\n </g>\n ) : null}\n {regions.map((region) => {\n const isInspected = region.id === inspected?.id;\n const isSelected = region.id === selected?.id;\n const action = regionCanDrill(region.id) ? drillActionLabel : \"Activate to select.\";\n const textValue = region.value === null ? \"No data\" : formatValue(region.value);\n return (\n <path\n key={region.id}\n className={`india-choropleth__region${isInspected ? \" india-choropleth__region--inspected\" : \"\"}${isSelected ? \" india-choropleth__region--selected\" : \"\"}${mergedReferenceIds.has(region.id) ? \" india-choropleth__region--reference-merged\" : \"\"}${isDimmed(region.id) ? \" india-choropleth__dimmed\" : \"\"}`}\n d={region.path}\n fill={colorFor(region.value, region, min, max, colorScale)}\n tabIndex={interactive ? 0 : -1}\n role={interactive ? \"button\" : undefined}\n aria-label={`${region.label}, ${textValue}. ${action}`}\n aria-pressed={interactive ? isSelected : undefined}\n aria-describedby={isInspected ? tooltipId : undefined}\n ref={(element) => { pathRefs.current[region.id] = element; }}\n onMouseEnter={interactive ? () => inspect(region) : undefined}\n // The canvas is much wider than the drawn map, so leaving a region\n // usually lands on blank canvas rather than leaving the canvas at all.\n // Without this the last-hovered tooltip stays pinned indefinitely.\n // Moving straight to a sibling region dispatches this leave and that\n // region's enter from the same native event, so the tooltip switches\n // in one batch instead of blanking.\n onMouseLeave={interactive ? () => inspect(null) : undefined}\n onFocus={interactive ? () => inspect(region) : undefined}\n // Tabbing to a sibling region re-inspects it synchronously right after\n // this fires, so clearing here only matters when focus leaves the map.\n onBlur={interactive ? () => inspect(null) : undefined}\n onClick={interactive ? () => activate(region) : undefined}\n onKeyDown={interactive ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") { event.preventDefault(); activate(region); }\n } : undefined}\n />\n );\n })}\n {/* A region whose largest part is smaller than the marker is invisible at\n this scale — Puducherry's enclaves are a couple of units — so it gets\n a dot in its own colour instead of nothing at all. The dot takes the\n pointer as well: it is painted over whatever is beneath it, so it is\n what the reader sees and aims at, and the outline it stands in for is\n too small to hover. Focus stays on the region path, its one tab stop. */}\n {smallMarkers.length > 0 ? (\n <g className=\"india-choropleth__small-markers\" aria-hidden=\"true\">\n {smallMarkers.map((region) => (\n <circle\n key={region.id}\n className={isDimmed(region.id) ? \"india-choropleth__dimmed\" : undefined}\n cx={region.centroid[0]}\n cy={region.centroid[1]}\n r={MIN_REGION_MARKER_SIZE / 2}\n fill={colorFor(region.value, region, min, max, colorScale)}\n onMouseEnter={interactive ? () => inspect(region) : undefined}\n onMouseLeave={interactive ? () => inspect(null) : undefined}\n onClick={interactive ? () => activate(region) : undefined}\n />\n ))}\n </g>\n ) : null}\n {showRegionValues ? (\n <>\n <g className=\"india-choropleth__value-leaders\" aria-hidden=\"true\">\n {valuePlacements.filter((p) => p.leader).map((p) => (\n <line\n key={p.region.id}\n className={isDimmed(p.region.id) ? \"india-choropleth__dimmed\" : undefined}\n x1={p.leader![0][0]} y1={p.leader![0][1]} x2={p.leader![1][0]} y2={p.leader![1][1]}\n />\n ))}\n </g>\n <g className={`india-choropleth__region-values${level === \"state\" ? \"\" : \" india-choropleth__region-values--district\"}`} aria-hidden=\"true\">\n {valuePlacements.map((p) => (\n <text\n key={p.region.id}\n className={isDimmed(p.region.id) ? \"india-choropleth__dimmed\" : undefined}\n x={p.at[0]} y={p.at[1]} textAnchor=\"middle\" dominantBaseline=\"central\"\n >\n {p.text}\n </text>\n ))}\n </g>\n </>\n ) : null}\n {visibleReferenceRegions.length > 0 ? (\n <g className=\"india-choropleth__reference-outline\" aria-hidden=\"true\">\n {visibleReferenceRegions.map((region) => <path key={region.id} d={region.path} fill=\"none\" />)}\n </g>\n ) : null}\n {/* Selection is drawn as a ring on top of everything, rather than by recoloring\n the region: on a choropleth the fill *is* the data, so overwriting it made the\n selected region's color stop meaning anything. Hovering a region lifts it 2px,\n so the ring matches that lift when the selected region is also the inspected\n one — otherwise the fill slides out from under its own outline. */}\n {selected ? (\n <g\n className={`india-choropleth__selection${selected.id === inspected?.id ? \" india-choropleth__selection--lifted\" : \"\"}`}\n aria-hidden=\"true\"\n >\n <path className=\"india-choropleth__selection-halo\" d={selected.path} fill=\"none\" />\n <path className=\"india-choropleth__selection-ring\" d={selected.path} fill=\"none\" />\n </g>\n ) : null}\n </svg>\n )}\n {tooltipContext && regions.length > 0 ? (\n <div ref={tooltipAnchorRef} className=\"india-choropleth__tooltip-anchor\" style={{ left: `${(inspected!.centroid[0] / VIEWBOX.width) * 100}%`, top: `${(inspected!.centroid[1] / VIEWBOX.height) * 100}%` }}>\n {/* Deliberately not `role=\"status\"`. As a live region it re-announced the whole\n tooltip on every hover *and* every focus move, and the content is richer now.\n The region aria-label already carries label + value, and `aria-describedby`\n reads this box out on focus — once, on demand. */}\n <div id={tooltipId} className=\"india-choropleth__tooltip\">\n {renderTooltip ? renderTooltip(tooltipContext) : defaultTooltip(tooltipContext, formatValue)}\n </div>\n </div>\n ) : null}\n </div>\n {showLegend ? (\n <div\n className=\"india-choropleth__legend\"\n role=\"group\"\n aria-label={`Color scale: ${legendLabels[0]} to ${legendLabels[1]} values`}\n onKeyDown={interactive ? (event) => { if (event.key === \"Escape\") { event.preventDefault(); setActiveBucket(null); } } : undefined}\n >\n <span>{legendLabels[0]}</span><div className=\"india-choropleth__swatches\" aria-hidden={interactive ? undefined : true}>\n {/* Each swatch filters the map to its own band. An empty band is left\n as a swatch you can still read — silently skipping it would hide\n the fact that the ramp has a gap there — but it does nothing,\n because filtering to nothing just dims the whole map. */}\n {buckets.map((bucket) => interactive ? (\n <button\n key={bucket.index}\n type=\"button\"\n className={`india-choropleth__swatch${filterBucket === bucket.index ? \" india-choropleth__swatch--active\" : \"\"}${filterBucket !== null && filterBucket !== bucket.index ? \" india-choropleth__swatch--muted\" : \"\"}`}\n style={{ backgroundColor: bucket.color }}\n aria-pressed={filterBucket === bucket.index}\n aria-disabled={bucket.matches === 0 ? true : undefined}\n aria-label={legendBucketLabel(bucket, formatValue)}\n // Sighted readers get the same sentence the accessible name carries,\n // which is the only place an empty band explains itself now that it\n // is no longer faded.\n title={legendBucketLabel(bucket, formatValue)}\n onClick={() => {\n if (bucket.matches === 0) return;\n setActiveBucket(filterBucket === bucket.index ? null : bucket.index);\n }}\n />\n ) : (\n <i className=\"india-choropleth__swatch\" key={bucket.index} style={{ backgroundColor: bucket.color }} />\n ))}\n </div><span>{legendLabels[1]}</span>\n {visibleReferenceRegions.length > 0 ? <><i className={`india-choropleth__reference-key${referenceOverlayFill === \"solid\" ? \" india-choropleth__reference-key--solid\" : \"\"}`} aria-hidden=\"true\" /><span>{referenceOverlayLegendLabel}</span></> : null}\n </div>\n ) : null}\n {renderInsights ? <aside className=\"india-choropleth__insights\" aria-live=\"polite\">{renderInsights(insightContext)}</aside> : null}\n </section>\n );\n}\n","import { feature as topoFeature } from \"topojson-client\";\nimport type { GeometryObject } from \"topojson-specification\";\nimport type { GeometrySource, MapFeatureCollection } from \"./types\";\n\nexport function asFeatureCollection(source: GeometrySource): MapFeatureCollection {\n if (\"type\" in source && source.type === \"FeatureCollection\") {\n return source;\n }\n\n const topoSource = source as Exclude<GeometrySource, MapFeatureCollection>;\n\n const object = (typeof topoSource.object === \"string\"\n ? topoSource.topology.objects[topoSource.object]!\n : topoSource.object) as unknown as GeometryObject;\n if (!object) {\n throw new Error(\"The named TopoJSON object does not exist in this topology.\");\n }\n\n const unpacked = topoFeature(topoSource.topology, object);\n return unpacked.type === \"FeatureCollection\"\n ? (unpacked as MapFeatureCollection)\n : { type: \"FeatureCollection\", features: [unpacked] };\n}\n\nexport function totalOf(values: readonly (number | null)[]) {\n return values.reduce<number>((total, value) => total + (value ?? 0), 0);\n}\n","/**\n * The legend, as a filter.\n *\n * Every value on the map is painted from one of the ramp's colours. That makes\n * the legend a ready-made set of value bands, and picking a band is the\n * question a reader of a choropleth actually has: *which regions are the dark\n * ones?* These helpers answer it — which swatch a value belongs to, what each\n * swatch stands for, and how many regions land there.\n *\n * [swatchIndexOf] is the single definition of that mapping: the renderers pick\n * a region's fill with it too, so \"highlight the regions painted in this\n * colour\" is true by construction rather than by two formulas agreeing.\n *\n * Pure functions of values, so the DOM renderer and the React component share\n * one definition of the behaviour.\n */\n\n/** One swatch: the colour, and what the map actually has in it. */\nexport interface LegendBucket {\n index: number;\n color: string;\n /**\n * Lowest and highest value that lands here, or null when nothing does.\n *\n * The band the ramp *nominally* covers is a half-step either side of the\n * swatch's own stop, which lands on numbers like \"24.333 to 31\" that appear\n * nowhere in the data. What a reader wants to know is what picking this\n * swatch will give them, so the range is measured from the regions in it.\n */\n from: number | null;\n to: number | null;\n /** How many regions land here. Zero means the swatch would filter to nothing. */\n matches: number;\n}\n\n/**\n * Which swatch a value is painted from, or null when there is nothing to paint\n * — no value, or no ramp to paint it with.\n *\n * A ramp of one colour, or data with no spread at all, collapses to the top\n * swatch: there is a single band and every value is in it.\n */\nexport function swatchIndexOf(value: number | null, min: number, max: number, count: number): number | null {\n if (value === null || count <= 0) return null;\n if (count === 1 || max === min) return count - 1;\n const index = Math.round(((value - min) / (max - min)) * (count - 1));\n return Math.min(Math.max(index, 0), count - 1);\n}\n\n/**\n * The ramp described swatch by swatch.\n *\n * With a function colour scale the renderers show the default ramp, because a\n * function has no swatches to show. The bands are still the honest reading of\n * \"lower to higher\"; the swatch colour just isn't any region's actual fill.\n */\nexport function legendBuckets(\n colors: readonly string[],\n values: readonly (number | null)[],\n min: number,\n max: number,\n): LegendBucket[] {\n const count = colors.length;\n return colors.map((color, index) => {\n const members = values.filter((value): value is number => swatchIndexOf(value, min, max, count) === index);\n return {\n index,\n color,\n from: members.length ? Math.min(...members) : null,\n to: members.length ? Math.max(...members) : null,\n matches: members.length,\n };\n });\n}\n\n/**\n * What a swatch does, in words — the accessible name for its control, since the\n * colour itself carries the meaning and a screen reader cannot see it.\n */\nexport function legendBucketLabel(bucket: LegendBucket, formatValue: (value: number) => string): string {\n if (bucket.matches === 0 || bucket.from === null || bucket.to === null) return \"No regions in this band\";\n const range = bucket.from === bucket.to\n ? formatValue(bucket.from)\n : `${formatValue(bucket.from)} to ${formatValue(bucket.to)}`;\n return `Highlight ${bucket.matches} ${bucket.matches === 1 ? \"region\" : \"regions\"}, ${range}`;\n}\n\n","/**\n * Edge handling for the centroid-anchored tooltip.\n *\n * The tooltip is anchored to the region's centroid rather than the cursor on\n * purpose: hover and keyboard focus run through the same inspect path, and a\n * focused region has no cursor position to follow. That anchor still has to be\n * nudged so the box never leaves the map — a state at the top of the map (Jammu\n * & Kashmir on a narrow screen) would otherwise render *above* the map, over\n * whatever the host page put there.\n *\n * Kept as a pure function of two rects so both the DOM renderer and the React\n * component can share the same math.\n */\n\nexport interface Rect {\n left: number;\n right: number;\n top: number;\n bottom: number;\n width: number;\n height: number;\n}\n\nexport interface TooltipPlacement {\n /** Horizontal correction in px, applied on top of the centering translate. */\n dx: number;\n /** Which side of the centroid the tooltip sits on. */\n side: \"above\" | \"below\";\n}\n\n/**\n * @param tooltip The tooltip's rect as currently laid out (centered above the centroid).\n * @param bounds The area the tooltip must stay inside — the map canvas.\n * @param gap Space between the centroid and the tooltip edge, in px.\n * @param padding Minimum breathing room between the tooltip and the bounds edge, in px.\n */\nexport function placeTooltip(tooltip: Rect, bounds: Rect, gap: number, padding = 4): TooltipPlacement {\n let dx = 0;\n // A tooltip wider than the space available can't satisfy both edges; favor the\n // left one so the region name (which leads the content) stays readable.\n if (tooltip.right > bounds.right - padding) dx = bounds.right - padding - tooltip.right;\n if (tooltip.left + dx < bounds.left + padding) dx = bounds.left + padding - tooltip.left;\n\n // Flip below only when there genuinely isn't room above. The flipped position\n // is `gap` below the centroid, which is `height + 2 * gap` further down.\n const side: TooltipPlacement[\"side\"] = tooltip.top < bounds.top + padding ? \"below\" : \"above\";\n\n return { dx, side };\n}\n","/**\n * Geometry for regions that are too small to use normally.\n *\n * Goa is a few view-box units across, Puducherry is scattered enclaves, and\n * Lakshadweep's islands are a fraction of a pixel at national scale. Left alone\n * they are invisible, unclickable, and their value labels either don't fit or\n * land on a neighbour. These helpers back the three fixes for that: a marker so\n * they can be seen, a click buffer so they can be hit, and label placement that\n * moves a number into open space beside the region.\n *\n * Pure functions of projected coordinates, so the DOM renderer, the React\n * component and the Dart port can all share one definition of the behaviour.\n */\n\nexport type Point = readonly [number, number];\n\n/** Axis-aligned box as `[minX, minY, maxX, maxY]`. */\nexport type Box = readonly [number, number, number, number];\n\n/**\n * Regions kept at their true size even when they are small enough to qualify\n * for exaggeration.\n *\n * Exaggeration assumes a region has room to grow into. Puducherry does not: it\n * is four coastal enclaves *inside* Tamil Nadu, so growing them to the\n * visibility threshold pushes each one several units into the state around it,\n * and the map ends up showing a Puducherry that is the wrong shape in the wrong\n * place. Lakshadweep's islands grow into open sea, where nothing is displaced,\n * which is the case the feature was built for.\n *\n * Matched on the id containing the name because host data brings its own id\n * scheme; the bundled layer uses `in-cs-34-puducherry`. A district id inside the\n * UT matches too, which is a no-op: drilled into, its districts fill the map and\n * are far past the threshold that would have grown them.\n */\nconst TRUE_GEOMETRY_REGIONS = [\"puducherry\"];\n\nexport function keepsTrueGeometry(id: string): boolean {\n const normalized = id.toLowerCase();\n return TRUE_GEOMETRY_REGIONS.some((name) => normalized.includes(name));\n}\n\nexport function boundsOfRing(ring: readonly Point[]): Box {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [x, y] of ring) {\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n return [minX, minY, maxX, maxY];\n}\n\n/**\n * Longest side of the largest ring's box. This is the measure of \"small\", not\n * the feature's overall bounds: an island group's bounds can be large while\n * every island in it is sub-pixel.\n */\nexport function largestRingExtent(rings: readonly (readonly Point[])[]): number {\n let largest = 0;\n for (const ring of rings) {\n if (ring.length === 0) continue;\n const [minX, minY, maxX, maxY] = boundsOfRing(ring);\n const extent = Math.max(maxX - minX, maxY - minY);\n if (extent > largest) largest = extent;\n }\n return largest;\n}\n\nfunction signedArea(ring: readonly Point[]): number {\n let total = 0;\n for (let i = 0; i < ring.length; i++) {\n const [ax, ay] = ring[i]!;\n const [bx, by] = ring[(i + 1) % ring.length]!;\n total += ax * by - bx * ay;\n }\n return total / 2;\n}\n\n/**\n * Area-weighted centroid of the largest ring.\n *\n * Deliberately not the whole feature's centroid: averaging across parts pulls\n * Andaman & Nicobar's label out to sea between its islands, and Gujarat's off\n * its own coast.\n */\nexport function labelPointFor(rings: readonly (readonly Point[])[], fallback: Point): Point {\n let largest: readonly Point[] | null = null;\n let largestArea = 0;\n for (const ring of rings) {\n if (ring.length < 3) continue;\n const area = Math.abs(signedArea(ring));\n if (area > largestArea) {\n largestArea = area;\n largest = ring;\n }\n }\n if (!largest || largestArea === 0) return fallback;\n\n const area = signedArea(largest);\n let cx = 0;\n let cy = 0;\n for (let i = 0; i < largest.length; i++) {\n const [ax, ay] = largest[i]!;\n const [bx, by] = largest[(i + 1) % largest.length]!;\n const cross = ax * by - bx * ay;\n cx += (ax + bx) * cross;\n cy += (ay + by) * cross;\n }\n const centroid: Point = [cx / (6 * area), cy / (6 * area)];\n return Number.isFinite(centroid[0]) && Number.isFinite(centroid[1]) ? centroid : fallback;\n}\n\n/** Distance from a point to the nearest edge of a box; zero when inside it. */\nexport function distanceToBox(point: Point, box: Box): number {\n const [x, y] = point;\n const [minX, minY, maxX, maxY] = box;\n const dx = Math.max(minX - x, 0) + Math.max(x - maxX, 0);\n const dy = Math.max(minY - y, 0) + Math.max(y - maxY, 0);\n return Math.hypot(dx, dy);\n}\n\n/** Distance to the nearest of a region's parts. */\nexport function distanceToParts(point: Point, parts: readonly Box[]): number {\n let nearest = Infinity;\n for (const part of parts) {\n const distance = distanceToBox(point, part);\n if (distance < nearest) nearest = distance;\n }\n return nearest;\n}\n\n/**\n * Angles to try when placing a small region's label, as turns from the\n * away-from-centre direction: straight out first, then progressively to either\n * side, and back inward only as a last resort.\n *\n * One fixed direction is not enough. Puducherry sits south-east of the map's\n * middle, so the radial direction runs inland into Tamil Nadu while its open\n * water is due east.\n */\nconst LABEL_SEARCH_TURNS = [\n 0,\n Math.PI / 6, -Math.PI / 6,\n Math.PI / 3, -Math.PI / 3,\n Math.PI / 2, -Math.PI / 2,\n (2 * Math.PI) / 3, -(2 * Math.PI) / 3,\n (5 * Math.PI) / 6, -(5 * Math.PI) / 6,\n Math.PI,\n] as const;\n\nexport interface OutsideLabelOptions {\n /** Where the region is, in view-box units. */\n anchor: Point;\n /** How far the label must clear the region itself. */\n clearance: number;\n /** Half the label's width and height, used to keep it inside the view box. */\n halfSize: Point;\n /** The view box the label must stay within, as `[width, height]`. */\n viewBox: Point;\n /** Direction is measured away from this point — normally the view box's middle. */\n centre: Point;\n /** Returns true when a label centred here would cover another region. */\n isBlocked: (candidate: Point) => boolean;\n}\n\n/**\n * Find a clear spot beside a region for its value label, or null when the\n * region is hemmed in on every side (Delhi, ringed by other states) and the\n * label is better left where it was.\n */\nexport function placeOutsideLabel(options: OutsideLabelOptions): Point | null {\n const { anchor, clearance, halfSize, viewBox, centre, isBlocked } = options;\n const dx = anchor[0] - centre[0];\n const dy = anchor[1] - centre[1];\n const base = dx === 0 && dy === 0 ? 0 : Math.atan2(dy, dx);\n\n for (const turn of LABEL_SEARCH_TURNS) {\n const angle = base + turn;\n const candidate: Point = [\n clamp(anchor[0] + Math.cos(angle) * clearance, halfSize[0], viewBox[0] - halfSize[0]),\n clamp(anchor[1] + Math.sin(angle) * clearance, halfSize[1], viewBox[1] - halfSize[1]),\n ];\n if (!isBlocked(candidate)) return candidate;\n }\n return null;\n}\n\nfunction clamp(value: number, lower: number, upper: number): number {\n return Math.min(Math.max(value, lower), upper);\n}\n\n/**\n * Grow a feature whose whole geometry is too small to see, about each part's\n * own centre.\n *\n * Two guards make this safe, and both were learned the hard way:\n *\n * 1. It applies only when the feature's *largest* part is under [minExtent] —\n * that is, the whole region is tiny. West Bengal is a large state whose\n * Sundarbans delta is fourteen small islets; enlarging those individually\n * blew them up into an overlapping mess across the river mouth. A region is\n * either small enough to need help or it is not.\n * 2. Once a feature qualifies, each part grows about its own centre until it is\n * visible. This preserves every part's location and aspect ratio instead of\n * turning a dispersed archipelago into one large, misplaced blob. The cap\n * keeps the tiniest specks from reading as real landmass.\n *\n * Growth is capped by [maxScale] so a speck never reads as a real landmass.\n */\nexport function enlargeSmallParts(\n rings: readonly (readonly Point[])[],\n minExtent: number,\n maxScale = 8,\n): Point[][] {\n const unchanged = () => rings.map((ring) => [...ring]);\n if (minExtent <= 0) return unchanged();\n // Only a feature that is small *as a whole* qualifies.\n const largest = largestRingExtent(rings);\n if (largest <= 0 || largest >= minExtent) return unchanged();\n\n return rings.map((ring) => {\n const [minX, minY, maxX, maxY] = boundsOfRing(ring);\n const extent = Math.max(maxX - minX, maxY - minY);\n const scale = extent <= 0 ? 1 : Math.min(minExtent / extent, maxScale);\n const cx = (minX + maxX) / 2;\n const cy = (minY + maxY) / 2;\n return ring.map(([x, y]) => [cx + (x - cx) * scale, cy + (y - cy) * scale] as Point);\n });\n}\n\n/**\n * Convex hull of a set of points, as one closed ring (Andrew's monotone chain).\n * Returns the input when there is nothing to wrap — fewer than three distinct\n * points, or all of them collinear.\n */\nexport function convexHull(points: readonly Point[]): Point[] {\n const sorted = [...points].sort((a, b) => a[0] - b[0] || a[1] - b[1]);\n const unique: Point[] = [];\n for (const point of sorted) {\n const prior = unique.at(-1);\n if (!prior || prior[0] !== point[0] || prior[1] !== point[1]) unique.push(point);\n }\n if (unique.length < 3) return unique.map((point) => [...point] as Point);\n\n const turn = (o: Point, a: Point, b: Point) =>\n (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);\n const half = (ordered: readonly Point[]) => {\n const chain: Point[] = [];\n for (const point of ordered) {\n while (chain.length >= 2 && turn(chain[chain.length - 2]!, chain[chain.length - 1]!, point) <= 0) chain.pop();\n chain.push(point);\n }\n return chain;\n };\n\n const lower = half(unique);\n const upper = half([...unique].reverse());\n // Each chain repeats the other's first point, so drop both endpoints once.\n const hull = [...lower.slice(0, -1), ...upper.slice(0, -1)];\n return hull.length >= 3 ? hull : unique.map((point) => [...point] as Point);\n}\n\n/**\n * One continuous hit area for a region scattered across separate parts: the\n * convex hull of everything it is drawn as.\n *\n * Lakshadweep is twenty specks in open sea. Even exaggerated they are a poor\n * pointer target, and the water they enclose is how the group reads on the map,\n * so treating that water as part of the region is what a reader expects. The\n * hull is safe to be generous with only because every renderer tests it *after*\n * every real outline has missed, so it can never take a hover from a neighbour\n * it happens to span.\n *\n * Null when the region needs no help: a single part, or already big enough to\n * point at directly ([maxExtent], the same \"too small to use\" threshold the\n * click buffer and value labels work from).\n */\nexport function scatteredHitArea(\n rings: readonly (readonly Point[])[],\n maxExtent: number,\n): Point[] | null {\n if (rings.length < 2 || maxExtent <= 0) return null;\n const extent = largestRingExtent(rings);\n if (extent <= 0 || extent >= maxExtent) return null;\n const hull = convexHull(rings.flat());\n return hull.length >= 3 ? hull : null;\n}\n\n/** An SVG path string for a set of rings, used when exaggeration has moved them. */\nexport function ringsToPath(rings: readonly (readonly Point[])[]): string {\n let d = \"\";\n for (const ring of rings) {\n if (ring.length === 0) continue;\n d += `M${ring[0]![0]},${ring[0]![1]}`;\n for (let i = 1; i < ring.length; i++) d += `L${ring[i]![0]},${ring[i]![1]}`;\n d += \"Z\";\n }\n return d;\n}\n","import { useCallback, useEffect, useState } from \"react\";\n\n/**\n * A value the host may control, or leave to the component.\n *\n * The uncontrolled slot follows the controlled value while one is supplied. It\n * used to sit untouched — `setValue` no-ops while controlled — so a host that\n * stopped controlling the prop got whatever the slot held before control began,\n * often many interactions stale, rather than what was on screen a moment ago.\n */\nexport function useControllableState<T>(controlled: T | undefined, initial: T) {\n const [uncontrolled, setUncontrolled] = useState<T>(initial);\n const value = controlled === undefined ? uncontrolled : controlled;\n useEffect(() => {\n if (controlled !== undefined) setUncontrolled(controlled);\n }, [controlled]);\n const setValue = useCallback((next: T) => {\n if (controlled === undefined) setUncontrolled(next);\n }, [controlled]);\n return [value, setValue] as const;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { IndiaChoropleth } from \"./IndiaChoropleth\";\nimport { asFeatureCollection } from \"./geometry\";\nimport {\n DEFAULT_DATA_BASE_URL,\n isInlineGeometry,\n loadDistrictTopology,\n loadSubDistrictTopology,\n resolveGeometry,\n statesUrl,\n type GeometryInput,\n} from \"./data-source\";\nimport { normalizeStateKey, resolveState } from \"./states\";\nimport type { GeometrySource, IndiaChoroplethProps, MapFeature, MapLayer, MapRegion } from \"./types\";\n\n/** Reads a feature's stable id. Matches the framework-free facade's default. */\nfunction defaultGetId(feature: MapFeature): string {\n return String(feature.properties?.id ?? feature.properties?.name);\n}\n\n/** Reads a feature's display name. Matches the framework-free facade's default. */\nfunction defaultGetLabel(feature: MapFeature): string {\n return String(feature.properties?.name ?? feature.properties?.id);\n}\n\n/**\n * Canonical storage key for a name. Known states/UTs collapse to their LGD id,\n * so `\"Goa\"`, `\"goa\"`, `\"GOA\"` and `\"in-cs-30-goa\"` are one entry; anything else\n * (custom geometry with its own labels) falls back to its normalized name.\n *\n * Identical to `BharatChoropleth#keyFor` in the framework-free package — both\n * read the same `states.ts`, which a test keeps byte-identical.\n */\nfunction keyFor(name: string): string {\n return resolveState(name)?.id ?? normalizeStateKey(name);\n}\n\n/**\n * Finds a feature's value, trying the most literal match first.\n *\n * Each key the caller wrote maps to the canonical key it addresses, never to a\n * value, so the value map stays the single source of truth and a later write\n * through one spelling is seen through every other.\n *\n * Exact id, then exact label, then each resolved through the state registry.\n * Ordering matters both ways round: an id that the registry does not know still\n * matches when the caller keyed by that id, and a caller who keyed by \"Orissa\"\n * still reaches Odisha. `has` rather than `??` throughout, so a deliberate null\n * reads as \"no data\" instead of falling through to the next candidate.\n */\nfunction lookUp(\n exactKeys: ReadonlyMap<string, string>,\n canonical: ReadonlyMap<string, number | null>,\n id: string,\n label: string,\n): number | null {\n for (const candidate of [\n exactKeys.get(id),\n exactKeys.get(label),\n resolveState(id)?.id,\n resolveState(label)?.id,\n normalizeStateKey(id),\n normalizeStateKey(label),\n ]) {\n if (candidate !== undefined && canonical.has(candidate)) return canonical.get(candidate) ?? null;\n }\n return null;\n}\n\n/** A number, or null for \"no data\". Anything not finite (NaN, Infinity) reads as no data. */\nfunction toValue(input: unknown): number | null {\n return typeof input === \"number\" && Number.isFinite(input) ? input : null;\n}\n\nexport interface BharatChoroplethProps extends Omit<IndiaChoroplethProps, \"states\"> {\n /**\n * Per-state values, keyed by any spelling the state registry accepts: display\n * name, slug, LGD id, former name, or a separator-free form. `Goa`, `goa`,\n * `Tamil Nadu`, `tamilnadu`, `Jammu & Kashmir`, `Orissa` and\n * `in-cs-30-goa` all resolve. Names it does not recognize are ignored with a\n * console warning rather than throwing.\n *\n * States you omit render as \"no data\", exactly as an explicit `null` does.\n *\n * This is the primary API. When both `values` and `data` are given, `values`\n * wins and `data` is ignored — they are not merged.\n */\n values?: Readonly<Record<string, number | null>>;\n /**\n * The same values as a row array, for data that already arrives that way.\n * Read through `regionKey` and `valueKey`. Ignored when `values` is given.\n *\n * ```tsx\n * <BharatChoropleth\n * data={[{ state: \"Telangana\", value: 82 }]}\n * regionKey=\"state\"\n * valueKey=\"value\"\n * />\n * ```\n */\n data?: readonly Readonly<Record<string, unknown>>[];\n /** Field on a `data` row holding the state name. Defaults to `\"region\"`. */\n regionKey?: string;\n /** Field on a `data` row holding the number. Defaults to `\"value\"`. Non-finite values read as \"no data\". */\n valueKey?: string;\n /**\n * Boundary data for the state/UT layer: inline GeoJSON/TopoJSON, a URL string\n * to fetch, or a promise of either. Omit to fetch the prepared current-vintage\n * state bundle from `dataBaseUrl`. The package itself bundles no geometry.\n */\n geometry?: GeometryInput;\n /**\n * District values, nested under the state each district belongs to.\n *\n * ```tsx\n * districtValues={{\n * Telangana: { Hyderabad: 90, \"Ranga Reddy\": 76 },\n * Maharashtra: { Aurangabad: 44 },\n * }}\n * ```\n *\n * The nesting is not decoration. District names repeat across states —\n * Aurangabad, Bilaspur and Hamirpur each name a district in two — and unlike\n * states there is no district registry to resolve a bare name against, so a\n * flat map could not say which one you meant. Under a state it is unambiguous.\n *\n * Outer keys resolve through the state registry, exactly like `values`, and are\n * checked immediately. Inner keys match a district's name, slug or id,\n * case-insensitively; they can only be checked once that state's districts have\n * been fetched, so a typo there is warned about when you first drill into it.\n *\n * Applies to whichever district layer is in use, including one from your own\n * `loadDistricts`: a district listed here takes this value, and any district not\n * listed keeps whatever the layer itself returned.\n *\n * There is no `subDistrictValues`. Three levels of nesting stops reading\n * clearly, and sub-district naming is far less settled than district naming —\n * set those through a custom `loadSubDistricts` instead.\n */\n districtValues?: Readonly<Record<string, Readonly<Record<string, number | null>>>>;\n /** Base URL for the prepared boundary bundles. Point it at your own copy of `data/generated` to self-host. */\n dataBaseUrl?: string;\n /**\n * Click-to-drill-down into districts. Defaults to `true` when the state layer\n * came from `dataBaseUrl` (district files live beside it), `false` when you\n * supplied your own `geometry` — pass `loadDistricts` yourself in that case.\n */\n districts?: boolean;\n /**\n * Click-to-drill-down from a district into its sub-districts (tehsils / taluks /\n * mandals / blocks). Defaults the same way `districts` does. Districts the\n * bundle has no sub-districts for stay leaves rather than erroring.\n */\n subDistricts?: boolean;\n /** Reads a feature's stable id. Defaults to `feature.properties.id`. Memoize a custom one — a new identity re-projects the map. */\n getId?: (feature: MapFeature) => string;\n /** Reads a feature's display name. Defaults to `feature.properties.name`. Memoize a custom one — a new identity re-projects the map. */\n getLabel?: (feature: MapFeature) => string;\n /** Called if boundary data fails to load. Without it the error is logged; either way the message is rendered in place of the map. */\n onError?: (error: Error) => void;\n}\n\n/**\n * The zero-config map: give it numbers keyed by state name, get a choropleth.\n *\n * ```tsx\n * import { BharatChoropleth } from \"bharat-choropleth\";\n * import \"bharat-choropleth/style.css\";\n *\n * <BharatChoropleth values={{ Telangana: 82, Karnataka: 74, Maharashtra: 91 }} />\n * ```\n *\n * Boundary data is fetched (never bundled), so the map shows a placeholder until\n * it lands. Drill-down into districts and sub-districts is on by default when\n * that default data source is in use.\n *\n * This is sugar over {@link IndiaChoropleth}, which remains the full renderer —\n * custom layers, controlled selection and drill-down, reference overlays, custom\n * tooltips. Every one of its props except `states` passes straight through, so\n * reaching for one is a prop, not a rewrite.\n */\nexport function BharatChoropleth({\n values,\n data,\n districtValues,\n regionKey = \"region\",\n valueKey = \"value\",\n geometry,\n dataBaseUrl = DEFAULT_DATA_BASE_URL,\n districts,\n subDistricts,\n getId = defaultGetId,\n getLabel = defaultGetLabel,\n onError,\n ...rest\n}: BharatChoroplethProps) {\n // `values` wins outright when both are given; merging two sources of truth for\n // the same state would make precedence a guess at the call site.\n const entries: readonly (readonly [name: string, value: number | null])[] = useMemo(() => {\n if (values) return Object.entries(values).map(([name, value]) => [name, toValue(value)] as const);\n if (data) return data.map((row) => [String(row[regionKey] ?? \"\"), toValue(row[valueKey])] as const);\n return [];\n }, [data, regionKey, valueKey, values]);\n\n const { valueMap, exactKeys, writtenAs, duplicated } = useMemo(() => {\n const valueMap = new Map<string, number | null>();\n /**\n * The caller's keys exactly as written, checked before the registry.\n *\n * Not every id belongs to the registry. The historical Census bundle in this\n * repository uses `in-hs-*` ids, which `resolveState` does not know, so a\n * feature keyed on its id would fall through to being keyed on its *label* —\n * and values written against ids would silently never match, leaving a fully\n * populated dataset rendering as \"No data\" everywhere. Keeping the literal\n * keys means id-keyed values work for any geometry, registry or not.\n */\n const exactKeys = new Map<string, string>();\n const writtenAs = new Map<string, string>();\n /**\n * Rows that name a region already seen.\n *\n * The last one used to win in silence, which is how a mis-shaped query turns\n * into a believed wrong number: a state showing one of its districts' totals\n * looks exactly like a state showing its own. The last value still wins —\n * changing that would move numbers under existing callers — but it is no\n * longer a secret, and the spelling kept below is deliberately the first.\n */\n const duplicated = new Map<string, number>();\n for (const [name, value] of entries) {\n const key = keyFor(name);\n if (valueMap.has(key)) duplicated.set(key, (duplicated.get(key) ?? 1) + 1);\n valueMap.set(key, value);\n exactKeys.set(name, key);\n // Keep the caller's own spelling so a warning quotes what they typed.\n if (!writtenAs.has(key)) writtenAs.set(key, name);\n }\n return { valueMap, exactKeys, writtenAs, duplicated };\n }, [entries]);\n\n /**\n * `{ Telangana: { Hyderabad: 90 } }` resolved to\n * `{ \"in-cs-36-telangana\" => { \"hyderabad\" => 90 } }`. The outer key goes\n * through the state registry so any spelling of the state works; the inner keys\n * are normalized the same way, which is what makes them match a district's\n * name, slug or id whatever case or separators the caller used.\n */\n const districtValueMap = useMemo(() => {\n const byState = new Map<string, Map<string, number | null>>();\n for (const [stateName, districts] of Object.entries(districtValues ?? {})) {\n const stateKey = keyFor(stateName);\n const inner = byState.get(stateKey) ?? new Map<string, number | null>();\n for (const [districtName, value] of Object.entries(districts ?? {})) {\n inner.set(normalizeStateKey(districtName), toValue(value));\n }\n byState.set(stateKey, inner);\n }\n return byState;\n }, [districtValues]);\n\n const districtSignature = useMemo(\n () =>\n JSON.stringify(\n [...districtValueMap]\n .map(([stateKey, inner]) => [stateKey, [...inner].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))] as const)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n ),\n [districtValueMap],\n );\n\n /**\n * Content hash of the resolved values. Callers write `values={{ Goa: 6 }}`\n * inline, so the object is new on every parent render; keying the layer on it\n * would re-project the whole national map each time. Keying on what the values\n * actually *are* means a re-render that changed nothing costs nothing.\n */\n const valueSignature = useMemo(\n () => JSON.stringify([...valueMap].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),\n [valueMap],\n );\n\n // Read by the drill-down loaders, which must keep a stable identity across\n // value changes: IndiaChoropleth re-runs its district effect when the loader\n // changes, so a loader rebuilt per value would refetch on every update.\n const valuesRef = useRef(valueMap);\n valuesRef.current = valueMap;\n const exactKeysRef = useRef(exactKeys);\n exactKeysRef.current = exactKeys;\n const districtValuesRef = useRef(districtValueMap);\n districtValuesRef.current = districtValueMap;\n\n const source: GeometryInput = geometry ?? statesUrl(dataBaseUrl);\n const inlineGeometry = isInlineGeometry(source) ? source : null;\n const [fetched, setFetched] = useState<GeometrySource | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n useEffect(() => {\n if (isInlineGeometry(source)) return; // already in hand — rendered without a placeholder\n const controller = typeof AbortController === \"function\" ? new AbortController() : null;\n let cancelled = false;\n setFetched(null);\n setError(null);\n resolveGeometry(source, \"states\", controller?.signal)\n .then((loaded) => {\n if (!cancelled) setFetched(loaded);\n })\n .catch((cause: unknown) => {\n if (cancelled || (cause instanceof Error && cause.name === \"AbortError\")) return;\n const failure = cause instanceof Error ? cause : new Error(\"BharatChoropleth: could not load boundary data.\");\n setError(failure);\n if (onErrorRef.current) onErrorRef.current(failure);\n else console.error(failure);\n });\n return () => {\n cancelled = true;\n controller?.abort();\n };\n // A URL string compares by value, so the usual case re-runs only on a real change.\n }, [source]);\n\n const resolvedGeometry = inlineGeometry ?? fetched;\n\n const statesLayer = useMemo<MapLayer | null>(() => {\n if (!resolvedGeometry) return null;\n /**\n * A district can share its parent's name (Lakshadweep), so resolve a\n * feature through the state registry by id first and only then by label,\n * rather than matching a bare slug against an id-keyed map.\n */\n return {\n geometry: resolvedGeometry,\n getId,\n getLabel,\n // `valueMap` is captured deliberately: the memo is keyed on the signature\n // of exactly these values, so the captured map and the key always agree.\n getValue: (feature) => lookUp(exactKeys, valueMap, getId(feature), getLabel(feature)),\n };\n // `valueSignature` is the dependency that stands in for `valueMap`; see above.\n }, [exactKeys, getId, getLabel, resolvedGeometry, valueSignature]);\n\n /**\n * Unknown names cannot be judged until the boundary data has landed and named\n * the real label set, so the warning is deferred rather than guessed at — the\n * same order the framework-free facade warns in. Warning during render would\n * also fire twice under StrictMode.\n */\n const warned = useRef(new Set<string>());\n useEffect(() => {\n if (!resolvedGeometry || !statesLayer) return;\n const known = new Set(\n asFeatureCollection(resolvedGeometry).features.map(\n (feature) =>\n resolveState(getId(feature))?.id ?? resolveState(getLabel(feature))?.id ?? normalizeStateKey(getLabel(feature)),\n ),\n );\n for (const [key, value] of valuesRef.current) {\n if (value === null || known.has(key) || warned.current.has(key)) continue;\n warned.current.add(key);\n console.warn(\n `BharatChoropleth: \"${writtenAs.get(key) ?? key}\" is not a recognized state/UT — its value is ignored.`,\n );\n }\n for (const [key, count] of duplicated) {\n const seen = `duplicate:${key}`;\n if (warned.current.has(seen)) continue;\n warned.current.add(seen);\n console.warn(\n `BharatChoropleth: \"${writtenAs.get(key) ?? key}\" appears in ${count} rows — the last one is shown. ` +\n `Aggregate it in the query if that is not what you meant.`,\n );\n }\n }, [duplicated, getId, getLabel, resolvedGeometry, statesLayer, valueSignature, writtenAs]);\n\n // Drill-down geometry is fetched once per id and held for the component's life.\n // IndiaChoropleth re-runs its district effect whenever the state layer changes\n // — which a value update does — so without this every value change refetched.\n interface DrillDownState {\n controller: AbortController | null;\n districts: Map<string, Promise<GeometrySource>>;\n subDistricts: Map<string, Promise<GeometrySource | null>>;\n }\n // Built lazily: `useRef(expr)` evaluates `expr` on every render and discards\n // it, and this component is built to re-render on every data tick.\n const drillDownRef = useRef<DrillDownState | null>(null);\n drillDownRef.current ??= {\n controller: typeof AbortController === \"function\" ? new AbortController() : null,\n districts: new Map(),\n subDistricts: new Map(),\n };\n const drillDown = drillDownRef.current;\n useEffect(() => () => drillDownRef.current?.controller?.abort(), []);\n\n /**\n * Overlays `districtValues` onto a district layer. A district named in the prop\n * takes that value; one that is not keeps whatever the layer returned, so a\n * caller's own `loadDistricts` still supplies everything they did not override.\n *\n * Warns once per state, after that state's districts have arrived — the only\n * point at which an unmatched name is known to be a typo rather than a district\n * that simply has not loaded yet.\n */\n const warnedDistricts = useRef(new Set<string>());\n const withDistrictValues = useCallback((layer: MapLayer, stateId: string): MapLayer => {\n const wanted = districtValuesRef.current.get(stateId);\n if (!wanted || wanted.size === 0) return layer;\n const keysFor = (feature: MapFeature) => [normalizeStateKey(layer.getId(feature)), normalizeStateKey(layer.getLabel(feature))];\n\n if (!warnedDistricts.current.has(stateId)) {\n warnedDistricts.current.add(stateId);\n const present = new Set(asFeatureCollection(layer.geometry).features.flatMap(keysFor));\n for (const [key, value] of wanted) {\n if (value !== null && !present.has(key)) {\n console.warn(`BharatChoropleth: \"${key}\" is not a district of this state — its value is ignored.`);\n }\n }\n }\n\n return {\n ...layer,\n getValue: (feature) => {\n for (const key of keysFor(feature)) {\n // `has` rather than `??`, so an explicit null reads as \"no data\"\n // instead of falling through to the layer's own value.\n if (wanted.has(key)) return wanted.get(key) ?? null;\n }\n return layer.getValue(feature);\n },\n };\n }, []);\n\n const usingDefaultData = geometry === undefined;\n const districtsEnabled = districts ?? usingDefaultData;\n const subDistrictsEnabled = subDistricts ?? usingDefaultData;\n\n const defaultDistrictLoader = useMemo(() => {\n if (!districtsEnabled) return undefined;\n return async (stateId: string): Promise<MapLayer> => {\n const cache = drillDown.districts;\n let pending = cache.get(stateId);\n if (!pending) {\n pending = loadDistrictTopology(dataBaseUrl, stateId, drillDown.controller?.signal);\n // A failed fetch must not be cached, or a retry can never succeed.\n pending.catch(() => cache.delete(stateId));\n cache.set(stateId, pending);\n }\n return withDistrictValues(\n {\n geometry: await pending,\n getId: defaultGetId,\n getLabel: defaultGetLabel,\n getValue: (feature) => lookUp(exactKeysRef.current, valuesRef.current, defaultGetId(feature), defaultGetLabel(feature)),\n },\n stateId,\n );\n };\n // `districtSignature` rebuilds the loader when district values change, which\n // is what makes the map repaint them: the renderer only re-derives a level\n // from a new layer object. The geometry behind it is cached, so this is a\n // repaint, not a refetch.\n }, [dataBaseUrl, districtSignature, districtsEnabled, withDistrictValues]);\n\n const defaultSubDistrictLoader = useMemo(() => {\n if (!subDistrictsEnabled) return undefined;\n return async (districtId: string): Promise<MapLayer | null> => {\n const cache = drillDown.subDistricts;\n let pending = cache.get(districtId);\n if (!pending) {\n pending = loadSubDistrictTopology(dataBaseUrl, districtId, drillDown.controller?.signal);\n pending.catch(() => cache.delete(districtId));\n cache.set(districtId, pending);\n }\n const geometry = await pending;\n // Null means the bundle holds no sub-districts for this district, which\n // leaves it a leaf rather than opening an empty level.\n if (!geometry) return null;\n return {\n geometry,\n getId: defaultGetId,\n getLabel: defaultGetLabel,\n getValue: (feature) => lookUp(exactKeysRef.current, valuesRef.current, defaultGetId(feature), defaultGetLabel(feature)),\n };\n };\n }, [dataBaseUrl, subDistrictsEnabled]);\n\n const callerLoadDistricts = rest.loadDistricts;\n const districtLoader = useMemo(() => {\n if (!callerLoadDistricts) return defaultDistrictLoader;\n return async (stateId: string, state: MapRegion) =>\n withDistrictValues(await callerLoadDistricts(stateId, state), stateId);\n }, [callerLoadDistricts, defaultDistrictLoader, districtSignature, withDistrictValues]);\n\n if (error) {\n return (\n <div className=\"bharat-choropleth__status bharat-choropleth__status--error\" role=\"alert\">\n {error.message}\n </div>\n );\n }\n\n if (!statesLayer) {\n return (\n <div className=\"bharat-choropleth__status\" role=\"status\">\n Loading map…\n </div>\n );\n }\n\n return (\n <IndiaChoropleth\n {...rest}\n states={statesLayer}\n loadDistricts={districtLoader}\n loadSubDistricts={rest.loadSubDistricts ?? defaultSubDistrictLoader}\n />\n );\n}\n","import type { GeometrySource, MapFeatureCollection } from \"./types\";\nimport type { Topology } from \"topojson-specification\";\n\n/**\n * Where the optional prepared boundary bundles are fetched from when the caller\n * doesn't supply their own `geometry`.\n *\n * The files are *fetched*, never bundled — the package still ships no boundary\n * geometry, and each asset keeps its own source's licence and attribution\n * alongside it (see `data/ATTRIBUTION.md`). The default bundle is\n * `datta07/INDIAN-SHAPEFILES` (MIT):\n *\n * > State/UT boundaries derived from datta07/INDIAN-SHAPEFILES (MIT).\n *\n * Self-host by copying `data/generated/` next to your app and passing\n * `dataBaseUrl: \"/maps\"`, which is the right call for offline or air-gapped\n * deployments and avoids a third-party CDN request at runtime.\n *\n * The ref is pinned to an immutable tag on purpose. A branch ref (`@main`) is\n * mutable and cached by jsDelivr for hours, so a data change would silently\n * alter — or break — every consumer's map at a time nobody chose. Bump this\n * deliberately, alongside a release.\n *\n * Bump it *in* the release, and check one asset of every level actually resolves\n * at the new tag. Leaving it behind does not fail loudly: a level whose files\n * the pinned tag predates 404s, and `loadSubDistrictTopology` reads a 404 as\n * \"this district has no sub-districts\", so the whole level just quietly goes\n * missing. That is exactly what 0.2.0 shipped with.\n */\nexport const DEFAULT_DATA_BASE_URL = \"https://cdn.jsdelivr.net/gh/shashankbudem/bharat-choropleth@v0.3.0/data/generated\";\n\nexport const ATTRIBUTION = \"State/UT, district and sub-district boundaries derived from datta07/INDIAN-SHAPEFILES (MIT).\";\n\n/** Anything `geometry` accepts: inline data, a URL to fetch, or a promise of either. */\nexport type GeometryInput = GeometrySource | string | Promise<GeometrySource | Topology | MapFeatureCollection>;\n\nfunction trimTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\nexport function statesUrl(baseUrl: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-states/states.topo.json`;\n}\n\nexport function districtsUrl(baseUrl: string, stateId: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-districts/districts/${stateId}.topo.json`;\n}\n\nexport function subDistrictsUrl(baseUrl: string, districtId: string): string {\n return `${trimTrailingSlash(baseUrl)}/current-2019-subdistricts/subdistricts/${districtId}.topo.json`;\n}\n\nasync function fetchJson(url: string, signal?: AbortSignal): Promise<unknown> {\n if (typeof fetch !== \"function\") {\n throw new Error(\n \"BharatChoropleth: no global fetch is available, so boundary data cannot be downloaded. Pass `geometry` with data you loaded yourself.\",\n );\n }\n const response = await fetch(url, { signal });\n if (!response.ok) {\n throw new Error(\n `BharatChoropleth: failed to load boundary data from ${url} (HTTP ${response.status}). ` +\n \"Set `dataBaseUrl` to your own copy of data/generated, or pass `geometry` directly.\",\n );\n }\n return response.json();\n}\n\n/**\n * Normalize a parsed payload into a `GeometrySource`. Accepts a raw TopoJSON\n * topology (the shape our own bundles have — the named object is picked for the\n * caller), a `{ topology, object }` pair, or a plain GeoJSON FeatureCollection.\n */\nexport function toGeometrySource(payload: unknown, preferredObject: string): GeometrySource {\n if (!payload || typeof payload !== \"object\") {\n throw new Error(\"BharatChoropleth: boundary data must be TopoJSON or a GeoJSON FeatureCollection.\");\n }\n const candidate = payload as Record<string, unknown>;\n\n if (candidate.type === \"FeatureCollection\") return payload as MapFeatureCollection;\n if (\"topology\" in candidate && \"object\" in candidate) return payload as GeometrySource;\n\n if (candidate.type === \"Topology\" && candidate.objects && typeof candidate.objects === \"object\") {\n const objects = candidate.objects as Record<string, unknown>;\n const object = preferredObject in objects ? preferredObject : Object.keys(objects)[0];\n if (!object) throw new Error(\"BharatChoropleth: the TopoJSON topology contains no objects.\");\n return { topology: payload as Topology, object };\n }\n\n throw new Error(\"BharatChoropleth: boundary data must be TopoJSON or a GeoJSON FeatureCollection.\");\n}\n\n/** Resolve whichever form of `geometry` the caller passed into usable geometry. */\nexport async function resolveGeometry(\n input: GeometryInput,\n preferredObject: string,\n signal?: AbortSignal,\n): Promise<GeometrySource> {\n if (typeof input === \"string\") return toGeometrySource(await fetchJson(input, signal), preferredObject);\n if (input instanceof Promise) return toGeometrySource(await input, preferredObject);\n return input;\n}\n\n/** True for geometry that is already in hand, so the map can render synchronously. */\nexport function isInlineGeometry(input: GeometryInput | undefined): input is GeometrySource {\n return typeof input === \"object\" && input !== null && !(input instanceof Promise);\n}\n\nexport async function loadDistrictTopology(\n baseUrl: string,\n stateId: string,\n signal?: AbortSignal,\n): Promise<GeometrySource> {\n return toGeometrySource(await fetchJson(districtsUrl(baseUrl, stateId), signal), \"districts\");\n}\n\n/**\n * Sub-districts for one district, or `null` where the bundle has no file for it.\n *\n * A missing file is the bundle's way of saying a district has no sub-district\n * level — three of the 788 current districts are in that position, and the\n * prepared bundle deliberately ships no asset for them. So a 404 resolves to\n * `null` (the district is a leaf) rather than raising, while any other failure\n * still surfaces as an error the map can report.\n */\nexport async function loadSubDistrictTopology(\n baseUrl: string,\n districtId: string,\n signal?: AbortSignal,\n): Promise<GeometrySource | null> {\n const url = subDistrictsUrl(baseUrl, districtId);\n if (typeof fetch !== \"function\") {\n throw new Error(\n \"BharatChoropleth: no global fetch is available, so boundary data cannot be downloaded. Pass `geometry` with data you loaded yourself.\",\n );\n }\n const response = await fetch(url, { signal });\n if (response.status === 404) return null;\n if (!response.ok) {\n throw new Error(\n `BharatChoropleth: failed to load boundary data from ${url} (HTTP ${response.status}). ` +\n \"Set `dataBaseUrl` to your own copy of data/generated, or pass `geometry` directly.\",\n );\n }\n return toGeometrySource(await response.json(), \"subdistricts\");\n}\n","/**\n * Static registry of the 36 current state/UT identities: id, display name, slug.\n *\n * This is *metadata only* — no coordinates, no boundary geometry. It exists so\n * `map.goa = 6` can be recognized, validated and warned about the instant the\n * script runs, before the (asynchronously fetched) boundary file has landed.\n * Boundary geometry itself still never ships inside this package.\n *\n * Kept in sync with `data/generated/current-2019-states/manifest.json`; ids are\n * LGD-derived and match the `districts/{stateId}.topo.json` filenames.\n */\nexport interface StateIdentity {\n /** LGD-derived stable id, e.g. `in-cs-30-goa`. */\n id: string;\n /** Display name as it appears in the boundary data, e.g. `Jammu & Kashmir`. */\n name: string;\n /** Hyphenated slug as it appears in the boundary data, e.g. `jammu-and-kashmir`. */\n slug: string;\n}\n\nexport const STATES: readonly StateIdentity[] = [\n { id: \"in-cs-01-jammu-and-kashmir\", name: \"Jammu & Kashmir\", slug: \"jammu-and-kashmir\" },\n { id: \"in-cs-02-himachal-pradesh\", name: \"Himachal Pradesh\", slug: \"himachal-pradesh\" },\n { id: \"in-cs-03-punjab\", name: \"Punjab\", slug: \"punjab\" },\n { id: \"in-cs-04-chandigarh\", name: \"Chandigarh\", slug: \"chandigarh\" },\n { id: \"in-cs-05-uttarakhand\", name: \"Uttarakhand\", slug: \"uttarakhand\" },\n { id: \"in-cs-06-haryana\", name: \"Haryana\", slug: \"haryana\" },\n { id: \"in-cs-07-delhi\", name: \"Delhi\", slug: \"delhi\" },\n { id: \"in-cs-08-rajasthan\", name: \"Rajasthan\", slug: \"rajasthan\" },\n { id: \"in-cs-09-uttar-pradesh\", name: \"Uttar Pradesh\", slug: \"uttar-pradesh\" },\n { id: \"in-cs-10-bihar\", name: \"Bihar\", slug: \"bihar\" },\n { id: \"in-cs-11-sikkim\", name: \"Sikkim\", slug: \"sikkim\" },\n { id: \"in-cs-12-arunachal-pradesh\", name: \"Arunachal Pradesh\", slug: \"arunachal-pradesh\" },\n { id: \"in-cs-13-nagaland\", name: \"Nagaland\", slug: \"nagaland\" },\n { id: \"in-cs-14-manipur\", name: \"Manipur\", slug: \"manipur\" },\n { id: \"in-cs-15-mizoram\", name: \"Mizoram\", slug: \"mizoram\" },\n { id: \"in-cs-16-tripura\", name: \"Tripura\", slug: \"tripura\" },\n { id: \"in-cs-17-meghalaya\", name: \"Meghalaya\", slug: \"meghalaya\" },\n { id: \"in-cs-18-assam\", name: \"Assam\", slug: \"assam\" },\n { id: \"in-cs-19-west-bengal\", name: \"West Bengal\", slug: \"west-bengal\" },\n { id: \"in-cs-20-jharkhand\", name: \"Jharkhand\", slug: \"jharkhand\" },\n { id: \"in-cs-21-odisha\", name: \"Odisha\", slug: \"odisha\" },\n { id: \"in-cs-22-chhattisgarh\", name: \"Chhattisgarh\", slug: \"chhattisgarh\" },\n { id: \"in-cs-23-madhya-pradesh\", name: \"Madhya Pradesh\", slug: \"madhya-pradesh\" },\n { id: \"in-cs-24-gujarat\", name: \"Gujarat\", slug: \"gujarat\" },\n { id: \"in-cs-26-dadra-and-nagar-haveli-and-daman-and-diu\", name: \"Dadra and Nagar Haveli and Daman and Diu\", slug: \"dadra-and-nagar-haveli-and-daman-and-diu\" },\n { id: \"in-cs-27-maharashtra\", name: \"Maharashtra\", slug: \"maharashtra\" },\n { id: \"in-cs-28-andhra-pradesh\", name: \"Andhra Pradesh\", slug: \"andhra-pradesh\" },\n { id: \"in-cs-29-karnataka\", name: \"Karnataka\", slug: \"karnataka\" },\n { id: \"in-cs-30-goa\", name: \"Goa\", slug: \"goa\" },\n { id: \"in-cs-31-lakshadweep\", name: \"Lakshadweep\", slug: \"lakshadweep\" },\n { id: \"in-cs-32-kerala\", name: \"Kerala\", slug: \"kerala\" },\n { id: \"in-cs-33-tamil-nadu\", name: \"Tamil Nadu\", slug: \"tamil-nadu\" },\n { id: \"in-cs-34-puducherry\", name: \"Puducherry\", slug: \"puducherry\" },\n { id: \"in-cs-35-andaman-and-nicobar\", name: \"Andaman & Nicobar\", slug: \"andaman-and-nicobar\" },\n { id: \"in-cs-36-telangana\", name: \"Telangana\", slug: \"telangana\" },\n { id: \"in-cs-37-ladakh\", name: \"Ladakh\", slug: \"ladakh\" },\n];\n\n/**\n * Former / colloquial / commonly-typed names, mapped to the canonical slug.\n * `map.orissa = 4` should not be a silent typo for someone who learned the\n * older name — it should just work.\n */\nconst ALIASES: Readonly<Record<string, string>> = {\n orissa: \"odisha\",\n pondicherry: \"puducherry\",\n uttaranchal: \"uttarakhand\",\n \"nct-of-delhi\": \"delhi\",\n \"new-delhi\": \"delhi\",\n \"delhi-nct\": \"delhi\",\n \"jammu-kashmir\": \"jammu-and-kashmir\",\n \"j-and-k\": \"jammu-and-kashmir\",\n jk: \"jammu-and-kashmir\",\n \"andaman-nicobar\": \"andaman-and-nicobar\",\n \"andaman-and-nicobar-islands\": \"andaman-and-nicobar\",\n \"dadra-and-nagar-haveli\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n \"daman-and-diu\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n dnhdd: \"dadra-and-nagar-haveli-and-daman-and-diu\",\n \"nagar-haveli\": \"dadra-and-nagar-haveli-and-daman-and-diu\",\n};\n\n/**\n * Canonical key for any user-supplied spelling: lowercase, `&` → `and`, every\n * run of non-alphanumerics → a single `-`. So `Tamil Nadu`, `tamil_nadu`,\n * `TAMIL-NADU` and `tamil nadu` all collapse to `tamil-nadu`.\n */\nexport function normalizeStateKey(input: string): string {\n return input\n .toLowerCase()\n .replace(/&/g, \" and \")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/** Separator-free form, so `tamilnadu` / `uttarpradesh` / `westbengal` also resolve. */\nfunction compact(key: string): string {\n return key.replace(/-/g, \"\");\n}\n\nconst BY_KEY = new Map<string, StateIdentity>();\nconst BY_COMPACT = new Map<string, StateIdentity>();\n\nfor (const state of STATES) {\n for (const key of [state.slug, normalizeStateKey(state.name), state.id]) {\n if (!BY_KEY.has(key)) BY_KEY.set(key, state);\n const compacted = compact(key);\n if (!BY_COMPACT.has(compacted)) BY_COMPACT.set(compacted, state);\n }\n}\nfor (const [alias, slug] of Object.entries(ALIASES)) {\n const state = BY_KEY.get(slug);\n if (!state) continue; // unreachable while ALIASES stays in sync with STATES\n if (!BY_KEY.has(alias)) BY_KEY.set(alias, state);\n const compacted = compact(alias);\n if (!BY_COMPACT.has(compacted)) BY_COMPACT.set(compacted, state);\n}\n\n/**\n * Resolve any spelling of a state/UT — display name, slug, LGD id, underscore\n * form, alias, or separator-free form — to its canonical identity.\n * Returns `undefined` for anything unrecognized, which callers treat as a typo.\n */\nexport function resolveState(input: string): StateIdentity | undefined {\n const key = normalizeStateKey(input);\n return BY_KEY.get(key) ?? BY_COMPACT.get(compact(key));\n}\n"],"mappings":"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","names":["useCallback","useEffect","useState","area","useEffect","useState","useCallback","useCallback","useEffect","useMemo","useRef","useState","jsx","useMemo","valueMap","exactKeys","writtenAs","duplicated","districts","useRef","useState","useEffect","useCallback","geometry"]}
|
package/dist/style.css
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
--india-map-focus: #0f766e;
|
|
11
11
|
--india-map-active: #ff725f;
|
|
12
12
|
--india-map-empty: #e7edf0;
|
|
13
|
+
/* Paired with --india-map-text: that variable colours the tooltip as well as the
|
|
14
|
+
map, so a dark theme that sets it must set this too, or it puts light text on
|
|
15
|
+
a white card. */
|
|
13
16
|
--india-map-tooltip-bg: #fff;
|
|
14
17
|
--india-map-tooltip-border: #b8c2ce;
|
|
15
18
|
--india-map-text: #081435;
|
|
@@ -94,8 +97,11 @@
|
|
|
94
97
|
.india-choropleth__tooltip-bar { display: block; height: .3rem; margin: .1rem 0 .15rem; border-radius: .3rem; background: var(--india-map-empty); overflow: hidden; }
|
|
95
98
|
.india-choropleth__tooltip-bar > span { display: block; height: 100%; border-radius: inherit; background: var(--india-map-active); }
|
|
96
99
|
.india-choropleth__legend { margin-top: .75rem; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .75rem; color: var(--india-map-muted); font: .8125rem/1.3 system-ui, sans-serif; }
|
|
97
|
-
.india-choropleth__swatches { display: flex; gap: .25rem; }
|
|
98
|
-
|
|
100
|
+
.india-choropleth__swatches { display: flex; gap: .25rem; flex: 1 1 auto; min-width: 0; max-width: 100%; }
|
|
101
|
+
/* Sized from the legend row, never the viewport: `vw` measures the browser
|
|
102
|
+
window, so in any embed narrower than the page the swatches pinned to
|
|
103
|
+
their maximum and overflowed the map they belong to. */
|
|
104
|
+
.india-choropleth__swatch { flex: 1 1 0; min-width: .5rem; max-width: 4.25rem; height: .75rem; border-radius: .1rem; }
|
|
99
105
|
/* On an interactive map each swatch filters to its own band. The bar stays the
|
|
100
106
|
same 12px it has always been; a transparent border above and below it lifts
|
|
101
107
|
the pointer and touch target to 24px, and the negative margin gives that
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bharat-choropleth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Accessible, dependency-light SVG choropleths for India drill-down dashboards.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"choropleth",
|
|
@@ -24,8 +24,13 @@
|
|
|
24
24
|
"url": "https://github.com/shashankbudem/bharat-choropleth/issues"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
|
-
"sideEffects": [
|
|
28
|
-
|
|
27
|
+
"sideEffects": [
|
|
28
|
+
"./dist/style.css"
|
|
29
|
+
],
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"CHANGELOG.md"
|
|
33
|
+
],
|
|
29
34
|
"main": "./dist/index.js",
|
|
30
35
|
"module": "./dist/index.js",
|
|
31
36
|
"types": "./dist/index.d.ts",
|