@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,121 @@
|
|
|
1
|
+
import { numberMagnitude, RequiredBy } from "../../utils/index.js"
|
|
2
|
+
import {
|
|
3
|
+
BinningStrategyIncludingManual,
|
|
4
|
+
LogBinningStrategy,
|
|
5
|
+
ResolvedLogBinningStrategy,
|
|
6
|
+
} from "../../types/index.js"
|
|
7
|
+
import {
|
|
8
|
+
calcMagnitudeDiff,
|
|
9
|
+
pruneUnusedBins,
|
|
10
|
+
ResolvedBinningStrategyConfig,
|
|
11
|
+
} from "./BinningStrategies.js"
|
|
12
|
+
import * as R from "remeda"
|
|
13
|
+
import { match } from "ts-pattern"
|
|
14
|
+
|
|
15
|
+
export const isLogBinningStrategy = (
|
|
16
|
+
strategy: BinningStrategyIncludingManual
|
|
17
|
+
): strategy is LogBinningStrategy => strategy.startsWith("log-")
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Automatically chooses a log binning strategy based on the magnitude difference between the min and max values.
|
|
21
|
+
* Very roughly, the resulting number of bins is roughly magnitudeDiff * numberOfLogSteps.
|
|
22
|
+
*
|
|
23
|
+
* The threshold numbers in here are chosen empirically by experimentation.
|
|
24
|
+
* The main idea is that we want to ideally have 5-8 bins, and with the number of resulting bins very roughly
|
|
25
|
+
* being `magnitudeDiff * numberOfLogSteps`, we arrive roughly at these thresholds:
|
|
26
|
+
* - for magnitudeDiff < 2.6, and 3 log steps, we'll end up with up to 8 bins
|
|
27
|
+
* - for magnitudeDiff between 2.6 and 3.6, and 2 log steps, we'll end up with 5 to 8 bins
|
|
28
|
+
* - for magnitudeDiff >= 3.6, and 1 log step, we'll end up with >=4 bins
|
|
29
|
+
* - ... there's probably also cases where we can end up with more than 8 bins, but these should be pretty rare
|
|
30
|
+
* (and in these case, the authors can still manually choose a different strategy)
|
|
31
|
+
*/
|
|
32
|
+
const autoChooseLogBinningStrategy = (
|
|
33
|
+
magnitudeDiff: number
|
|
34
|
+
): ResolvedLogBinningStrategy => {
|
|
35
|
+
if (magnitudeDiff >= 3.6) {
|
|
36
|
+
return "log-10"
|
|
37
|
+
}
|
|
38
|
+
if (magnitudeDiff >= 2.6) {
|
|
39
|
+
return "log-1-3"
|
|
40
|
+
}
|
|
41
|
+
return "log-1-2-5"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const runLogBinningStrategy = (
|
|
45
|
+
conf: RequiredBy<ResolvedBinningStrategyConfig, "minValue" | "maxValue">,
|
|
46
|
+
{ hasMidpoint }: { hasMidpoint?: boolean } = {}
|
|
47
|
+
): number[] => {
|
|
48
|
+
const { minValue, maxValue } = conf
|
|
49
|
+
|
|
50
|
+
if (minValue <= 0 || maxValue <= 0) {
|
|
51
|
+
throw new Error("Log binning strategy only supports positive values")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let resolvedStrategy: ResolvedLogBinningStrategy
|
|
55
|
+
if (conf.strategy === "log-auto") {
|
|
56
|
+
let magnitudeDiff = calcMagnitudeDiff(minValue, maxValue)
|
|
57
|
+
if (hasMidpoint) magnitudeDiff *= 1.8 // If there is a midpoint, we want to create fewer bins than we would otherwise
|
|
58
|
+
resolvedStrategy = autoChooseLogBinningStrategy(magnitudeDiff)
|
|
59
|
+
} else {
|
|
60
|
+
resolvedStrategy = conf.strategy as ResolvedLogBinningStrategy
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const bins = match(resolvedStrategy)
|
|
64
|
+
.with("log-10", () =>
|
|
65
|
+
createLogBins({ minValue, maxValue, logSteps: [1] })
|
|
66
|
+
)
|
|
67
|
+
.with("log-1-3", () =>
|
|
68
|
+
createLogBins({ minValue, maxValue, logSteps: [1, 3] })
|
|
69
|
+
)
|
|
70
|
+
.with("log-1-2-5", () =>
|
|
71
|
+
createLogBins({ minValue, maxValue, logSteps: [1, 2, 5] })
|
|
72
|
+
)
|
|
73
|
+
.exhaustive()
|
|
74
|
+
|
|
75
|
+
// Add the midpoint (most likely zero) if it is not already included in a bin
|
|
76
|
+
if (
|
|
77
|
+
!hasMidpoint &&
|
|
78
|
+
conf.midpoint !== undefined &&
|
|
79
|
+
bins[0] > conf.midpoint
|
|
80
|
+
) {
|
|
81
|
+
bins.unshift(conf.midpoint)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return bins
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const createLogBins = ({
|
|
88
|
+
minValue,
|
|
89
|
+
maxValue,
|
|
90
|
+
logSteps,
|
|
91
|
+
}: {
|
|
92
|
+
minValue: number
|
|
93
|
+
maxValue: number
|
|
94
|
+
logSteps: number[]
|
|
95
|
+
}): number[] => {
|
|
96
|
+
if (minValue <= 0 || maxValue <= 0) {
|
|
97
|
+
throw new Error("createLogBins only supports positive values")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// We need the -1 here to convert between magnitude and logarithms.
|
|
101
|
+
// Magnitude is defined such that magnitude(1) = 1, whereas log10(1) = 0.
|
|
102
|
+
// Because we generate factors as 10^magnitude, we need to adjust the values accordingly.
|
|
103
|
+
const exponentMin = numberMagnitude(minValue) - 1
|
|
104
|
+
const exponentMax = numberMagnitude(maxValue) - 1
|
|
105
|
+
|
|
106
|
+
const candidates = R.range(exponentMin, exponentMax + 1).flatMap(
|
|
107
|
+
(magnitude) => {
|
|
108
|
+
const factor = Math.pow(10, magnitude)
|
|
109
|
+
return logSteps.map((step) => step * factor)
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// Adding this extra value at the end is useful if we have, for example, maxValue = 99.
|
|
114
|
+
// Then the candidates above will go up to 50 (if logSteps includes 5), and here we'll then
|
|
115
|
+
// add 100 to the mix.
|
|
116
|
+
if ((R.last(candidates) ?? 0) < maxValue) {
|
|
117
|
+
candidates.push(1 * Math.pow(10, exponentMax + 1))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return pruneUnusedBins(candidates, { minValue, maxValue })
|
|
121
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as R from "remeda"
|
|
2
|
+
import { Color } from "../../utils/index.js"
|
|
3
|
+
import { ColorScheme } from "./ColorScheme"
|
|
4
|
+
import { getLeastUsedColor } from "./ColorUtils"
|
|
5
|
+
|
|
6
|
+
type CategoryId = string
|
|
7
|
+
export type CategoricalColorMap = Map<CategoryId, Color>
|
|
8
|
+
export type CategoricalColorMapReadonly = ReadonlyMap<CategoryId, Color>
|
|
9
|
+
|
|
10
|
+
export interface CategoricalColorAssignerProps {
|
|
11
|
+
colorScheme: ColorScheme
|
|
12
|
+
invertColorScheme?: boolean
|
|
13
|
+
|
|
14
|
+
/** The custom color mappings (most likely author-specified) to use. */
|
|
15
|
+
colorMap?: CategoricalColorMap
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A cache for custom colors or automatically selected colors for each identifier
|
|
19
|
+
* encountered.
|
|
20
|
+
*
|
|
21
|
+
* In the Grapher, this is persisted across charts, so that a line chart
|
|
22
|
+
* that turns into a bar chart will have a matching color scheme across
|
|
23
|
+
* both states.
|
|
24
|
+
*/
|
|
25
|
+
autoColorMapCache?: CategoricalColorMap
|
|
26
|
+
|
|
27
|
+
numColorsInUse?: number
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Assigns custom categorical colors, e.g. specified by an author for entities or variables.
|
|
32
|
+
*
|
|
33
|
+
* When an identifier doesn't have an assigned color, it uses the least used color in the scheme.
|
|
34
|
+
*
|
|
35
|
+
* Keeps a cache so that identical identifiers are assigned consistent colors. See
|
|
36
|
+
* Grapher#seriesColorMap for an example of a cache.
|
|
37
|
+
*/
|
|
38
|
+
export class CategoricalColorAssigner {
|
|
39
|
+
private colorScheme: ColorScheme
|
|
40
|
+
private invertColorScheme: boolean
|
|
41
|
+
private colorMap: CategoricalColorMapReadonly
|
|
42
|
+
private autoColorMapCache: CategoricalColorMap
|
|
43
|
+
private numColorsInUse?: number
|
|
44
|
+
|
|
45
|
+
constructor(props: CategoricalColorAssignerProps) {
|
|
46
|
+
this.colorScheme = props.colorScheme
|
|
47
|
+
this.invertColorScheme = props.invertColorScheme ?? false
|
|
48
|
+
this.colorMap = props.colorMap ?? new Map()
|
|
49
|
+
this.autoColorMapCache = props.autoColorMapCache ?? new Map()
|
|
50
|
+
this.numColorsInUse = props.numColorsInUse
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private get usedColors(): Color[] {
|
|
54
|
+
const merged: CategoricalColorMap = new Map([
|
|
55
|
+
...this.autoColorMapCache,
|
|
56
|
+
...this.colorMap,
|
|
57
|
+
])
|
|
58
|
+
return Array.from(merged.values())
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private get availableColors(): Color[] {
|
|
62
|
+
// copy the colors array because we might need to reverse it
|
|
63
|
+
const colors =
|
|
64
|
+
(this.numColorsInUse !== undefined
|
|
65
|
+
? this.colorScheme.getColors(this.numColorsInUse)
|
|
66
|
+
: R.last(this.colorScheme.colorSets)
|
|
67
|
+
)?.slice() ?? []
|
|
68
|
+
if (this.invertColorScheme) return colors.toReversed()
|
|
69
|
+
else return colors
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private get leastUsedColor(): Color {
|
|
73
|
+
const leastUsedColor = getLeastUsedColor(
|
|
74
|
+
this.availableColors,
|
|
75
|
+
this.usedColors
|
|
76
|
+
)
|
|
77
|
+
// TODO handle this better?
|
|
78
|
+
if (leastUsedColor === undefined) {
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.trace("Least used color is undefined, using black.", {
|
|
81
|
+
availableColors: this.availableColors,
|
|
82
|
+
usedColors: this.usedColors,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
return leastUsedColor ?? "#000"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
assign(id: CategoryId): Color {
|
|
89
|
+
let color = this.colorMap.get(id)
|
|
90
|
+
if (color === undefined) color = this.autoColorMapCache.get(id)
|
|
91
|
+
if (color === undefined && this.colorScheme.colorMap)
|
|
92
|
+
color = this.colorScheme.colorMap[id]
|
|
93
|
+
if (color === undefined) color = this.leastUsedColor
|
|
94
|
+
this.autoColorMapCache.set(id, color)
|
|
95
|
+
return color
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import colorbrewer from "colorbrewer"
|
|
2
|
+
import {
|
|
3
|
+
Color,
|
|
4
|
+
ColorSchemeInterface,
|
|
5
|
+
ColorSchemeName,
|
|
6
|
+
} from "../../types/index.js"
|
|
7
|
+
|
|
8
|
+
type ColorSchemeProps = { displayName: string; singleColorScale: boolean }
|
|
9
|
+
|
|
10
|
+
const ColorBrewerSchemeIndex: {
|
|
11
|
+
[key in ColorSchemeName]?: ColorSchemeProps
|
|
12
|
+
} = {
|
|
13
|
+
YlGn: { displayName: "Yellow-Green shades", singleColorScale: true },
|
|
14
|
+
YlGnBu: {
|
|
15
|
+
displayName: "Yellow-Green-Blue shades",
|
|
16
|
+
singleColorScale: false,
|
|
17
|
+
},
|
|
18
|
+
GnBu: { displayName: "Green-Blue shades", singleColorScale: true },
|
|
19
|
+
BuGn: { displayName: "Blue-Green shades", singleColorScale: true },
|
|
20
|
+
PuBuGn: {
|
|
21
|
+
displayName: "Purple-Blue-Green shades",
|
|
22
|
+
singleColorScale: false,
|
|
23
|
+
},
|
|
24
|
+
BuPu: { displayName: "Blue-Purple shades", singleColorScale: true },
|
|
25
|
+
RdPu: { displayName: "Red-Purple shades", singleColorScale: true },
|
|
26
|
+
PuRd: { displayName: "Purple-Red shades", singleColorScale: true },
|
|
27
|
+
OrRd: { displayName: "Orange-Red shades", singleColorScale: true },
|
|
28
|
+
YlOrRd: {
|
|
29
|
+
displayName: "Yellow-Orange-Red shades",
|
|
30
|
+
singleColorScale: true,
|
|
31
|
+
},
|
|
32
|
+
YlOrBr: {
|
|
33
|
+
displayName: "Yellow-Orange-Brown shades",
|
|
34
|
+
singleColorScale: true,
|
|
35
|
+
},
|
|
36
|
+
Purples: { displayName: "Purple shades", singleColorScale: true },
|
|
37
|
+
Blues: { displayName: "Blue shades", singleColorScale: true },
|
|
38
|
+
Greens: { displayName: "Green shades", singleColorScale: true },
|
|
39
|
+
Oranges: { displayName: "Orange shades", singleColorScale: true },
|
|
40
|
+
Reds: { displayName: "Red shades", singleColorScale: true },
|
|
41
|
+
Greys: { displayName: "Grey shades", singleColorScale: true },
|
|
42
|
+
PuOr: { displayName: "Purple-Orange", singleColorScale: false },
|
|
43
|
+
BrBG: { displayName: "Brown-Blue-Green", singleColorScale: false },
|
|
44
|
+
PRGn: { displayName: "Purple-Red-Green", singleColorScale: false },
|
|
45
|
+
PiYG: { displayName: "Magenta-Yellow-Green", singleColorScale: false },
|
|
46
|
+
RdBu: { displayName: "Red-Blue", singleColorScale: false },
|
|
47
|
+
RdGy: { displayName: "Red-Grey", singleColorScale: false },
|
|
48
|
+
RdYlBu: { displayName: "Red-Yellow-Blue", singleColorScale: false },
|
|
49
|
+
Spectral: { displayName: "Spectral colors", singleColorScale: false },
|
|
50
|
+
RdYlGn: { displayName: "Red-Yellow-Green", singleColorScale: false },
|
|
51
|
+
Accent: { displayName: "Accents", singleColorScale: false },
|
|
52
|
+
Dark2: { displayName: "Dark colors", singleColorScale: false },
|
|
53
|
+
Paired: { displayName: "Paired colors", singleColorScale: false },
|
|
54
|
+
Pastel1: { displayName: "Pastel 1 colors", singleColorScale: false },
|
|
55
|
+
Pastel2: { displayName: "Pastel 2 colors", singleColorScale: false },
|
|
56
|
+
Set1: { displayName: "Set 1 colors", singleColorScale: false },
|
|
57
|
+
Set2: { displayName: "Set 2 colors", singleColorScale: false },
|
|
58
|
+
Set3: { displayName: "Set 3 colors", singleColorScale: false },
|
|
59
|
+
PuBu: { displayName: "Purple-Blue shades", singleColorScale: true },
|
|
60
|
+
} as const
|
|
61
|
+
|
|
62
|
+
export const getColorBrewerScheme: (
|
|
63
|
+
name: string
|
|
64
|
+
) => ColorSchemeInterface | undefined = (name: string) => {
|
|
65
|
+
const props = ColorBrewerSchemeIndex[name as ColorSchemeName]
|
|
66
|
+
const colorSets = (colorbrewer as any)[name]
|
|
67
|
+
|
|
68
|
+
if (!props || !colorSets) return undefined
|
|
69
|
+
|
|
70
|
+
const colorSetsArray: Color[][] = []
|
|
71
|
+
Object.keys(colorSets).forEach(
|
|
72
|
+
(numColors) => (colorSetsArray[+numColors] = colorSets[numColors])
|
|
73
|
+
)
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
displayName: props.displayName,
|
|
77
|
+
colorSets: colorSetsArray,
|
|
78
|
+
singleColorScale: props.singleColorScale,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// gray shades
|
|
2
|
+
export const GRAY_100 = "#2d2e2d"
|
|
3
|
+
export const GRAY_90 = "#4e4e4e"
|
|
4
|
+
export const GRAY_80 = "#5b5b5b"
|
|
5
|
+
export const GRAY_70 = "#767676"
|
|
6
|
+
export const GRAY_60 = "#a1a1a1"
|
|
7
|
+
export const GRAY_50 = "#c6c6c6"
|
|
8
|
+
export const GRAY_30 = "#dadada"
|
|
9
|
+
export const GRAY_20 = "#e7e7e7"
|
|
10
|
+
export const GRAY_10 = "#f2f2f2"
|
|
11
|
+
export const GRAY_5 = "#f7f7f7"
|
|
12
|
+
|
|
13
|
+
export const GRAPHER_BACKGROUND_DEFAULT = "#ffffff"
|
|
14
|
+
export const GRAPHER_BACKGROUND_BEIGE = "#fbf9f3"
|
|
15
|
+
|
|
16
|
+
export const GRAPHER_DARK_TEXT = GRAY_80
|
|
17
|
+
export const GRAPHER_LIGHT_TEXT = GRAY_70
|
|
18
|
+
|
|
19
|
+
export const OWID_NO_DATA_GRAY = "#6e7581"
|
|
20
|
+
export const OWID_ERROR_COLOR = "ff0002"
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import * as _ from "lodash-es"
|
|
2
|
+
import { computed, toJS, makeObservable } from "mobx"
|
|
3
|
+
import { ColorScaleConfig } from "./ColorScaleConfig"
|
|
4
|
+
import { mapNullToUndefined, sortNumeric } from "../../utils/index.js"
|
|
5
|
+
import { pairs } from "d3-array"
|
|
6
|
+
import { ColorSchemes } from "../color/ColorSchemes"
|
|
7
|
+
import { ColorScheme } from "../color/ColorScheme"
|
|
8
|
+
import { ColorScaleBin, NumericBin, CategoricalBin } from "./ColorScaleBin"
|
|
9
|
+
import { OWID_NO_DATA_GRAY } from "./ColorConstants"
|
|
10
|
+
import {
|
|
11
|
+
ColorScaleConfigInterface,
|
|
12
|
+
ColorSchemeName,
|
|
13
|
+
Color,
|
|
14
|
+
CoreValueType,
|
|
15
|
+
OwidVariableRoundingMode,
|
|
16
|
+
} from "../../types/index.js"
|
|
17
|
+
import { CoreColumn } from "../../core-table/index.js"
|
|
18
|
+
import * as R from "remeda"
|
|
19
|
+
import { runBinningStrategy } from "./BinningStrategies.js"
|
|
20
|
+
|
|
21
|
+
export const NO_DATA_LABEL = "No data"
|
|
22
|
+
export const PROJECTED_DATA_LABEL = "Projected data"
|
|
23
|
+
|
|
24
|
+
export interface ColorScaleManager {
|
|
25
|
+
colorScaleConfig?: ColorScaleConfigInterface
|
|
26
|
+
hasNoDataBin?: boolean
|
|
27
|
+
hasProjectedDataBin?: boolean
|
|
28
|
+
defaultNoDataColor?: string
|
|
29
|
+
defaultBaseColorScheme?: ColorSchemeName
|
|
30
|
+
colorScaleColumn?: CoreColumn
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class ColorScale {
|
|
34
|
+
private manager: Readonly<ColorScaleManager>
|
|
35
|
+
constructor(manager: ColorScaleManager = {}) {
|
|
36
|
+
makeObservable(this)
|
|
37
|
+
this.manager = manager
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Config accessors
|
|
41
|
+
|
|
42
|
+
@computed get config(): ColorScaleConfigInterface {
|
|
43
|
+
return this.manager.colorScaleConfig ?? new ColorScaleConfig()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@computed get customNumericValues(): number[] {
|
|
47
|
+
return this.config.customNumericValues ?? []
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@computed get customNumericColorsActive(): boolean {
|
|
51
|
+
return this.config.customNumericColorsActive ?? false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@computed get customNumericColors(): (Color | undefined)[] {
|
|
55
|
+
return this.customNumericColorsActive
|
|
56
|
+
? mapNullToUndefined(this.config.customNumericColors)
|
|
57
|
+
: []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@computed get customHiddenCategories(): {
|
|
61
|
+
[key: string]: true | undefined
|
|
62
|
+
} {
|
|
63
|
+
return this.config.customHiddenCategories ?? {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@computed get customNumericLabels(): (string | undefined)[] {
|
|
67
|
+
if (!this.isManualBuckets) return []
|
|
68
|
+
|
|
69
|
+
const labels =
|
|
70
|
+
mapNullToUndefined(toJS(this.config.customNumericLabels)) || []
|
|
71
|
+
while (labels.length < this.numNumericBins) labels.push(undefined)
|
|
72
|
+
return labels
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@computed get isColorSchemeInverted(): boolean {
|
|
76
|
+
return this.config.colorSchemeInvert ?? false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@computed private get customCategoryLabels(): {
|
|
80
|
+
[key: string]: string | undefined
|
|
81
|
+
} {
|
|
82
|
+
return this.config.customCategoryLabels ?? {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@computed get baseColorScheme(): ColorSchemeName {
|
|
86
|
+
return (
|
|
87
|
+
this.config.baseColorScheme ??
|
|
88
|
+
this.manager.defaultBaseColorScheme ??
|
|
89
|
+
ColorSchemeName.BuGn
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@computed private get defaultColorScheme(): ColorScheme {
|
|
94
|
+
return ColorSchemes.get(ColorSchemeName.BuGn)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@computed private get defaultNoDataColor(): Color {
|
|
98
|
+
return this.manager.defaultNoDataColor ?? OWID_NO_DATA_GRAY
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@computed get colorScaleColumn(): CoreColumn | undefined {
|
|
102
|
+
return this.manager.colorScaleColumn
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@computed get legendDescription(): string | undefined {
|
|
106
|
+
return this.config.legendDescription
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Transforms
|
|
110
|
+
|
|
111
|
+
@computed private get hasNoDataBin(): boolean {
|
|
112
|
+
return this.manager.hasNoDataBin || false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@computed private get hasProjectedDataBin(): boolean {
|
|
116
|
+
return this.manager.hasProjectedDataBin || false
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@computed get sortedNumericValues(): number[] {
|
|
120
|
+
return sortNumeric(
|
|
121
|
+
this.colorScaleColumn?.values.filter(R.isNumber) ?? []
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@computed private get minPossibleValue(): number | undefined {
|
|
126
|
+
return R.first(this.sortedNumericValues)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@computed private get maxPossibleValue(): number | undefined {
|
|
130
|
+
return R.last(this.sortedNumericValues)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@computed private get categoricalValues(): string[] {
|
|
134
|
+
return this.colorScaleColumn?.sortedUniqNonEmptyStringVals ?? []
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@computed private get colorScheme(): ColorScheme {
|
|
138
|
+
return ColorSchemes.get(this.baseColorScheme) ?? this.defaultColorScheme
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@computed get singleColorScale(): boolean {
|
|
142
|
+
return this.colorScheme.singleColorScale
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@computed private get manualBinThresholds(): number[] {
|
|
146
|
+
if (!this.sortedNumericValues.length || this.numNumericBins <= 0)
|
|
147
|
+
return []
|
|
148
|
+
|
|
149
|
+
return this.customNumericValues
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// When automatic classification is turned on, this takes the numeric map data
|
|
153
|
+
// and works out some discrete ranges to assign colors to
|
|
154
|
+
@computed get autoBinThresholds(): number[] {
|
|
155
|
+
if (this.config.binningStrategy === "manual") {
|
|
156
|
+
throw new Error(
|
|
157
|
+
"Cannot compute automatic bin thresholds when binning is set to manual"
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
return runBinningStrategy({
|
|
161
|
+
sortedValues: this.sortedNumericValues,
|
|
162
|
+
isPercent: this.colorScaleColumn?.shortUnit === "%",
|
|
163
|
+
numDecimalPlaces: this.colorScaleColumn?.numDecimalPlaces,
|
|
164
|
+
|
|
165
|
+
strategy: this.config.binningStrategy,
|
|
166
|
+
createBinForMidpoint: this.config.createBinForMidpoint,
|
|
167
|
+
minValue: this.config.minValue,
|
|
168
|
+
maxValue: this.config.maxValue,
|
|
169
|
+
midpoint: this.config.midpoint,
|
|
170
|
+
midpointMode: this.config.midpointMode,
|
|
171
|
+
}).bins
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@computed private get bucketThresholds(): number[] {
|
|
175
|
+
return this.isManualBuckets
|
|
176
|
+
? this.manualBinThresholds
|
|
177
|
+
: this.autoBinThresholds
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Ensure there's always a custom color for "No data"
|
|
181
|
+
@computed private get customCategoryColors(): { [key: string]: Color } {
|
|
182
|
+
return {
|
|
183
|
+
[NO_DATA_LABEL]: this.defaultNoDataColor, // default 'no data' color
|
|
184
|
+
...this.config.customCategoryColors,
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@computed get noDataColor(): Color {
|
|
189
|
+
return this.customCategoryColors[NO_DATA_LABEL]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@computed get baseColors(): Color[] {
|
|
193
|
+
const { categoricalValues, colorScheme, isColorSchemeInverted } = this
|
|
194
|
+
const numColors = this.numNumericBins + categoricalValues.length
|
|
195
|
+
const colors = colorScheme.getColors(numColors)
|
|
196
|
+
|
|
197
|
+
if (isColorSchemeInverted) return colors.toReversed()
|
|
198
|
+
else return colors
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@computed get isManualBuckets(): boolean {
|
|
202
|
+
return this.config.binningStrategy === "manual"
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@computed get numNumericBins(): number {
|
|
206
|
+
if (!this.sortedNumericValues.length) return 0
|
|
207
|
+
|
|
208
|
+
return this.isManualBuckets
|
|
209
|
+
? Math.max(this.customNumericValues.length - 1, 0)
|
|
210
|
+
: this.autoBinThresholds.length - 1
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@computed private get numericLegendBins(): NumericBin[] {
|
|
214
|
+
const {
|
|
215
|
+
customNumericLabels,
|
|
216
|
+
minPossibleValue,
|
|
217
|
+
maxPossibleValue,
|
|
218
|
+
customNumericColors,
|
|
219
|
+
bucketThresholds,
|
|
220
|
+
baseColors,
|
|
221
|
+
} = this
|
|
222
|
+
|
|
223
|
+
if (minPossibleValue === undefined || maxPossibleValue === undefined)
|
|
224
|
+
return []
|
|
225
|
+
|
|
226
|
+
return pairs(bucketThresholds).map(([min, max], index) => {
|
|
227
|
+
const baseColor = baseColors[index]
|
|
228
|
+
const color = customNumericColors[index] ?? baseColor
|
|
229
|
+
const label = customNumericLabels[index]
|
|
230
|
+
|
|
231
|
+
const roundingOptions = {
|
|
232
|
+
roundingMode: OwidVariableRoundingMode.decimalPlaces,
|
|
233
|
+
}
|
|
234
|
+
const displayMin =
|
|
235
|
+
this.colorScaleColumn?.formatValueShort(min, roundingOptions) ??
|
|
236
|
+
min.toString()
|
|
237
|
+
const displayMax =
|
|
238
|
+
this.colorScaleColumn?.formatValueShort(max, roundingOptions) ??
|
|
239
|
+
max.toString()
|
|
240
|
+
|
|
241
|
+
const isFirst = index === 0
|
|
242
|
+
const isLast = index === bucketThresholds.length - 2
|
|
243
|
+
return new NumericBin({
|
|
244
|
+
isFirst,
|
|
245
|
+
isOpenLeft: isFirst && min > minPossibleValue,
|
|
246
|
+
isOpenRight: isLast && max < maxPossibleValue,
|
|
247
|
+
min,
|
|
248
|
+
max,
|
|
249
|
+
color,
|
|
250
|
+
label,
|
|
251
|
+
displayMin,
|
|
252
|
+
displayMax,
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@computed get legendBins(): ColorScaleBin[] {
|
|
258
|
+
// todo: turn comment into unit test
|
|
259
|
+
// Will eventually produce something like this:
|
|
260
|
+
// [{ min: 10, max: 20, minText: "10%", maxText: "20%", color: '#faeaef' },
|
|
261
|
+
// { min: 20, max: 30, minText: "20%", maxText: "30%", color: '#fefabc' },
|
|
262
|
+
// { value: 'Foobar', text: "Foobar Boop", color: '#bbbbbb'}]
|
|
263
|
+
return [
|
|
264
|
+
...this.numericLegendBins,
|
|
265
|
+
...this.categoricalLegendBins,
|
|
266
|
+
] as ColorScaleBin[]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@computed get categoricalLegendBins(): CategoricalBin[] {
|
|
270
|
+
const {
|
|
271
|
+
bucketThresholds,
|
|
272
|
+
baseColors,
|
|
273
|
+
hasNoDataBin,
|
|
274
|
+
hasProjectedDataBin,
|
|
275
|
+
categoricalValues,
|
|
276
|
+
customCategoryColors,
|
|
277
|
+
customCategoryLabels,
|
|
278
|
+
customHiddenCategories,
|
|
279
|
+
colorScheme,
|
|
280
|
+
} = this
|
|
281
|
+
|
|
282
|
+
let allCategoricalValues = categoricalValues
|
|
283
|
+
|
|
284
|
+
// Inject "No data" bin
|
|
285
|
+
if (hasNoDataBin && !allCategoricalValues.includes(NO_DATA_LABEL)) {
|
|
286
|
+
// The color scheme colors get applied in order, starting from first, and we only use
|
|
287
|
+
// as many colors as there are categorical values (excluding "No data").
|
|
288
|
+
// So in order to leave it colorless, we want to append the "No data" label last.
|
|
289
|
+
// -@danielgavrilov, 2020-06-02
|
|
290
|
+
allCategoricalValues = [...allCategoricalValues, NO_DATA_LABEL]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Inject "Projected data" bin
|
|
294
|
+
if (hasProjectedDataBin) {
|
|
295
|
+
allCategoricalValues = [
|
|
296
|
+
...allCategoricalValues,
|
|
297
|
+
PROJECTED_DATA_LABEL,
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return allCategoricalValues.map((value, index) => {
|
|
302
|
+
const boundingOffset = _.isEmpty(bucketThresholds)
|
|
303
|
+
? 0
|
|
304
|
+
: bucketThresholds.length - 1
|
|
305
|
+
|
|
306
|
+
// Use colorMap if available and the value exists in it
|
|
307
|
+
let baseColor: Color | undefined
|
|
308
|
+
if (colorScheme.colorMap && value in colorScheme.colorMap) {
|
|
309
|
+
baseColor = colorScheme.colorMap[value]
|
|
310
|
+
} else {
|
|
311
|
+
baseColor = baseColors[index + boundingOffset]
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const color = customCategoryColors[value] ?? baseColor
|
|
315
|
+
const label = customCategoryLabels[value] ?? value
|
|
316
|
+
|
|
317
|
+
return new CategoricalBin({
|
|
318
|
+
index,
|
|
319
|
+
value,
|
|
320
|
+
color,
|
|
321
|
+
label,
|
|
322
|
+
isHidden: !!customHiddenCategories[value],
|
|
323
|
+
})
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
getBinForValue(
|
|
328
|
+
value: CoreValueType | undefined
|
|
329
|
+
): ColorScaleBin | undefined {
|
|
330
|
+
return value === undefined
|
|
331
|
+
? undefined
|
|
332
|
+
: this.legendBins.find((bin) => bin.contains(value))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
getColor(value: CoreValueType | undefined): string | undefined {
|
|
336
|
+
if (value === undefined) return this.noDataColor
|
|
337
|
+
return this.getBinForValue(value)?.color
|
|
338
|
+
}
|
|
339
|
+
}
|