@buildcanada/charts 0.1.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 (404) hide show
  1. package/LICENSE.md +8 -0
  2. package/README.md +113 -0
  3. package/package.json +137 -0
  4. package/src/components/BodyPortal/BodyPortal.tsx +40 -0
  5. package/src/components/Button/Button.scss +110 -0
  6. package/src/components/Button/Button.tsx +101 -0
  7. package/src/components/Checkbox.scss +93 -0
  8. package/src/components/Checkbox.tsx +47 -0
  9. package/src/components/ExpandableToggle/ExpandableToggle.scss +123 -0
  10. package/src/components/ExpandableToggle/ExpandableToggle.tsx +60 -0
  11. package/src/components/GrapherTabIcon.tsx +156 -0
  12. package/src/components/GrapherTrendArrow.scss +16 -0
  13. package/src/components/GrapherTrendArrow.tsx +30 -0
  14. package/src/components/Halo/Halo.tsx +44 -0
  15. package/src/components/LabeledSwitch/LabeledSwitch.scss +109 -0
  16. package/src/components/LabeledSwitch/LabeledSwitch.tsx +62 -0
  17. package/src/components/MarkdownTextWrap/MarkdownTextWrap.tsx +1173 -0
  18. package/src/components/OverlayHeader.scss +18 -0
  19. package/src/components/OverlayHeader.tsx +29 -0
  20. package/src/components/RadioButton.scss +69 -0
  21. package/src/components/RadioButton.tsx +42 -0
  22. package/src/components/SimpleMarkdownText.tsx +89 -0
  23. package/src/components/TextInput.scss +17 -0
  24. package/src/components/TextInput.tsx +19 -0
  25. package/src/components/TextWrap/TextWrap.tsx +361 -0
  26. package/src/components/TextWrap/TextWrapUtils.ts +32 -0
  27. package/src/components/closeButton/CloseButton.scss +40 -0
  28. package/src/components/closeButton/CloseButton.tsx +27 -0
  29. package/src/components/index.ts +70 -0
  30. package/src/components/loadingIndicator/LoadingIndicator.scss +40 -0
  31. package/src/components/loadingIndicator/LoadingIndicator.tsx +28 -0
  32. package/src/components/markdown/remarkPlainLinks.ts +36 -0
  33. package/src/components/reactUtil.ts +20 -0
  34. package/src/components/stubs/CodeSnippet.tsx +19 -0
  35. package/src/components/stubs/DataCitation.tsx +16 -0
  36. package/src/components/stubs/IndicatorKeyData.tsx +45 -0
  37. package/src/components/stubs/IndicatorProcessing.tsx +15 -0
  38. package/src/components/stubs/IndicatorSources.tsx +15 -0
  39. package/src/components/styles/colors.scss +113 -0
  40. package/src/components/styles/mixins.scss +630 -0
  41. package/src/components/styles/typography.scss +579 -0
  42. package/src/components/styles/util.scss +89 -0
  43. package/src/components/styles/variables.scss +208 -0
  44. package/src/config/ChartsConfig.ts +163 -0
  45. package/src/config/ChartsProvider.tsx +157 -0
  46. package/src/config/index.ts +20 -0
  47. package/src/core-table/CoreTable.ts +1355 -0
  48. package/src/core-table/CoreTableColumns.ts +973 -0
  49. package/src/core-table/CoreTableUtils.ts +793 -0
  50. package/src/core-table/ErrorValues.ts +73 -0
  51. package/src/core-table/OwidTable.ts +1175 -0
  52. package/src/core-table/OwidTableSynthesizers.ts +272 -0
  53. package/src/core-table/OwidTableUtil.ts +76 -0
  54. package/src/core-table/Transforms.ts +484 -0
  55. package/src/core-table/index.ts +82 -0
  56. package/src/explorer/ColumnGrammar.ts +217 -0
  57. package/src/explorer/Explorer.sample.ts +212 -0
  58. package/src/explorer/Explorer.scss +148 -0
  59. package/src/explorer/Explorer.tsx +1283 -0
  60. package/src/explorer/ExplorerConstants.ts +85 -0
  61. package/src/explorer/ExplorerControls.scss +156 -0
  62. package/src/explorer/ExplorerControls.tsx +210 -0
  63. package/src/explorer/ExplorerDecisionMatrix.ts +471 -0
  64. package/src/explorer/ExplorerGrammar.ts +161 -0
  65. package/src/explorer/ExplorerProgram.ts +568 -0
  66. package/src/explorer/ExplorerUtils.ts +59 -0
  67. package/src/explorer/GrapherGrammar.ts +387 -0
  68. package/src/explorer/gridLang/GrammarUtils.ts +121 -0
  69. package/src/explorer/gridLang/GridCell.ts +298 -0
  70. package/src/explorer/gridLang/GridLangConstants.ts +255 -0
  71. package/src/explorer/gridLang/GridProgram.ts +311 -0
  72. package/src/explorer/gridLang/readme.md +17 -0
  73. package/src/explorer/index.ts +69 -0
  74. package/src/explorer/readme.md +19 -0
  75. package/src/explorer/urlMigrations/CO2UrlMigration.ts +46 -0
  76. package/src/explorer/urlMigrations/CovidUrlMigration.ts +37 -0
  77. package/src/explorer/urlMigrations/EnergyUrlMigration.ts +41 -0
  78. package/src/explorer/urlMigrations/ExplorerPageUrlMigrationSpec.ts +12 -0
  79. package/src/explorer/urlMigrations/ExplorerUrlMigrationUtils.ts +45 -0
  80. package/src/explorer/urlMigrations/ExplorerUrlMigrations.ts +33 -0
  81. package/src/explorer/urlMigrations/LegacyCovidUrlMigration.ts +144 -0
  82. package/src/explorer/urlMigrations/readme.md +39 -0
  83. package/src/grapher/axis/Axis.ts +973 -0
  84. package/src/grapher/axis/AxisConfig.ts +179 -0
  85. package/src/grapher/axis/AxisViews.tsx +597 -0
  86. package/src/grapher/barCharts/DiscreteBarChart.tsx +728 -0
  87. package/src/grapher/barCharts/DiscreteBarChartConstants.ts +60 -0
  88. package/src/grapher/barCharts/DiscreteBarChartHelpers.ts +338 -0
  89. package/src/grapher/barCharts/DiscreteBarChartState.ts +354 -0
  90. package/src/grapher/barCharts/DiscreteBarChartThumbnail.tsx +34 -0
  91. package/src/grapher/captionedChart/CaptionedChart.scss +61 -0
  92. package/src/grapher/captionedChart/CaptionedChart.tsx +523 -0
  93. package/src/grapher/captionedChart/Logos.tsx +141 -0
  94. package/src/grapher/captionedChart/LogosSVG.tsx +16 -0
  95. package/src/grapher/captionedChart/StaticChartRasterizer.tsx +178 -0
  96. package/src/grapher/captionedChart/assets/buildcanada-logo-square.svg +15 -0
  97. package/src/grapher/captionedChart/assets/buildcanada-logo.svg +15 -0
  98. package/src/grapher/captionedChart/assets/canadaspends.svg +7 -0
  99. package/src/grapher/captionedChart/readme.md +14 -0
  100. package/src/grapher/chart/Chart.tsx +62 -0
  101. package/src/grapher/chart/ChartAreaContent.tsx +172 -0
  102. package/src/grapher/chart/ChartDimension.ts +121 -0
  103. package/src/grapher/chart/ChartInterface.ts +83 -0
  104. package/src/grapher/chart/ChartManager.ts +113 -0
  105. package/src/grapher/chart/ChartTabs.ts +178 -0
  106. package/src/grapher/chart/ChartTypeMap.tsx +158 -0
  107. package/src/grapher/chart/ChartTypeSwitcher.tsx +26 -0
  108. package/src/grapher/chart/ChartUtils.tsx +364 -0
  109. package/src/grapher/chart/DimensionSlot.ts +45 -0
  110. package/src/grapher/chart/StaticChartWrapper.tsx +94 -0
  111. package/src/grapher/chart/guidedChartUtils.ts +82 -0
  112. package/src/grapher/color/BinningStrategies.ts +484 -0
  113. package/src/grapher/color/BinningStrategyEqualSizeBins.ts +132 -0
  114. package/src/grapher/color/BinningStrategyLogarithmic.ts +121 -0
  115. package/src/grapher/color/CategoricalColorAssigner.ts +97 -0
  116. package/src/grapher/color/ColorBrewerSchemes.ts +80 -0
  117. package/src/grapher/color/ColorConstants.ts +20 -0
  118. package/src/grapher/color/ColorScale.ts +339 -0
  119. package/src/grapher/color/ColorScaleBin.ts +147 -0
  120. package/src/grapher/color/ColorScaleConfig.ts +204 -0
  121. package/src/grapher/color/ColorScheme.ts +137 -0
  122. package/src/grapher/color/ColorSchemes.ts +149 -0
  123. package/src/grapher/color/ColorUtils.ts +86 -0
  124. package/src/grapher/color/CustomSchemes.ts +1772 -0
  125. package/src/grapher/color/readme.md +84 -0
  126. package/src/grapher/comparisonLine/ComparisonLine.tsx +31 -0
  127. package/src/grapher/comparisonLine/ComparisonLineConstants.ts +11 -0
  128. package/src/grapher/comparisonLine/ComparisonLineGenerator.ts +60 -0
  129. package/src/grapher/comparisonLine/ComparisonLineHelpers.ts +10 -0
  130. package/src/grapher/comparisonLine/CustomComparisonLine.tsx +159 -0
  131. package/src/grapher/comparisonLine/VerticalComparisonLine.tsx +208 -0
  132. package/src/grapher/controls/ActionButtons.scss +97 -0
  133. package/src/grapher/controls/ActionButtons.tsx +453 -0
  134. package/src/grapher/controls/CommandPalette.scss +50 -0
  135. package/src/grapher/controls/CommandPalette.tsx +74 -0
  136. package/src/grapher/controls/ContentSwitchers.scss +93 -0
  137. package/src/grapher/controls/ContentSwitchers.tsx +238 -0
  138. package/src/grapher/controls/Controls.scss +158 -0
  139. package/src/grapher/controls/DataTableFilterDropdown.scss +7 -0
  140. package/src/grapher/controls/DataTableFilterDropdown.tsx +168 -0
  141. package/src/grapher/controls/DataTableSearchField.scss +3 -0
  142. package/src/grapher/controls/DataTableSearchField.tsx +76 -0
  143. package/src/grapher/controls/Dropdown.scss +252 -0
  144. package/src/grapher/controls/Dropdown.tsx +235 -0
  145. package/src/grapher/controls/EntitySelectionToggle.tsx +135 -0
  146. package/src/grapher/controls/MapRegionDropdown.scss +3 -0
  147. package/src/grapher/controls/MapRegionDropdown.tsx +104 -0
  148. package/src/grapher/controls/MapResetButton.tsx +115 -0
  149. package/src/grapher/controls/MapZoomDropdown.scss +9 -0
  150. package/src/grapher/controls/MapZoomDropdown.tsx +270 -0
  151. package/src/grapher/controls/MapZoomToSelectionButton.tsx +87 -0
  152. package/src/grapher/controls/SearchField.scss +78 -0
  153. package/src/grapher/controls/SearchField.tsx +63 -0
  154. package/src/grapher/controls/SettingsMenu.scss +191 -0
  155. package/src/grapher/controls/SettingsMenu.tsx +399 -0
  156. package/src/grapher/controls/ShareMenu.scss +58 -0
  157. package/src/grapher/controls/ShareMenu.tsx +304 -0
  158. package/src/grapher/controls/SortIcon.tsx +39 -0
  159. package/src/grapher/controls/VerticalScrollContainer.tsx +263 -0
  160. package/src/grapher/controls/controlsRow/ControlsRow.tsx +168 -0
  161. package/src/grapher/controls/dropdown-icons.scss +4 -0
  162. package/src/grapher/controls/entityPicker/EntityPicker.scss +255 -0
  163. package/src/grapher/controls/entityPicker/EntityPicker.tsx +816 -0
  164. package/src/grapher/controls/entityPicker/EntityPickerConstants.ts +23 -0
  165. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.scss +129 -0
  166. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.tsx +463 -0
  167. package/src/grapher/controls/globalEntitySelector/GlobalEntitySelectorConstants.ts +3 -0
  168. package/src/grapher/controls/globalEntitySelector/readme.md +17 -0
  169. package/src/grapher/controls/settings/AbsRelToggle.tsx +64 -0
  170. package/src/grapher/controls/settings/AxisScaleToggle.tsx +53 -0
  171. package/src/grapher/controls/settings/FacetStrategySelector.tsx +110 -0
  172. package/src/grapher/controls/settings/FacetYDomainToggle.tsx +51 -0
  173. package/src/grapher/controls/settings/NoDataAreaToggle.tsx +38 -0
  174. package/src/grapher/controls/settings/ZoomToggle.tsx +36 -0
  175. package/src/grapher/core/EntitiesByRegionType.ts +174 -0
  176. package/src/grapher/core/EntityCodes.ts +19 -0
  177. package/src/grapher/core/EntityUrlBuilder.ts +200 -0
  178. package/src/grapher/core/FetchingGrapher.tsx +156 -0
  179. package/src/grapher/core/Grapher.tsx +760 -0
  180. package/src/grapher/core/GrapherAnalytics.ts +229 -0
  181. package/src/grapher/core/GrapherConstants.ts +173 -0
  182. package/src/grapher/core/GrapherState.tsx +3659 -0
  183. package/src/grapher/core/GrapherUrl.ts +184 -0
  184. package/src/grapher/core/GrapherUrlMigrations.ts +29 -0
  185. package/src/grapher/core/GrapherUseHelpers.tsx +147 -0
  186. package/src/grapher/core/LegacyToOwidTable.ts +841 -0
  187. package/src/grapher/core/grapher.entry.ts +5 -0
  188. package/src/grapher/core/grapher.scss +257 -0
  189. package/src/grapher/core/loadGrapherTableHelpers.ts +116 -0
  190. package/src/grapher/core/loadVariable.ts +104 -0
  191. package/src/grapher/core/relatedQuestion.ts +12 -0
  192. package/src/grapher/core/typography.scss +206 -0
  193. package/src/grapher/dataTable/DataTable.sample.ts +206 -0
  194. package/src/grapher/dataTable/DataTable.scss +249 -0
  195. package/src/grapher/dataTable/DataTable.tsx +1332 -0
  196. package/src/grapher/dataTable/DataTableConstants.ts +186 -0
  197. package/src/grapher/entitySelector/EntitySelector.scss +255 -0
  198. package/src/grapher/entitySelector/EntitySelector.tsx +1838 -0
  199. package/src/grapher/facet/FacetChart.tsx +943 -0
  200. package/src/grapher/facet/FacetChartConstants.ts +24 -0
  201. package/src/grapher/facet/FacetChartUtils.ts +51 -0
  202. package/src/grapher/facet/FacetMap.tsx +604 -0
  203. package/src/grapher/facet/FacetMapConstants.ts +23 -0
  204. package/src/grapher/facet/readme.md +13 -0
  205. package/src/grapher/focus/FocusArray.ts +79 -0
  206. package/src/grapher/footer/Footer.scss +63 -0
  207. package/src/grapher/footer/Footer.tsx +809 -0
  208. package/src/grapher/footer/FooterManager.ts +44 -0
  209. package/src/grapher/fullScreen/FullScreen.scss +11 -0
  210. package/src/grapher/fullScreen/FullScreen.tsx +61 -0
  211. package/src/grapher/header/Header.scss +35 -0
  212. package/src/grapher/header/Header.tsx +372 -0
  213. package/src/grapher/header/HeaderManager.ts +28 -0
  214. package/src/grapher/index.ts +157 -0
  215. package/src/grapher/interaction/InteractionState.ts +60 -0
  216. package/src/grapher/legend/HorizontalColorLegends.tsx +923 -0
  217. package/src/grapher/legend/LegendInteractionState.ts +40 -0
  218. package/src/grapher/legend/VerticalColorLegend.tsx +295 -0
  219. package/src/grapher/lineCharts/LineChart.tsx +968 -0
  220. package/src/grapher/lineCharts/LineChartConstants.ts +89 -0
  221. package/src/grapher/lineCharts/LineChartHelpers.ts +184 -0
  222. package/src/grapher/lineCharts/LineChartState.ts +394 -0
  223. package/src/grapher/lineCharts/LineChartThumbnail.tsx +437 -0
  224. package/src/grapher/lineCharts/Lines.tsx +258 -0
  225. package/src/grapher/lineLegend/LineLegend.tsx +723 -0
  226. package/src/grapher/lineLegend/LineLegendConstants.ts +9 -0
  227. package/src/grapher/lineLegend/LineLegendFilterAlgorithms.ts +143 -0
  228. package/src/grapher/lineLegend/LineLegendHelpers.ts +253 -0
  229. package/src/grapher/lineLegend/LineLegendTypes.ts +32 -0
  230. package/src/grapher/mapCharts/CanadaTopology.ts +17922 -0
  231. package/src/grapher/mapCharts/ChoroplethGlobe.tsx +949 -0
  232. package/src/grapher/mapCharts/ChoroplethMap.tsx +662 -0
  233. package/src/grapher/mapCharts/GeoFeatures.ts +184 -0
  234. package/src/grapher/mapCharts/GlobeController.ts +496 -0
  235. package/src/grapher/mapCharts/MapAnnotationPlacements.json +1040 -0
  236. package/src/grapher/mapCharts/MapAnnotationPlacements.ts +31 -0
  237. package/src/grapher/mapCharts/MapAnnotations.ts +723 -0
  238. package/src/grapher/mapCharts/MapChart.sample.ts +59 -0
  239. package/src/grapher/mapCharts/MapChart.scss +5 -0
  240. package/src/grapher/mapCharts/MapChart.tsx +720 -0
  241. package/src/grapher/mapCharts/MapChartConstants.ts +260 -0
  242. package/src/grapher/mapCharts/MapChartState.ts +416 -0
  243. package/src/grapher/mapCharts/MapChartThumbnail.tsx +25 -0
  244. package/src/grapher/mapCharts/MapComponents.tsx +338 -0
  245. package/src/grapher/mapCharts/MapConfig.ts +156 -0
  246. package/src/grapher/mapCharts/MapHelpers.ts +181 -0
  247. package/src/grapher/mapCharts/MapProjections.ts +49 -0
  248. package/src/grapher/mapCharts/MapSparkline.tsx +257 -0
  249. package/src/grapher/mapCharts/MapTooltip.scss +49 -0
  250. package/src/grapher/mapCharts/MapTooltip.tsx +409 -0
  251. package/src/grapher/mapCharts/MapTopology.ts +1766 -0
  252. package/src/grapher/mapCharts/d3-bboxCollide.js +204 -0
  253. package/src/grapher/mapCharts/d3-geo-projection.ts +198 -0
  254. package/src/grapher/modal/DownloadIcons.tsx +39 -0
  255. package/src/grapher/modal/DownloadModal.scss +300 -0
  256. package/src/grapher/modal/DownloadModal.tsx +1226 -0
  257. package/src/grapher/modal/EmbedModal.scss +40 -0
  258. package/src/grapher/modal/EmbedModal.tsx +160 -0
  259. package/src/grapher/modal/EntitySelectorModal.tsx +59 -0
  260. package/src/grapher/modal/Modal.scss +31 -0
  261. package/src/grapher/modal/Modal.tsx +90 -0
  262. package/src/grapher/modal/ModalHeader.scss +12 -0
  263. package/src/grapher/modal/ModalHeader.tsx +16 -0
  264. package/src/grapher/modal/SourcesDescriptions.scss +87 -0
  265. package/src/grapher/modal/SourcesDescriptions.tsx +89 -0
  266. package/src/grapher/modal/SourcesKeyDataTable.scss +49 -0
  267. package/src/grapher/modal/SourcesKeyDataTable.tsx +87 -0
  268. package/src/grapher/modal/SourcesModal.scss +301 -0
  269. package/src/grapher/modal/SourcesModal.tsx +568 -0
  270. package/src/grapher/noDataModal/NoDataModal.tsx +125 -0
  271. package/src/grapher/scatterCharts/ConnectedScatterLegend.tsx +143 -0
  272. package/src/grapher/scatterCharts/MultiColorPolyline.tsx +129 -0
  273. package/src/grapher/scatterCharts/NoDataSection.scss +14 -0
  274. package/src/grapher/scatterCharts/NoDataSection.tsx +56 -0
  275. package/src/grapher/scatterCharts/ScatterPlotChart.tsx +792 -0
  276. package/src/grapher/scatterCharts/ScatterPlotChartConstants.ts +157 -0
  277. package/src/grapher/scatterCharts/ScatterPlotChartState.ts +678 -0
  278. package/src/grapher/scatterCharts/ScatterPlotChartThumbnail.tsx +155 -0
  279. package/src/grapher/scatterCharts/ScatterPlotTooltip.tsx +560 -0
  280. package/src/grapher/scatterCharts/ScatterPoints.tsx +153 -0
  281. package/src/grapher/scatterCharts/ScatterPointsWithLabels.tsx +708 -0
  282. package/src/grapher/scatterCharts/ScatterSizeLegend.tsx +327 -0
  283. package/src/grapher/scatterCharts/ScatterUtils.ts +265 -0
  284. package/src/grapher/scatterCharts/Triangle.tsx +41 -0
  285. package/src/grapher/schema/README.md +33 -0
  286. package/src/grapher/schema/defaultGrapherConfig.ts +100 -0
  287. package/src/grapher/schema/grapher-schema.009.yaml +781 -0
  288. package/src/grapher/schema/migrations/helpers.ts +58 -0
  289. package/src/grapher/schema/migrations/migrate.ts +75 -0
  290. package/src/grapher/schema/migrations/migrations.ts +158 -0
  291. package/src/grapher/selection/MapSelectionArray.ts +99 -0
  292. package/src/grapher/selection/SelectionArray.ts +71 -0
  293. package/src/grapher/selection/readme.md +16 -0
  294. package/src/grapher/sidePanel/SidePanel.scss +10 -0
  295. package/src/grapher/sidePanel/SidePanel.tsx +23 -0
  296. package/src/grapher/slideInDrawer/SlideInDrawer.scss +57 -0
  297. package/src/grapher/slideInDrawer/SlideInDrawer.tsx +125 -0
  298. package/src/grapher/slideshowController/SlideShowController.tsx +43 -0
  299. package/src/grapher/slideshowController/readme.md +7 -0
  300. package/src/grapher/slopeCharts/MarkX.tsx +45 -0
  301. package/src/grapher/slopeCharts/Slope.tsx +102 -0
  302. package/src/grapher/slopeCharts/SlopeChart.tsx +1152 -0
  303. package/src/grapher/slopeCharts/SlopeChartConstants.ts +33 -0
  304. package/src/grapher/slopeCharts/SlopeChartHelpers.ts +73 -0
  305. package/src/grapher/slopeCharts/SlopeChartState.ts +392 -0
  306. package/src/grapher/slopeCharts/SlopeChartThumbnail.tsx +368 -0
  307. package/src/grapher/stackedCharts/AbstractStackedChartState.ts +370 -0
  308. package/src/grapher/stackedCharts/MarimekkoBars.tsx +190 -0
  309. package/src/grapher/stackedCharts/MarimekkoBarsForOneEntity.tsx +168 -0
  310. package/src/grapher/stackedCharts/MarimekkoChart.tsx +1144 -0
  311. package/src/grapher/stackedCharts/MarimekkoChartConstants.ts +112 -0
  312. package/src/grapher/stackedCharts/MarimekkoChartHelpers.ts +21 -0
  313. package/src/grapher/stackedCharts/MarimekkoChartState.ts +465 -0
  314. package/src/grapher/stackedCharts/MarimekkoChartThumbnail.tsx +168 -0
  315. package/src/grapher/stackedCharts/MarimekkoInternalLabels.tsx +124 -0
  316. package/src/grapher/stackedCharts/StackedAreaChart.tsx +678 -0
  317. package/src/grapher/stackedCharts/StackedAreaChartState.ts +34 -0
  318. package/src/grapher/stackedCharts/StackedAreaChartThumbnail.tsx +215 -0
  319. package/src/grapher/stackedCharts/StackedAreas.tsx +223 -0
  320. package/src/grapher/stackedCharts/StackedBarChart.tsx +619 -0
  321. package/src/grapher/stackedCharts/StackedBarChartState.ts +80 -0
  322. package/src/grapher/stackedCharts/StackedBarChartThumbnail.tsx +220 -0
  323. package/src/grapher/stackedCharts/StackedBarSegment.tsx +87 -0
  324. package/src/grapher/stackedCharts/StackedBars.tsx +102 -0
  325. package/src/grapher/stackedCharts/StackedConstants.ts +109 -0
  326. package/src/grapher/stackedCharts/StackedDiscreteBarChart.tsx +270 -0
  327. package/src/grapher/stackedCharts/StackedDiscreteBarChartState.ts +296 -0
  328. package/src/grapher/stackedCharts/StackedDiscreteBarChartThumbnail.tsx +27 -0
  329. package/src/grapher/stackedCharts/StackedDiscreteBars.tsx +648 -0
  330. package/src/grapher/stackedCharts/StackedUtils.ts +142 -0
  331. package/src/grapher/tabs/Tabs.scss +169 -0
  332. package/src/grapher/tabs/Tabs.tsx +54 -0
  333. package/src/grapher/tabs/TabsWithDropdown.scss +62 -0
  334. package/src/grapher/tabs/TabsWithDropdown.tsx +114 -0
  335. package/src/grapher/testData/OwidTestData.sample.ts +273 -0
  336. package/src/grapher/testData/OwidTestData.ts +64 -0
  337. package/src/grapher/timeline/TimelineComponent.scss +139 -0
  338. package/src/grapher/timeline/TimelineComponent.tsx +658 -0
  339. package/src/grapher/timeline/TimelineController.ts +368 -0
  340. package/src/grapher/timeline/readme.md +7 -0
  341. package/src/grapher/tooltip/Tooltip.scss +510 -0
  342. package/src/grapher/tooltip/Tooltip.tsx +294 -0
  343. package/src/grapher/tooltip/TooltipContents.tsx +383 -0
  344. package/src/grapher/tooltip/TooltipProps.ts +123 -0
  345. package/src/grapher/tooltip/TooltipState.ts +81 -0
  346. package/src/grapher/verticalLabels/VerticalLabels.tsx +31 -0
  347. package/src/grapher/verticalLabels/VerticalLabelsState.ts +154 -0
  348. package/src/index.ts +226 -0
  349. package/src/styles/charts.scss +15 -0
  350. package/src/types/NominalType.ts +30 -0
  351. package/src/types/OwidOrigin.ts +18 -0
  352. package/src/types/OwidSource.ts +9 -0
  353. package/src/types/OwidVariable.ts +133 -0
  354. package/src/types/OwidVariableDisplayConfigInterface.ts +49 -0
  355. package/src/types/analyticsTypes.ts +54 -0
  356. package/src/types/dbTypes/Tags.ts +11 -0
  357. package/src/types/domainTypes/Archive.ts +139 -0
  358. package/src/types/domainTypes/Author.ts +28 -0
  359. package/src/types/domainTypes/ContentGraph.ts +76 -0
  360. package/src/types/domainTypes/CoreTableTypes.ts +305 -0
  361. package/src/types/domainTypes/DeployStatus.ts +23 -0
  362. package/src/types/domainTypes/Layout.ts +34 -0
  363. package/src/types/domainTypes/Posts.ts +34 -0
  364. package/src/types/domainTypes/Search.ts +299 -0
  365. package/src/types/domainTypes/Site.ts +8 -0
  366. package/src/types/domainTypes/StaticViz.ts +64 -0
  367. package/src/types/domainTypes/Toc.ts +11 -0
  368. package/src/types/domainTypes/Tombstone.ts +19 -0
  369. package/src/types/domainTypes/Various.ts +79 -0
  370. package/src/types/gdocTypes/Gdoc.ts +280 -0
  371. package/src/types/grapherTypes/BinningStrategyTypes.ts +46 -0
  372. package/src/types/grapherTypes/GrapherConstants.ts +53 -0
  373. package/src/types/grapherTypes/GrapherTypes.ts +743 -0
  374. package/src/types/index.ts +316 -0
  375. package/src/types/wordpressTypes/WordpressTypes.ts +9 -0
  376. package/src/utils/Bounds.ts +439 -0
  377. package/src/utils/BrowserUtils.ts +12 -0
  378. package/src/utils/FuzzySearch.ts +74 -0
  379. package/src/utils/MultiDimDataPageConfig.ts +31 -0
  380. package/src/utils/OwidVariable.ts +82 -0
  381. package/src/utils/PointVector.ts +97 -0
  382. package/src/utils/PromiseCache.ts +36 -0
  383. package/src/utils/PromiseSwitcher.ts +52 -0
  384. package/src/utils/TimeBounds.ts +130 -0
  385. package/src/utils/Tippy.tsx +57 -0
  386. package/src/utils/Util.ts +2369 -0
  387. package/src/utils/archival/archivalDate.ts +48 -0
  388. package/src/utils/dayjs.ts +32 -0
  389. package/src/utils/formatValue.ts +242 -0
  390. package/src/utils/grapherConfigUtils.ts +81 -0
  391. package/src/utils/image.ts +225 -0
  392. package/src/utils/index.ts +318 -0
  393. package/src/utils/isPresent.ts +5 -0
  394. package/src/utils/metadataHelpers.ts +329 -0
  395. package/src/utils/persistable/Persistable.ts +82 -0
  396. package/src/utils/persistable/readme.md +50 -0
  397. package/src/utils/regions.json +5635 -0
  398. package/src/utils/regions.ts +463 -0
  399. package/src/utils/serializers.ts +16 -0
  400. package/src/utils/string.ts +42 -0
  401. package/src/utils/urls/Url.ts +195 -0
  402. package/src/utils/urls/UrlMigration.ts +10 -0
  403. package/src/utils/urls/UrlUtils.ts +54 -0
  404. package/src/utils/urls/readme.md +90 -0
