choropleth-kit 0.0.0-stage → 0.2.0

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.
Files changed (489) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -2
  3. package/dist/admin1/ad.d.ts +4 -0
  4. package/dist/admin1/ad.js +2 -0
  5. package/dist/admin1/ae.d.ts +4 -0
  6. package/dist/admin1/ae.js +2 -0
  7. package/dist/admin1/af.d.ts +4 -0
  8. package/dist/admin1/af.js +2 -0
  9. package/dist/admin1/ag.d.ts +4 -0
  10. package/dist/admin1/ag.js +2 -0
  11. package/dist/admin1/ai.d.ts +4 -0
  12. package/dist/admin1/ai.js +2 -0
  13. package/dist/admin1/al.d.ts +4 -0
  14. package/dist/admin1/al.js +2 -0
  15. package/dist/admin1/am.d.ts +4 -0
  16. package/dist/admin1/am.js +2 -0
  17. package/dist/admin1/ao.d.ts +4 -0
  18. package/dist/admin1/ao.js +2 -0
  19. package/dist/admin1/aq.d.ts +4 -0
  20. package/dist/admin1/aq.js +2 -0
  21. package/dist/admin1/ar.d.ts +4 -0
  22. package/dist/admin1/ar.js +2 -0
  23. package/dist/admin1/as.d.ts +4 -0
  24. package/dist/admin1/as.js +2 -0
  25. package/dist/admin1/at.d.ts +4 -0
  26. package/dist/admin1/at.js +2 -0
  27. package/dist/admin1/au.d.ts +4 -0
  28. package/dist/admin1/au.js +2 -0
  29. package/dist/admin1/aw.d.ts +4 -0
  30. package/dist/admin1/aw.js +2 -0
  31. package/dist/admin1/ax.d.ts +4 -0
  32. package/dist/admin1/ax.js +2 -0
  33. package/dist/admin1/az.d.ts +4 -0
  34. package/dist/admin1/az.js +2 -0
  35. package/dist/admin1/ba.d.ts +4 -0
  36. package/dist/admin1/ba.js +2 -0
  37. package/dist/admin1/bb.d.ts +4 -0
  38. package/dist/admin1/bb.js +2 -0
  39. package/dist/admin1/bd.d.ts +4 -0
  40. package/dist/admin1/bd.js +2 -0
  41. package/dist/admin1/be.d.ts +4 -0
  42. package/dist/admin1/be.js +2 -0
  43. package/dist/admin1/bf.d.ts +4 -0
  44. package/dist/admin1/bf.js +2 -0
  45. package/dist/admin1/bg.d.ts +4 -0
  46. package/dist/admin1/bg.js +2 -0
  47. package/dist/admin1/bh.d.ts +4 -0
  48. package/dist/admin1/bh.js +2 -0
  49. package/dist/admin1/bi.d.ts +4 -0
  50. package/dist/admin1/bi.js +2 -0
  51. package/dist/admin1/bj.d.ts +4 -0
  52. package/dist/admin1/bj.js +2 -0
  53. package/dist/admin1/bl.d.ts +4 -0
  54. package/dist/admin1/bl.js +2 -0
  55. package/dist/admin1/bm.d.ts +4 -0
  56. package/dist/admin1/bm.js +2 -0
  57. package/dist/admin1/bn.d.ts +4 -0
  58. package/dist/admin1/bn.js +2 -0
  59. package/dist/admin1/bo.d.ts +4 -0
  60. package/dist/admin1/bo.js +2 -0
  61. package/dist/admin1/br.d.ts +4 -0
  62. package/dist/admin1/br.js +2 -0
  63. package/dist/admin1/bs.d.ts +4 -0
  64. package/dist/admin1/bs.js +2 -0
  65. package/dist/admin1/bt.d.ts +4 -0
  66. package/dist/admin1/bt.js +2 -0
  67. package/dist/admin1/bw.d.ts +4 -0
  68. package/dist/admin1/bw.js +2 -0
  69. package/dist/admin1/by.d.ts +4 -0
  70. package/dist/admin1/by.js +2 -0
  71. package/dist/admin1/bz.d.ts +4 -0
  72. package/dist/admin1/bz.js +2 -0
  73. package/dist/admin1/ca.d.ts +4 -0
  74. package/dist/admin1/ca.js +2 -0
  75. package/dist/admin1/cd.d.ts +4 -0
  76. package/dist/admin1/cd.js +2 -0
  77. package/dist/admin1/cf.d.ts +4 -0
  78. package/dist/admin1/cf.js +2 -0
  79. package/dist/admin1/cg.d.ts +4 -0
  80. package/dist/admin1/cg.js +2 -0
  81. package/dist/admin1/ch.d.ts +4 -0
  82. package/dist/admin1/ch.js +2 -0
  83. package/dist/admin1/ci.d.ts +4 -0
  84. package/dist/admin1/ci.js +2 -0
  85. package/dist/admin1/ck.d.ts +4 -0
  86. package/dist/admin1/ck.js +2 -0
  87. package/dist/admin1/cl.d.ts +4 -0
  88. package/dist/admin1/cl.js +2 -0
  89. package/dist/admin1/cm.d.ts +4 -0
  90. package/dist/admin1/cm.js +2 -0
  91. package/dist/admin1/cn.d.ts +4 -0
  92. package/dist/admin1/cn.js +2 -0
  93. package/dist/admin1/co.d.ts +4 -0
  94. package/dist/admin1/co.js +2 -0
  95. package/dist/admin1/cr.d.ts +4 -0
  96. package/dist/admin1/cr.js +2 -0
  97. package/dist/admin1/cu.d.ts +4 -0
  98. package/dist/admin1/cu.js +2 -0
  99. package/dist/admin1/cv.d.ts +4 -0
  100. package/dist/admin1/cv.js +2 -0
  101. package/dist/admin1/cw.d.ts +4 -0
  102. package/dist/admin1/cw.js +2 -0
  103. package/dist/admin1/cy.d.ts +4 -0
  104. package/dist/admin1/cy.js +2 -0
  105. package/dist/admin1/cz.d.ts +4 -0
  106. package/dist/admin1/cz.js +2 -0
  107. package/dist/admin1/de.d.ts +4 -0
  108. package/dist/admin1/de.js +2 -0
  109. package/dist/admin1/dj.d.ts +4 -0
  110. package/dist/admin1/dj.js +2 -0
  111. package/dist/admin1/dk.d.ts +4 -0
  112. package/dist/admin1/dk.js +2 -0
  113. package/dist/admin1/dm.d.ts +4 -0
  114. package/dist/admin1/dm.js +2 -0
  115. package/dist/admin1/do.d.ts +4 -0
  116. package/dist/admin1/do.js +2 -0
  117. package/dist/admin1/dz.d.ts +4 -0
  118. package/dist/admin1/dz.js +2 -0
  119. package/dist/admin1/ec.d.ts +4 -0
  120. package/dist/admin1/ec.js +2 -0
  121. package/dist/admin1/ee.d.ts +4 -0
  122. package/dist/admin1/ee.js +2 -0
  123. package/dist/admin1/eg.d.ts +4 -0
  124. package/dist/admin1/eg.js +2 -0
  125. package/dist/admin1/eh.d.ts +4 -0
  126. package/dist/admin1/eh.js +2 -0
  127. package/dist/admin1/er.d.ts +4 -0
  128. package/dist/admin1/er.js +2 -0
  129. package/dist/admin1/es.d.ts +4 -0
  130. package/dist/admin1/es.js +2 -0
  131. package/dist/admin1/et.d.ts +4 -0
  132. package/dist/admin1/et.js +2 -0
  133. package/dist/admin1/fi.d.ts +4 -0
  134. package/dist/admin1/fi.js +2 -0
  135. package/dist/admin1/fj.d.ts +4 -0
  136. package/dist/admin1/fj.js +2 -0
  137. package/dist/admin1/fk.d.ts +4 -0
  138. package/dist/admin1/fk.js +2 -0
  139. package/dist/admin1/fm.d.ts +4 -0
  140. package/dist/admin1/fm.js +2 -0
  141. package/dist/admin1/fo.d.ts +4 -0
  142. package/dist/admin1/fo.js +2 -0
  143. package/dist/admin1/fr.d.ts +4 -0
  144. package/dist/admin1/fr.js +2 -0
  145. package/dist/admin1/ga.d.ts +4 -0
  146. package/dist/admin1/ga.js +2 -0
  147. package/dist/admin1/gb.d.ts +4 -0
  148. package/dist/admin1/gb.js +2 -0
  149. package/dist/admin1/gd.d.ts +4 -0
  150. package/dist/admin1/gd.js +2 -0
  151. package/dist/admin1/ge.d.ts +4 -0
  152. package/dist/admin1/ge.js +2 -0
  153. package/dist/admin1/gg.d.ts +4 -0
  154. package/dist/admin1/gg.js +2 -0
  155. package/dist/admin1/gh.d.ts +4 -0
  156. package/dist/admin1/gh.js +2 -0
  157. package/dist/admin1/gi.d.ts +4 -0
  158. package/dist/admin1/gi.js +2 -0
  159. package/dist/admin1/gl.d.ts +4 -0
  160. package/dist/admin1/gl.js +2 -0
  161. package/dist/admin1/gm.d.ts +4 -0
  162. package/dist/admin1/gm.js +2 -0
  163. package/dist/admin1/gn.d.ts +4 -0
  164. package/dist/admin1/gn.js +2 -0
  165. package/dist/admin1/gq.d.ts +4 -0
  166. package/dist/admin1/gq.js +2 -0
  167. package/dist/admin1/gr.d.ts +4 -0
  168. package/dist/admin1/gr.js +2 -0
  169. package/dist/admin1/gs.d.ts +4 -0
  170. package/dist/admin1/gs.js +2 -0
  171. package/dist/admin1/gt.d.ts +4 -0
  172. package/dist/admin1/gt.js +2 -0
  173. package/dist/admin1/gu.d.ts +4 -0
  174. package/dist/admin1/gu.js +2 -0
  175. package/dist/admin1/gw.d.ts +4 -0
  176. package/dist/admin1/gw.js +2 -0
  177. package/dist/admin1/gy.d.ts +4 -0
  178. package/dist/admin1/gy.js +2 -0
  179. package/dist/admin1/hk.d.ts +4 -0
  180. package/dist/admin1/hk.js +2 -0
  181. package/dist/admin1/hm.d.ts +4 -0
  182. package/dist/admin1/hm.js +2 -0
  183. package/dist/admin1/hn.d.ts +4 -0
  184. package/dist/admin1/hn.js +2 -0
  185. package/dist/admin1/hr.d.ts +4 -0
  186. package/dist/admin1/hr.js +2 -0
  187. package/dist/admin1/ht.d.ts +4 -0
  188. package/dist/admin1/ht.js +2 -0
  189. package/dist/admin1/hu.d.ts +4 -0
  190. package/dist/admin1/hu.js +2 -0
  191. package/dist/admin1/id.d.ts +4 -0
  192. package/dist/admin1/id.js +2 -0
  193. package/dist/admin1/ie.d.ts +4 -0
  194. package/dist/admin1/ie.js +2 -0
  195. package/dist/admin1/il.d.ts +4 -0
  196. package/dist/admin1/il.js +2 -0
  197. package/dist/admin1/im.d.ts +4 -0
  198. package/dist/admin1/im.js +2 -0
  199. package/dist/admin1/in.d.ts +4 -0
  200. package/dist/admin1/in.js +2 -0
  201. package/dist/admin1/io.d.ts +4 -0
  202. package/dist/admin1/io.js +2 -0
  203. package/dist/admin1/iq.d.ts +4 -0
  204. package/dist/admin1/iq.js +2 -0
  205. package/dist/admin1/ir.d.ts +4 -0
  206. package/dist/admin1/ir.js +2 -0
  207. package/dist/admin1/is.d.ts +4 -0
  208. package/dist/admin1/is.js +2 -0
  209. package/dist/admin1/it.d.ts +4 -0
  210. package/dist/admin1/it.js +2 -0
  211. package/dist/admin1/je.d.ts +4 -0
  212. package/dist/admin1/je.js +2 -0
  213. package/dist/admin1/jm.d.ts +4 -0
  214. package/dist/admin1/jm.js +2 -0
  215. package/dist/admin1/jo.d.ts +4 -0
  216. package/dist/admin1/jo.js +2 -0
  217. package/dist/admin1/jp.d.ts +4 -0
  218. package/dist/admin1/jp.js +2 -0
  219. package/dist/admin1/ke.d.ts +4 -0
  220. package/dist/admin1/ke.js +2 -0
  221. package/dist/admin1/kg.d.ts +4 -0
  222. package/dist/admin1/kg.js +2 -0
  223. package/dist/admin1/kh.d.ts +4 -0
  224. package/dist/admin1/kh.js +2 -0
  225. package/dist/admin1/ki.d.ts +4 -0
  226. package/dist/admin1/ki.js +2 -0
  227. package/dist/admin1/km.d.ts +4 -0
  228. package/dist/admin1/km.js +2 -0
  229. package/dist/admin1/kn.d.ts +4 -0
  230. package/dist/admin1/kn.js +2 -0
  231. package/dist/admin1/kp.d.ts +4 -0
  232. package/dist/admin1/kp.js +2 -0
  233. package/dist/admin1/kr.d.ts +4 -0
  234. package/dist/admin1/kr.js +2 -0
  235. package/dist/admin1/kw.d.ts +4 -0
  236. package/dist/admin1/kw.js +2 -0
  237. package/dist/admin1/ky.d.ts +4 -0
  238. package/dist/admin1/ky.js +2 -0
  239. package/dist/admin1/kz.d.ts +4 -0
  240. package/dist/admin1/kz.js +2 -0
  241. package/dist/admin1/la.d.ts +4 -0
  242. package/dist/admin1/la.js +2 -0
  243. package/dist/admin1/lb.d.ts +4 -0
  244. package/dist/admin1/lb.js +2 -0
  245. package/dist/admin1/lc.d.ts +4 -0
  246. package/dist/admin1/lc.js +2 -0
  247. package/dist/admin1/li.d.ts +4 -0
  248. package/dist/admin1/li.js +2 -0
  249. package/dist/admin1/lk.d.ts +4 -0
  250. package/dist/admin1/lk.js +2 -0
  251. package/dist/admin1/lr.d.ts +4 -0
  252. package/dist/admin1/lr.js +2 -0
  253. package/dist/admin1/ls.d.ts +4 -0
  254. package/dist/admin1/ls.js +2 -0
  255. package/dist/admin1/lt.d.ts +4 -0
  256. package/dist/admin1/lt.js +2 -0
  257. package/dist/admin1/lu.d.ts +4 -0
  258. package/dist/admin1/lu.js +2 -0
  259. package/dist/admin1/lv.d.ts +4 -0
  260. package/dist/admin1/lv.js +2 -0
  261. package/dist/admin1/ly.d.ts +4 -0
  262. package/dist/admin1/ly.js +2 -0
  263. package/dist/admin1/ma.d.ts +4 -0
  264. package/dist/admin1/ma.js +2 -0
  265. package/dist/admin1/mc.d.ts +4 -0
  266. package/dist/admin1/mc.js +2 -0
  267. package/dist/admin1/md.d.ts +4 -0
  268. package/dist/admin1/md.js +2 -0
  269. package/dist/admin1/me.d.ts +4 -0
  270. package/dist/admin1/me.js +2 -0
  271. package/dist/admin1/mf.d.ts +4 -0
  272. package/dist/admin1/mf.js +2 -0
  273. package/dist/admin1/mg.d.ts +4 -0
  274. package/dist/admin1/mg.js +2 -0
  275. package/dist/admin1/mh.d.ts +4 -0
  276. package/dist/admin1/mh.js +2 -0
  277. package/dist/admin1/mk.d.ts +4 -0
  278. package/dist/admin1/mk.js +2 -0
  279. package/dist/admin1/ml.d.ts +4 -0
  280. package/dist/admin1/ml.js +2 -0
  281. package/dist/admin1/mm.d.ts +4 -0
  282. package/dist/admin1/mm.js +2 -0
  283. package/dist/admin1/mn.d.ts +4 -0
  284. package/dist/admin1/mn.js +2 -0
  285. package/dist/admin1/mo.d.ts +4 -0
  286. package/dist/admin1/mo.js +2 -0
  287. package/dist/admin1/mp.d.ts +4 -0
  288. package/dist/admin1/mp.js +2 -0
  289. package/dist/admin1/mr.d.ts +4 -0
  290. package/dist/admin1/mr.js +2 -0
  291. package/dist/admin1/ms.d.ts +4 -0
  292. package/dist/admin1/ms.js +2 -0
  293. package/dist/admin1/mt.d.ts +4 -0
  294. package/dist/admin1/mt.js +2 -0
  295. package/dist/admin1/mu.d.ts +4 -0
  296. package/dist/admin1/mu.js +2 -0
  297. package/dist/admin1/mv.d.ts +4 -0
  298. package/dist/admin1/mv.js +2 -0
  299. package/dist/admin1/mw.d.ts +4 -0
  300. package/dist/admin1/mw.js +2 -0
  301. package/dist/admin1/mx.d.ts +4 -0
  302. package/dist/admin1/mx.js +2 -0
  303. package/dist/admin1/my.d.ts +4 -0
  304. package/dist/admin1/my.js +2 -0
  305. package/dist/admin1/mz.d.ts +4 -0
  306. package/dist/admin1/mz.js +2 -0
  307. package/dist/admin1/na.d.ts +4 -0
  308. package/dist/admin1/na.js +2 -0
  309. package/dist/admin1/nc.d.ts +4 -0
  310. package/dist/admin1/nc.js +2 -0
  311. package/dist/admin1/ne.d.ts +4 -0
  312. package/dist/admin1/ne.js +2 -0
  313. package/dist/admin1/nf.d.ts +4 -0
  314. package/dist/admin1/nf.js +2 -0
  315. package/dist/admin1/ng.d.ts +4 -0
  316. package/dist/admin1/ng.js +2 -0
  317. package/dist/admin1/ni.d.ts +4 -0
  318. package/dist/admin1/ni.js +2 -0
  319. package/dist/admin1/nl.d.ts +4 -0
  320. package/dist/admin1/nl.js +2 -0
  321. package/dist/admin1/no.d.ts +4 -0
  322. package/dist/admin1/no.js +2 -0
  323. package/dist/admin1/np.d.ts +4 -0
  324. package/dist/admin1/np.js +2 -0
  325. package/dist/admin1/nr.d.ts +4 -0
  326. package/dist/admin1/nr.js +2 -0
  327. package/dist/admin1/nu.d.ts +4 -0
  328. package/dist/admin1/nu.js +2 -0
  329. package/dist/admin1/nz.d.ts +4 -0
  330. package/dist/admin1/nz.js +2 -0
  331. package/dist/admin1/om.d.ts +4 -0
  332. package/dist/admin1/om.js +2 -0
  333. package/dist/admin1/pa.d.ts +4 -0
  334. package/dist/admin1/pa.js +2 -0
  335. package/dist/admin1/pe.d.ts +4 -0
  336. package/dist/admin1/pe.js +2 -0
  337. package/dist/admin1/pf.d.ts +4 -0
  338. package/dist/admin1/pf.js +2 -0
  339. package/dist/admin1/pg.d.ts +4 -0
  340. package/dist/admin1/pg.js +2 -0
  341. package/dist/admin1/ph.d.ts +4 -0
  342. package/dist/admin1/ph.js +2 -0
  343. package/dist/admin1/pk.d.ts +4 -0
  344. package/dist/admin1/pk.js +2 -0
  345. package/dist/admin1/pl.d.ts +4 -0
  346. package/dist/admin1/pl.js +2 -0
  347. package/dist/admin1/pm.d.ts +4 -0
  348. package/dist/admin1/pm.js +2 -0
  349. package/dist/admin1/pn.d.ts +4 -0
  350. package/dist/admin1/pn.js +2 -0
  351. package/dist/admin1/ps.d.ts +4 -0
  352. package/dist/admin1/ps.js +2 -0
  353. package/dist/admin1/pt.d.ts +4 -0
  354. package/dist/admin1/pt.js +2 -0
  355. package/dist/admin1/pw.d.ts +4 -0
  356. package/dist/admin1/pw.js +2 -0
  357. package/dist/admin1/py.d.ts +4 -0
  358. package/dist/admin1/py.js +2 -0
  359. package/dist/admin1/qa.d.ts +4 -0
  360. package/dist/admin1/qa.js +2 -0
  361. package/dist/admin1/ro.d.ts +4 -0
  362. package/dist/admin1/ro.js +2 -0
  363. package/dist/admin1/rs.d.ts +4 -0
  364. package/dist/admin1/rs.js +2 -0
  365. package/dist/admin1/ru.d.ts +4 -0
  366. package/dist/admin1/ru.js +2 -0
  367. package/dist/admin1/rw.d.ts +4 -0
  368. package/dist/admin1/rw.js +2 -0
  369. package/dist/admin1/sa.d.ts +4 -0
  370. package/dist/admin1/sa.js +2 -0
  371. package/dist/admin1/sb.d.ts +4 -0
  372. package/dist/admin1/sb.js +2 -0
  373. package/dist/admin1/sc.d.ts +4 -0
  374. package/dist/admin1/sc.js +2 -0
  375. package/dist/admin1/sd.d.ts +4 -0
  376. package/dist/admin1/sd.js +2 -0
  377. package/dist/admin1/se.d.ts +4 -0
  378. package/dist/admin1/se.js +2 -0
  379. package/dist/admin1/sg.d.ts +4 -0
  380. package/dist/admin1/sg.js +2 -0
  381. package/dist/admin1/sh.d.ts +4 -0
  382. package/dist/admin1/sh.js +2 -0
  383. package/dist/admin1/si.d.ts +4 -0
  384. package/dist/admin1/si.js +2 -0
  385. package/dist/admin1/sk.d.ts +4 -0
  386. package/dist/admin1/sk.js +2 -0
  387. package/dist/admin1/sl.d.ts +4 -0
  388. package/dist/admin1/sl.js +2 -0
  389. package/dist/admin1/sm.d.ts +4 -0
  390. package/dist/admin1/sm.js +2 -0
  391. package/dist/admin1/sn.d.ts +4 -0
  392. package/dist/admin1/sn.js +2 -0
  393. package/dist/admin1/so.d.ts +4 -0
  394. package/dist/admin1/so.js +2 -0
  395. package/dist/admin1/sr.d.ts +4 -0
  396. package/dist/admin1/sr.js +2 -0
  397. package/dist/admin1/ss.d.ts +4 -0
  398. package/dist/admin1/ss.js +2 -0
  399. package/dist/admin1/st.d.ts +4 -0
  400. package/dist/admin1/st.js +2 -0
  401. package/dist/admin1/sv.d.ts +4 -0
  402. package/dist/admin1/sv.js +2 -0
  403. package/dist/admin1/sy.d.ts +4 -0
  404. package/dist/admin1/sy.js +2 -0
  405. package/dist/admin1/sz.d.ts +4 -0
  406. package/dist/admin1/sz.js +2 -0
  407. package/dist/admin1/tc.d.ts +4 -0
  408. package/dist/admin1/tc.js +2 -0
  409. package/dist/admin1/td.d.ts +4 -0
  410. package/dist/admin1/td.js +2 -0
  411. package/dist/admin1/tf.d.ts +4 -0
  412. package/dist/admin1/tf.js +2 -0
  413. package/dist/admin1/tg.d.ts +4 -0
  414. package/dist/admin1/tg.js +2 -0
  415. package/dist/admin1/th.d.ts +4 -0
  416. package/dist/admin1/th.js +2 -0
  417. package/dist/admin1/tj.d.ts +4 -0
  418. package/dist/admin1/tj.js +2 -0
  419. package/dist/admin1/tk.d.ts +4 -0
  420. package/dist/admin1/tk.js +2 -0
  421. package/dist/admin1/tl.d.ts +4 -0
  422. package/dist/admin1/tl.js +2 -0
  423. package/dist/admin1/tm.d.ts +4 -0
  424. package/dist/admin1/tm.js +2 -0
  425. package/dist/admin1/tn.d.ts +4 -0
  426. package/dist/admin1/tn.js +2 -0
  427. package/dist/admin1/to.d.ts +4 -0
  428. package/dist/admin1/to.js +2 -0
  429. package/dist/admin1/tr.d.ts +4 -0
  430. package/dist/admin1/tr.js +2 -0
  431. package/dist/admin1/tt.d.ts +4 -0
  432. package/dist/admin1/tt.js +2 -0
  433. package/dist/admin1/tv.d.ts +4 -0
  434. package/dist/admin1/tv.js +2 -0
  435. package/dist/admin1/tw.d.ts +4 -0
  436. package/dist/admin1/tw.js +2 -0
  437. package/dist/admin1/tz.d.ts +4 -0
  438. package/dist/admin1/tz.js +2 -0
  439. package/dist/admin1/ua.d.ts +4 -0
  440. package/dist/admin1/ua.js +2 -0
  441. package/dist/admin1/ug.d.ts +4 -0
  442. package/dist/admin1/ug.js +2 -0
  443. package/dist/admin1/um.d.ts +4 -0
  444. package/dist/admin1/um.js +2 -0
  445. package/dist/admin1/us.d.ts +4 -0
  446. package/dist/admin1/us.js +2 -0
  447. package/dist/admin1/uy.d.ts +4 -0
  448. package/dist/admin1/uy.js +2 -0
  449. package/dist/admin1/uz.d.ts +4 -0
  450. package/dist/admin1/uz.js +2 -0
  451. package/dist/admin1/va.d.ts +4 -0
  452. package/dist/admin1/va.js +2 -0
  453. package/dist/admin1/vc.d.ts +4 -0
  454. package/dist/admin1/vc.js +2 -0
  455. package/dist/admin1/ve.d.ts +4 -0
  456. package/dist/admin1/ve.js +2 -0
  457. package/dist/admin1/vg.d.ts +4 -0
  458. package/dist/admin1/vg.js +2 -0
  459. package/dist/admin1/vi.d.ts +4 -0
  460. package/dist/admin1/vi.js +2 -0
  461. package/dist/admin1/vn.d.ts +4 -0
  462. package/dist/admin1/vn.js +2 -0
  463. package/dist/admin1/vu.d.ts +4 -0
  464. package/dist/admin1/vu.js +2 -0
  465. package/dist/admin1/wf.d.ts +4 -0
  466. package/dist/admin1/wf.js +2 -0
  467. package/dist/admin1/ws.d.ts +4 -0
  468. package/dist/admin1/ws.js +2 -0
  469. package/dist/admin1/xk.d.ts +4 -0
  470. package/dist/admin1/xk.js +2 -0
  471. package/dist/admin1/ye.d.ts +4 -0
  472. package/dist/admin1/ye.js +2 -0
  473. package/dist/admin1/za.d.ts +4 -0
  474. package/dist/admin1/za.js +2 -0
  475. package/dist/admin1/zm.d.ts +4 -0
  476. package/dist/admin1/zm.js +2 -0
  477. package/dist/admin1/zw.d.ts +4 -0
  478. package/dist/admin1/zw.js +2 -0
  479. package/dist/chunk-ZN6SLXL3.js +1932 -0
  480. package/dist/core.d.ts +173 -0
  481. package/dist/core.js +203 -0
  482. package/dist/drill-chain-C8hhHBnd.d.ts +1256 -0
  483. package/dist/geo.d.ts +89 -0
  484. package/dist/geo.js +457 -0
  485. package/dist/index.d.ts +1256 -0
  486. package/dist/index.js +3110 -0
  487. package/dist/styles.css +417 -0
  488. package/dist/styles.d.ts +2 -0
  489. package/package.json +147 -3
