@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,809 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { observable, computed, action, makeObservable } from "mobx"
|
|
3
|
+
import { observer } from "mobx-react"
|
|
4
|
+
import {
|
|
5
|
+
Bounds,
|
|
6
|
+
getRelativeMouse,
|
|
7
|
+
makeIdForHumanConsumption,
|
|
8
|
+
Url,
|
|
9
|
+
} from "../../utils/index.js"
|
|
10
|
+
import {
|
|
11
|
+
DATAPAGE_ABOUT_THIS_DATA_SECTION_ID,
|
|
12
|
+
MarkdownTextWrap,
|
|
13
|
+
TextWrap,
|
|
14
|
+
} from "../../components/index.js"
|
|
15
|
+
import { Tooltip } from "../tooltip/Tooltip"
|
|
16
|
+
import { FooterManager } from "./FooterManager"
|
|
17
|
+
import { ActionButtons } from "../controls/ActionButtons"
|
|
18
|
+
import {
|
|
19
|
+
BASE_FONT_SIZE,
|
|
20
|
+
DEFAULT_GRAPHER_BOUNDS,
|
|
21
|
+
GRAPHER_FOOTER_CLASS,
|
|
22
|
+
GRAPHER_FRAME_PADDING_HORIZONTAL,
|
|
23
|
+
GrapherModal,
|
|
24
|
+
} from "../core/GrapherConstants"
|
|
25
|
+
import { GRAPHER_LIGHT_TEXT } from "../color/ColorConstants"
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
|
|
29
|
+
The footer contains the sources, the note (optional), the action buttons and the license and origin URL (optional).
|
|
30
|
+
|
|
31
|
+
If all elements exist, they are laid out as follows:
|
|
32
|
+
+-------------------------------------------------------+
|
|
33
|
+
| Sources |
|
|
34
|
+
+------------------------------------+------------------+
|
|
35
|
+
| Note | |
|
|
36
|
+
+------------------------------------+ Action buttons |
|
|
37
|
+
| Origin URL | CC BY | |
|
|
38
|
+
+-------------------------------------------------------+
|
|
39
|
+
|
|
40
|
+
If the note is long, it is placed below the sources:
|
|
41
|
+
+-------------------------------------------------------+
|
|
42
|
+
| Sources |
|
|
43
|
+
+-------------------------------------------------------+
|
|
44
|
+
| Note |
|
|
45
|
+
+------------------------------------+------------------+
|
|
46
|
+
| Origin URL | CC BY | Action buttons |
|
|
47
|
+
+------------------------------------+------------------+
|
|
48
|
+
|
|
49
|
+
If the origin url and license are short enough, they are placed next to the sources:
|
|
50
|
+
+------------------------------+------------------------+
|
|
51
|
+
| Sources | Origin URL | CC BY |
|
|
52
|
+
+------------------------------+-----+------------------+
|
|
53
|
+
| Note | Action buttons |
|
|
54
|
+
+-------------------------------------------------------+
|
|
55
|
+
|
|
56
|
+
If the note is missing and the sources text is not too long, the sources are placed next to the action buttons:
|
|
57
|
+
+------------------------------------+------------------+
|
|
58
|
+
| Sources | |
|
|
59
|
+
+------------------------------------+ Action buttons |
|
|
60
|
+
| Origin URL | CC BY | |
|
|
61
|
+
+-------------------------------------------------------+
|
|
62
|
+
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
// keep in sync with sass variables in Footer.scss
|
|
66
|
+
const HORIZONTAL_PADDING = 8
|
|
67
|
+
|
|
68
|
+
interface FooterProps {
|
|
69
|
+
manager: FooterManager
|
|
70
|
+
maxWidth?: number
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
abstract class AbstractFooter<
|
|
74
|
+
Props extends FooterProps = FooterProps,
|
|
75
|
+
> extends React.Component<Props> {
|
|
76
|
+
verticalPadding = 4
|
|
77
|
+
|
|
78
|
+
constructor(props: Props) {
|
|
79
|
+
super(props)
|
|
80
|
+
|
|
81
|
+
makeObservable(this, {
|
|
82
|
+
tooltipTarget: observable.ref,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@computed protected get manager(): FooterManager {
|
|
87
|
+
return this.props.manager
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@computed protected get maxWidth(): number {
|
|
91
|
+
return this.props.maxWidth ?? DEFAULT_GRAPHER_BOUNDS.width
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@computed protected get useBaseFontSize(): boolean {
|
|
95
|
+
return !!this.manager.useBaseFontSize
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@computed protected get baseFontSize(): number {
|
|
99
|
+
return this.manager.fontSize ?? BASE_FONT_SIZE
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@computed protected get hideOriginUrl(): boolean {
|
|
103
|
+
return !!this.manager.hideOriginUrl
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@computed protected get sourcesLine(): string {
|
|
107
|
+
return this.manager.sourcesLine?.replace(/\r\n|\n|\r/g, "") ?? ""
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@computed protected get sourcesText(): string {
|
|
111
|
+
return `Data source: ${this.sourcesLine} - Learn more about this data`
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@computed protected get noteText(): string {
|
|
115
|
+
return this.manager.note ? `Note: ${this.manager.note}` : ""
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@computed protected get markdownNoteText(): string {
|
|
119
|
+
return this.manager.note ? `**Note:** ${this.manager.note}` : ""
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@computed protected get licenseText(): string {
|
|
123
|
+
const branding = this.manager.branding
|
|
124
|
+
if (this.manager.hasOWIDLogo) {
|
|
125
|
+
return branding?.licenseText ?? "CC BY"
|
|
126
|
+
}
|
|
127
|
+
return branding?.poweredByText ?? "Powered by Build Canada Charts"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@computed protected get licenseUrl(): string {
|
|
131
|
+
const branding = this.manager.branding
|
|
132
|
+
if (this.manager.hasOWIDLogo) {
|
|
133
|
+
return (
|
|
134
|
+
branding?.licenseUrl ??
|
|
135
|
+
"https://creativecommons.org/licenses/by/4.0/"
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
return branding?.poweredByUrl ?? ""
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
@computed protected get originUrlWithProtocol(): string {
|
|
142
|
+
return this.manager.originUrlWithProtocol ?? "http://localhost"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@computed protected get finalUrl(): string {
|
|
146
|
+
const originUrl = this.originUrlWithProtocol
|
|
147
|
+
const url = Url.fromURL(originUrl)
|
|
148
|
+
return url.originAndPath ?? ""
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@computed protected get correctedUrlText(): string | undefined {
|
|
152
|
+
const originUrl = this.originUrlWithProtocol
|
|
153
|
+
|
|
154
|
+
// Make sure the link is valid
|
|
155
|
+
if (!originUrl || !originUrl.toLowerCase().match(/^https?:\/\/./))
|
|
156
|
+
return undefined
|
|
157
|
+
|
|
158
|
+
const url = Url.fromURL(originUrl)
|
|
159
|
+
return url.originAndPath?.replace(/^https?:\/\//, "").replace(/\/$/, "") // remove trailing slash
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected static constructLicenseAndOriginUrlText(
|
|
163
|
+
urlText: string | undefined,
|
|
164
|
+
licenseText: string
|
|
165
|
+
): string {
|
|
166
|
+
if (!urlText) return licenseText
|
|
167
|
+
return [urlText, licenseText].join(" | ")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@computed protected get finalUrlText(): string | undefined {
|
|
171
|
+
const {
|
|
172
|
+
correctedUrlText,
|
|
173
|
+
licenseText,
|
|
174
|
+
fontSize,
|
|
175
|
+
maxWidth,
|
|
176
|
+
actionButtons,
|
|
177
|
+
} = this
|
|
178
|
+
|
|
179
|
+
if (this.hideOriginUrl) return undefined
|
|
180
|
+
|
|
181
|
+
if (!correctedUrlText) return undefined
|
|
182
|
+
|
|
183
|
+
const licenseAndOriginUrlText =
|
|
184
|
+
AbstractFooter.constructLicenseAndOriginUrlText(
|
|
185
|
+
correctedUrlText,
|
|
186
|
+
licenseText
|
|
187
|
+
)
|
|
188
|
+
const licenseAndOriginUrlWidth = Bounds.forText(
|
|
189
|
+
licenseAndOriginUrlText,
|
|
190
|
+
{ fontSize }
|
|
191
|
+
).width
|
|
192
|
+
|
|
193
|
+
// If the URL is too long, don't show it
|
|
194
|
+
if (
|
|
195
|
+
licenseAndOriginUrlWidth + HORIZONTAL_PADDING >
|
|
196
|
+
maxWidth - actionButtons.width
|
|
197
|
+
)
|
|
198
|
+
return undefined
|
|
199
|
+
|
|
200
|
+
return correctedUrlText
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@computed protected get licenseAndOriginUrlText(): string {
|
|
204
|
+
const { finalUrlText, licenseText } = this
|
|
205
|
+
return AbstractFooter.constructLicenseAndOriginUrlText(
|
|
206
|
+
finalUrlText,
|
|
207
|
+
licenseText
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@computed protected get lineHeight(): number {
|
|
212
|
+
return this.manager.isSmall ? 1.1 : 1.2
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@computed protected get fontSize(): number {
|
|
216
|
+
if (this.useBaseFontSize) {
|
|
217
|
+
return (11 / BASE_FONT_SIZE) * this.baseFontSize
|
|
218
|
+
}
|
|
219
|
+
return this.manager.isMedium ? 11 : 12
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@computed protected get sourcesFontSize(): number {
|
|
223
|
+
if (this.useBaseFontSize) {
|
|
224
|
+
return (12 / BASE_FONT_SIZE) * this.baseFontSize
|
|
225
|
+
}
|
|
226
|
+
return this.manager.isSmall ? 12 : 13
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@computed protected get showNote(): boolean {
|
|
230
|
+
return !this.manager.hideNote && !!this.noteText
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
@computed private get actionButtonsWidthWithIconsOnly(): number {
|
|
234
|
+
return new ActionButtons({
|
|
235
|
+
manager: this.manager,
|
|
236
|
+
maxWidth: this.maxWidth,
|
|
237
|
+
}).widthWithIconsOnly
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@computed private get useFullWidthSources(): boolean {
|
|
241
|
+
const {
|
|
242
|
+
showNote,
|
|
243
|
+
sourcesFontSize,
|
|
244
|
+
maxWidth,
|
|
245
|
+
sourcesText,
|
|
246
|
+
actionButtonsWidthWithIconsOnly,
|
|
247
|
+
} = this
|
|
248
|
+
if (showNote) return true
|
|
249
|
+
const sourcesWidth = Bounds.forText(sourcesText, {
|
|
250
|
+
fontSize: sourcesFontSize,
|
|
251
|
+
}).width
|
|
252
|
+
return sourcesWidth > 2 * (maxWidth - actionButtonsWidthWithIconsOnly)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@computed private get useFullWidthNote(): boolean {
|
|
256
|
+
const {
|
|
257
|
+
fontSize,
|
|
258
|
+
maxWidth,
|
|
259
|
+
noteText,
|
|
260
|
+
actionButtonsWidthWithIconsOnly,
|
|
261
|
+
} = this
|
|
262
|
+
const noteWidth = Bounds.forText(noteText, { fontSize }).width
|
|
263
|
+
return noteWidth > 2 * (maxWidth - actionButtonsWidthWithIconsOnly)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@computed protected get sourcesMaxWidth(): number {
|
|
267
|
+
if (this.useFullWidthSources) return this.maxWidth
|
|
268
|
+
return this.maxWidth - this.actionButtons.width - HORIZONTAL_PADDING
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@computed protected get noteMaxWidth(): number {
|
|
272
|
+
if (this.useFullWidthNote) return this.maxWidth
|
|
273
|
+
return this.maxWidth - this.actionButtons.width - HORIZONTAL_PADDING
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@computed protected get licenseAndOriginUrlMaxWidth(): number {
|
|
277
|
+
return this.maxWidth
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@computed protected get showLicenseNextToSources(): boolean {
|
|
281
|
+
const {
|
|
282
|
+
useFullWidthSources,
|
|
283
|
+
maxWidth,
|
|
284
|
+
sources,
|
|
285
|
+
licenseAndOriginUrl,
|
|
286
|
+
note,
|
|
287
|
+
} = this
|
|
288
|
+
if (!useFullWidthSources) return false
|
|
289
|
+
// if there's space, keep the license below the note
|
|
290
|
+
if (this.useFullWidthNote || note.htmlLines.length <= 1) return false
|
|
291
|
+
return (
|
|
292
|
+
sources.width + HORIZONTAL_PADDING + licenseAndOriginUrl.width <=
|
|
293
|
+
maxWidth
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@computed protected get sources(): MarkdownTextWrap {
|
|
298
|
+
const { lineHeight } = this
|
|
299
|
+
return new MarkdownTextWrap({
|
|
300
|
+
text: this.sourcesText,
|
|
301
|
+
maxWidth: this.sourcesMaxWidth,
|
|
302
|
+
fontSize: this.sourcesFontSize,
|
|
303
|
+
lineHeight,
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@computed protected get note(): MarkdownTextWrap {
|
|
308
|
+
const { fontSize, lineHeight, manager } = this
|
|
309
|
+
return new MarkdownTextWrap({
|
|
310
|
+
text: this.markdownNoteText,
|
|
311
|
+
maxWidth: this.noteMaxWidth,
|
|
312
|
+
fontSize,
|
|
313
|
+
lineHeight,
|
|
314
|
+
detailsOrderedByReference: manager.detailsOrderedByReference,
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@computed protected get licenseAndOriginUrl(): TextWrap {
|
|
319
|
+
const { fontSize, lineHeight } = this
|
|
320
|
+
return new TextWrap({
|
|
321
|
+
text: this.licenseAndOriginUrlText,
|
|
322
|
+
maxWidth: this.licenseAndOriginUrlMaxWidth,
|
|
323
|
+
rawHtml: true,
|
|
324
|
+
fontSize,
|
|
325
|
+
lineHeight,
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@computed private get actionButtonsMaxWidth(): number {
|
|
330
|
+
const {
|
|
331
|
+
correctedUrlText,
|
|
332
|
+
licenseText,
|
|
333
|
+
maxWidth,
|
|
334
|
+
fontSize,
|
|
335
|
+
sourcesFontSize,
|
|
336
|
+
useFullWidthSources,
|
|
337
|
+
sourcesText,
|
|
338
|
+
noteText,
|
|
339
|
+
showNote,
|
|
340
|
+
useFullWidthNote,
|
|
341
|
+
} = this
|
|
342
|
+
|
|
343
|
+
const sourcesWidth = Bounds.forText(sourcesText, {
|
|
344
|
+
fontSize: sourcesFontSize,
|
|
345
|
+
}).width
|
|
346
|
+
const noteWidth = Bounds.forText(noteText, { fontSize }).width
|
|
347
|
+
|
|
348
|
+
// text next to the action buttons
|
|
349
|
+
const leftTextWidth = !useFullWidthSources
|
|
350
|
+
? sourcesWidth
|
|
351
|
+
: showNote && !useFullWidthNote
|
|
352
|
+
? noteWidth
|
|
353
|
+
: 0
|
|
354
|
+
// text above the action buttons
|
|
355
|
+
// (taken into account to ensure the action buttons are not too close to clickable text)
|
|
356
|
+
const topTextWidth = useFullWidthSources
|
|
357
|
+
? useFullWidthNote
|
|
358
|
+
? noteWidth
|
|
359
|
+
: sourcesWidth
|
|
360
|
+
: 0
|
|
361
|
+
const licenseAndOriginUrlWidth = Bounds.forText(
|
|
362
|
+
AbstractFooter.constructLicenseAndOriginUrlText(
|
|
363
|
+
correctedUrlText,
|
|
364
|
+
licenseText
|
|
365
|
+
),
|
|
366
|
+
{ fontSize }
|
|
367
|
+
).width
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
maxWidth -
|
|
371
|
+
Math.max(topTextWidth, leftTextWidth, licenseAndOriginUrlWidth) -
|
|
372
|
+
HORIZONTAL_PADDING
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
@computed private get actionButtons(): ActionButtons {
|
|
377
|
+
return new ActionButtons({
|
|
378
|
+
manager: this.manager,
|
|
379
|
+
maxWidth: this.actionButtonsMaxWidth,
|
|
380
|
+
})
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@computed get height(): number {
|
|
384
|
+
return this.topContentHeight + this.bottomContentHeight
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
base = React.createRef<HTMLDivElement>()
|
|
388
|
+
tooltipTarget: { x: number; y: number } | undefined = undefined
|
|
389
|
+
|
|
390
|
+
@action.bound private onMouseMove(e: MouseEvent): void {
|
|
391
|
+
const cc = this.base.current?.querySelector(".cclogo")
|
|
392
|
+
if (cc && cc.matches(":hover")) {
|
|
393
|
+
const div = this.base.current as HTMLDivElement
|
|
394
|
+
const grapher = div.closest(".GrapherComponent")
|
|
395
|
+
if (grapher) {
|
|
396
|
+
const mouse = getRelativeMouse(grapher, e)
|
|
397
|
+
this.tooltipTarget = { x: mouse.x, y: mouse.y }
|
|
398
|
+
} else console.error("Grapher was falsy")
|
|
399
|
+
} else this.tooltipTarget = undefined
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
override componentDidMount(): void {
|
|
403
|
+
window.addEventListener("mousemove", this.onMouseMove)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
override componentWillUnmount(): void {
|
|
407
|
+
window.removeEventListener("mousemove", this.onMouseMove)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private renderLicense(): React.ReactElement {
|
|
411
|
+
return (
|
|
412
|
+
<div className="license" style={this.licenseAndOriginUrl.htmlStyle}>
|
|
413
|
+
{this.finalUrlText && (
|
|
414
|
+
<>
|
|
415
|
+
<a
|
|
416
|
+
href={this.finalUrl}
|
|
417
|
+
{...(this.manager.isInIFrame && {
|
|
418
|
+
target: "_blank",
|
|
419
|
+
rel: "noopener",
|
|
420
|
+
})}
|
|
421
|
+
>
|
|
422
|
+
{this.finalUrlText}
|
|
423
|
+
</a>{" "}
|
|
424
|
+
|{" "}
|
|
425
|
+
</>
|
|
426
|
+
)}
|
|
427
|
+
<a
|
|
428
|
+
className={this.manager.hasOWIDLogo ? "cclogo" : undefined}
|
|
429
|
+
href={this.licenseUrl}
|
|
430
|
+
style={{ textDecoration: "none" }}
|
|
431
|
+
{...(this.manager.isInIFrame && {
|
|
432
|
+
target: "_blank",
|
|
433
|
+
rel: "noopener",
|
|
434
|
+
})}
|
|
435
|
+
>
|
|
436
|
+
{this.licenseText}
|
|
437
|
+
</a>
|
|
438
|
+
</div>
|
|
439
|
+
)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private renderSources(): React.ReactElement | null {
|
|
443
|
+
const sources = new MarkdownTextWrap({
|
|
444
|
+
text: `**Data source:** ${this.sourcesLine}`,
|
|
445
|
+
maxWidth: this.sourcesMaxWidth,
|
|
446
|
+
fontSize: this.sourcesFontSize,
|
|
447
|
+
lineHeight: this.lineHeight,
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
return (
|
|
451
|
+
<p className="sources" style={sources.style}>
|
|
452
|
+
{sources.renderHTML()}
|
|
453
|
+
{" – "}
|
|
454
|
+
<a
|
|
455
|
+
className="learn-more-about-data"
|
|
456
|
+
data-track-note="chart_click_sources"
|
|
457
|
+
tabIndex={0}
|
|
458
|
+
onClick={action((e) => {
|
|
459
|
+
e.stopPropagation()
|
|
460
|
+
|
|
461
|
+
// if embbedded, open the sources modal
|
|
462
|
+
if (
|
|
463
|
+
this.manager.isEmbeddedInAnOwidPage ||
|
|
464
|
+
this.manager.isInIFrame
|
|
465
|
+
) {
|
|
466
|
+
this.manager.activeModal = GrapherModal.Sources
|
|
467
|
+
return
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// on data pages, scroll to the "Sources and Processing" section
|
|
471
|
+
// on grapher pages, open the sources modal
|
|
472
|
+
const datapageSectionId =
|
|
473
|
+
DATAPAGE_ABOUT_THIS_DATA_SECTION_ID
|
|
474
|
+
const sourcesElement =
|
|
475
|
+
document.getElementById(datapageSectionId)
|
|
476
|
+
if (sourcesElement && sourcesElement.scrollIntoView) {
|
|
477
|
+
sourcesElement.scrollIntoView({
|
|
478
|
+
behavior: "smooth",
|
|
479
|
+
})
|
|
480
|
+
this.manager.isInFullScreenMode = false
|
|
481
|
+
} else if (sourcesElement) {
|
|
482
|
+
window.location.hash = "#" + datapageSectionId
|
|
483
|
+
this.manager.isInFullScreenMode = false
|
|
484
|
+
} else {
|
|
485
|
+
// on grapher pages, open the sources modal
|
|
486
|
+
this.manager.activeModal = GrapherModal.Sources
|
|
487
|
+
}
|
|
488
|
+
})}
|
|
489
|
+
>
|
|
490
|
+
Learn more about this data
|
|
491
|
+
</a>
|
|
492
|
+
</p>
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
private renderNote(): React.ReactElement {
|
|
497
|
+
return (
|
|
498
|
+
<p className="note" style={this.note.style}>
|
|
499
|
+
{this.note.renderHTML()}
|
|
500
|
+
</p>
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private renderVerticalSpace(): React.ReactElement {
|
|
505
|
+
return (
|
|
506
|
+
<div
|
|
507
|
+
style={{
|
|
508
|
+
height: this.verticalPadding,
|
|
509
|
+
width: "100%",
|
|
510
|
+
}}
|
|
511
|
+
/>
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
@computed private get topContentHeight(): number {
|
|
516
|
+
const { sources, note } = this
|
|
517
|
+
|
|
518
|
+
const renderSources = this.useFullWidthSources
|
|
519
|
+
const renderNote = this.showNote && this.useFullWidthNote
|
|
520
|
+
|
|
521
|
+
if (!renderSources && !renderNote) return 0
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
(renderSources ? sources.height : 0) +
|
|
525
|
+
(renderSources && renderNote ? this.verticalPadding : 0) +
|
|
526
|
+
(renderNote ? note.height : 0) +
|
|
527
|
+
this.verticalPadding
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// renders the content above the action buttons
|
|
532
|
+
// make sure to keep this.topContentHeight in sync if you edit this method
|
|
533
|
+
private renderTopContent(): React.ReactElement | null {
|
|
534
|
+
const renderSources = this.useFullWidthSources
|
|
535
|
+
const renderNote = this.showNote && this.useFullWidthNote
|
|
536
|
+
const renderLicense = this.showLicenseNextToSources
|
|
537
|
+
|
|
538
|
+
if (!renderSources && !renderNote) return null
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<>
|
|
542
|
+
<div className="SourcesFooterHTMLTop">
|
|
543
|
+
{renderSources && (
|
|
544
|
+
<div className="SourcesAndLicense">
|
|
545
|
+
{this.renderSources()}
|
|
546
|
+
{renderLicense && this.renderLicense()}
|
|
547
|
+
</div>
|
|
548
|
+
)}
|
|
549
|
+
{renderSources && renderNote && this.renderVerticalSpace()}
|
|
550
|
+
{renderNote && this.renderNote()}
|
|
551
|
+
</div>
|
|
552
|
+
{this.renderVerticalSpace()}
|
|
553
|
+
</>
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
@computed private get bottomContentHeight(): number {
|
|
558
|
+
const { actionButtons, sources, note } = this
|
|
559
|
+
|
|
560
|
+
const renderSources = !this.useFullWidthSources
|
|
561
|
+
const renderNote = this.showNote && !this.useFullWidthNote
|
|
562
|
+
const renderLicense = !this.showLicenseNextToSources
|
|
563
|
+
const renderPadding = (renderSources || renderNote) && renderLicense
|
|
564
|
+
|
|
565
|
+
const textHeight =
|
|
566
|
+
(renderSources ? sources.height : renderNote ? note.height : 0) +
|
|
567
|
+
(renderPadding ? this.verticalPadding : 0) +
|
|
568
|
+
(renderLicense ? this.licenseAndOriginUrl.height : 0)
|
|
569
|
+
|
|
570
|
+
return Math.max(textHeight, actionButtons.height)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// renders the action buttons and the content next to it
|
|
574
|
+
// make sure to keep this.bottomContentHeight in sync if you edit this method
|
|
575
|
+
private renderBottomContent(): React.ReactElement {
|
|
576
|
+
const renderSources = !this.useFullWidthSources
|
|
577
|
+
const renderNote = this.showNote && !this.useFullWidthNote
|
|
578
|
+
const renderLicense = !this.showLicenseNextToSources
|
|
579
|
+
const renderPadding = (renderSources || renderNote) && renderLicense
|
|
580
|
+
|
|
581
|
+
const licenseOnly = !renderSources && !renderNote && renderLicense
|
|
582
|
+
const noteOnly = !renderSources && renderNote && !renderLicense
|
|
583
|
+
|
|
584
|
+
// center text next to the action buttons if it's only one or two lines
|
|
585
|
+
const style = {
|
|
586
|
+
alignItems:
|
|
587
|
+
licenseOnly || (noteOnly && this.note.htmlLines.length <= 2)
|
|
588
|
+
? "center"
|
|
589
|
+
: "flex-end",
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return (
|
|
593
|
+
<div className="SourcesFooterHTMLBottom" style={style}>
|
|
594
|
+
<div>
|
|
595
|
+
{renderSources
|
|
596
|
+
? this.renderSources()
|
|
597
|
+
: renderNote
|
|
598
|
+
? this.renderNote()
|
|
599
|
+
: null}
|
|
600
|
+
{renderPadding && this.renderVerticalSpace()}
|
|
601
|
+
{renderLicense && this.renderLicense()}
|
|
602
|
+
</div>
|
|
603
|
+
<ActionButtons
|
|
604
|
+
manager={this.manager}
|
|
605
|
+
maxWidth={this.actionButtonsMaxWidth}
|
|
606
|
+
/>
|
|
607
|
+
</div>
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
override render(): React.ReactElement {
|
|
612
|
+
const { tooltipTarget } = this
|
|
613
|
+
|
|
614
|
+
return (
|
|
615
|
+
<footer
|
|
616
|
+
className="SourcesFooterHTML"
|
|
617
|
+
style={{
|
|
618
|
+
padding: `0 ${GRAPHER_FRAME_PADDING_HORIZONTAL}px`,
|
|
619
|
+
}}
|
|
620
|
+
ref={this.base}
|
|
621
|
+
>
|
|
622
|
+
{this.renderTopContent()}
|
|
623
|
+
{this.renderBottomContent()}
|
|
624
|
+
{tooltipTarget && (
|
|
625
|
+
<Tooltip
|
|
626
|
+
id="footer"
|
|
627
|
+
tooltipManager={this.manager}
|
|
628
|
+
x={tooltipTarget.x}
|
|
629
|
+
y={tooltipTarget.y}
|
|
630
|
+
style={{
|
|
631
|
+
textAlign: "left",
|
|
632
|
+
maxWidth: "304px",
|
|
633
|
+
whiteSpace: "inherit",
|
|
634
|
+
fontSize: "14px",
|
|
635
|
+
padding: "0",
|
|
636
|
+
lineHeight: "21px",
|
|
637
|
+
fontWeight: 400,
|
|
638
|
+
letterSpacing: "0.01em",
|
|
639
|
+
}}
|
|
640
|
+
>
|
|
641
|
+
<p>
|
|
642
|
+
{this.manager.branding?.licenseTooltip ??
|
|
643
|
+
"These charts are licensed under Creative Commons; you are free to use, share, and adapt this material. Click through for more information. Please bear in mind that the underlying source data might be subject to different license terms from third-party authors."}
|
|
644
|
+
</p>
|
|
645
|
+
</Tooltip>
|
|
646
|
+
)}
|
|
647
|
+
</footer>
|
|
648
|
+
)
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
@observer
|
|
653
|
+
export class Footer extends AbstractFooter<FooterProps> {}
|
|
654
|
+
|
|
655
|
+
interface StaticFooterProps extends FooterProps {
|
|
656
|
+
targetX: number
|
|
657
|
+
targetY: number
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
@observer
|
|
661
|
+
export class StaticFooter extends AbstractFooter<StaticFooterProps> {
|
|
662
|
+
override verticalPadding = 4.5
|
|
663
|
+
|
|
664
|
+
constructor(props: StaticFooterProps) {
|
|
665
|
+
super(props)
|
|
666
|
+
|
|
667
|
+
makeObservable(this)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
671
|
+
override componentDidMount(): void {}
|
|
672
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
673
|
+
override componentWillUnmount(): void {}
|
|
674
|
+
|
|
675
|
+
protected override get hideOriginUrl(): boolean {
|
|
676
|
+
return !!this.manager.hideOriginUrl || !!this.manager.isStaticAndSmall
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
@computed private get textColor(): string {
|
|
680
|
+
return GRAPHER_LIGHT_TEXT
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
protected override get showLicenseNextToSources(): boolean {
|
|
684
|
+
return (
|
|
685
|
+
this.maxWidth - this.sources.width - HORIZONTAL_PADDING >
|
|
686
|
+
this.licenseAndOriginUrl.width
|
|
687
|
+
)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
protected override get finalUrlText(): string | undefined {
|
|
691
|
+
const { correctedUrlText, licenseText, fontSize, maxWidth } = this
|
|
692
|
+
|
|
693
|
+
if (this.hideOriginUrl) return undefined
|
|
694
|
+
|
|
695
|
+
if (!correctedUrlText) return undefined
|
|
696
|
+
|
|
697
|
+
const licenseAndOriginUrlText = Footer.constructLicenseAndOriginUrlText(
|
|
698
|
+
correctedUrlText,
|
|
699
|
+
licenseText
|
|
700
|
+
)
|
|
701
|
+
const licenseAndOriginUrlWidth = Bounds.forText(
|
|
702
|
+
licenseAndOriginUrlText,
|
|
703
|
+
{ fontSize }
|
|
704
|
+
).width
|
|
705
|
+
|
|
706
|
+
// If the URL is too long, don't show it
|
|
707
|
+
if (licenseAndOriginUrlWidth > maxWidth) return undefined
|
|
708
|
+
|
|
709
|
+
return correctedUrlText
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
protected override get licenseAndOriginUrlText(): string {
|
|
713
|
+
const { finalUrl, finalUrlText, licenseText, licenseUrl, textColor } =
|
|
714
|
+
this
|
|
715
|
+
const linkStyle = `fill: ${textColor};`
|
|
716
|
+
const targetAttr = this.manager.isInIFrame
|
|
717
|
+
? ' target="_blank" rel="noopener"'
|
|
718
|
+
: ""
|
|
719
|
+
const licenseSvg = `<a style="${linkStyle}" href="${licenseUrl}"${targetAttr}>${licenseText}</a>`
|
|
720
|
+
if (!finalUrlText) return licenseSvg
|
|
721
|
+
const originUrlSvg = `<a style="${linkStyle}" href="${finalUrl}"${targetAttr}>${finalUrlText}</a>`
|
|
722
|
+
return [originUrlSvg, licenseSvg].join(" | ")
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
protected override get sourcesText(): string {
|
|
726
|
+
return `**Data source:** ${this.sourcesLine}`
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
protected override get fontSize(): number {
|
|
730
|
+
if (this.manager.isStaticAndSmall) return 14
|
|
731
|
+
return this.useBaseFontSize
|
|
732
|
+
? Math.round((13 / BASE_FONT_SIZE) * this.baseFontSize)
|
|
733
|
+
: 13
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
protected override get sourcesFontSize(): number {
|
|
737
|
+
return this.fontSize
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
protected override get sourcesMaxWidth(): number {
|
|
741
|
+
return this.maxWidth
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
protected override get noteMaxWidth(): number {
|
|
745
|
+
return this.maxWidth
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
protected override get licenseAndOriginUrlMaxWidth(): number {
|
|
749
|
+
return this.maxWidth
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
override get height(): number {
|
|
753
|
+
return (
|
|
754
|
+
this.sources.height +
|
|
755
|
+
(this.showNote ? this.note.height + this.verticalPadding : 0) +
|
|
756
|
+
(this.showLicenseNextToSources
|
|
757
|
+
? 0
|
|
758
|
+
: this.licenseAndOriginUrl.height + this.verticalPadding)
|
|
759
|
+
)
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
override render(): React.ReactElement {
|
|
763
|
+
const {
|
|
764
|
+
sources,
|
|
765
|
+
note,
|
|
766
|
+
licenseAndOriginUrl,
|
|
767
|
+
showLicenseNextToSources,
|
|
768
|
+
maxWidth,
|
|
769
|
+
} = this
|
|
770
|
+
const { targetX, targetY } = this.props
|
|
771
|
+
|
|
772
|
+
return (
|
|
773
|
+
<g
|
|
774
|
+
id={makeIdForHumanConsumption(GRAPHER_FOOTER_CLASS)}
|
|
775
|
+
className="SourcesFooter"
|
|
776
|
+
style={{ fill: this.textColor }}
|
|
777
|
+
>
|
|
778
|
+
{sources.renderSVG(targetX, targetY, {
|
|
779
|
+
id: makeIdForHumanConsumption("sources"),
|
|
780
|
+
})}
|
|
781
|
+
{this.showNote &&
|
|
782
|
+
note.renderSVG(
|
|
783
|
+
targetX,
|
|
784
|
+
targetY + sources.height + this.verticalPadding,
|
|
785
|
+
{
|
|
786
|
+
id: makeIdForHumanConsumption("note"),
|
|
787
|
+
detailsMarker: this.manager.detailsMarkerInSvg,
|
|
788
|
+
}
|
|
789
|
+
)}
|
|
790
|
+
{showLicenseNextToSources
|
|
791
|
+
? licenseAndOriginUrl.renderSVG(
|
|
792
|
+
targetX + maxWidth - licenseAndOriginUrl.width,
|
|
793
|
+
targetY,
|
|
794
|
+
{ id: makeIdForHumanConsumption("origin-url") }
|
|
795
|
+
)
|
|
796
|
+
: licenseAndOriginUrl.renderSVG(
|
|
797
|
+
targetX,
|
|
798
|
+
targetY +
|
|
799
|
+
sources.height +
|
|
800
|
+
(this.showNote
|
|
801
|
+
? note.height + this.verticalPadding
|
|
802
|
+
: 0) +
|
|
803
|
+
this.verticalPadding,
|
|
804
|
+
{ id: makeIdForHumanConsumption("origin-url") }
|
|
805
|
+
)}
|
|
806
|
+
</g>
|
|
807
|
+
)
|
|
808
|
+
}
|
|
809
|
+
}
|