@@ -0,0 +1,662 @@
1
+ import * as _ from "lodash-es"
2
+ import React from "react"
3
+ import {
4
+ Bounds,
5
+ isTouchDevice,
6
+ makeIdForHumanConsumption,
7
+ excludeUndefined,
8
+ EntityName,
9
+ MapRegionName,
10
+ } from "../../utils/index.js"
11
+ import { computed, action, observable, makeObservable } from "mobx"
12
+ import { observer } from "mobx-react"
13
+ import { Quadtree, quadtree } from "d3-quadtree"
14
+ import {
15
+ MapRenderFeature,
16
+ SVGMouseEvent,
17
+ GEO_FEATURES_CLASSNAME,
18
+ ChoroplethMapManager,
19
+ ChoroplethSeriesByName,
20
+ CHOROPLETH_MAP_CLASSNAME,
21
+ ExternalAnnotation,
22
+ InternalAnnotation,
23
+ Annotation,
24
+ ANNOTATION_COLOR_LIGHT,
25
+ ANNOTATION_COLOR_DARK,
26
+ RenderFeature,
27
+ PROJECTED_DATA_LEGEND_COLOR,
28
+ MapViewport,
29
+ MAP_VIEWPORTS,
30
+ MAP_REGION_LABELS,
31
+ } from "./MapChartConstants"
32
+ import { getGeoFeaturesForMap } from "./GeoFeatures"
33
+ import {
34
+ BackgroundCountry,
35
+ CountryWithData,
36
+ CountryWithNoData,
37
+ ExternalValueAnnotation,
38
+ InternalValueAnnotation,
39
+ NoDataPattern,
40
+ ProjectedDataPattern,
41
+ } from "./MapComponents"
42
+ import { Patterns } from "../core/GrapherConstants"
43
+ import {
44
+ detectNearbyFeature,
45
+ getCountriesByRegion,
46
+ getForegroundFeatures,
47
+ sortFeaturesByInteractionStateAndSize,
48
+ } from "./MapHelpers"
49
+ import {
50
+ makeInternalAnnotationForFeature,
51
+ makeExternalAnnotationForFeature,
52
+ repositionAndFilterExternalAnnotations,
53
+ } from "./MapAnnotations"
54
+ import { isDarkColor } from "../color/ColorUtils"
55
+ import { MapConfig } from "./MapConfig"
56
+ import { MapSelectionArray } from "../selection/MapSelectionArray"
57
+ import { MAP_PROJECTIONS } from "./MapProjections"
58
+
59
+ @observer
60
+ export class ChoroplethMap extends React.Component<{
61
+ manager: ChoroplethMapManager
62
+ }> {
63
+ base = React.createRef<SVGGElement>()
64
+
65
+ private hoverEnterFeature: MapRenderFeature | undefined = undefined
66
+ private hoverNearbyFeature: MapRenderFeature | undefined = undefined
67
+
68
+ constructor(props: { manager: ChoroplethMapManager }) {
69
+ super(props)
70
+
71
+ makeObservable<
72
+ ChoroplethMap,
73
+ "hoverEnterFeature" | "hoverNearbyFeature"
74
+ >(this, {
75
+ hoverEnterFeature: observable,
76
+ hoverNearbyFeature: observable,
77
+ })
78
+ }
79
+
80
+ @computed private get isTouchDevice(): boolean {
81
+ return isTouchDevice()
82
+ }
83
+
84
+ @computed private get manager(): ChoroplethMapManager {
85
+ return this.props.manager
86
+ }
87
+
88
+ @computed get mapConfig(): MapConfig {
89
+ return this.manager.mapConfig
90
+ }
91
+
92
+ @computed.struct private get bounds(): Bounds {
93
+ return this.manager.choroplethMapBounds
94
+ }
95
+
96
+ @computed private get selectionArray(): MapSelectionArray {
97
+ return this.manager.selectionArray ?? new MapSelectionArray()
98
+ }
99
+
100
+ @computed private get viewport(): MapViewport {
101
+ return this.manager.mapViewport ?? MAP_VIEWPORTS[this.mapConfig.region]
102
+ }
103
+
104
+ @computed.struct private get choroplethData(): ChoroplethSeriesByName {
105
+ return this.manager.choroplethData
106
+ }
107
+
108
+ @computed private get hoverFeature(): MapRenderFeature | undefined {
109
+ return this.hoverEnterFeature || this.hoverNearbyFeature
110
+ }
111
+
112
+ // Combine bounding boxes to get the extents of the entire map
113
+ @computed private get mapBounds(): Bounds {
114
+ const allBounds = this.features.map((feature) => feature.projBounds)
115
+ return Bounds.merge(allBounds)
116
+ }
117
+
118
+ // Calculate what scaling should be applied to the untransformed map to match the current viewport to the container
119
+ @computed private get viewportScale(): number {
120
+ const { bounds, viewport, mapBounds } = this
121
+ const viewportWidth = viewport.width * mapBounds.width
122
+ const viewportHeight = viewport.height * mapBounds.height
123
+ return Math.min(
124
+ bounds.width / viewportWidth,
125
+ bounds.height / viewportHeight
126
+ )
127
+ }
128
+
129
+ @computed private get viewportScaleSqrt(): number {
130
+ return Math.sqrt(this.viewportScale)
131
+ }
132
+
133
+ @computed private get matrixTransform(): string {
134
+ const { bounds, mapBounds, viewport, viewportScale } = this
135
+
136
+ // Calculate our reference dimensions. These values are independent of the current
137
+ // map translation and scaling.
138
+ const mapX = mapBounds.x + 1
139
+ const mapY = mapBounds.y + 1
140
+
141
+ // Work out how to center the map, accounting for the new scaling we've worked out
142
+ const newWidth = mapBounds.width * viewportScale
143
+ const newHeight = mapBounds.height * viewportScale
144
+ const boundsCenterX = bounds.left + bounds.width / 2
145
+ const boundsCenterY = bounds.top + bounds.height / 2
146
+ const newCenterX =
147
+ mapX + (viewportScale - 1) * mapBounds.x + viewport.x * newWidth
148
+ const newCenterY =
149
+ mapY + (viewportScale - 1) * mapBounds.y + viewport.y * newHeight
150
+ const newOffsetX = boundsCenterX - newCenterX
151
+ const newOffsetY = boundsCenterY - newCenterY
152
+
153
+ const matrixComponents = [
154
+ viewportScale,
155
+ 0,
156
+ 0,
157
+ viewportScale,
158
+ newOffsetX,
159
+ newOffsetY,
160
+ ]
161
+ const matrixComponentsRounded = matrixComponents.map((c) =>
162
+ _.round(c, 4)
163
+ )
164
+
165
+ const matrixStr = `matrix(${matrixComponentsRounded.join(",")})`
166
+ return matrixStr
167
+ }
168
+
169
+ @computed private get features(): MapRenderFeature[] {
170
+ return getGeoFeaturesForMap(this.mapConfig.region)
171
+ }
172
+
173
+ @computed private get featuresInRegion(): MapRenderFeature[] {
174
+ const { features, mapConfig } = this
175
+
176
+ if (mapConfig.region === MapRegionName.World) return features
177
+
178
+ // For Canada, all features from getGeoFeaturesForMap are provinces to display
179
+ if (mapConfig.region === MapRegionName.Canada) return features
180
+
181
+ const countriesByProjection = getCountriesByRegion(
182
+ MAP_REGION_LABELS[mapConfig.region]
183
+ )
184
+ if (countriesByProjection === undefined) return []
185
+
186
+ return features.filter((feature) =>
187
+ countriesByProjection.has(feature.id)
188
+ )
189
+ }
190
+
191
+ @computed private get featuresById(): Map<string, MapRenderFeature> {
192
+ return new Map(this.features.map((feature) => [feature.id, feature]))
193
+ }
194
+
195
+ @computed private get foregroundFeatures(): MapRenderFeature[] {
196
+ return getForegroundFeatures(this.featuresInRegion, this.selectionArray)
197
+ }
198
+
199
+ @computed
200
+ private get backgroundFeatures(): MapRenderFeature[] {
201
+ return _.difference(this.features, this.foregroundFeatures)
202
+ }
203
+
204
+ @computed private get backgroundFeatureIdSet(): Set<EntityName> {
205
+ return new Set(this.backgroundFeatures.map((feature) => feature.id))
206
+ }
207
+
208
+ @computed private get featuresWithData(): MapRenderFeature[] {
209
+ return this.foregroundFeatures.filter((feature) =>
210
+ this.choroplethData.has(feature.id)
211
+ )
212
+ }
213
+
214
+ @computed private get sortedFeaturesWithData(): MapRenderFeature[] {
215
+ // sort features so that hovered or selected features are rendered last
216
+ // and smaller countries are rendered on top of bigger ones
217
+ return sortFeaturesByInteractionStateAndSize(this.featuresWithData, {
218
+ isHovered: (featureId: string) =>
219
+ this.manager.getHoverState?.(featureId)?.active ?? false,
220
+ isSelected: (featureId) =>
221
+ this.manager.isSelected?.(featureId) ?? false,
222
+ })
223
+ }
224
+
225
+ @computed private get featuresWithNoData(): MapRenderFeature[] {
226
+ return _.difference(this.foregroundFeatures, this.featuresWithData)
227
+ }
228
+
229
+ @computed private get binColors(): string[] {
230
+ return this.manager.binColors ?? []
231
+ }
232
+
233
+ @computed private get quadtree(): Quadtree<MapRenderFeature> {
234
+ return quadtree<MapRenderFeature>()
235
+ .x((feature) => feature.projBounds.centerX)
236
+ .y((feature) => feature.projBounds.centerY)
237
+ .addAll(this.foregroundFeatures)
238
+ }
239
+
240
+ @computed private get projection(): any {
241
+ return MAP_PROJECTIONS[this.mapConfig.region]
242
+ }
243
+
244
+ @computed private get shouldShowAnnotations(): boolean {
245
+ return !!(
246
+ this.manager.mapColumn.hasNumberFormatting &&
247
+ !this.mapConfig.tooltipUseCustomLabels
248
+ )
249
+ }
250
+
251
+ private formatAnnotationLabel(value: string | number): string {
252
+ return this.manager.mapColumn.formatValueShortWithAbbreviations(value)
253
+ }
254
+
255
+ @computed private get annotationCandidateFeatures(): MapRenderFeature[] {
256
+ if (!this.shouldShowAnnotations) return []
257
+
258
+ return excludeUndefined(
259
+ this.mapConfig.selection.selectedCountryNamesInForeground.map(
260
+ (name) => this.featuresById.get(name)
261
+ )
262
+ ).filter((feature) => this.foregroundFeatures.includes(feature))
263
+ }
264
+
265
+ /* Naively placed annotations that might be overlapping */
266
+ @computed
267
+ private get annotationCandidates(): Annotation[] {
268
+ return excludeUndefined(
269
+ this.annotationCandidateFeatures.map((feature) => {
270
+ const series = this.choroplethData.get(feature.id)
271
+ if (!series) return
272
+
273
+ const labelColor = isDarkColor(series.color)
274
+ ? ANNOTATION_COLOR_LIGHT
275
+ : ANNOTATION_COLOR_DARK
276
+
277
+ const args = {
278
+ feature,
279
+ projection: this.projection,
280
+ formattedValue: this.formatAnnotationLabel(series.value),
281
+ fontSizeScale: this.viewportScale,
282
+ color: labelColor,
283
+ }
284
+
285
+ // try to fit the annotation inside the feature
286
+ const internalAnnotation =
287
+ makeInternalAnnotationForFeature(args)
288
+ if (internalAnnotation) return internalAnnotation
289
+
290
+ // place the annotation outside of the feature
291
+ const externalAnnotation =
292
+ makeExternalAnnotationForFeature(args)
293
+ if (externalAnnotation) return externalAnnotation
294
+
295
+ return undefined
296
+ })
297
+ )
298
+ }
299
+
300
+ @computed
301
+ private get internalAnnotations(): InternalAnnotation[] {
302
+ return this.annotationCandidates.filter(
303
+ (annotation) => annotation.type === "internal"
304
+ )
305
+ }
306
+
307
+ @computed
308
+ private get externalAnnotations(): ExternalAnnotation[] {
309
+ const { projection, backgroundFeatureIdSet } = this
310
+ const annotations = this.annotationCandidates.filter(
311
+ (annotation) => annotation.type === "external"
312
+ )
313
+ return repositionAndFilterExternalAnnotations({
314
+ annotations,
315
+ projection,
316
+ backgroundFeatureIdSet,
317
+ })
318
+ }
319
+
320
+ // Map uses a hybrid approach to mouseover
321
+ // If mouse is inside an element, that is prioritized
322
+ // Otherwise we do a quadtree search for the closest center point of a feature bounds,
323
+ // so that we can hover very small countries without trouble
324
+ @action.bound private detectNearbyFeature(
325
+ event: MouseEvent | TouchEvent
326
+ ): MapRenderFeature | undefined {
327
+ if (this.hoverEnterFeature || !this.base.current) return
328
+
329
+ const nearbyFeature = detectNearbyFeature({
330
+ quadtree: this.quadtree,
331
+ element: this.base.current,
332
+ event,
333
+ })
334
+
335
+ if (!nearbyFeature) {
336
+ this.hoverNearbyFeature = undefined
337
+ this.manager.onMapMouseLeave?.()
338
+ return
339
+ }
340
+
341
+ if (nearbyFeature.id !== this.hoverNearbyFeature?.id) {
342
+ this.hoverNearbyFeature = nearbyFeature
343
+ this.manager.onMapMouseOver?.(nearbyFeature.geo)
344
+ }
345
+
346
+ return nearbyFeature
347
+ }
348
+
349
+ @action.bound private onMouseMove(event: MouseEvent): void {
350
+ this.detectNearbyFeature(event)
351
+ }
352
+
353
+ @action.bound private onMouseEnter(feature: MapRenderFeature): void {
354
+ this.setHoverEnterFeature(feature)
355
+ }
356
+
357
+ @action.bound private onMouseLeave(): void {
358
+ // Fixes an issue where clicking on a country that overlaps with the
359
+ // tooltip causes the tooltip to disappear shortly after being rendered
360
+ if (this.isTouchDevice) return
361
+
362
+ this.clearHoverEnterFeature()
363
+ }
364
+
365
+ @action.bound private setHoverEnterFeature(
366
+ feature: MapRenderFeature
367
+ ): void {
368
+ if (this.hoverEnterFeature?.id === feature.id) return
369
+
370
+ this.hoverEnterFeature = feature
371
+ this.manager.onMapMouseOver?.(feature.geo)
372
+ }
373
+
374
+ @action.bound private clearHoverEnterFeature(): void {
375
+ this.hoverEnterFeature = undefined
376
+ this.manager.onMapMouseLeave?.()
377
+ }
378
+
379
+ @action.bound private onTouchStart(feature: MapRenderFeature): void {
380
+ this.setHoverEnterFeature(feature)
381
+ }
382
+
383
+ @action.bound private onClick(feature: MapRenderFeature): void {
384
+ const {
385
+ isMapSelectionEnabled,
386
+ mapConfig: { selection },
387
+ globeController,
388
+ } = this.manager
389
+
390
+ this.setHoverEnterFeature(feature)
391
+
392
+ const is2dContinentActive = this.mapConfig.is2dContinentActive()
393
+
394
+ if (isMapSelectionEnabled) {
395
+ // Select/deselect the country if allowed
396
+ selection.toggleSelection(feature.id)
397
+ } else if (
398
+ // Don't rotate if the maps shows a continent in 2d mode
399
+ !is2dContinentActive &&
400
+ // Don't rotate if the map is faceted
401
+ !this.manager.isFaceted &&
402
+ // Don't rotate if the user is zoomed in, otherwise they can get stuck in 3D mode
403
+ window?.visualViewport?.scale === 1
404
+ ) {
405
+ // Rotate to the selected country on the globe
406
+ globeController?.rotateToCountry(feature.id)
407
+ }
408
+ }
409
+
410
+ @action.bound private onDocumentClick(): void {
411
+ this.manager.globeController?.dismissCountryFocus()
412
+ if (this.hoverEnterFeature || this.hoverNearbyFeature) {
413
+ this.hoverEnterFeature = undefined
414
+ this.hoverNearbyFeature = undefined
415
+ this.manager.onMapMouseLeave?.()
416
+ }
417
+ }
418
+
419
+ private renderInternalAnnotations(): React.ReactElement | undefined {
420
+ if (this.internalAnnotations.length === 0) return
421
+
422
+ return (
423
+ <g id={makeIdForHumanConsumption("annotations-internal")}>
424
+ {this.internalAnnotations.map((annotation) => (
425
+ <InternalValueAnnotation
426
+ key={annotation.id}
427
+ annotation={annotation}
428
+ strokeScale={this.viewportScaleSqrt}
429
+ showOutline={
430
+ this.choroplethData.get(annotation.id)?.isProjection
431
+ }
432
+ />
433
+ ))}
434
+ </g>
435
+ )
436
+ }
437
+
438
+ private renderExternalAnnotations(): React.ReactElement | undefined {
439
+ if (this.externalAnnotations.length === 0) return
440
+
441
+ return (
442
+ <g
443
+ id={makeIdForHumanConsumption("annotations-external")}
444
+ className="ExternalAnnotations"
445
+ >
446
+ {this.externalAnnotations.map((annotation) => (
447
+ <ExternalValueAnnotation
448
+ key={annotation.id}
449
+ annotation={annotation}
450
+ strokeScale={this.viewportScaleSqrt}
451
+ onMouseEnter={action((feature: RenderFeature) =>
452
+ this.setHoverEnterFeature(
453
+ feature as MapRenderFeature
454
+ )
455
+ )}
456
+ onMouseLeave={action(() =>
457
+ this.clearHoverEnterFeature()
458
+ )}
459
+ />
460
+ ))}
461
+ </g>
462
+ )
463
+ }
464
+
465
+ renderFeaturesInBackground(): React.ReactElement | undefined {
466
+ if (this.backgroundFeatures.length === 0) return
467
+
468
+ return (
469
+ <g id={makeIdForHumanConsumption("countries-background")}>
470
+ {this.backgroundFeatures.map((feature) => (
471
+ <BackgroundCountry key={feature.id} feature={feature} />
472
+ ))}
473
+ </g>
474
+ )
475
+ }
476
+
477
+ renderFeaturesWithoutData(): React.ReactElement | undefined {
478
+ if (this.featuresWithNoData.length === 0) return
479
+ const patternId = Patterns.noDataPatternForMap
480
+
481
+ return (
482
+ <g
483
+ id={makeIdForHumanConsumption("countries-without-data")}
484
+ className="noDataFeatures"
485
+ >
486
+ <defs>
487
+ <NoDataPattern
488
+ patternId={patternId}
489
+ scale={1 / this.viewportScale} // The scale is crucial and projection specific
490
+ />
491
+ </defs>
492
+
493
+ {this.featuresWithNoData.map((feature) => (
494
+ <CountryWithNoData
495
+ key={feature.id}
496
+ feature={feature}
497
+ patternId={patternId}
498
+ isSelected={this.manager.isSelected?.(feature.id)}
499
+ hover={this.manager.getHoverState?.(feature.id)}
500
+ strokeScale={this.viewportScaleSqrt}
501
+ onClick={(event) => {
502
+ // don't invoke a second click on parent that
503
+ // catches clicks on 'nearby' features
504
+ event.stopPropagation()
505
+
506
+ this.onClick(feature)
507
+ }}
508
+ onTouchStart={() => this.onTouchStart(feature)}
509
+ onMouseEnter={this.onMouseEnter}
510
+ onMouseLeave={this.onMouseLeave}
511
+ />
512
+ ))}
513
+ </g>
514
+ )
515
+ }
516
+
517
+ renderFeaturesWithData(): React.ReactElement | undefined {
518
+ if (this.sortedFeaturesWithData.length === 0) return
519
+
520
+ return (
521
+ <g id={makeIdForHumanConsumption("countries-with-data")}>
522
+ {this.manager.hasProjectedData && (
523
+ <defs>
524
+ {/* Pattern used by the map legend for the projected data bin */}
525
+ <ProjectedDataPattern
526
+ key={PROJECTED_DATA_LEGEND_COLOR}
527
+ color={PROJECTED_DATA_LEGEND_COLOR}
528
+ forLegend
529
+ />
530
+ {/* Patterns used by the map legend. The map legend can't re-use
531
+ the features' patterns defined below because those are scaled
532
+ by the viewport. */}
533
+ {this.binColors.map((color, index) => (
534
+ <ProjectedDataPattern
535
+ key={`${color}-${index}`}
536
+ color={color}
537
+ forLegend
538
+ />
539
+ ))}
540
+
541
+ {/* Pattern used by features */}
542
+ {this.binColors.map((color, index) => (
543
+ <ProjectedDataPattern
544
+ key={`${color}-${index}`}
545
+ color={color}
546
+ scale={1 / this.viewportScale}
547
+ />
548
+ ))}
549
+ </defs>
550
+ )}
551
+
552
+ {this.sortedFeaturesWithData.map((feature) => {
553
+ const series = this.choroplethData.get(feature.id)
554
+ if (!series) return null
555
+ return (
556
+ <CountryWithData
557
+ key={feature.id}
558
+ feature={feature}
559
+ series={series}
560
+ isSelected={this.manager.isSelected?.(feature.id)}
561
+ hover={this.manager.getHoverState?.(feature.id)}
562
+ strokeScale={this.viewportScaleSqrt}
563
+ onClick={(event) => {
564
+ // don't invoke a second click on parent that
565
+ // catches clicks on 'nearby' features
566
+ event.stopPropagation()
567
+
568
+ this.onClick(feature)
569
+ }}
570
+ onTouchStart={() => this.onTouchStart(feature)}
571
+ onMouseEnter={this.onMouseEnter}
572
+ onMouseLeave={this.onMouseLeave}
573
+ />
574
+ )
575
+ })}
576
+ </g>
577
+ )
578
+ }
579
+
580
+ renderStatic(): React.ReactElement {
581
+ return (
582
+ <g
583
+ id={makeIdForHumanConsumption("map")}
584
+ transform={this.matrixTransform}
585
+ >
586
+ {this.renderFeaturesInBackground()}
587
+ {this.renderFeaturesWithoutData()}
588
+ {this.renderFeaturesWithData()}
589
+ {this.renderInternalAnnotations()}
590
+ {this.renderExternalAnnotations()}
591
+ </g>
592
+ )
593
+ }
594
+
595
+ override componentDidMount(): void {
596
+ document.addEventListener("touchstart", this.onDocumentClick, {
597
+ capture: true,
598
+ passive: true,
599
+ })
600
+ }
601
+
602
+ override componentWillUnmount(): void {
603
+ document.removeEventListener("touchstart", this.onDocumentClick, {
604
+ capture: true,
605
+ })
606
+ }
607
+
608
+ renderInteractive(): React.ReactElement {
609
+ const { bounds, matrixTransform } = this
610
+
611
+ // this needs to be referenced here or it will be recomputed on every mousemove
612
+ const _cachedCentroids = this.quadtree
613
+
614
+ // SVG layering is based on order of appearance in the element tree (later elements rendered on top)
615
+ // The ordering here is quite careful
616
+ return (
617
+ <g
618
+ ref={this.base}
619
+ className={CHOROPLETH_MAP_CLASSNAME}
620
+ onMouseDown={
621
+ (ev: SVGMouseEvent): void =>
622
+ ev.preventDefault() /* Without this, title may get selected while shift clicking */
623
+ }
624
+ onMouseMove={(ev: SVGMouseEvent): void =>
625
+ this.onMouseMove(ev.nativeEvent)
626
+ }
627
+ onMouseLeave={this.onMouseLeave}
628
+ onClick={() => {
629
+ // invoke a click on a feature when clicking nearby one
630
+ if (this.hoverNearbyFeature)
631
+ this.onClick(this.hoverNearbyFeature)
632
+ }}
633
+ style={{ cursor: this.hoverFeature ? "pointer" : undefined }}
634
+ >
635
+ <rect
636
+ x={bounds.x}
637
+ y={bounds.y}
638
+ width={bounds.width}
639
+ height={bounds.height}
640
+ fill="rgba(255,255,255,0)"
641
+ opacity={0}
642
+ />
643
+ <g
644
+ className={GEO_FEATURES_CLASSNAME}
645
+ transform={matrixTransform}
646
+ >
647
+ {this.renderFeaturesInBackground()}
648
+ {this.renderFeaturesWithoutData()}
649
+ {this.renderFeaturesWithData()}
650
+ {this.renderInternalAnnotations()}
651
+ {this.renderExternalAnnotations()}
652
+ </g>
653
+ </g>
654
+ )
655
+ }
656
+
657
+ override render(): React.ReactElement {
658
+ return this.manager.isStatic
659
+ ? this.renderStatic()
660
+ : this.renderInteractive()
661
+ }
662
+ }