@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,304 @@
1
+ import { observer } from "mobx-react"
2
+ import * as React from "react"
3
+ import { computed, action, makeObservable } from "mobx"
4
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
5
+ import {
6
+ faCode,
7
+ faShareAlt,
8
+ faLink,
9
+ faEdit,
10
+ faPanorama,
11
+ faImage,
12
+ } from "@fortawesome/free-solid-svg-icons"
13
+ import {
14
+ canWriteToClipboard,
15
+ copyToClipboard,
16
+ isAndroid,
17
+ isIOS,
18
+ } from "../../utils/index.js"
19
+ import { GrapherModal } from "../core/GrapherConstants"
20
+ import { isTargetOutsideElement } from "../chart/ChartUtils.js"
21
+ import { GrapherRasterizeFn } from "../captionedChart/StaticChartRasterizer.js"
22
+
23
+ export interface ShareMenuManager {
24
+ slug?: string
25
+ currentTitle?: string
26
+ canonicalUrl?: string
27
+ editUrl?: string
28
+ createNarrativeChartUrl?: string
29
+ activeModal?: GrapherModal
30
+ rasterize: GrapherRasterizeFn
31
+ }
32
+
33
+ interface ShareMenuProps {
34
+ manager: ShareMenuManager
35
+ onDismiss?: () => void
36
+ right?: number
37
+ }
38
+
39
+ interface ShareMenuState {
40
+ canWriteToClipboard: boolean
41
+ copiedLink: boolean
42
+ copiedPng: boolean
43
+ }
44
+
45
+ type ShareApiManager = Pick<ShareMenuManager, "canonicalUrl" | "currentTitle">
46
+
47
+ const getShareData = (manager: ShareApiManager): ShareData | undefined => {
48
+ if (!manager.canonicalUrl) return undefined
49
+
50
+ return {
51
+ title: manager.currentTitle ?? "",
52
+ url: manager.canonicalUrl,
53
+ }
54
+ }
55
+
56
+ const canUseShareApi = (manager: ShareApiManager): boolean => {
57
+ const shareData = getShareData(manager)
58
+ if (!shareData) return false
59
+
60
+ return (
61
+ "share" in navigator &&
62
+ "canShare" in navigator &&
63
+ navigator.canShare(shareData)
64
+ )
65
+ }
66
+
67
+ // On mobile OSs, the system-level share API does a way better job of providing
68
+ // relevant options to the user than our own <ShareMenu> does - for example,
69
+ // quick access to messaging apps, the user's frequent contacts, etc.
70
+ // So, on Android and iOS, we want to just show the system-level share dialog
71
+ // immediately when the user clicks the share button, rather than showing our
72
+ // own menu.
73
+ // See https://github.com/owid/owid-grapher/issues/3446
74
+ // -@marcelgerber, 2024-04-24
75
+ export const shouldShareUsingShareApi = (manager: ShareApiManager): boolean =>
76
+ (isAndroid() || isIOS()) && canUseShareApi(manager)
77
+
78
+ export const shareUsingShareApi = async (
79
+ manager: ShareApiManager
80
+ ): Promise<void> => {
81
+ if (!navigator.share) return
82
+
83
+ const shareData = getShareData(manager)
84
+ if (!shareData) return
85
+
86
+ try {
87
+ await navigator.share(shareData)
88
+ } catch (err) {
89
+ console.error("couldn't share using navigator.share", err)
90
+ }
91
+ }
92
+
93
+ @observer
94
+ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {
95
+ private menuRef = React.createRef<HTMLDivElement>()
96
+
97
+ constructor(props: ShareMenuProps) {
98
+ super(props)
99
+
100
+ makeObservable(this)
101
+
102
+ this.state = {
103
+ canWriteToClipboard: false,
104
+ copiedLink: false,
105
+ copiedPng: false,
106
+ }
107
+ }
108
+
109
+ static shouldShow(manager: ShareMenuManager): boolean {
110
+ const test = new ShareMenu({ manager })
111
+ return test.showShareMenu
112
+ }
113
+
114
+ @computed get manager(): ShareMenuManager {
115
+ return this.props.manager
116
+ }
117
+
118
+ @computed get title(): string {
119
+ return this.manager.currentTitle ?? ""
120
+ }
121
+
122
+ @computed get showShareMenu(): boolean {
123
+ return !!this.canonicalUrl || !!this.manager.editUrl
124
+ }
125
+
126
+ @computed get canonicalUrl(): string | undefined {
127
+ return this.manager.canonicalUrl
128
+ }
129
+
130
+ @action.bound dismiss(): void {
131
+ this.props.onDismiss?.()
132
+ }
133
+
134
+ @action.bound onClickSomewhere(e: Event): void {
135
+ if (
136
+ this.menuRef.current &&
137
+ e.target &&
138
+ isTargetOutsideElement(e.target, this.menuRef.current)
139
+ ) {
140
+ this.dismiss()
141
+ }
142
+ }
143
+
144
+ override componentDidMount(): void {
145
+ document.addEventListener("click", this.onClickSomewhere)
146
+ void canWriteToClipboard().then((canWriteToClipboard) =>
147
+ this.setState({ canWriteToClipboard })
148
+ )
149
+ }
150
+
151
+ override componentWillUnmount(): void {
152
+ document.removeEventListener("click", this.onClickSomewhere)
153
+ }
154
+
155
+ @action.bound onEmbed(e: React.MouseEvent): void {
156
+ const { canonicalUrl, manager } = this
157
+ if (!canonicalUrl) return
158
+ manager.activeModal = GrapherModal.Embed
159
+ this.dismiss()
160
+ e.stopPropagation()
161
+ }
162
+
163
+ @action.bound async onNavigatorShare(): Promise<void> {
164
+ await shareUsingShareApi(this.manager)
165
+ }
166
+
167
+ @action.bound async onCopyUrl(): Promise<void> {
168
+ if (!this.canonicalUrl) return
169
+
170
+ try {
171
+ this.setState({ copiedLink: false, copiedPng: false })
172
+ const success = await copyToClipboard(this.canonicalUrl)
173
+ if (success) {
174
+ this.setState({ copiedLink: true, copiedPng: false })
175
+ } else {
176
+ console.error("Failed to copy link to clipboard")
177
+ }
178
+ } catch (err) {
179
+ console.error(
180
+ "couldn't copy to clipboard using navigator.clipboard",
181
+ err
182
+ )
183
+ }
184
+ }
185
+
186
+ @action.bound async onCopyPng(): Promise<void> {
187
+ const { manager } = this
188
+
189
+ try {
190
+ this.setState({ copiedLink: false, copiedPng: false })
191
+
192
+ const rasterizePromise = manager.rasterize({
193
+ // Don't include DoDs in the static export when copying PNG to clipboard
194
+ includeDetails: false,
195
+ })
196
+
197
+ // Safari needs the clipboard.write call to happen without a delay after the user interaction,
198
+ // so it's important that we pass a promise to ClipboardItem, not await the rasterization first
199
+ // see https://stackoverflow.com/a/68241516/10670163
200
+ await navigator.clipboard.write([
201
+ new ClipboardItem({
202
+ "image/png": rasterizePromise.then(({ blob }) => blob),
203
+ }),
204
+ ])
205
+
206
+ this.setState({ copiedLink: false, copiedPng: true })
207
+ } catch (err) {
208
+ console.error("couldn't copy PNG to clipboard", err)
209
+ }
210
+ }
211
+
212
+ @computed get canUseShareApi(): boolean {
213
+ return canUseShareApi(this.manager)
214
+ }
215
+
216
+ @computed get showCopyPngButton(): boolean {
217
+ return this.state.canWriteToClipboard
218
+ }
219
+
220
+ override render(): React.ReactElement {
221
+ const { canUseShareApi, manager, showCopyPngButton } = this
222
+ const { editUrl, createNarrativeChartUrl } = manager
223
+
224
+ const width = 200
225
+ const right = this.props.right ?? 0
226
+ const style: React.CSSProperties = {
227
+ width,
228
+ right: Math.max(-width * 0.5, -right),
229
+ }
230
+
231
+ return (
232
+ <div className="ShareMenu" ref={this.menuRef} style={style}>
233
+ <h2>Share</h2>
234
+ {this.canonicalUrl && (
235
+ <a
236
+ className="embed"
237
+ title="Embed this interactive visualization in another HTML document, e.g. a blog post or website"
238
+ data-track-note="chart_share_embed"
239
+ onClick={this.onEmbed}
240
+ >
241
+ <FontAwesomeIcon className="icon" icon={faCode} />
242
+ Embed this chart
243
+ </a>
244
+ )}
245
+ {canUseShareApi && (
246
+ <a
247
+ title="Share this visualization with an app on your device"
248
+ data-track-note="chart_share_navigator"
249
+ onClick={this.onNavigatorShare}
250
+ >
251
+ <FontAwesomeIcon className="icon" icon={faShareAlt} />
252
+ Share via&hellip;
253
+ </a>
254
+ )}
255
+ {showCopyPngButton && (
256
+ <a
257
+ title="Copy an image of this chart to the clipboard"
258
+ data-track-note="chart_share_copy_png"
259
+ onClick={this.onCopyPng}
260
+ >
261
+ <FontAwesomeIcon className="icon" icon={faImage} />
262
+ {this.state.copiedPng
263
+ ? "Chart copied!"
264
+ : "Copy chart as image"}
265
+ </a>
266
+ )}
267
+ {this.state.canWriteToClipboard && this.canonicalUrl && (
268
+ <a
269
+ title="Copy a link to this chart to the clipboard"
270
+ data-track-note="chart_share_copylink"
271
+ onClick={this.onCopyUrl}
272
+ >
273
+ <FontAwesomeIcon className="icon" icon={faLink} />
274
+ {this.state.copiedLink
275
+ ? "Link copied!"
276
+ : "Copy link to chart"}
277
+ </a>
278
+ )}
279
+ {editUrl && (
280
+ <a
281
+ title="Edit chart"
282
+ href={editUrl}
283
+ target="_blank"
284
+ rel="noopener"
285
+ >
286
+ <FontAwesomeIcon className="icon" icon={faEdit} />
287
+ Edit
288
+ </a>
289
+ )}
290
+ {createNarrativeChartUrl && (
291
+ <a
292
+ title="Create a narrative chart"
293
+ href={createNarrativeChartUrl}
294
+ target="_blank"
295
+ rel="noopener"
296
+ >
297
+ <FontAwesomeIcon className="icon" icon={faPanorama} />
298
+ Create narrative chart
299
+ </a>
300
+ )}
301
+ </div>
302
+ )
303
+ }
304
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from "react"
2
+ import classnames from "classnames"
3
+ import { IconDefinition } from "@fortawesome/fontawesome-svg-core/index"
4
+ import {
5
+ faSortAlphaUpAlt,
6
+ faSortAlphaDown,
7
+ faSortAmountUpAlt,
8
+ faSortAmountDown,
9
+ } from "@fortawesome/free-solid-svg-icons"
10
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
11
+ import { SortOrder } from "../../types/index.js"
12
+
13
+ export function SortIcon(props: {
14
+ type?: "text" | "numeric"
15
+ isActiveIcon?: boolean
16
+ order: SortOrder
17
+ }): React.ReactElement {
18
+ const type = props.type ?? "numeric"
19
+ const isActiveIcon = props.isActiveIcon ?? false
20
+
21
+ let faIcon: IconDefinition
22
+
23
+ if (type === "text")
24
+ faIcon =
25
+ props.order === SortOrder.desc ? faSortAlphaUpAlt : faSortAlphaDown
26
+ else
27
+ faIcon =
28
+ props.order === SortOrder.desc
29
+ ? faSortAmountDown
30
+ : faSortAmountUpAlt
31
+
32
+ return (
33
+ <span
34
+ className={classnames({ "sort-icon": true, active: isActiveIcon })}
35
+ >
36
+ <FontAwesomeIcon icon={faIcon} />
37
+ </span>
38
+ )
39
+ }
@@ -0,0 +1,263 @@
1
+ /* eslint-disable react/prop-types */
2
+ import { useEffect, useState } from "react"
3
+
4
+ import * as React from "react"
5
+ import classnames from "classnames"
6
+
7
+ type VerticalScrollContainerProps = React.DetailedHTMLProps<
8
+ React.HTMLAttributes<HTMLDivElement>,
9
+ HTMLDivElement
10
+ > & {
11
+ scrollingShadows?: boolean
12
+ scrollLock?: boolean
13
+ contentsId?: string
14
+ }
15
+
16
+ type ReactRef<T> =
17
+ | ((instance: T | null) => void)
18
+ | React.MutableRefObject<T | null>
19
+ | null
20
+
21
+ function useCombinedRefs<T>(...refs: ReactRef<T>[]): React.RefObject<T | null> {
22
+ const targetRef = React.useRef<T>(null)
23
+
24
+ React.useEffect(() => {
25
+ refs.forEach((ref) => {
26
+ if (!ref) return
27
+ if (typeof ref === "function") {
28
+ ref(targetRef.current || null)
29
+ } else {
30
+ ref.current = targetRef.current || null
31
+ }
32
+ })
33
+ }, [refs])
34
+
35
+ return targetRef
36
+ }
37
+
38
+ export const VerticalScrollContainer = React.forwardRef(
39
+ function VerticalScrollContainer(
40
+ props: VerticalScrollContainerProps,
41
+ ref: ReactRef<HTMLDivElement>
42
+ ) {
43
+ let {
44
+ scrollingShadows,
45
+ className,
46
+ children,
47
+ contentsId,
48
+ style,
49
+ scrollLock,
50
+ ...rest
51
+ } = props
52
+
53
+ scrollingShadows ??= true
54
+
55
+ const scrollContainerRef = useCombinedRefs<HTMLDivElement>(ref)
56
+ const [scrollTop, scrollBottom] = useScrollBounds(
57
+ scrollContainerRef,
58
+ contentsId
59
+ )
60
+
61
+ useScrollLock(scrollContainerRef, {
62
+ enable: scrollLock,
63
+ doNotLockIfNoScroll: true,
64
+ })
65
+
66
+ return (
67
+ <div
68
+ className="VerticalScrollContainerShadows"
69
+ style={{
70
+ position: "relative",
71
+ height: "100%",
72
+ }}
73
+ >
74
+ {scrollingShadows && (
75
+ <ScrollingShadow
76
+ direction="down"
77
+ size={10}
78
+ opacity={getShadowOpacity(0.15, 80, scrollTop)}
79
+ />
80
+ )}
81
+ <div
82
+ className={classnames(className, "VerticalScrollContainer")}
83
+ style={{
84
+ overflowY: "auto",
85
+ position: "absolute",
86
+ top: 0,
87
+ right: 0,
88
+ bottom: 0,
89
+ left: 0,
90
+ ...style,
91
+ }}
92
+ ref={scrollContainerRef}
93
+ {...rest}
94
+ >
95
+ {children}
96
+ </div>
97
+ {scrollingShadows && (
98
+ <ScrollingShadow
99
+ direction="up"
100
+ size={10}
101
+ opacity={getShadowOpacity(0.15, 80, scrollBottom)}
102
+ />
103
+ )}
104
+ </div>
105
+ )
106
+ }
107
+ )
108
+
109
+ function getShadowOpacity(
110
+ maxOpacity: number,
111
+ maxDistance: number,
112
+ scrollDistance: number | undefined
113
+ ): number {
114
+ const distance =
115
+ scrollDistance !== undefined ? Math.min(scrollDistance, maxDistance) : 0
116
+ return (distance / maxDistance) * maxOpacity
117
+ }
118
+
119
+ const ScrollingShadow = (props: {
120
+ direction: "up" | "down"
121
+ size: number
122
+ opacity: number
123
+ }): React.ReactElement => {
124
+ // "Eased" gradient
125
+ // https://larsenwork.com/easing-gradients/
126
+ const background = `linear-gradient(
127
+ to ${props.direction === "up" ? "bottom" : "top"},
128
+ hsla(0, 0%, 0%, 0) 0%,
129
+ hsla(0, 0%, 0%, 0.104) 25.8%,
130
+ hsla(0, 0%, 0%, 0.45) 60.9%,
131
+ hsla(0, 0%, 0%, 0.825) 88.7%,
132
+ hsl(0, 0%, 0%) 100%
133
+ )`
134
+ return (
135
+ <div
136
+ style={{
137
+ position: "absolute",
138
+ left: 0,
139
+ right: 0,
140
+ [props.direction === "up" ? "bottom" : "top"]: 0,
141
+ height: `${props.size}px`,
142
+ background: background,
143
+ opacity: props.opacity,
144
+ pointerEvents: "none",
145
+ zIndex: 2,
146
+ }}
147
+ />
148
+ )
149
+ }
150
+
151
+ /**
152
+ * A throttled function that returns the available scrollTop and scrollBottom
153
+ * @param ref
154
+ * @param contentsId when this string changes, it's a signal that the contents have re-rendered and
155
+ * the height has likely changed.
156
+ */
157
+ function useScrollBounds<ElementType extends HTMLElement>(
158
+ ref: React.RefObject<ElementType | null>,
159
+ contentsId?: string
160
+ ): [number | undefined, number | undefined] {
161
+ const [scrollTop, onScrollTop] = useState<number | undefined>(undefined)
162
+ const [scrollBottom, onScrollBottom] = useState<number | undefined>(
163
+ undefined
164
+ )
165
+
166
+ useEffect(() => {
167
+ const el = ref.current!
168
+ if (el) {
169
+ let pendingUpdate = false
170
+
171
+ function onScroll(): void {
172
+ const { scrollTop, scrollHeight, offsetHeight } = el
173
+ onScrollTop(scrollTop)
174
+ onScrollBottom(scrollHeight - offsetHeight - scrollTop)
175
+ pendingUpdate = false
176
+ }
177
+ onScroll() // execute for first time to setState
178
+
179
+ const onScrollThrottled = (): void => {
180
+ if (!pendingUpdate) {
181
+ window.requestAnimationFrame(onScroll)
182
+ pendingUpdate = true
183
+ }
184
+ }
185
+
186
+ el.addEventListener("scroll", onScrollThrottled)
187
+ return (): void => {
188
+ el.removeEventListener("scroll", onScrollThrottled)
189
+ }
190
+ }
191
+ return
192
+ }, [contentsId, ref])
193
+
194
+ return [scrollTop, scrollBottom]
195
+ }
196
+
197
+ interface ScrollLockOptions {
198
+ enable: boolean
199
+ doNotLockIfNoScroll: boolean
200
+ }
201
+
202
+ /**
203
+ * React hook to prevent scroll events propagating to parent element.
204
+ * @param ref the ReactRef of the scrolling container
205
+ */
206
+ function useScrollLock<ElementType extends HTMLElement>(
207
+ ref: React.RefObject<ElementType | null>,
208
+ opts?: Partial<ScrollLockOptions>
209
+ ): void {
210
+ useEffect(() => {
211
+ const el = ref.current
212
+ const options: ScrollLockOptions = {
213
+ doNotLockIfNoScroll: false,
214
+ enable: true,
215
+ ...opts,
216
+ }
217
+ if (options.enable && el) {
218
+ function onWheel(ev: WheelEvent): void {
219
+ const el = ref.current
220
+ if (el) {
221
+ const delta = ev.deltaY
222
+ const up = delta < 0
223
+ const { scrollTop, scrollHeight, offsetHeight } = el
224
+
225
+ if (
226
+ options.doNotLockIfNoScroll &&
227
+ scrollHeight <= offsetHeight
228
+ ) {
229
+ return
230
+ }
231
+
232
+ function prevent(): void {
233
+ ev.stopPropagation()
234
+ ev.preventDefault()
235
+ }
236
+
237
+ if (
238
+ !up &&
239
+ delta > scrollHeight - offsetHeight - scrollTop
240
+ ) {
241
+ // Scrolling down, but this will take us past the bottom.
242
+ el.scrollTop = scrollHeight
243
+ return prevent()
244
+ } else if (up && -delta > scrollTop) {
245
+ // Scrolling up, but this will take us past the top.
246
+ el.scrollTop = 0
247
+ return prevent()
248
+ }
249
+ }
250
+ }
251
+ el.addEventListener("wheel", onWheel as any, {
252
+ // We need to be in non-passive mode to be able to cancel the event
253
+ passive: false,
254
+ })
255
+ return (): void => {
256
+ if (el) {
257
+ el.removeEventListener("wheel", onWheel as any)
258
+ }
259
+ }
260
+ }
261
+ return
262
+ }, [opts, ref])
263
+ }