package/dist/index.js ADDED
@@ -0,0 +1,3110 @@
1
+ import {
2
+ DEFAULT_EMPTY_FILL,
3
+ DEFAULT_FLIGHT_CURVATURE,
4
+ DEFAULT_FOCUS_BOUNDS,
5
+ FIT_TRANSFORM,
6
+ INERTIA_TAU_MS,
7
+ THEMES,
8
+ bubbleScale,
9
+ buildBreadcrumbs,
10
+ buildProjectionChain,
11
+ checkDrillStep,
12
+ clampPath,
13
+ classifyDrill,
14
+ computeAspectRatio,
15
+ constrainTransform,
16
+ contextOf,
17
+ createBorders,
18
+ createDrillState,
19
+ createIntentTimer,
20
+ createLruCache,
21
+ createProjection,
22
+ createZoomFilter,
23
+ createZoomInterpolator,
24
+ defaultFormatValue,
25
+ defineTheme,
26
+ drillDirectionBetween,
27
+ drillReducer,
28
+ dropOverseas,
29
+ extendTheme,
30
+ flightDuration,
31
+ formatDrillRequestProblem,
32
+ formatJoinDiagnostics,
33
+ formatNoIdsDiagnostic,
34
+ formatTooDeepRequest,
35
+ formatWrongLevelDataHint,
36
+ geographyKeyFor,
37
+ getAnchorPoint,
38
+ getFocusExtent,
39
+ getFocusGeometry,
40
+ inertiaOffset,
41
+ isFitTransform,
42
+ isThemeName,
43
+ joinData,
44
+ preserveView,
45
+ regionRotation,
46
+ relativeTransform,
47
+ releaseVelocity,
48
+ resolveControls,
49
+ resolveDrillData,
50
+ resolveLabels,
51
+ resolveRegion,
52
+ resolveTheme,
53
+ resolveVariant,
54
+ resolveZoomOptions,
55
+ resolveZoomTarget,
56
+ savedCameraFor,
57
+ themeToCssVars,
58
+ toFeatureCollection,
59
+ toViewState,
60
+ unwrapGeography,
61
+ validateLevels,
62
+ zoomKeyAction
63
+ } from "./chunk-ZN6SLXL3.js";
64
+
65
+ // src/react/use-choropleth.ts
66
+ import {
67
+ useEffect as useEffect6,
68
+ useLayoutEffect,
69
+ useMemo as useMemo4,
70
+ useRef as useRef5,
71
+ useState as useState5
72
+ } from "react";
73
+ import { geoPath } from "d3-geo";
74
+
75
+ // src/react/drill-levels.ts
76
+ var defineDrillLevels = (levels) => {
77
+ validateLevels(levels);
78
+ return levels;
79
+ };
80
+ var buildDrillEvent = ({
81
+ from,
82
+ to,
83
+ item
84
+ }) => {
85
+ const direction = drillDirectionBetween(from, to);
86
+ if (direction === null) return null;
87
+ return {
88
+ direction,
89
+ from: contextOf(from),
90
+ to: contextOf(to),
91
+ ...item === void 0 ? {} : { item }
92
+ };
93
+ };
94
+ var isDrillable = (levels, depth, item) => {
95
+ const level = levels[depth];
96
+ if (level === void 0 || depth >= levels.length - 1) return false;
97
+ const { canDrill } = level;
98
+ if (canDrill === void 0) return true;
99
+ return typeof canDrill === "function" ? canDrill(item) : canDrill;
100
+ };
101
+ var sourceDepth = (levels, depth) => {
102
+ let source = Math.min(depth, levels.length - 1);
103
+ while (source > 0 && levels[source]?.geography === "inherit") source--;
104
+ return source;
105
+ };
106
+
107
+ // src/react/use-drill-choreography.ts
108
+ import { useEffect, useRef } from "react";
109
+
110
+ // src/react/env.ts
111
+ var isProduction = () => {
112
+ try {
113
+ return process.env.NODE_ENV === "production";
114
+ } catch {
115
+ return false;
116
+ }
117
+ };
118
+ var prefersReducedMotion = () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
119
+
120
+ // src/react/use-drill-choreography.ts
121
+ var REVEAL_MS = 450;
122
+ var lastOf = (items) => items[items.length - 1];
123
+ var useDrillChoreography = ({
124
+ engine,
125
+ store,
126
+ shownChain,
127
+ chainFor
128
+ }) => {
129
+ const { active, machine, dispatch } = engine;
130
+ const latest = useRef({ engine, shownChain, chainFor });
131
+ useEffect(() => {
132
+ latest.current = { engine, shownChain, chainFor };
133
+ });
134
+ const flying = machine.phase === "flying";
135
+ const flightId = machine.flightId;
136
+ useEffect(() => {
137
+ if (!active || !flying) return;
138
+ const { engine: current, shownChain: shown, chainFor: build } = latest.current;
139
+ const state = current.machine;
140
+ const finish = (completed) => dispatch({ type: "FLIGHT_END", id: flightId, completed });
141
+ let destination;
142
+ if (state.direction === "up") {
143
+ destination = state.returnCamera ?? savedCameraFor(state, state.path) ?? FIT_TRANSFORM;
144
+ } else {
145
+ const levels = current.chainLevelsFor(state.path);
146
+ const next = levels === null ? null : build(levels, state.path);
147
+ const from = lastOf(shown?.projections ?? []);
148
+ const to = lastOf(next?.projections ?? []);
149
+ destination = from === void 0 || to === void 0 || next?.missing != null ? null : relativeTransform(from, to);
150
+ }
151
+ const api = store.getZoom();
152
+ if (api === null || destination === null) {
153
+ finish(true);
154
+ return;
155
+ }
156
+ const flight = { cancelled: false };
157
+ void api.flyTo(destination).then(({ completed }) => {
158
+ if (!flight.cancelled) finish(completed);
159
+ });
160
+ return () => {
161
+ flight.cancelled = true;
162
+ };
163
+ }, [active, flying, flightId, store, dispatch]);
164
+ const revealing = machine.phase === "revealing";
165
+ useEffect(() => {
166
+ if (!active || !revealing) return;
167
+ const timer = setTimeout(
168
+ () => dispatch({ type: "REVEAL_END" }),
169
+ prefersReducedMotion() ? 0 : REVEAL_MS
170
+ );
171
+ return () => clearTimeout(timer);
172
+ }, [active, revealing, dispatch]);
173
+ };
174
+
175
+ // src/react/map-item.ts
176
+ var ROOT_LEVEL = { id: "root", depth: 0 };
177
+ var isSameItem = (a, b) => a === b || a !== null && b !== null && a.kind === b.kind && a.id === b.id;
178
+
179
+ // src/react/map-store.ts
180
+ import { useSyncExternalStore } from "react";
181
+ var GESTURE_HINT_MS = 1500;
182
+ var INITIAL_STATE = {
183
+ hovered: null,
184
+ pointer: null,
185
+ pointerType: "mouse",
186
+ interacting: false,
187
+ camera: FIT_TRANSFORM,
188
+ gestureHint: null
189
+ };
190
+ var createMapStore = () => {
191
+ let state = INITIAL_STATE;
192
+ let hintTimer;
193
+ let zoomApi = null;
194
+ let drillApi = null;
195
+ let fallbackView = null;
196
+ const listeners = /* @__PURE__ */ new Set();
197
+ const store = {
198
+ subscribe: (listener) => {
199
+ listeners.add(listener);
200
+ return () => listeners.delete(listener);
201
+ },
202
+ getState: () => state,
203
+ setState: (patch) => {
204
+ const changed = Object.keys(patch).some(
205
+ (key) => !Object.is(state[key], patch[key])
206
+ );
207
+ if (!changed) return;
208
+ state = { ...state, ...patch };
209
+ for (const listener of listeners) listener();
210
+ },
211
+ showGestureHint: (kind) => {
212
+ clearTimeout(hintTimer);
213
+ store.setState({ gestureHint: kind });
214
+ hintTimer = setTimeout(() => store.setState({ gestureHint: null }), GESTURE_HINT_MS);
215
+ },
216
+ dispose: () => clearTimeout(hintTimer),
217
+ registerZoom: (api) => {
218
+ zoomApi = api;
219
+ },
220
+ getZoom: () => zoomApi,
221
+ registerDrill: (api) => {
222
+ drillApi = api;
223
+ },
224
+ getDrill: () => drillApi,
225
+ registerFallbackView: (read) => {
226
+ fallbackView = read;
227
+ },
228
+ getFallbackView: () => fallbackView?.() ?? null
229
+ };
230
+ return store;
231
+ };
232
+ var useMapStore = (store, selector) => useSyncExternalStore(
233
+ store.subscribe,
234
+ () => selector(store.getState()),
235
+ () => selector(INITIAL_STATE)
236
+ );
237
+
238
+ // src/react/use-container-size.ts
239
+ import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
240
+ var DEFAULT_WIDTH = 960;
241
+ var DEFAULT_ASPECT_RATIO = 960 / 500;
242
+ var useContainerSize = ({
243
+ initial,
244
+ aspectRatio = DEFAULT_ASPECT_RATIO,
245
+ useContainerHeight = false
246
+ } = {}) => {
247
+ const ref = useRef2(null);
248
+ const [measured, setMeasured] = useState(null);
249
+ useEffect2(() => {
250
+ const element = ref.current;
251
+ if (element === null || typeof ResizeObserver === "undefined") return;
252
+ const observer = new ResizeObserver(([entry]) => {
253
+ if (entry === void 0) return;
254
+ const { width, height: height2 } = entry.contentRect;
255
+ if (!(width > 0)) return;
256
+ setMeasured(
257
+ (prev) => prev?.width === width && prev.height === height2 ? prev : { width, height: height2 }
258
+ );
259
+ });
260
+ observer.observe(element);
261
+ return () => observer.disconnect();
262
+ }, []);
263
+ if (measured === null) {
264
+ return { ref, ...initial ?? { width: DEFAULT_WIDTH, height: DEFAULT_WIDTH / aspectRatio } };
265
+ }
266
+ const height = useContainerHeight && measured.height > 0 ? measured.height : measured.width / aspectRatio;
267
+ return { ref, width: measured.width, height };
268
+ };
269
+
270
+ // src/react/use-drill-engine.ts
271
+ import {
272
+ useCallback as useCallback2,
273
+ useEffect as useEffect4,
274
+ useMemo as useMemo3,
275
+ useReducer,
276
+ useRef as useRef4,
277
+ useState as useState3
278
+ } from "react";
279
+
280
+ // src/react/region.ts
281
+ import { useMemo } from "react";
282
+
283
+ // src/geo/overseas.ts
284
+ var OVERSEAS_TERRITORIES = {
285
+ // French Guiana.
286
+ FR: [
287
+ [
288
+ [-56, 1],
289
+ [-51, 7]
290
+ ]
291
+ ],
292
+ // Svalbard.
293
+ NO: [
294
+ [
295
+ [0, 73],
296
+ [40, 82]
297
+ ]
298
+ ],
299
+ // Hawaii.
300
+ US: [
301
+ [
302
+ [-162, 18],
303
+ [-154, 23]
304
+ ]
305
+ ]
306
+ };
307
+
308
+ // src/geo/region-presets.ts
309
+ var ids = (list) => list.split(" ");
310
+ var northernEurope = ids("AX DK EE FI FO GB GG IE IM IS JE LT LV NO SE");
311
+ var westernEurope = ids("AT BE CH DE FR LI LU MC NL");
312
+ var southernEurope = ids("AD AL BA ES GI GR HR IT ME MK MT PT RS SI SM VA XK");
313
+ var easternEurope = ids("BG BY CZ HU MD PL RO SK UA");
314
+ var northernAfrica = ids("DZ EG EH LY MA SD TN");
315
+ var easternAfrica = ids("BI DJ ER ET KE KM MG MU MW MZ RW SC SO SS TZ UG ZM ZW");
316
+ var middleAfrica = ids("AO CD CF CG CM GA GQ ST TD");
317
+ var southernAfrica = ids("BW LS NA SZ ZA");
318
+ var westernAfrica = ids("BF BJ CI CV GH GM GN GW LR ML MR NE NG SL SN TG");
319
+ var centralAsia = ids("KG KZ TJ TM UZ");
320
+ var easternAsia = ids("CN HK JP KP KR MN MO TW");
321
+ var southeasternAsia = ids("BN ID KH LA MM MY PH SG TH TL VN");
322
+ var southernAsia = ids("AF BD BT IN IR LK MV NP PK");
323
+ var westernAsia = ids("AE AM AZ BH CY GE IL IQ JO KW LB OM PS QA SA SY TR YE");
324
+ var usAndCanada = ids("BM CA GL US");
325
+ var centralAmerica = ids("BZ CR GT HN MX NI PA SV");
326
+ var caribbean = ids("AG AW BB BS CU CW DM DO GD HT JM KN KY LC PR TT VC");
327
+ var southAmerica = ids("AR BO BR CL CO EC FK GY PE PY SR UY VE");
328
+ var australiaAndNewZealand = ids("AU NZ");
329
+ var melanesia = ids("FJ NC PG SB VU");
330
+ var micronesia = ids("FM GU KI MH NR PW");
331
+ var polynesia = ids("PF TO TV WS");
332
+ var REGION_PRESETS = {
333
+ // Continents: disjoint, each one is the union of its subregions.
334
+ africa: [
335
+ ...northernAfrica,
336
+ ...easternAfrica,
337
+ ...middleAfrica,
338
+ ...southernAfrica,
339
+ ...westernAfrica
340
+ ],
341
+ asia: [...centralAsia, ...easternAsia, ...southeasternAsia, ...southernAsia, ...westernAsia],
342
+ europe: [...northernEurope, ...westernEurope, ...southernEurope, ...easternEurope],
343
+ northAmerica: [...usAndCanada, ...centralAmerica, ...caribbean],
344
+ southAmerica,
345
+ oceania: [...australiaAndNewZealand, ...melanesia, ...micronesia, ...polynesia],
346
+ // Subregions (UN geoscheme).
347
+ northernAfrica,
348
+ easternAfrica,
349
+ middleAfrica,
350
+ southernAfrica,
351
+ westernAfrica,
352
+ centralAsia,
353
+ easternAsia,
354
+ southeasternAsia,
355
+ southernAsia,
356
+ westernAsia,
357
+ northernEurope,
358
+ westernEurope,
359
+ southernEurope,
360
+ easternEurope,
361
+ usAndCanada,
362
+ centralAmerica,
363
+ caribbean,
364
+ australiaAndNewZealand,
365
+ melanesia,
366
+ micronesia,
367
+ polynesia,
368
+ // Political / commercial groupings: they may overlap with everything else. FIRST CUT, to be confirmed with
369
+ // the tools that will use them (see FEAT-022); each one is a maintenance cost.
370
+ eu27: ids("AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK"),
371
+ nordics: ids("DK FI IS NO SE"),
372
+ iberia: ids("ES PT"),
373
+ latam: ids("AR BO BR CL CO CR CU DO EC GT HN HT MX NI PA PE PR PY SV UY VE"),
374
+ mena: ids("AE BH DZ EG EH IL IQ IR JO KW LB LY MA OM PS QA SA SY TN YE")
375
+ };
376
+
377
+ // src/react/region.ts
378
+ var regionKey = (region) => typeof region === "string" ? region : region.join("\0");
379
+ var useRegion = (region) => {
380
+ const key = region === void 0 ? void 0 : regionKey(region);
381
+ return useMemo(
382
+ () => region === void 0 || key === void 0 ? void 0 : { key, ids: resolveRegion(region, REGION_PRESETS) },
383
+ // `key` summarizes the content of `region`.
384
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
385
+ [key]
386
+ );
387
+ };
388
+ var cache = /* @__PURE__ */ new WeakMap();
389
+ var withoutOverseas = (fc, region) => {
390
+ const byKey = cache.get(fc) ?? /* @__PURE__ */ new Map();
391
+ cache.set(fc, byKey);
392
+ const found = byKey.get(region.key);
393
+ if (found !== void 0) return found;
394
+ const features = dropOverseas(fc.features, OVERSEAS_TERRITORIES);
395
+ const created = features.every((feature, i) => feature === fc.features[i]) ? fc : { ...fc, features };
396
+ byKey.set(region.key, created);
397
+ return created;
398
+ };
399
+
400
+ // src/react/use-drill-down.ts
401
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState2, useSyncExternalStore as useSyncExternalStore2 } from "react";
402
+ var createDrillBridge = () => {
403
+ let status = "idle";
404
+ const listeners = /* @__PURE__ */ new Set();
405
+ return {
406
+ getStatus: () => status,
407
+ setStatus: (next) => {
408
+ if (next === status) return;
409
+ status = next;
410
+ listeners.forEach((listener) => listener());
411
+ },
412
+ subscribe: (listener) => {
413
+ listeners.add(listener);
414
+ return () => void listeners.delete(listener);
415
+ }
416
+ };
417
+ };
418
+ var DRILL_BRIDGE = /* @__PURE__ */ Symbol("choropleth.drillBridge");
419
+ var samePath = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
420
+ var warn = (message) => {
421
+ if (!isProduction()) console.warn(`[choropleth] useDrillDown: ${message}`);
422
+ };
423
+ var useDrillDown = ({
424
+ levels,
425
+ path: controlledPath,
426
+ defaultPath,
427
+ onPathChange,
428
+ getLabel
429
+ }) => {
430
+ useMemo2(() => validateLevels(levels), [levels]);
431
+ const controlled = controlledPath !== void 0;
432
+ const [innerPath, setInnerPath] = useState2(
433
+ () => clampPath(defaultPath ?? [], levels.length).path
434
+ );
435
+ const [bridge] = useState2(createDrillBridge);
436
+ const requested = controlled ? controlledPath : innerPath;
437
+ const { path, clamped } = clampPath(requested, levels.length);
438
+ const pathKey = JSON.stringify(requested);
439
+ useEffect3(() => {
440
+ if (clamped) {
441
+ warn(
442
+ `path ${pathKey} is deeper than the ${levels.length} level(s) allow; it was truncated to ${JSON.stringify(path)}.`
443
+ );
444
+ }
445
+ }, [clamped, pathKey]);
446
+ const latest = useRef3({ path, levels: levels.length, controlled, onPathChange });
447
+ useEffect3(() => {
448
+ latest.current = { path, levels: levels.length, controlled, onPathChange };
449
+ });
450
+ const commit = useCallback((next) => {
451
+ const current = latest.current;
452
+ const { path: valid, clamped: tooDeep } = clampPath(next, current.levels);
453
+ if (tooDeep) {
454
+ warn(
455
+ `cannot drill to ${JSON.stringify(next)}: only ${current.levels} level(s) are defined (max depth ${current.levels - 1}).`
456
+ );
457
+ }
458
+ if (samePath(valid, current.path)) return valid;
459
+ latest.current = { ...current, path: valid };
460
+ if (!current.controlled) setInnerPath(valid);
461
+ current.onPathChange?.(valid);
462
+ return valid;
463
+ }, []);
464
+ const drillDown = useCallback(
465
+ (id) => commit([...latest.current.path, id]),
466
+ [commit]
467
+ );
468
+ const drillUp = useCallback(
469
+ (steps = 1) => {
470
+ const up = Number.isFinite(steps) ? Math.max(0, Math.floor(steps)) : 0;
471
+ const { path: current } = latest.current;
472
+ return commit(current.slice(0, Math.max(0, current.length - up)));
473
+ },
474
+ [commit]
475
+ );
476
+ const drillTo = commit;
477
+ const reset = useCallback(() => commit([]), [commit]);
478
+ const status = useSyncExternalStore2(bridge.subscribe, bridge.getStatus, bridge.getStatus);
479
+ const rootLabel = levels[0]?.label ?? levels[0]?.id ?? "";
480
+ const breadcrumbs = useMemo2(
481
+ () => buildBreadcrumbs({
482
+ path,
483
+ rootLabel,
484
+ getLabel: getLabel === void 0 ? void 0 : ({ id, depth }) => getLabel({ id, depth })
485
+ }),
486
+ // `getLabel` is usually an inline function: its identity must not recompute the breadcrumbs on every render.
487
+ // The route (`pathKey`) and the root do.
488
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
489
+ [pathKey, rootLabel]
490
+ );
491
+ const level = levels[path.length] ?? levels[levels.length - 1];
492
+ const context = useMemo2(() => contextOf(path), [path]);
493
+ return useMemo2(
494
+ () => ({
495
+ levels,
496
+ path,
497
+ context,
498
+ level,
499
+ breadcrumbs,
500
+ status,
501
+ canDrillUp: path.length > 0,
502
+ drillDown,
503
+ drillUp,
504
+ drillTo,
505
+ reset,
506
+ [DRILL_BRIDGE]: bridge
507
+ }),
508
+ [levels, path, context, level, breadcrumbs, status, drillDown, drillUp, drillTo, reset, bridge]
509
+ );
510
+ };
511
+ var isDrillController = (drill) => !Array.isArray(drill);
512
+
513
+ // src/react/use-drill-engine.ts
514
+ var REJECTED_AFTER_MS = 1e3;
515
+ var defaultFeatureId = (feature) => feature.id === void 0 ? void 0 : String(feature.id);
516
+ var collectionIds = /* @__PURE__ */ new WeakMap();
517
+ var nextCollectionId = 1;
518
+ var idOfCollection = (collection) => {
519
+ if (collection === void 0) return "-";
520
+ let id = collectionIds.get(collection);
521
+ if (id === void 0) {
522
+ id = nextCollectionId++;
523
+ collectionIds.set(collection, id);
524
+ }
525
+ return String(id);
526
+ };
527
+ var samePath2 = (a, b) => a.length === b.length && a.every((id, i) => id === b[i]);
528
+ var collections = /* @__PURE__ */ new WeakMap();
529
+ var filtered = /* @__PURE__ */ new WeakMap();
530
+ var featureCollectionOf = (geography, objectName) => {
531
+ const byName = collections.get(geography) ?? /* @__PURE__ */ new Map();
532
+ collections.set(geography, byName);
533
+ const name = objectName ?? "";
534
+ const found = byName.get(name);
535
+ if (found !== void 0) return found;
536
+ const created = toFeatureCollection({ geography, objectName });
537
+ byName.set(name, created);
538
+ return created;
539
+ };
540
+ var stableFilter = (raw, keep) => {
541
+ const kept = raw.features.flatMap((feature, index) => keep(feature) ? [index] : []);
542
+ if (kept.length === raw.features.length) return raw;
543
+ const signature = kept.join(",");
544
+ const bySignature = filtered.get(raw) ?? /* @__PURE__ */ new Map();
545
+ filtered.set(raw, bySignature);
546
+ const found = bySignature.get(signature);
547
+ if (found !== void 0) return found;
548
+ const created = {
549
+ ...raw,
550
+ features: kept.flatMap((index) => {
551
+ const feature = raw.features[index];
552
+ return feature === void 0 ? [] : [feature];
553
+ })
554
+ };
555
+ bySignature.set(signature, created);
556
+ return created;
557
+ };
558
+ var isSync = (input) => typeof input !== "function" && !("then" in input);
559
+ var resolveInput = (input) => isSync(input) ? Promise.resolve(input) : (typeof input === "function" ? input() : input).then(unwrapGeography);
560
+ var fixedGeography = (level) => {
561
+ const { geography } = level;
562
+ return typeof geography === "function" || geography === "inherit" || !isSync(geography) ? void 0 : geography;
563
+ };
564
+ var NO_LEVELS = [
565
+ { id: "root", geography: { type: "FeatureCollection", features: [] } }
566
+ ];
567
+ var EMPTY_PATH = [];
568
+ var EMPTY_COLLECTION = { type: "FeatureCollection", features: [] };
569
+ var useDrillEngine = ({
570
+ drill,
571
+ data,
572
+ filterFeatures,
573
+ region,
574
+ store,
575
+ onDrill,
576
+ onDrillEnd,
577
+ onDrillError
578
+ }) => {
579
+ const active = drill !== void 0;
580
+ const internal = useDrillDown({
581
+ levels: drill === void 0 || isDrillController(drill) ? NO_LEVELS : drill
582
+ });
583
+ const controller = drill === void 0 ? null : isDrillController(drill) ? drill : internal;
584
+ const levels = controller?.levels ?? NO_LEVELS;
585
+ const target = controller?.path ?? EMPTY_PATH;
586
+ const targetKey = JSON.stringify(target);
587
+ const [machine, dispatch] = useReducer(drillReducer, target, createDrillState);
588
+ const [cache2] = useState3(() => createLruCache());
589
+ const [version, setVersion] = useState3(0);
590
+ const [failed, setFailed] = useState3(/* @__PURE__ */ new Map());
591
+ const pending = useRef4(/* @__PURE__ */ new Set());
592
+ const sourceOf = useCallback2((depth2) => sourceDepth(levels, depth2), [levels]);
593
+ const keyAt = (depth2, path) => geographyKeyFor(levels, path.slice(0, sourceOf(depth2)));
594
+ const geographyAt = (depth2, path) => {
595
+ const level2 = levels[sourceOf(depth2)];
596
+ if (level2 === void 0) return void 0;
597
+ return fixedGeography(level2) ?? cache2.get(keyAt(depth2, path));
598
+ };
599
+ const mounted = useRef4(false);
600
+ useEffect4(() => {
601
+ mounted.current = true;
602
+ return () => {
603
+ mounted.current = false;
604
+ };
605
+ }, []);
606
+ const request = (path, prefetching, retryFailed = false) => {
607
+ for (let depth2 = 0; depth2 <= path.length; depth2++) {
608
+ const source = sourceOf(depth2);
609
+ const level2 = levels[source];
610
+ if (level2 === void 0 || fixedGeography(level2) !== void 0) continue;
611
+ const key = keyAt(depth2, path);
612
+ if (cache2.has(key) || pending.current.has(key)) continue;
613
+ if (failed.has(key) && !retryFailed) continue;
614
+ const input = typeof level2.geography === "function" ? level2.geography(contextOf(path.slice(0, source))) : level2.geography;
615
+ if (input === "inherit") continue;
616
+ pending.current.add(key);
617
+ resolveInput(input).then(
618
+ (loaded) => {
619
+ pending.current.delete(key);
620
+ cache2.set(key, loaded);
621
+ if (mounted.current) setVersion((v) => v + 1);
622
+ },
623
+ (error) => {
624
+ pending.current.delete(key);
625
+ if (!mounted.current) return;
626
+ if (prefetching) setVersion((v) => v + 1);
627
+ else setFailed((previous2) => new Map(previous2).set(key, error));
628
+ }
629
+ );
630
+ }
631
+ };
632
+ const latestRequest = useRef4(request);
633
+ useEffect4(() => {
634
+ latestRequest.current = request;
635
+ });
636
+ const prefetch = useCallback2(
637
+ (path) => {
638
+ if (active) latestRequest.current(path, true);
639
+ },
640
+ [active]
641
+ );
642
+ const requestedTarget = useRef4("");
643
+ useEffect4(() => {
644
+ const retryFailed = requestedTarget.current !== targetKey;
645
+ requestedTarget.current = targetKey;
646
+ if (active) request(target, false, retryFailed);
647
+ }, [active, targetKey, levels, version]);
648
+ const keepFor = (depth2, path) => {
649
+ const source = sourceOf(depth2);
650
+ return (feature) => {
651
+ for (let k = source; k <= depth2; k++) {
652
+ const level2 = levels[k];
653
+ if (level2?.filterFeatures?.(feature, contextOf(path.slice(0, k))) === false) return false;
654
+ }
655
+ if (source !== 0) return true;
656
+ if (region !== void 0) {
657
+ const id = (levels[0]?.getFeatureId ?? defaultFeatureId)(feature);
658
+ if (id === void 0 || !region.ids.has(id)) return false;
659
+ }
660
+ return filterFeatures === void 0 || filterFeatures(feature);
661
+ };
662
+ };
663
+ const featuresAt = (depth2, path) => {
664
+ const geography2 = geographyAt(depth2, path);
665
+ if (geography2 === void 0) return void 0;
666
+ const source = sourceOf(depth2);
667
+ const raw = featureCollectionOf(geography2, levels[source]?.objectName);
668
+ const kept = stableFilter(raw, keepFor(depth2, path));
669
+ return source === 0 && region !== void 0 ? withoutOverseas(kept, region) : kept;
670
+ };
671
+ const rootGeography = active ? geographyAt(0, EMPTY_PATH) : void 0;
672
+ const rootAll = rootGeography === void 0 || region === void 0 ? void 0 : featureCollectionOf(rootGeography, levels[sourceOf(0)]?.objectName);
673
+ const featuresFor = (path) => Array.from({ length: path.length + 1 }, (_, d) => featuresAt(d, path));
674
+ const chainLevelOf = (depth2, fc) => ({
675
+ features: fc.features,
676
+ idOf: levels[depth2]?.getFeatureId ?? levels[sourceOf(depth2)]?.getFeatureId ?? defaultFeatureId
677
+ });
678
+ const chainLevelsFor = (path) => {
679
+ const collectionsByDepth = featuresFor(path);
680
+ const parents = collectionsByDepth.slice(0, path.length);
681
+ if (parents.some((fc) => fc === void 0)) return null;
682
+ return collectionsByDepth.map((fc, depth2) => chainLevelOf(depth2, fc ?? EMPTY_COLLECTION));
683
+ };
684
+ const targetFeatures = active ? featuresFor(target) : [];
685
+ const targetGeometry = active && targetFeatures.every((f) => f !== void 0);
686
+ const targetEmpty = targetGeometry && (targetFeatures[target.length]?.features.length ?? 0) === 0;
687
+ const liveData = resolveDrillData(data, contextOf(target));
688
+ const targetReady = targetGeometry && !targetEmpty && liveData !== void 0;
689
+ const shownPath = machine.phase === "revealing" ? machine.path : machine.settledPath;
690
+ const shownKey = JSON.stringify(shownPath);
691
+ const shownIsTarget = samePath2(shownPath, target);
692
+ const shownFeatures = active ? shownIsTarget ? targetFeatures : featuresFor(shownPath) : [];
693
+ const hasShown = active && shownFeatures.every((f) => f !== void 0);
694
+ const [latest] = useState3(() => ({
695
+ data: void 0,
696
+ path: shownPath,
697
+ chain: [],
698
+ fills: /* @__PURE__ */ new Map()
699
+ }));
700
+ const rememberFills = useCallback2(
701
+ (fills) => {
702
+ latest.fills = fills;
703
+ },
704
+ [latest]
705
+ );
706
+ const chainSig = `${shownKey}|${shownFeatures.map(idOfCollection).join(",")}`;
707
+ const chain = useMemo3(
708
+ () => shownFeatures.flatMap((fc, depth2) => fc === void 0 ? [] : [chainLevelOf(depth2, fc)]),
709
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
710
+ [chainSig, levels]
711
+ );
712
+ const [seen, setSeen] = useState3(shownPath);
713
+ const [previous, setPrevious] = useState3(null);
714
+ if (!samePath2(seen, shownPath)) {
715
+ setSeen(shownPath);
716
+ setPrevious({ path: latest.path, chain: latest.chain, fills: latest.fills });
717
+ }
718
+ useEffect4(() => {
719
+ latest.path = shownPath;
720
+ latest.chain = chain;
721
+ if (shownIsTarget && liveData !== void 0) latest.data = liveData;
722
+ });
723
+ const depth = shownPath.length;
724
+ const level = active ? levels[depth] ?? null : null;
725
+ const sourceLevel = levels[sourceOf(depth)];
726
+ const features = hasShown ? shownFeatures[depth] : void 0;
727
+ const geography = hasShown ? geographyAt(depth, shownPath) : void 0;
728
+ const emptyError = useMemo3(() => {
729
+ if (!targetEmpty) return null;
730
+ const parent = target.at(-1) ?? null;
731
+ const id = levels[target.length]?.id ?? "?";
732
+ return new Error(
733
+ `[choropleth] drill level "${id}" has 0 features for "${parent}". Hint: return false from canDrill for it.`
734
+ );
735
+ }, [targetEmpty, targetKey, levels]);
736
+ const failure = (() => {
737
+ if (!active) return null;
738
+ for (let d = 0; d <= target.length; d++) {
739
+ const error = failed.get(keyAt(d, target));
740
+ if (error !== void 0) return { error };
741
+ }
742
+ return emptyError === null ? null : { error: emptyError };
743
+ })();
744
+ const latestProps = useRef4({ onDrill, onDrillEnd, onDrillError, controller, target, machine });
745
+ useEffect4(() => {
746
+ latestProps.current = { onDrill, onDrillEnd, onDrillError, controller, target, machine };
747
+ });
748
+ const waiters = useRef4([]);
749
+ const finish = (waiter, completed) => {
750
+ clearTimeout(waiter.timer);
751
+ waiters.current = waiters.current.filter((w) => w !== waiter);
752
+ waiter.resolve({ completed });
753
+ };
754
+ const arrived = (waiter) => {
755
+ const { target: wanted, machine: current } = latestProps.current;
756
+ if (!samePath2(wanted, waiter.target)) return false;
757
+ waiter.seen = true;
758
+ return current.phase === "idle" && samePath2(current.settledPath, waiter.target);
759
+ };
760
+ const navigate = useCallback2((run) => {
761
+ const ctl = latestProps.current.controller;
762
+ if (ctl === null) return Promise.resolve({ completed: false });
763
+ const wanted = run(ctl);
764
+ return new Promise((resolve) => {
765
+ const waiter = { target: wanted, resolve, seen: false, timer: void 0 };
766
+ waiter.timer = setTimeout(() => {
767
+ if (!waiter.seen) finish(waiter, false);
768
+ }, REJECTED_AFTER_MS);
769
+ for (const other of waiters.current) {
770
+ if (!samePath2(other.target, wanted)) finish(other, false);
771
+ }
772
+ waiters.current.push(waiter);
773
+ if (arrived(waiter)) finish(waiter, true);
774
+ });
775
+ }, []);
776
+ useEffect4(() => {
777
+ for (const waiter of waiters.current) {
778
+ if (arrived(waiter)) finish(waiter, true);
779
+ else if (waiter.seen && !samePath2(target, waiter.target)) finish(waiter, false);
780
+ }
781
+ });
782
+ useEffect4(
783
+ () => () => {
784
+ for (const waiter of waiters.current) finish(waiter, false);
785
+ },
786
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
787
+ []
788
+ );
789
+ const dispatched = useRef4(target);
790
+ const machineAtDispatch = useRef4(machine);
791
+ const clicked = useRef4(void 0);
792
+ const noteItem = useCallback2((item) => {
793
+ clicked.current = item;
794
+ }, []);
795
+ const settledKey = JSON.stringify(machine.settledPath);
796
+ useEffect4(() => {
797
+ if (!active || controller === null) return;
798
+ if (samePath2(dispatched.current, target)) return;
799
+ const direction = classifyDrill(machine.settledPath, target);
800
+ const eager = direction === "down" || direction === "lateral" || direction === "equal";
801
+ if (!eager && !targetReady) return;
802
+ dispatched.current = target;
803
+ machineAtDispatch.current = machine;
804
+ const view = store.getZoom()?.getView();
805
+ dispatch({
806
+ type: "DRILL",
807
+ to: target,
808
+ cached: targetReady,
809
+ ...view === void 0 ? {} : { camera: { k: view.k, x: view.x, y: view.y } }
810
+ });
811
+ }, [active, targetKey, targetReady, settledKey]);
812
+ useEffect4(() => {
813
+ if (!active || machine.geometry !== "pending" || machine.geometryKey === null) return;
814
+ if (targetReady && samePath2(machine.path, target)) {
815
+ dispatch({ type: "GEOMETRY_READY", key: machine.geometryKey });
816
+ }
817
+ }, [active, targetReady, machine.geometry, machine.geometryKey]);
818
+ const reported = useRef4(null);
819
+ useEffect4(() => {
820
+ if (!active || failure === null || reported.current === failure.error) return;
821
+ reported.current = failure.error;
822
+ const { onDrillError: report, controller: ctl, machine: current } = latestProps.current;
823
+ const wanted = latestProps.current.target;
824
+ if (report !== void 0) report({ error: failure.error, ctx: contextOf(wanted) });
825
+ else if (!isProduction()) {
826
+ const reason = failure.error instanceof Error ? failure.error.message : String(failure.error);
827
+ console.error(
828
+ reason.startsWith("[choropleth]") ? reason : `[choropleth] drill: failed to load the geometry for ${JSON.stringify(wanted)}: ${reason}`
829
+ );
830
+ }
831
+ if (samePath2(dispatched.current, wanted) && machine.geometryKey !== null) {
832
+ dispatch({ type: "GEOMETRY_ERROR", key: machine.geometryKey });
833
+ } else if (ctl !== null) {
834
+ dispatched.current = current.settledPath;
835
+ ctl.drillTo(current.settledPath);
836
+ }
837
+ }, [active, failure]);
838
+ const machinePathKey = JSON.stringify(machine.path);
839
+ useEffect4(() => {
840
+ if (!active || controller === null) return;
841
+ if (machine === machineAtDispatch.current) return;
842
+ if (!samePath2(dispatched.current, target) || samePath2(machine.path, target)) return;
843
+ dispatched.current = machine.path;
844
+ machineAtDispatch.current = machine;
845
+ controller.drillTo(machine.path);
846
+ }, [active, machinePathKey, targetKey]);
847
+ const lastTarget = useRef4(target);
848
+ useEffect4(() => {
849
+ if (!active || samePath2(lastTarget.current, target)) return;
850
+ const event = buildDrillEvent({
851
+ from: lastTarget.current,
852
+ to: target,
853
+ item: clicked.current
854
+ });
855
+ lastTarget.current = target;
856
+ clicked.current = void 0;
857
+ if (event !== null) latestProps.current.onDrill?.(event);
858
+ }, [active, targetKey]);
859
+ const lastEnd = useRef4(machine.settledPath);
860
+ useEffect4(() => {
861
+ if (!active || machine.phase !== "idle") return;
862
+ const previousEnd = lastEnd.current;
863
+ if (samePath2(previousEnd, machine.settledPath)) return;
864
+ lastEnd.current = machine.settledPath;
865
+ const event = buildDrillEvent({ from: previousEnd, to: machine.settledPath });
866
+ if (event !== null) latestProps.current.onDrillEnd?.(event);
867
+ }, [active, machine.phase, settledKey]);
868
+ const status = !active ? "loading" : hasShown ? "ready" : failure !== null ? "error" : "loading";
869
+ const bridge = controller?.[DRILL_BRIDGE];
870
+ const phase = machine.phase;
871
+ useEffect4(() => {
872
+ bridge?.setStatus(phase);
873
+ }, [bridge, phase]);
874
+ return {
875
+ active,
876
+ controller,
877
+ levels,
878
+ shownPath,
879
+ shownKey,
880
+ depth,
881
+ level,
882
+ geography,
883
+ status,
884
+ objectName: sourceLevel?.objectName,
885
+ features,
886
+ levelFeatures: shownFeatures,
887
+ getFeatureId: level?.getFeatureId ?? sourceLevel?.getFeatureId,
888
+ chain,
889
+ chainSig,
890
+ filterFeatures: active && hasShown ? keepFor(depth, shownPath) : void 0,
891
+ rootFeatures: hasShown ? shownFeatures[0] : void 0,
892
+ rootAll,
893
+ data: !active ? void 0 : shownIsTarget ? liveData : latest.data,
894
+ noteItem,
895
+ machine,
896
+ dispatch,
897
+ previous,
898
+ rememberFills,
899
+ chainLevelsFor,
900
+ target,
901
+ prefetch,
902
+ navigate
903
+ };
904
+ };
905
+
906
+ // src/react/use-geography.ts
907
+ import { useEffect as useEffect5, useState as useState4 } from "react";
908
+ var promisesByKey = /* @__PURE__ */ new Map();
909
+ var promisesByIdentity = /* @__PURE__ */ new WeakMap();
910
+ var valuesByKey = /* @__PURE__ */ new Map();
911
+ var valuesByIdentity = /* @__PURE__ */ new WeakMap();
912
+ var isDeferred = (input) => typeof input === "function" || "then" in input;
913
+ var peek = (input, key) => {
914
+ if (!isDeferred(input)) return input;
915
+ return key === void 0 ? valuesByIdentity.get(input) : valuesByKey.get(key);
916
+ };
917
+ var load = (input, key) => {
918
+ const cached = key === void 0 ? promisesByIdentity.get(input) : promisesByKey.get(key);
919
+ if (cached !== void 0) return cached;
920
+ const promise = (typeof input === "function" ? input() : input).then((module) => {
921
+ const value = unwrapGeography(module);
922
+ if (key === void 0) valuesByIdentity.set(input, value);
923
+ else valuesByKey.set(key, value);
924
+ return value;
925
+ });
926
+ if (key === void 0) promisesByIdentity.set(input, promise);
927
+ else promisesByKey.set(key, promise);
928
+ promise.catch(() => {
929
+ if (key === void 0) promisesByIdentity.delete(input);
930
+ else promisesByKey.delete(key);
931
+ });
932
+ return promise;
933
+ };
934
+ var useGeography = (input, key, onError) => {
935
+ const ready = peek(input, key);
936
+ const [loaded, setLoaded] = useState4(void 0);
937
+ const [failed, setFailed] = useState4(false);
938
+ useEffect5(() => {
939
+ if (ready !== void 0 || !isDeferred(input)) return;
940
+ let cancelled = false;
941
+ load(input, key).then(
942
+ (value) => {
943
+ if (cancelled) return;
944
+ setFailed(false);
945
+ setLoaded(value);
946
+ },
947
+ (reason) => {
948
+ if (cancelled) return;
949
+ const error = reason instanceof Error ? reason : new Error(String(reason));
950
+ setFailed(true);
951
+ if (onError === void 0)
952
+ console.error(`[choropleth] Failed to load geography: ${error.message}`);
953
+ else onError(error);
954
+ }
955
+ );
956
+ return () => {
957
+ cancelled = true;
958
+ };
959
+ }, [input, key, ready]);
960
+ const geography = ready ?? loaded;
961
+ if (geography !== void 0) return { geography, status: "ready" };
962
+ return { geography: void 0, status: failed ? "error" : "loading" };
963
+ };
964
+ var clearGeographyCache = () => {
965
+ promisesByKey.clear();
966
+ valuesByKey.clear();
967
+ };
968
+
969
+ // src/react/use-choropleth.ts
970
+ var FALLBACK_POINT_RADIUS = 4;
971
+ var useChoropleth = (rawOptions) => {
972
+ const [store] = useState5(createMapStore);
973
+ const region = useRegion(rawOptions.region);
974
+ const engine = useDrillEngine({
975
+ drill: rawOptions.drill,
976
+ data: rawOptions.data,
977
+ filterFeatures: rawOptions.filterFeatures,
978
+ region,
979
+ store,
980
+ onDrill: rawOptions.onDrill,
981
+ onDrillEnd: rawOptions.onDrillEnd,
982
+ onDrillError: rawOptions.onDrillError
983
+ });
984
+ const encoding = engine.level?.encoding;
985
+ const options = engine.active ? {
986
+ ...rawOptions,
987
+ ...encoding,
988
+ getId: encoding?.getId ?? rawOptions.getId,
989
+ data: engine.data,
990
+ getFeatureId: engine.getFeatureId ?? rawOptions.getFeatureId
991
+ } : { ...rawOptions, data: resolveDrillData(rawOptions.data, contextOf([])) };
992
+ const { getId, getFeatureId, projection, fallbackPoints } = options;
993
+ const isBubble = options.variant === "bubble";
994
+ const labels = resolveLabels(options.labels);
995
+ const single = useGeography(
996
+ engine.active ? NO_GEOGRAPHY : options.geography ?? NO_GEOGRAPHY,
997
+ options.geographyKey,
998
+ options.onGeographyError
999
+ );
1000
+ if (!engine.active && options.geography === void 0) {
1001
+ throw new Error(
1002
+ "[choropleth] Missing `geography`: pass `geography` (TopoJSON/GeoJSON) or `drill` (a list of levels)."
1003
+ );
1004
+ }
1005
+ const geography = engine.active ? engine.geography : single.geography;
1006
+ const status = engine.active ? engine.status : single.status;
1007
+ const objectName = engine.active ? engine.objectName : options.objectName;
1008
+ const regionIdOf = getFeatureId ?? defaultFeatureId2;
1009
+ const userFilter = engine.active ? engine.filterFeatures : options.filterFeatures;
1010
+ const filterFeatures = engine.active || region === void 0 ? userFilter : (feature) => {
1011
+ const id = regionIdOf(feature);
1012
+ return id !== void 0 && region.ids.has(id) && (userFilter?.(feature) ?? true);
1013
+ };
1014
+ const allFeatures = useMemo4(
1015
+ () => engine.active ? engine.features ?? EMPTY_FEATURES : geography === void 0 ? EMPTY_FEATURES : toFeatureCollection({ geography, objectName }),
1016
+ // `engine.features` already comes filtered and is stable (the engine's caches).
1017
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
1018
+ [engine.active, engine.features, geography, objectName]
1019
+ );
1020
+ const keptIndexes = filterFeatures === void 0 ? null : allFeatures.features.flatMap((f, index) => filterFeatures(f) ? [index] : []);
1021
+ const keptKey = keptIndexes === null ? "" : keptIndexes.join(",");
1022
+ const features = useMemo4(() => {
1023
+ const kept = keptIndexes === null ? allFeatures : {
1024
+ ...allFeatures,
1025
+ features: keptIndexes.flatMap((index) => {
1026
+ const feature = allFeatures.features[index];
1027
+ return feature === void 0 ? [] : [feature];
1028
+ })
1029
+ };
1030
+ return engine.active || region === void 0 ? kept : withoutOverseas(kept, region);
1031
+ }, [allFeatures, keptKey, engine.active, region?.key]);
1032
+ const bordersGeometry = useMemo4(
1033
+ () => geography === void 0 ? null : createBorders({ geography, objectName, filterFeatures }),
1034
+ // `filterFeatures` is summarized in `keptKey`: its identity must not relaunch the computation.
1035
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
1036
+ [geography, objectName, keptKey]
1037
+ );
1038
+ const rotationFeatures = engine.active ? engine.rootFeatures : features;
1039
+ const rotation = useMemo4(() => {
1040
+ if (region === void 0 || rotationFeatures === void 0) return void 0;
1041
+ const points = Object.entries(fallbackPoints ?? {}).flatMap(
1042
+ ([id, point]) => region.ids.has(id) ? [point] : []
1043
+ );
1044
+ return regionRotation(rotationFeatures.features, points);
1045
+ }, [region?.key, rotationFeatures, fallbackPoints]);
1046
+ const rotateLon = rotation?.[0];
1047
+ const rotate = useMemo4(
1048
+ () => rotateLon === void 0 ? void 0 : [rotateLon, 0],
1049
+ [rotateLon]
1050
+ );
1051
+ const autoAspect = useMemo4(
1052
+ () => options.aspectRatio === "auto" ? computeAspectRatio({
1053
+ name: projection,
1054
+ // With drill the proportion is the root's: it must not change when going down a level.
1055
+ featureCollection: (engine.active ? engine.rootFeatures : void 0) ?? features,
1056
+ rotate
1057
+ }) : void 0,
1058
+ [options.aspectRatio, projection, features, engine.active, engine.rootFeatures, rotate]
1059
+ );
1060
+ const measured = useContainerSize({
1061
+ aspectRatio: options.aspectRatio === "auto" ? autoAspect : options.aspectRatio
1062
+ });
1063
+ const width = options.width ?? measured.width;
1064
+ const height = options.height ?? measured.height;
1065
+ const rebasePadding = typeof options.zoom === "object" ? options.zoom.padding ?? 24 : 24;
1066
+ const drillFocusBounds = engine.levels.reduce(
1067
+ (all, level) => ({ ...all, ...level.focusBounds }),
1068
+ { ...DEFAULT_FOCUS_BOUNDS, ...options.focusBounds }
1069
+ );
1070
+ const focusKey = JSON.stringify(drillFocusBounds);
1071
+ const chain = useMemo4(
1072
+ () => engine.active && engine.chain.length > 0 && width > 0 && height > 0 ? buildProjectionChain({
1073
+ levels: engine.chain,
1074
+ path: engine.shownPath,
1075
+ name: projection,
1076
+ width,
1077
+ height,
1078
+ padding: rebasePadding,
1079
+ focusBounds: drillFocusBounds,
1080
+ rotate
1081
+ }) : null,
1082
+ // `chainSig` summarizes the content of `engine.chain`; `focusKey`, that of `drillFocusBounds`.
1083
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
1084
+ [engine.active, engine.chainSig, projection, width, height, rebasePadding, focusKey, rotate]
1085
+ );
1086
+ const chainMissing = chain?.missing ?? null;
1087
+ useEffect6(() => {
1088
+ if (chainMissing !== null && !isProduction()) {
1089
+ console.warn(
1090
+ `[choropleth] drill: could not frame region "${chainMissing}" (it is not in its parent level, or it has no extent). The map keeps the parent's framing.`
1091
+ );
1092
+ }
1093
+ }, [chainMissing]);
1094
+ const chainFor = (levels, path) => buildProjectionChain({
1095
+ levels,
1096
+ path,
1097
+ name: projection,
1098
+ width,
1099
+ height,
1100
+ padding: rebasePadding,
1101
+ focusBounds: drillFocusBounds,
1102
+ rotate
1103
+ });
1104
+ useDrillChoreography({ engine, store, shownChain: chain, chainFor });
1105
+ const { machine, previous } = engine;
1106
+ const leaving = machine.layers.some((layer) => layer.role === "exiting");
1107
+ const previousChain = useMemo4(
1108
+ () => engine.active && previous !== null && width > 0 && height > 0 && (leaving || machine.direction === "up") ? buildProjectionChain({
1109
+ levels: [...previous.chain],
1110
+ path: previous.path,
1111
+ name: projection,
1112
+ width,
1113
+ height,
1114
+ padding: rebasePadding,
1115
+ focusBounds: drillFocusBounds,
1116
+ rotate
1117
+ }) : null,
1118
+ // `previous` changes only when the level changes; `focusKey` summarizes `drillFocusBounds`.
1119
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
1120
+ [
1121
+ engine.active,
1122
+ previous,
1123
+ leaving,
1124
+ machine.direction,
1125
+ projection,
1126
+ width,
1127
+ height,
1128
+ rebasePadding,
1129
+ focusKey,
1130
+ rotate
1131
+ ]
1132
+ );
1133
+ const activeProjection = chain?.projections[chain.projections.length - 1];
1134
+ const layersKey = machine.layers.map((layer) => `${layer.key}:${layer.role}`).join("|");
1135
+ const contextMode = engine.level?.context ?? "siblings";
1136
+ const drillLayers = useMemo4(() => {
1137
+ if (!engine.active || chain === null || activeProjection === void 0) return [];
1138
+ const views = [];
1139
+ const activeSource = sourceDepth(engine.levels, engine.depth);
1140
+ for (const layer of machine.layers) {
1141
+ let features2;
1142
+ let projectionOf;
1143
+ let idOf2;
1144
+ let focusId;
1145
+ if (layer.role === "context") {
1146
+ if (contextMode === "none" || layer.depth >= engine.depth) continue;
1147
+ if (sourceDepth(engine.levels, layer.depth) === activeSource) continue;
1148
+ features2 = engine.levelFeatures[layer.depth]?.features;
1149
+ projectionOf = chain.projections[layer.depth];
1150
+ idOf2 = engine.chain[layer.depth]?.idOf;
1151
+ focusId = engine.shownPath[layer.depth];
1152
+ } else if (layer.role === "exiting") {
1153
+ features2 = previous?.chain[layer.depth]?.features;
1154
+ projectionOf = previousChain?.projections[layer.depth];
1155
+ idOf2 = previous?.chain[layer.depth]?.idOf;
1156
+ } else {
1157
+ continue;
1158
+ }
1159
+ if (features2 === void 0 || projectionOf === void 0 || idOf2 === void 0) continue;
1160
+ const path = geoPath(projectionOf);
1161
+ const level = engine.levels[layer.depth];
1162
+ const paths2 = features2.flatMap((feature) => {
1163
+ const d = path(feature);
1164
+ if (d == null) return [];
1165
+ const id = idOf2(feature);
1166
+ const drillable = layer.role === "context" && id !== void 0 && isDrillable(engine.levels, layer.depth, {
1167
+ kind: "region",
1168
+ id,
1169
+ feature,
1170
+ datum: void 0,
1171
+ level: { id: level?.id ?? ROOT_LEVEL.id, depth: layer.depth },
1172
+ drillable: true
1173
+ });
1174
+ const fill = layer.role === "exiting" && id !== void 0 ? previous?.fills.get(id) : void 0;
1175
+ return [{ id, d, focus: id !== void 0 && id === focusId, drillable, fill }];
1176
+ });
1177
+ views.push({
1178
+ key: layer.key,
1179
+ role: layer.role,
1180
+ depth: layer.depth,
1181
+ transform: relativeTransform(projectionOf, activeProjection),
1182
+ paths: paths2
1183
+ });
1184
+ }
1185
+ return views;
1186
+ }, [engine.active, chain, previousChain, previous, layersKey, engine.chainSig, contextMode]);
1187
+ const controller = engine.controller;
1188
+ const drillNav = useMemo4(() => {
1189
+ if (!engine.active || controller === null) return null;
1190
+ const rootLabel = engine.levels[0]?.label ?? labels.breadcrumbRoot;
1191
+ const items = controller.breadcrumbs.map((crumb) => {
1192
+ let label = crumb.label;
1193
+ if (crumb.id === null) label = rootLabel;
1194
+ else if (crumb.label === crumb.id) {
1195
+ const parent = engine.chain[crumb.depth - 1];
1196
+ const feature = parent?.features.find((f) => parent.idOf(f) === crumb.id);
1197
+ label = (feature === void 0 ? void 0 : nameOf(feature)) ?? crumb.id;
1198
+ }
1199
+ return {
1200
+ depth: crumb.depth,
1201
+ id: crumb.id,
1202
+ label,
1203
+ current: crumb.depth === controller.path.length
1204
+ };
1205
+ });
1206
+ return {
1207
+ items,
1208
+ canDrillUp: controller.canDrillUp,
1209
+ drillTo: (depth) => controller.drillTo(controller.path.slice(0, depth)),
1210
+ drillUp: (steps) => controller.drillUp(steps)
1211
+ };
1212
+ }, [
1213
+ engine.active,
1214
+ controller,
1215
+ controller?.path,
1216
+ engine.chainSig,
1217
+ engine.levels,
1218
+ labels.breadcrumbRoot
1219
+ ]);
1220
+ const drillFocusId = engine.active && (machine.phase === "flying" || machine.phase === "loading") && machine.direction !== "up" && machine.direction !== null ? machine.path.at(-1) ?? null : null;
1221
+ const { paths, bordersPath, anchors, project, invert, toPath } = useMemo4(() => {
1222
+ const geoProjection = chain?.projections[chain.projections.length - 1] ?? createProjection({
1223
+ name: projection,
1224
+ width,
1225
+ height,
1226
+ featureCollection: features,
1227
+ rotate
1228
+ });
1229
+ const toPath2 = geoPath(geoProjection);
1230
+ const toPoint = (coordinates) => {
1231
+ const at = geoProjection([...coordinates]);
1232
+ return at !== null && Number.isFinite(at[0]) && Number.isFinite(at[1]) ? at : null;
1233
+ };
1234
+ return {
1235
+ paths: features.features.map((f) => toPath2(f)),
1236
+ bordersPath: bordersGeometry === null ? null : toPath2(bordersGeometry),
1237
+ // Anchors are only computed for `bubble` (the only thing that uses them). `geoPath`'s centroid counts
1238
+ // distant territories (France's bubble would land in the Atlantic): the anchor is the pole of
1239
+ // inaccessibility of the main body. The centroid remains as a fallback.
1240
+ anchors: isBubble ? features.features.map((f) => {
1241
+ const anchor = getAnchorPoint({
1242
+ geometry: getFocusGeometry({ feature: f, path: toPath2 }),
1243
+ project: toPoint
1244
+ });
1245
+ if (anchor !== null) return anchor;
1246
+ const [x, y] = toPath2.centroid(f);
1247
+ return Number.isFinite(x) && Number.isFinite(y) ? [x, y] : null;
1248
+ }) : [],
1249
+ project: toPoint,
1250
+ toPath: toPath2,
1251
+ // The reverse: pixels → [lon, lat]. Used by `toViewState` for the public `ViewState`.
1252
+ invert: (point) => {
1253
+ const at = geoProjection.invert?.([point[0], point[1]]);
1254
+ return at != null && Number.isFinite(at[0]) && Number.isFinite(at[1]) ? [at[0], at[1]] : null;
1255
+ }
1256
+ };
1257
+ }, [features, bordersGeometry, projection, width, height, isBubble, chain, rotate]);
1258
+ const lastData = useRef5(options.data);
1259
+ if (options.data !== void 0) lastData.current = options.data;
1260
+ const data = options.data ?? lastData.current ?? NO_DATA;
1261
+ const dataLoading = options.loading ?? options.data === void 0;
1262
+ const idOf = getFeatureId ?? defaultFeatureId2;
1263
+ const join = joinData({ features: features.features, data, getId, getFeatureId: idOf });
1264
+ const outOfRegion = [];
1265
+ let unmatchedIds = join.unmatchedData;
1266
+ const regionDrawn = !engine.active || sourceDepth(engine.levels, engine.depth) === 0;
1267
+ if (region !== void 0 && regionDrawn && unmatchedIds.length > 0) {
1268
+ const world = engine.active ? engine.rootAll : allFeatures;
1269
+ const worldIds = /* @__PURE__ */ new Set();
1270
+ for (const feature of world?.features ?? []) {
1271
+ const id = idOf(feature);
1272
+ if (id !== void 0) worldIds.add(id);
1273
+ }
1274
+ unmatchedIds = unmatchedIds.filter((id) => {
1275
+ const outside = !region.ids.has(id) && (worldIds.has(id) || fallbackPoints?.[id] !== void 0);
1276
+ if (outside) outOfRegion.push(id);
1277
+ return !outside;
1278
+ });
1279
+ }
1280
+ const fallbacks = [];
1281
+ if (fallbackPoints !== void 0 && unmatchedIds.length > 0) {
1282
+ const wanted = new Set(unmatchedIds.filter((id) => fallbackPoints[id] !== void 0));
1283
+ const seen = /* @__PURE__ */ new Set();
1284
+ for (const datum of data) {
1285
+ const id = getId(datum);
1286
+ const coordinates = fallbackPoints[id];
1287
+ const at = coordinates === void 0 ? null : project(coordinates);
1288
+ if (wanted.has(id) && !seen.has(id) && coordinates !== void 0 && at !== null) {
1289
+ seen.add(id);
1290
+ fallbacks.push({ id, datum, coordinates, at });
1291
+ }
1292
+ }
1293
+ }
1294
+ const fallbackIds = new Set(fallbacks.map((f) => f.id));
1295
+ const hasOwnCoordinates = options.variant === "bubble" && options.getCoordinates !== void 0;
1296
+ const joinReport = {
1297
+ unmatchedData: hasOwnCoordinates ? [] : unmatchedIds.filter((id) => !fallbackIds.has(id)),
1298
+ featuresWithoutData: hasOwnCoordinates ? [] : join.featuresWithoutData,
1299
+ duplicateDataIds: join.duplicateDataIds,
1300
+ outOfRegion: hasOwnCoordinates ? [] : outOfRegion
1301
+ };
1302
+ const entries = features.features.map((feature) => {
1303
+ const id = idOf(feature);
1304
+ return { id, datum: id === void 0 ? void 0 : join.joined.get(id) };
1305
+ });
1306
+ const allEntries = [...entries, ...fallbacks.map(({ id, datum }) => ({ id, datum }))];
1307
+ const formatValue = options.formatValue ?? defaultFormatValue;
1308
+ const emptyFill = options.emptyFill ?? EMPTY_FILL_VAR;
1309
+ const variant = resolveVariant({
1310
+ config: options,
1311
+ entries: allEntries,
1312
+ data,
1313
+ getId,
1314
+ centroids: [...anchors, ...fallbacks.map((f) => f.at)],
1315
+ project,
1316
+ emptyFill,
1317
+ formatValue,
1318
+ emptyLabel: options.emptyLabel
1319
+ });
1320
+ const levelInfo = engine.active ? { id: engine.level?.id ?? ROOT_LEVEL.id, depth: engine.depth } : ROOT_LEVEL;
1321
+ const regions = [];
1322
+ const anonymousPaths = [];
1323
+ features.features.forEach((feature, index) => {
1324
+ const path = paths[index];
1325
+ const entry = entries[index];
1326
+ if (path == null) return;
1327
+ if (entry?.id === void 0) {
1328
+ anonymousPaths.push(path);
1329
+ return;
1330
+ }
1331
+ const datum = entry.datum;
1332
+ let wave;
1333
+ if (engine.active && machine.phase === "revealing") {
1334
+ const [cx2, cy] = toPath.centroid(feature);
1335
+ const reach = Math.hypot(width, height) / 2;
1336
+ wave = Number.isFinite(cx2) && Number.isFinite(cy) ? Math.min(1, Math.hypot(cx2 - width / 2, cy - height / 2) / reach) : 0;
1337
+ }
1338
+ const base = {
1339
+ id: entry.id,
1340
+ feature,
1341
+ datum,
1342
+ path,
1343
+ value: variant.values[index] ?? null,
1344
+ formattedValue: variant.labels[index] ?? "",
1345
+ fill: variant.fills[index] ?? "",
1346
+ wave,
1347
+ opacity: void 0,
1348
+ className: void 0,
1349
+ pattern: void 0,
1350
+ classIndex: variant.classes[index] ?? null,
1351
+ drillable: engine.active && isDrillable(engine.levels, engine.depth, {
1352
+ kind: "region",
1353
+ id: entry.id,
1354
+ feature,
1355
+ datum,
1356
+ level: levelInfo,
1357
+ drillable: true
1358
+ })
1359
+ };
1360
+ const custom = options.getRegionStyle?.(base);
1361
+ regions.push(
1362
+ custom === void 0 ? base : {
1363
+ ...base,
1364
+ fill: custom.fill ?? base.fill,
1365
+ opacity: custom.opacity,
1366
+ className: custom.className,
1367
+ pattern: custom.pattern
1368
+ }
1369
+ );
1370
+ });
1371
+ const fallbackCoordinates = new Map(fallbacks.map((f) => [f.id, f.coordinates]));
1372
+ const bubbles = isBubble ? variant.bubbles.map((bubble) => {
1373
+ const coordinates = fallbackCoordinates.get(bubble.id);
1374
+ return bubble.coordinates === null && coordinates !== void 0 ? { ...bubble, coordinates } : bubble;
1375
+ }) : variant.bubbles;
1376
+ const fallbackMarks = isBubble ? [] : fallbacks.map((f, index) => {
1377
+ const at = features.features.length + index;
1378
+ return {
1379
+ id: f.id,
1380
+ x: f.at[0],
1381
+ y: f.at[1],
1382
+ r: FALLBACK_POINT_RADIUS,
1383
+ fill: variant.fills[at] ?? emptyFill,
1384
+ datum: f.datum,
1385
+ value: variant.values[at] ?? null,
1386
+ formattedValue: variant.labels[at] ?? "",
1387
+ coordinates: f.coordinates
1388
+ };
1389
+ });
1390
+ const reportKey = [
1391
+ data.length,
1392
+ joinReport.unmatchedData.join(""),
1393
+ joinReport.featuresWithoutData.join(""),
1394
+ joinReport.duplicateDataIds.join(""),
1395
+ joinReport.outOfRegion.join("")
1396
+ ].join("\0");
1397
+ const { onJoinReport, diagnostics = true } = options;
1398
+ useEffect6(() => {
1399
+ if (status !== "ready") return;
1400
+ onJoinReport?.(joinReport);
1401
+ if (!diagnostics || isProduction()) return;
1402
+ const message = formatJoinDiagnostics({ ...joinReport, dataCount: data.length });
1403
+ const levelHint = formatWrongLevelDataHint({
1404
+ drillActive: engine.active,
1405
+ dataIsSelector: typeof rawOptions.data === "function",
1406
+ dataCount: data.length,
1407
+ unmatchedCount: joinReport.unmatchedData.length,
1408
+ levelId: engine.level?.id ?? null
1409
+ });
1410
+ if (message !== null) {
1411
+ console.warn(levelHint === null ? message : `${message}
1412
+ ${levelHint}`);
1413
+ }
1414
+ }, [reportKey, status]);
1415
+ const emptyRegion = region !== void 0 && regionDrawn && features.features.length === 0;
1416
+ useEffect6(() => {
1417
+ if (status === "ready" && emptyRegion && diagnostics && !isProduction()) {
1418
+ console.warn(
1419
+ `[choropleth] region "${region?.key}" matches no region of the geometry. Regions are matched by ISO 3166-1 alpha-2 code against \`feature.id\`: if your geometry uses other ids, pass \`getFeatureId\`.`
1420
+ );
1421
+ }
1422
+ }, [emptyRegion, status, diagnostics, region?.key]);
1423
+ const noIdsMessage = formatNoIdsDiagnostic({
1424
+ featureCount: features.features.length,
1425
+ idCount: regions.length,
1426
+ levelId: engine.level?.id ?? null,
1427
+ customGetFeatureId: getFeatureId !== void 0
1428
+ });
1429
+ useEffect6(() => {
1430
+ if (status === "ready" && noIdsMessage !== null && diagnostics && !isProduction()) {
1431
+ console.warn(noIdsMessage);
1432
+ }
1433
+ }, [noIdsMessage, status, diagnostics]);
1434
+ const { onSelectedIdChange } = options;
1435
+ const controlled = options.selectedId !== void 0;
1436
+ const selectable = controlled || options.defaultSelectedId !== void 0 || onSelectedIdChange !== void 0;
1437
+ const [internalSelectedId, setInternalSelectedId] = useState5(
1438
+ options.defaultSelectedId ?? null
1439
+ );
1440
+ const selectedId = controlled ? options.selectedId ?? null : internalSelectedId;
1441
+ const [selectionLevel, setSelectionLevel] = useState5(engine.shownKey);
1442
+ if (selectionLevel !== engine.shownKey) {
1443
+ setSelectionLevel(engine.shownKey);
1444
+ if (!controlled && internalSelectedId !== null) setInternalSelectedId(null);
1445
+ }
1446
+ const setSelectedId = (id) => {
1447
+ if (!controlled) setInternalSelectedId(id);
1448
+ onSelectedIdChange?.(id);
1449
+ };
1450
+ const { onItemClick, onItemHover } = options;
1451
+ const { rememberFills } = engine;
1452
+ const drillActive = engine.active;
1453
+ useEffect6(() => {
1454
+ if (drillActive) rememberFills(new Map(regions.map((region2) => [region2.id, region2.fill])));
1455
+ });
1456
+ const regionsById = new Map(regions.map((region2) => [region2.id, region2]));
1457
+ const featuresById = /* @__PURE__ */ new Map();
1458
+ features.features.forEach((feature) => {
1459
+ const id = idOf(feature);
1460
+ if (id !== void 0) featuresById.set(id, feature);
1461
+ });
1462
+ const bubblesById = new Map(bubbles.filter((b) => b.coordinates !== null).map((b) => [b.id, b]));
1463
+ const fallbackMarksById = new Map(fallbackMarks.map((f) => [f.id, f]));
1464
+ const getItemContext = (ref) => {
1465
+ if (ref.kind === "region") {
1466
+ const region2 = regionsById.get(ref.id);
1467
+ if (region2 === void 0) return void 0;
1468
+ return {
1469
+ kind: "region",
1470
+ id: region2.id,
1471
+ feature: region2.feature,
1472
+ datum: region2.datum,
1473
+ level: levelInfo,
1474
+ drillable: region2.drillable,
1475
+ value: region2.value,
1476
+ formattedValue: region2.formattedValue,
1477
+ color: region2.fill,
1478
+ label: nameOf(region2.feature) ?? region2.id
1479
+ };
1480
+ }
1481
+ const bubble = bubblesById.get(ref.id);
1482
+ const mark = bubble === void 0 ? fallbackMarksById.get(ref.id) : void 0;
1483
+ const coordinates = bubble?.coordinates ?? mark?.coordinates;
1484
+ const source = bubble ?? mark;
1485
+ if (source === void 0 || coordinates == null) return void 0;
1486
+ const feature = featuresById.get(source.id);
1487
+ const value = bubble === void 0 ? mark?.value ?? null : bubble.value;
1488
+ return {
1489
+ kind: "point",
1490
+ id: source.id,
1491
+ coordinates,
1492
+ feature,
1493
+ datum: source.datum,
1494
+ level: levelInfo,
1495
+ drillable: false,
1496
+ value,
1497
+ formattedValue: bubble === void 0 ? mark?.formattedValue ?? "" : formatValue(bubble.value),
1498
+ color: bubble === void 0 ? mark?.fill ?? emptyFill : bubble.fill,
1499
+ label: (feature === void 0 ? void 0 : nameOf(feature)) ?? source.id
1500
+ };
1501
+ };
1502
+ const previewed = useRef5(null);
1503
+ const hoverItem = (ref) => {
1504
+ if (isSameItem(store.getState().hovered, ref)) return;
1505
+ if (!isSameItem(previewed.current, ref)) previewed.current = null;
1506
+ store.setState({ hovered: ref });
1507
+ onItemHover?.(ref === null ? null : getItemContext(ref) ?? null);
1508
+ };
1509
+ const clickItem = (ref) => {
1510
+ const item = getItemContext(ref);
1511
+ if (item === void 0) return;
1512
+ if (ref.kind === "region" && item.drillable && engine.controller !== null) {
1513
+ if (store.getState().pointerType === "touch" && !isSameItem(previewed.current, ref)) {
1514
+ hoverItem(ref);
1515
+ previewed.current = ref;
1516
+ return;
1517
+ }
1518
+ previewed.current = null;
1519
+ engine.noteItem(item);
1520
+ engine.controller.drillTo([...engine.shownPath, ref.id]);
1521
+ } else if (ref.kind === "region" && selectable) {
1522
+ setSelectedId(ref.id === selectedId ? null : ref.id);
1523
+ }
1524
+ onItemClick?.(item);
1525
+ };
1526
+ const { onDrillIntent } = options;
1527
+ const intentRef = useRef5({ regionsById, shownPath: engine.shownPath, idle: true, onDrillIntent });
1528
+ const { prefetch } = engine;
1529
+ useEffect6(() => {
1530
+ intentRef.current = {
1531
+ regionsById,
1532
+ shownPath: engine.shownPath,
1533
+ idle: engine.machine.phase === "idle",
1534
+ onDrillIntent
1535
+ };
1536
+ });
1537
+ useEffect6(() => {
1538
+ if (!engine.active) return;
1539
+ const timer = createIntentTimer({
1540
+ onIntent: (id) => {
1541
+ const current = intentRef.current;
1542
+ if (!current.idle) return;
1543
+ const path = [...current.shownPath, id];
1544
+ prefetch(path);
1545
+ current.onDrillIntent?.(contextOf(path));
1546
+ }
1547
+ });
1548
+ const sync = () => {
1549
+ const hovered = store.getState().hovered;
1550
+ const drillable = hovered?.kind === "region" && intentRef.current.regionsById.get(hovered.id)?.drillable;
1551
+ timer.hover(hovered !== null && drillable === true ? hovered.id : null);
1552
+ };
1553
+ sync();
1554
+ const unsubscribe = store.subscribe(sync);
1555
+ return () => {
1556
+ unsubscribe();
1557
+ timer.dispose();
1558
+ };
1559
+ }, [engine.active, store, prefetch]);
1560
+ const findRequestProblem = (path) => {
1561
+ const shown = engine.shownPath;
1562
+ for (let depth = 0; depth < path.length; depth++) {
1563
+ if (depth > shown.length || depth > 0 && path[depth - 1] !== shown[depth - 1]) break;
1564
+ const id = path[depth];
1565
+ const level = engine.chain[depth];
1566
+ const knownIds = level === void 0 ? void 0 : new Set(level.features.flatMap((feature) => level.idOf(feature) ?? []));
1567
+ const problem = checkDrillStep({
1568
+ id,
1569
+ depth,
1570
+ levelId: engine.levels[depth]?.id ?? "?",
1571
+ knownIds,
1572
+ drillable: depth === shown.length && !dataLoading ? regionsById.get(id)?.drillable : void 0
1573
+ });
1574
+ if (problem !== null) return problem;
1575
+ }
1576
+ return null;
1577
+ };
1578
+ const latestDrill = useRef5({ findRequestProblem, engine });
1579
+ useEffect6(() => {
1580
+ latestDrill.current = { findRequestProblem, engine };
1581
+ });
1582
+ const drillApi = useMemo4(() => {
1583
+ const refuse = (message) => {
1584
+ if (!isProduction()) console.warn(message);
1585
+ return Promise.resolve({ completed: false });
1586
+ };
1587
+ const go = (path, source) => {
1588
+ const { findRequestProblem: find, engine: current } = latestDrill.current;
1589
+ if (clampPath(path, current.levels.length).clamped) {
1590
+ return refuse(formatTooDeepRequest(path, current.levels.length, source));
1591
+ }
1592
+ const problem = find(path);
1593
+ if (problem !== null) return refuse(formatDrillRequestProblem(problem, source));
1594
+ return current.navigate((controller2) => controller2.drillTo(path));
1595
+ };
1596
+ return {
1597
+ drillDown: (id) => go([...latestDrill.current.engine.target, id], "drillDown()"),
1598
+ drillUp: (steps) => latestDrill.current.engine.navigate((controller2) => controller2.drillUp(steps)),
1599
+ drillTo: (path) => go(path, "drillTo()")
1600
+ };
1601
+ }, []);
1602
+ useEffect6(() => {
1603
+ if (!engine.active) return;
1604
+ store.registerDrill(drillApi);
1605
+ return () => store.registerDrill(null);
1606
+ }, [engine.active, store, drillApi]);
1607
+ const targetKey = JSON.stringify(engine.target);
1608
+ useEffect6(() => {
1609
+ if (!engine.active || isProduction()) return;
1610
+ const problem = latestDrill.current.findRequestProblem(engine.target);
1611
+ if (problem !== null) console.warn(formatDrillRequestProblem(problem, "drill path"));
1612
+ }, [engine.active, targetKey]);
1613
+ const leaveHandler = (ref) => (event) => {
1614
+ if (event.pointerType !== "touch" && isSameItem(store.getState().hovered, ref)) {
1615
+ hoverItem(null);
1616
+ }
1617
+ };
1618
+ const getRegionProps = (region2) => {
1619
+ const ref = { kind: "region", id: region2.id };
1620
+ return {
1621
+ "data-region-id": region2.id,
1622
+ "data-selected": region2.id === selectedId ? true : void 0,
1623
+ onClick: () => clickItem(ref),
1624
+ onPointerEnter: () => hoverItem(ref),
1625
+ onPointerLeave: leaveHandler(ref)
1626
+ };
1627
+ };
1628
+ const getPointProps = (point) => {
1629
+ const ref = { kind: "point", id: point.id };
1630
+ return {
1631
+ "data-point-id": point.id,
1632
+ onClick: () => clickItem(ref),
1633
+ onPointerEnter: () => hoverItem(ref),
1634
+ onPointerLeave: leaveHandler(ref)
1635
+ };
1636
+ };
1637
+ const shownKey = engine.shownKey;
1638
+ const previousLevel = useRef5(shownKey);
1639
+ useIsoLayoutEffect(() => {
1640
+ if (!engine.active || previousLevel.current === shownKey) return;
1641
+ previousLevel.current = shownKey;
1642
+ const from = chain?.projections[chain.projections.length - 1];
1643
+ const to = previousChain?.projections[previousChain.projections.length - 1];
1644
+ const camera = machine.direction === "up" && from !== void 0 && to !== void 0 ? relativeTransform(from, to) : FIT_TRANSFORM;
1645
+ void store.getZoom()?.flyTo(camera, { duration: 0, silent: true });
1646
+ }, [engine.active, shownKey, store]);
1647
+ const zoomOptions = resolveZoomOptions(options.zoom, {
1648
+ // With `drill` the click already does something (go down a level): the double click must NOT also zoom
1649
+ // ×2, or the second click would interrupt the drill flight and end up as a plain zoom.
1650
+ clickIsBusy: onItemClick !== void 0 || selectable || engine.active
1651
+ });
1652
+ const focusBounds = { ...DEFAULT_FOCUS_BOUNDS, ...options.focusBounds };
1653
+ const getRegionExtent = (id) => {
1654
+ const feature = featuresById.get(id);
1655
+ return feature === void 0 ? null : getFocusExtent({ feature, path: toPath, project });
1656
+ };
1657
+ const resolveCamera = (target, cameraOptions) => {
1658
+ if (zoomOptions === null) return null;
1659
+ const size = { width, height };
1660
+ const transform = resolveZoomTarget({
1661
+ target,
1662
+ size,
1663
+ padding: cameraOptions?.padding ?? zoomOptions.padding,
1664
+ scaleExtent: [zoomOptions.min, zoomOptions.max],
1665
+ project,
1666
+ getRegionExtent,
1667
+ focusBounds
1668
+ });
1669
+ return transform !== null && zoomOptions.constrain === "contain" ? constrainTransform({
1670
+ transform,
1671
+ content: [
1672
+ [0, 0],
1673
+ [width, height]
1674
+ ],
1675
+ view: [
1676
+ [0, 0],
1677
+ [width, height]
1678
+ ]
1679
+ }) : transform;
1680
+ };
1681
+ return {
1682
+ containerRef: measured.ref,
1683
+ width,
1684
+ height,
1685
+ drillPhase: engine.active ? machine.phase : null,
1686
+ drillFocusId,
1687
+ drillLayers,
1688
+ drillNav,
1689
+ selectContextItem: (depth, id) => {
1690
+ engine.controller?.drillTo([...engine.shownPath.slice(0, depth), id]);
1691
+ },
1692
+ status,
1693
+ dataLoading,
1694
+ regions,
1695
+ anonymousPaths,
1696
+ emptyFill,
1697
+ fallbackPoints: fallbackMarks,
1698
+ labels,
1699
+ zoom: zoomOptions,
1700
+ onViewChange: typeof options.zoom === "object" ? options.zoom.onViewChange : void 0,
1701
+ invert,
1702
+ project,
1703
+ focusId: options.focusId,
1704
+ resolveCamera,
1705
+ borders: bordersPath,
1706
+ legend: variant.legend,
1707
+ sizeLegend: variant.sizeLegend,
1708
+ bubbles,
1709
+ scale: variant.scale,
1710
+ joinReport,
1711
+ formatValue,
1712
+ clickable: onItemClick !== void 0 || selectable,
1713
+ store,
1714
+ selectedId,
1715
+ setSelectedId,
1716
+ hoverItem,
1717
+ clickItem,
1718
+ getItemContext,
1719
+ getRegionProps,
1720
+ getPointProps
1721
+ };
1722
+ };
1723
+ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect;
1724
+ var NO_GEOGRAPHY = { type: "FeatureCollection", features: [] };
1725
+ var EMPTY_FEATURES = { type: "FeatureCollection", features: [] };
1726
+ var NO_DATA = [];
1727
+ var EMPTY_FILL_VAR = `var(--cm-empty-fill, ${DEFAULT_EMPTY_FILL})`;
1728
+ var nameOf = (feature) => {
1729
+ const name = feature.properties?.name;
1730
+ return typeof name === "string" ? name : void 0;
1731
+ };
1732
+ var defaultFeatureId2 = (feature) => feature.id === void 0 ? void 0 : String(feature.id);
1733
+
1734
+ // src/react/choropleth-context.ts
1735
+ import { createContext, useContext } from "react";
1736
+ var ChoroplethContext = createContext(null);
1737
+ var useChoroplethContext = () => {
1738
+ const value = useContext(ChoroplethContext);
1739
+ if (value === null) {
1740
+ throw new Error(
1741
+ "[choropleth] Choropleth components (Svg, Regions, Legend\u2026) must be rendered inside <Choropleth.Root>."
1742
+ );
1743
+ }
1744
+ return value;
1745
+ };
1746
+ var useChoroplethApi = () => useChoroplethContext().api;
1747
+ var useDefId = (name) => useChoroplethContext().defId(name);
1748
+
1749
+ // src/react/components/defs.tsx
1750
+ import { jsx, jsxs } from "react/jsx-runtime";
1751
+ var Defs = ({ children }) => {
1752
+ const { defId } = useChoroplethContext();
1753
+ const color = {
1754
+ color: "var(--cm-pattern-color, color-mix(in srgb, var(--cm-label-color, #111111) 45%, transparent))"
1755
+ };
1756
+ return /* @__PURE__ */ jsxs("defs", { children: [
1757
+ /* @__PURE__ */ jsx(
1758
+ "pattern",
1759
+ {
1760
+ id: defId("hatch"),
1761
+ width: "6",
1762
+ height: "6",
1763
+ patternUnits: "userSpaceOnUse",
1764
+ patternTransform: "rotate(45)",
1765
+ style: color,
1766
+ children: /* @__PURE__ */ jsx("line", { x1: "0", y1: "0", x2: "0", y2: "6", stroke: "currentColor", strokeWidth: "2" })
1767
+ }
1768
+ ),
1769
+ /* @__PURE__ */ jsx("pattern", { id: defId("dots"), width: "6", height: "6", patternUnits: "userSpaceOnUse", style: color, children: /* @__PURE__ */ jsx("circle", { cx: "3", cy: "3", r: "1.2", fill: "currentColor" }) }),
1770
+ children
1771
+ ] });
1772
+ };
1773
+
1774
+ // src/react/components/patterns.tsx
1775
+ import { jsx as jsx2 } from "react/jsx-runtime";
1776
+ var Patterns = () => {
1777
+ const { choropleth, defId, emptyPattern } = useChoroplethContext();
1778
+ const marks = choropleth.regions.flatMap((region) => {
1779
+ const name = region.pattern ?? (emptyPattern !== null && region.value === null ? emptyPattern : void 0);
1780
+ return name === void 0 ? [] : [{ id: region.id, d: region.path, name }];
1781
+ });
1782
+ if (marks.length === 0) return null;
1783
+ return /* @__PURE__ */ jsx2("g", { className: "cm-patterns", pointerEvents: "none", "aria-hidden": "true", children: marks.map(({ id, d, name }) => /* @__PURE__ */ jsx2(
1784
+ "path",
1785
+ {
1786
+ d,
1787
+ fill: `url(#${defId(name)})`
1788
+ },
1789
+ id
1790
+ )) });
1791
+ };
1792
+
1793
+ // src/react/use-camera.ts
1794
+ import { useMemo as useMemo5, useSyncExternalStore as useSyncExternalStore3 } from "react";
1795
+ var useCamera = (selector) => {
1796
+ const { store, api } = useChoroplethContext();
1797
+ const getSnapshot = useMemo5(
1798
+ () => createCameraSnapshot(store, () => api.getView(), selector),
1799
+ [store, api, selector]
1800
+ );
1801
+ return useSyncExternalStore3(store.subscribe, getSnapshot, getSnapshot);
1802
+ };
1803
+ var createCameraSnapshot = (store, getView, selector) => {
1804
+ let lastCamera;
1805
+ let last;
1806
+ return () => {
1807
+ const camera = store.getState().camera;
1808
+ if (last !== void 0 && Object.is(camera, lastCamera)) return last.value;
1809
+ const value = selector(getView());
1810
+ lastCamera = camera;
1811
+ if (last === void 0 || !shallowEqual(last.value, value)) last = { value };
1812
+ return last.value;
1813
+ };
1814
+ };
1815
+ var shallowEqual = (a, b) => {
1816
+ if (Object.is(a, b)) return true;
1817
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
1818
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1819
+ const keysA = Object.keys(a);
1820
+ if (keysA.length !== Object.keys(b).length) return false;
1821
+ return keysA.every(
1822
+ (key) => Object.hasOwn(b, key) && Object.is(a[key], b[key])
1823
+ );
1824
+ };
1825
+
1826
+ // src/react/use-zoom.ts
1827
+ import { useEffect as useEffect7, useRef as useRef6 } from "react";
1828
+ import { easeCubicInOut, easeCubicOut } from "d3-ease";
1829
+ import { select } from "d3-selection";
1830
+ import "d3-transition";
1831
+ import { zoom, zoomIdentity, zoomTransform } from "d3-zoom";
1832
+
1833
+ // src/react/fade.ts
1834
+ var FADE_OUT_MS = 100;
1835
+ var FADE_IN_MS = 150;
1836
+ var fadeThrough = (element, jump) => {
1837
+ if (typeof element.animate !== "function") {
1838
+ jump();
1839
+ return { promise: Promise.resolve({ completed: true }), cancel: () => {
1840
+ } };
1841
+ }
1842
+ const animations = [];
1843
+ const out = element.animate([{ opacity: 1 }, { opacity: 0 }], {
1844
+ duration: FADE_OUT_MS,
1845
+ fill: "forwards"
1846
+ });
1847
+ animations.push(out);
1848
+ const promise = out.finished.then(() => {
1849
+ jump();
1850
+ const back = element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: FADE_IN_MS });
1851
+ animations.push(back);
1852
+ out.cancel();
1853
+ return back.finished;
1854
+ }).then(
1855
+ () => ({ completed: true }),
1856
+ () => ({ completed: false })
1857
+ );
1858
+ return { promise, cancel: () => animations.forEach((animation) => animation.cancel()) };
1859
+ };
1860
+
1861
+ // src/react/use-zoom.ts
1862
+ var BUTTON_ZOOM_MS = 300;
1863
+ var isPressOnDrillTarget = (event, root) => {
1864
+ if (event.type !== "mousedown" && event.type !== "touchstart") return false;
1865
+ const phase = root.getAttribute("data-drill-phase");
1866
+ if (phase !== "flying" && phase !== "loading") return false;
1867
+ const target = event.target;
1868
+ return target instanceof Element && target.closest("[data-drill-target]") !== null;
1869
+ };
1870
+ var CLICK_DISTANCE_PX = 4;
1871
+ var ZOOMED_EPSILON = 1.001;
1872
+ var useZoom = ({
1873
+ svgRef,
1874
+ viewportRef,
1875
+ rootRef,
1876
+ store,
1877
+ width,
1878
+ height,
1879
+ options,
1880
+ invert,
1881
+ project,
1882
+ onViewChange,
1883
+ resolveCamera,
1884
+ focusId,
1885
+ ready
1886
+ }) => {
1887
+ const latest = useRef6({ invert, project, onViewChange, resolveCamera });
1888
+ useEffect7(() => {
1889
+ latest.current = { invert, project, onViewChange, resolveCamera };
1890
+ });
1891
+ const previous = useRef6(null);
1892
+ const enabled = options !== null;
1893
+ const {
1894
+ min,
1895
+ max,
1896
+ wheel,
1897
+ pinch,
1898
+ pan,
1899
+ doubleClick,
1900
+ constrain,
1901
+ initialView,
1902
+ bubbleScaling,
1903
+ inertia
1904
+ } = options ?? NO_ZOOM;
1905
+ useEffect7(() => {
1906
+ const svg = svgRef.current;
1907
+ const viewport = viewportRef.current;
1908
+ const root = rootRef.current;
1909
+ if (!enabled || !ready || svg === null || viewport === null || root === null) return;
1910
+ const size = { width, height };
1911
+ const content = [
1912
+ [0, 0],
1913
+ [width, height]
1914
+ ];
1915
+ let restoring = false;
1916
+ let samples = [];
1917
+ let glide = null;
1918
+ let gesture = { k: 1, x: 0, y: 0, moving: false };
1919
+ const commit = (transform, notify) => {
1920
+ const camera = { k: transform.k, x: transform.x, y: transform.y };
1921
+ store.setState({ camera, interacting: false });
1922
+ if (!notify) return;
1923
+ latest.current.onViewChange?.(
1924
+ toViewState({
1925
+ transform: camera,
1926
+ size,
1927
+ invert: latest.current.invert,
1928
+ level: ROOT_LEVEL
1929
+ })
1930
+ );
1931
+ };
1932
+ const stopGlide = () => {
1933
+ if (glide === null) return;
1934
+ cancelAnimationFrame(glide.frame);
1935
+ glide = null;
1936
+ commit(zoomTransform(svg), true);
1937
+ };
1938
+ const baseFilter = createZoomFilter({
1939
+ wheel,
1940
+ pinch,
1941
+ pan,
1942
+ doubleClick,
1943
+ onGestureHint: (kind) => store.showGestureHint(kind)
1944
+ });
1945
+ const behavior = zoom().extent([
1946
+ [0, 0],
1947
+ [width, height]
1948
+ ]).scaleExtent([min, max]).clickDistance(CLICK_DISTANCE_PX).filter((event) => {
1949
+ if (isPressOnDrillTarget(event, root)) return false;
1950
+ return baseFilter(event);
1951
+ }).on("start", (event) => {
1952
+ if (event.sourceEvent === null) return;
1953
+ stopGlide();
1954
+ samples = [];
1955
+ gesture = {
1956
+ k: event.transform.k,
1957
+ x: event.transform.x,
1958
+ y: event.transform.y,
1959
+ moving: false
1960
+ };
1961
+ }).on(
1962
+ "zoom",
1963
+ ({ transform, sourceEvent }) => {
1964
+ viewport.setAttribute("transform", transform.toString());
1965
+ root.style.setProperty("--cm-k", String(transform.k));
1966
+ root.style.setProperty(
1967
+ "--cm-bubble-scale",
1968
+ String(bubbleScale(transform.k, bubbleScaling))
1969
+ );
1970
+ if (sourceEvent !== null && !gesture.moving) {
1971
+ const moved = sourceEvent.type === "wheel" || transform.k !== gesture.k || Math.hypot(transform.x - gesture.x, transform.y - gesture.y) > CLICK_DISTANCE_PX;
1972
+ if (moved) {
1973
+ gesture.moving = true;
1974
+ store.setState({ interacting: true });
1975
+ }
1976
+ }
1977
+ if (sourceEvent !== null) {
1978
+ samples.push({
1979
+ time: performance.now(),
1980
+ k: transform.k,
1981
+ x: transform.x,
1982
+ y: transform.y
1983
+ });
1984
+ if (samples.length > MAX_SAMPLES) samples.shift();
1985
+ }
1986
+ root.toggleAttribute("data-zoomed", transform.k > ZOOMED_EPSILON);
1987
+ }
1988
+ ).on(
1989
+ "end",
1990
+ ({ transform, sourceEvent }) => {
1991
+ if (glide !== null) return;
1992
+ const velocity = inertia && sourceEvent !== null && !restoring && !prefersReducedMotion() ? releaseVelocity({ samples, now: performance.now() }) : null;
1993
+ samples = [];
1994
+ if (velocity !== null) {
1995
+ store.setState({ camera: { k: transform.k, x: transform.x, y: transform.y } });
1996
+ startGlide(transform, velocity);
1997
+ return;
1998
+ }
1999
+ commit(transform, !restoring);
2000
+ }
2001
+ );
2002
+ if (constrain === "contain") {
2003
+ behavior.constrain((transform, extent) => {
2004
+ const c = constrainTransform({ transform, content, view: extent });
2005
+ return zoomIdentity.translate(c.x, c.y).scale(c.k);
2006
+ });
2007
+ }
2008
+ behavior.interpolate(createZoomInterpolator(DEFAULT_FLIGHT_CURVATURE));
2009
+ const selection = select(svg).call(behavior);
2010
+ if (wheel === "cooperative") svg.style.touchAction = "pan-x pan-y";
2011
+ let restored = store.getState().camera;
2012
+ const before = previous.current;
2013
+ if (before !== null && (before.size.width !== width || before.size.height !== height)) {
2014
+ const moved = preserveView({
2015
+ transform: before.camera,
2016
+ oldSize: before.size,
2017
+ newSize: size,
2018
+ invertOld: before.invert,
2019
+ projectNew: latest.current.project
2020
+ });
2021
+ restored = constrain === "contain" ? constrainTransform({ transform: moved, content, view: content }) : moved;
2022
+ }
2023
+ restoring = true;
2024
+ behavior.transform(selection, zoomIdentity.translate(restored.x, restored.y).scale(restored.k));
2025
+ restoring = false;
2026
+ previous.current = null;
2027
+ const startGlide = (origin, [vx, vy]) => {
2028
+ const start = performance.now();
2029
+ let last = origin;
2030
+ store.setState({ interacting: true });
2031
+ const step = () => {
2032
+ const elapsed = performance.now() - start;
2033
+ const wanted = {
2034
+ k: origin.k,
2035
+ x: origin.x + inertiaOffset({ velocity: vx, elapsed }),
2036
+ y: origin.y + inertiaOffset({ velocity: vy, elapsed })
2037
+ };
2038
+ const next = constrain === "contain" ? constrainTransform({ transform: wanted, content, view: content }) : wanted;
2039
+ behavior.transform(selection, zoomIdentity.translate(next.x, next.y).scale(next.k));
2040
+ const now = zoomTransform(svg);
2041
+ const moved = Math.hypot(now.x - last.x, now.y - last.y);
2042
+ last = now;
2043
+ const remaining = Math.hypot(vx, vy) * INERTIA_TAU_MS * Math.exp(-elapsed / INERTIA_TAU_MS);
2044
+ if (remaining < GLIDE_STOP_PX || elapsed > 0 && moved < 0.01) {
2045
+ glide = null;
2046
+ commit(now, true);
2047
+ return;
2048
+ }
2049
+ glide = { frame: requestAnimationFrame(step) };
2050
+ };
2051
+ glide = { frame: requestAnimationFrame(step) };
2052
+ };
2053
+ const onPointerDown = () => stopGlide();
2054
+ svg.addEventListener("pointerdown", onPointerDown);
2055
+ let fade = null;
2056
+ const animate = (duration, apply, {
2057
+ ease = easeCubicInOut,
2058
+ silent = false
2059
+ } = {}) => {
2060
+ fade?.cancel();
2061
+ fade = null;
2062
+ stopGlide();
2063
+ if (duration <= 0) {
2064
+ restoring = silent;
2065
+ apply(selection);
2066
+ restoring = false;
2067
+ return Promise.resolve({ completed: true });
2068
+ }
2069
+ if (prefersReducedMotion()) {
2070
+ fade = fadeThrough(viewport, () => apply(selection));
2071
+ return fade.promise;
2072
+ }
2073
+ const transition = selection.transition().duration(duration).ease(ease);
2074
+ apply(transition);
2075
+ return transition.end().then(
2076
+ () => ({ completed: true }),
2077
+ () => ({ completed: false })
2078
+ );
2079
+ };
2080
+ const toZoomIdentity = ({ k, x, y }) => zoomIdentity.translate(x, y).scale(k);
2081
+ const current = () => {
2082
+ const { k, x, y } = zoomTransform(svg);
2083
+ return { k, x, y };
2084
+ };
2085
+ const api = {
2086
+ zoomTo: (target, { duration, padding, silent } = {}) => {
2087
+ const destination = latest.current.resolveCamera(target, { padding });
2088
+ if (destination === null) {
2089
+ if (!isProduction()) {
2090
+ console.warn(
2091
+ `[choropleth] zoomTo: could not resolve ${JSON.stringify(target)} (unknown region id, or a point outside the projection).`
2092
+ );
2093
+ }
2094
+ return Promise.resolve({ completed: false });
2095
+ }
2096
+ const length = duration ?? flightDuration({ from: current(), to: destination, size });
2097
+ return animate(
2098
+ length,
2099
+ (t) => behavior.transform(t, toZoomIdentity(destination)),
2100
+ {
2101
+ silent
2102
+ }
2103
+ );
2104
+ },
2105
+ flyTo: (camera, { duration, silent } = {}) => {
2106
+ const length = duration ?? flightDuration({ from: current(), to: camera, size });
2107
+ return animate(
2108
+ length,
2109
+ (t) => behavior.transform(t, toZoomIdentity(camera)),
2110
+ {
2111
+ silent
2112
+ }
2113
+ );
2114
+ },
2115
+ zoomBy: (factor, { duration = BUTTON_ZOOM_MS } = {}) => animate(duration, (t) => behavior.scaleBy(t, factor), {
2116
+ ease: easeCubicOut
2117
+ }),
2118
+ resetView: ({ duration, silent } = {}) => {
2119
+ const length = duration ?? flightDuration({ from: current(), to: FIT_TRANSFORM, size });
2120
+ return animate(length, (t) => behavior.transform(t, zoomIdentity), {
2121
+ silent
2122
+ });
2123
+ },
2124
+ getView: () => toViewState({
2125
+ transform: current(),
2126
+ size,
2127
+ invert: latest.current.invert,
2128
+ level: ROOT_LEVEL
2129
+ })
2130
+ };
2131
+ store.registerZoom(api);
2132
+ return () => {
2133
+ const live = zoomTransform(svg);
2134
+ previous.current = {
2135
+ size,
2136
+ invert: latest.current.invert,
2137
+ camera: { k: live.k, x: live.x, y: live.y }
2138
+ };
2139
+ svg.removeEventListener("pointerdown", onPointerDown);
2140
+ if (glide !== null) cancelAnimationFrame(glide.frame);
2141
+ glide = null;
2142
+ fade?.cancel();
2143
+ store.registerZoom(null);
2144
+ selection.interrupt();
2145
+ selection.on(".zoom", null);
2146
+ svg.style.removeProperty("touch-action");
2147
+ root.toggleAttribute("data-zoomed", false);
2148
+ root.style.removeProperty("--cm-bubble-scale");
2149
+ store.setState({ interacting: false });
2150
+ };
2151
+ }, [
2152
+ svgRef,
2153
+ viewportRef,
2154
+ rootRef,
2155
+ store,
2156
+ enabled,
2157
+ ready,
2158
+ width,
2159
+ height,
2160
+ min,
2161
+ max,
2162
+ wheel,
2163
+ pinch,
2164
+ pan,
2165
+ doubleClick,
2166
+ constrain,
2167
+ bubbleScaling,
2168
+ inertia
2169
+ ]);
2170
+ const applied = useRef6(UNSET);
2171
+ const focus = focusId ?? null;
2172
+ useEffect7(() => {
2173
+ if (!enabled || !ready) return;
2174
+ const api = store.getZoom();
2175
+ if (api === null) return;
2176
+ if (applied.current === UNSET) {
2177
+ applied.current = focus;
2178
+ const first = focus ?? initialView;
2179
+ if (first !== void 0 && first !== null)
2180
+ void api.zoomTo(first, { duration: 0, silent: true });
2181
+ return;
2182
+ }
2183
+ if (focus === applied.current) return;
2184
+ applied.current = focus;
2185
+ if (focus === null) void api.resetView();
2186
+ else void api.zoomTo(focus);
2187
+ }, [store, enabled, ready, focus]);
2188
+ useEffect7(() => () => store.dispose(), [store]);
2189
+ };
2190
+ var MAX_SAMPLES = 12;
2191
+ var GLIDE_STOP_PX = 0.5;
2192
+ var UNSET = /* @__PURE__ */ Symbol("unset");
2193
+ var NO_ZOOM = {
2194
+ min: 1,
2195
+ max: 1,
2196
+ wheel: "never",
2197
+ pinch: false,
2198
+ pan: false,
2199
+ doubleClick: false,
2200
+ constrain: "contain",
2201
+ bubbleScaling: "sqrt",
2202
+ inertia: false,
2203
+ keyboard: false,
2204
+ padding: 0,
2205
+ initialView: void 0
2206
+ };
2207
+
2208
+ // src/react/class-names.ts
2209
+ var cx = (...parts) => parts.filter((part) => typeof part === "string" && part !== "").join(" ");
2210
+
2211
+ // src/react/components/borders.tsx
2212
+ import { jsx as jsx3 } from "react/jsx-runtime";
2213
+ var Borders = () => {
2214
+ const { choropleth, classNames } = useChoroplethContext();
2215
+ if (choropleth.borders === null) return null;
2216
+ return /* @__PURE__ */ jsx3(
2217
+ "path",
2218
+ {
2219
+ className: cx("cm-borders", classNames.borders),
2220
+ d: choropleth.borders,
2221
+ pointerEvents: "none"
2222
+ }
2223
+ );
2224
+ };
2225
+
2226
+ // src/react/components/breadcrumb.tsx
2227
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
2228
+ var CORNERS = {
2229
+ "top-left": { top: 12, left: 12 },
2230
+ "top-right": { top: 12, right: 12 },
2231
+ "bottom-left": { bottom: 12, left: 12 },
2232
+ "bottom-right": { bottom: 12, right: 12 }
2233
+ };
2234
+ var Breadcrumb = ({ position, className, children }) => {
2235
+ const { choropleth, classNames } = useChoroplethContext();
2236
+ const { drillNav, labels } = choropleth;
2237
+ if (drillNav === null) return null;
2238
+ if (children !== void 0) return /* @__PURE__ */ jsx4(Fragment, { children: children(drillNav) });
2239
+ const { items, canDrillUp, drillTo, drillUp } = drillNav;
2240
+ return /* @__PURE__ */ jsxs2(
2241
+ "nav",
2242
+ {
2243
+ className: cx("cm-breadcrumb", classNames.breadcrumb, className),
2244
+ "aria-label": labels.breadcrumb,
2245
+ style: position === void 0 ? void 0 : { position: "absolute", ...CORNERS[position] },
2246
+ children: [
2247
+ canDrillUp && /* @__PURE__ */ jsx4(
2248
+ "button",
2249
+ {
2250
+ type: "button",
2251
+ className: "cm-breadcrumb__back",
2252
+ "aria-label": labels.drillUp,
2253
+ onClick: () => drillUp(),
2254
+ children: "\u2190"
2255
+ }
2256
+ ),
2257
+ /* @__PURE__ */ jsx4("ol", { className: "cm-breadcrumb__list", children: items.map((item) => /* @__PURE__ */ jsx4("li", { className: cx("cm-breadcrumb__item", classNames.breadcrumbItem), children: item.current ? /* @__PURE__ */ jsx4("span", { className: "cm-breadcrumb__current", "aria-current": "page", children: item.label }) : /* @__PURE__ */ jsx4(
2258
+ "button",
2259
+ {
2260
+ type: "button",
2261
+ className: "cm-breadcrumb__link",
2262
+ onClick: () => drillTo(item.depth),
2263
+ children: item.label
2264
+ }
2265
+ ) }, item.depth)) })
2266
+ ]
2267
+ }
2268
+ );
2269
+ };
2270
+
2271
+ // src/react/components/bubbles.tsx
2272
+ import { jsx as jsx5 } from "react/jsx-runtime";
2273
+ var Bubbles = () => {
2274
+ const { choropleth, classNames } = useChoroplethContext();
2275
+ const { bubbles, clickable, getPointProps } = choropleth;
2276
+ if (bubbles.length === 0) return null;
2277
+ return /* @__PURE__ */ jsx5("g", { className: cx("cm-bubbles", classNames.bubbles), pointerEvents: "none", children: bubbles.map((bubble) => {
2278
+ const isPoint = bubble.coordinates !== null;
2279
+ return /* @__PURE__ */ jsx5(
2280
+ "circle",
2281
+ {
2282
+ className: cx("cm-bubble", classNames.bubble),
2283
+ "data-bubble-id": bubble.id,
2284
+ cx: bubble.x,
2285
+ cy: bubble.y,
2286
+ r: bubble.r,
2287
+ style: { fill: bubble.fill, cursor: isPoint && clickable ? "pointer" : void 0 },
2288
+ pointerEvents: isPoint ? "auto" : void 0,
2289
+ ...isPoint ? getPointProps(bubble) : void 0
2290
+ },
2291
+ bubble.id
2292
+ );
2293
+ }) });
2294
+ };
2295
+
2296
+ // src/react/components/drill-layers.tsx
2297
+ import { Fragment as Fragment2, jsx as jsx6 } from "react/jsx-runtime";
2298
+ var DrillLayers = ({ role }) => {
2299
+ const { choropleth, classNames } = useChoroplethContext();
2300
+ const { drillLayers, selectContextItem } = choropleth;
2301
+ const layers = drillLayers.filter((layer) => layer.role === role);
2302
+ if (layers.length === 0) return null;
2303
+ return /* @__PURE__ */ jsx6(Fragment2, { children: layers.map((layer) => {
2304
+ const { k, x, y } = layer.transform;
2305
+ return /* @__PURE__ */ jsx6(
2306
+ "g",
2307
+ {
2308
+ className: cx("cm-layer", classNames.layer),
2309
+ "data-role": layer.role,
2310
+ "data-depth": layer.depth,
2311
+ transform: `translate(${x},${y}) scale(${k})`,
2312
+ pointerEvents: layer.role === "exiting" ? "none" : void 0,
2313
+ onClick: layer.role === "context" ? (event) => {
2314
+ const id = event.target.closest?.("[data-context-id]")?.getAttribute("data-context-id");
2315
+ const known = id == null ? void 0 : layer.paths.find((p) => p.id === id);
2316
+ if (known?.drillable === true && id != null) selectContextItem(layer.depth, id);
2317
+ } : void 0,
2318
+ children: layer.paths.map((path, index) => /* @__PURE__ */ jsx6(
2319
+ "path",
2320
+ {
2321
+ className: "cm-context-region",
2322
+ d: path.d,
2323
+ "data-context-id": path.id,
2324
+ "data-focus": path.focus ? "" : void 0,
2325
+ "data-drillable": path.drillable ? "" : void 0,
2326
+ style: path.fill === void 0 ? void 0 : { fill: path.fill }
2327
+ },
2328
+ path.id ?? `anonymous-${index}`
2329
+ ))
2330
+ },
2331
+ layer.key
2332
+ );
2333
+ }) });
2334
+ };
2335
+
2336
+ // src/react/components/fallback-points.tsx
2337
+ import { jsx as jsx7 } from "react/jsx-runtime";
2338
+ var FallbackPoints = () => {
2339
+ const { choropleth } = useChoroplethContext();
2340
+ const { fallbackPoints, clickable, getPointProps } = choropleth;
2341
+ if (fallbackPoints.length === 0) return null;
2342
+ return /* @__PURE__ */ jsx7("g", { className: "cm-fallback-points", children: fallbackPoints.map((point) => /* @__PURE__ */ jsx7(
2343
+ "circle",
2344
+ {
2345
+ className: "cm-fallback-point",
2346
+ cx: point.x,
2347
+ cy: point.y,
2348
+ r: point.r,
2349
+ style: { fill: point.fill, cursor: clickable ? "pointer" : void 0 },
2350
+ ...getPointProps(point)
2351
+ },
2352
+ point.id
2353
+ )) });
2354
+ };
2355
+
2356
+ // src/react/components/gesture-hint.tsx
2357
+ import { useState as useState6 } from "react";
2358
+
2359
+ // src/react/platform.ts
2360
+ import { useSyncExternalStore as useSyncExternalStore4 } from "react";
2361
+ var isApplePlatform = () => {
2362
+ if (typeof navigator === "undefined") return false;
2363
+ const platform = navigator.userAgentData?.platform ?? navigator.platform ?? "";
2364
+ return /Mac|iPhone|iPad|iPod/i.test(platform);
2365
+ };
2366
+ var subscribeNothing = () => () => {
2367
+ };
2368
+ var useIsApplePlatform = () => useSyncExternalStore4(subscribeNothing, isApplePlatform, () => false);
2369
+
2370
+ // src/react/components/gesture-hint.tsx
2371
+ import { jsx as jsx8 } from "react/jsx-runtime";
2372
+ var GestureHint = ({ className }) => {
2373
+ const { choropleth, store, classNames } = useChoroplethContext();
2374
+ const hint = useMapStore(store, (s) => s.gestureHint);
2375
+ const apple = useIsApplePlatform();
2376
+ const [last, setLast] = useState6("wheel");
2377
+ if (hint !== null && hint !== last) setLast(hint);
2378
+ const { labels } = choropleth;
2379
+ const text = last === "touch" ? labels.gestureHintTouch : apple ? labels.gestureHintWheelMac : labels.gestureHintWheel;
2380
+ return /* @__PURE__ */ jsx8(
2381
+ "div",
2382
+ {
2383
+ className: cx("cm-gesture-hint", classNames.gestureHint, className),
2384
+ "aria-hidden": "true",
2385
+ "data-visible": hint === null ? void 0 : "",
2386
+ children: text
2387
+ }
2388
+ );
2389
+ };
2390
+
2391
+ // src/react/components/legend.tsx
2392
+ import { useEffect as useEffect8 } from "react";
2393
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
2394
+ var GRADIENT_STOPS = 5;
2395
+ var Legend = ({
2396
+ title,
2397
+ showCount = false,
2398
+ className,
2399
+ variant = "swatches",
2400
+ interactive = true,
2401
+ children
2402
+ }) => {
2403
+ const { choropleth, classNames, emptyPattern } = useChoroplethContext();
2404
+ const { legend: items, sizeLegend: sizeItems, scale, formatValue, containerRef } = choropleth;
2405
+ const canHighlight = interactive && choropleth.regions.some((r) => r.classIndex !== null);
2406
+ const highlight = (key) => {
2407
+ const root = containerRef.current;
2408
+ if (root === null) return;
2409
+ if (key === null) root.removeAttribute("data-highlight-class");
2410
+ else root.setAttribute("data-highlight-class", key);
2411
+ };
2412
+ useEffect8(
2413
+ () => () => containerRef.current?.removeAttribute("data-highlight-class"),
2414
+ [containerRef]
2415
+ );
2416
+ const classKey = (item, index) => item.empty === true ? "empty" : String(index);
2417
+ const hoverProps = (item, index) => canHighlight ? {
2418
+ "data-class": classKey(item, index),
2419
+ onPointerEnter: () => highlight(classKey(item, index)),
2420
+ onPointerLeave: () => highlight(null)
2421
+ } : {};
2422
+ const swatchProps = (item) => ({
2423
+ style: { backgroundColor: item.color },
2424
+ "data-pattern": item.empty === true && emptyPattern !== null ? emptyPattern : void 0
2425
+ });
2426
+ if (children !== void 0) return /* @__PURE__ */ jsx9(Fragment3, { children: children({ items, sizeItems, scale, formatValue }) });
2427
+ const rootClass = cx("cm-legend", classNames.legend, className);
2428
+ const indexed = items.map((item, index) => ({ item, index }));
2429
+ const classRows = indexed.filter(({ item }) => item.empty !== true);
2430
+ const emptyItem = indexed.find(({ item }) => item.empty === true) ?? null;
2431
+ const barItems = variant === "bar" && scale?.kind !== "continuous" && classRows.length > 0 && classRows.every(({ item }) => item.from !== void 0 && item.to !== void 0) ? classRows : null;
2432
+ const barTicks = barItems === null ? [] : [...barItems.map(({ item }) => item.from), barItems.at(-1)?.item.to];
2433
+ const [min, max] = scale?.domain ?? [0, 1];
2434
+ return /* @__PURE__ */ jsxs3("div", { className: rootClass, children: [
2435
+ title !== void 0 && /* @__PURE__ */ jsx9("div", { className: "cm-legend__title", children: title }),
2436
+ scale !== null && scale.kind === "continuous" ? /* @__PURE__ */ jsxs3("div", { children: [
2437
+ /* @__PURE__ */ jsx9(
2438
+ "div",
2439
+ {
2440
+ className: "cm-legend__gradient",
2441
+ "data-legend-gradient": true,
2442
+ style: {
2443
+ background: `linear-gradient(to right, ${Array.from(
2444
+ { length: GRADIENT_STOPS },
2445
+ (_, i) => scale.getColor(min + (max - min) * i / (GRADIENT_STOPS - 1))
2446
+ ).join(", ")})`
2447
+ }
2448
+ }
2449
+ ),
2450
+ /* @__PURE__ */ jsxs3("div", { className: "cm-legend__range", children: [
2451
+ /* @__PURE__ */ jsx9("span", { children: formatValue(min) }),
2452
+ /* @__PURE__ */ jsx9("span", { children: formatValue(max) })
2453
+ ] })
2454
+ ] }) : barItems !== null ? /* @__PURE__ */ jsxs3("div", { className: "cm-legend__bar-wrap", children: [
2455
+ /* @__PURE__ */ jsx9("div", { className: "cm-legend__bar", role: "list", children: barItems.map(({ item, index }) => /* @__PURE__ */ jsx9(
2456
+ "span",
2457
+ {
2458
+ role: "listitem",
2459
+ className: cx("cm-legend__segment", classNames.legendItem),
2460
+ style: { backgroundColor: item.color },
2461
+ title: `${item.label} (${item.count})`,
2462
+ ...hoverProps(item, index)
2463
+ },
2464
+ item.label
2465
+ )) }),
2466
+ /* @__PURE__ */ jsx9("div", { className: "cm-legend__ticks", "aria-hidden": "true", children: barTicks.map((value, i) => /* @__PURE__ */ jsx9(
2467
+ "span",
2468
+ {
2469
+ className: "cm-legend__tick",
2470
+ style: { left: `${i / (barTicks.length - 1) * 100}%` },
2471
+ children: formatValue(value)
2472
+ },
2473
+ i
2474
+ )) }),
2475
+ emptyItem !== null && /* @__PURE__ */ jsxs3(
2476
+ "div",
2477
+ {
2478
+ className: cx("cm-legend__item", classNames.legendItem),
2479
+ ...hoverProps(emptyItem.item, emptyItem.index),
2480
+ children: [
2481
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": true, className: "cm-legend__swatch", ...swatchProps(emptyItem.item) }),
2482
+ /* @__PURE__ */ jsx9("span", { children: emptyItem.item.label }),
2483
+ showCount && /* @__PURE__ */ jsxs3("span", { className: "cm-legend__count", children: [
2484
+ "(",
2485
+ emptyItem.item.count,
2486
+ ")"
2487
+ ] })
2488
+ ]
2489
+ }
2490
+ )
2491
+ ] }) : items.length > 0 && /* @__PURE__ */ jsx9("ul", { className: "cm-legend__items", children: items.map((item, index) => /* @__PURE__ */ jsxs3(
2492
+ "li",
2493
+ {
2494
+ className: cx("cm-legend__item", classNames.legendItem),
2495
+ ...hoverProps(item, index),
2496
+ children: [
2497
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": true, className: "cm-legend__swatch", ...swatchProps(item) }),
2498
+ /* @__PURE__ */ jsx9("span", { children: item.label }),
2499
+ showCount && /* @__PURE__ */ jsxs3("span", { className: "cm-legend__count", children: [
2500
+ "(",
2501
+ item.count,
2502
+ ")"
2503
+ ] })
2504
+ ]
2505
+ },
2506
+ item.label
2507
+ )) }),
2508
+ sizeItems.length > 0 && /* @__PURE__ */ jsx9("ul", { className: "cm-legend__sizes", children: sizeItems.map((item) => /* @__PURE__ */ jsxs3("li", { className: "cm-legend__size", children: [
2509
+ /* @__PURE__ */ jsx9(
2510
+ "svg",
2511
+ {
2512
+ "aria-hidden": true,
2513
+ width: item.radius * 2 + 2,
2514
+ height: item.radius * 2 + 2,
2515
+ className: "cm-legend__circle",
2516
+ children: /* @__PURE__ */ jsx9("circle", { cx: item.radius + 1, cy: item.radius + 1, r: item.radius })
2517
+ }
2518
+ ),
2519
+ /* @__PURE__ */ jsx9("span", { children: item.label })
2520
+ ] }, item.label)) })
2521
+ ] });
2522
+ };
2523
+
2524
+ // src/react/components/regions.tsx
2525
+ import { memo } from "react";
2526
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
2527
+ var RegionPath = memo(
2528
+ ({
2529
+ id,
2530
+ d,
2531
+ fill,
2532
+ drillable,
2533
+ empty,
2534
+ dataClass,
2535
+ className,
2536
+ opacity,
2537
+ target,
2538
+ wave
2539
+ }) => /* @__PURE__ */ jsx10(
2540
+ "path",
2541
+ {
2542
+ className: cx("cm-region", className),
2543
+ "data-region-id": id,
2544
+ "data-empty": empty ? "" : void 0,
2545
+ "data-class": dataClass,
2546
+ "data-drillable": drillable ? "" : void 0,
2547
+ "data-drill-target": target ? "" : void 0,
2548
+ d,
2549
+ style: {
2550
+ fill,
2551
+ ...opacity === void 0 ? void 0 : { opacity },
2552
+ ...wave === void 0 ? void 0 : { "--cm-wave": wave }
2553
+ }
2554
+ }
2555
+ )
2556
+ );
2557
+ var AnonymousPath = memo(({ d, fill }) => /* @__PURE__ */ jsx10("path", { className: "cm-region cm-region--anonymous", d, style: { fill, cursor: "default" } }));
2558
+ var regionIdFrom = (event) => event.target.closest?.("[data-region-id]")?.getAttribute("data-region-id") ?? null;
2559
+ var Regions = () => {
2560
+ const { choropleth, store, classNames } = useChoroplethContext();
2561
+ const {
2562
+ regions,
2563
+ anonymousPaths,
2564
+ emptyFill,
2565
+ borders,
2566
+ clickable,
2567
+ hoverItem,
2568
+ clickItem,
2569
+ drillFocusId
2570
+ } = choropleth;
2571
+ const known = new Set(regions.map((region) => region.id));
2572
+ const regionRef = (event) => {
2573
+ const id = regionIdFrom(event);
2574
+ return id !== null && known.has(id) ? { kind: "region", id } : null;
2575
+ };
2576
+ const outlined = borders === null;
2577
+ return /* @__PURE__ */ jsxs4(
2578
+ "g",
2579
+ {
2580
+ className: cx("cm-regions", classNames.regions),
2581
+ "data-outlined": outlined ? "" : void 0,
2582
+ style: clickable ? { cursor: "pointer" } : void 0,
2583
+ onClick: (event) => {
2584
+ const ref = regionRef(event);
2585
+ if (ref !== null) clickItem(ref);
2586
+ },
2587
+ onPointerOver: (event) => {
2588
+ const ref = regionRef(event);
2589
+ if (ref !== null) {
2590
+ hoverItem(ref);
2591
+ } else if (store.getState().hovered?.kind === "region") {
2592
+ hoverItem(null);
2593
+ }
2594
+ },
2595
+ onPointerLeave: (event) => {
2596
+ if (event.pointerType === "touch") return;
2597
+ if (store.getState().hovered?.kind === "region") hoverItem(null);
2598
+ },
2599
+ children: [
2600
+ anonymousPaths.map((d, index) => /* @__PURE__ */ jsx10(AnonymousPath, { d, fill: emptyFill }, `anonymous-${index}`)),
2601
+ regions.map((region) => /* @__PURE__ */ jsx10(
2602
+ RegionPath,
2603
+ {
2604
+ id: region.id,
2605
+ d: region.path,
2606
+ fill: region.fill,
2607
+ drillable: region.drillable,
2608
+ empty: region.value === null,
2609
+ dataClass: region.classIndex === null ? "empty" : String(region.classIndex),
2610
+ className: cx(classNames.region, region.className),
2611
+ opacity: region.opacity,
2612
+ target: region.id === drillFocusId,
2613
+ wave: region.wave
2614
+ },
2615
+ region.id
2616
+ ))
2617
+ ]
2618
+ }
2619
+ );
2620
+ };
2621
+
2622
+ // src/react/components/root.tsx
2623
+ import {
2624
+ forwardRef,
2625
+ useCallback as useCallback3,
2626
+ useEffect as useEffect9,
2627
+ useId,
2628
+ useImperativeHandle,
2629
+ useMemo as useMemo6,
2630
+ useRef as useRef7
2631
+ } from "react";
2632
+
2633
+ // src/react/handle.ts
2634
+ var EMPTY_VIEW = {
2635
+ k: 1,
2636
+ x: 0,
2637
+ y: 0,
2638
+ zoom: 1,
2639
+ center: [0, 0],
2640
+ bounds: [
2641
+ [-180, -90],
2642
+ [180, 90]
2643
+ ],
2644
+ level: { id: "root", depth: 0 }
2645
+ };
2646
+ var NOT_AVAILABLE = Promise.resolve({ completed: false });
2647
+ var noDrill = (method) => {
2648
+ if (!isProduction()) {
2649
+ console.warn(`[choropleth] ${method}() needs the \`drill\` prop; this map has none.`);
2650
+ }
2651
+ return NOT_AVAILABLE;
2652
+ };
2653
+ var createHandle = (store) => ({
2654
+ zoomTo: (target, options) => store.getZoom()?.zoomTo(target, options) ?? NOT_AVAILABLE,
2655
+ zoomBy: (factor, options) => store.getZoom()?.zoomBy(factor, options) ?? NOT_AVAILABLE,
2656
+ resetView: (options) => store.getZoom()?.resetView(options) ?? NOT_AVAILABLE,
2657
+ getView: () => store.getZoom()?.getView() ?? store.getFallbackView() ?? EMPTY_VIEW,
2658
+ drillDown: (id) => store.getDrill()?.drillDown(id) ?? noDrill("drillDown"),
2659
+ drillUp: (steps) => store.getDrill()?.drillUp(steps) ?? noDrill("drillUp"),
2660
+ drillTo: (path) => store.getDrill()?.drillTo(path) ?? noDrill("drillTo")
2661
+ });
2662
+
2663
+ // src/react/components/root.tsx
2664
+ import { jsx as jsx11 } from "react/jsx-runtime";
2665
+ var RootInner = ({
2666
+ children,
2667
+ className,
2668
+ style,
2669
+ unstyled,
2670
+ theme,
2671
+ emptyPattern,
2672
+ classNames,
2673
+ dimOthers = "selection",
2674
+ ...options
2675
+ }, ref) => {
2676
+ const choropleth = useChoropleth({
2677
+ ...options,
2678
+ dimOthers
2679
+ });
2680
+ const { containerRef, store, status, dataLoading, selectedId, drillPhase } = choropleth;
2681
+ const viewportRef = useRef7(null);
2682
+ useEffect9(() => {
2683
+ const sync = () => {
2684
+ containerRef.current?.toggleAttribute("data-interacting", store.getState().interacting);
2685
+ };
2686
+ sync();
2687
+ return store.subscribe(sync);
2688
+ }, [store, containerRef]);
2689
+ const { width, height, invert } = choropleth;
2690
+ useEffect9(() => {
2691
+ store.registerFallbackView(
2692
+ () => toViewState({ transform: FIT_TRANSFORM, size: { width, height }, invert, level: ROOT_LEVEL })
2693
+ );
2694
+ return () => store.registerFallbackView(null);
2695
+ }, [store, width, height, invert]);
2696
+ const api = useMemo6(() => createHandle(store), [store]);
2697
+ useImperativeHandle(ref, () => api, [api]);
2698
+ const themeVars = useMemo6(() => themeToCssVars(resolveTheme(theme)), [theme]);
2699
+ const slots = useMemo6(() => classNames ?? {}, [classNames]);
2700
+ const instance = useId().replace(/[^a-zA-Z0-9_-]/g, "");
2701
+ const defId = useCallback3((name) => `cm${instance}-${name}`, [instance]);
2702
+ const pattern = emptyPattern ?? resolveTheme(theme)?.empty?.pattern;
2703
+ const resolvedPattern = pattern === "hatch" || pattern === "dots" ? pattern : null;
2704
+ const handlePointer = (event) => {
2705
+ const rect = event.currentTarget.getBoundingClientRect();
2706
+ store.setState({
2707
+ pointer: { x: event.clientX - rect.left, y: event.clientY - rect.top },
2708
+ // The drill asks for one or two taps depending on what is touching (a pen's wheel counts as a mouse).
2709
+ pointerType: event.pointerType === "touch" ? "touch" : event.pointerType === "pen" ? "pen" : "mouse"
2710
+ });
2711
+ };
2712
+ return /* @__PURE__ */ jsx11(
2713
+ ChoroplethContext.Provider,
2714
+ {
2715
+ value: {
2716
+ choropleth,
2717
+ store,
2718
+ viewportRef,
2719
+ api,
2720
+ classNames: slots,
2721
+ defId,
2722
+ emptyPattern: resolvedPattern
2723
+ },
2724
+ children: /* @__PURE__ */ jsx11(
2725
+ "div",
2726
+ {
2727
+ ref: containerRef,
2728
+ className: cx("cm-root", slots.root, className),
2729
+ "data-unstyled": unstyled ? "" : void 0,
2730
+ "data-theme": theme === void 0 ? void 0 : isThemeName(theme) ? theme : "custom",
2731
+ "data-status": status,
2732
+ "data-loading": dataLoading ? "" : void 0,
2733
+ "data-dim": dimOthers,
2734
+ "data-drill-phase": drillPhase === null || drillPhase === "idle" ? void 0 : drillPhase,
2735
+ "data-has-selection": selectedId === null ? void 0 : "",
2736
+ style: { position: "relative", ...themeVars, ...style },
2737
+ onPointerMove: handlePointer,
2738
+ onPointerDown: handlePointer,
2739
+ onPointerLeave: (event) => {
2740
+ if (event.pointerType !== "touch") store.setState({ pointer: null });
2741
+ },
2742
+ children
2743
+ }
2744
+ )
2745
+ }
2746
+ );
2747
+ };
2748
+ var Root = forwardRef(RootInner);
2749
+
2750
+ // src/react/components/selection.tsx
2751
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
2752
+ var Selection = () => {
2753
+ const { choropleth, store, classNames } = useChoroplethContext();
2754
+ const { regions, bubbles, fallbackPoints, selectedId } = choropleth;
2755
+ const hovered = useMapStore(store, (s) => s.hovered);
2756
+ const hoveredRegion = hovered?.kind === "region" ? regions.find((r) => r.id === hovered.id) : void 0;
2757
+ const hoveredPoint = hovered?.kind === "point" ? bubbles.find((b) => b.coordinates !== null && b.id === hovered.id) ?? fallbackPoints.find((p) => p.id === hovered.id) : void 0;
2758
+ const selectedRegion = selectedId === null ? void 0 : regions.find((r) => r.id === selectedId);
2759
+ return /* @__PURE__ */ jsxs5("g", { className: cx("cm-selection", classNames.selection), pointerEvents: "none", children: [
2760
+ hoveredRegion !== void 0 && /* @__PURE__ */ jsx12("path", { "data-selection": "hover", d: hoveredRegion.path, style: { fill: hoveredRegion.fill } }),
2761
+ hoveredPoint !== void 0 && /* @__PURE__ */ jsx12("circle", { "data-selection": "hover", cx: hoveredPoint.x, cy: hoveredPoint.y, r: hoveredPoint.r }),
2762
+ selectedRegion !== void 0 && /* @__PURE__ */ jsx12(
2763
+ "path",
2764
+ {
2765
+ "data-selection": "selected",
2766
+ d: selectedRegion.path,
2767
+ style: { fill: selectedRegion.fill }
2768
+ }
2769
+ )
2770
+ ] });
2771
+ };
2772
+
2773
+ // src/react/components/svg.tsx
2774
+ import { useRef as useRef8 } from "react";
2775
+
2776
+ // src/react/components/viewport.tsx
2777
+ import { jsx as jsx13 } from "react/jsx-runtime";
2778
+ var Viewport = ({ children }) => {
2779
+ const { viewportRef } = useChoroplethContext();
2780
+ return /* @__PURE__ */ jsx13("g", { ref: viewportRef, className: "cm-viewport", children });
2781
+ };
2782
+
2783
+ // src/react/components/svg.tsx
2784
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
2785
+ var Svg = ({ children, className, ariaLabel }) => {
2786
+ const { choropleth, store, viewportRef, classNames } = useChoroplethContext();
2787
+ const {
2788
+ width,
2789
+ height,
2790
+ status,
2791
+ labels,
2792
+ zoom: zoom2,
2793
+ invert,
2794
+ project,
2795
+ onViewChange,
2796
+ resolveCamera,
2797
+ focusId,
2798
+ containerRef
2799
+ } = choropleth;
2800
+ const svgRef = useRef8(null);
2801
+ useZoom({
2802
+ svgRef,
2803
+ viewportRef,
2804
+ rootRef: containerRef,
2805
+ store,
2806
+ width,
2807
+ height,
2808
+ options: zoom2,
2809
+ invert,
2810
+ project,
2811
+ onViewChange,
2812
+ resolveCamera,
2813
+ focusId,
2814
+ ready: status === "ready"
2815
+ });
2816
+ if (status === "loading") {
2817
+ return /* @__PURE__ */ jsx14("div", { className: "cm-skeleton", style: { aspectRatio: `${width} / ${height}` } });
2818
+ }
2819
+ if (status === "error") {
2820
+ return /* @__PURE__ */ jsx14("div", { className: "cm-error", role: "alert", style: { aspectRatio: `${width} / ${height}` }, children: labels.geographyError });
2821
+ }
2822
+ const { drillNav } = choropleth;
2823
+ const keyboard = zoom2 !== null && zoom2.keyboard || drillNav !== null;
2824
+ const handleKeyDown = (event) => {
2825
+ if (drillNav?.canDrillUp === true && (event.key === "Escape" || event.key === "Backspace") && !event.ctrlKey && !event.metaKey && !event.altKey) {
2826
+ event.preventDefault();
2827
+ drillNav.drillUp();
2828
+ return;
2829
+ }
2830
+ if (zoom2 === null || !zoom2.keyboard) return;
2831
+ const action = zoomKeyAction(event);
2832
+ if (action === null) return;
2833
+ event.preventDefault();
2834
+ const api = store.getZoom();
2835
+ if (action === "reset") void api?.resetView();
2836
+ else void api?.zoomBy(action === "in" ? 2 : 0.5);
2837
+ };
2838
+ return /* @__PURE__ */ jsx14(
2839
+ "svg",
2840
+ {
2841
+ ref: svgRef,
2842
+ tabIndex: keyboard ? 0 : void 0,
2843
+ onKeyDown: keyboard ? handleKeyDown : void 0,
2844
+ className: cx("cm-svg", classNames.svg, className),
2845
+ viewBox: `0 0 ${width} ${height}`,
2846
+ role: "img",
2847
+ "aria-label": ariaLabel,
2848
+ style: { display: "block", width: "100%", height: "auto" },
2849
+ children: children ?? /* @__PURE__ */ jsxs6(Fragment4, { children: [
2850
+ /* @__PURE__ */ jsx14(Defs, {}),
2851
+ /* @__PURE__ */ jsxs6(Viewport, { children: [
2852
+ /* @__PURE__ */ jsx14(DrillLayers, { role: "context" }),
2853
+ /* @__PURE__ */ jsx14(Regions, {}),
2854
+ /* @__PURE__ */ jsx14(Patterns, {}),
2855
+ /* @__PURE__ */ jsx14(Borders, {}),
2856
+ /* @__PURE__ */ jsx14(Bubbles, {}),
2857
+ /* @__PURE__ */ jsx14(FallbackPoints, {}),
2858
+ /* @__PURE__ */ jsx14(Selection, {}),
2859
+ /* @__PURE__ */ jsx14(DrillLayers, { role: "exiting" })
2860
+ ] })
2861
+ ] })
2862
+ }
2863
+ );
2864
+ };
2865
+
2866
+ // src/react/components/tooltip.tsx
2867
+ import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
2868
+ var OFFSET = 12;
2869
+ var DefaultContent = ({
2870
+ label,
2871
+ formattedValue,
2872
+ drillable,
2873
+ noData,
2874
+ drillHint
2875
+ }) => {
2876
+ return /* @__PURE__ */ jsxs7(Fragment5, { children: [
2877
+ /* @__PURE__ */ jsx15("strong", { children: label }),
2878
+ /* @__PURE__ */ jsx15("div", { children: formattedValue === "" ? noData : formattedValue }),
2879
+ drillable && /* @__PURE__ */ jsx15("div", { className: "cm-tooltip__hint", children: drillHint })
2880
+ ] });
2881
+ };
2882
+ var Tooltip = ({ className, children }) => {
2883
+ const { choropleth, store, classNames } = useChoroplethContext();
2884
+ const point = useMapStore(store, (s) => s.pointer);
2885
+ const hovered = useMapStore(store, (s) => s.hovered);
2886
+ const touch = useMapStore(store, (s) => s.pointerType === "touch");
2887
+ const context = hovered === null ? void 0 : choropleth.getItemContext(hovered);
2888
+ if (context === void 0 || point === null) return null;
2889
+ const flipX = point.x > choropleth.width / 2;
2890
+ const flipY = point.y > choropleth.height / 2;
2891
+ return /* @__PURE__ */ jsx15(
2892
+ "div",
2893
+ {
2894
+ role: "tooltip",
2895
+ className: cx(
2896
+ "cm-tooltip",
2897
+ // Only the default content has a box; if you pass `children`, the look is yours.
2898
+ children === void 0 && "cm-tooltip--default",
2899
+ classNames.tooltip,
2900
+ className
2901
+ ),
2902
+ style: {
2903
+ position: "absolute",
2904
+ left: point.x + (flipX ? -OFFSET : OFFSET),
2905
+ top: point.y + (flipY ? -OFFSET : OFFSET),
2906
+ transform: `translate(${flipX ? "-100%" : "0"}, ${flipY ? "-100%" : "0"})`,
2907
+ pointerEvents: "none"
2908
+ },
2909
+ children: children === void 0 ? /* @__PURE__ */ jsx15(
2910
+ DefaultContent,
2911
+ {
2912
+ ...context,
2913
+ noData: choropleth.labels.noData,
2914
+ drillHint: touch ? choropleth.labels.tapAgainHint : choropleth.labels.drillHint
2915
+ }
2916
+ ) : children(context)
2917
+ }
2918
+ );
2919
+ };
2920
+
2921
+ // src/react/components/zoom-controls.tsx
2922
+ import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
2923
+ var CORNERS2 = {
2924
+ "top-left": { top: 12, left: 12 },
2925
+ "top-right": { top: 12, right: 12 },
2926
+ "bottom-left": { bottom: 12, left: 12 },
2927
+ "bottom-right": { bottom: 12, right: 12 }
2928
+ };
2929
+ var STEP = 2;
2930
+ var ZoomControls = ({
2931
+ position = "bottom-right",
2932
+ reset = true,
2933
+ className
2934
+ }) => {
2935
+ const { choropleth, store, classNames } = useChoroplethContext();
2936
+ const { zoom: zoom2, labels } = choropleth;
2937
+ const camera = useMapStore(store, (s) => s.camera);
2938
+ if (zoom2 === null) return null;
2939
+ const atMax = camera.k >= zoom2.max - 1e-6;
2940
+ const atMin = camera.k <= zoom2.min + 1e-6;
2941
+ return /* @__PURE__ */ jsxs8(
2942
+ "div",
2943
+ {
2944
+ className: cx("cm-zoom-controls", classNames.controls, className),
2945
+ style: { position: "absolute", ...CORNERS2[position] },
2946
+ children: [
2947
+ /* @__PURE__ */ jsx16(
2948
+ "button",
2949
+ {
2950
+ type: "button",
2951
+ className: cx("cm-zoom-button", classNames.controlButton),
2952
+ "aria-label": labels.zoomIn,
2953
+ disabled: atMax,
2954
+ onClick: () => void store.getZoom()?.zoomBy(STEP),
2955
+ children: /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "+" })
2956
+ }
2957
+ ),
2958
+ /* @__PURE__ */ jsx16(
2959
+ "button",
2960
+ {
2961
+ type: "button",
2962
+ className: cx("cm-zoom-button", classNames.controlButton),
2963
+ "aria-label": labels.zoomOut,
2964
+ disabled: atMin,
2965
+ onClick: () => void store.getZoom()?.zoomBy(1 / STEP),
2966
+ children: /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u2212" })
2967
+ }
2968
+ ),
2969
+ reset && /* @__PURE__ */ jsx16(
2970
+ "button",
2971
+ {
2972
+ type: "button",
2973
+ className: cx("cm-zoom-button", classNames.controlButton),
2974
+ "aria-label": labels.resetView,
2975
+ disabled: isFitTransform(camera),
2976
+ onClick: () => void store.getZoom()?.resetView(),
2977
+ children: /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u27F2" })
2978
+ }
2979
+ )
2980
+ ]
2981
+ }
2982
+ );
2983
+ };
2984
+
2985
+ // src/react/choropleth.ts
2986
+ var Choropleth = {
2987
+ Root,
2988
+ Svg,
2989
+ Regions,
2990
+ Borders,
2991
+ Defs,
2992
+ Patterns,
2993
+ Breadcrumb,
2994
+ Bubbles,
2995
+ DrillLayers,
2996
+ FallbackPoints,
2997
+ Selection,
2998
+ Viewport,
2999
+ ZoomControls,
3000
+ GestureHint,
3001
+ Legend,
3002
+ Tooltip
3003
+ };
3004
+
3005
+ // src/react/choropleth-map.tsx
3006
+ import {
3007
+ forwardRef as forwardRef2
3008
+ } from "react";
3009
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
3010
+ var CORNERS3 = {
3011
+ "top-left": { top: 12, left: 12 },
3012
+ "top-right": { top: 12, right: 12 },
3013
+ "bottom-left": { bottom: 12, left: 12 },
3014
+ "bottom-right": { bottom: 12, right: 12 }
3015
+ };
3016
+ var ChoroplethMapInner = ({
3017
+ legend,
3018
+ breadcrumb,
3019
+ renderTooltip,
3020
+ className,
3021
+ style,
3022
+ unstyled,
3023
+ theme,
3024
+ emptyPattern,
3025
+ classNames,
3026
+ ariaLabel,
3027
+ ...options
3028
+ }, ref) => {
3029
+ const legendOptions = legend === false ? null : legend ?? {};
3030
+ const controls = resolveControls(options.zoom);
3031
+ return /* @__PURE__ */ jsxs9(
3032
+ Root,
3033
+ {
3034
+ ref,
3035
+ ...options,
3036
+ emptyLabel: options.emptyLabel ?? resolveLabels(options.labels).noData,
3037
+ className,
3038
+ style,
3039
+ unstyled,
3040
+ theme,
3041
+ emptyPattern,
3042
+ classNames,
3043
+ children: [
3044
+ /* @__PURE__ */ jsx17(Svg, { ariaLabel }),
3045
+ legendOptions !== null && /* @__PURE__ */ jsx17("div", { style: { position: "absolute", ...CORNERS3[legendOptions.position ?? "bottom-left"] }, children: /* @__PURE__ */ jsx17(
3046
+ Legend,
3047
+ {
3048
+ title: legendOptions.title,
3049
+ showCount: legendOptions.showCount,
3050
+ variant: legendOptions.variant,
3051
+ interactive: legendOptions.interactive
3052
+ }
3053
+ ) }),
3054
+ options.drill !== void 0 && breadcrumb !== false && /* @__PURE__ */ jsx17(Breadcrumb, { position: breadcrumb?.position ?? "top-left" }),
3055
+ controls !== null && /* @__PURE__ */ jsx17(ZoomControls, { position: controls.position, reset: controls.reset }),
3056
+ /* @__PURE__ */ jsx17(GestureHint, {}),
3057
+ /* @__PURE__ */ jsx17(Tooltip, { children: renderTooltip })
3058
+ ]
3059
+ }
3060
+ );
3061
+ };
3062
+ var ChoroplethMap = forwardRef2(ChoroplethMapInner);
3063
+ export {
3064
+ Borders,
3065
+ Breadcrumb,
3066
+ Bubbles,
3067
+ Choropleth,
3068
+ ChoroplethMap,
3069
+ DRILL_BRIDGE,
3070
+ Defs,
3071
+ DrillLayers,
3072
+ FALLBACK_POINT_RADIUS,
3073
+ FallbackPoints,
3074
+ GestureHint,
3075
+ Legend,
3076
+ Patterns,
3077
+ REVEAL_MS,
3078
+ ROOT_LEVEL,
3079
+ Regions,
3080
+ Root,
3081
+ Selection,
3082
+ Svg,
3083
+ THEMES,
3084
+ Tooltip,
3085
+ Viewport,
3086
+ ZoomControls,
3087
+ buildDrillEvent,
3088
+ clearGeographyCache,
3089
+ createDrillBridge,
3090
+ createMapStore,
3091
+ defineDrillLevels,
3092
+ defineTheme,
3093
+ extendTheme,
3094
+ isDrillController,
3095
+ isDrillable,
3096
+ sourceDepth,
3097
+ themeToCssVars,
3098
+ useCamera,
3099
+ useChoropleth,
3100
+ useChoroplethApi,
3101
+ useContainerSize,
3102
+ useDefId,
3103
+ useDrillChoreography,
3104
+ useDrillDown,
3105
+ useDrillEngine,
3106
+ useGeography,
3107
+ useMapStore,
3108
+ useZoom
3109
+ };
3110
+ //# sourceMappingURL=index.js.map