@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,943 @@
|
|
|
1
|
+
import * as _ from "lodash-es"
|
|
2
|
+
import React from "react"
|
|
3
|
+
import * as R from "remeda"
|
|
4
|
+
import { observer } from "mobx-react"
|
|
5
|
+
import {
|
|
6
|
+
Bounds,
|
|
7
|
+
excludeUndefined,
|
|
8
|
+
getIdealGridParams,
|
|
9
|
+
IDEAL_PLOT_ASPECT_RATIO,
|
|
10
|
+
GridParameters,
|
|
11
|
+
Position,
|
|
12
|
+
PositionMap,
|
|
13
|
+
HorizontalAlign,
|
|
14
|
+
Color,
|
|
15
|
+
makeIdForHumanConsumption,
|
|
16
|
+
exposeInstanceOnWindow,
|
|
17
|
+
SplitBoundsPadding,
|
|
18
|
+
} from "../../utils/index.js"
|
|
19
|
+
import { shortenWithEllipsis } from "../../components/index.js"
|
|
20
|
+
import { action, computed, makeObservable, observable } from "mobx"
|
|
21
|
+
import {
|
|
22
|
+
BASE_FONT_SIZE,
|
|
23
|
+
DEFAULT_GRAPHER_BOUNDS,
|
|
24
|
+
} from "../core/GrapherConstants"
|
|
25
|
+
import {
|
|
26
|
+
GRAPHER_CHART_TYPES,
|
|
27
|
+
GrapherChartType,
|
|
28
|
+
FacetAxisDomain,
|
|
29
|
+
FacetStrategy,
|
|
30
|
+
SeriesColorMap,
|
|
31
|
+
SeriesStrategy,
|
|
32
|
+
AxisConfigInterface,
|
|
33
|
+
ChartErrorInfo,
|
|
34
|
+
} from "../../types/index.js"
|
|
35
|
+
import { ChartComponent, makeChartInstance } from "../chart/ChartTypeMap"
|
|
36
|
+
import { ChartManager } from "../chart/ChartManager"
|
|
37
|
+
import { ChartInterface, ChartState } from "../chart/ChartInterface"
|
|
38
|
+
import {
|
|
39
|
+
calculateAspectRatio,
|
|
40
|
+
getFacetGridPadding,
|
|
41
|
+
getFontSize,
|
|
42
|
+
getLabelPadding,
|
|
43
|
+
} from "./FacetChartUtils"
|
|
44
|
+
import {
|
|
45
|
+
FacetSeries,
|
|
46
|
+
FacetChartProps,
|
|
47
|
+
PlacedFacetSeries,
|
|
48
|
+
FacetChartManager,
|
|
49
|
+
} from "./FacetChartConstants"
|
|
50
|
+
import { OwidTable, CoreColumn } from "../../core-table/index.js"
|
|
51
|
+
import { autoDetectYColumnSlugs, makeSelectionArray } from "../chart/ChartUtils"
|
|
52
|
+
import { SelectionArray } from "../selection/SelectionArray"
|
|
53
|
+
import { AxisConfig } from "../axis/AxisConfig"
|
|
54
|
+
import { HorizontalAxis, VerticalAxis } from "../axis/Axis"
|
|
55
|
+
import {
|
|
56
|
+
HorizontalCategoricalColorLegend,
|
|
57
|
+
HorizontalColorLegend,
|
|
58
|
+
HorizontalColorLegendManager,
|
|
59
|
+
HorizontalNumericColorLegend,
|
|
60
|
+
} from "../legend/HorizontalColorLegends"
|
|
61
|
+
import {
|
|
62
|
+
CategoricalBin,
|
|
63
|
+
ColorScaleBin,
|
|
64
|
+
NumericBin,
|
|
65
|
+
} from "../color/ColorScaleBin"
|
|
66
|
+
import { GRAPHER_DARK_TEXT } from "../color/ColorConstants"
|
|
67
|
+
import { FocusArray } from "../focus/FocusArray"
|
|
68
|
+
import {
|
|
69
|
+
LegendInteractionState,
|
|
70
|
+
LegendStyleConfig,
|
|
71
|
+
} from "../legend/LegendInteractionState"
|
|
72
|
+
|
|
73
|
+
const SHARED_X_AXIS_MIN_FACET_COUNT = 12
|
|
74
|
+
|
|
75
|
+
const facetBackgroundColor = "none" // we don't use color yet but may use it for background later
|
|
76
|
+
|
|
77
|
+
const getContentBounds = (
|
|
78
|
+
containerBounds: Bounds,
|
|
79
|
+
manager: ChartManager,
|
|
80
|
+
chartInstance: ChartInterface
|
|
81
|
+
): Bounds => {
|
|
82
|
+
let bounds = containerBounds
|
|
83
|
+
const axes = [
|
|
84
|
+
{ config: manager.xAxisConfig, axis: chartInstance.xAxis },
|
|
85
|
+
{ config: manager.yAxisConfig, axis: chartInstance.yAxis },
|
|
86
|
+
]
|
|
87
|
+
for (const { config, axis } of axes) {
|
|
88
|
+
if (!config || !axis) continue
|
|
89
|
+
if (!config.hideAxis && config.minSize !== undefined) {
|
|
90
|
+
bounds = bounds.pad({ [axis.orient]: config.minSize })
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return bounds
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const shouldHideFacetAxis = (
|
|
97
|
+
axis: HorizontalAxis | VerticalAxis | undefined,
|
|
98
|
+
edges: Set<Position>,
|
|
99
|
+
sharedAxesSizes: PositionMap<number>
|
|
100
|
+
): boolean => {
|
|
101
|
+
if (!axis) return false
|
|
102
|
+
if (axis.hideAxis) return true
|
|
103
|
+
return axis.orient in sharedAxesSizes && !edges.has(axis.orient)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface AxisInfo {
|
|
107
|
+
config: AxisConfigInterface
|
|
108
|
+
axisAccessor: (
|
|
109
|
+
chartInstance: ChartInterface
|
|
110
|
+
) => HorizontalAxis | VerticalAxis | undefined
|
|
111
|
+
uniform: boolean
|
|
112
|
+
/** only considered when `uniform` is `true`, otherwise ignored */
|
|
113
|
+
shared: boolean
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface AxesInfo {
|
|
117
|
+
x: AxisInfo
|
|
118
|
+
y: AxisInfo
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@observer
|
|
122
|
+
export class FacetChart
|
|
123
|
+
extends React.Component<FacetChartProps>
|
|
124
|
+
implements ChartState, HorizontalColorLegendManager
|
|
125
|
+
{
|
|
126
|
+
constructor(props: FacetChartProps) {
|
|
127
|
+
super(props)
|
|
128
|
+
|
|
129
|
+
makeObservable<FacetChart, "legendHoverBin">(this, {
|
|
130
|
+
legendHoverBin: observable.ref,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
transformTable(table: OwidTable): OwidTable {
|
|
135
|
+
return table
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@computed get inputTable(): OwidTable {
|
|
139
|
+
return this.manager.table
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@computed get transformedTable(): OwidTable {
|
|
143
|
+
return (
|
|
144
|
+
this.manager.transformedTable ??
|
|
145
|
+
this.transformTable(this.inputTable)
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@computed private get manager(): FacetChartManager {
|
|
150
|
+
return this.props.manager
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@computed private get chartTypeName(): GrapherChartType {
|
|
154
|
+
return this.props.chartTypeName ?? GRAPHER_CHART_TYPES.LineChart
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@computed get isStatic(): boolean {
|
|
158
|
+
return !!this.manager.isStatic
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@computed get errorInfo(): ChartErrorInfo {
|
|
162
|
+
return { reason: "" }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@computed private get bounds(): Bounds {
|
|
166
|
+
return this.props.bounds ?? DEFAULT_GRAPHER_BOUNDS
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@computed private get legendPadding(): number {
|
|
170
|
+
const { isNumericLegend, fontSize } = this
|
|
171
|
+
return isNumericLegend ? 1.5 * fontSize : 0.875 * fontSize
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@computed private get facetsContainerBounds(): Bounds {
|
|
175
|
+
const legendHeightWithPadding =
|
|
176
|
+
this.showLegend && this.legend.height > 0
|
|
177
|
+
? this.legend.height + this.legendPadding
|
|
178
|
+
: 0
|
|
179
|
+
return this.bounds.padTop(
|
|
180
|
+
legendHeightWithPadding + 1.25 * this.facetFontSize
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@computed get fontSize(): number {
|
|
185
|
+
return this.manager.fontSize ?? BASE_FONT_SIZE
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@computed private get facetFontSize(): number {
|
|
189
|
+
return getFontSize(this.bounds.width, this.series.length, this.fontSize)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@computed private get yAxisConfig(): AxisConfig {
|
|
193
|
+
return new AxisConfig(this.manager.yAxisConfig, {
|
|
194
|
+
fontSize: this.facetFontSize,
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@computed private get uniformYAxis(): boolean {
|
|
199
|
+
// default to shared
|
|
200
|
+
const facetDomain =
|
|
201
|
+
this.yAxisConfig.facetDomain || FacetAxisDomain.shared
|
|
202
|
+
return facetDomain === FacetAxisDomain.shared
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@computed private get uniformXAxis(): boolean {
|
|
206
|
+
// TODO: maybe should not be the default for ScatterPlot?
|
|
207
|
+
return true
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@computed private get facetCount(): number {
|
|
211
|
+
return this.series.length
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@computed private get gridParams(): GridParameters {
|
|
215
|
+
const count = this.facetCount
|
|
216
|
+
const { width, height } = this.bounds
|
|
217
|
+
|
|
218
|
+
const containerAspectRatio = calculateAspectRatio(width, height)
|
|
219
|
+
|
|
220
|
+
return getIdealGridParams({
|
|
221
|
+
count,
|
|
222
|
+
containerAspectRatio,
|
|
223
|
+
idealAspectRatio: IDEAL_PLOT_ASPECT_RATIO,
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@computed private get facetGridPadding(): SplitBoundsPadding {
|
|
228
|
+
const { isSharedXAxis, facetFontSize } = this
|
|
229
|
+
return getFacetGridPadding({
|
|
230
|
+
baseFontSize: facetFontSize,
|
|
231
|
+
shouldAddRowPadding: !isSharedXAxis,
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@computed private get hideFacetLegends(): boolean {
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Passing this color map is important to ensure that all facets use the same entity colors
|
|
240
|
+
seriesColorMap: SeriesColorMap = this.manager?.seriesColorMap ?? new Map()
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Holds the intermediate render properties for chart views, before axes are synchronized,
|
|
244
|
+
* collapsed, aligned, etc.
|
|
245
|
+
*
|
|
246
|
+
* An example: a StackedArea has a Y axis domain that is the largest sum of all columns.
|
|
247
|
+
* In order to avoid replicating that logic here (stacking values), we initialize StackedArea
|
|
248
|
+
* instances, without rendering them. In a later method, we use those intermediate chart views to
|
|
249
|
+
* determine the final axes for facets, e.g. for a uniform axis, we would iterate through all
|
|
250
|
+
* instances to find the full extent of the domain.
|
|
251
|
+
*
|
|
252
|
+
* @danielgavrilov, 2021-07-13
|
|
253
|
+
*/
|
|
254
|
+
@computed private get intermediatePlacedSeries(): PlacedFacetSeries[] {
|
|
255
|
+
const { manager, series, facetCount, seriesColorMap, legendHoverBin } =
|
|
256
|
+
this
|
|
257
|
+
|
|
258
|
+
// Copy properties from manager to facets
|
|
259
|
+
const fontSize = this.facetFontSize
|
|
260
|
+
// We are using `bounds` instead of `facetsContainerBounds` because the legend
|
|
261
|
+
// is not yet created, and it is derived from the intermediate chart series.
|
|
262
|
+
const gridBoundsArr = this.bounds.grid(
|
|
263
|
+
this.gridParams,
|
|
264
|
+
this.facetGridPadding
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
const {
|
|
268
|
+
yColumnSlug,
|
|
269
|
+
xColumnSlug,
|
|
270
|
+
yColumnSlugs,
|
|
271
|
+
colorColumnSlug,
|
|
272
|
+
numericColorColumnSlug,
|
|
273
|
+
categoricalColorColumnSlug,
|
|
274
|
+
sizeColumnSlug,
|
|
275
|
+
isRelativeMode,
|
|
276
|
+
colorScale,
|
|
277
|
+
sortConfig,
|
|
278
|
+
startHandleTimeBound,
|
|
279
|
+
startTime,
|
|
280
|
+
endTime,
|
|
281
|
+
missingDataStrategy,
|
|
282
|
+
backgroundColor,
|
|
283
|
+
focusArray,
|
|
284
|
+
isStatic,
|
|
285
|
+
base,
|
|
286
|
+
tooltip,
|
|
287
|
+
shouldPinTooltipToBottom,
|
|
288
|
+
} = manager
|
|
289
|
+
|
|
290
|
+
// Use compact labels, e.g. 50k instead of 50,000.
|
|
291
|
+
const numberAbbreviation = facetCount > 2 ? "short" : "long"
|
|
292
|
+
const globalXAxisConfig: AxisConfigInterface = {
|
|
293
|
+
tickFormattingOptions: { numberAbbreviation },
|
|
294
|
+
}
|
|
295
|
+
const globalYAxisConfig: AxisConfigInterface = {
|
|
296
|
+
tickFormattingOptions: { numberAbbreviation },
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// We infer that the user cares about the trend if the axis is not uniform
|
|
300
|
+
// and the metrics on all facets are the same
|
|
301
|
+
const careAboutTrend = !this.uniformYAxis
|
|
302
|
+
if (careAboutTrend) {
|
|
303
|
+
// Force disable nice axes if we care about the trend,
|
|
304
|
+
// because nice axes misrepresent trends.
|
|
305
|
+
globalYAxisConfig.nice = false
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const table = this.transformedTable
|
|
309
|
+
|
|
310
|
+
// In order to produce consistent color scales across facets, we need to pass
|
|
311
|
+
// all possible color values from `inputTable`.
|
|
312
|
+
const colorScaleColumnOverride = this.inputTable.get(colorColumnSlug)
|
|
313
|
+
|
|
314
|
+
return series.map((series, index) => {
|
|
315
|
+
const { bounds } = gridBoundsArr[index]
|
|
316
|
+
const showLegend = !this.hideFacetLegends
|
|
317
|
+
|
|
318
|
+
const hidePoints = true
|
|
319
|
+
const hideNoDataSection = true
|
|
320
|
+
|
|
321
|
+
// NOTE: The order of overrides is important!
|
|
322
|
+
// We need to preserve most config coming in.
|
|
323
|
+
const manager: ChartManager = {
|
|
324
|
+
table,
|
|
325
|
+
fontSize,
|
|
326
|
+
showLegend,
|
|
327
|
+
hidePoints,
|
|
328
|
+
yColumnSlug,
|
|
329
|
+
xColumnSlug,
|
|
330
|
+
yColumnSlugs,
|
|
331
|
+
colorColumnSlug,
|
|
332
|
+
categoricalColorColumnSlug,
|
|
333
|
+
numericColorColumnSlug,
|
|
334
|
+
sizeColumnSlug,
|
|
335
|
+
isRelativeMode,
|
|
336
|
+
seriesColorMap,
|
|
337
|
+
colorScale,
|
|
338
|
+
colorScaleColumnOverride,
|
|
339
|
+
sortConfig,
|
|
340
|
+
startHandleTimeBound,
|
|
341
|
+
startTime,
|
|
342
|
+
endTime,
|
|
343
|
+
missingDataStrategy,
|
|
344
|
+
backgroundColor,
|
|
345
|
+
hideNoDataSection,
|
|
346
|
+
focusArray,
|
|
347
|
+
isStatic,
|
|
348
|
+
base,
|
|
349
|
+
tooltip,
|
|
350
|
+
shouldPinTooltipToBottom,
|
|
351
|
+
externalLegendHoverBin: legendHoverBin,
|
|
352
|
+
...series.manager,
|
|
353
|
+
xAxisConfig: {
|
|
354
|
+
...globalXAxisConfig,
|
|
355
|
+
...this.manager.xAxisConfig,
|
|
356
|
+
...series.manager.xAxisConfig,
|
|
357
|
+
},
|
|
358
|
+
yAxisConfig: {
|
|
359
|
+
...globalYAxisConfig,
|
|
360
|
+
...this.manager.yAxisConfig,
|
|
361
|
+
...series.manager.yAxisConfig,
|
|
362
|
+
},
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
bounds,
|
|
366
|
+
contentBounds: bounds,
|
|
367
|
+
manager,
|
|
368
|
+
seriesName: series.seriesName,
|
|
369
|
+
color: series.color,
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@computed get intermediateChartInstances(): ChartInterface[] {
|
|
375
|
+
return this.intermediatePlacedSeries.map(({ bounds, manager }) => {
|
|
376
|
+
return makeChartInstance({
|
|
377
|
+
manager,
|
|
378
|
+
bounds,
|
|
379
|
+
chartType: this.chartTypeName,
|
|
380
|
+
variant: this.manager.variant,
|
|
381
|
+
})
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
@computed private get isSharedYAxis(): boolean {
|
|
386
|
+
// When the Y axis is uniform for all facets:
|
|
387
|
+
// - for most charts, we want to only show the axis on the left-most facet charts, and omit
|
|
388
|
+
// it on the others
|
|
389
|
+
// - for bar charts the Y axis is plotted horizontally, so we don't want to omit it
|
|
390
|
+
return (
|
|
391
|
+
this.uniformYAxis &&
|
|
392
|
+
![
|
|
393
|
+
GRAPHER_CHART_TYPES.StackedDiscreteBar,
|
|
394
|
+
GRAPHER_CHART_TYPES.DiscreteBar,
|
|
395
|
+
].includes(this.chartTypeName as any)
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@computed private get isSharedXAxis(): boolean {
|
|
400
|
+
return (
|
|
401
|
+
this.uniformXAxis &&
|
|
402
|
+
// TODO: do this for stacked area charts and line charts as well?
|
|
403
|
+
this.chartTypeName === GRAPHER_CHART_TYPES.StackedBar &&
|
|
404
|
+
this.facetCount >= SHARED_X_AXIS_MIN_FACET_COUNT
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Only made public for testing
|
|
409
|
+
@computed get placedSeries(): PlacedFacetSeries[] {
|
|
410
|
+
const { intermediateChartInstances } = this
|
|
411
|
+
|
|
412
|
+
// If one of the charts should use a value-based color scheme,
|
|
413
|
+
// switch them all over for consistency
|
|
414
|
+
const useValueBasedColorScheme = intermediateChartInstances.some(
|
|
415
|
+
(instance) => instance.shouldUseValueBasedColorScheme
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
// Define the global axis config, shared between all facets
|
|
419
|
+
const sharedAxesSizes: PositionMap<number> = {}
|
|
420
|
+
|
|
421
|
+
const axes: AxesInfo = {
|
|
422
|
+
x: {
|
|
423
|
+
config: {},
|
|
424
|
+
axisAccessor: (instance) => instance.xAxis,
|
|
425
|
+
uniform: this.uniformXAxis,
|
|
426
|
+
shared: this.isSharedXAxis,
|
|
427
|
+
},
|
|
428
|
+
y: {
|
|
429
|
+
config: {},
|
|
430
|
+
axisAccessor: (instance) => instance.yAxis,
|
|
431
|
+
uniform: this.uniformYAxis,
|
|
432
|
+
shared: this.isSharedYAxis,
|
|
433
|
+
},
|
|
434
|
+
}
|
|
435
|
+
R.values(axes).forEach(({ config, axisAccessor, uniform, shared }) => {
|
|
436
|
+
// max size is the width (if vertical axis) or height (if horizontal axis)
|
|
437
|
+
const axisWithMaxSize = _.maxBy(
|
|
438
|
+
intermediateChartInstances.map(axisAccessor),
|
|
439
|
+
(axis) => axis?.size
|
|
440
|
+
)
|
|
441
|
+
if (uniform) {
|
|
442
|
+
const axes = excludeUndefined(
|
|
443
|
+
intermediateChartInstances.map(axisAccessor)
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
// If the axes are uniform, we want to find the full domain extent across all facets
|
|
447
|
+
const domains = axes.map((axis) => axis.domain)
|
|
448
|
+
config.min = _.min(domains.map((d) => d[0]))
|
|
449
|
+
config.max = _.max(domains.map((d) => d[1]))
|
|
450
|
+
|
|
451
|
+
// Find domain values across all facets
|
|
452
|
+
const domainValues = _.uniq(
|
|
453
|
+
axes.flatMap((axis) => axis.config.domainValues ?? [])
|
|
454
|
+
)
|
|
455
|
+
if (domainValues.length > 0) config.domainValues = domainValues
|
|
456
|
+
|
|
457
|
+
// Find ticks across all facets
|
|
458
|
+
const ticks = _.uniq(
|
|
459
|
+
axes.flatMap((axis) => axis.config.ticks ?? [])
|
|
460
|
+
)
|
|
461
|
+
if (ticks.length > 0) config.ticks = ticks
|
|
462
|
+
|
|
463
|
+
// If there was at least one chart with a non-undefined axis,
|
|
464
|
+
// this variable will be populated
|
|
465
|
+
if (axisWithMaxSize) {
|
|
466
|
+
// Create a new axis object with the full domain extent
|
|
467
|
+
const axis = axisWithMaxSize.clone()
|
|
468
|
+
const { size } = axis.updateDomainPreservingUserSettings([
|
|
469
|
+
config.min,
|
|
470
|
+
config.max,
|
|
471
|
+
])
|
|
472
|
+
config.minSize = size
|
|
473
|
+
if (shared) {
|
|
474
|
+
const sharedAxisSize =
|
|
475
|
+
axis.orient === Position.bottom ? 0 : size
|
|
476
|
+
sharedAxesSizes[axis.orient] = sharedAxisSize
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
} else if (axisWithMaxSize) {
|
|
480
|
+
config.minSize = axisWithMaxSize.size
|
|
481
|
+
}
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
// Allocate space for shared axes, so that the content areas of charts are all equal.
|
|
485
|
+
// If the axes are "shared", then an axis will only plotted on the facets that are on the
|
|
486
|
+
// same side as the axis.
|
|
487
|
+
// For example, a vertical Y axis would be plotted on the left-most charts only.
|
|
488
|
+
// An exception is the bottom axis, which gets plotted on the top row of charts, instead of
|
|
489
|
+
// the bottom row of charts.
|
|
490
|
+
const fullBounds = this.facetsContainerBounds.pad(sharedAxesSizes)
|
|
491
|
+
const gridBoundsArr = fullBounds.grid(
|
|
492
|
+
this.gridParams,
|
|
493
|
+
this.facetGridPadding
|
|
494
|
+
)
|
|
495
|
+
return this.intermediatePlacedSeries.map((series, i) => {
|
|
496
|
+
const chartInstance = intermediateChartInstances[i]
|
|
497
|
+
const { xAxis, yAxis } = chartInstance
|
|
498
|
+
const { bounds: initialGridBounds, cellEdges } = gridBoundsArr[i]
|
|
499
|
+
let bounds = initialGridBounds
|
|
500
|
+
// Only expand bounds if the facet is on the same edge as the shared axes
|
|
501
|
+
for (const edge of cellEdges) {
|
|
502
|
+
bounds = bounds.expand({
|
|
503
|
+
[edge]: sharedAxesSizes[edge],
|
|
504
|
+
})
|
|
505
|
+
}
|
|
506
|
+
// NOTE: The order of overrides is important!
|
|
507
|
+
// We need to preserve most config coming in.
|
|
508
|
+
const manager = {
|
|
509
|
+
...series.manager,
|
|
510
|
+
useValueBasedColorScheme,
|
|
511
|
+
xAxisConfig: {
|
|
512
|
+
// For now, sharing an x axis means hiding the tick labels of inner facets.
|
|
513
|
+
// This means that none of the x axes are actually hidden (we just don't plot their tick labels).
|
|
514
|
+
// If we ever allow shared x axes to be actually hidden, we need to be careful with how we determine
|
|
515
|
+
// the `minSize` – in the intermediate series (at this time) all axes are shown in
|
|
516
|
+
// order to find the one with maximum size, but in the placed series, some axes are
|
|
517
|
+
// hidden. This expands the available area for the chart, which can in turn increase
|
|
518
|
+
// the number of ticks shown, which can make the size of the axis in the placed
|
|
519
|
+
// series greater than the one in the intermediate series.
|
|
520
|
+
hideTickLabels: shouldHideFacetAxis(
|
|
521
|
+
xAxis,
|
|
522
|
+
cellEdges,
|
|
523
|
+
sharedAxesSizes
|
|
524
|
+
),
|
|
525
|
+
...series.manager.xAxisConfig,
|
|
526
|
+
...axes.x.config,
|
|
527
|
+
},
|
|
528
|
+
yAxisConfig: {
|
|
529
|
+
hideAxis: shouldHideFacetAxis(
|
|
530
|
+
yAxis,
|
|
531
|
+
cellEdges,
|
|
532
|
+
sharedAxesSizes
|
|
533
|
+
),
|
|
534
|
+
...series.manager.yAxisConfig,
|
|
535
|
+
...axes.y.config,
|
|
536
|
+
},
|
|
537
|
+
}
|
|
538
|
+
const contentBounds = getContentBounds(
|
|
539
|
+
bounds,
|
|
540
|
+
manager,
|
|
541
|
+
chartInstance
|
|
542
|
+
)
|
|
543
|
+
return {
|
|
544
|
+
...series,
|
|
545
|
+
bounds,
|
|
546
|
+
contentBounds,
|
|
547
|
+
manager,
|
|
548
|
+
}
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
@computed get selectionArray(): SelectionArray {
|
|
553
|
+
return makeSelectionArray(this.manager.selection)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
@computed get focusArray(): FocusArray {
|
|
557
|
+
return this.manager.focusArray ?? new FocusArray()
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
@computed private get entityFacets(): FacetSeries[] {
|
|
561
|
+
const table = this.transformedTable.filterByEntityNames(
|
|
562
|
+
this.selectionArray.selectedEntityNames
|
|
563
|
+
)
|
|
564
|
+
return this.selectionArray.selectedEntityNames.map((seriesName) => {
|
|
565
|
+
const seriesTable = table.filterByEntityNames([seriesName])
|
|
566
|
+
// Only set overrides for this facet strategy.
|
|
567
|
+
// Default properties are set elsewhere.
|
|
568
|
+
const manager: ChartManager = {
|
|
569
|
+
table: seriesTable,
|
|
570
|
+
selection: [seriesName],
|
|
571
|
+
seriesStrategy: SeriesStrategy.column,
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
seriesName,
|
|
575
|
+
color: facetBackgroundColor,
|
|
576
|
+
manager,
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
@computed private get columnFacets(): FacetSeries[] {
|
|
582
|
+
return this.yColumns.map((col) => ({
|
|
583
|
+
seriesName: col.displayName,
|
|
584
|
+
color: facetBackgroundColor,
|
|
585
|
+
// Only set overrides for this facet strategy.
|
|
586
|
+
// Default properties are set elsewhere.
|
|
587
|
+
manager: {
|
|
588
|
+
selection: this.selectionArray,
|
|
589
|
+
yColumnSlug: col.slug,
|
|
590
|
+
yColumnSlugs: [col.slug],
|
|
591
|
+
seriesStrategy: SeriesStrategy.entity,
|
|
592
|
+
},
|
|
593
|
+
}))
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
@computed private get yColumns(): CoreColumn[] {
|
|
597
|
+
return this.yColumnSlugs.map((slug) => this.inputTable.get(slug))
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
@computed private get yColumnSlugs(): string[] {
|
|
601
|
+
return autoDetectYColumnSlugs(this.manager)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
@computed private get facetStrategy(): FacetStrategy {
|
|
605
|
+
return this.manager.facetStrategy ?? FacetStrategy.none
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
@computed get series(): FacetSeries[] {
|
|
609
|
+
return this.facetStrategy === FacetStrategy.metric
|
|
610
|
+
? this.columnFacets
|
|
611
|
+
: this.entityFacets
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// legend utils
|
|
615
|
+
|
|
616
|
+
@computed private get externalLegends(): HorizontalColorLegendManager[] {
|
|
617
|
+
return excludeUndefined(
|
|
618
|
+
this.intermediateChartInstances.map(
|
|
619
|
+
(instance) => instance.externalLegend
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
@computed private get isNumericLegend(): boolean {
|
|
625
|
+
return this.externalLegends.some((legend) =>
|
|
626
|
+
legend.numericLegendData?.some((bin) => bin instanceof NumericBin)
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
@computed private get LegendClass():
|
|
631
|
+
| typeof HorizontalNumericColorLegend
|
|
632
|
+
| typeof HorizontalCategoricalColorLegend {
|
|
633
|
+
return this.isNumericLegend
|
|
634
|
+
? HorizontalNumericColorLegend
|
|
635
|
+
: HorizontalCategoricalColorLegend
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
@computed private get showLegend(): boolean {
|
|
639
|
+
const { isNumericLegend, categoricalLegendData, numericLegendData } =
|
|
640
|
+
this
|
|
641
|
+
if (this.manager.isDisplayedAlongsideComplementaryTable) return false
|
|
642
|
+
const hasBins =
|
|
643
|
+
categoricalLegendData.length > 0 || numericLegendData.length > 0
|
|
644
|
+
if (!hasBins) return false
|
|
645
|
+
if (isNumericLegend) return true
|
|
646
|
+
if (
|
|
647
|
+
this.props.chartTypeName ===
|
|
648
|
+
GRAPHER_CHART_TYPES.StackedDiscreteBar &&
|
|
649
|
+
this.facetStrategy === FacetStrategy.metric
|
|
650
|
+
) {
|
|
651
|
+
return false
|
|
652
|
+
}
|
|
653
|
+
if (
|
|
654
|
+
categoricalLegendData.length > 1 ||
|
|
655
|
+
// If the facetStrategy is metric, then the legend (probably?) shows entity items.
|
|
656
|
+
// If the user happens to select only a single entity, we don't want to collapse the
|
|
657
|
+
// legend, because it's (probably?) the only information about what is selected.
|
|
658
|
+
// This is fragile and ideally we shouldn't be making assumptions about what type of
|
|
659
|
+
// items are shown on the legend, but it works for now...
|
|
660
|
+
// -@danielgavrilov, 2021-09-28
|
|
661
|
+
(this.facetStrategy === FacetStrategy.metric &&
|
|
662
|
+
this.props.manager.canSelectMultipleEntities)
|
|
663
|
+
) {
|
|
664
|
+
return true
|
|
665
|
+
}
|
|
666
|
+
return false
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
private getExternalLegendProp<
|
|
670
|
+
Prop extends keyof HorizontalColorLegendManager,
|
|
671
|
+
>(prop: Prop): HorizontalColorLegendManager[Prop] | undefined {
|
|
672
|
+
for (const externalLegend of this.externalLegends) {
|
|
673
|
+
if (externalLegend[prop] !== undefined) {
|
|
674
|
+
return externalLegend[prop]
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return undefined
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private getUniqBins<Bin extends ColorScaleBin>(bins: Bin[]): Bin[] {
|
|
681
|
+
return _.uniqWith(bins, (binA, binB): boolean => {
|
|
682
|
+
// For categorical bins, the `.equals()` method isn't good enough,
|
|
683
|
+
// because it only compares `.index`, which in this case can be
|
|
684
|
+
// identical even when the bins are not, because they are coming
|
|
685
|
+
// from different charts (facets).
|
|
686
|
+
if (binA instanceof CategoricalBin) {
|
|
687
|
+
return binA.text === binB.text
|
|
688
|
+
}
|
|
689
|
+
return binA.equals(binB)
|
|
690
|
+
})
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// legend props
|
|
694
|
+
|
|
695
|
+
@computed get legendX(): number {
|
|
696
|
+
return this.bounds.x
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
@computed get numericLegendY(): number {
|
|
700
|
+
return this.bounds.top
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
@computed get categoryLegendY(): number {
|
|
704
|
+
return this.bounds.top
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
@computed get legendMaxWidth(): number {
|
|
708
|
+
return this.bounds.width
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
@computed get legendAlign(): HorizontalAlign {
|
|
712
|
+
return this.isNumericLegend
|
|
713
|
+
? HorizontalAlign.center
|
|
714
|
+
: HorizontalAlign.left
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
@computed get legendTitle(): string | undefined {
|
|
718
|
+
return this.getExternalLegendProp("legendTitle")
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
@computed get legendHeight(): number | undefined {
|
|
722
|
+
return this.getExternalLegendProp("legendHeight")
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
@computed get legendTickSize(): number | undefined {
|
|
726
|
+
return this.getExternalLegendProp("legendTickSize")
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
@computed get numericBinSize(): number | undefined {
|
|
730
|
+
return this.getExternalLegendProp("numericBinSize")
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
@computed get hoverColors(): Color[] | undefined {
|
|
734
|
+
if (!this.legendHoverBin) return undefined
|
|
735
|
+
return [this.legendHoverBin.color]
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
@computed get activeColors(): Color[] | undefined {
|
|
739
|
+
if (!this.focusArray) return undefined
|
|
740
|
+
|
|
741
|
+
// find colours of all currently focused series
|
|
742
|
+
const activeColors = _.uniq(
|
|
743
|
+
this.intermediateChartInstances.flatMap((chartInstance) =>
|
|
744
|
+
chartInstance.chartState.series
|
|
745
|
+
.filter((series) => this.focusArray.has(series.seriesName))
|
|
746
|
+
.map((series) => series.color)
|
|
747
|
+
)
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
return activeColors.length > 0 ? activeColors : undefined
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
@computed get numericLegendData(): ColorScaleBin[] {
|
|
754
|
+
if (!this.isNumericLegend || !this.hideFacetLegends) return []
|
|
755
|
+
const allBins: ColorScaleBin[] = this.externalLegends.flatMap(
|
|
756
|
+
(legend) => [
|
|
757
|
+
...(legend.numericLegendData ?? []),
|
|
758
|
+
...(legend.categoricalLegendData ?? []),
|
|
759
|
+
]
|
|
760
|
+
)
|
|
761
|
+
const uniqBins = this.getUniqBins(allBins)
|
|
762
|
+
const sortedBins = _.sortBy(
|
|
763
|
+
uniqBins,
|
|
764
|
+
(bin) => bin instanceof CategoricalBin
|
|
765
|
+
)
|
|
766
|
+
return sortedBins
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
@computed get categoricalLegendData(): CategoricalBin[] {
|
|
770
|
+
if (this.isNumericLegend || !this.hideFacetLegends) return []
|
|
771
|
+
|
|
772
|
+
const allBins: CategoricalBin[] = this.externalLegends
|
|
773
|
+
.flatMap((legend) => [
|
|
774
|
+
...(legend.numericLegendData ?? []),
|
|
775
|
+
...(legend.categoricalLegendData ?? []),
|
|
776
|
+
])
|
|
777
|
+
.filter((bin) => bin instanceof CategoricalBin) as CategoricalBin[]
|
|
778
|
+
|
|
779
|
+
const uniqBins = this.getUniqBins(allBins).map(
|
|
780
|
+
// Remap index to ensure it's unique (the above procedure can lead to duplicates)
|
|
781
|
+
(bin, index) => new CategoricalBin({ ...bin.props, index })
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
// Hide single-item legends for metric facets
|
|
785
|
+
if (this.facetStrategy === FacetStrategy.metric && uniqBins.length <= 1)
|
|
786
|
+
return []
|
|
787
|
+
|
|
788
|
+
// Stacked area and bar charts reverse the stacking order
|
|
789
|
+
if (
|
|
790
|
+
this.chartTypeName === GRAPHER_CHART_TYPES.StackedArea ||
|
|
791
|
+
this.chartTypeName === GRAPHER_CHART_TYPES.StackedBar
|
|
792
|
+
)
|
|
793
|
+
return _.reverse(uniqBins)
|
|
794
|
+
|
|
795
|
+
return uniqBins
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
private legendHoverBin: ColorScaleBin | undefined = undefined
|
|
799
|
+
|
|
800
|
+
@action.bound onLegendMouseOver(bin: ColorScaleBin): void {
|
|
801
|
+
this.legendHoverBin = bin
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
@action.bound onLegendMouseLeave(): void {
|
|
805
|
+
this.legendHoverBin = undefined
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
@action.bound onLegendClick(bin: ColorScaleBin): void {
|
|
809
|
+
if (!this.manager.focusArray || !this.isFocusModeSupported) return
|
|
810
|
+
|
|
811
|
+
// find all series (of all facets) that are contained in the bin
|
|
812
|
+
const seriesNames = _.uniq(
|
|
813
|
+
this.intermediateChartInstances.flatMap((chartInstance) =>
|
|
814
|
+
chartInstance.chartState.series
|
|
815
|
+
.filter((series) => bin.contains(series.seriesName))
|
|
816
|
+
.map((series) => series.seriesName)
|
|
817
|
+
)
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
this.manager.focusArray.toggle(...seriesNames)
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
getLegendBinState(bin: ColorScaleBin): LegendInteractionState {
|
|
824
|
+
if (!this.activeColors && !this.hoverColors)
|
|
825
|
+
return LegendInteractionState.Default
|
|
826
|
+
|
|
827
|
+
const isHovered = this.hoverColors?.includes(bin.color)
|
|
828
|
+
if (isHovered) return LegendInteractionState.Focused
|
|
829
|
+
|
|
830
|
+
const isActive = this.activeColors?.includes(bin.color)
|
|
831
|
+
return isActive
|
|
832
|
+
? LegendInteractionState.Focused
|
|
833
|
+
: LegendInteractionState.Muted
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
@computed get legendStyleConfig(): LegendStyleConfig | undefined {
|
|
837
|
+
return this.externalLegends[0]?.legendStyleConfig
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
@computed get numericLegendStyleConfig(): LegendStyleConfig | undefined {
|
|
841
|
+
return this.externalLegends[0]?.numericLegendStyleConfig
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
@computed get categoricalLegendStyleConfig():
|
|
845
|
+
| LegendStyleConfig
|
|
846
|
+
| undefined {
|
|
847
|
+
return this.externalLegends[0]?.categoricalLegendStyleConfig
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// end of legend props
|
|
851
|
+
|
|
852
|
+
@computed private get legend(): HorizontalColorLegend {
|
|
853
|
+
return new this.LegendClass({ manager: this })
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
@computed private get isFocusModeSupported(): boolean {
|
|
857
|
+
return (
|
|
858
|
+
this.chartTypeName === GRAPHER_CHART_TYPES.LineChart ||
|
|
859
|
+
this.chartTypeName === GRAPHER_CHART_TYPES.SlopeChart
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* In order to display a potentially long facet label in the potentially tight space, we
|
|
865
|
+
* shrink and shorten the label as follows to prevent overlap between neighbouring labels:
|
|
866
|
+
* - If the label already fits, we're happy :)
|
|
867
|
+
* - Otherwise, we calculate the ideal font size where it would fit perfectly.
|
|
868
|
+
* However, in order to not make the label tiny, we cap the font size at 0.7 * baseFontSize
|
|
869
|
+
* - If the label still doesn't fit, we shorten it to the number of characters that fit and append an ellispsis (…)
|
|
870
|
+
* -@MarcelGerber, 2021-10-28
|
|
871
|
+
*/
|
|
872
|
+
private shrinkAndShortenFacetLabel(
|
|
873
|
+
label: string,
|
|
874
|
+
availableWidth: number,
|
|
875
|
+
baseFontSize: number // font size to use when we're not shrinking
|
|
876
|
+
): { fontSize: number; shortenedLabel: string } {
|
|
877
|
+
// How much width would we need if we were to render the text at font size 1?
|
|
878
|
+
// We calculate this to compute the ideal font size from the available width.
|
|
879
|
+
const textBounds = Bounds.forText(label, {
|
|
880
|
+
fontSize: 1,
|
|
881
|
+
})
|
|
882
|
+
const idealFontSize = availableWidth / textBounds.width
|
|
883
|
+
|
|
884
|
+
// Clamp the ideal font size: 0.7 * baseFontSize <= fontSize <= baseFontSize
|
|
885
|
+
const fontSize = Math.min(
|
|
886
|
+
baseFontSize,
|
|
887
|
+
Math.max(0.7 * baseFontSize, idealFontSize)
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
if (fontSize > idealFontSize) {
|
|
891
|
+
label = shortenWithEllipsis(label, availableWidth, { fontSize })
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return { fontSize, shortenedLabel: label }
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
override componentDidMount(): void {
|
|
898
|
+
exposeInstanceOnWindow(this, "facetChart")
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
override render(): React.ReactElement {
|
|
902
|
+
const { facetFontSize, LegendClass, showLegend } = this
|
|
903
|
+
return (
|
|
904
|
+
<React.Fragment>
|
|
905
|
+
{showLegend && <LegendClass manager={this} />}
|
|
906
|
+
{this.placedSeries.map((facetChart, index: number) => {
|
|
907
|
+
const { bounds, contentBounds, seriesName } = facetChart
|
|
908
|
+
const labelPadding = getLabelPadding(facetFontSize)
|
|
909
|
+
|
|
910
|
+
const { fontSize, shortenedLabel } =
|
|
911
|
+
this.shrinkAndShortenFacetLabel(
|
|
912
|
+
seriesName,
|
|
913
|
+
contentBounds.width,
|
|
914
|
+
facetFontSize
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
return (
|
|
918
|
+
<React.Fragment key={index}>
|
|
919
|
+
<text
|
|
920
|
+
x={contentBounds.x}
|
|
921
|
+
y={contentBounds.top - labelPadding}
|
|
922
|
+
fill={GRAPHER_DARK_TEXT}
|
|
923
|
+
fontSize={fontSize}
|
|
924
|
+
style={{ fontWeight: 700 }}
|
|
925
|
+
>
|
|
926
|
+
{shortenedLabel}
|
|
927
|
+
<title>{seriesName}</title>
|
|
928
|
+
</text>
|
|
929
|
+
<g id={makeIdForHumanConsumption(seriesName)}>
|
|
930
|
+
<ChartComponent
|
|
931
|
+
manager={facetChart.manager}
|
|
932
|
+
chartType={this.chartTypeName}
|
|
933
|
+
variant={this.manager.variant}
|
|
934
|
+
bounds={bounds}
|
|
935
|
+
/>
|
|
936
|
+
</g>
|
|
937
|
+
</React.Fragment>
|
|
938
|
+
)
|
|
939
|
+
})}
|
|
940
|
+
</React.Fragment>
|
|
941
|
+
)
|
|
942
|
+
}
|
|
943
|
+
}
|