@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,760 @@
1
+ import * as _ from "lodash-es"
2
+ import React from "react"
3
+
4
+ import {
5
+ observable,
6
+ computed,
7
+ action,
8
+ autorun,
9
+ reaction,
10
+ makeObservable,
11
+ } from "mobx"
12
+ import {
13
+ bind,
14
+ next,
15
+ sampleFrom,
16
+ exposeInstanceOnWindow,
17
+ QueryParams,
18
+ MultipleOwidVariableDataDimensionsMap,
19
+ Bounds,
20
+ strToQueryParams,
21
+ queryParamsToStr,
22
+ setWindowQueryStr,
23
+ } from "../../utils/index.js"
24
+ import { BodyPortal } from "../../components/index.js"
25
+ import {
26
+ ScaleType,
27
+ AnnotationFieldsInTitle,
28
+ GrapherInterface,
29
+ LegacyGrapherInterface,
30
+ DetailDictionary,
31
+ GrapherTooltipAnchor,
32
+ NarrativeChartInfo,
33
+ ArchiveContext,
34
+ AdditionalGrapherDataFetchFn,
35
+ GrapherVariant,
36
+ Time,
37
+ } from "../../types/index.js"
38
+ import { OwidTable } from "../../core-table/index.js"
39
+ import {
40
+ GRAPHER_LOADED_EVENT_NAME,
41
+ GrapherModal,
42
+ } from "../core/GrapherConstants"
43
+
44
+ import { FullScreen } from "../fullScreen/FullScreen"
45
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
46
+ import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"
47
+ import { TooltipContainer } from "../tooltip/Tooltip"
48
+ import { EntitySelectorModal } from "../modal/EntitySelectorModal"
49
+ import { DownloadModal } from "../modal/DownloadModal"
50
+ import { observer } from "mobx-react"
51
+ import "d3-transition"
52
+ import { SourcesModal } from "../modal/SourcesModal"
53
+ import { Command, CommandPalette } from "../controls/CommandPalette"
54
+ import { EmbedModal } from "../modal/EmbedModal"
55
+ import Mousetrap from "mousetrap"
56
+ import { SelectionArray } from "../selection/SelectionArray"
57
+ import { legacyToOwidTableAndDimensionsWithMandatorySlug } from "./LegacyToOwidTable"
58
+ import classnames from "classnames"
59
+ import { SidePanel } from "../sidePanel/SidePanel"
60
+ import { EntitySelector } from "../entitySelector/EntitySelector"
61
+ import { SlideInDrawer } from "../slideInDrawer/SlideInDrawer"
62
+ import { FocusArray } from "../focus/FocusArray"
63
+ import { Chart } from "../chart/Chart.js"
64
+ import { flushSync } from "react-dom"
65
+ import { GrapherState } from "./GrapherState.js"
66
+
67
+ declare global {
68
+ interface Window {
69
+ details?: DetailDictionary
70
+ admin?: any // TODO: use stricter type
71
+ }
72
+ }
73
+
74
+ export const DEFAULT_MS_PER_TICK = 100
75
+
76
+ // Exactly the same as GrapherInterface, but contains options that developers want but authors won't be touching.
77
+ export interface GrapherProgrammaticInterface extends GrapherInterface {
78
+ queryStr?: string
79
+ bounds?: Bounds
80
+ table?: OwidTable
81
+ baseUrl?: string
82
+ bakedGrapherURL?: string
83
+ adminBaseUrl?: string
84
+ env?: string
85
+ highlightedTimesInLineChart?: Time[]
86
+ baseFontSize?: number
87
+ staticBounds?: Bounds
88
+ variant?: GrapherVariant
89
+ isDisplayedAlongsideComplementaryTable?: boolean
90
+
91
+ hideTitle?: boolean
92
+ hideSubtitle?: boolean
93
+ hideNote?: boolean
94
+ hideOriginUrl?: boolean
95
+
96
+ hideEntityControls?: boolean
97
+ forceHideAnnotationFieldsInTitle?: AnnotationFieldsInTitle
98
+ hasTableTab?: boolean
99
+ hideShareButton?: boolean
100
+ hideExploreTheDataButton?: boolean
101
+ hideRelatedQuestion?: boolean
102
+ isSocialMediaExport?: boolean
103
+ enableMapSelection?: boolean
104
+
105
+ enableKeyboardShortcuts?: boolean
106
+ bindUrlToWindow?: boolean
107
+ isEmbeddedInAnOwidPage?: boolean
108
+ isEmbeddedInADataPage?: boolean
109
+ isConfigReady?: boolean
110
+ isDataReady?: boolean
111
+ canHideExternalControlsInEmbed?: boolean
112
+
113
+ narrativeChartInfo?: MinimalNarrativeChartInfo
114
+ archiveContext?: ArchiveContext
115
+
116
+ manager?: GrapherManager
117
+ additionalDataLoaderFn?: AdditionalGrapherDataFetchFn
118
+ }
119
+
120
+ export type MinimalNarrativeChartInfo = Pick<
121
+ NarrativeChartInfo,
122
+ "name" | "parentChartSlug" | "queryParamsForParentChart"
123
+ >
124
+
125
+ interface AnalyticsContext {
126
+ mdimSlug?: string
127
+ mdimViewConfigId?: string
128
+ }
129
+
130
+ export interface GrapherManager {
131
+ baseUrl?: string
132
+ canonicalUrl?: string
133
+ selection?: SelectionArray
134
+ focusArray?: FocusArray
135
+ adminEditPath?: string
136
+ adminCreateNarrativeChartPath?: string
137
+ analyticsContext?: AnalyticsContext
138
+ }
139
+
140
+ export interface GrapherProps {
141
+ grapherState: GrapherState
142
+ }
143
+
144
+ @observer
145
+ export class Grapher extends React.Component<GrapherProps> {
146
+ @computed get grapherState(): GrapherState {
147
+ return this.props.grapherState
148
+ }
149
+
150
+ // #region Observable props not in any interface
151
+
152
+ // stored on Grapher so state is preserved when switching to full-screen mode
153
+
154
+ private legacyVariableDataJson:
155
+ | MultipleOwidVariableDataDimensionsMap
156
+ | undefined = undefined
157
+ private hasLoggedGAViewEvent = false
158
+ private hasBeenVisible = false
159
+ private uncaughtError: Error | undefined = undefined
160
+
161
+ constructor(props: { grapherState: GrapherState }) {
162
+ super(props)
163
+
164
+ makeObservable<
165
+ Grapher,
166
+ "legacyVariableDataJson" | "hasBeenVisible" | "uncaughtError"
167
+ >(this, {
168
+ legacyVariableDataJson: observable,
169
+ hasBeenVisible: observable,
170
+ uncaughtError: observable,
171
+ })
172
+ }
173
+
174
+ // Convenience method for debugging
175
+ windowQueryParams(str = location.search): QueryParams {
176
+ return strToQueryParams(str)
177
+ }
178
+
179
+ @action.bound private _setInputTable(
180
+ json: MultipleOwidVariableDataDimensionsMap,
181
+ legacyConfig: Partial<LegacyGrapherInterface>
182
+ ): void {
183
+ // TODO grapher model: switch this to downloading multiple data and metadata files
184
+
185
+ const startMark = performance.now()
186
+ const tableWithColors = legacyToOwidTableAndDimensionsWithMandatorySlug(
187
+ json,
188
+ legacyConfig.dimensions ?? [],
189
+ legacyConfig.selectedEntityColors
190
+ )
191
+ this.grapherState.createPerformanceMeasurement(
192
+ "legacyToOwidTableAndDimensions",
193
+ startMark
194
+ )
195
+
196
+ this.grapherState.inputTable = tableWithColors
197
+ }
198
+
199
+ @action rebuildInputOwidTable(): void {
200
+ // TODO grapher model: switch this to downloading multiple data and metadata files
201
+ if (!this.legacyVariableDataJson) return
202
+ this._setInputTable(
203
+ this.legacyVariableDataJson,
204
+ this.grapherState.legacyConfigAsAuthored
205
+ )
206
+ }
207
+
208
+ // Keeps a running cache of series colors at the Grapher level.
209
+
210
+ @bind dispose(): void {
211
+ this.grapherState.disposers.forEach((dispose) => dispose())
212
+ }
213
+
214
+ @action.bound setError(err: Error): void {
215
+ this.uncaughtError = err
216
+ }
217
+
218
+ @action.bound clearErrors(): void {
219
+ this.uncaughtError = undefined
220
+ }
221
+
222
+ private get commandPalette(): React.ReactElement | null {
223
+ return this.props.grapherState.enableKeyboardShortcuts ? (
224
+ <CommandPalette commands={this.keyboardShortcuts} display="none" />
225
+ ) : null
226
+ }
227
+
228
+ @action.bound private toggleTabCommand(): void {
229
+ this.grapherState.setTab(
230
+ next(this.grapherState.availableTabs, this.grapherState.activeTab)
231
+ )
232
+ }
233
+
234
+ @action.bound private togglePlayingCommand(): void {
235
+ void this.grapherState.timelineController.togglePlay()
236
+ }
237
+
238
+ private get keyboardShortcuts(): Command[] {
239
+ const temporaryFacetTestCommands = _.range(0, 10).map((num) => {
240
+ return {
241
+ combo: `${num}`,
242
+ fn: (): void => this.randomSelection(num),
243
+ }
244
+ })
245
+ const shortcuts = [
246
+ ...temporaryFacetTestCommands,
247
+ {
248
+ combo: "t",
249
+ fn: (): void => this.toggleTabCommand(),
250
+ title: "Toggle tab",
251
+ category: "Navigation",
252
+ },
253
+ {
254
+ combo: "?",
255
+ fn: (): void => CommandPalette.togglePalette(),
256
+ title: `Toggle Help`,
257
+ category: "Navigation",
258
+ },
259
+ {
260
+ combo: "a",
261
+ fn: (): void => {
262
+ if (this.grapherState.selection.hasSelection) {
263
+ this.grapherState.selection.clearSelection()
264
+ this.grapherState.focusArray.clear()
265
+ } else {
266
+ this.grapherState.selection.setSelectedEntities(
267
+ this.grapherState.availableEntityNames
268
+ )
269
+ }
270
+ },
271
+ title: this.grapherState.selection.hasSelection
272
+ ? `Select None`
273
+ : `Select All`,
274
+ category: "Selection",
275
+ },
276
+ {
277
+ combo: "f",
278
+ fn: (): void => {
279
+ this.grapherState.hideFacetControl =
280
+ !this.grapherState.hideFacetControl
281
+ },
282
+ title: `Toggle Faceting`,
283
+ category: "Chart",
284
+ },
285
+ {
286
+ combo: "p",
287
+ fn: (): void => this.togglePlayingCommand(),
288
+ title: this.grapherState.isPlaying ? `Pause` : `Play`,
289
+ category: "Timeline",
290
+ },
291
+ {
292
+ combo: "l",
293
+ fn: (): void => this.toggleYScaleTypeCommand(),
294
+ title: "Toggle Y log/linear",
295
+ category: "Chart",
296
+ },
297
+ {
298
+ combo: "w",
299
+ fn: (): void => this.toggleFullScreenMode(),
300
+ title: `Toggle full-screen mode`,
301
+ category: "Chart",
302
+ },
303
+ {
304
+ combo: "s",
305
+ fn: (): void => {
306
+ const isSourcesModalOpen =
307
+ this.grapherState.activeModal === GrapherModal.Sources
308
+ this.grapherState.activeModal = isSourcesModalOpen
309
+ ? undefined
310
+ : GrapherModal.Sources
311
+ },
312
+ title: `Toggle sources modal`,
313
+ category: "Chart",
314
+ },
315
+ {
316
+ combo: "d",
317
+ fn: (): void => {
318
+ const isDownloadModalOpen =
319
+ this.grapherState.activeModal === GrapherModal.Download
320
+ this.grapherState.activeModal = isDownloadModalOpen
321
+ ? undefined
322
+ : GrapherModal.Download
323
+ },
324
+ title: "Toggle download modal",
325
+ category: "Chart",
326
+ },
327
+ { combo: "esc", fn: (): void => this.clearErrors() },
328
+ {
329
+ combo: "z",
330
+ fn: (): void => this.toggleTimelineCommand(),
331
+ title: "Latest/Earliest/All period",
332
+ category: "Timeline",
333
+ },
334
+ {
335
+ combo: "shift+o",
336
+ fn: (): void => this.grapherState.clearQueryParams(),
337
+ title: "Reset to original",
338
+ category: "Navigation",
339
+ },
340
+ {
341
+ combo: "g",
342
+ fn: (): void => this.grapherState.globeController.toggleGlobe(),
343
+ title: "Toggle globe view",
344
+ category: "Map",
345
+ },
346
+ ]
347
+
348
+ if (this.grapherState.slideShow) {
349
+ const slideShow = this.grapherState.slideShow
350
+ shortcuts.push({
351
+ combo: "right",
352
+ fn: () => slideShow.playNext(),
353
+ title: "Next chart",
354
+ category: "Browse",
355
+ })
356
+ shortcuts.push({
357
+ combo: "left",
358
+ fn: () => slideShow.playPrevious(),
359
+ title: "Previous chart",
360
+ category: "Browse",
361
+ })
362
+ }
363
+
364
+ return shortcuts
365
+ }
366
+
367
+ @action.bound private toggleTimelineCommand(): void {
368
+ // Todo: add tests for this
369
+ this.grapherState.setTimeFromTimeQueryParam(
370
+ next(["latest", "earliest", ".."], this.grapherState.timeParam!)
371
+ )
372
+ }
373
+
374
+ @action.bound private toggleYScaleTypeCommand(): void {
375
+ this.grapherState.yAxis.scaleType = next(
376
+ [ScaleType.linear, ScaleType.log],
377
+ this.grapherState.yAxis.scaleType
378
+ )
379
+ }
380
+
381
+ @action.bound randomSelection(num: number): void {
382
+ // Continent, Population, GDP PC, GDP, PopDens, UN, Language, etc.
383
+ this.clearErrors()
384
+ const currentSelection =
385
+ this.grapherState.selection.selectedEntityNames.length
386
+ const newNum = num ? num : currentSelection ? currentSelection * 2 : 10
387
+ this.grapherState.selection.setSelectedEntities(
388
+ sampleFrom(
389
+ this.grapherState.availableEntityNames,
390
+ newNum,
391
+ Date.now()
392
+ )
393
+ )
394
+ }
395
+ @action.bound toggleFullScreenMode(): void {
396
+ this.grapherState.isInFullScreenMode =
397
+ !this.grapherState.isInFullScreenMode
398
+ }
399
+
400
+ @action.bound dismissFullScreen(): void {
401
+ // if a modal is open, dismiss it instead of exiting full-screen mode
402
+ if (
403
+ this.grapherState.isModalOpen ||
404
+ this.grapherState.isShareMenuActive
405
+ ) {
406
+ this.grapherState.isEntitySelectorModalOrDrawerOpen = false
407
+ this.grapherState.activeModal = undefined
408
+ this.grapherState.isShareMenuActive = false
409
+ } else {
410
+ this.grapherState.isInFullScreenMode = false
411
+ }
412
+ }
413
+
414
+ private renderError(): React.ReactElement {
415
+ return (
416
+ <div
417
+ title={this.uncaughtError?.message}
418
+ style={{
419
+ width: "100%",
420
+ height: "100%",
421
+ position: "relative",
422
+ display: "flex",
423
+ flexDirection: "column",
424
+ justifyContent: "center",
425
+ textAlign: "center",
426
+ lineHeight: 1.5,
427
+ padding: "48px",
428
+ }}
429
+ >
430
+ <p style={{ color: "#cc0000", fontWeight: 700 }}>
431
+ <FontAwesomeIcon icon={faExclamationTriangle} />
432
+ There was a problem loading this chart
433
+ </p>
434
+ <p>
435
+ We have been notified of this error, please check back later
436
+ whether it's been fixed. If the error persists, get in touch
437
+ with us at{" "}
438
+ <a
439
+ href={`mailto:info@ourworldindata.org?subject=Broken chart on page ${window.location.href}`}
440
+ >
441
+ info@ourworldindata.org
442
+ </a>
443
+ .
444
+ </p>
445
+ {this.uncaughtError && this.uncaughtError.message && (
446
+ <pre style={{ fontSize: "11px" }}>
447
+ Error: {this.uncaughtError.message}
448
+ </pre>
449
+ )}
450
+ </div>
451
+ )
452
+ }
453
+
454
+ private renderGrapherComponent(): React.ReactElement {
455
+ const containerClasses = classnames({
456
+ GrapherComponent: true,
457
+ GrapherPortraitClass: this.grapherState.isPortrait,
458
+ isStatic: this.grapherState.isStatic,
459
+ isExportingToSvgOrPng: this.grapherState.isExportingToSvgOrPng,
460
+ GrapherComponentNarrow: this.grapherState.isNarrow,
461
+ GrapherComponentSemiNarrow: this.grapherState.isSemiNarrow,
462
+ GrapherComponentSmall: this.grapherState.isSmall,
463
+ GrapherComponentMedium: this.grapherState.isMedium,
464
+ })
465
+
466
+ const containerStyle = {
467
+ width: this.grapherState.activeBounds.width,
468
+ height: this.grapherState.activeBounds.height,
469
+ fontSize: this.grapherState.isExportingToSvgOrPng
470
+ ? 18
471
+ : Math.min(16, this.grapherState.fontSize), // cap font size at 16px
472
+ }
473
+
474
+ return (
475
+ <div
476
+ ref={this.grapherState.base}
477
+ className={containerClasses}
478
+ style={containerStyle}
479
+ data-grapher-url={JSON.stringify({
480
+ grapherUrl: this.grapherState.canonicalUrl,
481
+ narrativeChartName:
482
+ this.grapherState.narrativeChartInfo?.name,
483
+ })}
484
+ >
485
+ {this.commandPalette}
486
+ {this.uncaughtError ? this.renderError() : this.renderReady()}
487
+ </div>
488
+ )
489
+ }
490
+
491
+ override render(): React.ReactElement | undefined {
492
+ // Used in the admin to render a static preview of the chart
493
+ if (this.grapherState.isExportingToSvgOrPng)
494
+ return <Chart manager={this.grapherState} />
495
+
496
+ if (this.grapherState.isInFullScreenMode) {
497
+ return (
498
+ <FullScreen
499
+ onDismiss={this.dismissFullScreen}
500
+ overlayColor={
501
+ this.grapherState.isModalOpen ? "#999999" : "#fff"
502
+ }
503
+ >
504
+ {this.renderGrapherComponent()}
505
+ </FullScreen>
506
+ )
507
+ }
508
+
509
+ return this.renderGrapherComponent()
510
+ }
511
+
512
+ private renderReady(): React.ReactElement | null {
513
+ if (!this.hasBeenVisible) return null
514
+
515
+ const entitySelectorArray = this.grapherState.isOnMapTab
516
+ ? this.grapherState.mapConfig.selection
517
+ : this.grapherState.selection
518
+
519
+ return (
520
+ <>
521
+ {/* Chart and entity selector */}
522
+ <div className="CaptionedChartAndSidePanel">
523
+ <Chart manager={this.grapherState} />
524
+
525
+ {this.grapherState.sidePanelBounds && (
526
+ <SidePanel bounds={this.grapherState.sidePanelBounds}>
527
+ <EntitySelector
528
+ manager={this.grapherState}
529
+ selection={entitySelectorArray}
530
+ />
531
+ </SidePanel>
532
+ )}
533
+ </div>
534
+
535
+ {/* Modals */}
536
+ {this.grapherState.activeModal === GrapherModal.Sources &&
537
+ this.grapherState.isReady && (
538
+ <SourcesModal manager={this.grapherState} />
539
+ )}
540
+ {this.grapherState.activeModal === GrapherModal.Download &&
541
+ this.grapherState.isReady && (
542
+ <DownloadModal manager={this.grapherState} />
543
+ )}
544
+ {this.grapherState.activeModal === GrapherModal.Embed &&
545
+ this.grapherState.isReady && (
546
+ <EmbedModal manager={this.grapherState} />
547
+ )}
548
+ {this.grapherState.isEntitySelectorModalOpen && (
549
+ <EntitySelectorModal manager={this.grapherState} />
550
+ )}
551
+
552
+ {/* Entity selector in a slide-in drawer */}
553
+ <SlideInDrawer
554
+ grapherRef={this.grapherState.base}
555
+ active={this.grapherState.isEntitySelectorDrawerOpen}
556
+ toggle={() => {
557
+ this.grapherState.isEntitySelectorModalOrDrawerOpen =
558
+ !this.grapherState.isEntitySelectorModalOrDrawerOpen
559
+ }}
560
+ >
561
+ <EntitySelector
562
+ manager={this.grapherState}
563
+ selection={entitySelectorArray}
564
+ autoFocus={true}
565
+ />
566
+ </SlideInDrawer>
567
+
568
+ {/* Tooltip: either pin to the bottom or render into the chart area */}
569
+ {this.grapherState.shouldPinTooltipToBottom ? (
570
+ <BodyPortal>
571
+ <TooltipContainer
572
+ tooltipManager={this.grapherState}
573
+ anchor={GrapherTooltipAnchor.bottom}
574
+ />
575
+ </BodyPortal>
576
+ ) : (
577
+ <TooltipContainer
578
+ tooltipManager={this.grapherState}
579
+ containerBounds={this.grapherState.captionedChartBounds}
580
+ />
581
+ )}
582
+ </>
583
+ )
584
+ }
585
+
586
+ // Chart should only render SVG when it's on the screen
587
+ @action.bound private setUpIntersectionObserver(): void {
588
+ if (typeof window !== "undefined" && "IntersectionObserver" in window) {
589
+ const observer = new IntersectionObserver(
590
+ (entries) => {
591
+ entries.forEach((entry) => {
592
+ if (entry.isIntersecting) {
593
+ // We need to render this immediately to avoid a Safari bug, where Safari
594
+ // is seemingly blocking rendering during the initial fetches, and will then
595
+ // subsequently render using the wrong bounds.
596
+ flushSync(
597
+ action(() => {
598
+ this.hasBeenVisible = true
599
+ })
600
+ )
601
+
602
+ if (!this.hasLoggedGAViewEvent) {
603
+ this.hasLoggedGAViewEvent = true
604
+
605
+ if (this.grapherState.narrativeChartInfo) {
606
+ this.grapherState.analytics.logGrapherView(
607
+ this.grapherState.narrativeChartInfo
608
+ .parentChartSlug,
609
+ {
610
+ narrativeChartName:
611
+ this.grapherState
612
+ .narrativeChartInfo.name,
613
+ }
614
+ )
615
+ this.hasLoggedGAViewEvent = true
616
+ } else if (this.grapherState.slug) {
617
+ this.grapherState.analytics.logGrapherView(
618
+ this.grapherState.slug
619
+ )
620
+ this.hasLoggedGAViewEvent = true
621
+ }
622
+ }
623
+
624
+ // dismiss tooltip when less than 2/3 of the chart is visible
625
+ const tooltip = this.grapherState.tooltip?.get()
626
+ const isNotVisible = !entry.isIntersecting
627
+ const isPartiallyVisible =
628
+ entry.isIntersecting &&
629
+ entry.intersectionRatio < 0.66
630
+ if (
631
+ tooltip &&
632
+ (isNotVisible || isPartiallyVisible)
633
+ ) {
634
+ tooltip.dismiss?.()
635
+ }
636
+ }
637
+ })
638
+ },
639
+ { threshold: [0, 0.66] }
640
+ )
641
+ observer.observe(this.grapherState.containerElement!)
642
+ this.grapherState.disposers.push(() => observer.disconnect())
643
+ } else {
644
+ // IntersectionObserver not available; we may be in a Node environment, just render
645
+ this.hasBeenVisible = true
646
+ }
647
+ }
648
+
649
+ @action.bound private setBaseFontSize(): void {
650
+ this.grapherState.baseFontSize =
651
+ this.grapherState.computeBaseFontSizeFromWidth(
652
+ this.grapherState.captionedChartBounds
653
+ )
654
+ }
655
+
656
+ // Binds chart properties to global window title and URL. This should only
657
+ // ever be invoked from top-level JavaScript.
658
+ private bindToWindow(): void {
659
+ // There is a surprisingly considerable performance overhead to updating the url
660
+ // while animating, so we debounce to allow e.g. smoother timelines
661
+ const pushParams = (): void =>
662
+ setWindowQueryStr(queryParamsToStr(this.grapherState.changedParams))
663
+ const debouncedPushParams = _.debounce(pushParams, 100)
664
+
665
+ reaction(
666
+ () => this.grapherState.changedParams,
667
+ () => (this.debounceMode ? debouncedPushParams() : pushParams())
668
+ )
669
+
670
+ autorun(() => (document.title = this.grapherState.currentTitle))
671
+ }
672
+
673
+ @action.bound private setUpWindowResizeEventHandler(): void {
674
+ const updateWindowDimensions = action((): void => {
675
+ this.grapherState.windowInnerWidth = window.innerWidth
676
+ this.grapherState.windowInnerHeight = window.innerHeight
677
+ })
678
+ const onResize = _.debounce(updateWindowDimensions, 400, {
679
+ leading: true,
680
+ })
681
+
682
+ if (typeof window !== "undefined") {
683
+ updateWindowDimensions()
684
+ window.addEventListener("resize", onResize)
685
+ this.grapherState.disposers.push(() => {
686
+ window.removeEventListener("resize", onResize)
687
+ })
688
+ }
689
+ }
690
+
691
+ override componentDidMount(): void {
692
+ this.setBaseFontSize()
693
+ this.setUpIntersectionObserver()
694
+ this.setUpWindowResizeEventHandler()
695
+ exposeInstanceOnWindow(this, "grapher")
696
+ // Emit a custom event when the grapher is ready
697
+ // We can use this in global scripts that depend on the grapher e.g. the site-screenshots tool
698
+ this.grapherState.disposers.push(
699
+ reaction(
700
+ () => this.grapherState.isReady,
701
+ () => {
702
+ if (this.grapherState.isReady) {
703
+ document.dispatchEvent(
704
+ new CustomEvent(GRAPHER_LOADED_EVENT_NAME, {
705
+ detail: { grapher: this },
706
+ })
707
+ )
708
+ }
709
+ }
710
+ ),
711
+ reaction(
712
+ () => this.grapherState.facetStrategy,
713
+ () => this.grapherState.focusArray.clear()
714
+ )
715
+ )
716
+ if (this.grapherState.bindUrlToWindow) this.bindToWindow()
717
+ if (this.grapherState.enableKeyboardShortcuts)
718
+ this.bindKeyboardShortcuts()
719
+ }
720
+
721
+ private _shortcutsBound = false
722
+ private bindKeyboardShortcuts(): void {
723
+ if (this._shortcutsBound) return
724
+ this.keyboardShortcuts.forEach((shortcut) => {
725
+ Mousetrap.bind(shortcut.combo, () => {
726
+ shortcut.fn()
727
+ this.grapherState.analytics.logKeyboardShortcut(
728
+ shortcut.title || "",
729
+ shortcut.combo
730
+ )
731
+ return false
732
+ })
733
+ })
734
+ this._shortcutsBound = true
735
+ }
736
+
737
+ private unbindKeyboardShortcuts(): void {
738
+ if (!this._shortcutsBound) return
739
+ this.keyboardShortcuts.forEach((shortcut) => {
740
+ Mousetrap.unbind(shortcut.combo)
741
+ })
742
+ this._shortcutsBound = false
743
+ }
744
+
745
+ override componentWillUnmount(): void {
746
+ this.unbindKeyboardShortcuts()
747
+ this.dispose()
748
+ }
749
+
750
+ override componentDidUpdate(): void {
751
+ this.setBaseFontSize()
752
+ }
753
+
754
+ override componentDidCatch(error: Error): void {
755
+ this.setError(error)
756
+ this.grapherState.analytics.logGrapherViewError(error)
757
+ }
758
+
759
+ debounceMode = false
760
+ }