@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,18 @@
1
+ .overlay-header {
2
+ --padding: var(--modal-padding, 16px);
3
+
4
+ display: flex;
5
+ justify-content: space-between;
6
+ align-items: center;
7
+ padding: var(--padding) var(--padding) 16px;
8
+
9
+ .title {
10
+ @include grapher_h5-black-caps;
11
+ color: $light-text;
12
+ }
13
+
14
+ .close-button {
15
+ margin-left: 8px;
16
+ flex-shrink: 0;
17
+ }
18
+ }
@@ -0,0 +1,29 @@
1
+ import * as React from "react"
2
+ import cx from "classnames"
3
+ import { CloseButton } from "./closeButton/CloseButton.js"
4
+
5
+ export function OverlayHeader({
6
+ title,
7
+ onTitleClick,
8
+ onDismiss,
9
+ className,
10
+ }: {
11
+ title: string
12
+ onTitleClick?: () => void
13
+ onDismiss?: () => void
14
+ className?: string
15
+ }): React.ReactElement {
16
+ return (
17
+ <div className={cx("overlay-header", className)}>
18
+ <h2
19
+ className={cx("title", {
20
+ clickable: !!onTitleClick,
21
+ })}
22
+ onClick={onTitleClick}
23
+ >
24
+ {title}
25
+ </h2>
26
+ {onDismiss && <CloseButton onClick={onDismiss} />}
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,69 @@
1
+ @use "sass:math";
2
+
3
+ .radio {
4
+ $radio-size: 16px;
5
+
6
+ $light-stroke: $gray-30;
7
+ $hover-stroke: $blue-30;
8
+ $active-fill: $blue-50;
9
+
10
+ position: relative;
11
+
12
+ label {
13
+ margin: 0; // style leak in admin
14
+ cursor: pointer;
15
+ }
16
+
17
+ input {
18
+ position: absolute;
19
+ opacity: 0;
20
+ left: 0;
21
+ pointer-events: none;
22
+ }
23
+
24
+ .outer {
25
+ position: absolute;
26
+ left: 0;
27
+ top: 0;
28
+ width: $radio-size;
29
+ height: $radio-size;
30
+
31
+ background: white;
32
+ pointer-events: none;
33
+ border-radius: 50%;
34
+ border: 1px solid $light-stroke;
35
+
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+
40
+ .inner {
41
+ width: math.div($radio-size, 2);
42
+ height: math.div($radio-size, 2);
43
+ background-color: $active-fill;
44
+ border-radius: 50%;
45
+ }
46
+ }
47
+
48
+ input:focus-visible + .outer {
49
+ outline: 2px solid $controls-color;
50
+ }
51
+
52
+ input:checked + .outer {
53
+ border-color: $hover-stroke;
54
+ }
55
+
56
+ .label {
57
+ @include grapher_label-2-regular;
58
+ padding-left: $radio-size + 8px;
59
+ cursor: pointer;
60
+ user-select: none;
61
+ color: $dark-text;
62
+ }
63
+
64
+ &:hover {
65
+ input:not(:checked) + .outer {
66
+ border-color: $hover-stroke;
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,42 @@
1
+ import * as React from "react"
2
+ import cx from "classnames"
3
+
4
+ export const RadioButton = ({
5
+ className,
6
+ checked,
7
+ onChange,
8
+ label,
9
+ group,
10
+ disabled,
11
+ id,
12
+ "data-test": testHook,
13
+ }: {
14
+ className?: string
15
+ checked: boolean
16
+ onChange: React.ChangeEventHandler<HTMLInputElement>
17
+ label: React.ReactNode
18
+ group?: string
19
+ disabled?: boolean
20
+ id?: string
21
+ "data-test"?: string
22
+ }) => {
23
+ return (
24
+ <div className={cx("radio", className)}>
25
+ <label>
26
+ <input
27
+ type="radio"
28
+ id={id}
29
+ data-test={testHook}
30
+ disabled={disabled}
31
+ name={group}
32
+ checked={checked}
33
+ onChange={onChange}
34
+ />
35
+ <div className="outer">
36
+ {checked && <div className="inner" />}
37
+ </div>
38
+ <div className="label">{label}</div>
39
+ </label>
40
+ </div>
41
+ )
42
+ }
@@ -0,0 +1,89 @@
1
+ import * as React from "react"
2
+ import Markdown, {
3
+ type Options as MarkdownOptions,
4
+ type Components as MarkdownComponents,
5
+ } from "react-markdown"
6
+ import { remarkPlainLinks } from "./markdown/remarkPlainLinks.js"
7
+ import { visit } from "unist-util-visit"
8
+ import type { Plugin } from "unified"
9
+ import type { Root } from "hast"
10
+
11
+ type SimpleMarkdownTextProps = {
12
+ text: string
13
+ useParagraphs?: boolean // by default, text is wrapped in <p> tags
14
+ }
15
+
16
+ const transformDodLinks: Plugin<[], Root> = () => {
17
+ return function (tree) {
18
+ visit(tree, "element", function (node) {
19
+ if (node.tagName === "a")
20
+ if (
21
+ node.properties &&
22
+ typeof node.properties.href === "string" &&
23
+ node.properties.href.startsWith("#dod:")
24
+ ) {
25
+ // use a regex to split the #dod: part from the term of the href. Use a named capture group
26
+ // to capture the term
27
+ const match = node.properties.href.match(/#dod:(?<term>.+)/)
28
+ if (match) {
29
+ node.tagName = "span"
30
+ node.properties.class = "dod-span"
31
+ node.properties["data-id"] = match.groups?.term
32
+ node.properties["aria-expanded"] = "false"
33
+ node.properties["tabindex"] = 0
34
+ delete node.properties.href
35
+ }
36
+ }
37
+ })
38
+ }
39
+ }
40
+
41
+ // NOTE: We currently don't need to render markdown in React. We should be able
42
+ // to render it only during baking/on the server and pass the HTML as string.
43
+ // This way we could reduce the bundle size by not including a markdown library
44
+ // and improve performance.
45
+ export class SimpleMarkdownText extends React.Component<SimpleMarkdownTextProps> {
46
+ get useParagraphs(): boolean {
47
+ return this.props.useParagraphs ?? true
48
+ }
49
+
50
+ get markdownCustomComponents(): MarkdownComponents | undefined {
51
+ if (!this.useParagraphs) {
52
+ // "unwrap" the text by rendering <p /> as a react fragment
53
+ return {
54
+ p: ({ children }) => (
55
+ <React.Fragment>{children}</React.Fragment>
56
+ ),
57
+ }
58
+ }
59
+
60
+ return undefined
61
+ }
62
+
63
+ override render(): React.ReactElement | null {
64
+ const options: Omit<MarkdownOptions, "children"> = {
65
+ rehypePlugins: [transformDodLinks],
66
+ remarkPlugins: [remarkPlainLinks],
67
+ components: this.markdownCustomComponents,
68
+ }
69
+ return <Markdown {...options}>{this.props.text}</Markdown>
70
+ }
71
+ }
72
+
73
+ // TODO: remove this component once all backported indicators
74
+ // etc have switched from HTML to markdown for their sources
75
+ export const HtmlOrSimpleMarkdownText = (props: {
76
+ text: string
77
+ }): React.ReactElement => {
78
+ // check the text for closing a, li or p tags. If
79
+ // one is found, render using dangerouslySetInnerHTML,
80
+ // otherwise use SimpleMarkdownText
81
+ const { text } = props
82
+ const htmlRegex = /<\/(a|li|p)>/
83
+ const match = text.match(htmlRegex)
84
+ if (match) {
85
+ return <span dangerouslySetInnerHTML={{ __html: text }} />
86
+ } else {
87
+ return <SimpleMarkdownText text={text} />
88
+ }
89
+ }
@@ -0,0 +1,17 @@
1
+ .text-input {
2
+ @include body-3-medium;
3
+ padding: 8px 16px;
4
+ color: $blue-90;
5
+ border: 1px solid $blue-20;
6
+ border-radius: 0;
7
+ height: 40px;
8
+
9
+ &:focus {
10
+ outline: none;
11
+ border: 1px solid $blue-40;
12
+ }
13
+
14
+ &::placeholder {
15
+ color: $blue-40;
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+ import { forwardRef } from "react"
3
+ import cx from "classnames"
4
+
5
+ export type TextInputProps = React.InputHTMLAttributes<HTMLInputElement> & {
6
+ className?: string
7
+ }
8
+
9
+ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
10
+ function TextInput({ className, ...props }: TextInputProps, ref) {
11
+ return (
12
+ <input
13
+ className={cx("text-input", className)}
14
+ ref={ref}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+ )
@@ -0,0 +1,361 @@
1
+ import * as _ from "lodash-es"
2
+ import * as R from "remeda"
3
+ import {
4
+ stripHTML,
5
+ Bounds,
6
+ FontFamily,
7
+ VerticalAlign,
8
+ } from "../../utils/index.js"
9
+ import { computed, makeObservable } from "mobx"
10
+ import * as React from "react"
11
+ import { Fragment, joinFragments, splitIntoFragments } from "./TextWrapUtils"
12
+ import { match } from "ts-pattern"
13
+
14
+ declare type FontSize = number
15
+
16
+ interface TextWrapProps {
17
+ text: string
18
+ maxWidth: number
19
+ lineHeight?: number
20
+ fontSize: FontSize
21
+ fontWeight?: number
22
+ separators?: string[]
23
+ rawHtml?: boolean
24
+ verticalAlign?: VerticalAlign
25
+ }
26
+
27
+ interface WrapLine {
28
+ text: string
29
+ width: number
30
+ height: number
31
+ }
32
+
33
+ interface OpenHtmlTag {
34
+ tag: string // e.g. "a" for an <a> tag, or "span" for a <span> tag
35
+ fullTag: string // e.g. "<a href='https://ourworldindata.org'>"
36
+ }
37
+
38
+ interface SVGRenderProps {
39
+ textProps?: React.SVGProps<SVGTextElement>
40
+ id?: string
41
+ }
42
+
43
+ const HTML_OPENING_CLOSING_TAG_REGEX = /<(\/?)([A-Za-z]+)( [^<>]*)?>/g
44
+
45
+ function startsWithNewline(text: string): boolean {
46
+ return /^\n/.test(text)
47
+ }
48
+
49
+ /**
50
+ * Shortens text to fit within a target width using binary search.
51
+ * Returns the longest substring that fits within the target width.
52
+ */
53
+ export const shortenForTargetWidth = (
54
+ text: string,
55
+ targetWidth: number,
56
+ fontSettings: {
57
+ fontSize?: number
58
+ fontWeight?: number
59
+ fontFamily?: FontFamily
60
+ } = {}
61
+ ): string => {
62
+ // Use binary search to find the largest substring that fits within the target width
63
+ let low = 0
64
+ let high = text.length
65
+ while (low <= high) {
66
+ const mid = (high + low) >> 1
67
+ const bounds = Bounds.forText(text.slice(0, mid), fontSettings)
68
+ if (bounds.width < targetWidth) {
69
+ low = mid + 1
70
+ } else {
71
+ high = mid - 1
72
+ }
73
+ }
74
+ return text.slice(0, low - 1)
75
+ }
76
+
77
+ /** Shortens text to fit within the target width and appends an ellipsis (…) */
78
+ export const shortenWithEllipsis = (
79
+ text: string,
80
+ targetWidth: number,
81
+ fontSettings: {
82
+ fontSize?: number
83
+ fontWeight?: number
84
+ fontFamily?: FontFamily
85
+ } = {}
86
+ ): string => {
87
+ const ellipsis = "…"
88
+ const ellipsisWidth = Bounds.forText(ellipsis, fontSettings).width
89
+ const truncatedText = shortenForTargetWidth(
90
+ text,
91
+ targetWidth - ellipsisWidth,
92
+ fontSettings
93
+ )
94
+ return `${truncatedText}${ellipsis}`
95
+ }
96
+
97
+ export class TextWrap {
98
+ props: TextWrapProps
99
+ constructor(props: TextWrapProps) {
100
+ makeObservable(this)
101
+ this.props = props
102
+ }
103
+
104
+ @computed get maxWidth(): number {
105
+ return this.props.maxWidth ?? Infinity
106
+ }
107
+ @computed get lineHeight(): number {
108
+ return this.props.lineHeight ?? 1.1
109
+ }
110
+ @computed get fontSize(): FontSize {
111
+ return this.props.fontSize ?? 1
112
+ }
113
+ @computed get fontWeight(): number | undefined {
114
+ return this.props.fontWeight
115
+ }
116
+ @computed get verticalAlign(): VerticalAlign {
117
+ return this.props.verticalAlign ?? VerticalAlign.bottom
118
+ }
119
+ @computed get text(): string {
120
+ return this.props.text
121
+ }
122
+ @computed get separators(): string[] {
123
+ return this.props.separators ?? [" "]
124
+ }
125
+
126
+ // We need to take care that HTML tags are not split across lines.
127
+ // Instead, we want every line to have opening and closing tags for all tags that appear.
128
+ // This is so we don't produce invalid HTML.
129
+ processHtmlTags(lines: WrapLine[]): WrapLine[] {
130
+ const currentlyOpenTags: OpenHtmlTag[] = []
131
+ for (const line of lines) {
132
+ // Prepend any still-open tags to the start of the line
133
+ const prependOpenTags = currentlyOpenTags
134
+ .map((t) => t.fullTag)
135
+ .join("")
136
+
137
+ const tagMatches = line.text.matchAll(
138
+ HTML_OPENING_CLOSING_TAG_REGEX
139
+ )
140
+ for (const tag of tagMatches) {
141
+ const isOpeningTag = tag[1] !== "/"
142
+ if (isOpeningTag) {
143
+ currentlyOpenTags.push({
144
+ tag: tag[2],
145
+ fullTag: tag[0],
146
+ })
147
+ } else {
148
+ if (
149
+ !currentlyOpenTags.length ||
150
+ currentlyOpenTags.at(-1)?.tag !== tag[2]
151
+ ) {
152
+ throw new Error(
153
+ "TextWrap: Opening and closing HTML tags do not match"
154
+ )
155
+ }
156
+ currentlyOpenTags.pop()
157
+ }
158
+ }
159
+
160
+ // Append any unclosed tags to the end of the line
161
+ const appendCloseTags = currentlyOpenTags
162
+ .toReversed()
163
+ .map((t) => `</${t.tag}>`)
164
+ .join("")
165
+ line.text = prependOpenTags + line.text + appendCloseTags
166
+ }
167
+ return lines
168
+ }
169
+
170
+ @computed get lines(): WrapLine[] {
171
+ const { text, separators, maxWidth, fontSize, fontWeight } = this
172
+
173
+ // Prepend spaces so that the string is also split before newline characters
174
+ // See startsWithNewline
175
+ const fragments = splitIntoFragments(
176
+ text.replace(/\n/g, " \n"),
177
+ separators
178
+ )
179
+
180
+ const lines: WrapLine[] = []
181
+
182
+ let line: Fragment[] = []
183
+ let lineBounds = Bounds.empty()
184
+
185
+ fragments.forEach((fragment) => {
186
+ const nextLine = line.concat([fragment])
187
+
188
+ // Strip HTML if a raw string is passed
189
+ const text = this.props.rawHtml
190
+ ? stripHTML(joinFragments(nextLine))
191
+ : joinFragments(nextLine)
192
+
193
+ const nextBounds = Bounds.forText(text, {
194
+ fontSize,
195
+ fontWeight,
196
+ })
197
+
198
+ if (
199
+ startsWithNewline(fragment.text) ||
200
+ (nextBounds.width + 10 > maxWidth && line.length >= 1)
201
+ ) {
202
+ // Introduce a newline _before_ this word
203
+ lines.push({
204
+ text: joinFragments(line),
205
+ width: lineBounds.width,
206
+ height: lineBounds.height,
207
+ })
208
+ // ... and start a new line with this word (with a potential leading newline stripped)
209
+ const wordWithoutNewline = fragment.text.replace(/^\n/, "")
210
+ line = [
211
+ {
212
+ text: wordWithoutNewline,
213
+ separator: fragment.separator,
214
+ },
215
+ ]
216
+ lineBounds = Bounds.forText(wordWithoutNewline, {
217
+ fontSize,
218
+ fontWeight,
219
+ })
220
+ } else {
221
+ line = nextLine
222
+ lineBounds = nextBounds
223
+ }
224
+ })
225
+
226
+ // Push the last line
227
+ if (line.length > 0)
228
+ lines.push({
229
+ text: joinFragments(line),
230
+ width: lineBounds.width,
231
+ height: lineBounds.height,
232
+ })
233
+
234
+ // Process HTML to ensure that each opening tag has a matching closing tag _in each line_
235
+ if (this.props.rawHtml) return this.processHtmlTags(lines)
236
+ else return lines
237
+ }
238
+
239
+ @computed get lineCount(): number {
240
+ return this.lines.length
241
+ }
242
+
243
+ @computed get singleLineHeight(): number {
244
+ return this.fontSize * this.lineHeight
245
+ }
246
+
247
+ @computed get height(): number {
248
+ if (this.lineCount === 0) return 0
249
+ return this.lineCount * this.singleLineHeight
250
+ }
251
+
252
+ @computed get width(): number {
253
+ return _.max(this.lines.map((l) => l.width)) ?? 0
254
+ }
255
+
256
+ @computed get lastLineWidth(): number {
257
+ return R.last(this.lines)?.width ?? 0
258
+ }
259
+
260
+ @computed get htmlStyle(): any {
261
+ const { fontSize, fontWeight, lineHeight } = this
262
+ return {
263
+ fontSize: fontSize.toFixed(2) + "px",
264
+ fontWeight: fontWeight,
265
+ lineHeight: lineHeight,
266
+ overflowY: "visible",
267
+ }
268
+ }
269
+
270
+ getPositionForSvgRendering(x: number, y: number): [number, number] {
271
+ const { lines, fontSize, lineHeight, height, verticalAlign } = this
272
+
273
+ // Magic number set through experimentation.
274
+ // The HTML and SVG renderers need to position lines identically.
275
+ // This number was tweaked until the overlaid HTML and SVG outputs
276
+ // overlap.
277
+ const HEIGHT_CORRECTION_FACTOR = 0.74
278
+
279
+ const textHeight = _.max(lines.map((line) => line.height)) ?? 0
280
+ const correctedTextHeight = textHeight * HEIGHT_CORRECTION_FACTOR
281
+ const containerHeight = lineHeight * fontSize
282
+ const correctedY =
283
+ y + (containerHeight - (containerHeight - correctedTextHeight) / 2)
284
+
285
+ const renderY = match(verticalAlign)
286
+ .with(VerticalAlign.top, () => correctedY - height)
287
+ .with(VerticalAlign.middle, () => correctedY - height / 2)
288
+ .with(VerticalAlign.bottom, () => correctedY)
289
+ .exhaustive()
290
+
291
+ return [x, renderY]
292
+ }
293
+
294
+ renderHTML(): React.ReactElement | null {
295
+ const { props, lines } = this
296
+
297
+ if (lines.length === 0) return null
298
+
299
+ return (
300
+ <span>
301
+ {lines.map((line, index) => {
302
+ const content = props.rawHtml ? (
303
+ <span
304
+ dangerouslySetInnerHTML={{
305
+ __html: line.text,
306
+ }}
307
+ />
308
+ ) : (
309
+ <span>{line.text}</span>
310
+ )
311
+ return (
312
+ <React.Fragment key={index}>
313
+ {content}
314
+ <br />
315
+ </React.Fragment>
316
+ )
317
+ })}
318
+ </span>
319
+ )
320
+ }
321
+
322
+ renderSVG(x: number, y: number, options: SVGRenderProps = {}) {
323
+ const { props, lines, fontSize, fontWeight } = this
324
+
325
+ if (lines.length === 0) return <></>
326
+
327
+ const [renderX, renderY] = this.getPositionForSvgRendering(x, y)
328
+
329
+ return (
330
+ <text
331
+ id={options.id}
332
+ fontSize={fontSize.toFixed(2)}
333
+ fontWeight={fontWeight}
334
+ x={renderX.toFixed(1)}
335
+ y={renderY.toFixed(1)}
336
+ {...options.textProps}
337
+ >
338
+ {lines.map((line, i) => {
339
+ const x = renderX
340
+ const y = renderY + this.singleLineHeight * i
341
+
342
+ if (props.rawHtml)
343
+ return (
344
+ <tspan
345
+ key={i}
346
+ x={x}
347
+ y={y}
348
+ dangerouslySetInnerHTML={{ __html: line.text }}
349
+ />
350
+ )
351
+ else
352
+ return (
353
+ <tspan key={i} x={x} y={y}>
354
+ {line.text}
355
+ </tspan>
356
+ )
357
+ })}
358
+ </text>
359
+ )
360
+ }
361
+ }
@@ -0,0 +1,32 @@
1
+ import * as _ from "lodash-es"
2
+
3
+ export type Fragment = {
4
+ text: string
5
+ separator: string
6
+ }
7
+
8
+ export function splitIntoFragments(
9
+ text: string,
10
+ separators = [" "]
11
+ ): Fragment[] {
12
+ if (_.isEmpty(text)) return []
13
+ const fragments: Fragment[] = []
14
+ let currText = ""
15
+ for (const char of text) {
16
+ if (separators.includes(char)) {
17
+ fragments.push({ text: currText, separator: char })
18
+ currText = ""
19
+ } else {
20
+ currText += char
21
+ }
22
+ }
23
+ fragments.push({ text: currText, separator: "" })
24
+ return fragments
25
+ }
26
+
27
+ export function joinFragments(fragments: Fragment[]): string {
28
+ return fragments
29
+ .map(({ text, separator }) => text + separator)
30
+ .join("")
31
+ .trim()
32
+ }