@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,973 @@
1
+ import * as _ from "lodash-es"
2
+ import {
3
+ anyToString,
4
+ csvEscape,
5
+ formatYear,
6
+ formatDay,
7
+ sortNumeric,
8
+ dateDiffInDays,
9
+ omitUndefinedValues,
10
+ isPresent,
11
+ dayjs,
12
+ OwidSource,
13
+ formatValue,
14
+ checkIsVeryShortUnit,
15
+ TickFormattingOptions,
16
+ OwidVariableDisplayConfigInterface,
17
+ ColumnSlug,
18
+ PrimitiveType,
19
+ imemo,
20
+ ToleranceStrategy,
21
+ IndicatorTitleWithFragments,
22
+ stripOuterParentheses,
23
+ } from "../utils/index.js"
24
+ import { CoreTable } from "./CoreTable.js"
25
+ import {
26
+ Time,
27
+ JsTypes,
28
+ CoreValueType,
29
+ ColumnTypeNames,
30
+ CoreColumnDef,
31
+ EntityName,
32
+ OwidVariableRow,
33
+ ErrorValue,
34
+ OwidVariableRoundingMode,
35
+ } from "../types/index.js"
36
+ import { ErrorValueTypes, isNotErrorValue } from "./ErrorValues.js"
37
+ import {
38
+ getOriginalStartTimeColumnSlug,
39
+ getOriginalTimeColumnSlug,
40
+ getOriginalValueColumnSlug,
41
+ } from "./OwidTableUtil.js"
42
+ import * as R from "remeda"
43
+
44
+ export abstract class AbstractCoreColumn<JS_TYPE extends PrimitiveType> {
45
+ def: CoreColumnDef
46
+ table: CoreTable
47
+
48
+ constructor(table: CoreTable, def: CoreColumnDef) {
49
+ this.table = table
50
+ this.def = def
51
+ }
52
+
53
+ abstract jsType: JsTypes
54
+
55
+ parse(val: unknown): any {
56
+ return val
57
+ }
58
+
59
+ @imemo get isMissing(): boolean {
60
+ return this instanceof MissingColumn
61
+ }
62
+
63
+ @imemo get isTimeColumn(): boolean {
64
+ return this instanceof TimeColumn
65
+ }
66
+
67
+ @imemo get hasNumberFormatting(): boolean {
68
+ return this instanceof AbstractColumnWithNumberFormatting
69
+ }
70
+
71
+ // todo: migrate from unitConversionFactor to computed columns instead. then delete this.
72
+ // note: unitConversionFactor is used >400 times in charts and >800 times in variables!!!
73
+ @imemo get unitConversionFactor(): number {
74
+ return this.display?.conversionFactor ?? 1
75
+ }
76
+
77
+ @imemo get isAllIntegers(): boolean {
78
+ return false
79
+ }
80
+
81
+ @imemo get tolerance(): number {
82
+ return this.display?.tolerance ?? this.def.tolerance ?? 0
83
+ }
84
+
85
+ @imemo get toleranceStrategy(): ToleranceStrategy | undefined {
86
+ return this.def.toleranceStrategy
87
+ }
88
+
89
+ @imemo get display(): OwidVariableDisplayConfigInterface | undefined {
90
+ return this.def.display
91
+ }
92
+
93
+ abstract formatValue(
94
+ value: unknown,
95
+ options?: TickFormattingOptions
96
+ ): string
97
+
98
+ formatValueForMobile(
99
+ value: unknown,
100
+ options?: TickFormattingOptions
101
+ ): string {
102
+ return this.formatValue(value, options)
103
+ }
104
+
105
+ formatValueShortWithAbbreviations(
106
+ value: unknown,
107
+ options?: TickFormattingOptions
108
+ ): string {
109
+ return this.formatValue(value, options)
110
+ }
111
+
112
+ formatValueShort(value: unknown, options?: TickFormattingOptions): string {
113
+ return this.formatValue(value, options)
114
+ }
115
+
116
+ formatValueLong(value: unknown, options?: TickFormattingOptions): string {
117
+ return this.formatValue(value, options)
118
+ }
119
+
120
+ formatForTick(value: unknown, options?: TickFormattingOptions): string {
121
+ return this.formatValueShort(value, options)
122
+ }
123
+
124
+ // A method for formatting for CSV
125
+ formatForCsv(value: JS_TYPE): string {
126
+ return csvEscape(anyToString(value))
127
+ }
128
+
129
+ formatTime(time: number): string {
130
+ return this.originalTimeColumn.formatValue(time)
131
+ }
132
+
133
+ @imemo get roundingMode(): OwidVariableRoundingMode {
134
+ return (
135
+ this.display?.roundingMode ?? OwidVariableRoundingMode.decimalPlaces
136
+ )
137
+ }
138
+
139
+ @imemo get roundsToFixedDecimals(): boolean {
140
+ return this.roundingMode === OwidVariableRoundingMode.decimalPlaces
141
+ }
142
+
143
+ @imemo get roundsToSignificantFigures(): boolean {
144
+ return this.roundingMode === OwidVariableRoundingMode.significantFigures
145
+ }
146
+
147
+ @imemo get numDecimalPlaces(): number {
148
+ return this.display?.numDecimalPlaces ?? 2
149
+ }
150
+
151
+ @imemo get numSignificantFigures(): number {
152
+ return this.display?.numSignificantFigures ?? 3
153
+ }
154
+
155
+ @imemo get unit(): string | undefined {
156
+ const unit = this.display?.unit ?? this.def.unit
157
+ return unit?.trim()
158
+ }
159
+
160
+ @imemo get shortUnit(): string | undefined {
161
+ const shortUnit =
162
+ this.display?.shortUnit ?? this.def.shortUnit ?? undefined
163
+ if (shortUnit !== undefined) return shortUnit
164
+
165
+ const unit = this.unit
166
+
167
+ if (!unit) return undefined
168
+
169
+ if (unit.length < 3) return unit
170
+ if (checkIsVeryShortUnit(unit[0])) return unit[0]
171
+
172
+ return undefined
173
+ }
174
+
175
+ /**
176
+ * Returns the full unit string for display, but only if it is different from the shortUnit.
177
+ * This avoids redundant display of units when the short and full units are the same.
178
+ * Also strips parentheses from the beginning and end of the unit.
179
+ */
180
+ @imemo get displayUnit(): string | undefined {
181
+ // The unit is considered trivial if it is the same as the short unit
182
+ const tooTrivial = this.unit === this.shortUnit
183
+ const displayUnit = !tooTrivial ? this.unit : undefined
184
+
185
+ // Remove parentheses from the beginning and end of the unit
186
+ const strippedUnit = displayUnit
187
+ ? stripOuterParentheses(displayUnit)
188
+ : undefined
189
+
190
+ return strippedUnit
191
+ }
192
+
193
+ // Returns a map where the key is a series slug such as "name" and the value is a set
194
+ // of all the unique values that this column has for that particular series.
195
+ getUniqueValuesGroupedBy(
196
+ indexColumnSlug: ColumnSlug
197
+ ): Map<PrimitiveType, Set<PrimitiveType>> {
198
+ const map = new Map<PrimitiveType, Set<PrimitiveType>>()
199
+ const values = this.values
200
+ const indexValues = this.table.getValuesAtIndices(
201
+ indexColumnSlug,
202
+ this.validRowIndices
203
+ ) as PrimitiveType[]
204
+ indexValues.forEach((indexVal, index) => {
205
+ if (!map.has(indexVal)) map.set(indexVal, new Set())
206
+ map.get(indexVal)!.add(values[index])
207
+ })
208
+ return map
209
+ }
210
+
211
+ @imemo get description(): string | undefined {
212
+ return this.def.description
213
+ }
214
+
215
+ @imemo get isEmpty(): boolean {
216
+ return this.valuesIncludingErrorValues.length === 0
217
+ }
218
+
219
+ @imemo get name(): string {
220
+ return this.def.name ?? this.def.slug
221
+ }
222
+
223
+ @imemo get nonEmptyName(): string {
224
+ return this.def.name || this.def.slug
225
+ }
226
+
227
+ @imemo get displayName(): string {
228
+ return (
229
+ this.display?.name ??
230
+ // this is a bit of an unusual fallback - if display.name is not given, titlePublic is the next best thing before name
231
+ this.def.presentation?.titlePublic ??
232
+ this.name ??
233
+ ""
234
+ )
235
+ }
236
+
237
+ @imemo get nonEmptyDisplayName(): string {
238
+ return (
239
+ this.display?.name ||
240
+ // this is a bit of an unusual fallback - if display.name is not given, titlePublic is the next best thing before name
241
+ this.def.presentation?.titlePublic ||
242
+ this.nonEmptyName
243
+ )
244
+ }
245
+
246
+ @imemo get titlePublicOrDisplayName(): IndicatorTitleWithFragments {
247
+ return this.def.presentation?.titlePublic
248
+ ? {
249
+ title: this.def.presentation?.titlePublic,
250
+ attributionShort: this.def.presentation?.attributionShort,
251
+ titleVariant: this.def.presentation?.titleVariant,
252
+ }
253
+ : { title: this.display?.name || this.name || "" }
254
+ }
255
+
256
+ @imemo get datasetId(): number | undefined {
257
+ return this.def.datasetId
258
+ }
259
+
260
+ @imemo get datasetName(): string | undefined {
261
+ return this.def.datasetName
262
+ }
263
+
264
+ // todo: is the isString necessary?
265
+ @imemo get sortedUniqNonEmptyStringVals(): JS_TYPE[] {
266
+ return Array.from(
267
+ new Set(this.values.filter(R.isString).filter((i) => i))
268
+ ).sort()
269
+ }
270
+
271
+ @imemo get slug(): string {
272
+ return this.def.slug
273
+ }
274
+
275
+ @imemo get valuesToIndices(): Map<any, number[]> {
276
+ const map = new Map<any, number[]>()
277
+ this.valuesIncludingErrorValues.forEach((value, index) => {
278
+ if (!map.has(value)) map.set(value, [])
279
+ map.get(value)!.push(index)
280
+ })
281
+ return map
282
+ }
283
+
284
+ indicesWhere(value: JS_TYPE | JS_TYPE[]): any {
285
+ const queries = Array.isArray(value) ? value : [value]
286
+ return _.union(
287
+ ...queries
288
+ .map((val) => this.valuesToIndices.get(val))
289
+ .filter(isPresent)
290
+ )
291
+ }
292
+
293
+ // We approximate whether a column is parsed simply by looking at the first row.
294
+ needsParsing(value: unknown): boolean {
295
+ // Skip parsing if explicit flag is set
296
+ if (this.def.skipParsing) return false
297
+
298
+ // Never parse computeds. The computed should return the correct JS type. Ideally we can provide some error messaging around this.
299
+ if (this.def.transform) return false
300
+
301
+ // If we already tried to parse it and failed we consider it "parsed"
302
+ if (value instanceof ErrorValue) return false
303
+
304
+ // If the passed value is of the correct type consider the column parsed.
305
+ if (typeof value === this.jsType) return false
306
+ return true
307
+ }
308
+
309
+ @imemo get isProjection(): boolean {
310
+ return !!this.display?.isProjection
311
+ }
312
+
313
+ @imemo get uniqValues(): JS_TYPE[] {
314
+ const set = this.uniqValuesAsSet
315
+
316
+ // Turn into array, faster than spread operator
317
+ const arr = new Array(set.size)
318
+ let i = 0
319
+ set.forEach((val) => (arr[i++] = val))
320
+ return arr
321
+ }
322
+
323
+ @imemo get uniqValuesAsSet(): Set<JS_TYPE> {
324
+ return new Set(this.values)
325
+ }
326
+
327
+ /**
328
+ * Returns all values including ErrorValues..
329
+ * Normally you want just the valid values, like `[45000, 50000, ...]`. But sometimes you
330
+ * need the ErrorValues too like `[45000, DivideByZeroError, 50000,...]`
331
+ */
332
+ @imemo get valuesIncludingErrorValues(): CoreValueType[] {
333
+ const { table, slug } = this
334
+ return table.has(slug) ? table.columnStore[slug] : []
335
+ }
336
+
337
+ @imemo get validRowIndices(): number[] {
338
+ const indices: number[] = []
339
+ for (let i = 0; i < this.valuesIncludingErrorValues.length; i++) {
340
+ const value = this.valuesIncludingErrorValues[i]
341
+ if (isNotErrorValue(value)) indices.push(i)
342
+ }
343
+ return indices
344
+ }
345
+
346
+ @imemo get values(): JS_TYPE[] {
347
+ const values: JS_TYPE[] = []
348
+
349
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
350
+ for (let i = 0; i < this.valuesIncludingErrorValues.length; i++) {
351
+ const value = this.valuesIncludingErrorValues[i]
352
+ if (isNotErrorValue(value)) values.push(value as JS_TYPE)
353
+ }
354
+ return values
355
+ }
356
+
357
+ @imemo get originalTimeColumnSlug(): string {
358
+ return getOriginalTimeColumnSlug(this.table, this.slug)
359
+ }
360
+
361
+ @imemo get originalTimeColumn(): CoreColumn {
362
+ return this.table.get(this.originalTimeColumnSlug)
363
+ }
364
+
365
+ @imemo get originalStartTimeColumnSlug(): string {
366
+ return getOriginalStartTimeColumnSlug(this.table, this.slug)
367
+ }
368
+
369
+ @imemo get originalStartTimeColumn(): CoreColumn {
370
+ return this.table.get(this.originalStartTimeColumnSlug)
371
+ }
372
+
373
+ @imemo get originalTimes(): number[] {
374
+ const { originalTimeColumnSlug } = this
375
+ if (!originalTimeColumnSlug) return []
376
+ return this.table.getValuesAtIndices(
377
+ originalTimeColumnSlug,
378
+ this.validRowIndices
379
+ ) as number[]
380
+ }
381
+
382
+ @imemo get originalValueColumnSlug(): string | undefined {
383
+ return getOriginalValueColumnSlug(this.table, this.slug)
384
+ }
385
+
386
+ @imemo get originalValues(): JS_TYPE[] {
387
+ const { originalValueColumnSlug } = this
388
+ if (!originalValueColumnSlug) return []
389
+ return this.table.getValuesAtIndices(
390
+ originalValueColumnSlug,
391
+ this.validRowIndices
392
+ ) as JS_TYPE[]
393
+ }
394
+
395
+ @imemo get minValue(): JS_TYPE | undefined {
396
+ return _.min(this.values)
397
+ }
398
+
399
+ @imemo get maxValue(): JS_TYPE | undefined {
400
+ return _.max(this.values)
401
+ }
402
+
403
+ @imemo get numErrorValues(): number {
404
+ return this.valuesIncludingErrorValues.length - this.numValues
405
+ }
406
+
407
+ // Number of correctly parsed values
408
+ @imemo get numValues(): number {
409
+ return this.values.length
410
+ }
411
+
412
+ @imemo get numUniqs(): number {
413
+ return this.uniqValuesAsSet.size
414
+ }
415
+
416
+ @imemo get valuesAscending(): JS_TYPE[] {
417
+ const values = this.values.slice()
418
+ return this.jsType === "string" ? values.sort() : sortNumeric(values)
419
+ }
420
+
421
+ get source(): OwidSource {
422
+ const { def } = this
423
+ return {
424
+ name: def.sourceName,
425
+ link: def.sourceLink,
426
+ dataPublishedBy: def.dataPublishedBy,
427
+ dataPublisherSource: def.dataPublisherSource,
428
+ retrievedDate: def.retrievedDate,
429
+ additionalInfo: def.additionalInfo,
430
+ }
431
+ }
432
+
433
+ // todo: remove. should not be on coretable
434
+ @imemo private get allTimes(): Time[] {
435
+ return this.table.getTimesAtIndices(this.validRowIndices)
436
+ }
437
+
438
+ // todo: remove. should not be on coretable
439
+ @imemo get uniqTimesAsc(): Time[] {
440
+ return sortNumeric(_.uniq(this.allTimes))
441
+ }
442
+
443
+ // todo: remove. should not be on coretable
444
+ @imemo get maxTime(): Time {
445
+ return _.max(this.allTimes) as Time
446
+ }
447
+
448
+ // todo: remove. should not be on coretable
449
+ @imemo get minTime(): Time {
450
+ return _.min(this.allTimes) as Time
451
+ }
452
+
453
+ // todo: remove? Should not be on CoreTable
454
+ @imemo get uniqEntityNames(): EntityName[] {
455
+ return _.uniq(this.allEntityNames)
456
+ }
457
+
458
+ // todo: remove? Should not be on CoreTable
459
+ @imemo private get allEntityNames(): EntityName[] {
460
+ return this.table.getValuesAtIndices(
461
+ this.table.entityNameSlug,
462
+ this.validRowIndices
463
+ ) as EntityName[]
464
+ }
465
+
466
+ // todo: remove? Should not be on CoreTable
467
+ // assumes table is sorted by time
468
+ @imemo get owidRows(): OwidVariableRow<JS_TYPE>[] {
469
+ const entities = this.allEntityNames
470
+ const times = this.allTimes
471
+ const values = this.values
472
+ const originalTimes = this.originalTimes
473
+ const originalValues = this.originalValues
474
+ return _.range(0, originalTimes.length).map((index) => {
475
+ return omitUndefinedValues({
476
+ entityName: entities[index],
477
+ time: times[index],
478
+ value: values[index],
479
+ originalTime: originalTimes[index],
480
+ originalValue: originalValues[index],
481
+ })
482
+ })
483
+ }
484
+
485
+ // todo: remove? Should not be on CoreTable
486
+ @imemo get owidRowsByEntityName(): Map<
487
+ EntityName,
488
+ OwidVariableRow<JS_TYPE>[]
489
+ > {
490
+ const map = new Map<EntityName, OwidVariableRow<JS_TYPE>[]>()
491
+ this.owidRows.forEach((row) => {
492
+ if (!map.has(row.entityName)) map.set(row.entityName, [])
493
+ map.get(row.entityName)!.push(row)
494
+ })
495
+ return map
496
+ }
497
+
498
+ // todo: remove? Should not be on CoreTable
499
+ @imemo get owidRowByEntityNameAndTime(): Map<
500
+ EntityName,
501
+ Map<Time, OwidVariableRow<JS_TYPE>>
502
+ > {
503
+ const valueByEntityNameAndTime = new Map<
504
+ EntityName,
505
+ Map<Time, OwidVariableRow<JS_TYPE>>
506
+ >()
507
+ this.owidRows.forEach((row) => {
508
+ if (!valueByEntityNameAndTime.has(row.entityName))
509
+ valueByEntityNameAndTime.set(row.entityName, new Map())
510
+ valueByEntityNameAndTime.get(row.entityName)!.set(row.time, row)
511
+ })
512
+ return valueByEntityNameAndTime
513
+ }
514
+
515
+ // todo: remove? Should not be on CoreTable
516
+ @imemo get valuesByTime(): Map<Time, JS_TYPE[]> {
517
+ const map = new Map<Time, JS_TYPE[]>()
518
+ this.owidRows.forEach((row) => {
519
+ if (!map.has(row.time)) map.set(row.time, [])
520
+ map.get(row.time)!.push(row.value)
521
+ })
522
+ return map
523
+ }
524
+
525
+ // todo: remove? Should not be on CoreTable
526
+ @imemo get valueByTimeAndEntityName(): Map<Time, Map<EntityName, JS_TYPE>> {
527
+ const valueByTimeAndEntityName = new Map<
528
+ Time,
529
+ Map<EntityName, JS_TYPE>
530
+ >()
531
+ this.owidRows.forEach((row) => {
532
+ if (!valueByTimeAndEntityName.has(row.time))
533
+ valueByTimeAndEntityName.set(row.time, new Map())
534
+ valueByTimeAndEntityName
535
+ .get(row.time)!
536
+ .set(row.entityName, row.value)
537
+ })
538
+ return valueByTimeAndEntityName
539
+ }
540
+
541
+ // todo: remove? Should not be on CoreTable
542
+ // NOTE: this uses the original times, so any tolerance is effectively unapplied.
543
+ @imemo get valueByEntityNameAndOriginalTime(): Map<
544
+ EntityName,
545
+ Map<Time, JS_TYPE>
546
+ > {
547
+ const valueByEntityNameAndTime = new Map<
548
+ EntityName,
549
+ Map<Time, JS_TYPE>
550
+ >()
551
+ this.owidRows.forEach((row) => {
552
+ if (!valueByEntityNameAndTime.has(row.entityName))
553
+ valueByEntityNameAndTime.set(row.entityName, new Map())
554
+ valueByEntityNameAndTime
555
+ .get(row.entityName)!
556
+ .set(row.originalTime, row.value)
557
+ })
558
+ return valueByEntityNameAndTime
559
+ }
560
+ }
561
+
562
+ export type CoreColumn = AbstractCoreColumn<any>
563
+
564
+ export class MissingColumn extends AbstractCoreColumn<any> {
565
+ jsType = JsTypes.string
566
+
567
+ formatValue(): string {
568
+ return ""
569
+ }
570
+ }
571
+
572
+ class StringColumn extends AbstractCoreColumn<string> {
573
+ jsType = JsTypes.string
574
+
575
+ formatValue(value: unknown): string {
576
+ return anyToString(value)
577
+ }
578
+
579
+ override parse(val: unknown): any {
580
+ if (val === null) return ErrorValueTypes.NullButShouldBeString
581
+ if (val === undefined) return ErrorValueTypes.UndefinedButShouldBeString
582
+ return String(val) || ""
583
+ }
584
+ }
585
+
586
+ class SeriesAnnotationColumn extends StringColumn {}
587
+ class CategoricalColumn extends StringColumn {}
588
+
589
+ class OrdinalColumn extends CategoricalColumn {
590
+ @imemo get allowedValuesSorted(): string[] | undefined {
591
+ return this.def.sort
592
+ }
593
+
594
+ @imemo override get sortedUniqNonEmptyStringVals(): string[] {
595
+ return this.allowedValuesSorted
596
+ ? this.allowedValuesSorted
597
+ : super.sortedUniqNonEmptyStringVals
598
+ }
599
+ }
600
+
601
+ class RegionColumn extends OrdinalColumn {}
602
+ class ContinentColumn extends RegionColumn {}
603
+ class ColorColumn extends CategoricalColumn {}
604
+
605
+ class BooleanColumn extends AbstractCoreColumn<boolean> {
606
+ jsType = JsTypes.boolean
607
+
608
+ formatValue(value: unknown): "true" | "false" {
609
+ return value ? "true" : "false"
610
+ }
611
+
612
+ override parse(val: unknown): boolean {
613
+ return !!val
614
+ }
615
+ }
616
+
617
+ abstract class AbstractColumnWithNumberFormatting<
618
+ T extends PrimitiveType,
619
+ > extends AbstractCoreColumn<T> {
620
+ jsType = JsTypes.number
621
+
622
+ formatValue(value: unknown, options?: TickFormattingOptions): string {
623
+ if (_.isNumber(value)) {
624
+ return formatValue(value, {
625
+ roundingMode: this.roundingMode,
626
+ numDecimalPlaces: this.numDecimalPlaces,
627
+ numSignificantFigures: this.numSignificantFigures,
628
+ ...options,
629
+ })
630
+ }
631
+ return ""
632
+ }
633
+
634
+ override formatValueShortWithAbbreviations(
635
+ value: unknown,
636
+ options?: TickFormattingOptions
637
+ ): string {
638
+ return super.formatValueShortWithAbbreviations(value, {
639
+ numberAbbreviation: "short",
640
+ // only include a unit if it's very short (e.g. %, $ or £)
641
+ ...omitUndefinedValues({
642
+ unit:
643
+ this.shortUnit !== undefined &&
644
+ checkIsVeryShortUnit(this.shortUnit)
645
+ ? this.shortUnit
646
+ : undefined,
647
+ }),
648
+ ...options,
649
+ })
650
+ }
651
+
652
+ override formatValueShort(
653
+ value: unknown,
654
+ options?: TickFormattingOptions
655
+ ): string {
656
+ return super.formatValueShort(value, {
657
+ ...omitUndefinedValues({
658
+ unit: this.shortUnit,
659
+ }),
660
+ ...options,
661
+ })
662
+ }
663
+
664
+ override formatValueLong(
665
+ value: unknown,
666
+ options?: TickFormattingOptions
667
+ ): string {
668
+ return super.formatValueLong(value, {
669
+ ...omitUndefinedValues({
670
+ unit: this.unit,
671
+ }),
672
+ ...options,
673
+ })
674
+ }
675
+
676
+ @imemo override get isAllIntegers(): boolean {
677
+ return this.values.every(
678
+ (val) => typeof val === "number" && val % 1 === 0
679
+ )
680
+ }
681
+ }
682
+
683
+ /**
684
+ * We strive to have clearly typed variables in the future, but for now our
685
+ * grapher variables are still untyped. Most are number-only, but we also have some
686
+ * string-only, and even some mixed ones.
687
+ * Hence, NumberOrStringColumn is used to store grapher variables.
688
+ * It extends AbstractColumnWithNumberFormatting, which ensures that we have
689
+ * implementations of formatValueShortWithAbbreviations and the like already.
690
+ * -- @marcelgerber, 2022-07-01
691
+ */
692
+ class NumberOrStringColumn extends AbstractColumnWithNumberFormatting<
693
+ number | string
694
+ > {
695
+ override formatValue(
696
+ value: unknown,
697
+ options?: TickFormattingOptions
698
+ ): string {
699
+ if (_.isNumber(value)) {
700
+ return super.formatValue(value, options)
701
+ }
702
+ return anyToString(value)
703
+ }
704
+ override parse(val: unknown): number | string | ErrorValue {
705
+ if (val === null) return ErrorValueTypes.NullButShouldBeNumber
706
+ if (val === undefined) return ErrorValueTypes.UndefinedButShouldBeNumber
707
+ if (Number.isNaN(val)) return ErrorValueTypes.NaNButShouldBeNumber
708
+
709
+ const valAsString = String(val)
710
+ const num = parseFloat(valAsString)
711
+ if (Number.isNaN(num)) return valAsString // return string value
712
+
713
+ return num
714
+ }
715
+ }
716
+
717
+ abstract class AbstractNumericColumn extends AbstractColumnWithNumberFormatting<number> {
718
+ override parse(val: unknown): number | ErrorValue {
719
+ if (val === null) return ErrorValueTypes.NullButShouldBeNumber
720
+ if (val === undefined) return ErrorValueTypes.UndefinedButShouldBeNumber
721
+ if (val === "") return ErrorValueTypes.BlankButShouldBeNumber
722
+ if (isNaN(Number(val))) return ErrorValueTypes.NaNButShouldBeNumber
723
+
724
+ const res = this._parse(val)
725
+ if (isNaN(res))
726
+ return ErrorValueTypes.NotAParseableNumberButShouldBeNumber
727
+
728
+ return res
729
+ }
730
+
731
+ protected _parse(val: unknown): number {
732
+ return parseFloat(String(val))
733
+ }
734
+ }
735
+
736
+ class NumericColumn extends AbstractNumericColumn {}
737
+ class NumericCategoricalColumn extends AbstractNumericColumn {}
738
+
739
+ class IntegerColumn extends NumericColumn {
740
+ override formatValue(
741
+ value: unknown,
742
+ options?: TickFormattingOptions
743
+ ): string {
744
+ return super.formatValue(value, {
745
+ numDecimalPlaces: 0,
746
+ ...options,
747
+ })
748
+ }
749
+
750
+ protected override _parse(val: unknown): number {
751
+ return parseInt(String(val))
752
+ }
753
+ }
754
+
755
+ class CurrencyColumn extends NumericColumn {
756
+ override formatValue(
757
+ value: unknown,
758
+ options?: TickFormattingOptions
759
+ ): string {
760
+ return super.formatValue(value, {
761
+ roundingMode: OwidVariableRoundingMode.decimalPlaces,
762
+ numDecimalPlaces: 0,
763
+ unit: this.shortUnit,
764
+ ...options,
765
+ })
766
+ }
767
+
768
+ @imemo override get shortUnit(): string {
769
+ return "$"
770
+ }
771
+ }
772
+
773
+ // Expects 50% to be 50
774
+ class PercentageColumn extends NumericColumn {
775
+ override formatValue(
776
+ value: number,
777
+ options?: TickFormattingOptions
778
+ ): string {
779
+ return super.formatValue(value, {
780
+ unit: this.shortUnit,
781
+ numDecimalPlaces: 1,
782
+ ...options,
783
+ })
784
+ }
785
+
786
+ @imemo override get shortUnit(): string {
787
+ return "%"
788
+ }
789
+ }
790
+
791
+ // Same as %, but indicates it's part of a group of columns that add up to 100%.
792
+ // Might not need this.
793
+ class RelativePercentageColumn extends PercentageColumn {}
794
+
795
+ class PercentChangeOverTimeColumn extends PercentageColumn {
796
+ override formatValue(
797
+ value: number,
798
+ options?: TickFormattingOptions
799
+ ): string {
800
+ return super.formatValue(value, {
801
+ showPlus: true,
802
+ ...options,
803
+ })
804
+ }
805
+ }
806
+
807
+ class DecimalPercentageColumn extends PercentageColumn {}
808
+ class RatioColumn extends NumericColumn {}
809
+
810
+ // todo: remove. should not be in coretable
811
+ class EntityIdColumn extends NumericCategoricalColumn {}
812
+ class EntityCodeColumn extends CategoricalColumn {}
813
+ class EntityNameColumn extends CategoricalColumn {}
814
+
815
+ // todo: cleanup time columns. current schema is a little incorrect.
816
+ export abstract class TimeColumn extends AbstractCoreColumn<number> {
817
+ jsType = JsTypes.number
818
+
819
+ abstract preposition: string
820
+
821
+ @imemo override get displayName(): string {
822
+ return _.capitalize(this.name)
823
+ }
824
+
825
+ override formatTime(time: number): string {
826
+ return this.formatValue(time)
827
+ }
828
+
829
+ override parse(val: unknown): number | ErrorValue {
830
+ return parseInt(String(val))
831
+ }
832
+ }
833
+
834
+ class YearColumn extends TimeColumn {
835
+ preposition = "in"
836
+
837
+ formatValue(value: number): string {
838
+ // Include BCE
839
+ return formatYear(value)
840
+ }
841
+ }
842
+
843
+ class DayColumn extends TimeColumn {
844
+ preposition = "on"
845
+
846
+ // We cache these values because running `formatDay` thousands of times takes some time.
847
+ static formatValueCache = new Map<number, string>()
848
+ formatValue(value: number): string {
849
+ if (!DayColumn.formatValueCache.has(value)) {
850
+ const formatted = formatDay(value)
851
+ DayColumn.formatValueCache.set(value, formatted)
852
+ return formatted
853
+ }
854
+ return DayColumn.formatValueCache.get(value)!
855
+ }
856
+
857
+ static formatValueForMobileCache = new Map<number, string>()
858
+ override formatValueForMobile(value: number): string {
859
+ if (!DayColumn.formatValueForMobileCache.has(value)) {
860
+ const formatted = formatDay(value, { format: "MMM D, 'YY" })
861
+ DayColumn.formatValueForMobileCache.set(value, formatted)
862
+ return formatted
863
+ }
864
+ return DayColumn.formatValueForMobileCache.get(value)!
865
+ }
866
+
867
+ static formatForCsvCache = new Map<number, string>()
868
+ override formatForCsv(value: number): string {
869
+ if (!DayColumn.formatForCsvCache.has(value)) {
870
+ const formatted = formatDay(value, { format: "YYYY-MM-DD" })
871
+ DayColumn.formatForCsvCache.set(value, formatted)
872
+ return formatted
873
+ }
874
+ return DayColumn.formatForCsvCache.get(value)!
875
+ }
876
+ }
877
+
878
+ const dateToTimeCache = new Map<string, Time>() // Cache for performance
879
+ class DateColumn extends DayColumn {
880
+ override parse(val: unknown): number {
881
+ // skip parsing if a date is a number, it's already been parsed
882
+ if (typeof val === "number") return val
883
+ const valAsString = String(val)
884
+ if (!dateToTimeCache.has(valAsString))
885
+ dateToTimeCache.set(
886
+ valAsString,
887
+ dateDiffInDays(
888
+ dayjs.utc(valAsString).toDate(),
889
+ dayjs.utc("2020-01-21").toDate()
890
+ )
891
+ )
892
+ return dateToTimeCache.get(valAsString)!
893
+ }
894
+ }
895
+
896
+ class QuarterColumn extends TimeColumn {
897
+ preposition = "in"
898
+
899
+ private static regEx = /^([+-]?\d+)-Q([1-4])$/
900
+
901
+ override parse(val: unknown): number | ErrorValue {
902
+ // skip parsing if a date is a number, it's already been parsed
903
+ if (typeof val === "number") return val
904
+ if (typeof val === "string") {
905
+ const match = val.match(QuarterColumn.regEx)
906
+ if (match) {
907
+ const [, year, quarter] = match
908
+ return parseInt(year) * 4 + (parseInt(quarter) - 1)
909
+ }
910
+ }
911
+ return ErrorValueTypes.InvalidQuarterValue
912
+ }
913
+
914
+ private static numToQuarter(value: number): number[] {
915
+ const year = Math.floor(value / 4)
916
+ const quarter = (Math.abs(value) % 4) + 1
917
+ return [year, quarter]
918
+ }
919
+
920
+ formatValue(value: number): string {
921
+ const [year, quarter] = QuarterColumn.numToQuarter(value)
922
+ return `Q${quarter}/${year}`
923
+ }
924
+
925
+ override formatForCsv(value: number): string {
926
+ const [year, quarter] = QuarterColumn.numToQuarter(value)
927
+ return `${year}-Q${quarter}`
928
+ }
929
+ }
930
+
931
+ class PopulationColumn extends IntegerColumn {}
932
+ class PopulationDensityColumn extends NumericColumn {}
933
+
934
+ class AgeColumn extends NumericColumn {}
935
+
936
+ export const ColumnTypeMap = {
937
+ String: StringColumn,
938
+ SeriesAnnotation: SeriesAnnotationColumn,
939
+ Categorical: CategoricalColumn,
940
+ Ordinal: OrdinalColumn,
941
+ Region: RegionColumn,
942
+ Continent: ContinentColumn,
943
+ NumberOrString: NumberOrStringColumn,
944
+ Numeric: NumericColumn,
945
+ Day: DayColumn,
946
+ Date: DateColumn,
947
+ Year: YearColumn,
948
+ Quarter: QuarterColumn,
949
+ Time: TimeColumn,
950
+ Boolean: BooleanColumn,
951
+ Currency: CurrencyColumn,
952
+ Percentage: PercentageColumn,
953
+ RelativePercentage: RelativePercentageColumn,
954
+ Integer: IntegerColumn,
955
+ DecimalPercentage: DecimalPercentageColumn,
956
+ PercentChangeOverTime: PercentChangeOverTimeColumn,
957
+ Ratio: RatioColumn,
958
+ Color: ColorColumn,
959
+ EntityCode: EntityCodeColumn,
960
+ EntityId: EntityIdColumn,
961
+ EntityName: EntityNameColumn,
962
+ Population: PopulationColumn,
963
+ PopulationDensity: PopulationDensityColumn,
964
+ Age: AgeColumn,
965
+ }
966
+
967
+ // Keep this in. This is used as a compile-time check that ColumnTypeMap covers all
968
+ // column names defined in ColumnTypeNames, since that is quite difficult to ensure
969
+ // otherwise without losing inferred type information.
970
+
971
+ const _ColumnTypeMap: {
972
+ [key in ColumnTypeNames]: unknown
973
+ } = ColumnTypeMap