@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,720 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Bounds,
|
|
3
|
+
getRelativeMouse,
|
|
4
|
+
guid,
|
|
5
|
+
exposeInstanceOnWindow,
|
|
6
|
+
Color,
|
|
7
|
+
HorizontalAlign,
|
|
8
|
+
} from "../../utils/index.js"
|
|
9
|
+
import { observable, computed, action, makeObservable } from "mobx"
|
|
10
|
+
import { observer } from "mobx-react"
|
|
11
|
+
import {
|
|
12
|
+
HorizontalCategoricalColorLegend,
|
|
13
|
+
HorizontalColorLegendManager,
|
|
14
|
+
HorizontalNumericColorLegend,
|
|
15
|
+
} from "../legend/HorizontalColorLegends"
|
|
16
|
+
import { select } from "d3-selection"
|
|
17
|
+
import { easeCubic } from "d3-ease"
|
|
18
|
+
import { MapTooltip } from "./MapTooltip"
|
|
19
|
+
import { TooltipState } from "../tooltip/Tooltip.js"
|
|
20
|
+
import { CoreColumn } from "../../core-table/index.js"
|
|
21
|
+
import {
|
|
22
|
+
GeoFeature,
|
|
23
|
+
MapBracket,
|
|
24
|
+
MapChartManager,
|
|
25
|
+
ChoroplethSeriesByName,
|
|
26
|
+
ChoroplethMapManager,
|
|
27
|
+
DEFAULT_STROKE_COLOR,
|
|
28
|
+
HOVER_STROKE_COLOR,
|
|
29
|
+
HOVER_STROKE_WIDTH,
|
|
30
|
+
MAP_CHART_CLASSNAME,
|
|
31
|
+
MapColumnInfo,
|
|
32
|
+
PROJECTED_DATA_LEGEND_COLOR,
|
|
33
|
+
MapViewport,
|
|
34
|
+
} from "./MapChartConstants"
|
|
35
|
+
import { MapConfig } from "./MapConfig"
|
|
36
|
+
import { ColorScale } from "../color/ColorScale"
|
|
37
|
+
import {
|
|
38
|
+
BASE_FONT_SIZE,
|
|
39
|
+
DEFAULT_GRAPHER_BOUNDS,
|
|
40
|
+
GRAPHER_MAX_TOOLTIP_WIDTH,
|
|
41
|
+
Patterns,
|
|
42
|
+
} from "../core/GrapherConstants"
|
|
43
|
+
import { ChartInterface } from "../chart/ChartInterface"
|
|
44
|
+
import {
|
|
45
|
+
CategoricalBin,
|
|
46
|
+
ColorScaleBin,
|
|
47
|
+
isCategoricalBin,
|
|
48
|
+
isNoDataBin,
|
|
49
|
+
isNumericBin,
|
|
50
|
+
isProjectedDataBin,
|
|
51
|
+
NumericBin,
|
|
52
|
+
} from "../color/ColorScaleBin"
|
|
53
|
+
import {
|
|
54
|
+
LegendInteractionState,
|
|
55
|
+
LegendStyleConfig,
|
|
56
|
+
} from "../legend/LegendInteractionState"
|
|
57
|
+
import {
|
|
58
|
+
ColumnSlug,
|
|
59
|
+
GrapherVariant,
|
|
60
|
+
MapRegionName,
|
|
61
|
+
} from "../../types/index.js"
|
|
62
|
+
import { ClipPath, makeClipPath } from "../chart/ChartUtils"
|
|
63
|
+
import { NoDataModal } from "../noDataModal/NoDataModal"
|
|
64
|
+
import { Component, createRef } from "react"
|
|
65
|
+
import { ChoroplethMap } from "./ChoroplethMap"
|
|
66
|
+
import { ChoroplethGlobe } from "./ChoroplethGlobe"
|
|
67
|
+
import { GlobeController } from "./GlobeController"
|
|
68
|
+
import { MapSelectionArray } from "../selection/MapSelectionArray.js"
|
|
69
|
+
import { match } from "ts-pattern"
|
|
70
|
+
import { makeProjectedDataPatternId } from "./MapComponents"
|
|
71
|
+
import { MapChartState } from "./MapChartState"
|
|
72
|
+
import { ChartComponentProps } from "../chart/ChartTypeMap.js"
|
|
73
|
+
import { InteractionState } from "../interaction/InteractionState"
|
|
74
|
+
|
|
75
|
+
export type MapChartProps = ChartComponentProps<MapChartState>
|
|
76
|
+
|
|
77
|
+
export const PADDING_BETWEEN_MAP_AND_LEGEND = 8
|
|
78
|
+
export const PADDING_BELOW_MAP_LEGEND = 4
|
|
79
|
+
export const PADDING_BETWEEN_MAP_LEGENDS = 4
|
|
80
|
+
export const MAP_LEGEND_MAX_WIDTH_RATIO = 0.95
|
|
81
|
+
|
|
82
|
+
@observer
|
|
83
|
+
export class MapChart
|
|
84
|
+
extends Component<MapChartProps>
|
|
85
|
+
implements
|
|
86
|
+
ChartInterface,
|
|
87
|
+
HorizontalColorLegendManager,
|
|
88
|
+
ChoroplethMapManager
|
|
89
|
+
{
|
|
90
|
+
constructor(props: MapChartProps) {
|
|
91
|
+
super(props)
|
|
92
|
+
|
|
93
|
+
makeObservable(this, {
|
|
94
|
+
hoverBracket: observable,
|
|
95
|
+
tooltipState: observable,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The currently hovered map bracket.
|
|
101
|
+
*
|
|
102
|
+
* Hovering a map bracket highlights all countries within that bracket on the map.
|
|
103
|
+
*/
|
|
104
|
+
hoverBracket: MapBracket | undefined = undefined
|
|
105
|
+
|
|
106
|
+
tooltipState = new TooltipState<{
|
|
107
|
+
featureId: string
|
|
108
|
+
}>()
|
|
109
|
+
|
|
110
|
+
@computed get chartState(): MapChartState {
|
|
111
|
+
return this.props.chartState
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@computed get selectionArray(): MapSelectionArray {
|
|
115
|
+
return this.chartState.selectionArray
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@computed get mapColumn(): CoreColumn {
|
|
119
|
+
return this.chartState.mapColumn
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@computed private get mapColumnSlug(): ColumnSlug {
|
|
123
|
+
return this.chartState.mapColumnSlug
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@computed private get mapColumnInfo(): MapColumnInfo {
|
|
127
|
+
return this.chartState.mapColumnInfo
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@computed get hasProjectedData(): boolean {
|
|
131
|
+
return this.mapColumnInfo.type !== "historical"
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@computed private get targetTime(): number | undefined {
|
|
135
|
+
return this.chartState.targetTime
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@computed private get bounds(): Bounds {
|
|
139
|
+
return this.props.bounds ?? DEFAULT_GRAPHER_BOUNDS
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@computed get choroplethData(): ChoroplethSeriesByName {
|
|
143
|
+
return this.chartState.seriesMap
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
base = createRef<SVGGElement>()
|
|
147
|
+
@action.bound onMapMouseOver(feature: GeoFeature): void {
|
|
148
|
+
if (feature.id !== undefined) {
|
|
149
|
+
const featureId = feature.id as string
|
|
150
|
+
this.mapConfig.hoverCountry = featureId
|
|
151
|
+
this.tooltipState.target = { featureId }
|
|
152
|
+
this.manager.logGrapherInteractionEvent?.(
|
|
153
|
+
"map_country_hover",
|
|
154
|
+
featureId
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@action.bound onMapMouseMove(ev: React.MouseEvent): void {
|
|
160
|
+
const ref = this.manager?.base?.current
|
|
161
|
+
if (ref) {
|
|
162
|
+
this.tooltipState.position = getRelativeMouse(ref, ev)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@action.bound onMapMouseLeave(): void {
|
|
167
|
+
this.mapConfig.hoverCountry = undefined
|
|
168
|
+
this.tooltipState.target = null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@computed private get manager(): MapChartManager {
|
|
172
|
+
return this.chartState.manager
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@computed get globeController(): GlobeController {
|
|
176
|
+
return this.manager.globeController ?? new GlobeController(this)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@computed get mapViewport(): MapViewport | undefined {
|
|
180
|
+
return this.manager.mapViewport
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@computed get isFaceted(): boolean | undefined {
|
|
184
|
+
return this.manager.isFaceted
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@computed get isMapSelectionEnabled(): boolean {
|
|
188
|
+
return !!this.manager.isMapSelectionEnabled
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
override componentWillUnmount(): void {
|
|
192
|
+
this.onMapMouseLeave()
|
|
193
|
+
this.onLegendMouseLeave()
|
|
194
|
+
document.removeEventListener("keydown", this.onDocumentKeyDown)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@action.bound onLegendMouseEnter(bracket: MapBracket): void {
|
|
198
|
+
this.manager.logGrapherInteractionEvent?.(
|
|
199
|
+
"map_legend_hover",
|
|
200
|
+
bracket.label
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@action.bound onLegendMouseOver(bracket: MapBracket): void {
|
|
205
|
+
this.hoverBracket = bracket
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@action.bound onLegendMouseLeave(): void {
|
|
209
|
+
this.hoverBracket = undefined
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@computed get mapConfig(): MapConfig {
|
|
213
|
+
return this.chartState.mapConfig
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@action.bound onRegionChange(value: MapRegionName): void {
|
|
217
|
+
this.mapConfig.region = value
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@computed private get colorScale(): ColorScale {
|
|
221
|
+
return this.chartState.colorScale
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@action.bound onDocumentKeyDown(e: KeyboardEvent): void {
|
|
225
|
+
// Hide the globe on hitting the Escape key
|
|
226
|
+
if (e.key === "Escape" && this.mapConfig.globe.isActive) {
|
|
227
|
+
this.globeController.hideGlobe()
|
|
228
|
+
this.globeController.resetGlobe()
|
|
229
|
+
this.mapConfig.region = MapRegionName.World
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@computed get externalLegend(): HorizontalColorLegendManager | undefined {
|
|
234
|
+
const {
|
|
235
|
+
numericLegendData,
|
|
236
|
+
categoricalLegendData,
|
|
237
|
+
legendMaxWidth,
|
|
238
|
+
legendStyleConfig,
|
|
239
|
+
} = this
|
|
240
|
+
|
|
241
|
+
if (this.manager.showLegend) return undefined
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
numericLegendData,
|
|
245
|
+
categoricalLegendData,
|
|
246
|
+
legendMaxWidth,
|
|
247
|
+
legendStyleConfig,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
@computed private get disableIntroAnimation(): boolean {
|
|
252
|
+
// The intro animation transitions from a neutral color to the actual color.
|
|
253
|
+
// That doesn't work if a pattern is used to fill the country outlines,
|
|
254
|
+
// which is the case for projected data.
|
|
255
|
+
if (this.mapColumnInfo.type !== "historical") return true
|
|
256
|
+
|
|
257
|
+
return !!this.manager.disableIntroAnimation
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
override componentDidMount(): void {
|
|
261
|
+
if (!this.disableIntroAnimation) {
|
|
262
|
+
select(this.base.current)
|
|
263
|
+
.selectAll(`.${MAP_CHART_CLASSNAME} path`)
|
|
264
|
+
.attr("data-fill", function () {
|
|
265
|
+
return (this as SVGPathElement).getAttribute("fill")
|
|
266
|
+
})
|
|
267
|
+
.attr("fill", this.colorScale.noDataColor)
|
|
268
|
+
.transition()
|
|
269
|
+
.duration(500)
|
|
270
|
+
.ease(easeCubic)
|
|
271
|
+
.attr("fill", function () {
|
|
272
|
+
return (this as SVGPathElement).getAttribute("data-fill")
|
|
273
|
+
})
|
|
274
|
+
.attr("data-fill", function () {
|
|
275
|
+
return (this as SVGPathElement).getAttribute("fill")
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
exposeInstanceOnWindow(this)
|
|
279
|
+
|
|
280
|
+
document.addEventListener("keydown", this.onDocumentKeyDown)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
@computed private get legendData(): ColorScaleBin[] {
|
|
284
|
+
return this.colorScale.legendBins.filter((bin) => !bin.isHidden)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** The value of the currently hovered feature/country */
|
|
288
|
+
@computed private get hoverValue(): string | number | undefined {
|
|
289
|
+
if (!this.mapConfig.hoverCountry) return undefined
|
|
290
|
+
|
|
291
|
+
const series = this.choroplethData.get(this.mapConfig.hoverCountry)
|
|
292
|
+
if (!series) return "No data"
|
|
293
|
+
|
|
294
|
+
return series.value
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private isHovered(featureId: string): boolean {
|
|
298
|
+
const { mapConfig, hoverBracket } = this
|
|
299
|
+
const { externalLegendHoverBin } = this.manager
|
|
300
|
+
|
|
301
|
+
// Check if a country is focused on the globe
|
|
302
|
+
if (mapConfig.globe.focusCountry === featureId) return true
|
|
303
|
+
|
|
304
|
+
// Check if a country is hovered
|
|
305
|
+
if (mapConfig.hoverCountry === featureId) return true
|
|
306
|
+
|
|
307
|
+
// Check if the legend bracket of a country is hovered
|
|
308
|
+
const series = this.choroplethData.get(featureId)
|
|
309
|
+
if (
|
|
310
|
+
hoverBracket?.contains(series?.value, {
|
|
311
|
+
isProjection: series?.isProjection,
|
|
312
|
+
})
|
|
313
|
+
)
|
|
314
|
+
return true
|
|
315
|
+
|
|
316
|
+
// Check if the external legend bracket of a country is hovered (used in faceted maps)
|
|
317
|
+
if (externalLegendHoverBin?.contains(series?.value)) return true
|
|
318
|
+
|
|
319
|
+
return false
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
isSelected(featureId: string): boolean {
|
|
323
|
+
return this.selectionArray.selectedSet.has(featureId)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
getHoverState(featureId: string): InteractionState {
|
|
327
|
+
const isHovered = this.isHovered(featureId)
|
|
328
|
+
const isHoverModeActive = !!(
|
|
329
|
+
this.hoverBracket || this.manager.externalLegendHoverBin
|
|
330
|
+
)
|
|
331
|
+
return new InteractionState(isHovered, isHoverModeActive)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@computed get fontSize(): number {
|
|
335
|
+
return this.manager.fontSize ?? BASE_FONT_SIZE
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
@computed get choroplethMapBounds(): Bounds {
|
|
339
|
+
return this.bounds.padBottom(
|
|
340
|
+
this.legendHeight
|
|
341
|
+
? this.legendHeight +
|
|
342
|
+
PADDING_BETWEEN_MAP_AND_LEGEND +
|
|
343
|
+
PADDING_BELOW_MAP_LEGEND
|
|
344
|
+
: 0
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@computed private get region(): MapRegionName {
|
|
349
|
+
return this.mapConfig.region
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
@computed private get shouldAddProjectionPatternToLegendBins(): boolean {
|
|
353
|
+
return match(this.mapColumnInfo)
|
|
354
|
+
.with({ type: "historical" }, () => false)
|
|
355
|
+
.with({ type: "projected" }, () => true)
|
|
356
|
+
.with({ type: "historical+projected" }, (info) =>
|
|
357
|
+
// Only add a pattern to the legend bins if _all_ values are projections.
|
|
358
|
+
// If there is even a single non-projected (historical) value, the legend
|
|
359
|
+
// should use solid colors.
|
|
360
|
+
this.chartState.transformedTable
|
|
361
|
+
.get(info.slugForIsProjectionColumn)
|
|
362
|
+
.values.every((value) => value === true)
|
|
363
|
+
)
|
|
364
|
+
.exhaustive()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private maybeAddPatternRefToBin<Bin extends ColorScaleBin>(bin: Bin): Bin {
|
|
368
|
+
if (isNoDataBin(bin))
|
|
369
|
+
return new CategoricalBin({
|
|
370
|
+
...bin.props,
|
|
371
|
+
patternRef: Patterns.noDataPattern,
|
|
372
|
+
}) as Bin
|
|
373
|
+
|
|
374
|
+
if (isProjectedDataBin(bin)) {
|
|
375
|
+
const patternRef = makeProjectedDataPatternId(
|
|
376
|
+
PROJECTED_DATA_LEGEND_COLOR,
|
|
377
|
+
{ forLegend: true }
|
|
378
|
+
)
|
|
379
|
+
return new CategoricalBin({ ...bin.props, patternRef }) as Bin
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (this.shouldAddProjectionPatternToLegendBins) {
|
|
383
|
+
const patternRef = makeProjectedDataPatternId(bin.color, {
|
|
384
|
+
forLegend: true,
|
|
385
|
+
})
|
|
386
|
+
return (
|
|
387
|
+
bin instanceof CategoricalBin
|
|
388
|
+
? new CategoricalBin({ ...bin.props, patternRef })
|
|
389
|
+
: new NumericBin({ ...bin.props, patternRef })
|
|
390
|
+
) as Bin
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return bin
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@computed get numericLegendData(): ColorScaleBin[] {
|
|
397
|
+
const hasNoDataBin = this.legendData.some((bin) => isNoDataBin(bin))
|
|
398
|
+
if (this.hasCategoricalLegendData || !hasNoDataBin)
|
|
399
|
+
return this.legendData
|
|
400
|
+
.filter((bin) => isNumericBin(bin))
|
|
401
|
+
.map((bin) => this.maybeAddPatternRefToBin(bin))
|
|
402
|
+
|
|
403
|
+
const bins: ColorScaleBin[] = this.legendData
|
|
404
|
+
.filter((bin) => isNumericBin(bin) || isNoDataBin(bin))
|
|
405
|
+
.map((bin) => this.maybeAddPatternRefToBin(bin))
|
|
406
|
+
|
|
407
|
+
// Move the no-data bin from the end to the start
|
|
408
|
+
return [bins[bins.length - 1], ...bins.slice(0, -1)]
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
@computed private get numMembersPerCategoricalBinByIndex(): Map<
|
|
412
|
+
number,
|
|
413
|
+
number
|
|
414
|
+
> {
|
|
415
|
+
const { chartState, legendData } = this
|
|
416
|
+
return new Map(
|
|
417
|
+
legendData
|
|
418
|
+
.filter((bin) => isCategoricalBin(bin))
|
|
419
|
+
.map((bin) => {
|
|
420
|
+
const memberCount = chartState.series.filter((series) =>
|
|
421
|
+
bin.contains(series.value)
|
|
422
|
+
).length
|
|
423
|
+
return [bin.index, memberCount]
|
|
424
|
+
})
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
@computed get categoricalLegendData(): CategoricalBin[] {
|
|
429
|
+
let categoricalLegendData = this.legendData
|
|
430
|
+
.filter((bin) => isCategoricalBin(bin))
|
|
431
|
+
.map((bin) => this.maybeAddPatternRefToBin(bin))
|
|
432
|
+
|
|
433
|
+
// Hide empty bins in thumbnails
|
|
434
|
+
if (
|
|
435
|
+
this.isStatic &&
|
|
436
|
+
this.manager.variant === GrapherVariant.Thumbnail
|
|
437
|
+
) {
|
|
438
|
+
categoricalLegendData = categoricalLegendData.filter((bin) => {
|
|
439
|
+
const memberCount =
|
|
440
|
+
this.numMembersPerCategoricalBinByIndex.get(bin.index) ?? 0
|
|
441
|
+
return (
|
|
442
|
+
isNoDataBin(bin) ||
|
|
443
|
+
isProjectedDataBin(bin) ||
|
|
444
|
+
memberCount > 0
|
|
445
|
+
)
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return categoricalLegendData
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@computed private get hasCategoricalLegendData(): boolean {
|
|
453
|
+
return this.categoricalLegendData.length > 1
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
@computed get binColors(): string[] {
|
|
457
|
+
return this.legendData.map((bin) => bin.color)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@computed private get numericHoverBracket(): ColorScaleBin | undefined {
|
|
461
|
+
const { hoverBracket, hoverValue, numericLegendData } = this
|
|
462
|
+
const { externalLegendHoverBin } = this.manager
|
|
463
|
+
|
|
464
|
+
if (hoverBracket) return hoverBracket
|
|
465
|
+
|
|
466
|
+
if (hoverValue !== undefined)
|
|
467
|
+
return numericLegendData.find((bin) => bin.contains(hoverValue))
|
|
468
|
+
|
|
469
|
+
return externalLegendHoverBin
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
@computed private get categoricalHoverBracket():
|
|
473
|
+
| CategoricalBin
|
|
474
|
+
| undefined {
|
|
475
|
+
const { hoverBracket, hoverValue, categoricalLegendData } = this
|
|
476
|
+
|
|
477
|
+
if (hoverBracket && hoverBracket instanceof CategoricalBin)
|
|
478
|
+
return hoverBracket
|
|
479
|
+
|
|
480
|
+
if (hoverValue !== undefined)
|
|
481
|
+
return categoricalLegendData.find((bin) => bin.contains(hoverValue))
|
|
482
|
+
|
|
483
|
+
return undefined
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
getLegendBinState(bin: ColorScaleBin): LegendInteractionState {
|
|
487
|
+
if (!this.categoricalHoverBracket && !this.numericHoverBracket)
|
|
488
|
+
return LegendInteractionState.Default
|
|
489
|
+
|
|
490
|
+
// Check if this bin is being hovered
|
|
491
|
+
if (
|
|
492
|
+
this.categoricalHoverBracket &&
|
|
493
|
+
bin.equals(this.categoricalHoverBracket)
|
|
494
|
+
) {
|
|
495
|
+
return LegendInteractionState.Focused
|
|
496
|
+
}
|
|
497
|
+
if (this.numericHoverBracket && bin.equals(this.numericHoverBracket)) {
|
|
498
|
+
return LegendInteractionState.Focused
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return LegendInteractionState.Muted
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
legendStyleConfig: LegendStyleConfig = {
|
|
505
|
+
marker: {
|
|
506
|
+
default: { stroke: DEFAULT_STROKE_COLOR },
|
|
507
|
+
focused: {
|
|
508
|
+
stroke: HOVER_STROKE_COLOR,
|
|
509
|
+
strokeWidth: HOVER_STROKE_WIDTH,
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
@computed get legendMaxWidth(): number {
|
|
515
|
+
// it seems nice to have just a little bit of extra padding left and right
|
|
516
|
+
return this.bounds.width * MAP_LEGEND_MAX_WIDTH_RATIO
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
@computed get legendX(): number {
|
|
520
|
+
return this.bounds.x + (this.bounds.width - this.legendMaxWidth) / 2
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@computed get legendHeight(): number {
|
|
524
|
+
return this.categoryLegendHeight + this.numericLegendHeight
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
@computed private get numericLegendHeight(): number {
|
|
528
|
+
return this.numericLegend ? this.numericLegend.height : 0
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
@computed private get categoryLegendHeight(): number {
|
|
532
|
+
return this.categoryLegend ? this.categoryLegend.height : 0
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
@computed private get categoryLegend():
|
|
536
|
+
| HorizontalCategoricalColorLegend
|
|
537
|
+
| undefined {
|
|
538
|
+
if (this.manager.isDisplayedAlongsideComplementaryTable)
|
|
539
|
+
return undefined
|
|
540
|
+
return this.manager.showLegend && this.categoricalLegendData.length > 1
|
|
541
|
+
? new HorizontalCategoricalColorLegend({ manager: this })
|
|
542
|
+
: undefined
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@computed private get numericLegend():
|
|
546
|
+
| HorizontalNumericColorLegend
|
|
547
|
+
| undefined {
|
|
548
|
+
if (this.manager.isDisplayedAlongsideComplementaryTable)
|
|
549
|
+
return undefined
|
|
550
|
+
return this.manager.showLegend && this.numericLegendData.length > 1
|
|
551
|
+
? new HorizontalNumericColorLegend({ manager: this })
|
|
552
|
+
: undefined
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
@computed get categoryLegendY(): number {
|
|
556
|
+
if (!this.categoryLegend) return 0
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
this.bounds.bottom -
|
|
560
|
+
this.categoryLegend.height -
|
|
561
|
+
PADDING_BELOW_MAP_LEGEND
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
@computed get legendAlign(): HorizontalAlign {
|
|
566
|
+
return HorizontalAlign.center
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
@computed get numericLegendY(): number {
|
|
570
|
+
if (!this.numericLegend) return 0
|
|
571
|
+
return (
|
|
572
|
+
this.bounds.bottom -
|
|
573
|
+
this.numericLegendHeight -
|
|
574
|
+
PADDING_BELOW_MAP_LEGEND -
|
|
575
|
+
// If present, the category legend is placed below the numeric legend
|
|
576
|
+
(this.categoryLegend
|
|
577
|
+
? this.categoryLegendHeight + PADDING_BETWEEN_MAP_LEGENDS
|
|
578
|
+
: 0)
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
@computed get hoverColors(): Color[] | undefined {
|
|
583
|
+
if (!this.hoverBracket) return undefined
|
|
584
|
+
return [this.hoverBracket.color]
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
@computed get isStatic(): boolean {
|
|
588
|
+
return this.manager.isStatic ?? false
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
@computed private get renderUid(): number {
|
|
592
|
+
return guid()
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
@computed private get clipPath(): ClipPath {
|
|
596
|
+
return makeClipPath({
|
|
597
|
+
renderUid: this.renderUid,
|
|
598
|
+
box: this.choroplethMapBounds,
|
|
599
|
+
})
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
@computed get numericBinSize(): number {
|
|
603
|
+
return 0.625 * this.fontSize
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
renderMapLegend(): React.ReactElement {
|
|
607
|
+
const { numericLegend, categoryLegend } = this
|
|
608
|
+
|
|
609
|
+
return (
|
|
610
|
+
<>
|
|
611
|
+
{numericLegend && (
|
|
612
|
+
<HorizontalNumericColorLegend manager={this} />
|
|
613
|
+
)}
|
|
614
|
+
{categoryLegend && (
|
|
615
|
+
<HorizontalCategoricalColorLegend manager={this} />
|
|
616
|
+
)}
|
|
617
|
+
</>
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
renderMapOrGlobe({ clipping = true } = {}): React.ReactElement {
|
|
622
|
+
const mapOrGlobe = this.mapConfig.globe.isActive ? (
|
|
623
|
+
<ChoroplethGlobe manager={this} />
|
|
624
|
+
) : (
|
|
625
|
+
<ChoroplethMap manager={this} />
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
if (!clipping) return mapOrGlobe
|
|
629
|
+
|
|
630
|
+
return (
|
|
631
|
+
<>
|
|
632
|
+
{this.clipPath.element}
|
|
633
|
+
<g clipPath={this.clipPath.id}>{mapOrGlobe}</g>
|
|
634
|
+
</>
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
renderStatic(): React.ReactElement {
|
|
639
|
+
// Clipping the chart area is only necessary when the map is
|
|
640
|
+
// zoomed in or we're showing the globe. If that isn't the case,
|
|
641
|
+
// then we don't add a clipping element since it introduces noise
|
|
642
|
+
// in SVG editing programs like Figma.
|
|
643
|
+
const clipping =
|
|
644
|
+
this.mapConfig.globe.isActive || this.region !== MapRegionName.World
|
|
645
|
+
|
|
646
|
+
return (
|
|
647
|
+
<>
|
|
648
|
+
{this.renderMapOrGlobe({ clipping })}
|
|
649
|
+
{this.renderMapLegend()}
|
|
650
|
+
</>
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
renderInteractive(): React.ReactElement {
|
|
655
|
+
const { tooltipState } = this
|
|
656
|
+
|
|
657
|
+
let sparklineWidth: number | undefined
|
|
658
|
+
if (this.manager.shouldPinTooltipToBottom) {
|
|
659
|
+
const windowWidth = window?.innerWidth ?? 240
|
|
660
|
+
sparklineWidth = Math.min(
|
|
661
|
+
GRAPHER_MAX_TOOLTIP_WIDTH,
|
|
662
|
+
windowWidth - 8
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const tooltipCountry =
|
|
667
|
+
tooltipState.target?.featureId ??
|
|
668
|
+
// show a pinned-to-the-bottom tooltip when focused on a country
|
|
669
|
+
(this.manager.shouldPinTooltipToBottom
|
|
670
|
+
? this.mapConfig.globe.focusCountry
|
|
671
|
+
: undefined)
|
|
672
|
+
|
|
673
|
+
return (
|
|
674
|
+
<g
|
|
675
|
+
ref={this.base}
|
|
676
|
+
className={MAP_CHART_CLASSNAME}
|
|
677
|
+
onMouseMove={this.onMapMouseMove}
|
|
678
|
+
>
|
|
679
|
+
{this.renderMapOrGlobe()}
|
|
680
|
+
{this.renderMapLegend()}
|
|
681
|
+
{tooltipCountry && (
|
|
682
|
+
<MapTooltip
|
|
683
|
+
mapColumnSlug={this.mapColumnSlug}
|
|
684
|
+
mapColumnInfo={this.mapColumnInfo}
|
|
685
|
+
entityName={tooltipCountry}
|
|
686
|
+
position={tooltipState.position}
|
|
687
|
+
fading={tooltipState.fading}
|
|
688
|
+
timeSeriesTable={this.chartState.inputTable}
|
|
689
|
+
formatValueForTooltip={
|
|
690
|
+
this.chartState.formatValueForTooltip
|
|
691
|
+
}
|
|
692
|
+
manager={this.manager}
|
|
693
|
+
lineColorScale={this.colorScale}
|
|
694
|
+
targetTime={this.targetTime}
|
|
695
|
+
targetTimes={this.manager.highlightedTimesInTooltip}
|
|
696
|
+
sparklineWidth={sparklineWidth}
|
|
697
|
+
dismissTooltip={action(() => {
|
|
698
|
+
this.mapConfig.hoverCountry = undefined
|
|
699
|
+
this.tooltipState.target = null
|
|
700
|
+
this.globeController.dismissCountryFocus()
|
|
701
|
+
})}
|
|
702
|
+
/>
|
|
703
|
+
)}
|
|
704
|
+
</g>
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
override render(): React.ReactElement {
|
|
709
|
+
if (this.chartState.errorInfo.reason)
|
|
710
|
+
return (
|
|
711
|
+
<NoDataModal
|
|
712
|
+
manager={this.manager}
|
|
713
|
+
bounds={this.props.bounds}
|
|
714
|
+
message={this.chartState.errorInfo.reason}
|
|
715
|
+
/>
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
return this.isStatic ? this.renderStatic() : this.renderInteractive()
|
|
719
|
+
}
|
|
720
|
+
}
|