@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,662 @@
|
|
|
1
|
+
import * as _ from "lodash-es"
|
|
2
|
+
import React from "react"
|
|
3
|
+
import {
|
|
4
|
+
Bounds,
|
|
5
|
+
isTouchDevice,
|
|
6
|
+
makeIdForHumanConsumption,
|
|
7
|
+
excludeUndefined,
|
|
8
|
+
EntityName,
|
|
9
|
+
MapRegionName,
|
|
10
|
+
} from "../../utils/index.js"
|
|
11
|
+
import { computed, action, observable, makeObservable } from "mobx"
|
|
12
|
+
import { observer } from "mobx-react"
|
|
13
|
+
import { Quadtree, quadtree } from "d3-quadtree"
|
|
14
|
+
import {
|
|
15
|
+
MapRenderFeature,
|
|
16
|
+
SVGMouseEvent,
|
|
17
|
+
GEO_FEATURES_CLASSNAME,
|
|
18
|
+
ChoroplethMapManager,
|
|
19
|
+
ChoroplethSeriesByName,
|
|
20
|
+
CHOROPLETH_MAP_CLASSNAME,
|
|
21
|
+
ExternalAnnotation,
|
|
22
|
+
InternalAnnotation,
|
|
23
|
+
Annotation,
|
|
24
|
+
ANNOTATION_COLOR_LIGHT,
|
|
25
|
+
ANNOTATION_COLOR_DARK,
|
|
26
|
+
RenderFeature,
|
|
27
|
+
PROJECTED_DATA_LEGEND_COLOR,
|
|
28
|
+
MapViewport,
|
|
29
|
+
MAP_VIEWPORTS,
|
|
30
|
+
MAP_REGION_LABELS,
|
|
31
|
+
} from "./MapChartConstants"
|
|
32
|
+
import { getGeoFeaturesForMap } from "./GeoFeatures"
|
|
33
|
+
import {
|
|
34
|
+
BackgroundCountry,
|
|
35
|
+
CountryWithData,
|
|
36
|
+
CountryWithNoData,
|
|
37
|
+
ExternalValueAnnotation,
|
|
38
|
+
InternalValueAnnotation,
|
|
39
|
+
NoDataPattern,
|
|
40
|
+
ProjectedDataPattern,
|
|
41
|
+
} from "./MapComponents"
|
|
42
|
+
import { Patterns } from "../core/GrapherConstants"
|
|
43
|
+
import {
|
|
44
|
+
detectNearbyFeature,
|
|
45
|
+
getCountriesByRegion,
|
|
46
|
+
getForegroundFeatures,
|
|
47
|
+
sortFeaturesByInteractionStateAndSize,
|
|
48
|
+
} from "./MapHelpers"
|
|
49
|
+
import {
|
|
50
|
+
makeInternalAnnotationForFeature,
|
|
51
|
+
makeExternalAnnotationForFeature,
|
|
52
|
+
repositionAndFilterExternalAnnotations,
|
|
53
|
+
} from "./MapAnnotations"
|
|
54
|
+
import { isDarkColor } from "../color/ColorUtils"
|
|
55
|
+
import { MapConfig } from "./MapConfig"
|
|
56
|
+
import { MapSelectionArray } from "../selection/MapSelectionArray"
|
|
57
|
+
import { MAP_PROJECTIONS } from "./MapProjections"
|
|
58
|
+
|
|
59
|
+
@observer
|
|
60
|
+
export class ChoroplethMap extends React.Component<{
|
|
61
|
+
manager: ChoroplethMapManager
|
|
62
|
+
}> {
|
|
63
|
+
base = React.createRef<SVGGElement>()
|
|
64
|
+
|
|
65
|
+
private hoverEnterFeature: MapRenderFeature | undefined = undefined
|
|
66
|
+
private hoverNearbyFeature: MapRenderFeature | undefined = undefined
|
|
67
|
+
|
|
68
|
+
constructor(props: { manager: ChoroplethMapManager }) {
|
|
69
|
+
super(props)
|
|
70
|
+
|
|
71
|
+
makeObservable<
|
|
72
|
+
ChoroplethMap,
|
|
73
|
+
"hoverEnterFeature" | "hoverNearbyFeature"
|
|
74
|
+
>(this, {
|
|
75
|
+
hoverEnterFeature: observable,
|
|
76
|
+
hoverNearbyFeature: observable,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@computed private get isTouchDevice(): boolean {
|
|
81
|
+
return isTouchDevice()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@computed private get manager(): ChoroplethMapManager {
|
|
85
|
+
return this.props.manager
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@computed get mapConfig(): MapConfig {
|
|
89
|
+
return this.manager.mapConfig
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@computed.struct private get bounds(): Bounds {
|
|
93
|
+
return this.manager.choroplethMapBounds
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@computed private get selectionArray(): MapSelectionArray {
|
|
97
|
+
return this.manager.selectionArray ?? new MapSelectionArray()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@computed private get viewport(): MapViewport {
|
|
101
|
+
return this.manager.mapViewport ?? MAP_VIEWPORTS[this.mapConfig.region]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@computed.struct private get choroplethData(): ChoroplethSeriesByName {
|
|
105
|
+
return this.manager.choroplethData
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@computed private get hoverFeature(): MapRenderFeature | undefined {
|
|
109
|
+
return this.hoverEnterFeature || this.hoverNearbyFeature
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Combine bounding boxes to get the extents of the entire map
|
|
113
|
+
@computed private get mapBounds(): Bounds {
|
|
114
|
+
const allBounds = this.features.map((feature) => feature.projBounds)
|
|
115
|
+
return Bounds.merge(allBounds)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Calculate what scaling should be applied to the untransformed map to match the current viewport to the container
|
|
119
|
+
@computed private get viewportScale(): number {
|
|
120
|
+
const { bounds, viewport, mapBounds } = this
|
|
121
|
+
const viewportWidth = viewport.width * mapBounds.width
|
|
122
|
+
const viewportHeight = viewport.height * mapBounds.height
|
|
123
|
+
return Math.min(
|
|
124
|
+
bounds.width / viewportWidth,
|
|
125
|
+
bounds.height / viewportHeight
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@computed private get viewportScaleSqrt(): number {
|
|
130
|
+
return Math.sqrt(this.viewportScale)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@computed private get matrixTransform(): string {
|
|
134
|
+
const { bounds, mapBounds, viewport, viewportScale } = this
|
|
135
|
+
|
|
136
|
+
// Calculate our reference dimensions. These values are independent of the current
|
|
137
|
+
// map translation and scaling.
|
|
138
|
+
const mapX = mapBounds.x + 1
|
|
139
|
+
const mapY = mapBounds.y + 1
|
|
140
|
+
|
|
141
|
+
// Work out how to center the map, accounting for the new scaling we've worked out
|
|
142
|
+
const newWidth = mapBounds.width * viewportScale
|
|
143
|
+
const newHeight = mapBounds.height * viewportScale
|
|
144
|
+
const boundsCenterX = bounds.left + bounds.width / 2
|
|
145
|
+
const boundsCenterY = bounds.top + bounds.height / 2
|
|
146
|
+
const newCenterX =
|
|
147
|
+
mapX + (viewportScale - 1) * mapBounds.x + viewport.x * newWidth
|
|
148
|
+
const newCenterY =
|
|
149
|
+
mapY + (viewportScale - 1) * mapBounds.y + viewport.y * newHeight
|
|
150
|
+
const newOffsetX = boundsCenterX - newCenterX
|
|
151
|
+
const newOffsetY = boundsCenterY - newCenterY
|
|
152
|
+
|
|
153
|
+
const matrixComponents = [
|
|
154
|
+
viewportScale,
|
|
155
|
+
0,
|
|
156
|
+
0,
|
|
157
|
+
viewportScale,
|
|
158
|
+
newOffsetX,
|
|
159
|
+
newOffsetY,
|
|
160
|
+
]
|
|
161
|
+
const matrixComponentsRounded = matrixComponents.map((c) =>
|
|
162
|
+
_.round(c, 4)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const matrixStr = `matrix(${matrixComponentsRounded.join(",")})`
|
|
166
|
+
return matrixStr
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@computed private get features(): MapRenderFeature[] {
|
|
170
|
+
return getGeoFeaturesForMap(this.mapConfig.region)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@computed private get featuresInRegion(): MapRenderFeature[] {
|
|
174
|
+
const { features, mapConfig } = this
|
|
175
|
+
|
|
176
|
+
if (mapConfig.region === MapRegionName.World) return features
|
|
177
|
+
|
|
178
|
+
// For Canada, all features from getGeoFeaturesForMap are provinces to display
|
|
179
|
+
if (mapConfig.region === MapRegionName.Canada) return features
|
|
180
|
+
|
|
181
|
+
const countriesByProjection = getCountriesByRegion(
|
|
182
|
+
MAP_REGION_LABELS[mapConfig.region]
|
|
183
|
+
)
|
|
184
|
+
if (countriesByProjection === undefined) return []
|
|
185
|
+
|
|
186
|
+
return features.filter((feature) =>
|
|
187
|
+
countriesByProjection.has(feature.id)
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@computed private get featuresById(): Map<string, MapRenderFeature> {
|
|
192
|
+
return new Map(this.features.map((feature) => [feature.id, feature]))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@computed private get foregroundFeatures(): MapRenderFeature[] {
|
|
196
|
+
return getForegroundFeatures(this.featuresInRegion, this.selectionArray)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@computed
|
|
200
|
+
private get backgroundFeatures(): MapRenderFeature[] {
|
|
201
|
+
return _.difference(this.features, this.foregroundFeatures)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@computed private get backgroundFeatureIdSet(): Set<EntityName> {
|
|
205
|
+
return new Set(this.backgroundFeatures.map((feature) => feature.id))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@computed private get featuresWithData(): MapRenderFeature[] {
|
|
209
|
+
return this.foregroundFeatures.filter((feature) =>
|
|
210
|
+
this.choroplethData.has(feature.id)
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@computed private get sortedFeaturesWithData(): MapRenderFeature[] {
|
|
215
|
+
// sort features so that hovered or selected features are rendered last
|
|
216
|
+
// and smaller countries are rendered on top of bigger ones
|
|
217
|
+
return sortFeaturesByInteractionStateAndSize(this.featuresWithData, {
|
|
218
|
+
isHovered: (featureId: string) =>
|
|
219
|
+
this.manager.getHoverState?.(featureId)?.active ?? false,
|
|
220
|
+
isSelected: (featureId) =>
|
|
221
|
+
this.manager.isSelected?.(featureId) ?? false,
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@computed private get featuresWithNoData(): MapRenderFeature[] {
|
|
226
|
+
return _.difference(this.foregroundFeatures, this.featuresWithData)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@computed private get binColors(): string[] {
|
|
230
|
+
return this.manager.binColors ?? []
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@computed private get quadtree(): Quadtree<MapRenderFeature> {
|
|
234
|
+
return quadtree<MapRenderFeature>()
|
|
235
|
+
.x((feature) => feature.projBounds.centerX)
|
|
236
|
+
.y((feature) => feature.projBounds.centerY)
|
|
237
|
+
.addAll(this.foregroundFeatures)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@computed private get projection(): any {
|
|
241
|
+
return MAP_PROJECTIONS[this.mapConfig.region]
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@computed private get shouldShowAnnotations(): boolean {
|
|
245
|
+
return !!(
|
|
246
|
+
this.manager.mapColumn.hasNumberFormatting &&
|
|
247
|
+
!this.mapConfig.tooltipUseCustomLabels
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private formatAnnotationLabel(value: string | number): string {
|
|
252
|
+
return this.manager.mapColumn.formatValueShortWithAbbreviations(value)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@computed private get annotationCandidateFeatures(): MapRenderFeature[] {
|
|
256
|
+
if (!this.shouldShowAnnotations) return []
|
|
257
|
+
|
|
258
|
+
return excludeUndefined(
|
|
259
|
+
this.mapConfig.selection.selectedCountryNamesInForeground.map(
|
|
260
|
+
(name) => this.featuresById.get(name)
|
|
261
|
+
)
|
|
262
|
+
).filter((feature) => this.foregroundFeatures.includes(feature))
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Naively placed annotations that might be overlapping */
|
|
266
|
+
@computed
|
|
267
|
+
private get annotationCandidates(): Annotation[] {
|
|
268
|
+
return excludeUndefined(
|
|
269
|
+
this.annotationCandidateFeatures.map((feature) => {
|
|
270
|
+
const series = this.choroplethData.get(feature.id)
|
|
271
|
+
if (!series) return
|
|
272
|
+
|
|
273
|
+
const labelColor = isDarkColor(series.color)
|
|
274
|
+
? ANNOTATION_COLOR_LIGHT
|
|
275
|
+
: ANNOTATION_COLOR_DARK
|
|
276
|
+
|
|
277
|
+
const args = {
|
|
278
|
+
feature,
|
|
279
|
+
projection: this.projection,
|
|
280
|
+
formattedValue: this.formatAnnotationLabel(series.value),
|
|
281
|
+
fontSizeScale: this.viewportScale,
|
|
282
|
+
color: labelColor,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// try to fit the annotation inside the feature
|
|
286
|
+
const internalAnnotation =
|
|
287
|
+
makeInternalAnnotationForFeature(args)
|
|
288
|
+
if (internalAnnotation) return internalAnnotation
|
|
289
|
+
|
|
290
|
+
// place the annotation outside of the feature
|
|
291
|
+
const externalAnnotation =
|
|
292
|
+
makeExternalAnnotationForFeature(args)
|
|
293
|
+
if (externalAnnotation) return externalAnnotation
|
|
294
|
+
|
|
295
|
+
return undefined
|
|
296
|
+
})
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@computed
|
|
301
|
+
private get internalAnnotations(): InternalAnnotation[] {
|
|
302
|
+
return this.annotationCandidates.filter(
|
|
303
|
+
(annotation) => annotation.type === "internal"
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@computed
|
|
308
|
+
private get externalAnnotations(): ExternalAnnotation[] {
|
|
309
|
+
const { projection, backgroundFeatureIdSet } = this
|
|
310
|
+
const annotations = this.annotationCandidates.filter(
|
|
311
|
+
(annotation) => annotation.type === "external"
|
|
312
|
+
)
|
|
313
|
+
return repositionAndFilterExternalAnnotations({
|
|
314
|
+
annotations,
|
|
315
|
+
projection,
|
|
316
|
+
backgroundFeatureIdSet,
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Map uses a hybrid approach to mouseover
|
|
321
|
+
// If mouse is inside an element, that is prioritized
|
|
322
|
+
// Otherwise we do a quadtree search for the closest center point of a feature bounds,
|
|
323
|
+
// so that we can hover very small countries without trouble
|
|
324
|
+
@action.bound private detectNearbyFeature(
|
|
325
|
+
event: MouseEvent | TouchEvent
|
|
326
|
+
): MapRenderFeature | undefined {
|
|
327
|
+
if (this.hoverEnterFeature || !this.base.current) return
|
|
328
|
+
|
|
329
|
+
const nearbyFeature = detectNearbyFeature({
|
|
330
|
+
quadtree: this.quadtree,
|
|
331
|
+
element: this.base.current,
|
|
332
|
+
event,
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
if (!nearbyFeature) {
|
|
336
|
+
this.hoverNearbyFeature = undefined
|
|
337
|
+
this.manager.onMapMouseLeave?.()
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (nearbyFeature.id !== this.hoverNearbyFeature?.id) {
|
|
342
|
+
this.hoverNearbyFeature = nearbyFeature
|
|
343
|
+
this.manager.onMapMouseOver?.(nearbyFeature.geo)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return nearbyFeature
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@action.bound private onMouseMove(event: MouseEvent): void {
|
|
350
|
+
this.detectNearbyFeature(event)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@action.bound private onMouseEnter(feature: MapRenderFeature): void {
|
|
354
|
+
this.setHoverEnterFeature(feature)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@action.bound private onMouseLeave(): void {
|
|
358
|
+
// Fixes an issue where clicking on a country that overlaps with the
|
|
359
|
+
// tooltip causes the tooltip to disappear shortly after being rendered
|
|
360
|
+
if (this.isTouchDevice) return
|
|
361
|
+
|
|
362
|
+
this.clearHoverEnterFeature()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@action.bound private setHoverEnterFeature(
|
|
366
|
+
feature: MapRenderFeature
|
|
367
|
+
): void {
|
|
368
|
+
if (this.hoverEnterFeature?.id === feature.id) return
|
|
369
|
+
|
|
370
|
+
this.hoverEnterFeature = feature
|
|
371
|
+
this.manager.onMapMouseOver?.(feature.geo)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@action.bound private clearHoverEnterFeature(): void {
|
|
375
|
+
this.hoverEnterFeature = undefined
|
|
376
|
+
this.manager.onMapMouseLeave?.()
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
@action.bound private onTouchStart(feature: MapRenderFeature): void {
|
|
380
|
+
this.setHoverEnterFeature(feature)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@action.bound private onClick(feature: MapRenderFeature): void {
|
|
384
|
+
const {
|
|
385
|
+
isMapSelectionEnabled,
|
|
386
|
+
mapConfig: { selection },
|
|
387
|
+
globeController,
|
|
388
|
+
} = this.manager
|
|
389
|
+
|
|
390
|
+
this.setHoverEnterFeature(feature)
|
|
391
|
+
|
|
392
|
+
const is2dContinentActive = this.mapConfig.is2dContinentActive()
|
|
393
|
+
|
|
394
|
+
if (isMapSelectionEnabled) {
|
|
395
|
+
// Select/deselect the country if allowed
|
|
396
|
+
selection.toggleSelection(feature.id)
|
|
397
|
+
} else if (
|
|
398
|
+
// Don't rotate if the maps shows a continent in 2d mode
|
|
399
|
+
!is2dContinentActive &&
|
|
400
|
+
// Don't rotate if the map is faceted
|
|
401
|
+
!this.manager.isFaceted &&
|
|
402
|
+
// Don't rotate if the user is zoomed in, otherwise they can get stuck in 3D mode
|
|
403
|
+
window?.visualViewport?.scale === 1
|
|
404
|
+
) {
|
|
405
|
+
// Rotate to the selected country on the globe
|
|
406
|
+
globeController?.rotateToCountry(feature.id)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@action.bound private onDocumentClick(): void {
|
|
411
|
+
this.manager.globeController?.dismissCountryFocus()
|
|
412
|
+
if (this.hoverEnterFeature || this.hoverNearbyFeature) {
|
|
413
|
+
this.hoverEnterFeature = undefined
|
|
414
|
+
this.hoverNearbyFeature = undefined
|
|
415
|
+
this.manager.onMapMouseLeave?.()
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private renderInternalAnnotations(): React.ReactElement | undefined {
|
|
420
|
+
if (this.internalAnnotations.length === 0) return
|
|
421
|
+
|
|
422
|
+
return (
|
|
423
|
+
<g id={makeIdForHumanConsumption("annotations-internal")}>
|
|
424
|
+
{this.internalAnnotations.map((annotation) => (
|
|
425
|
+
<InternalValueAnnotation
|
|
426
|
+
key={annotation.id}
|
|
427
|
+
annotation={annotation}
|
|
428
|
+
strokeScale={this.viewportScaleSqrt}
|
|
429
|
+
showOutline={
|
|
430
|
+
this.choroplethData.get(annotation.id)?.isProjection
|
|
431
|
+
}
|
|
432
|
+
/>
|
|
433
|
+
))}
|
|
434
|
+
</g>
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private renderExternalAnnotations(): React.ReactElement | undefined {
|
|
439
|
+
if (this.externalAnnotations.length === 0) return
|
|
440
|
+
|
|
441
|
+
return (
|
|
442
|
+
<g
|
|
443
|
+
id={makeIdForHumanConsumption("annotations-external")}
|
|
444
|
+
className="ExternalAnnotations"
|
|
445
|
+
>
|
|
446
|
+
{this.externalAnnotations.map((annotation) => (
|
|
447
|
+
<ExternalValueAnnotation
|
|
448
|
+
key={annotation.id}
|
|
449
|
+
annotation={annotation}
|
|
450
|
+
strokeScale={this.viewportScaleSqrt}
|
|
451
|
+
onMouseEnter={action((feature: RenderFeature) =>
|
|
452
|
+
this.setHoverEnterFeature(
|
|
453
|
+
feature as MapRenderFeature
|
|
454
|
+
)
|
|
455
|
+
)}
|
|
456
|
+
onMouseLeave={action(() =>
|
|
457
|
+
this.clearHoverEnterFeature()
|
|
458
|
+
)}
|
|
459
|
+
/>
|
|
460
|
+
))}
|
|
461
|
+
</g>
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
renderFeaturesInBackground(): React.ReactElement | undefined {
|
|
466
|
+
if (this.backgroundFeatures.length === 0) return
|
|
467
|
+
|
|
468
|
+
return (
|
|
469
|
+
<g id={makeIdForHumanConsumption("countries-background")}>
|
|
470
|
+
{this.backgroundFeatures.map((feature) => (
|
|
471
|
+
<BackgroundCountry key={feature.id} feature={feature} />
|
|
472
|
+
))}
|
|
473
|
+
</g>
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
renderFeaturesWithoutData(): React.ReactElement | undefined {
|
|
478
|
+
if (this.featuresWithNoData.length === 0) return
|
|
479
|
+
const patternId = Patterns.noDataPatternForMap
|
|
480
|
+
|
|
481
|
+
return (
|
|
482
|
+
<g
|
|
483
|
+
id={makeIdForHumanConsumption("countries-without-data")}
|
|
484
|
+
className="noDataFeatures"
|
|
485
|
+
>
|
|
486
|
+
<defs>
|
|
487
|
+
<NoDataPattern
|
|
488
|
+
patternId={patternId}
|
|
489
|
+
scale={1 / this.viewportScale} // The scale is crucial and projection specific
|
|
490
|
+
/>
|
|
491
|
+
</defs>
|
|
492
|
+
|
|
493
|
+
{this.featuresWithNoData.map((feature) => (
|
|
494
|
+
<CountryWithNoData
|
|
495
|
+
key={feature.id}
|
|
496
|
+
feature={feature}
|
|
497
|
+
patternId={patternId}
|
|
498
|
+
isSelected={this.manager.isSelected?.(feature.id)}
|
|
499
|
+
hover={this.manager.getHoverState?.(feature.id)}
|
|
500
|
+
strokeScale={this.viewportScaleSqrt}
|
|
501
|
+
onClick={(event) => {
|
|
502
|
+
// don't invoke a second click on parent that
|
|
503
|
+
// catches clicks on 'nearby' features
|
|
504
|
+
event.stopPropagation()
|
|
505
|
+
|
|
506
|
+
this.onClick(feature)
|
|
507
|
+
}}
|
|
508
|
+
onTouchStart={() => this.onTouchStart(feature)}
|
|
509
|
+
onMouseEnter={this.onMouseEnter}
|
|
510
|
+
onMouseLeave={this.onMouseLeave}
|
|
511
|
+
/>
|
|
512
|
+
))}
|
|
513
|
+
</g>
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
renderFeaturesWithData(): React.ReactElement | undefined {
|
|
518
|
+
if (this.sortedFeaturesWithData.length === 0) return
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
<g id={makeIdForHumanConsumption("countries-with-data")}>
|
|
522
|
+
{this.manager.hasProjectedData && (
|
|
523
|
+
<defs>
|
|
524
|
+
{/* Pattern used by the map legend for the projected data bin */}
|
|
525
|
+
<ProjectedDataPattern
|
|
526
|
+
key={PROJECTED_DATA_LEGEND_COLOR}
|
|
527
|
+
color={PROJECTED_DATA_LEGEND_COLOR}
|
|
528
|
+
forLegend
|
|
529
|
+
/>
|
|
530
|
+
{/* Patterns used by the map legend. The map legend can't re-use
|
|
531
|
+
the features' patterns defined below because those are scaled
|
|
532
|
+
by the viewport. */}
|
|
533
|
+
{this.binColors.map((color, index) => (
|
|
534
|
+
<ProjectedDataPattern
|
|
535
|
+
key={`${color}-${index}`}
|
|
536
|
+
color={color}
|
|
537
|
+
forLegend
|
|
538
|
+
/>
|
|
539
|
+
))}
|
|
540
|
+
|
|
541
|
+
{/* Pattern used by features */}
|
|
542
|
+
{this.binColors.map((color, index) => (
|
|
543
|
+
<ProjectedDataPattern
|
|
544
|
+
key={`${color}-${index}`}
|
|
545
|
+
color={color}
|
|
546
|
+
scale={1 / this.viewportScale}
|
|
547
|
+
/>
|
|
548
|
+
))}
|
|
549
|
+
</defs>
|
|
550
|
+
)}
|
|
551
|
+
|
|
552
|
+
{this.sortedFeaturesWithData.map((feature) => {
|
|
553
|
+
const series = this.choroplethData.get(feature.id)
|
|
554
|
+
if (!series) return null
|
|
555
|
+
return (
|
|
556
|
+
<CountryWithData
|
|
557
|
+
key={feature.id}
|
|
558
|
+
feature={feature}
|
|
559
|
+
series={series}
|
|
560
|
+
isSelected={this.manager.isSelected?.(feature.id)}
|
|
561
|
+
hover={this.manager.getHoverState?.(feature.id)}
|
|
562
|
+
strokeScale={this.viewportScaleSqrt}
|
|
563
|
+
onClick={(event) => {
|
|
564
|
+
// don't invoke a second click on parent that
|
|
565
|
+
// catches clicks on 'nearby' features
|
|
566
|
+
event.stopPropagation()
|
|
567
|
+
|
|
568
|
+
this.onClick(feature)
|
|
569
|
+
}}
|
|
570
|
+
onTouchStart={() => this.onTouchStart(feature)}
|
|
571
|
+
onMouseEnter={this.onMouseEnter}
|
|
572
|
+
onMouseLeave={this.onMouseLeave}
|
|
573
|
+
/>
|
|
574
|
+
)
|
|
575
|
+
})}
|
|
576
|
+
</g>
|
|
577
|
+
)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
renderStatic(): React.ReactElement {
|
|
581
|
+
return (
|
|
582
|
+
<g
|
|
583
|
+
id={makeIdForHumanConsumption("map")}
|
|
584
|
+
transform={this.matrixTransform}
|
|
585
|
+
>
|
|
586
|
+
{this.renderFeaturesInBackground()}
|
|
587
|
+
{this.renderFeaturesWithoutData()}
|
|
588
|
+
{this.renderFeaturesWithData()}
|
|
589
|
+
{this.renderInternalAnnotations()}
|
|
590
|
+
{this.renderExternalAnnotations()}
|
|
591
|
+
</g>
|
|
592
|
+
)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
override componentDidMount(): void {
|
|
596
|
+
document.addEventListener("touchstart", this.onDocumentClick, {
|
|
597
|
+
capture: true,
|
|
598
|
+
passive: true,
|
|
599
|
+
})
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
override componentWillUnmount(): void {
|
|
603
|
+
document.removeEventListener("touchstart", this.onDocumentClick, {
|
|
604
|
+
capture: true,
|
|
605
|
+
})
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
renderInteractive(): React.ReactElement {
|
|
609
|
+
const { bounds, matrixTransform } = this
|
|
610
|
+
|
|
611
|
+
// this needs to be referenced here or it will be recomputed on every mousemove
|
|
612
|
+
const _cachedCentroids = this.quadtree
|
|
613
|
+
|
|
614
|
+
// SVG layering is based on order of appearance in the element tree (later elements rendered on top)
|
|
615
|
+
// The ordering here is quite careful
|
|
616
|
+
return (
|
|
617
|
+
<g
|
|
618
|
+
ref={this.base}
|
|
619
|
+
className={CHOROPLETH_MAP_CLASSNAME}
|
|
620
|
+
onMouseDown={
|
|
621
|
+
(ev: SVGMouseEvent): void =>
|
|
622
|
+
ev.preventDefault() /* Without this, title may get selected while shift clicking */
|
|
623
|
+
}
|
|
624
|
+
onMouseMove={(ev: SVGMouseEvent): void =>
|
|
625
|
+
this.onMouseMove(ev.nativeEvent)
|
|
626
|
+
}
|
|
627
|
+
onMouseLeave={this.onMouseLeave}
|
|
628
|
+
onClick={() => {
|
|
629
|
+
// invoke a click on a feature when clicking nearby one
|
|
630
|
+
if (this.hoverNearbyFeature)
|
|
631
|
+
this.onClick(this.hoverNearbyFeature)
|
|
632
|
+
}}
|
|
633
|
+
style={{ cursor: this.hoverFeature ? "pointer" : undefined }}
|
|
634
|
+
>
|
|
635
|
+
<rect
|
|
636
|
+
x={bounds.x}
|
|
637
|
+
y={bounds.y}
|
|
638
|
+
width={bounds.width}
|
|
639
|
+
height={bounds.height}
|
|
640
|
+
fill="rgba(255,255,255,0)"
|
|
641
|
+
opacity={0}
|
|
642
|
+
/>
|
|
643
|
+
<g
|
|
644
|
+
className={GEO_FEATURES_CLASSNAME}
|
|
645
|
+
transform={matrixTransform}
|
|
646
|
+
>
|
|
647
|
+
{this.renderFeaturesInBackground()}
|
|
648
|
+
{this.renderFeaturesWithoutData()}
|
|
649
|
+
{this.renderFeaturesWithData()}
|
|
650
|
+
{this.renderInternalAnnotations()}
|
|
651
|
+
{this.renderExternalAnnotations()}
|
|
652
|
+
</g>
|
|
653
|
+
</g>
|
|
654
|
+
)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
override render(): React.ReactElement {
|
|
658
|
+
return this.manager.isStatic
|
|
659
|
+
? this.renderStatic()
|
|
660
|
+
: this.renderInteractive()
|
|
661
|
+
}
|
|
662
|
+
}
|