@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,708 @@
1
+ import * as _ from "lodash-es"
2
+ import * as R from "remeda"
3
+ import { type BaseType, type Selection, select } from "d3-selection"
4
+ import { ScaleLinear } from "d3-scale"
5
+ import { NoDataModal } from "../noDataModal/NoDataModal"
6
+ import { SortOrder } from "../../types/index.js"
7
+ import {
8
+ Bounds,
9
+ PointVector,
10
+ sortNumeric,
11
+ makeSafeForCSS,
12
+ getRelativeMouse,
13
+ intersection,
14
+ guid,
15
+ makeIdForHumanConsumption,
16
+ } from "../../utils/index.js"
17
+ import { computed, action, observable, makeObservable } from "mobx"
18
+ import { observer } from "mobx-react"
19
+ import * as React from "react"
20
+ import { Halo } from "../../components/index.js"
21
+ import { MultiColorPolyline } from "./MultiColorPolyline"
22
+ import {
23
+ ScatterPointsWithLabelsProps,
24
+ ScatterRenderSeries,
25
+ ScatterLabel,
26
+ ScatterSeries,
27
+ SCATTER_LINE_MIN_WIDTH,
28
+ SCATTER_POINT_MIN_RADIUS,
29
+ SCATTER_POINT_HOVER_TARGET_RANGE,
30
+ ScatterRenderPoint,
31
+ SCATTER_LABEL_MIN_FONT_SIZE_FACTOR,
32
+ } from "./ScatterPlotChartConstants"
33
+ import { ScatterLine, ScatterPoint } from "./ScatterPoints"
34
+ import {
35
+ makeStartLabel,
36
+ makeMidLabels,
37
+ makeEndLabel,
38
+ labelPriority,
39
+ } from "./ScatterUtils"
40
+ import { Triangle } from "./Triangle"
41
+ import { ColorScale } from "../color/ColorScale"
42
+ import {
43
+ BASE_FONT_SIZE,
44
+ GRAPHER_TEXT_OUTLINE_FACTOR,
45
+ } from "../core/GrapherConstants"
46
+
47
+ // This is the component that actually renders the points. The higher level ScatterPlot class renders points, legends, comparison lines, etc.
48
+ @observer
49
+ export class ScatterPointsWithLabels extends React.Component<ScatterPointsWithLabelsProps> {
50
+ base = React.createRef<SVGGElement>()
51
+
52
+ // closest point by quadtree search
53
+ private nearSeries: ScatterSeries | undefined = undefined
54
+ // currently hovered-over point via mouseenter/leave
55
+ private overSeries: ScatterSeries | undefined = undefined
56
+
57
+ constructor(props: ScatterPointsWithLabelsProps) {
58
+ super(props)
59
+
60
+ makeObservable<ScatterPointsWithLabels, "nearSeries" | "overSeries">(
61
+ this,
62
+ {
63
+ nearSeries: observable,
64
+ overSeries: observable,
65
+ }
66
+ )
67
+ }
68
+
69
+ @computed private get seriesArray(): ScatterSeries[] {
70
+ return this.props.seriesArray
71
+ }
72
+
73
+ @computed private get focusedSeriesNames(): string[] {
74
+ return (
75
+ intersection(
76
+ this.props.focusedSeriesNames || [],
77
+ this.seriesArray.map((g) => g.seriesName)
78
+ ) ?? []
79
+ )
80
+ }
81
+
82
+ @computed private get hoveredSeriesNames(): string[] {
83
+ return this.props.hoveredSeriesNames ?? []
84
+ }
85
+
86
+ @computed private get tooltipSeriesName(): string | undefined {
87
+ return this.props.tooltipSeriesName
88
+ }
89
+
90
+ // Layered mode occurs when any entity on the chart is hovered or focused
91
+ // Then, a special "foreground" set of entities is rendered over the background
92
+ @computed private get isLayerMode(): boolean {
93
+ return (
94
+ this.focusedSeriesNames.length > 0 ||
95
+ this.hoveredSeriesNames.length > 0 ||
96
+ this.props.isHoverModeActive ||
97
+ // if the user has selected entities that are not in the chart,
98
+ // we want to move all entities into the background
99
+ ((this.props.focusedSeriesNames ?? []).length > 0 &&
100
+ this.focusedSeriesNames.length === 0)
101
+ )
102
+ }
103
+
104
+ @computed private get bounds(): Bounds {
105
+ return this.props.dualAxis.innerBounds
106
+ }
107
+
108
+ // When focusing multiple entities, we hide some information to declutter
109
+ @computed private get isSubtleForeground(): boolean {
110
+ return (
111
+ this.focusedSeriesNames.length > 1 &&
112
+ this.props.seriesArray.some((series) => series.points.length > 2)
113
+ )
114
+ }
115
+
116
+ @computed private get colorScale(): ColorScale | undefined {
117
+ return this.props.colorScale
118
+ }
119
+
120
+ @computed private get sizeScale(): ScaleLinear<number, number> {
121
+ return this.props.sizeScale
122
+ }
123
+
124
+ @computed private get fontScale(): ScaleLinear<number, number> | undefined {
125
+ return this.props.fontScale
126
+ }
127
+
128
+ @computed private get hideScatterLabels(): boolean {
129
+ return !!this.props.hideScatterLabels
130
+ }
131
+
132
+ private getPointRadius(value: number | undefined): number {
133
+ const radius =
134
+ value !== undefined
135
+ ? this.sizeScale(value)
136
+ : this.sizeScale.range()[0]
137
+ // We are enforcing the minimum radius/width just before render,
138
+ // it should not be enforced earlier than that.
139
+ return Math.max(
140
+ radius,
141
+ this.props.isConnected
142
+ ? SCATTER_LINE_MIN_WIDTH
143
+ : SCATTER_POINT_MIN_RADIUS
144
+ )
145
+ }
146
+
147
+ private getLabelFontSize(value: number | undefined): number {
148
+ if (!this.fontScale) return BASE_FONT_SIZE
149
+ const fontSize =
150
+ value !== undefined
151
+ ? this.fontScale(value)
152
+ : this.fontScale.range()[0]
153
+ return Math.max(
154
+ fontSize,
155
+ SCATTER_LABEL_MIN_FONT_SIZE_FACTOR * this.props.baseFontSize
156
+ )
157
+ }
158
+
159
+ @computed private get hideConnectedScatterLines(): boolean {
160
+ return this.props.hideConnectedScatterLines
161
+ }
162
+
163
+ // Pre-transform data for rendering
164
+ @computed private get initialRenderSeries(): ScatterRenderSeries[] {
165
+ const { seriesArray, colorScale, bounds } = this
166
+ const xAxis = this.props.dualAxis.horizontalAxis.clone()
167
+ xAxis.range = bounds.xRange()
168
+ const yAxis = this.props.dualAxis.verticalAxis.clone()
169
+ yAxis.range = this.bounds.yRange()
170
+
171
+ return sortNumeric(
172
+ seriesArray.map((series): ScatterRenderSeries => {
173
+ const points = series.points.map(
174
+ (point): ScatterRenderPoint => {
175
+ const scaleColor =
176
+ colorScale !== undefined
177
+ ? colorScale.getColor(point.color)
178
+ : undefined
179
+ return {
180
+ position: new PointVector(
181
+ Math.floor(xAxis.place(point.x)),
182
+ Math.floor(yAxis.place(point.y))
183
+ ),
184
+ color: scaleColor ?? series.color,
185
+ size: this.getPointRadius(point.size),
186
+ time: point.time,
187
+ label: point.label,
188
+ }
189
+ }
190
+ )
191
+
192
+ return {
193
+ seriesName: series.seriesName,
194
+ displayKey: "key-" + makeSafeForCSS(series.seriesName),
195
+ color: series.color,
196
+ size: R.last(points)!.size,
197
+ fontSize: this.getLabelFontSize(
198
+ R.last(series.points)!.size
199
+ ),
200
+ points,
201
+ text: series.label,
202
+ midLabels: [],
203
+ allLabels: [],
204
+ offsetVector: PointVector.zero,
205
+ }
206
+ }),
207
+ (d) => d.size,
208
+ SortOrder.desc
209
+ )
210
+ }
211
+
212
+ @computed private get renderSeries(): ScatterRenderSeries[] {
213
+ // Draw the largest points first so that smaller ones can sit on top of them
214
+ const renderData = this.initialRenderSeries
215
+
216
+ for (const series of renderData) {
217
+ series.isHover = this.hoveredSeriesNames.includes(series.seriesName)
218
+ series.isFocus = this.focusedSeriesNames.includes(series.seriesName)
219
+ series.isTooltip = this.tooltipSeriesName === series.seriesName
220
+ series.isForeground = series.isHover || series.isFocus
221
+ if (series.isHover) series.size += 1
222
+ }
223
+
224
+ for (const series of renderData) {
225
+ series.startLabel = makeStartLabel(
226
+ series,
227
+ this.isSubtleForeground,
228
+ this.hideConnectedScatterLines,
229
+ this.props.baseFontSize
230
+ )
231
+ series.midLabels = makeMidLabels(
232
+ series,
233
+ this.isSubtleForeground,
234
+ this.hideConnectedScatterLines,
235
+ this.props.baseFontSize
236
+ )
237
+ series.endLabel = makeEndLabel(
238
+ series,
239
+ this.isSubtleForeground,
240
+ this.hideConnectedScatterLines,
241
+ this.props.baseFontSize
242
+ )
243
+ series.allLabels = [series.startLabel]
244
+ .concat(series.midLabels)
245
+ .concat([series.endLabel])
246
+ .filter((x) => x) as ScatterLabel[]
247
+ }
248
+
249
+ const labels = renderData.flatMap((series) => series.allLabels)
250
+
251
+ // Ensure labels fit inside bounds
252
+ // Must do before collision detection since it'll change the positions
253
+ this.moveLabelsInsideChartBounds(labels, this.bounds)
254
+
255
+ const labelsByPriority = sortNumeric(
256
+ labels,
257
+ (l) => labelPriority(l),
258
+ SortOrder.desc
259
+ )
260
+ if (this.focusedSeriesNames.length > 0)
261
+ this.hideUnselectedLabels(labelsByPriority)
262
+ if (this.hideScatterLabels) {
263
+ this.hideLabels(labelsByPriority, this.hoveredSeriesNames.length)
264
+ }
265
+
266
+ this.hideCollidingLabelsByPriority(labelsByPriority)
267
+
268
+ return renderData
269
+ }
270
+
271
+ private hideLabels(
272
+ labelsByPriority: ScatterLabel[],
273
+ nHoveredLabels: number
274
+ ): void {
275
+ labelsByPriority
276
+ .filter((label) => !(label.series.isHover && nHoveredLabels === 1))
277
+ .forEach((label) => (label.isHidden = true))
278
+ }
279
+
280
+ private hideUnselectedLabels(labelsByPriority: ScatterLabel[]): void {
281
+ labelsByPriority
282
+ .filter((label) => !label.series.isFocus && !label.series.isHover)
283
+ .forEach((label) => (label.isHidden = true))
284
+ }
285
+
286
+ private hideCollidingLabelsByPriority(
287
+ labelsByPriority: ScatterLabel[]
288
+ ): void {
289
+ for (let i = 0; i < labelsByPriority.length; i++) {
290
+ const higherPriorityLabel = labelsByPriority[i]
291
+ if (higherPriorityLabel.isHidden) continue
292
+
293
+ for (let j = i + 1; j < labelsByPriority.length; j++) {
294
+ const lowerPriorityLabel = labelsByPriority[j]
295
+ if (lowerPriorityLabel.isHidden) continue
296
+
297
+ const isHighlightedEndLabelOfEqualPriority =
298
+ lowerPriorityLabel.isEnd &&
299
+ (lowerPriorityLabel.series.isHover ||
300
+ lowerPriorityLabel.series.isFocus) &&
301
+ higherPriorityLabel.series.isHover ===
302
+ lowerPriorityLabel.series.isHover &&
303
+ higherPriorityLabel.series.isFocus ===
304
+ lowerPriorityLabel.series.isFocus
305
+
306
+ if (
307
+ isHighlightedEndLabelOfEqualPriority
308
+ ? // For highlighted end labels of equal priority, we want to allow some
309
+ // overlap – labels are still readable even if they overlap
310
+ higherPriorityLabel.bounds
311
+ .pad(6) // allow up to 6px of overlap
312
+ .intersects(lowerPriorityLabel.bounds)
313
+ : // For non-highlighted labels we want to leave more space between labels,
314
+ // partly to have a less noisy chart, and partly to prevent readers from
315
+ // thinking that "everything is labelled". In the past this has made
316
+ // readers think that if a label doesn't exist, it isn't plotted on the
317
+ // chart.
318
+ higherPriorityLabel.bounds
319
+ .pad(-6)
320
+ .intersects(lowerPriorityLabel.bounds)
321
+ ) {
322
+ lowerPriorityLabel.isHidden = true
323
+ }
324
+ }
325
+ }
326
+ }
327
+
328
+ // todo: move this to bounds class with a test
329
+ private moveLabelsInsideChartBounds(
330
+ labels: ScatterLabel[],
331
+ bounds: Bounds
332
+ ): void {
333
+ for (const label of labels) {
334
+ if (label.bounds.left < bounds.left - 1)
335
+ label.bounds = label.bounds.set({
336
+ x: label.bounds.x + label.bounds.width,
337
+ })
338
+ else if (label.bounds.right > bounds.right + 1)
339
+ label.bounds = label.bounds.set({
340
+ x: label.bounds.x - label.bounds.width,
341
+ })
342
+
343
+ if (label.bounds.top < bounds.top - 1)
344
+ label.bounds = label.bounds.set({ y: bounds.top })
345
+ else if (label.bounds.bottom > bounds.bottom + 1)
346
+ label.bounds = label.bounds.set({
347
+ y: bounds.bottom - label.bounds.height,
348
+ })
349
+ }
350
+ }
351
+
352
+ // Use a hybrid approach to mouseover:
353
+ // If the mouse is near the centroid of an element, that is prioritized
354
+ // Otherwise we fall back to the dot that the cursor is currently hovering over (if any)
355
+
356
+ @action.bound onPointMouseEnter(seriesName: string): void {
357
+ this.overSeries = this.seriesArray.find(
358
+ (series) => series.seriesName === seriesName
359
+ )
360
+ // only select if we're not already close to another point's center
361
+ if (this.overSeries && !this.nearSeries)
362
+ this.props.onMouseEnter?.(this.overSeries)
363
+ }
364
+
365
+ @action.bound onPointMouseLeave(): void {
366
+ this.overSeries = undefined
367
+ if (!this.nearSeries) this.props.onMouseLeave?.()
368
+ }
369
+
370
+ @action.bound onMouseMove(ev: React.MouseEvent<SVGGElement>): void {
371
+ if (this.base.current) {
372
+ const { x, y } = getRelativeMouse(this.base.current, ev)
373
+
374
+ // be more fine grained about finding nearby points when the cursor is
375
+ // already hovering over a larger dot in the background
376
+ const range = this.overSeries
377
+ ? SCATTER_POINT_HOVER_TARGET_RANGE / 4
378
+ : SCATTER_POINT_HOVER_TARGET_RANGE
379
+
380
+ // search for closest point to cursor position within range
381
+ const nearby = this.props.quadtree?.find(x, y, range)?.series
382
+ if (nearby) {
383
+ // only trigger listener if the selection has changed
384
+ if (nearby.seriesName !== this.nearSeries?.seriesName) {
385
+ this.props.onMouseEnter?.(nearby)
386
+ }
387
+ } else if (this.nearSeries) {
388
+ // if we've just moved out of range of a nearby point, fall back to
389
+ // the currently hovered-over dot (if there is one)
390
+ this.props.onMouseLeave?.()
391
+ if (this.overSeries) {
392
+ this.props.onMouseEnter?.(this.overSeries)
393
+ }
394
+ }
395
+ this.nearSeries = nearby
396
+ }
397
+ }
398
+
399
+ @action.bound onMouseLeave(): void {
400
+ // hide tooltip and clear hover state when leaving the chart's bounds
401
+ this.nearSeries = undefined
402
+ this.overSeries = undefined
403
+ if (this.props.onMouseLeave) this.props.onMouseLeave()
404
+ }
405
+
406
+ @action.bound onClick(): void {
407
+ if (this.props.onClick) this.props.onClick()
408
+ }
409
+
410
+ @computed get backgroundSeries(): ScatterRenderSeries[] {
411
+ return this.renderSeries.filter((series) => !series.isForeground)
412
+ }
413
+
414
+ @computed get foregroundSeries(): ScatterRenderSeries[] {
415
+ return this.renderSeries.filter((series) => !!series.isForeground)
416
+ }
417
+
418
+ private renderBackgroundSeries(): React.ReactElement | null {
419
+ const { backgroundSeries, isLayerMode, hideConnectedScatterLines } =
420
+ this
421
+
422
+ if (hideConnectedScatterLines) return null
423
+
424
+ return (
425
+ <g id={makeIdForHumanConsumption("points")}>
426
+ {backgroundSeries.map((series) => (
427
+ <ScatterLine
428
+ key={series.seriesName}
429
+ series={series}
430
+ isLayerMode={isLayerMode}
431
+ onMouseEnter={this.onPointMouseEnter}
432
+ onMouseLeave={this.onPointMouseLeave}
433
+ />
434
+ ))}
435
+ </g>
436
+ )
437
+ }
438
+
439
+ private renderBackgroundLabels(): React.ReactElement {
440
+ const { isLayerMode } = this
441
+ return (
442
+ <g
443
+ id={makeIdForHumanConsumption("labels")}
444
+ className="backgroundLabels"
445
+ fill={!isLayerMode ? "#333" : "#aaa"}
446
+ >
447
+ {this.backgroundSeries.map((series) => {
448
+ return series.allLabels
449
+ .filter((label) => !label.isHidden)
450
+ .map((label) => (
451
+ <Halo
452
+ key={series.displayKey + "-endLabel"}
453
+ id={makeIdForHumanConsumption(
454
+ "outline",
455
+ series.seriesName
456
+ )}
457
+ outlineWidth={
458
+ GRAPHER_TEXT_OUTLINE_FACTOR * label.fontSize
459
+ }
460
+ outlineColor={this.props.backgroundColor}
461
+ >
462
+ <text
463
+ id={makeIdForHumanConsumption(
464
+ "label",
465
+ label.text
466
+ )}
467
+ x={label.bounds.x.toFixed(2)}
468
+ y={(
469
+ label.bounds.y + label.bounds.height
470
+ ).toFixed(2)}
471
+ fontSize={label.fontSize.toFixed(2)}
472
+ fontWeight={label.fontWeight}
473
+ fill={isLayerMode ? "#aaa" : label.color}
474
+ style={{ pointerEvents: "none" }}
475
+ >
476
+ {label.text}
477
+ </text>
478
+ </Halo>
479
+ ))
480
+ })}
481
+ </g>
482
+ )
483
+ }
484
+
485
+ @computed get renderUid(): number {
486
+ return guid()
487
+ }
488
+
489
+ private renderForegroundSeries(): React.ReactElement {
490
+ const { isSubtleForeground, hideConnectedScatterLines } = this
491
+ return (
492
+ <g id={makeIdForHumanConsumption("points")}>
493
+ {this.foregroundSeries.map((series) => {
494
+ const lastPoint = R.last(series.points)!
495
+ const strokeWidth =
496
+ (hideConnectedScatterLines
497
+ ? 3
498
+ : series.isHover
499
+ ? 3
500
+ : isSubtleForeground
501
+ ? 1.5
502
+ : 2) +
503
+ lastPoint.size / 2
504
+
505
+ if (series.points.length === 1)
506
+ return (
507
+ <ScatterPoint
508
+ key={series.displayKey}
509
+ series={series}
510
+ hideFocusRing={this.props.hideFocusRing}
511
+ onMouseEnter={this.onPointMouseEnter}
512
+ onMouseLeave={this.onPointMouseLeave}
513
+ />
514
+ )
515
+
516
+ const firstValue = R.first(series.points)
517
+ const opacity = isSubtleForeground ? 0.9 : 1
518
+ const radius = hideConnectedScatterLines
519
+ ? strokeWidth
520
+ : strokeWidth / 2 + 1
521
+ let rotation = PointVector.angle(
522
+ series.offsetVector,
523
+ PointVector.up
524
+ )
525
+ if (series.offsetVector.x < 0) rotation = -rotation
526
+ return (
527
+ <g
528
+ id={makeIdForHumanConsumption(
529
+ "time-scatter",
530
+ series.displayKey
531
+ )}
532
+ key={series.displayKey}
533
+ className={series.displayKey}
534
+ >
535
+ {!hideConnectedScatterLines && (
536
+ <MultiColorPolyline
537
+ points={series.points.map((point) => ({
538
+ x: point.position.x,
539
+ y: point.position.y,
540
+ color: point.color,
541
+ }))}
542
+ strokeWidth={strokeWidth}
543
+ opacity={opacity}
544
+ />
545
+ )}
546
+ {(series.isFocus || hideConnectedScatterLines) &&
547
+ firstValue && (
548
+ <circle
549
+ cx={firstValue.position.x}
550
+ cy={firstValue.position.y}
551
+ r={radius}
552
+ fill={firstValue.color}
553
+ opacity={opacity}
554
+ stroke={firstValue.color}
555
+ strokeOpacity={0.6}
556
+ />
557
+ )}
558
+ {(series.isHover || hideConnectedScatterLines) &&
559
+ series.points
560
+ .slice(
561
+ 1,
562
+ hideConnectedScatterLines
563
+ ? undefined
564
+ : -1
565
+ )
566
+ .map((v, index) => (
567
+ <circle
568
+ key={index}
569
+ cx={v.position.x}
570
+ cy={v.position.y}
571
+ r={radius}
572
+ fill={v.color}
573
+ stroke="none"
574
+ />
575
+ ))}
576
+ {!hideConnectedScatterLines && (
577
+ <Triangle
578
+ cx={lastPoint.position.x}
579
+ cy={lastPoint.position.y}
580
+ r={1.5 + strokeWidth}
581
+ rotation={rotation}
582
+ fill={lastPoint.color}
583
+ opacity={opacity}
584
+ />
585
+ )}
586
+ </g>
587
+ )
588
+ })}
589
+ </g>
590
+ )
591
+ }
592
+
593
+ private renderForegroundLabels(): React.ReactElement {
594
+ return (
595
+ <g id={makeIdForHumanConsumption("labels")}>
596
+ {this.foregroundSeries.map((series) => {
597
+ return series.allLabels
598
+ .filter((label) => !label.isHidden)
599
+ .map((label, index) => (
600
+ <Halo
601
+ id={makeIdForHumanConsumption(
602
+ "outline",
603
+ series.seriesName
604
+ )}
605
+ key={`${series.displayKey}-label-${index}`}
606
+ outlineWidth={
607
+ GRAPHER_TEXT_OUTLINE_FACTOR * label.fontSize
608
+ }
609
+ outlineColor={this.props.backgroundColor}
610
+ >
611
+ <text
612
+ id={makeIdForHumanConsumption(
613
+ "label",
614
+ series.seriesName
615
+ )}
616
+ x={label.bounds.x.toFixed(2)}
617
+ y={(
618
+ label.bounds.y + label.bounds.height
619
+ ).toFixed(2)}
620
+ fontSize={label.fontSize}
621
+ fontWeight={label.fontWeight}
622
+ fill={label.color}
623
+ >
624
+ {label.text}
625
+ </text>
626
+ </Halo>
627
+ ))
628
+ })}
629
+ </g>
630
+ )
631
+ }
632
+
633
+ animSelection?: Selection<BaseType, unknown, SVGGElement | null, unknown>
634
+
635
+ private runAnimation(): void {
636
+ const radiuses: string[] = []
637
+ this.animSelection = select(this.base.current).selectAll("circle")
638
+
639
+ this.animSelection
640
+ .each(function () {
641
+ const circle = this as SVGCircleElement
642
+ radiuses.push(circle.getAttribute("r") as string)
643
+ circle.setAttribute("r", "0")
644
+ })
645
+ .transition()
646
+ .duration(500)
647
+ .attr("r", (_, i) => radiuses[i])
648
+ .on("end", () => this.forceUpdate())
649
+ }
650
+
651
+ override componentDidMount(): void {
652
+ if (!this.props.disableIntroAnimation) {
653
+ this.runAnimation()
654
+ }
655
+ }
656
+
657
+ override componentWillUnmount(): void {
658
+ if (this.animSelection) this.animSelection.interrupt()
659
+ }
660
+
661
+ override render(): React.ReactElement {
662
+ const { bounds, renderSeries, renderUid } = this
663
+ const clipBounds = bounds.pad(-10)
664
+
665
+ if (_.isEmpty(renderSeries))
666
+ return (
667
+ <NoDataModal
668
+ manager={this.props.noDataModalManager}
669
+ bounds={bounds}
670
+ />
671
+ )
672
+
673
+ return (
674
+ <g
675
+ ref={this.base}
676
+ id={makeIdForHumanConsumption("scatter-points")}
677
+ className="PointsWithLabels clickable"
678
+ clipPath={`url(#scatterBounds-${renderUid})`}
679
+ onMouseMove={this.onMouseMove}
680
+ onMouseLeave={this.onMouseLeave}
681
+ onClick={this.onClick}
682
+ >
683
+ <rect
684
+ key="background"
685
+ x={bounds.x}
686
+ y={bounds.y}
687
+ width={bounds.width}
688
+ height={bounds.height}
689
+ fill="rgba(255,255,255,0)"
690
+ />
691
+ <defs>
692
+ <clipPath id={`scatterBounds-${renderUid}`}>
693
+ <rect
694
+ x={clipBounds.x}
695
+ y={clipBounds.y}
696
+ width={clipBounds.width}
697
+ height={clipBounds.height}
698
+ />
699
+ </clipPath>
700
+ </defs>
701
+ {this.renderBackgroundSeries()}
702
+ {this.renderBackgroundLabels()}
703
+ {this.renderForegroundSeries()}
704
+ {this.renderForegroundLabels()}
705
+ </g>
706
+ )
707
+ }
708
+ }