@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,723 @@
1
+ // This implements the line labels that appear to the right of the lines/polygons in LineCharts/StackedAreas.
2
+ import * as _ from "lodash-es"
3
+ import * as React from "react"
4
+ import {
5
+ Bounds,
6
+ makeIdForHumanConsumption,
7
+ excludeUndefined,
8
+ } from "../../utils/index.js"
9
+ import { TextWrap, Halo, MarkdownTextWrap } from "../../components/index.js"
10
+ import { computed, makeObservable } from "mobx"
11
+ import { observer } from "mobx-react"
12
+ import { VerticalAxis } from "../axis/Axis"
13
+ import {
14
+ Color,
15
+ EntityName,
16
+ SeriesName,
17
+ VerticalAlign,
18
+ } from "../../types/index.js"
19
+ import {
20
+ BASE_FONT_SIZE,
21
+ GRAPHER_FONT_SCALE_12,
22
+ GRAPHER_OPACITY_MUTE,
23
+ GRAPHER_TEXT_OUTLINE_FACTOR,
24
+ } from "../core/GrapherConstants"
25
+ import { darkenColorForText } from "../color/ColorUtils"
26
+ import { AxisConfig } from "../axis/AxisConfig.js"
27
+ import { GRAPHER_BACKGROUND_DEFAULT } from "../color/ColorConstants"
28
+ import {
29
+ findImportantSeriesThatFitIntoTheAvailableSpace,
30
+ findSeriesThatFitIntoTheAvailableSpace,
31
+ } from "./LineLegendFilterAlgorithms.js"
32
+ import {
33
+ ANNOTATION_PADDING,
34
+ DEFAULT_CONNECTOR_LINE_WIDTH,
35
+ DEFAULT_FONT_WEIGHT,
36
+ LEGEND_ITEM_MIN_SPACING,
37
+ MARKER_MARGIN,
38
+ } from "./LineLegendConstants.js"
39
+ import { getSeriesKey } from "./LineLegendHelpers"
40
+ import { LineLabelSeries, PlacedSeries, SizedSeries } from "./LineLegendTypes"
41
+
42
+ function groupBounds(group: PlacedSeries[]): Bounds {
43
+ const first = group[0]
44
+ const last = group[group.length - 1]
45
+ const height = last.bounds.bottom - first.bounds.top
46
+ const width = Math.max(first.bounds.width, last.bounds.width)
47
+ return new Bounds(first.bounds.x, first.bounds.y, width, height)
48
+ }
49
+
50
+ function stackGroupVertically(
51
+ group: PlacedSeries[],
52
+ y: number
53
+ ): PlacedSeries[] {
54
+ let currentY = y
55
+ group.forEach((mark) => {
56
+ mark.bounds = mark.bounds.set({ y: currentY })
57
+ mark.repositions += 1
58
+ currentY += mark.bounds.height + LEGEND_ITEM_MIN_SPACING
59
+ })
60
+ return group
61
+ }
62
+
63
+ interface LineLabelsProps {
64
+ series: PlacedSeries[]
65
+ needsConnectorLines: boolean
66
+ showTextOutline?: boolean
67
+ textOutlineColor?: Color
68
+ anchor?: "start" | "end"
69
+ isStatic?: boolean
70
+ cursor?: string
71
+ onClick?: (series: PlacedSeries) => void
72
+ onMouseOver?: (series: PlacedSeries) => void
73
+ onMouseLeave?: (series: PlacedSeries) => void
74
+ }
75
+
76
+ @observer
77
+ class LineLabels extends React.Component<LineLabelsProps> {
78
+ constructor(props: LineLabelsProps) {
79
+ super(props)
80
+ makeObservable(this)
81
+ }
82
+
83
+ @computed private get anchor(): "start" | "end" {
84
+ return this.props.anchor ?? "start"
85
+ }
86
+
87
+ @computed private get showTextOutline(): boolean {
88
+ return this.props.showTextOutline ?? false
89
+ }
90
+
91
+ @computed private get textOutlineColor(): Color {
92
+ return this.props.textOutlineColor ?? GRAPHER_BACKGROUND_DEFAULT
93
+ }
94
+
95
+ private textOpacityForSeries(series: PlacedSeries): number {
96
+ const { hover, focus } = series
97
+
98
+ if (hover && focus) {
99
+ const isInForeground =
100
+ hover.active || focus.active || (focus.idle && hover.idle)
101
+ return isInForeground ? 1 : GRAPHER_OPACITY_MUTE
102
+ }
103
+
104
+ if (hover) return hover.background ? GRAPHER_OPACITY_MUTE : 1
105
+ if (focus) return focus.background ? GRAPHER_OPACITY_MUTE : 1
106
+
107
+ return 1
108
+ }
109
+
110
+ @computed private get markers(): {
111
+ series: PlacedSeries
112
+ labelText: { x: number; y: number }
113
+ connectorLine: { x1: number; x2: number }
114
+ }[] {
115
+ return this.props.series.map((series) => {
116
+ const direction = this.anchor === "start" ? 1 : -1
117
+ const markerMargin = direction * MARKER_MARGIN
118
+ const connectorLineWidth = direction * DEFAULT_CONNECTOR_LINE_WIDTH
119
+
120
+ const { x } = series.origBounds
121
+ const connectorLine = {
122
+ x1: x + markerMargin,
123
+ x2: x + connectorLineWidth - markerMargin,
124
+ }
125
+
126
+ const textX = this.props.needsConnectorLines
127
+ ? connectorLine.x2 + markerMargin
128
+ : x + markerMargin
129
+ const textY = series.bounds.y
130
+
131
+ return {
132
+ series,
133
+ labelText: { x: textX, y: textY },
134
+ connectorLine,
135
+ }
136
+ })
137
+ }
138
+
139
+ @computed private get textLabels(): React.ReactElement {
140
+ return (
141
+ <g id={makeIdForHumanConsumption("text-labels")}>
142
+ {this.markers.map(({ series, labelText }, index) => {
143
+ const textColor = darkenColorForText(series.color)
144
+ const textProps = {
145
+ fill: textColor,
146
+ opacity: this.textOpacityForSeries(series),
147
+ textAnchor: this.anchor,
148
+ }
149
+
150
+ return (
151
+ <Halo
152
+ id={makeIdForHumanConsumption(
153
+ "outline",
154
+ series.seriesName
155
+ )}
156
+ key={getSeriesKey(series, index)}
157
+ show={this.showTextOutline}
158
+ outlineWidth={
159
+ GRAPHER_TEXT_OUTLINE_FACTOR *
160
+ series.textWrap.fontSize
161
+ }
162
+ outlineColor={this.textOutlineColor}
163
+ >
164
+ {series.textWrap.renderSVG(
165
+ labelText.x,
166
+ labelText.y,
167
+ {
168
+ textProps,
169
+ id: makeIdForHumanConsumption(
170
+ "label",
171
+ series.seriesName
172
+ ),
173
+ }
174
+ )}
175
+ </Halo>
176
+ )
177
+ })}
178
+ </g>
179
+ )
180
+ }
181
+
182
+ @computed private get textAnnotations(): React.ReactElement | undefined {
183
+ const markersWithAnnotations = this.markers.filter(
184
+ ({ series }) => series.annotationTextWrap !== undefined
185
+ )
186
+ if (!markersWithAnnotations) return
187
+ return (
188
+ <g id={makeIdForHumanConsumption("text-annotations")}>
189
+ {markersWithAnnotations.map(({ series, labelText }, index) => {
190
+ if (!series.annotationTextWrap) return
191
+ return (
192
+ <Halo
193
+ id={series.seriesName}
194
+ key={getSeriesKey(series, index)}
195
+ show={this.showTextOutline}
196
+ outlineWidth={
197
+ GRAPHER_TEXT_OUTLINE_FACTOR *
198
+ series.annotationTextWrap.fontSize
199
+ }
200
+ outlineColor={this.textOutlineColor}
201
+ >
202
+ {series.annotationTextWrap.renderSVG(
203
+ labelText.x,
204
+ labelText.y +
205
+ series.textWrap.height +
206
+ ANNOTATION_PADDING,
207
+ {
208
+ textProps: {
209
+ fill: "#333",
210
+ opacity:
211
+ this.textOpacityForSeries(series),
212
+ textAnchor: this.anchor,
213
+ style: { fontWeight: 300 },
214
+ },
215
+ }
216
+ )}
217
+ </Halo>
218
+ )
219
+ })}
220
+ </g>
221
+ )
222
+ }
223
+
224
+ @computed private get connectorLines(): React.ReactElement | undefined {
225
+ if (!this.props.needsConnectorLines) return
226
+ return (
227
+ <g id={makeIdForHumanConsumption("connectors")}>
228
+ {this.markers.map(({ series, connectorLine }, index) => {
229
+ const { x1, x2 } = connectorLine
230
+ const {
231
+ level,
232
+ totalLevels,
233
+ origBounds: { centerY: leftCenterY },
234
+ bounds: { centerY: rightCenterY },
235
+ } = series
236
+
237
+ const step = (x2 - x1) / (totalLevels + 1)
238
+ const markerXMid = x1 + step + level * step
239
+ const d = `M${x1},${leftCenterY} H${markerXMid} V${rightCenterY} H${x2}`
240
+ const lineColor =
241
+ series.hover?.background || series.focus?.background
242
+ ? "#eee"
243
+ : "#999"
244
+
245
+ return (
246
+ <path
247
+ id={makeIdForHumanConsumption(series.seriesName)}
248
+ key={getSeriesKey(series, index)}
249
+ d={d}
250
+ stroke={lineColor}
251
+ strokeWidth={0.5}
252
+ fill="none"
253
+ />
254
+ )
255
+ })}
256
+ </g>
257
+ )
258
+ }
259
+
260
+ @computed private get interactions(): React.ReactElement | undefined {
261
+ return (
262
+ <g>
263
+ {this.props.series.map((series, index) => {
264
+ const x =
265
+ this.anchor === "start"
266
+ ? series.origBounds.x
267
+ : series.origBounds.x - series.bounds.width
268
+ return (
269
+ <g
270
+ key={getSeriesKey(series, index)}
271
+ onMouseOver={() => this.props.onMouseOver?.(series)}
272
+ onMouseLeave={() =>
273
+ this.props.onMouseLeave?.(series)
274
+ }
275
+ onClick={() => this.props.onClick?.(series)}
276
+ style={{ cursor: this.props.cursor }}
277
+ >
278
+ <rect
279
+ x={x}
280
+ y={series.bounds.y}
281
+ width={series.bounds.width}
282
+ height={series.bounds.height}
283
+ fill="#fff"
284
+ opacity={0}
285
+ />
286
+ </g>
287
+ )
288
+ })}
289
+ </g>
290
+ )
291
+ }
292
+
293
+ override render(): React.ReactElement {
294
+ return (
295
+ <>
296
+ {this.connectorLines}
297
+ {this.textAnnotations}
298
+ {this.textLabels}
299
+ {!this.props.isStatic && this.interactions}
300
+ </>
301
+ )
302
+ }
303
+ }
304
+
305
+ export interface LineLegendProps {
306
+ series: LineLabelSeries[]
307
+ yAxis?: VerticalAxis
308
+
309
+ // positioning
310
+ x?: number
311
+ yRange?: [number, number]
312
+ maxWidth?: number
313
+ xAnchor?: "start" | "end"
314
+ verticalAlign?: VerticalAlign
315
+
316
+ // presentation
317
+ fontSize?: number
318
+ fontWeight?: number
319
+ showTextOutlines?: boolean
320
+ textOutlineColor?: Color
321
+
322
+ // used to determine which series should be labelled when there is limited space
323
+ seriesNamesSortedByImportance?: SeriesName[]
324
+
325
+ // interactions
326
+ isStatic?: boolean // don't add interactions if true
327
+ onClick?: (key: SeriesName) => void
328
+ onMouseOver?: (key: SeriesName) => void
329
+ onMouseLeave?: () => void
330
+ }
331
+
332
+ @observer
333
+ export class LineLegend extends React.Component<LineLegendProps> {
334
+ constructor(props: LineLegendProps) {
335
+ super(props)
336
+ makeObservable(this)
337
+ }
338
+
339
+ /**
340
+ * Larger than the actual width since the width of the connector lines
341
+ * is always added, even if they're not rendered.
342
+ *
343
+ * This is partly due to a circular dependency (in line and stacked area
344
+ * charts), partly to avoid jumpy layout changes (slope charts).
345
+ */
346
+ static stableWidth(props: LineLegendProps): number {
347
+ const test = new LineLegend(props)
348
+ return test.stableWidth
349
+ }
350
+
351
+ static width(props: LineLegendProps): number {
352
+ const test = new LineLegend(props)
353
+ return test.width
354
+ }
355
+
356
+ static fontSize(props: Partial<LineLegendProps>): number {
357
+ const test = new LineLegend(props as LineLegendProps)
358
+ return test.fontSize
359
+ }
360
+
361
+ static maxLevel(props: Partial<LineLegendProps>): number {
362
+ const test = new LineLegend(props as LineLegendProps)
363
+ return test.maxLevel
364
+ }
365
+
366
+ static visibleSeriesNames(props: LineLegendProps): SeriesName[] {
367
+ const test = new LineLegend(props as LineLegendProps)
368
+ return test.visibleSeriesNames
369
+ }
370
+
371
+ @computed private get fontSize(): number {
372
+ return Math.floor(
373
+ GRAPHER_FONT_SCALE_12 * (this.props.fontSize ?? BASE_FONT_SIZE)
374
+ )
375
+ }
376
+
377
+ @computed private get fontWeight(): number {
378
+ return this.props.fontWeight ?? DEFAULT_FONT_WEIGHT
379
+ }
380
+
381
+ @computed private get maxWidth(): number {
382
+ return this.props.maxWidth ?? 300
383
+ }
384
+
385
+ @computed private get yAxis(): VerticalAxis {
386
+ return this.props.yAxis ?? new VerticalAxis(new AxisConfig())
387
+ }
388
+
389
+ @computed private get verticalAlign(): VerticalAlign {
390
+ return this.props.verticalAlign ?? VerticalAlign.middle
391
+ }
392
+
393
+ @computed private get textMaxWidth(): number {
394
+ return this.maxWidth - DEFAULT_CONNECTOR_LINE_WIDTH
395
+ }
396
+
397
+ private makeLabelTextWrap(
398
+ series: LineLabelSeries,
399
+ { fontWeights }: { fontWeights: { label: number; value?: number } }
400
+ ): TextWrap | MarkdownTextWrap {
401
+ if (!series.formattedValue) {
402
+ return new TextWrap({
403
+ text: series.label,
404
+ maxWidth: this.textMaxWidth,
405
+ fontSize: this.fontSize,
406
+ fontWeight: fontWeights?.label,
407
+ })
408
+ }
409
+
410
+ const isTextLabelBold = (fontWeights?.label ?? 400) >= 700
411
+ const isValueLabelBold = (fontWeights?.value ?? 400) >= 700
412
+
413
+ // text label fragments
414
+ const textLabel = { text: series.label, bold: isTextLabelBold }
415
+ const valueLabel = {
416
+ text: series.formattedValue,
417
+ bold: isValueLabelBold,
418
+ }
419
+
420
+ const newLine = series.placeFormattedValueInNewLine
421
+ ? "always"
422
+ : "avoid-wrap"
423
+
424
+ return MarkdownTextWrap.fromFragments({
425
+ main: textLabel,
426
+ secondary: valueLabel,
427
+ newLine,
428
+ textWrapProps: {
429
+ maxWidth: this.textMaxWidth,
430
+ fontSize: this.fontSize,
431
+ },
432
+ })
433
+ }
434
+
435
+ private makeAnnotationTextWrap(
436
+ series: LineLabelSeries
437
+ ): TextWrap | undefined {
438
+ if (!series.annotation) return undefined
439
+ const maxWidth = Math.min(this.textMaxWidth, 150)
440
+ return new TextWrap({
441
+ text: series.annotation,
442
+ maxWidth,
443
+ fontSize: this.fontSize * 0.9,
444
+ lineHeight: 1,
445
+ })
446
+ }
447
+
448
+ @computed.struct get sizedSeries(): SizedSeries[] {
449
+ const { fontWeight: globalFontWeight } = this
450
+ return this.props.series.map((series) => {
451
+ // font weight priority:
452
+ // series focus state > presense of value label > globally set font weight
453
+ const activeFontWeight = series.focus?.active ? 700 : undefined
454
+ const seriesFontWeight = series.formattedValue ? 700 : undefined
455
+ const fontWeight =
456
+ activeFontWeight ?? seriesFontWeight ?? globalFontWeight
457
+
458
+ const fontWeights = { label: fontWeight, value: 400 }
459
+ const textWrap = this.makeLabelTextWrap(series, { fontWeights })
460
+
461
+ const annotationTextWrap = this.makeAnnotationTextWrap(series)
462
+ const annotationWidth = annotationTextWrap?.width ?? 0
463
+ const annotationHeight = annotationTextWrap
464
+ ? ANNOTATION_PADDING + annotationTextWrap.height
465
+ : 0
466
+
467
+ return {
468
+ ...series,
469
+ textWrap,
470
+ annotationTextWrap,
471
+ width: Math.max(textWrap.width, annotationWidth),
472
+ height: textWrap.height + annotationHeight,
473
+ }
474
+ })
475
+ }
476
+
477
+ @computed private get maxLabelWidth(): number {
478
+ const { sizedSeries = [] } = this
479
+ return _.max(sizedSeries.map((d) => d.width)) ?? 0
480
+ }
481
+
482
+ @computed get stableWidth(): number {
483
+ return this.maxLabelWidth + DEFAULT_CONNECTOR_LINE_WIDTH + MARKER_MARGIN
484
+ }
485
+
486
+ @computed get width(): number {
487
+ return this.needsLines
488
+ ? this.stableWidth
489
+ : this.maxLabelWidth + MARKER_MARGIN
490
+ }
491
+
492
+ @computed get onMouseOver(): any {
493
+ return this.props.onMouseOver ?? _.noop
494
+ }
495
+ @computed get onMouseLeave(): any {
496
+ return this.props.onMouseLeave ?? _.noop
497
+ }
498
+ @computed get onClick(): any {
499
+ return this.props.onClick ?? _.noop
500
+ }
501
+
502
+ @computed get legendX(): number {
503
+ return this.props.x ?? 0
504
+ }
505
+
506
+ @computed get legendY(): [number, number] {
507
+ const range = this.props.yRange ?? this.yAxis.range
508
+ return [Math.min(range[1], range[0]), Math.max(range[1], range[0])]
509
+ }
510
+
511
+ private getYPositionForSeriesLabel(series: SizedSeries): number {
512
+ const y = this.yAxis.place(series.yValue)
513
+ const lineHeight = series.textWrap.singleLineHeight
514
+ switch (this.verticalAlign) {
515
+ case VerticalAlign.middle:
516
+ return y - series.height / 2
517
+ case VerticalAlign.top:
518
+ return y - lineHeight / 2
519
+ case VerticalAlign.bottom:
520
+ return y - series.height + lineHeight / 2
521
+ }
522
+ }
523
+
524
+ // Naive initial placement of each mark at the target height, before collision detection
525
+ @computed private get initialPlacedSeries(): PlacedSeries[] {
526
+ const { yAxis, legendX, legendY } = this
527
+
528
+ const [legendYMin, legendYMax] = legendY
529
+
530
+ return this.sizedSeries.map((series) => {
531
+ const labelHeight = series.height
532
+ const labelWidth = series.width + DEFAULT_CONNECTOR_LINE_WIDTH
533
+
534
+ const midY = yAxis.place(series.yValue)
535
+ const origBounds = new Bounds(
536
+ legendX,
537
+ midY - series.height / 2,
538
+ labelWidth,
539
+ labelHeight
540
+ )
541
+
542
+ // ensure label doesn't go beyond the top or bottom of the chart
543
+ const initialY = this.getYPositionForSeriesLabel(series)
544
+ const y = Math.min(
545
+ Math.max(initialY, legendYMin),
546
+ legendYMax - labelHeight
547
+ )
548
+ const bounds = new Bounds(legendX, y, labelWidth, labelHeight)
549
+
550
+ return {
551
+ ...series,
552
+ y,
553
+ midY,
554
+ origBounds,
555
+ bounds,
556
+ repositions: 0,
557
+ level: 0,
558
+ totalLevels: 0,
559
+ }
560
+ })
561
+ }
562
+
563
+ @computed get initialPlacedSeriesByName(): Map<EntityName, PlacedSeries> {
564
+ return new Map(this.initialPlacedSeries.map((d) => [d.seriesName, d]))
565
+ }
566
+
567
+ @computed get placedSeries(): PlacedSeries[] {
568
+ const [yLegendMin, yLegendMax] = this.legendY
569
+
570
+ // ensure list is sorted by the visual position in ascending order
571
+ const sortedSeries = _.sortBy(
572
+ this.visiblePlacedSeries,
573
+ (label) => label.midY
574
+ )
575
+
576
+ const groups: PlacedSeries[][] = sortedSeries.map((mark) => [
577
+ { ...mark },
578
+ ])
579
+
580
+ let hasOverlap
581
+
582
+ do {
583
+ hasOverlap = false
584
+ for (let i = 0; i < groups.length - 1; i++) {
585
+ const topGroup = groups[i]
586
+ const bottomGroup = groups[i + 1]
587
+ const topBounds = groupBounds(topGroup)
588
+ const bottomBounds = groupBounds(bottomGroup)
589
+ if (topBounds.intersects(bottomBounds)) {
590
+ const overlapHeight =
591
+ topBounds.bottom -
592
+ bottomBounds.top +
593
+ LEGEND_ITEM_MIN_SPACING
594
+ const newHeight =
595
+ topBounds.height +
596
+ LEGEND_ITEM_MIN_SPACING +
597
+ bottomBounds.height
598
+ const targetY =
599
+ topBounds.top -
600
+ overlapHeight *
601
+ (bottomGroup.length /
602
+ (topGroup.length + bottomGroup.length))
603
+ const overflowTop = Math.max(yLegendMin - targetY, 0)
604
+ const overflowBottom = Math.max(
605
+ targetY + newHeight - yLegendMax,
606
+ 0
607
+ )
608
+ const newY = targetY + overflowTop - overflowBottom
609
+ const newGroup = [...topGroup, ...bottomGroup]
610
+ stackGroupVertically(newGroup, newY)
611
+ groups.splice(i, 2, newGroup)
612
+ hasOverlap = true
613
+ break
614
+ }
615
+ }
616
+ } while (hasOverlap && groups.length > 1)
617
+
618
+ for (const group of groups) {
619
+ let currentLevel = 0
620
+ let prevSign = 0
621
+ for (const series of group) {
622
+ const currentSign = Math.sign(
623
+ series.bounds.y - series.origBounds.y
624
+ )
625
+ if (prevSign === currentSign) {
626
+ currentLevel -= currentSign
627
+ }
628
+ series.level = currentLevel
629
+ prevSign = currentSign
630
+ }
631
+ const minLevel = _.min(group.map((mark) => mark.level)) as number
632
+ const maxLevel = _.max(group.map((mark) => mark.level)) as number
633
+ for (const mark of group) {
634
+ mark.level -= minLevel
635
+ mark.totalLevels = maxLevel - minLevel + 1
636
+ }
637
+ }
638
+
639
+ return groups.flat()
640
+ }
641
+
642
+ @computed get seriesSortedByImportance(): PlacedSeries[] | undefined {
643
+ if (!this.props.seriesNamesSortedByImportance) return undefined
644
+ return excludeUndefined(
645
+ this.props.seriesNamesSortedByImportance.map((seriesName) =>
646
+ this.initialPlacedSeriesByName.get(seriesName)
647
+ )
648
+ )
649
+ }
650
+
651
+ private computeHeight(series: PlacedSeries[]): number {
652
+ return (
653
+ _.sumBy(series, (series) => series.bounds.height) +
654
+ (series.length - 1) * LEGEND_ITEM_MIN_SPACING
655
+ )
656
+ }
657
+
658
+ @computed get visiblePlacedSeries(): PlacedSeries[] {
659
+ const { initialPlacedSeries, seriesSortedByImportance, legendY } = this
660
+ const availableHeight = Math.abs(legendY[1] - legendY[0])
661
+ const totalHeight = this.computeHeight(initialPlacedSeries)
662
+
663
+ // early return if filtering is not needed
664
+ if (totalHeight <= availableHeight) return initialPlacedSeries
665
+
666
+ // if a list of series sorted by importance is provided, use it
667
+ if (seriesSortedByImportance) {
668
+ return findImportantSeriesThatFitIntoTheAvailableSpace(
669
+ seriesSortedByImportance,
670
+ availableHeight
671
+ )
672
+ }
673
+
674
+ // otherwise use the default filtering
675
+ return findSeriesThatFitIntoTheAvailableSpace(
676
+ initialPlacedSeries,
677
+ availableHeight
678
+ )
679
+ }
680
+
681
+ @computed get visibleSeriesNames(): SeriesName[] {
682
+ return this.visiblePlacedSeries.map((series) => series.seriesName)
683
+ }
684
+
685
+ @computed get visibleSeriesHeight(): number {
686
+ return this.computeHeight(this.visiblePlacedSeries)
687
+ }
688
+
689
+ // Does this placement need line markers or is the position of the labels already clear?
690
+ @computed private get needsLines(): boolean {
691
+ return this.placedSeries.some((series) => series.totalLevels > 1)
692
+ }
693
+
694
+ @computed private get maxLevel(): number {
695
+ return _.max(this.placedSeries.map((series) => series.totalLevels)) ?? 0
696
+ }
697
+
698
+ override render(): React.ReactElement {
699
+ return (
700
+ <g
701
+ id={makeIdForHumanConsumption("line-labels")}
702
+ className="LineLabels"
703
+ >
704
+ <LineLabels
705
+ series={this.placedSeries}
706
+ needsConnectorLines={this.needsLines}
707
+ showTextOutline={this.props.showTextOutlines}
708
+ textOutlineColor={this.props.textOutlineColor}
709
+ anchor={this.props.xAnchor}
710
+ isStatic={this.props.isStatic}
711
+ onMouseOver={(series): void =>
712
+ this.onMouseOver(series.seriesName)
713
+ }
714
+ onClick={(series): void => this.onClick(series.seriesName)}
715
+ onMouseLeave={(series): void =>
716
+ this.onMouseLeave(series.seriesName)
717
+ }
718
+ cursor={this.props.onClick ? "pointer" : "default"}
719
+ />
720
+ </g>
721
+ )
722
+ }
723
+ }