@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.
- package/LICENSE.md +8 -0
- package/README.md +113 -0
- package/package.json +137 -0
- package/src/components/BodyPortal/BodyPortal.tsx +40 -0
- package/src/components/Button/Button.scss +110 -0
- package/src/components/Button/Button.tsx +101 -0
- package/src/components/Checkbox.scss +93 -0
- package/src/components/Checkbox.tsx +47 -0
- package/src/components/ExpandableToggle/ExpandableToggle.scss +123 -0
- package/src/components/ExpandableToggle/ExpandableToggle.tsx +60 -0
- package/src/components/GrapherTabIcon.tsx +156 -0
- package/src/components/GrapherTrendArrow.scss +16 -0
- package/src/components/GrapherTrendArrow.tsx +30 -0
- package/src/components/Halo/Halo.tsx +44 -0
- package/src/components/LabeledSwitch/LabeledSwitch.scss +109 -0
- package/src/components/LabeledSwitch/LabeledSwitch.tsx +62 -0
- package/src/components/MarkdownTextWrap/MarkdownTextWrap.tsx +1173 -0
- package/src/components/OverlayHeader.scss +18 -0
- package/src/components/OverlayHeader.tsx +29 -0
- package/src/components/RadioButton.scss +69 -0
- package/src/components/RadioButton.tsx +42 -0
- package/src/components/SimpleMarkdownText.tsx +89 -0
- package/src/components/TextInput.scss +17 -0
- package/src/components/TextInput.tsx +19 -0
- package/src/components/TextWrap/TextWrap.tsx +361 -0
- package/src/components/TextWrap/TextWrapUtils.ts +32 -0
- package/src/components/closeButton/CloseButton.scss +40 -0
- package/src/components/closeButton/CloseButton.tsx +27 -0
- package/src/components/index.ts +70 -0
- package/src/components/loadingIndicator/LoadingIndicator.scss +40 -0
- package/src/components/loadingIndicator/LoadingIndicator.tsx +28 -0
- package/src/components/markdown/remarkPlainLinks.ts +36 -0
- package/src/components/reactUtil.ts +20 -0
- package/src/components/stubs/CodeSnippet.tsx +19 -0
- package/src/components/stubs/DataCitation.tsx +16 -0
- package/src/components/stubs/IndicatorKeyData.tsx +45 -0
- package/src/components/stubs/IndicatorProcessing.tsx +15 -0
- package/src/components/stubs/IndicatorSources.tsx +15 -0
- package/src/components/styles/colors.scss +113 -0
- package/src/components/styles/mixins.scss +630 -0
- package/src/components/styles/typography.scss +579 -0
- package/src/components/styles/util.scss +89 -0
- package/src/components/styles/variables.scss +208 -0
- package/src/config/ChartsConfig.ts +163 -0
- package/src/config/ChartsProvider.tsx +157 -0
- package/src/config/index.ts +20 -0
- package/src/core-table/CoreTable.ts +1355 -0
- package/src/core-table/CoreTableColumns.ts +973 -0
- package/src/core-table/CoreTableUtils.ts +793 -0
- package/src/core-table/ErrorValues.ts +73 -0
- package/src/core-table/OwidTable.ts +1175 -0
- package/src/core-table/OwidTableSynthesizers.ts +272 -0
- package/src/core-table/OwidTableUtil.ts +76 -0
- package/src/core-table/Transforms.ts +484 -0
- package/src/core-table/index.ts +82 -0
- package/src/explorer/ColumnGrammar.ts +217 -0
- package/src/explorer/Explorer.sample.ts +212 -0
- package/src/explorer/Explorer.scss +148 -0
- package/src/explorer/Explorer.tsx +1283 -0
- package/src/explorer/ExplorerConstants.ts +85 -0
- package/src/explorer/ExplorerControls.scss +156 -0
- package/src/explorer/ExplorerControls.tsx +210 -0
- package/src/explorer/ExplorerDecisionMatrix.ts +471 -0
- package/src/explorer/ExplorerGrammar.ts +161 -0
- package/src/explorer/ExplorerProgram.ts +568 -0
- package/src/explorer/ExplorerUtils.ts +59 -0
- package/src/explorer/GrapherGrammar.ts +387 -0
- package/src/explorer/gridLang/GrammarUtils.ts +121 -0
- package/src/explorer/gridLang/GridCell.ts +298 -0
- package/src/explorer/gridLang/GridLangConstants.ts +255 -0
- package/src/explorer/gridLang/GridProgram.ts +311 -0
- package/src/explorer/gridLang/readme.md +17 -0
- package/src/explorer/index.ts +69 -0
- package/src/explorer/readme.md +19 -0
- package/src/explorer/urlMigrations/CO2UrlMigration.ts +46 -0
- package/src/explorer/urlMigrations/CovidUrlMigration.ts +37 -0
- package/src/explorer/urlMigrations/EnergyUrlMigration.ts +41 -0
- package/src/explorer/urlMigrations/ExplorerPageUrlMigrationSpec.ts +12 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrationUtils.ts +45 -0
- package/src/explorer/urlMigrations/ExplorerUrlMigrations.ts +33 -0
- package/src/explorer/urlMigrations/LegacyCovidUrlMigration.ts +144 -0
- package/src/explorer/urlMigrations/readme.md +39 -0
- package/src/grapher/axis/Axis.ts +973 -0
- package/src/grapher/axis/AxisConfig.ts +179 -0
- package/src/grapher/axis/AxisViews.tsx +597 -0
- package/src/grapher/barCharts/DiscreteBarChart.tsx +728 -0
- package/src/grapher/barCharts/DiscreteBarChartConstants.ts +60 -0
- package/src/grapher/barCharts/DiscreteBarChartHelpers.ts +338 -0
- package/src/grapher/barCharts/DiscreteBarChartState.ts +354 -0
- package/src/grapher/barCharts/DiscreteBarChartThumbnail.tsx +34 -0
- package/src/grapher/captionedChart/CaptionedChart.scss +61 -0
- package/src/grapher/captionedChart/CaptionedChart.tsx +523 -0
- package/src/grapher/captionedChart/Logos.tsx +141 -0
- package/src/grapher/captionedChart/LogosSVG.tsx +16 -0
- package/src/grapher/captionedChart/StaticChartRasterizer.tsx +178 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo-square.svg +15 -0
- package/src/grapher/captionedChart/assets/buildcanada-logo.svg +15 -0
- package/src/grapher/captionedChart/assets/canadaspends.svg +7 -0
- package/src/grapher/captionedChart/readme.md +14 -0
- package/src/grapher/chart/Chart.tsx +62 -0
- package/src/grapher/chart/ChartAreaContent.tsx +172 -0
- package/src/grapher/chart/ChartDimension.ts +121 -0
- package/src/grapher/chart/ChartInterface.ts +83 -0
- package/src/grapher/chart/ChartManager.ts +113 -0
- package/src/grapher/chart/ChartTabs.ts +178 -0
- package/src/grapher/chart/ChartTypeMap.tsx +158 -0
- package/src/grapher/chart/ChartTypeSwitcher.tsx +26 -0
- package/src/grapher/chart/ChartUtils.tsx +364 -0
- package/src/grapher/chart/DimensionSlot.ts +45 -0
- package/src/grapher/chart/StaticChartWrapper.tsx +94 -0
- package/src/grapher/chart/guidedChartUtils.ts +82 -0
- package/src/grapher/color/BinningStrategies.ts +484 -0
- package/src/grapher/color/BinningStrategyEqualSizeBins.ts +132 -0
- package/src/grapher/color/BinningStrategyLogarithmic.ts +121 -0
- package/src/grapher/color/CategoricalColorAssigner.ts +97 -0
- package/src/grapher/color/ColorBrewerSchemes.ts +80 -0
- package/src/grapher/color/ColorConstants.ts +20 -0
- package/src/grapher/color/ColorScale.ts +339 -0
- package/src/grapher/color/ColorScaleBin.ts +147 -0
- package/src/grapher/color/ColorScaleConfig.ts +204 -0
- package/src/grapher/color/ColorScheme.ts +137 -0
- package/src/grapher/color/ColorSchemes.ts +149 -0
- package/src/grapher/color/ColorUtils.ts +86 -0
- package/src/grapher/color/CustomSchemes.ts +1772 -0
- package/src/grapher/color/readme.md +84 -0
- package/src/grapher/comparisonLine/ComparisonLine.tsx +31 -0
- package/src/grapher/comparisonLine/ComparisonLineConstants.ts +11 -0
- package/src/grapher/comparisonLine/ComparisonLineGenerator.ts +60 -0
- package/src/grapher/comparisonLine/ComparisonLineHelpers.ts +10 -0
- package/src/grapher/comparisonLine/CustomComparisonLine.tsx +159 -0
- package/src/grapher/comparisonLine/VerticalComparisonLine.tsx +208 -0
- package/src/grapher/controls/ActionButtons.scss +97 -0
- package/src/grapher/controls/ActionButtons.tsx +453 -0
- package/src/grapher/controls/CommandPalette.scss +50 -0
- package/src/grapher/controls/CommandPalette.tsx +74 -0
- package/src/grapher/controls/ContentSwitchers.scss +93 -0
- package/src/grapher/controls/ContentSwitchers.tsx +238 -0
- package/src/grapher/controls/Controls.scss +158 -0
- package/src/grapher/controls/DataTableFilterDropdown.scss +7 -0
- package/src/grapher/controls/DataTableFilterDropdown.tsx +168 -0
- package/src/grapher/controls/DataTableSearchField.scss +3 -0
- package/src/grapher/controls/DataTableSearchField.tsx +76 -0
- package/src/grapher/controls/Dropdown.scss +252 -0
- package/src/grapher/controls/Dropdown.tsx +235 -0
- package/src/grapher/controls/EntitySelectionToggle.tsx +135 -0
- package/src/grapher/controls/MapRegionDropdown.scss +3 -0
- package/src/grapher/controls/MapRegionDropdown.tsx +104 -0
- package/src/grapher/controls/MapResetButton.tsx +115 -0
- package/src/grapher/controls/MapZoomDropdown.scss +9 -0
- package/src/grapher/controls/MapZoomDropdown.tsx +270 -0
- package/src/grapher/controls/MapZoomToSelectionButton.tsx +87 -0
- package/src/grapher/controls/SearchField.scss +78 -0
- package/src/grapher/controls/SearchField.tsx +63 -0
- package/src/grapher/controls/SettingsMenu.scss +191 -0
- package/src/grapher/controls/SettingsMenu.tsx +399 -0
- package/src/grapher/controls/ShareMenu.scss +58 -0
- package/src/grapher/controls/ShareMenu.tsx +304 -0
- package/src/grapher/controls/SortIcon.tsx +39 -0
- package/src/grapher/controls/VerticalScrollContainer.tsx +263 -0
- package/src/grapher/controls/controlsRow/ControlsRow.tsx +168 -0
- package/src/grapher/controls/dropdown-icons.scss +4 -0
- package/src/grapher/controls/entityPicker/EntityPicker.scss +255 -0
- package/src/grapher/controls/entityPicker/EntityPicker.tsx +816 -0
- package/src/grapher/controls/entityPicker/EntityPickerConstants.ts +23 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.scss +129 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelector.tsx +463 -0
- package/src/grapher/controls/globalEntitySelector/GlobalEntitySelectorConstants.ts +3 -0
- package/src/grapher/controls/globalEntitySelector/readme.md +17 -0
- package/src/grapher/controls/settings/AbsRelToggle.tsx +64 -0
- package/src/grapher/controls/settings/AxisScaleToggle.tsx +53 -0
- package/src/grapher/controls/settings/FacetStrategySelector.tsx +110 -0
- package/src/grapher/controls/settings/FacetYDomainToggle.tsx +51 -0
- package/src/grapher/controls/settings/NoDataAreaToggle.tsx +38 -0
- package/src/grapher/controls/settings/ZoomToggle.tsx +36 -0
- package/src/grapher/core/EntitiesByRegionType.ts +174 -0
- package/src/grapher/core/EntityCodes.ts +19 -0
- package/src/grapher/core/EntityUrlBuilder.ts +200 -0
- package/src/grapher/core/FetchingGrapher.tsx +156 -0
- package/src/grapher/core/Grapher.tsx +760 -0
- package/src/grapher/core/GrapherAnalytics.ts +229 -0
- package/src/grapher/core/GrapherConstants.ts +173 -0
- package/src/grapher/core/GrapherState.tsx +3659 -0
- package/src/grapher/core/GrapherUrl.ts +184 -0
- package/src/grapher/core/GrapherUrlMigrations.ts +29 -0
- package/src/grapher/core/GrapherUseHelpers.tsx +147 -0
- package/src/grapher/core/LegacyToOwidTable.ts +841 -0
- package/src/grapher/core/grapher.entry.ts +5 -0
- package/src/grapher/core/grapher.scss +257 -0
- package/src/grapher/core/loadGrapherTableHelpers.ts +116 -0
- package/src/grapher/core/loadVariable.ts +104 -0
- package/src/grapher/core/relatedQuestion.ts +12 -0
- package/src/grapher/core/typography.scss +206 -0
- package/src/grapher/dataTable/DataTable.sample.ts +206 -0
- package/src/grapher/dataTable/DataTable.scss +249 -0
- package/src/grapher/dataTable/DataTable.tsx +1332 -0
- package/src/grapher/dataTable/DataTableConstants.ts +186 -0
- package/src/grapher/entitySelector/EntitySelector.scss +255 -0
- package/src/grapher/entitySelector/EntitySelector.tsx +1838 -0
- package/src/grapher/facet/FacetChart.tsx +943 -0
- package/src/grapher/facet/FacetChartConstants.ts +24 -0
- package/src/grapher/facet/FacetChartUtils.ts +51 -0
- package/src/grapher/facet/FacetMap.tsx +604 -0
- package/src/grapher/facet/FacetMapConstants.ts +23 -0
- package/src/grapher/facet/readme.md +13 -0
- package/src/grapher/focus/FocusArray.ts +79 -0
- package/src/grapher/footer/Footer.scss +63 -0
- package/src/grapher/footer/Footer.tsx +809 -0
- package/src/grapher/footer/FooterManager.ts +44 -0
- package/src/grapher/fullScreen/FullScreen.scss +11 -0
- package/src/grapher/fullScreen/FullScreen.tsx +61 -0
- package/src/grapher/header/Header.scss +35 -0
- package/src/grapher/header/Header.tsx +372 -0
- package/src/grapher/header/HeaderManager.ts +28 -0
- package/src/grapher/index.ts +157 -0
- package/src/grapher/interaction/InteractionState.ts +60 -0
- package/src/grapher/legend/HorizontalColorLegends.tsx +923 -0
- package/src/grapher/legend/LegendInteractionState.ts +40 -0
- package/src/grapher/legend/VerticalColorLegend.tsx +295 -0
- package/src/grapher/lineCharts/LineChart.tsx +968 -0
- package/src/grapher/lineCharts/LineChartConstants.ts +89 -0
- package/src/grapher/lineCharts/LineChartHelpers.ts +184 -0
- package/src/grapher/lineCharts/LineChartState.ts +394 -0
- package/src/grapher/lineCharts/LineChartThumbnail.tsx +437 -0
- package/src/grapher/lineCharts/Lines.tsx +258 -0
- package/src/grapher/lineLegend/LineLegend.tsx +723 -0
- package/src/grapher/lineLegend/LineLegendConstants.ts +9 -0
- package/src/grapher/lineLegend/LineLegendFilterAlgorithms.ts +143 -0
- package/src/grapher/lineLegend/LineLegendHelpers.ts +253 -0
- package/src/grapher/lineLegend/LineLegendTypes.ts +32 -0
- package/src/grapher/mapCharts/CanadaTopology.ts +17922 -0
- package/src/grapher/mapCharts/ChoroplethGlobe.tsx +949 -0
- package/src/grapher/mapCharts/ChoroplethMap.tsx +662 -0
- package/src/grapher/mapCharts/GeoFeatures.ts +184 -0
- package/src/grapher/mapCharts/GlobeController.ts +496 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.json +1040 -0
- package/src/grapher/mapCharts/MapAnnotationPlacements.ts +31 -0
- package/src/grapher/mapCharts/MapAnnotations.ts +723 -0
- package/src/grapher/mapCharts/MapChart.sample.ts +59 -0
- package/src/grapher/mapCharts/MapChart.scss +5 -0
- package/src/grapher/mapCharts/MapChart.tsx +720 -0
- package/src/grapher/mapCharts/MapChartConstants.ts +260 -0
- package/src/grapher/mapCharts/MapChartState.ts +416 -0
- package/src/grapher/mapCharts/MapChartThumbnail.tsx +25 -0
- package/src/grapher/mapCharts/MapComponents.tsx +338 -0
- package/src/grapher/mapCharts/MapConfig.ts +156 -0
- package/src/grapher/mapCharts/MapHelpers.ts +181 -0
- package/src/grapher/mapCharts/MapProjections.ts +49 -0
- package/src/grapher/mapCharts/MapSparkline.tsx +257 -0
- package/src/grapher/mapCharts/MapTooltip.scss +49 -0
- package/src/grapher/mapCharts/MapTooltip.tsx +409 -0
- package/src/grapher/mapCharts/MapTopology.ts +1766 -0
- package/src/grapher/mapCharts/d3-bboxCollide.js +204 -0
- package/src/grapher/mapCharts/d3-geo-projection.ts +198 -0
- package/src/grapher/modal/DownloadIcons.tsx +39 -0
- package/src/grapher/modal/DownloadModal.scss +300 -0
- package/src/grapher/modal/DownloadModal.tsx +1226 -0
- package/src/grapher/modal/EmbedModal.scss +40 -0
- package/src/grapher/modal/EmbedModal.tsx +160 -0
- package/src/grapher/modal/EntitySelectorModal.tsx +59 -0
- package/src/grapher/modal/Modal.scss +31 -0
- package/src/grapher/modal/Modal.tsx +90 -0
- package/src/grapher/modal/ModalHeader.scss +12 -0
- package/src/grapher/modal/ModalHeader.tsx +16 -0
- package/src/grapher/modal/SourcesDescriptions.scss +87 -0
- package/src/grapher/modal/SourcesDescriptions.tsx +89 -0
- package/src/grapher/modal/SourcesKeyDataTable.scss +49 -0
- package/src/grapher/modal/SourcesKeyDataTable.tsx +87 -0
- package/src/grapher/modal/SourcesModal.scss +301 -0
- package/src/grapher/modal/SourcesModal.tsx +568 -0
- package/src/grapher/noDataModal/NoDataModal.tsx +125 -0
- package/src/grapher/scatterCharts/ConnectedScatterLegend.tsx +143 -0
- package/src/grapher/scatterCharts/MultiColorPolyline.tsx +129 -0
- package/src/grapher/scatterCharts/NoDataSection.scss +14 -0
- package/src/grapher/scatterCharts/NoDataSection.tsx +56 -0
- package/src/grapher/scatterCharts/ScatterPlotChart.tsx +792 -0
- package/src/grapher/scatterCharts/ScatterPlotChartConstants.ts +157 -0
- package/src/grapher/scatterCharts/ScatterPlotChartState.ts +678 -0
- package/src/grapher/scatterCharts/ScatterPlotChartThumbnail.tsx +155 -0
- package/src/grapher/scatterCharts/ScatterPlotTooltip.tsx +560 -0
- package/src/grapher/scatterCharts/ScatterPoints.tsx +153 -0
- package/src/grapher/scatterCharts/ScatterPointsWithLabels.tsx +708 -0
- package/src/grapher/scatterCharts/ScatterSizeLegend.tsx +327 -0
- package/src/grapher/scatterCharts/ScatterUtils.ts +265 -0
- package/src/grapher/scatterCharts/Triangle.tsx +41 -0
- package/src/grapher/schema/README.md +33 -0
- package/src/grapher/schema/defaultGrapherConfig.ts +100 -0
- package/src/grapher/schema/grapher-schema.009.yaml +781 -0
- package/src/grapher/schema/migrations/helpers.ts +58 -0
- package/src/grapher/schema/migrations/migrate.ts +75 -0
- package/src/grapher/schema/migrations/migrations.ts +158 -0
- package/src/grapher/selection/MapSelectionArray.ts +99 -0
- package/src/grapher/selection/SelectionArray.ts +71 -0
- package/src/grapher/selection/readme.md +16 -0
- package/src/grapher/sidePanel/SidePanel.scss +10 -0
- package/src/grapher/sidePanel/SidePanel.tsx +23 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.scss +57 -0
- package/src/grapher/slideInDrawer/SlideInDrawer.tsx +125 -0
- package/src/grapher/slideshowController/SlideShowController.tsx +43 -0
- package/src/grapher/slideshowController/readme.md +7 -0
- package/src/grapher/slopeCharts/MarkX.tsx +45 -0
- package/src/grapher/slopeCharts/Slope.tsx +102 -0
- package/src/grapher/slopeCharts/SlopeChart.tsx +1152 -0
- package/src/grapher/slopeCharts/SlopeChartConstants.ts +33 -0
- package/src/grapher/slopeCharts/SlopeChartHelpers.ts +73 -0
- package/src/grapher/slopeCharts/SlopeChartState.ts +392 -0
- package/src/grapher/slopeCharts/SlopeChartThumbnail.tsx +368 -0
- package/src/grapher/stackedCharts/AbstractStackedChartState.ts +370 -0
- package/src/grapher/stackedCharts/MarimekkoBars.tsx +190 -0
- package/src/grapher/stackedCharts/MarimekkoBarsForOneEntity.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoChart.tsx +1144 -0
- package/src/grapher/stackedCharts/MarimekkoChartConstants.ts +112 -0
- package/src/grapher/stackedCharts/MarimekkoChartHelpers.ts +21 -0
- package/src/grapher/stackedCharts/MarimekkoChartState.ts +465 -0
- package/src/grapher/stackedCharts/MarimekkoChartThumbnail.tsx +168 -0
- package/src/grapher/stackedCharts/MarimekkoInternalLabels.tsx +124 -0
- package/src/grapher/stackedCharts/StackedAreaChart.tsx +678 -0
- package/src/grapher/stackedCharts/StackedAreaChartState.ts +34 -0
- package/src/grapher/stackedCharts/StackedAreaChartThumbnail.tsx +215 -0
- package/src/grapher/stackedCharts/StackedAreas.tsx +223 -0
- package/src/grapher/stackedCharts/StackedBarChart.tsx +619 -0
- package/src/grapher/stackedCharts/StackedBarChartState.ts +80 -0
- package/src/grapher/stackedCharts/StackedBarChartThumbnail.tsx +220 -0
- package/src/grapher/stackedCharts/StackedBarSegment.tsx +87 -0
- package/src/grapher/stackedCharts/StackedBars.tsx +102 -0
- package/src/grapher/stackedCharts/StackedConstants.ts +109 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChart.tsx +270 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartState.ts +296 -0
- package/src/grapher/stackedCharts/StackedDiscreteBarChartThumbnail.tsx +27 -0
- package/src/grapher/stackedCharts/StackedDiscreteBars.tsx +648 -0
- package/src/grapher/stackedCharts/StackedUtils.ts +142 -0
- package/src/grapher/tabs/Tabs.scss +169 -0
- package/src/grapher/tabs/Tabs.tsx +54 -0
- package/src/grapher/tabs/TabsWithDropdown.scss +62 -0
- package/src/grapher/tabs/TabsWithDropdown.tsx +114 -0
- package/src/grapher/testData/OwidTestData.sample.ts +273 -0
- package/src/grapher/testData/OwidTestData.ts +64 -0
- package/src/grapher/timeline/TimelineComponent.scss +139 -0
- package/src/grapher/timeline/TimelineComponent.tsx +658 -0
- package/src/grapher/timeline/TimelineController.ts +368 -0
- package/src/grapher/timeline/readme.md +7 -0
- package/src/grapher/tooltip/Tooltip.scss +510 -0
- package/src/grapher/tooltip/Tooltip.tsx +294 -0
- package/src/grapher/tooltip/TooltipContents.tsx +383 -0
- package/src/grapher/tooltip/TooltipProps.ts +123 -0
- package/src/grapher/tooltip/TooltipState.ts +81 -0
- package/src/grapher/verticalLabels/VerticalLabels.tsx +31 -0
- package/src/grapher/verticalLabels/VerticalLabelsState.ts +154 -0
- package/src/index.ts +226 -0
- package/src/styles/charts.scss +15 -0
- package/src/types/NominalType.ts +30 -0
- package/src/types/OwidOrigin.ts +18 -0
- package/src/types/OwidSource.ts +9 -0
- package/src/types/OwidVariable.ts +133 -0
- package/src/types/OwidVariableDisplayConfigInterface.ts +49 -0
- package/src/types/analyticsTypes.ts +54 -0
- package/src/types/dbTypes/Tags.ts +11 -0
- package/src/types/domainTypes/Archive.ts +139 -0
- package/src/types/domainTypes/Author.ts +28 -0
- package/src/types/domainTypes/ContentGraph.ts +76 -0
- package/src/types/domainTypes/CoreTableTypes.ts +305 -0
- package/src/types/domainTypes/DeployStatus.ts +23 -0
- package/src/types/domainTypes/Layout.ts +34 -0
- package/src/types/domainTypes/Posts.ts +34 -0
- package/src/types/domainTypes/Search.ts +299 -0
- package/src/types/domainTypes/Site.ts +8 -0
- package/src/types/domainTypes/StaticViz.ts +64 -0
- package/src/types/domainTypes/Toc.ts +11 -0
- package/src/types/domainTypes/Tombstone.ts +19 -0
- package/src/types/domainTypes/Various.ts +79 -0
- package/src/types/gdocTypes/Gdoc.ts +280 -0
- package/src/types/grapherTypes/BinningStrategyTypes.ts +46 -0
- package/src/types/grapherTypes/GrapherConstants.ts +53 -0
- package/src/types/grapherTypes/GrapherTypes.ts +743 -0
- package/src/types/index.ts +316 -0
- package/src/types/wordpressTypes/WordpressTypes.ts +9 -0
- package/src/utils/Bounds.ts +439 -0
- package/src/utils/BrowserUtils.ts +12 -0
- package/src/utils/FuzzySearch.ts +74 -0
- package/src/utils/MultiDimDataPageConfig.ts +31 -0
- package/src/utils/OwidVariable.ts +82 -0
- package/src/utils/PointVector.ts +97 -0
- package/src/utils/PromiseCache.ts +36 -0
- package/src/utils/PromiseSwitcher.ts +52 -0
- package/src/utils/TimeBounds.ts +130 -0
- package/src/utils/Tippy.tsx +57 -0
- package/src/utils/Util.ts +2369 -0
- package/src/utils/archival/archivalDate.ts +48 -0
- package/src/utils/dayjs.ts +32 -0
- package/src/utils/formatValue.ts +242 -0
- package/src/utils/grapherConfigUtils.ts +81 -0
- package/src/utils/image.ts +225 -0
- package/src/utils/index.ts +318 -0
- package/src/utils/isPresent.ts +5 -0
- package/src/utils/metadataHelpers.ts +329 -0
- package/src/utils/persistable/Persistable.ts +82 -0
- package/src/utils/persistable/readme.md +50 -0
- package/src/utils/regions.json +5635 -0
- package/src/utils/regions.ts +463 -0
- package/src/utils/serializers.ts +16 -0
- package/src/utils/string.ts +42 -0
- package/src/utils/urls/Url.ts +195 -0
- package/src/utils/urls/UrlMigration.ts +10 -0
- package/src/utils/urls/UrlUtils.ts +54 -0
- package/src/utils/urls/readme.md +90 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import * as _ from "lodash-es"
|
|
2
|
+
import entities from "./regions.json"
|
|
3
|
+
import { lazy } from "./Util.js"
|
|
4
|
+
import { EntityName } from "../types/index.js"
|
|
5
|
+
|
|
6
|
+
export enum RegionType {
|
|
7
|
+
Country = "country",
|
|
8
|
+
Other = "other",
|
|
9
|
+
Aggregate = "aggregate",
|
|
10
|
+
Continent = "continent",
|
|
11
|
+
IncomeGroup = "income_group",
|
|
12
|
+
Province = "province",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BaseRegion {
|
|
16
|
+
regionType: RegionType
|
|
17
|
+
name: string
|
|
18
|
+
code: string
|
|
19
|
+
slug: string
|
|
20
|
+
shortName?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Country extends BaseRegion {
|
|
24
|
+
regionType: RegionType.Country | RegionType.Other
|
|
25
|
+
shortCode?: string
|
|
26
|
+
isMappable?: boolean
|
|
27
|
+
isHistorical?: boolean
|
|
28
|
+
isUnlisted?: boolean
|
|
29
|
+
variantNames?: string[]
|
|
30
|
+
article?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Aggregate extends BaseRegion {
|
|
34
|
+
regionType: RegionType.Aggregate
|
|
35
|
+
definedBy?: AggregateSource
|
|
36
|
+
translationCodes?: string[]
|
|
37
|
+
members: string[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Continent extends BaseRegion {
|
|
41
|
+
name: OwidContinentName
|
|
42
|
+
regionType: RegionType.Continent
|
|
43
|
+
translationCodes?: string[]
|
|
44
|
+
members: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IncomeGroup extends BaseRegion {
|
|
48
|
+
name: OwidIncomeGroupName
|
|
49
|
+
regionType: RegionType.IncomeGroup
|
|
50
|
+
members: string[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface Province extends BaseRegion {
|
|
54
|
+
regionType: RegionType.Province
|
|
55
|
+
parentCountry: string
|
|
56
|
+
shortCode?: string
|
|
57
|
+
isMappable?: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type Region = Country | Aggregate | Continent | IncomeGroup | Province
|
|
61
|
+
|
|
62
|
+
export const regions: Region[] = entities as Region[]
|
|
63
|
+
|
|
64
|
+
type OwidContinentName =
|
|
65
|
+
| "Africa"
|
|
66
|
+
| "Asia"
|
|
67
|
+
| "Europe"
|
|
68
|
+
| "North America"
|
|
69
|
+
| "Oceania"
|
|
70
|
+
| "South America"
|
|
71
|
+
|
|
72
|
+
export type OwidIncomeGroupName =
|
|
73
|
+
| "OWID_LIC"
|
|
74
|
+
| "OWID_LMC"
|
|
75
|
+
| "OWID_UMC"
|
|
76
|
+
| "OWID_HIC"
|
|
77
|
+
|
|
78
|
+
export const aggregateSources = [
|
|
79
|
+
"pew", // Pew Research Center
|
|
80
|
+
"unm49", // UN M49 (UN country codes for statistical use)
|
|
81
|
+
"unsd", // UN Statistical Division
|
|
82
|
+
"unsdg", // UN SDG
|
|
83
|
+
"wb", // World Bank
|
|
84
|
+
"who", // World Health Organization
|
|
85
|
+
] as const
|
|
86
|
+
export type AggregateSource = (typeof aggregateSources)[number]
|
|
87
|
+
|
|
88
|
+
export function checkIsOwidIncomeGroupName(
|
|
89
|
+
name: string
|
|
90
|
+
): name is OwidIncomeGroupName {
|
|
91
|
+
return (
|
|
92
|
+
name === "OWID_LIC" ||
|
|
93
|
+
name === "OWID_LMC" ||
|
|
94
|
+
name === "OWID_UMC" ||
|
|
95
|
+
name === "OWID_HIC"
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function checkIsCountry(region: Region): region is Country {
|
|
100
|
+
return (
|
|
101
|
+
region.regionType === RegionType.Country ||
|
|
102
|
+
region.regionType === RegionType.Other
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function checkIsOwidContinent(region: Region): region is Continent {
|
|
107
|
+
return region.regionType === RegionType.Continent
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function checkIsIncomeGroup(region: Region): region is IncomeGroup {
|
|
111
|
+
return region.regionType === RegionType.IncomeGroup
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function checkIsProvince(region: Region): region is Province {
|
|
115
|
+
return region.regionType === RegionType.Province
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function checkHasMembers(
|
|
119
|
+
region?: Region
|
|
120
|
+
): region is Aggregate | Continent | IncomeGroup {
|
|
121
|
+
return region !== undefined && "members" in region
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const countries: Country[] = regions.filter(
|
|
125
|
+
(entity) =>
|
|
126
|
+
entity.regionType === RegionType.Country &&
|
|
127
|
+
!entity.isUnlisted &&
|
|
128
|
+
!entity.isHistorical
|
|
129
|
+
) as Country[]
|
|
130
|
+
|
|
131
|
+
export const listedRegionsNames = lazy(() =>
|
|
132
|
+
regions
|
|
133
|
+
.filter((entity) => checkIsCountry(entity) && !entity.isUnlisted)
|
|
134
|
+
.map((entity) => entity.name)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
export const mappableCountries: Country[] = regions.filter(
|
|
138
|
+
(country): country is Country =>
|
|
139
|
+
checkIsCountry(country) && !!country.isMappable
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
export const getOthers = lazy(
|
|
143
|
+
() =>
|
|
144
|
+
entities.filter(
|
|
145
|
+
(entity) => entity.regionType === RegionType.Other
|
|
146
|
+
) as Country[]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
export const getAggregates = lazy(
|
|
150
|
+
() =>
|
|
151
|
+
entities.filter(
|
|
152
|
+
(entity) => entity.regionType === RegionType.Aggregate
|
|
153
|
+
) as Aggregate[]
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
export const getContinents = lazy(
|
|
157
|
+
() =>
|
|
158
|
+
entities.filter(
|
|
159
|
+
(entity) => entity.regionType === RegionType.Continent
|
|
160
|
+
) as Continent[]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
export const getIncomeGroups = lazy(
|
|
164
|
+
() =>
|
|
165
|
+
entities.filter(
|
|
166
|
+
(entity) => entity.regionType === RegionType.IncomeGroup
|
|
167
|
+
) as IncomeGroup[]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
export const getProvinces = lazy(
|
|
171
|
+
() =>
|
|
172
|
+
entities.filter(
|
|
173
|
+
(entity) => entity.regionType === RegionType.Province
|
|
174
|
+
) as Province[]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
export const mappableProvinces: Province[] = regions.filter(
|
|
178
|
+
(region): region is Province =>
|
|
179
|
+
checkIsProvince(region) && !!region.isMappable
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
const regionsByName = lazy(() =>
|
|
183
|
+
Object.fromEntries(regions.map((region) => [region.name, region]))
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
const regionsBySlug = lazy(() =>
|
|
187
|
+
Object.fromEntries(regions.map((region) => [region.slug, region]))
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
const regionsByCode = lazy(() =>
|
|
191
|
+
Object.fromEntries(regions.map((region) => [region.code, region]))
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
export const countriesByName = lazy(() =>
|
|
195
|
+
Object.fromEntries(countries.map((country) => [country.name, country]))
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
export const incomeGroupsByName = lazy(
|
|
199
|
+
() =>
|
|
200
|
+
Object.fromEntries(
|
|
201
|
+
regions
|
|
202
|
+
.filter((region) => checkIsOwidIncomeGroupName(region.code))
|
|
203
|
+
.map((region) => [region.code, region])
|
|
204
|
+
) as Record<OwidIncomeGroupName, IncomeGroup>
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
const countriesBySlug = lazy(() =>
|
|
208
|
+
Object.fromEntries(countries.map((country) => [country.slug, country]))
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
const provincesByName = lazy(() =>
|
|
212
|
+
Object.fromEntries(
|
|
213
|
+
mappableProvinces.map((province) => [province.name, province])
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
export const getProvinceByName = (name: string): Province | undefined =>
|
|
218
|
+
provincesByName()[name]
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Lazy-loaded map of region names to their parent regions.
|
|
222
|
+
*
|
|
223
|
+
* This creates a reverse lookup from child regions to all the parent regions
|
|
224
|
+
* that contain them. For example, if "France" is a member of both "Europe"
|
|
225
|
+
* and "European Union", then parentRegions.get("France") would return
|
|
226
|
+
* both regions.
|
|
227
|
+
*/
|
|
228
|
+
const parentRegions = lazy(() => {
|
|
229
|
+
const parentRegions = new Map<string, Region[]>()
|
|
230
|
+
for (const region of regions) {
|
|
231
|
+
// Only process regions that can contain other regions
|
|
232
|
+
if (!checkHasMembers(region)) continue
|
|
233
|
+
|
|
234
|
+
for (const memberCode of region.members) {
|
|
235
|
+
const subRegion = getRegionByCode(memberCode)
|
|
236
|
+
if (!subRegion) continue
|
|
237
|
+
if (!parentRegions.has(subRegion.name))
|
|
238
|
+
parentRegions.set(subRegion.name, [])
|
|
239
|
+
parentRegions.get(subRegion.name)!.push(region)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return parentRegions
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get all parent regions that contain the specified region as a member.
|
|
247
|
+
*
|
|
248
|
+
* For example, France's parent regions are 'Europe', 'European Union',
|
|
249
|
+
* 'High-income countries', etc.
|
|
250
|
+
*/
|
|
251
|
+
export const getParentRegions = (regionName: string): Region[] => {
|
|
252
|
+
return parentRegions().get(regionName) ?? []
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get all sibling regions that share the same parent(s) as the specified region.
|
|
257
|
+
*
|
|
258
|
+
* For example, returns other European countries for 'Germany' or other
|
|
259
|
+
* continents for 'Europe'.
|
|
260
|
+
*/
|
|
261
|
+
export const getSiblingRegions = (regionName: string): Region[] => {
|
|
262
|
+
const parentRegions = getParentRegions(regionName)
|
|
263
|
+
const siblingCodes = _.uniq(
|
|
264
|
+
parentRegions.flatMap((region) =>
|
|
265
|
+
checkHasMembers(region) ? region.members : []
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
return siblingCodes
|
|
269
|
+
.map(getRegionByCode)
|
|
270
|
+
.filter((region) => region && region.name !== regionName) as Region[]
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const getCountryNamesForRegionRecursive = (region: Region): string[] => {
|
|
274
|
+
if (!checkHasMembers(region)) return [region.name]
|
|
275
|
+
return region.members.reduce<string[]>((countryNames, memberCode) => {
|
|
276
|
+
const subRegion = getRegionByCode(memberCode)
|
|
277
|
+
if (!subRegion) return countryNames
|
|
278
|
+
return [
|
|
279
|
+
...countryNames,
|
|
280
|
+
...getCountryNamesForRegionRecursive(subRegion),
|
|
281
|
+
]
|
|
282
|
+
}, [])
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const getCountryNamesForRegion = (
|
|
286
|
+
region: Exclude<Region, Country>
|
|
287
|
+
): string[] => {
|
|
288
|
+
return getCountryNamesForRegionRecursive(region)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const regionsByNameOrVariantNameLowercase = lazy(
|
|
292
|
+
() =>
|
|
293
|
+
new Map(
|
|
294
|
+
regions.flatMap((region) => {
|
|
295
|
+
const names = [region.name.toLowerCase()]
|
|
296
|
+
if ("variantNames" in region && region.variantNames) {
|
|
297
|
+
names.push(
|
|
298
|
+
...region.variantNames.map((variant) =>
|
|
299
|
+
variant.toLowerCase()
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
return names.map((name) => [name, region])
|
|
304
|
+
})
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
const currentAndHistoricalCountryNames = lazy(() =>
|
|
309
|
+
regions
|
|
310
|
+
.filter(({ regionType }) => regionType === RegionType.Country)
|
|
311
|
+
.map(({ name }) => name.toLowerCase())
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
export const isCountryName = (name: string): boolean =>
|
|
315
|
+
currentAndHistoricalCountryNames().includes(name.toLowerCase())
|
|
316
|
+
|
|
317
|
+
export const getCountryByName = (name: string): Country | undefined =>
|
|
318
|
+
countriesByName()[name]
|
|
319
|
+
|
|
320
|
+
export const getCountryBySlug = (slug: string): Country | undefined =>
|
|
321
|
+
countriesBySlug()[slug]
|
|
322
|
+
|
|
323
|
+
export const getRegionByName = (name: string): Region | undefined =>
|
|
324
|
+
regionsByName()[name]
|
|
325
|
+
|
|
326
|
+
export const getRegionBySlug = (slug: string): Region | undefined =>
|
|
327
|
+
regionsBySlug()[slug]
|
|
328
|
+
|
|
329
|
+
const getRegionByCode = (code: string): Region | undefined =>
|
|
330
|
+
regionsByCode()[code]
|
|
331
|
+
|
|
332
|
+
export const getRegionByNameOrVariantName = (
|
|
333
|
+
nameOrVariantName: string
|
|
334
|
+
): Region | undefined =>
|
|
335
|
+
regionsByNameOrVariantNameLowercase().get(nameOrVariantName.toLowerCase())
|
|
336
|
+
|
|
337
|
+
const _IntlDisplayNamesInstances = new Map<string, Intl.DisplayNames>()
|
|
338
|
+
const getRegionTranslation = (
|
|
339
|
+
regionCode: string,
|
|
340
|
+
languageCode: string
|
|
341
|
+
): string | undefined => {
|
|
342
|
+
try {
|
|
343
|
+
if (!_IntlDisplayNamesInstances.has(languageCode)) {
|
|
344
|
+
_IntlDisplayNamesInstances.set(
|
|
345
|
+
languageCode,
|
|
346
|
+
new Intl.DisplayNames([languageCode], {
|
|
347
|
+
type: "region",
|
|
348
|
+
fallback: "none",
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
return _IntlDisplayNamesInstances.get(languageCode)!.of(regionCode)
|
|
353
|
+
} catch {
|
|
354
|
+
return undefined
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const _regionAlternativeNames = new Map<string, string[] | undefined>()
|
|
359
|
+
export const getRegionAlternativeNames = (
|
|
360
|
+
regionName: string,
|
|
361
|
+
languages: readonly string[]
|
|
362
|
+
): string[] | undefined => {
|
|
363
|
+
if (!_regionAlternativeNames.has(regionName)) {
|
|
364
|
+
const region = getRegionByNameOrVariantName(regionName)
|
|
365
|
+
if (region) {
|
|
366
|
+
const names = new Set<string>()
|
|
367
|
+
if ("variantNames" in region && region.variantNames) {
|
|
368
|
+
for (const variant of region.variantNames) {
|
|
369
|
+
names.add(variant)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const codesForTranslation =
|
|
374
|
+
("translationCodes" in region && region.translationCodes) ||
|
|
375
|
+
("shortCode" in region && region.shortCode)
|
|
376
|
+
if (codesForTranslation) {
|
|
377
|
+
const translations = languages
|
|
378
|
+
.flatMap((lang) => {
|
|
379
|
+
if (Array.isArray(codesForTranslation))
|
|
380
|
+
return codesForTranslation.map((code) =>
|
|
381
|
+
getRegionTranslation(code, lang)
|
|
382
|
+
)
|
|
383
|
+
else
|
|
384
|
+
return getRegionTranslation(
|
|
385
|
+
codesForTranslation,
|
|
386
|
+
lang
|
|
387
|
+
)
|
|
388
|
+
})
|
|
389
|
+
.filter((name) => name !== undefined)
|
|
390
|
+
|
|
391
|
+
translations.forEach((translation) => names.add(translation))
|
|
392
|
+
}
|
|
393
|
+
_regionAlternativeNames.set(regionName, Array.from(names))
|
|
394
|
+
} else {
|
|
395
|
+
_regionAlternativeNames.set(regionName, undefined)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return _regionAlternativeNames.get(regionName)!
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Regions that require the definite article "the" before their name
|
|
402
|
+
// Maintained here instead of in the ETL because it's such a specific piece of metadata.
|
|
403
|
+
const regionsWithArticles = new Set([
|
|
404
|
+
"Aland Islands",
|
|
405
|
+
"Netherlands Antilles",
|
|
406
|
+
"United Arab Emirates",
|
|
407
|
+
"French Southern Territories",
|
|
408
|
+
"Bahrain",
|
|
409
|
+
"Bahamas",
|
|
410
|
+
"Central African Republic",
|
|
411
|
+
"Cocos Islands",
|
|
412
|
+
"Democratic Republic of Congo",
|
|
413
|
+
"Congo",
|
|
414
|
+
"Cook Islands",
|
|
415
|
+
"Comoros",
|
|
416
|
+
"Cayman Islands",
|
|
417
|
+
"Dominican Republic",
|
|
418
|
+
"Western Sahara",
|
|
419
|
+
"Falkland Islands",
|
|
420
|
+
"Faroe Islands",
|
|
421
|
+
"United Kingdom",
|
|
422
|
+
"Gambia",
|
|
423
|
+
"Heard Island and McDonald Islands",
|
|
424
|
+
"Isle of Man",
|
|
425
|
+
"British Indian Ocean Territory",
|
|
426
|
+
"Maldives",
|
|
427
|
+
"Marshall Islands",
|
|
428
|
+
"Northern Mariana Islands",
|
|
429
|
+
"Netherlands",
|
|
430
|
+
"Grand Duchy of Baden",
|
|
431
|
+
"Kingdom of Bavaria",
|
|
432
|
+
"Democratic Republic of Vietnam",
|
|
433
|
+
"Kingdom of the Two Sicilies",
|
|
434
|
+
"Duchy of Modena and Reggio",
|
|
435
|
+
"Orange Free State",
|
|
436
|
+
"Duchy of Parma and Piacenza",
|
|
437
|
+
"Federal Republic of Central America",
|
|
438
|
+
"Republic of Vietnam",
|
|
439
|
+
"Kingdom of Sardinia",
|
|
440
|
+
"Kingdom of Saxony",
|
|
441
|
+
"Sudan (former)",
|
|
442
|
+
"Grand Duchy of Tuscany",
|
|
443
|
+
"USSR",
|
|
444
|
+
"Kingdom of Wurttemberg",
|
|
445
|
+
"Yemen Arab Republic",
|
|
446
|
+
"Yemen People's Republic",
|
|
447
|
+
"Philippines",
|
|
448
|
+
"Gaza Strip",
|
|
449
|
+
"South Georgia and the South Sandwich Islands",
|
|
450
|
+
"Solomon Islands",
|
|
451
|
+
"Seychelles",
|
|
452
|
+
"Turks and Caicos Islands",
|
|
453
|
+
"United States",
|
|
454
|
+
"Vatican",
|
|
455
|
+
"British Virgin Islands",
|
|
456
|
+
"United States Virgin Islands",
|
|
457
|
+
])
|
|
458
|
+
|
|
459
|
+
export const articulateEntity = (entityName: EntityName): string => {
|
|
460
|
+
return regionsWithArticles.has(entityName)
|
|
461
|
+
? `the ${entityName}`
|
|
462
|
+
: entityName
|
|
463
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const jsonCommentDelimiter = "\n//EMBEDDED_JSON\n"
|
|
2
|
+
// Stringifies JSON for placing into an arbitrary doc, for later extraction without parsing the whole doc
|
|
3
|
+
export const serializeJSONForHTML = (
|
|
4
|
+
obj: unknown,
|
|
5
|
+
delimiter = jsonCommentDelimiter
|
|
6
|
+
): string =>
|
|
7
|
+
`${delimiter}${
|
|
8
|
+
obj === undefined ? "" : JSON.stringify(obj, null, 2)
|
|
9
|
+
}${delimiter}`
|
|
10
|
+
export const deserializeJSONFromHTML = (
|
|
11
|
+
html: string,
|
|
12
|
+
delimiter = jsonCommentDelimiter
|
|
13
|
+
): any => {
|
|
14
|
+
const json = html.split(delimiter)[1]
|
|
15
|
+
return json === undefined || json === "" ? undefined : JSON.parse(json)
|
|
16
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const snakeToCamel = (str: string): string =>
|
|
2
|
+
str.replace(/(_\w)/g, (char) => char[1].toUpperCase())
|
|
3
|
+
|
|
4
|
+
export const camelCaseProperties = (
|
|
5
|
+
obj: Record<string, unknown>
|
|
6
|
+
): Record<string, unknown> => {
|
|
7
|
+
const newObj: any = {}
|
|
8
|
+
for (const key in obj) {
|
|
9
|
+
newObj[snakeToCamel(key)] = obj[key]
|
|
10
|
+
}
|
|
11
|
+
return newObj
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Converts a string to title case, with support for hyphenated words
|
|
16
|
+
* e.g. 'WELCOME to jean-édouard' -> 'Welcome To Jean-Édouard!'
|
|
17
|
+
*/
|
|
18
|
+
export const titleCase = (str: string): string => {
|
|
19
|
+
return str
|
|
20
|
+
.split(" ")
|
|
21
|
+
.map(function (word) {
|
|
22
|
+
return word
|
|
23
|
+
.split("-")
|
|
24
|
+
.map(function (subWord) {
|
|
25
|
+
return (
|
|
26
|
+
subWord.charAt(0).toUpperCase() +
|
|
27
|
+
subWord.substring(1).toLowerCase()
|
|
28
|
+
)
|
|
29
|
+
})
|
|
30
|
+
.join("-")
|
|
31
|
+
})
|
|
32
|
+
.join(" ")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function toAsciiQuotes(str: string): string {
|
|
36
|
+
return str.replace(/[“”]/g, '"').replace(/[‘’]/g, "'")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// https://stackoverflow.com/a/37511463/9846837
|
|
40
|
+
export function removeDiacritics(str: string): string {
|
|
41
|
+
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
|
42
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import urlParseLib from "url-parse"
|
|
2
|
+
import { gdocUrlRegex, QueryParams } from "../../types/index.js"
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
detailOnDemandRegex,
|
|
6
|
+
guidedChartRegex,
|
|
7
|
+
excludeUndefined,
|
|
8
|
+
omitUndefinedValues,
|
|
9
|
+
} from "../Util.js"
|
|
10
|
+
|
|
11
|
+
import { queryParamsToStr, strToQueryParams } from "./UrlUtils.js"
|
|
12
|
+
import * as R from "remeda"
|
|
13
|
+
|
|
14
|
+
const parseUrl = (url: string): urlParseLib<string> => urlParseLib(url, {})
|
|
15
|
+
|
|
16
|
+
const ensureStartsWith = (str: string, start: string): string => {
|
|
17
|
+
if (str.startsWith(start)) return str
|
|
18
|
+
return `${start}${str}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ensureQueryStrFormat = (queryStr: string): string =>
|
|
22
|
+
ensureStartsWith(queryStr, "?")
|
|
23
|
+
const ensureHashFormat = (queryStr: string): string =>
|
|
24
|
+
ensureStartsWith(queryStr, "#")
|
|
25
|
+
|
|
26
|
+
interface UrlProps {
|
|
27
|
+
readonly origin?: string // https://ourworldindata.org
|
|
28
|
+
readonly pathname?: string // /grapher/abc
|
|
29
|
+
readonly queryStr?: string // ?stackMode=relative
|
|
30
|
+
readonly hash?: string // #articles
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class Url {
|
|
34
|
+
private props: UrlProps
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param url Absolute or relative URL
|
|
38
|
+
*/
|
|
39
|
+
static fromURL(url: string): Url {
|
|
40
|
+
const { origin, pathname, query, hash } = parseUrl(url)
|
|
41
|
+
return new Url({
|
|
42
|
+
origin:
|
|
43
|
+
origin !== undefined && origin !== "null" ? origin : undefined,
|
|
44
|
+
pathname,
|
|
45
|
+
queryStr: query,
|
|
46
|
+
hash,
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static fromQueryStr(queryStr: string): Url {
|
|
51
|
+
return new Url({
|
|
52
|
+
queryStr: ensureQueryStrFormat(queryStr),
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static fromQueryParams(queryParams: QueryParams): Url {
|
|
57
|
+
return new Url({
|
|
58
|
+
queryStr: queryParamsToStr(queryParams),
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private constructor(props: UrlProps = {}) {
|
|
63
|
+
this.props = {
|
|
64
|
+
...props,
|
|
65
|
+
pathname:
|
|
66
|
+
props.pathname !== undefined
|
|
67
|
+
? props.pathname
|
|
68
|
+
: props.origin
|
|
69
|
+
? ""
|
|
70
|
+
: undefined,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get origin(): string | undefined {
|
|
75
|
+
return this.props.origin
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get pathname(): string | undefined {
|
|
79
|
+
return this.props.pathname
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get slug(): string | undefined {
|
|
83
|
+
return this.props.pathname?.split("/").pop()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get originAndPath(): string | undefined {
|
|
87
|
+
const strings = excludeUndefined([this.origin, this.pathname])
|
|
88
|
+
if (strings.length === 0) return undefined
|
|
89
|
+
return strings.join("")
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get queryStr(): string {
|
|
93
|
+
const { queryStr } = this.props
|
|
94
|
+
// Drop a single trailing `?`, if there is one
|
|
95
|
+
return queryStr && queryStr !== "?"
|
|
96
|
+
? ensureQueryStrFormat(queryStr)
|
|
97
|
+
: ""
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get hash(): string {
|
|
101
|
+
const { hash } = this.props
|
|
102
|
+
return hash ? ensureHashFormat(hash) : ""
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get fullUrl(): string {
|
|
106
|
+
return excludeUndefined([
|
|
107
|
+
this.origin,
|
|
108
|
+
this.pathname,
|
|
109
|
+
this.queryStr,
|
|
110
|
+
this.hash,
|
|
111
|
+
]).join("")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
get fullUrlNoTrailingSlash(): string {
|
|
115
|
+
return this.fullUrl.replace(/\/$/, "") || "/"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get queryParams(): QueryParams {
|
|
119
|
+
return strToQueryParams(this.queryStr)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get encodedQueryParams(): QueryParams {
|
|
123
|
+
return strToQueryParams(this.queryStr, true)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get isGoogleDoc(): boolean {
|
|
127
|
+
return !!(this.pathname && gdocUrlRegex.test(this.fullUrl))
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
get isGrapher(): boolean {
|
|
131
|
+
return !!(this.pathname && /^\/grapher\/[\w]+/.test(this.pathname))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get isDod(): boolean {
|
|
135
|
+
return !!(this.fullUrl && detailOnDemandRegex.test(this.fullUrl))
|
|
136
|
+
}
|
|
137
|
+
get isGuidedChart(): boolean {
|
|
138
|
+
return !!(this.fullUrl && guidedChartRegex.test(this.fullUrl))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get isUpload(): boolean {
|
|
142
|
+
return !!(this.pathname && /^\/uploads\/[\w]+/.test(this.pathname))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// todo(refactor): move outisde of generic Url class
|
|
146
|
+
// see EXPLORERS_ROUTE_FOLDER
|
|
147
|
+
get isExplorer(): boolean {
|
|
148
|
+
return !!(this.pathname && /^\/explorers\/[\w]+/.test(this.pathname))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
update(props: UrlProps): Url {
|
|
152
|
+
return new Url({
|
|
153
|
+
...this.props,
|
|
154
|
+
...props,
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setQueryParams(queryParams: QueryParams): Url {
|
|
159
|
+
return new Url({
|
|
160
|
+
...this.props,
|
|
161
|
+
queryStr: queryParamsToStr(queryParams),
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
updateQueryParams(queryParams: QueryParams): Url {
|
|
166
|
+
return this.update({
|
|
167
|
+
queryStr: queryParamsToStr(
|
|
168
|
+
omitUndefinedValues({
|
|
169
|
+
...this.queryParams,
|
|
170
|
+
...queryParams,
|
|
171
|
+
})
|
|
172
|
+
),
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
areQueryParamsEqual(otherUrl: Url): boolean {
|
|
177
|
+
const thisParams = this.queryParams
|
|
178
|
+
const otherParams = otherUrl.queryParams
|
|
179
|
+
|
|
180
|
+
return R.isShallowEqual(thisParams, otherParams)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const setWindowUrl = (url: Url): void => {
|
|
185
|
+
const pathname = url.pathname ?? window.location.pathname
|
|
186
|
+
window.history.replaceState(
|
|
187
|
+
null,
|
|
188
|
+
document.title,
|
|
189
|
+
excludeUndefined([pathname, url.queryStr, url.hash]).join("")
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export const getWindowUrl = (): Url => {
|
|
194
|
+
return Url.fromURL(window.location.href)
|
|
195
|
+
}
|