@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,809 @@
1
+ import * as React from "react"
2
+ import { observable, computed, action, makeObservable } from "mobx"
3
+ import { observer } from "mobx-react"
4
+ import {
5
+ Bounds,
6
+ getRelativeMouse,
7
+ makeIdForHumanConsumption,
8
+ Url,
9
+ } from "../../utils/index.js"
10
+ import {
11
+ DATAPAGE_ABOUT_THIS_DATA_SECTION_ID,
12
+ MarkdownTextWrap,
13
+ TextWrap,
14
+ } from "../../components/index.js"
15
+ import { Tooltip } from "../tooltip/Tooltip"
16
+ import { FooterManager } from "./FooterManager"
17
+ import { ActionButtons } from "../controls/ActionButtons"
18
+ import {
19
+ BASE_FONT_SIZE,
20
+ DEFAULT_GRAPHER_BOUNDS,
21
+ GRAPHER_FOOTER_CLASS,
22
+ GRAPHER_FRAME_PADDING_HORIZONTAL,
23
+ GrapherModal,
24
+ } from "../core/GrapherConstants"
25
+ import { GRAPHER_LIGHT_TEXT } from "../color/ColorConstants"
26
+
27
+ /*
28
+
29
+ The footer contains the sources, the note (optional), the action buttons and the license and origin URL (optional).
30
+
31
+ If all elements exist, they are laid out as follows:
32
+ +-------------------------------------------------------+
33
+ | Sources |
34
+ +------------------------------------+------------------+
35
+ | Note | |
36
+ +------------------------------------+ Action buttons |
37
+ | Origin URL | CC BY | |
38
+ +-------------------------------------------------------+
39
+
40
+ If the note is long, it is placed below the sources:
41
+ +-------------------------------------------------------+
42
+ | Sources |
43
+ +-------------------------------------------------------+
44
+ | Note |
45
+ +------------------------------------+------------------+
46
+ | Origin URL | CC BY | Action buttons |
47
+ +------------------------------------+------------------+
48
+
49
+ If the origin url and license are short enough, they are placed next to the sources:
50
+ +------------------------------+------------------------+
51
+ | Sources | Origin URL | CC BY |
52
+ +------------------------------+-----+------------------+
53
+ | Note | Action buttons |
54
+ +-------------------------------------------------------+
55
+
56
+ If the note is missing and the sources text is not too long, the sources are placed next to the action buttons:
57
+ +------------------------------------+------------------+
58
+ | Sources | |
59
+ +------------------------------------+ Action buttons |
60
+ | Origin URL | CC BY | |
61
+ +-------------------------------------------------------+
62
+
63
+ */
64
+
65
+ // keep in sync with sass variables in Footer.scss
66
+ const HORIZONTAL_PADDING = 8
67
+
68
+ interface FooterProps {
69
+ manager: FooterManager
70
+ maxWidth?: number
71
+ }
72
+
73
+ abstract class AbstractFooter<
74
+ Props extends FooterProps = FooterProps,
75
+ > extends React.Component<Props> {
76
+ verticalPadding = 4
77
+
78
+ constructor(props: Props) {
79
+ super(props)
80
+
81
+ makeObservable(this, {
82
+ tooltipTarget: observable.ref,
83
+ })
84
+ }
85
+
86
+ @computed protected get manager(): FooterManager {
87
+ return this.props.manager
88
+ }
89
+
90
+ @computed protected get maxWidth(): number {
91
+ return this.props.maxWidth ?? DEFAULT_GRAPHER_BOUNDS.width
92
+ }
93
+
94
+ @computed protected get useBaseFontSize(): boolean {
95
+ return !!this.manager.useBaseFontSize
96
+ }
97
+
98
+ @computed protected get baseFontSize(): number {
99
+ return this.manager.fontSize ?? BASE_FONT_SIZE
100
+ }
101
+
102
+ @computed protected get hideOriginUrl(): boolean {
103
+ return !!this.manager.hideOriginUrl
104
+ }
105
+
106
+ @computed protected get sourcesLine(): string {
107
+ return this.manager.sourcesLine?.replace(/\r\n|\n|\r/g, "") ?? ""
108
+ }
109
+
110
+ @computed protected get sourcesText(): string {
111
+ return `Data source: ${this.sourcesLine} - Learn more about this data`
112
+ }
113
+
114
+ @computed protected get noteText(): string {
115
+ return this.manager.note ? `Note: ${this.manager.note}` : ""
116
+ }
117
+
118
+ @computed protected get markdownNoteText(): string {
119
+ return this.manager.note ? `**Note:** ${this.manager.note}` : ""
120
+ }
121
+
122
+ @computed protected get licenseText(): string {
123
+ const branding = this.manager.branding
124
+ if (this.manager.hasOWIDLogo) {
125
+ return branding?.licenseText ?? "CC BY"
126
+ }
127
+ return branding?.poweredByText ?? "Powered by Build Canada Charts"
128
+ }
129
+
130
+ @computed protected get licenseUrl(): string {
131
+ const branding = this.manager.branding
132
+ if (this.manager.hasOWIDLogo) {
133
+ return (
134
+ branding?.licenseUrl ??
135
+ "https://creativecommons.org/licenses/by/4.0/"
136
+ )
137
+ }
138
+ return branding?.poweredByUrl ?? ""
139
+ }
140
+
141
+ @computed protected get originUrlWithProtocol(): string {
142
+ return this.manager.originUrlWithProtocol ?? "http://localhost"
143
+ }
144
+
145
+ @computed protected get finalUrl(): string {
146
+ const originUrl = this.originUrlWithProtocol
147
+ const url = Url.fromURL(originUrl)
148
+ return url.originAndPath ?? ""
149
+ }
150
+
151
+ @computed protected get correctedUrlText(): string | undefined {
152
+ const originUrl = this.originUrlWithProtocol
153
+
154
+ // Make sure the link is valid
155
+ if (!originUrl || !originUrl.toLowerCase().match(/^https?:\/\/./))
156
+ return undefined
157
+
158
+ const url = Url.fromURL(originUrl)
159
+ return url.originAndPath?.replace(/^https?:\/\//, "").replace(/\/$/, "") // remove trailing slash
160
+ }
161
+
162
+ protected static constructLicenseAndOriginUrlText(
163
+ urlText: string | undefined,
164
+ licenseText: string
165
+ ): string {
166
+ if (!urlText) return licenseText
167
+ return [urlText, licenseText].join(" | ")
168
+ }
169
+
170
+ @computed protected get finalUrlText(): string | undefined {
171
+ const {
172
+ correctedUrlText,
173
+ licenseText,
174
+ fontSize,
175
+ maxWidth,
176
+ actionButtons,
177
+ } = this
178
+
179
+ if (this.hideOriginUrl) return undefined
180
+
181
+ if (!correctedUrlText) return undefined
182
+
183
+ const licenseAndOriginUrlText =
184
+ AbstractFooter.constructLicenseAndOriginUrlText(
185
+ correctedUrlText,
186
+ licenseText
187
+ )
188
+ const licenseAndOriginUrlWidth = Bounds.forText(
189
+ licenseAndOriginUrlText,
190
+ { fontSize }
191
+ ).width
192
+
193
+ // If the URL is too long, don't show it
194
+ if (
195
+ licenseAndOriginUrlWidth + HORIZONTAL_PADDING >
196
+ maxWidth - actionButtons.width
197
+ )
198
+ return undefined
199
+
200
+ return correctedUrlText
201
+ }
202
+
203
+ @computed protected get licenseAndOriginUrlText(): string {
204
+ const { finalUrlText, licenseText } = this
205
+ return AbstractFooter.constructLicenseAndOriginUrlText(
206
+ finalUrlText,
207
+ licenseText
208
+ )
209
+ }
210
+
211
+ @computed protected get lineHeight(): number {
212
+ return this.manager.isSmall ? 1.1 : 1.2
213
+ }
214
+
215
+ @computed protected get fontSize(): number {
216
+ if (this.useBaseFontSize) {
217
+ return (11 / BASE_FONT_SIZE) * this.baseFontSize
218
+ }
219
+ return this.manager.isMedium ? 11 : 12
220
+ }
221
+
222
+ @computed protected get sourcesFontSize(): number {
223
+ if (this.useBaseFontSize) {
224
+ return (12 / BASE_FONT_SIZE) * this.baseFontSize
225
+ }
226
+ return this.manager.isSmall ? 12 : 13
227
+ }
228
+
229
+ @computed protected get showNote(): boolean {
230
+ return !this.manager.hideNote && !!this.noteText
231
+ }
232
+
233
+ @computed private get actionButtonsWidthWithIconsOnly(): number {
234
+ return new ActionButtons({
235
+ manager: this.manager,
236
+ maxWidth: this.maxWidth,
237
+ }).widthWithIconsOnly
238
+ }
239
+
240
+ @computed private get useFullWidthSources(): boolean {
241
+ const {
242
+ showNote,
243
+ sourcesFontSize,
244
+ maxWidth,
245
+ sourcesText,
246
+ actionButtonsWidthWithIconsOnly,
247
+ } = this
248
+ if (showNote) return true
249
+ const sourcesWidth = Bounds.forText(sourcesText, {
250
+ fontSize: sourcesFontSize,
251
+ }).width
252
+ return sourcesWidth > 2 * (maxWidth - actionButtonsWidthWithIconsOnly)
253
+ }
254
+
255
+ @computed private get useFullWidthNote(): boolean {
256
+ const {
257
+ fontSize,
258
+ maxWidth,
259
+ noteText,
260
+ actionButtonsWidthWithIconsOnly,
261
+ } = this
262
+ const noteWidth = Bounds.forText(noteText, { fontSize }).width
263
+ return noteWidth > 2 * (maxWidth - actionButtonsWidthWithIconsOnly)
264
+ }
265
+
266
+ @computed protected get sourcesMaxWidth(): number {
267
+ if (this.useFullWidthSources) return this.maxWidth
268
+ return this.maxWidth - this.actionButtons.width - HORIZONTAL_PADDING
269
+ }
270
+
271
+ @computed protected get noteMaxWidth(): number {
272
+ if (this.useFullWidthNote) return this.maxWidth
273
+ return this.maxWidth - this.actionButtons.width - HORIZONTAL_PADDING
274
+ }
275
+
276
+ @computed protected get licenseAndOriginUrlMaxWidth(): number {
277
+ return this.maxWidth
278
+ }
279
+
280
+ @computed protected get showLicenseNextToSources(): boolean {
281
+ const {
282
+ useFullWidthSources,
283
+ maxWidth,
284
+ sources,
285
+ licenseAndOriginUrl,
286
+ note,
287
+ } = this
288
+ if (!useFullWidthSources) return false
289
+ // if there's space, keep the license below the note
290
+ if (this.useFullWidthNote || note.htmlLines.length <= 1) return false
291
+ return (
292
+ sources.width + HORIZONTAL_PADDING + licenseAndOriginUrl.width <=
293
+ maxWidth
294
+ )
295
+ }
296
+
297
+ @computed protected get sources(): MarkdownTextWrap {
298
+ const { lineHeight } = this
299
+ return new MarkdownTextWrap({
300
+ text: this.sourcesText,
301
+ maxWidth: this.sourcesMaxWidth,
302
+ fontSize: this.sourcesFontSize,
303
+ lineHeight,
304
+ })
305
+ }
306
+
307
+ @computed protected get note(): MarkdownTextWrap {
308
+ const { fontSize, lineHeight, manager } = this
309
+ return new MarkdownTextWrap({
310
+ text: this.markdownNoteText,
311
+ maxWidth: this.noteMaxWidth,
312
+ fontSize,
313
+ lineHeight,
314
+ detailsOrderedByReference: manager.detailsOrderedByReference,
315
+ })
316
+ }
317
+
318
+ @computed protected get licenseAndOriginUrl(): TextWrap {
319
+ const { fontSize, lineHeight } = this
320
+ return new TextWrap({
321
+ text: this.licenseAndOriginUrlText,
322
+ maxWidth: this.licenseAndOriginUrlMaxWidth,
323
+ rawHtml: true,
324
+ fontSize,
325
+ lineHeight,
326
+ })
327
+ }
328
+
329
+ @computed private get actionButtonsMaxWidth(): number {
330
+ const {
331
+ correctedUrlText,
332
+ licenseText,
333
+ maxWidth,
334
+ fontSize,
335
+ sourcesFontSize,
336
+ useFullWidthSources,
337
+ sourcesText,
338
+ noteText,
339
+ showNote,
340
+ useFullWidthNote,
341
+ } = this
342
+
343
+ const sourcesWidth = Bounds.forText(sourcesText, {
344
+ fontSize: sourcesFontSize,
345
+ }).width
346
+ const noteWidth = Bounds.forText(noteText, { fontSize }).width
347
+
348
+ // text next to the action buttons
349
+ const leftTextWidth = !useFullWidthSources
350
+ ? sourcesWidth
351
+ : showNote && !useFullWidthNote
352
+ ? noteWidth
353
+ : 0
354
+ // text above the action buttons
355
+ // (taken into account to ensure the action buttons are not too close to clickable text)
356
+ const topTextWidth = useFullWidthSources
357
+ ? useFullWidthNote
358
+ ? noteWidth
359
+ : sourcesWidth
360
+ : 0
361
+ const licenseAndOriginUrlWidth = Bounds.forText(
362
+ AbstractFooter.constructLicenseAndOriginUrlText(
363
+ correctedUrlText,
364
+ licenseText
365
+ ),
366
+ { fontSize }
367
+ ).width
368
+
369
+ return (
370
+ maxWidth -
371
+ Math.max(topTextWidth, leftTextWidth, licenseAndOriginUrlWidth) -
372
+ HORIZONTAL_PADDING
373
+ )
374
+ }
375
+
376
+ @computed private get actionButtons(): ActionButtons {
377
+ return new ActionButtons({
378
+ manager: this.manager,
379
+ maxWidth: this.actionButtonsMaxWidth,
380
+ })
381
+ }
382
+
383
+ @computed get height(): number {
384
+ return this.topContentHeight + this.bottomContentHeight
385
+ }
386
+
387
+ base = React.createRef<HTMLDivElement>()
388
+ tooltipTarget: { x: number; y: number } | undefined = undefined
389
+
390
+ @action.bound private onMouseMove(e: MouseEvent): void {
391
+ const cc = this.base.current?.querySelector(".cclogo")
392
+ if (cc && cc.matches(":hover")) {
393
+ const div = this.base.current as HTMLDivElement
394
+ const grapher = div.closest(".GrapherComponent")
395
+ if (grapher) {
396
+ const mouse = getRelativeMouse(grapher, e)
397
+ this.tooltipTarget = { x: mouse.x, y: mouse.y }
398
+ } else console.error("Grapher was falsy")
399
+ } else this.tooltipTarget = undefined
400
+ }
401
+
402
+ override componentDidMount(): void {
403
+ window.addEventListener("mousemove", this.onMouseMove)
404
+ }
405
+
406
+ override componentWillUnmount(): void {
407
+ window.removeEventListener("mousemove", this.onMouseMove)
408
+ }
409
+
410
+ private renderLicense(): React.ReactElement {
411
+ return (
412
+ <div className="license" style={this.licenseAndOriginUrl.htmlStyle}>
413
+ {this.finalUrlText && (
414
+ <>
415
+ <a
416
+ href={this.finalUrl}
417
+ {...(this.manager.isInIFrame && {
418
+ target: "_blank",
419
+ rel: "noopener",
420
+ })}
421
+ >
422
+ {this.finalUrlText}
423
+ </a>{" "}
424
+ |{" "}
425
+ </>
426
+ )}
427
+ <a
428
+ className={this.manager.hasOWIDLogo ? "cclogo" : undefined}
429
+ href={this.licenseUrl}
430
+ style={{ textDecoration: "none" }}
431
+ {...(this.manager.isInIFrame && {
432
+ target: "_blank",
433
+ rel: "noopener",
434
+ })}
435
+ >
436
+ {this.licenseText}
437
+ </a>
438
+ </div>
439
+ )
440
+ }
441
+
442
+ private renderSources(): React.ReactElement | null {
443
+ const sources = new MarkdownTextWrap({
444
+ text: `**Data source:** ${this.sourcesLine}`,
445
+ maxWidth: this.sourcesMaxWidth,
446
+ fontSize: this.sourcesFontSize,
447
+ lineHeight: this.lineHeight,
448
+ })
449
+
450
+ return (
451
+ <p className="sources" style={sources.style}>
452
+ {sources.renderHTML()}
453
+ {" – "}
454
+ <a
455
+ className="learn-more-about-data"
456
+ data-track-note="chart_click_sources"
457
+ tabIndex={0}
458
+ onClick={action((e) => {
459
+ e.stopPropagation()
460
+
461
+ // if embbedded, open the sources modal
462
+ if (
463
+ this.manager.isEmbeddedInAnOwidPage ||
464
+ this.manager.isInIFrame
465
+ ) {
466
+ this.manager.activeModal = GrapherModal.Sources
467
+ return
468
+ }
469
+
470
+ // on data pages, scroll to the "Sources and Processing" section
471
+ // on grapher pages, open the sources modal
472
+ const datapageSectionId =
473
+ DATAPAGE_ABOUT_THIS_DATA_SECTION_ID
474
+ const sourcesElement =
475
+ document.getElementById(datapageSectionId)
476
+ if (sourcesElement && sourcesElement.scrollIntoView) {
477
+ sourcesElement.scrollIntoView({
478
+ behavior: "smooth",
479
+ })
480
+ this.manager.isInFullScreenMode = false
481
+ } else if (sourcesElement) {
482
+ window.location.hash = "#" + datapageSectionId
483
+ this.manager.isInFullScreenMode = false
484
+ } else {
485
+ // on grapher pages, open the sources modal
486
+ this.manager.activeModal = GrapherModal.Sources
487
+ }
488
+ })}
489
+ >
490
+ Learn more about this data
491
+ </a>
492
+ </p>
493
+ )
494
+ }
495
+
496
+ private renderNote(): React.ReactElement {
497
+ return (
498
+ <p className="note" style={this.note.style}>
499
+ {this.note.renderHTML()}
500
+ </p>
501
+ )
502
+ }
503
+
504
+ private renderVerticalSpace(): React.ReactElement {
505
+ return (
506
+ <div
507
+ style={{
508
+ height: this.verticalPadding,
509
+ width: "100%",
510
+ }}
511
+ />
512
+ )
513
+ }
514
+
515
+ @computed private get topContentHeight(): number {
516
+ const { sources, note } = this
517
+
518
+ const renderSources = this.useFullWidthSources
519
+ const renderNote = this.showNote && this.useFullWidthNote
520
+
521
+ if (!renderSources && !renderNote) return 0
522
+
523
+ return (
524
+ (renderSources ? sources.height : 0) +
525
+ (renderSources && renderNote ? this.verticalPadding : 0) +
526
+ (renderNote ? note.height : 0) +
527
+ this.verticalPadding
528
+ )
529
+ }
530
+
531
+ // renders the content above the action buttons
532
+ // make sure to keep this.topContentHeight in sync if you edit this method
533
+ private renderTopContent(): React.ReactElement | null {
534
+ const renderSources = this.useFullWidthSources
535
+ const renderNote = this.showNote && this.useFullWidthNote
536
+ const renderLicense = this.showLicenseNextToSources
537
+
538
+ if (!renderSources && !renderNote) return null
539
+
540
+ return (
541
+ <>
542
+ <div className="SourcesFooterHTMLTop">
543
+ {renderSources && (
544
+ <div className="SourcesAndLicense">
545
+ {this.renderSources()}
546
+ {renderLicense && this.renderLicense()}
547
+ </div>
548
+ )}
549
+ {renderSources && renderNote && this.renderVerticalSpace()}
550
+ {renderNote && this.renderNote()}
551
+ </div>
552
+ {this.renderVerticalSpace()}
553
+ </>
554
+ )
555
+ }
556
+
557
+ @computed private get bottomContentHeight(): number {
558
+ const { actionButtons, sources, note } = this
559
+
560
+ const renderSources = !this.useFullWidthSources
561
+ const renderNote = this.showNote && !this.useFullWidthNote
562
+ const renderLicense = !this.showLicenseNextToSources
563
+ const renderPadding = (renderSources || renderNote) && renderLicense
564
+
565
+ const textHeight =
566
+ (renderSources ? sources.height : renderNote ? note.height : 0) +
567
+ (renderPadding ? this.verticalPadding : 0) +
568
+ (renderLicense ? this.licenseAndOriginUrl.height : 0)
569
+
570
+ return Math.max(textHeight, actionButtons.height)
571
+ }
572
+
573
+ // renders the action buttons and the content next to it
574
+ // make sure to keep this.bottomContentHeight in sync if you edit this method
575
+ private renderBottomContent(): React.ReactElement {
576
+ const renderSources = !this.useFullWidthSources
577
+ const renderNote = this.showNote && !this.useFullWidthNote
578
+ const renderLicense = !this.showLicenseNextToSources
579
+ const renderPadding = (renderSources || renderNote) && renderLicense
580
+
581
+ const licenseOnly = !renderSources && !renderNote && renderLicense
582
+ const noteOnly = !renderSources && renderNote && !renderLicense
583
+
584
+ // center text next to the action buttons if it's only one or two lines
585
+ const style = {
586
+ alignItems:
587
+ licenseOnly || (noteOnly && this.note.htmlLines.length <= 2)
588
+ ? "center"
589
+ : "flex-end",
590
+ }
591
+
592
+ return (
593
+ <div className="SourcesFooterHTMLBottom" style={style}>
594
+ <div>
595
+ {renderSources
596
+ ? this.renderSources()
597
+ : renderNote
598
+ ? this.renderNote()
599
+ : null}
600
+ {renderPadding && this.renderVerticalSpace()}
601
+ {renderLicense && this.renderLicense()}
602
+ </div>
603
+ <ActionButtons
604
+ manager={this.manager}
605
+ maxWidth={this.actionButtonsMaxWidth}
606
+ />
607
+ </div>
608
+ )
609
+ }
610
+
611
+ override render(): React.ReactElement {
612
+ const { tooltipTarget } = this
613
+
614
+ return (
615
+ <footer
616
+ className="SourcesFooterHTML"
617
+ style={{
618
+ padding: `0 ${GRAPHER_FRAME_PADDING_HORIZONTAL}px`,
619
+ }}
620
+ ref={this.base}
621
+ >
622
+ {this.renderTopContent()}
623
+ {this.renderBottomContent()}
624
+ {tooltipTarget && (
625
+ <Tooltip
626
+ id="footer"
627
+ tooltipManager={this.manager}
628
+ x={tooltipTarget.x}
629
+ y={tooltipTarget.y}
630
+ style={{
631
+ textAlign: "left",
632
+ maxWidth: "304px",
633
+ whiteSpace: "inherit",
634
+ fontSize: "14px",
635
+ padding: "0",
636
+ lineHeight: "21px",
637
+ fontWeight: 400,
638
+ letterSpacing: "0.01em",
639
+ }}
640
+ >
641
+ <p>
642
+ {this.manager.branding?.licenseTooltip ??
643
+ "These charts are licensed under Creative Commons; you are free to use, share, and adapt this material. Click through for more information. Please bear in mind that the underlying source data might be subject to different license terms from third-party authors."}
644
+ </p>
645
+ </Tooltip>
646
+ )}
647
+ </footer>
648
+ )
649
+ }
650
+ }
651
+
652
+ @observer
653
+ export class Footer extends AbstractFooter<FooterProps> {}
654
+
655
+ interface StaticFooterProps extends FooterProps {
656
+ targetX: number
657
+ targetY: number
658
+ }
659
+
660
+ @observer
661
+ export class StaticFooter extends AbstractFooter<StaticFooterProps> {
662
+ override verticalPadding = 4.5
663
+
664
+ constructor(props: StaticFooterProps) {
665
+ super(props)
666
+
667
+ makeObservable(this)
668
+ }
669
+
670
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
671
+ override componentDidMount(): void {}
672
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
673
+ override componentWillUnmount(): void {}
674
+
675
+ protected override get hideOriginUrl(): boolean {
676
+ return !!this.manager.hideOriginUrl || !!this.manager.isStaticAndSmall
677
+ }
678
+
679
+ @computed private get textColor(): string {
680
+ return GRAPHER_LIGHT_TEXT
681
+ }
682
+
683
+ protected override get showLicenseNextToSources(): boolean {
684
+ return (
685
+ this.maxWidth - this.sources.width - HORIZONTAL_PADDING >
686
+ this.licenseAndOriginUrl.width
687
+ )
688
+ }
689
+
690
+ protected override get finalUrlText(): string | undefined {
691
+ const { correctedUrlText, licenseText, fontSize, maxWidth } = this
692
+
693
+ if (this.hideOriginUrl) return undefined
694
+
695
+ if (!correctedUrlText) return undefined
696
+
697
+ const licenseAndOriginUrlText = Footer.constructLicenseAndOriginUrlText(
698
+ correctedUrlText,
699
+ licenseText
700
+ )
701
+ const licenseAndOriginUrlWidth = Bounds.forText(
702
+ licenseAndOriginUrlText,
703
+ { fontSize }
704
+ ).width
705
+
706
+ // If the URL is too long, don't show it
707
+ if (licenseAndOriginUrlWidth > maxWidth) return undefined
708
+
709
+ return correctedUrlText
710
+ }
711
+
712
+ protected override get licenseAndOriginUrlText(): string {
713
+ const { finalUrl, finalUrlText, licenseText, licenseUrl, textColor } =
714
+ this
715
+ const linkStyle = `fill: ${textColor};`
716
+ const targetAttr = this.manager.isInIFrame
717
+ ? ' target="_blank" rel="noopener"'
718
+ : ""
719
+ const licenseSvg = `<a style="${linkStyle}" href="${licenseUrl}"${targetAttr}>${licenseText}</a>`
720
+ if (!finalUrlText) return licenseSvg
721
+ const originUrlSvg = `<a style="${linkStyle}" href="${finalUrl}"${targetAttr}>${finalUrlText}</a>`
722
+ return [originUrlSvg, licenseSvg].join(" | ")
723
+ }
724
+
725
+ protected override get sourcesText(): string {
726
+ return `**Data source:** ${this.sourcesLine}`
727
+ }
728
+
729
+ protected override get fontSize(): number {
730
+ if (this.manager.isStaticAndSmall) return 14
731
+ return this.useBaseFontSize
732
+ ? Math.round((13 / BASE_FONT_SIZE) * this.baseFontSize)
733
+ : 13
734
+ }
735
+
736
+ protected override get sourcesFontSize(): number {
737
+ return this.fontSize
738
+ }
739
+
740
+ protected override get sourcesMaxWidth(): number {
741
+ return this.maxWidth
742
+ }
743
+
744
+ protected override get noteMaxWidth(): number {
745
+ return this.maxWidth
746
+ }
747
+
748
+ protected override get licenseAndOriginUrlMaxWidth(): number {
749
+ return this.maxWidth
750
+ }
751
+
752
+ override get height(): number {
753
+ return (
754
+ this.sources.height +
755
+ (this.showNote ? this.note.height + this.verticalPadding : 0) +
756
+ (this.showLicenseNextToSources
757
+ ? 0
758
+ : this.licenseAndOriginUrl.height + this.verticalPadding)
759
+ )
760
+ }
761
+
762
+ override render(): React.ReactElement {
763
+ const {
764
+ sources,
765
+ note,
766
+ licenseAndOriginUrl,
767
+ showLicenseNextToSources,
768
+ maxWidth,
769
+ } = this
770
+ const { targetX, targetY } = this.props
771
+
772
+ return (
773
+ <g
774
+ id={makeIdForHumanConsumption(GRAPHER_FOOTER_CLASS)}
775
+ className="SourcesFooter"
776
+ style={{ fill: this.textColor }}
777
+ >
778
+ {sources.renderSVG(targetX, targetY, {
779
+ id: makeIdForHumanConsumption("sources"),
780
+ })}
781
+ {this.showNote &&
782
+ note.renderSVG(
783
+ targetX,
784
+ targetY + sources.height + this.verticalPadding,
785
+ {
786
+ id: makeIdForHumanConsumption("note"),
787
+ detailsMarker: this.manager.detailsMarkerInSvg,
788
+ }
789
+ )}
790
+ {showLicenseNextToSources
791
+ ? licenseAndOriginUrl.renderSVG(
792
+ targetX + maxWidth - licenseAndOriginUrl.width,
793
+ targetY,
794
+ { id: makeIdForHumanConsumption("origin-url") }
795
+ )
796
+ : licenseAndOriginUrl.renderSVG(
797
+ targetX,
798
+ targetY +
799
+ sources.height +
800
+ (this.showNote
801
+ ? note.height + this.verticalPadding
802
+ : 0) +
803
+ this.verticalPadding,
804
+ { id: makeIdForHumanConsumption("origin-url") }
805
+ )}
806
+ </g>
807
+ )
808
+ }
809
+ }