@cdc/chart 4.25.8 → 4.25.11
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/.claude/settings.local.json +9 -0
- package/dist/{cdcchart-1a1724a1.es.js → cdcchart-dgT_1dIT.es.js} +136 -151
- package/dist/cdcchart.js +44236 -40355
- package/examples/feature/__data__/planet-example-data.json +0 -30
- package/examples/feature/boxplot/valid-boxplot.csv +38 -17
- package/examples/grouped-bar-test.json +400 -0
- package/examples/private/DEV-11825.json +573 -0
- package/examples/private/d.json +382 -0
- package/examples/private/example-2.json +49784 -0
- package/examples/private/f2.json +1 -0
- package/examples/private/f4.json +1577 -0
- package/examples/private/forecast.json +1180 -0
- package/examples/private/lollipop.json +468 -0
- package/examples/private/na.json +913 -0
- package/examples/private/new.json +48756 -0
- package/examples/private/pie-chart-legend.json +904 -0
- package/examples/private/test-data.csv +28 -0
- package/examples/suppressed_tooltip.json +480 -0
- package/index.html +2 -133
- package/package.json +25 -7
- package/src/CdcChart.tsx +9 -13
- package/src/CdcChartComponent.tsx +403 -92
- package/src/_stories/Chart.Anchors.stories.tsx +2 -2
- package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
- package/src/_stories/Chart.CI.stories.tsx +1 -1
- package/src/_stories/Chart.Combo.stories.tsx +18 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
- package/src/_stories/Chart.Filters.stories.tsx +2 -2
- package/src/_stories/Chart.Forecast.stories.tsx +36 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +520 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.Patterns.stories.tsx +20 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +220 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
- package/src/_stories/Chart.SmallMultiples.stories.tsx +47 -0
- package/src/_stories/Chart.stories.tsx +8 -5
- package/src/_stories/Chart.tooltip.stories.tsx +1 -1
- package/src/_stories/ChartAnnotation.stories.tsx +7 -4
- package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
- package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
- package/src/_stories/ChartBar.Editor.stories.tsx +3580 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +658 -0
- package/src/_stories/ChartEditor.stories.tsx +59 -60
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
- package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
- package/src/_stories/_mock/combo.json +451 -0
- package/src/_stories/_mock/editor-test-configs.json +376 -0
- package/src/_stories/_mock/editor-test-datasets.json +477 -0
- package/src/_stories/_mock/editor-tests/bar-chart-editor-test.json +255 -0
- package/src/_stories/_mock/editor-tests/bar-chart-general-test.json +267 -0
- package/src/_stories/_mock/editor-tests/bar-chart-test.json +237 -0
- package/src/_stories/_mock/forecast_combo_with_gaps.json +913 -0
- package/src/_stories/_mock/pie_config.json +257 -62
- package/src/_stories/_mock/small_multiples/small_multiples_bars.json +1944 -0
- package/src/_stories/_mock/small_multiples/small_multiples_big_data_bars.json +1114 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines.json +2646 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines_colors.json +1305 -0
- package/src/_stories/_mock/small_multiples/small_multiples_stacked_bars.json +1936 -0
- package/src/_stories/_mock/stacked-pattern-test.json +520 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Annotations/components/findNearestDatum.ts +6 -41
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -6
- package/src/components/AreaChart/index.tsx +1 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +161 -22
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +138 -5
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
- package/src/components/BarChart/components/BarChart.Vertical.tsx +155 -22
- package/src/components/BarChart/helpers/index.ts +43 -4
- package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
- package/src/components/BarChart/helpers/useBarChart.ts +25 -3
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
- package/src/components/BoxPlot/helpers/index.ts +3 -3
- package/src/components/Brush/BrushChart.tsx +1 -1
- package/src/components/DeviationBar.jsx +9 -6
- package/src/components/EditorPanel/EditorPanel.tsx +563 -229
- package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +96 -111
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +19 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +461 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +80 -67
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +422 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +188 -139
- package/src/components/EditorPanel/components/Panels/index.tsx +5 -1
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
- package/src/components/EditorPanel/editor-panel.scss +0 -20
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
- package/src/components/EditorPanel/useEditorPermissions.ts +7 -15
- package/src/components/Forecasting/Forecasting.tsx +175 -27
- package/src/components/ForestPlot/ForestPlot.tsx +11 -7
- package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
- package/src/components/Legend/Legend.Component.tsx +114 -14
- package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
- package/src/components/Legend/helpers/getLegendClasses.ts +0 -1
- package/src/components/LegendWrapper.tsx +1 -1
- package/src/components/LineChart/LineChartProps.ts +0 -3
- package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
- package/src/components/LineChart/helpers.ts +1 -1
- package/src/components/LineChart/index.tsx +38 -15
- package/src/components/LinearChart.tsx +96 -84
- package/src/components/PairedBarChart.jsx +6 -4
- package/src/components/PieChart/PieChart.tsx +170 -54
- package/src/components/Regions/components/Regions.tsx +3 -24
- package/src/components/Sankey/components/Sankey.tsx +7 -1
- package/src/components/Sankey/types/index.ts +1 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +198 -0
- package/src/components/SmallMultiples/SmallMultiples.css +32 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +271 -0
- package/src/components/SmallMultiples/index.ts +2 -0
- package/src/data/initial-state.js +327 -293
- package/src/helpers/buildForecastPaletteMappings.ts +112 -0
- package/src/helpers/buildForecastPaletteOptions.ts +71 -0
- package/src/helpers/getColorScale.ts +82 -8
- package/src/{hooks/useMinMax.ts → helpers/getMinMax.ts} +14 -7
- package/src/helpers/getNewRuntime.ts +1 -1
- package/src/helpers/getTransformedData.ts +1 -1
- package/src/helpers/getYAxisAutoPadding.ts +53 -0
- package/src/helpers/smallMultiplesHelpers.ts +529 -0
- package/src/hooks/useChartHoverAnalytics.tsx +44 -0
- package/src/hooks/useProgrammaticTooltip.ts +96 -0
- package/src/hooks/useReduceData.ts +105 -70
- package/src/hooks/useScales.ts +88 -34
- package/src/hooks/useSmallMultipleSynchronization.ts +59 -0
- package/src/hooks/useTooltip.tsx +116 -29
- package/src/index.jsx +0 -2
- package/src/scss/main.scss +13 -80
- package/src/store/chart.actions.ts +2 -0
- package/src/store/chart.reducer.ts +5 -1
- package/src/test/CdcChart.test.jsx +8 -3
- package/src/types/ChartConfig.ts +53 -11
- package/src/types/ChartContext.ts +4 -0
- package/vite.config.js +1 -1
- package/vitest.config.ts +16 -0
- package/src/_stories/_mock/pie_data.json +0 -218
- package/src/components/AreaChart/components/AreaChart.jsx +0 -109
- package/src/coreStyles_chart.scss +0 -3
- package/src/helpers/configHelpers.ts +0 -28
- package/src/helpers/generateColorsArray.ts +0 -8
- package/src/helpers/sort.ts +0 -7
- package/src/hooks/useActiveElement.js +0 -19
- package/src/hooks/useChartClasses.js +0 -41
- package/src/hooks/useColorPalette.js +0 -76
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a palette lookup map with backward-compatibility mappings for forecast charts
|
|
3
|
+
*
|
|
4
|
+
* This function creates multiple naming format mappings to support:
|
|
5
|
+
* - V1 configs using old naming like "Sequential Blue" (Title Case with spaces)
|
|
6
|
+
* - V2 configs using new naming like "sequential-blue" (lowercase with hyphens)
|
|
7
|
+
* - MPX legacy aliases (v1 only)
|
|
8
|
+
*
|
|
9
|
+
* @param processedPalettes - The palette data processed through updatePaletteNames
|
|
10
|
+
* @param paletteVersion - The palette version (1 or 2) from the config
|
|
11
|
+
* @returns A palette map with keys in various formats pointing to color arrays
|
|
12
|
+
*/
|
|
13
|
+
export const buildForecastPaletteMappings = (
|
|
14
|
+
processedPalettes: Record<string, string[]>,
|
|
15
|
+
paletteVersion: number
|
|
16
|
+
): Record<string, string[]> => {
|
|
17
|
+
const paletteMap: Record<string, string[]> = {}
|
|
18
|
+
|
|
19
|
+
// Create base mappings with multiple naming formats for backward compatibility:
|
|
20
|
+
// - sequential-blue (hyphenated)
|
|
21
|
+
// - sequential_blue (underscore)
|
|
22
|
+
// - Sequential Blue (with spaces)
|
|
23
|
+
Object.keys(processedPalettes).forEach(key => {
|
|
24
|
+
const value = processedPalettes[key]
|
|
25
|
+
// Original key
|
|
26
|
+
paletteMap[key] = value
|
|
27
|
+
// Lowercase with hyphens
|
|
28
|
+
paletteMap[key.toLowerCase().replace(/ /g, '-')] = value
|
|
29
|
+
// Lowercase with underscores
|
|
30
|
+
paletteMap[key.toLowerCase().replace(/ /g, '_')] = value
|
|
31
|
+
// Original key variations
|
|
32
|
+
paletteMap[key.replace(/_/g, '-')] = value
|
|
33
|
+
paletteMap[key.toLowerCase()] = value
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (paletteVersion === 1) {
|
|
37
|
+
// V1: Add MPX legacy aliases
|
|
38
|
+
// Note: Sequential Blue Two was aliased as "Sequential Blue 2 (MPX)" and Sequential Orange as "Sequential Orange (MPX)"
|
|
39
|
+
const MPX_ALIASES: Record<string, string[]> = {
|
|
40
|
+
'sequential-blue-two': ['sequential-blue-2-(mpx)', 'sequential-blue-2-(MPX)'],
|
|
41
|
+
'sequential-blue-tworeverse': ['sequential-blue-2-(mpx)reverse', 'sequential-blue-2-(MPX)reverse'],
|
|
42
|
+
'sequential-orange': ['sequential-orange-(mpx)', 'sequential-orange-(MPX)'],
|
|
43
|
+
'sequential-orangereverse': ['sequential-orange-(mpx)reverse', 'sequential-orange-(MPX)reverse']
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Object.entries(MPX_ALIASES).forEach(([canonical, aliases]) => {
|
|
47
|
+
const palette = paletteMap[canonical]
|
|
48
|
+
if (palette) {
|
|
49
|
+
aliases.forEach(alias => {
|
|
50
|
+
paletteMap[alias] = palette
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
} else {
|
|
55
|
+
// V2: Add backward compatibility mappings for migrated configs
|
|
56
|
+
// Map old v1 sequential palette names (Title Case with spaces) to v2 palettes
|
|
57
|
+
// Also includes MPX aliases since users may have selected those in v1
|
|
58
|
+
const V2_MIGRATION_ALIASES: Record<string, string[]> = {
|
|
59
|
+
'sequential-blue': [
|
|
60
|
+
'Sequential Blue',
|
|
61
|
+
'sequential blue',
|
|
62
|
+
'Sequential Blue Two',
|
|
63
|
+
'Sequential Blue Three',
|
|
64
|
+
'sequential-blue-2-(mpx)',
|
|
65
|
+
'sequential-blue-2-(MPX)',
|
|
66
|
+
'Sequential Blue 2 (MPX)'
|
|
67
|
+
],
|
|
68
|
+
'sequential-bluereverse': [
|
|
69
|
+
'Sequential Blue Reverse',
|
|
70
|
+
'sequential bluereverse',
|
|
71
|
+
'Sequential Blue Two Reverse',
|
|
72
|
+
'Sequential Blue Three Reverse',
|
|
73
|
+
'sequential-blue-2-(mpx)reverse',
|
|
74
|
+
'sequential-blue-2-(MPX)reverse',
|
|
75
|
+
'Sequential Blue 2 (MPX) Reverse'
|
|
76
|
+
],
|
|
77
|
+
'sequential-green': ['Sequential Green', 'sequential green'],
|
|
78
|
+
'sequential-greenreverse': ['Sequential Green Reverse', 'sequential greenreverse'],
|
|
79
|
+
'sequential-orange': [
|
|
80
|
+
'Sequential Orange',
|
|
81
|
+
'sequential orange',
|
|
82
|
+
'Sequential Orange Two',
|
|
83
|
+
'sequential-orange-(mpx)',
|
|
84
|
+
'sequential-orange-(MPX)',
|
|
85
|
+
'Sequential Orange (MPX)'
|
|
86
|
+
],
|
|
87
|
+
'sequential-orangereverse': [
|
|
88
|
+
'Sequential Orange Reverse',
|
|
89
|
+
'sequential orangereverse',
|
|
90
|
+
'Sequential Orange Two Reverse',
|
|
91
|
+
'sequential-orange-(mpx)reverse',
|
|
92
|
+
'sequential-orange-(MPX)reverse',
|
|
93
|
+
'Sequential Orange (MPX) Reverse'
|
|
94
|
+
],
|
|
95
|
+
'sequential-purple': ['Sequential Purple', 'sequential purple'],
|
|
96
|
+
'sequential-purplereverse': ['Sequential Purple Reverse', 'sequential purplereverse'],
|
|
97
|
+
'sequential-teal': ['Sequential Teal', 'sequential teal'],
|
|
98
|
+
'sequential-tealreverse': ['Sequential Teal Reverse', 'sequential tealreverse']
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Object.entries(V2_MIGRATION_ALIASES).forEach(([canonical, aliases]) => {
|
|
102
|
+
const palette = paletteMap[canonical]
|
|
103
|
+
if (palette) {
|
|
104
|
+
aliases.forEach(alias => {
|
|
105
|
+
paletteMap[alias] = palette
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return paletteMap
|
|
112
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds user-friendly palette options for forecast color dropdowns
|
|
3
|
+
*
|
|
4
|
+
* This function takes processed palette data and transforms it into a format
|
|
5
|
+
* suitable for dropdown UI with clean, readable display names.
|
|
6
|
+
*
|
|
7
|
+
* @param processedPalettes - The palette data processed through updatePaletteNames
|
|
8
|
+
* @param paletteVersion - The palette version (1 or 2) from the config
|
|
9
|
+
* @returns An object with kebab-case keys and user-friendly display names as values
|
|
10
|
+
*/
|
|
11
|
+
export const buildForecastPaletteOptions = (
|
|
12
|
+
processedPalettes: Record<string, string[]>,
|
|
13
|
+
paletteVersion: number
|
|
14
|
+
): Record<string, string> => {
|
|
15
|
+
const paletteOptions: Record<string, string> = {}
|
|
16
|
+
|
|
17
|
+
// Create user-friendly options with clean, readable names
|
|
18
|
+
Object.keys(processedPalettes).forEach(key => {
|
|
19
|
+
// Clean up the display name:
|
|
20
|
+
// 1. Replace underscores with spaces
|
|
21
|
+
// 2. Add space before "reverse" if it's concatenated
|
|
22
|
+
// 3. Capitalize first letter of each word
|
|
23
|
+
const cleanName = key
|
|
24
|
+
.replace(/_/g, ' ') // Replace underscores with spaces
|
|
25
|
+
.replace(/reverse$/i, ' Reverse') // Add space before reverse
|
|
26
|
+
.replace(/\b\w/g, char => char.toUpperCase()) // Capitalize first letter of each word
|
|
27
|
+
|
|
28
|
+
// Use lowercase with hyphens as the key for consistency with existing saved values
|
|
29
|
+
// Convert both underscores and spaces to hyphens
|
|
30
|
+
const displayKey = key.replace(/_/g, '-').replace(/ /g, '-').toLowerCase()
|
|
31
|
+
paletteOptions[displayKey] = cleanName
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Add MPX aliases for v1 backward compatibility (these were historical names)
|
|
35
|
+
if (paletteVersion === 1) {
|
|
36
|
+
// Map "Sequential Blue Two" to MPX alias
|
|
37
|
+
if (paletteOptions['sequential-blue-two']) {
|
|
38
|
+
paletteOptions['sequential-blue-2-(mpx)'] = 'Sequential Blue 2 (MPX)'
|
|
39
|
+
}
|
|
40
|
+
if (paletteOptions['sequential-blue-tworeverse']) {
|
|
41
|
+
paletteOptions['sequential-blue-2-(mpx)reverse'] = 'Sequential Blue 2 (MPX) Reverse'
|
|
42
|
+
}
|
|
43
|
+
// Map "Sequential Orange" to MPX alias
|
|
44
|
+
if (paletteOptions['sequential-orange']) {
|
|
45
|
+
paletteOptions['sequential-orange-(mpx)'] = 'Sequential Orange (MPX)'
|
|
46
|
+
}
|
|
47
|
+
if (paletteOptions['sequential-orangereverse']) {
|
|
48
|
+
paletteOptions['sequential-orange-(mpx)reverse'] = 'Sequential Orange (MPX) Reverse'
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
// V2 backward compatibility: add options for old v1 sequential palette names
|
|
52
|
+
// These are for migrated configs that still have v1-style names like "Sequential Blue"
|
|
53
|
+
if (paletteOptions['sequential-blue']) {
|
|
54
|
+
paletteOptions['sequential-blue'] = 'Sequential Blue'
|
|
55
|
+
}
|
|
56
|
+
if (paletteOptions['sequential-green']) {
|
|
57
|
+
paletteOptions['sequential-green'] = 'Sequential Green'
|
|
58
|
+
}
|
|
59
|
+
if (paletteOptions['sequential-orange']) {
|
|
60
|
+
paletteOptions['sequential-orange'] = 'Sequential Orange'
|
|
61
|
+
}
|
|
62
|
+
if (paletteOptions['sequential-purple']) {
|
|
63
|
+
paletteOptions['sequential-purple'] = 'Sequential Purple'
|
|
64
|
+
}
|
|
65
|
+
if (paletteOptions['sequential-teal']) {
|
|
66
|
+
paletteOptions['sequential-teal'] = 'Sequential Teal'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return paletteOptions
|
|
71
|
+
}
|
|
@@ -1,20 +1,94 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
2
|
+
import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
3
|
+
import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
|
|
2
4
|
import { scaleOrdinal } from '@visx/scale'
|
|
3
5
|
import { ChartConfig } from '../types/ChartConfig'
|
|
6
|
+
import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
7
|
+
import { getFallbackColorPalette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
|
|
8
|
+
import {
|
|
9
|
+
v2ColorDistribution,
|
|
10
|
+
divergentColorDistribution,
|
|
11
|
+
colorblindColorDistribution
|
|
12
|
+
} from '@cdc/core/helpers/palettes/colorDistributions'
|
|
4
13
|
|
|
5
14
|
export const getColorScale = (config: ChartConfig): ((value: string) => string) => {
|
|
6
15
|
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
7
16
|
? config.twoColor.palette
|
|
8
|
-
: config.palette
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
: config.general?.palette?.name
|
|
18
|
+
const colorPalettes = filterChartColorPalettes(config)
|
|
19
|
+
|
|
20
|
+
// Get the correct version of two-color palettes
|
|
21
|
+
const version = getColorPaletteVersion(config)
|
|
22
|
+
const versionKey = `v${version}`
|
|
23
|
+
const versionedTwoColorPalette = twoColorPalette[versionKey] || twoColorPalette.v2
|
|
24
|
+
|
|
25
|
+
// For paired/deviation bars, only use two-color palettes
|
|
26
|
+
const palettesSource = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
27
|
+
? versionedTwoColorPalette
|
|
28
|
+
: colorPalettes
|
|
29
|
+
|
|
30
|
+
const allPalettes: Record<string, string[]> = { ...versionedTwoColorPalette, ...colorPalettes }
|
|
31
|
+
|
|
32
|
+
// Migrate old palette name if needed
|
|
33
|
+
const migratedPaletteName = configPalette ? configPalette : getFallbackColorPalette(config)
|
|
34
|
+
|
|
35
|
+
// Check for customColorsOrdered first (direct 1-to-1 mapping, no distribution needed)
|
|
36
|
+
if (config.general?.palette?.customColorsOrdered && Array.isArray(config.general.palette.customColorsOrdered)) {
|
|
37
|
+
const customColorsOrdered = config.general.palette.customColorsOrdered
|
|
38
|
+
return scaleOrdinal({
|
|
39
|
+
domain: config.runtime.seriesLabelsAll,
|
|
40
|
+
range: customColorsOrdered,
|
|
41
|
+
unknown: null
|
|
42
|
+
})
|
|
43
|
+
}
|
|
12
44
|
|
|
13
|
-
|
|
14
|
-
palette
|
|
45
|
+
let palette =
|
|
46
|
+
config.general?.palette?.customColors ||
|
|
47
|
+
palettesSource[migratePaletteWithMap(migratedPaletteName, paletteMigrationMap, false)] ||
|
|
48
|
+
palettesSource[configPalette]
|
|
49
|
+
|
|
50
|
+
// Fallback to a default palette if none found
|
|
51
|
+
if (!palette) {
|
|
52
|
+
console.warn(`Palette "${configPalette}" not found, falling back to default`)
|
|
53
|
+
palette = Object.values(allPalettes)[0] || ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
|
|
15
54
|
}
|
|
16
55
|
|
|
17
|
-
|
|
56
|
+
let numberOfKeys = config.runtime.seriesKeys.length
|
|
57
|
+
|
|
58
|
+
// Apply enhanced color distribution (same logic as pie charts)
|
|
59
|
+
const paletteVersion = getColorPaletteVersion(config)
|
|
60
|
+
|
|
61
|
+
// Skip enhanced distribution if not v2, too many keys, or wrong palette length
|
|
62
|
+
if (paletteVersion !== 2 || numberOfKeys > 9 || palette.length !== 9) {
|
|
63
|
+
// Use existing logic for v1 palettes and other cases
|
|
64
|
+
while (numberOfKeys > palette.length) {
|
|
65
|
+
palette = palette.concat(palette)
|
|
66
|
+
}
|
|
67
|
+
palette = palette.slice(0, numberOfKeys)
|
|
68
|
+
} else {
|
|
69
|
+
// Apply enhanced distribution for v2 palettes
|
|
70
|
+
const isSequential = configPalette && configPalette.includes('sequential')
|
|
71
|
+
const isDivergent = configPalette && configPalette.includes('divergent')
|
|
72
|
+
const isColorblindSafe =
|
|
73
|
+
configPalette && (configPalette.includes('colorblindsafe') || configPalette.includes('qualitative_standard'))
|
|
74
|
+
|
|
75
|
+
// Determine which distribution to use based on palette type
|
|
76
|
+
let distributionMap = null
|
|
77
|
+
if (isDivergent) {
|
|
78
|
+
distributionMap = divergentColorDistribution
|
|
79
|
+
} else if (isColorblindSafe) {
|
|
80
|
+
distributionMap = colorblindColorDistribution
|
|
81
|
+
} else if (isSequential) {
|
|
82
|
+
distributionMap = v2ColorDistribution
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (distributionMap && distributionMap[numberOfKeys]) {
|
|
86
|
+
const distributionIndices = distributionMap[numberOfKeys]
|
|
87
|
+
palette = distributionIndices.map((index: number) => palette[index])
|
|
88
|
+
} else {
|
|
89
|
+
palette = palette.slice(0, numberOfKeys)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
18
92
|
|
|
19
93
|
return scaleOrdinal({
|
|
20
94
|
domain: config.runtime.seriesLabelsAll,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { ChartConfig } from '../types/ChartConfig'
|
|
2
2
|
import _ from 'lodash'
|
|
3
|
-
import ConfigContext from '../ConfigContext'
|
|
4
|
-
import { useContext } from 'react'
|
|
5
3
|
|
|
6
|
-
type
|
|
4
|
+
type GetMinMaxProps = {
|
|
7
5
|
/** config - standard chart config */
|
|
8
6
|
config: ChartConfig
|
|
9
7
|
/** minValue - starting minimum value */
|
|
@@ -18,9 +16,20 @@ type UseMinMaxProps = {
|
|
|
18
16
|
tableData: Object[]
|
|
19
17
|
/** isAllLine: if all series are line type including dashed lines */
|
|
20
18
|
isAllLine: boolean
|
|
19
|
+
/** convertLineToBarGraph - whether line charts should be rendered as bar graphs */
|
|
20
|
+
convertLineToBarGraph?: boolean
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const getMinMax = ({
|
|
24
|
+
config,
|
|
25
|
+
minValue,
|
|
26
|
+
maxValue,
|
|
27
|
+
existPositiveValue,
|
|
28
|
+
data,
|
|
29
|
+
isAllLine,
|
|
30
|
+
tableData,
|
|
31
|
+
convertLineToBarGraph
|
|
32
|
+
}: GetMinMaxProps) => {
|
|
24
33
|
let min = 0
|
|
25
34
|
let max = 0
|
|
26
35
|
|
|
@@ -28,8 +37,6 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
28
37
|
let leftMax = 0
|
|
29
38
|
let rightMax = 0
|
|
30
39
|
|
|
31
|
-
const { convertLineToBarGraph } = useContext(ConfigContext)
|
|
32
|
-
|
|
33
40
|
if (!data) {
|
|
34
41
|
return { min, max }
|
|
35
42
|
}
|
|
@@ -238,4 +245,4 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
238
245
|
|
|
239
246
|
return { min, max, leftMax, rightMax }
|
|
240
247
|
}
|
|
241
|
-
export default
|
|
248
|
+
export default getMinMax
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
|
|
3
3
|
export const getNewRuntime = (visualizationConfig, newFilteredData) => {
|
|
4
|
-
const runtime =
|
|
4
|
+
const runtime = visualizationConfig.runtime ? { ...visualizationConfig.runtime } : {}
|
|
5
5
|
runtime.series = []
|
|
6
6
|
runtime.seriesLabels = {}
|
|
7
7
|
runtime.seriesLabelsAll = []
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the Y-axis auto padding to prevent data labels from overlapping with axis tick labels.
|
|
5
|
+
* This is used when inline labels are enabled and there's potential for overlap.
|
|
6
|
+
*
|
|
7
|
+
* @param yScale - The D3 scale object for the Y-axis (must have .ticks() method)
|
|
8
|
+
* @param handleNumTicks - The number of ticks to display on the axis
|
|
9
|
+
* @param maxValue - The maximum data value (from useReduceData)
|
|
10
|
+
* @param minValue - The minimum data value (from useReduceData)
|
|
11
|
+
* @param config - The chart configuration object
|
|
12
|
+
* @returns The calculated auto padding percentage (0-100+), or 0 if no padding needed
|
|
13
|
+
*/
|
|
14
|
+
export const getYAxisAutoPadding = (
|
|
15
|
+
yScale: any,
|
|
16
|
+
handleNumTicks: number,
|
|
17
|
+
maxValue: number,
|
|
18
|
+
minValue: number,
|
|
19
|
+
config: ChartConfig
|
|
20
|
+
): number => {
|
|
21
|
+
// Early returns for cases where auto padding is not needed
|
|
22
|
+
if (!yScale?.ticks || config.orientation === 'horizontal' || config.yAxis?.max) {
|
|
23
|
+
return 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ticks = yScale.ticks(handleNumTicks)
|
|
27
|
+
|
|
28
|
+
if (!Array.isArray(ticks) || ticks.length === 0) {
|
|
29
|
+
return 0
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// minimum percentage of the max value that the distance should be from the top grid line
|
|
33
|
+
const MINIMUM_DISTANCE_PERCENTAGE = 0.025
|
|
34
|
+
|
|
35
|
+
const topGridLine = Math.max(...ticks)
|
|
36
|
+
const needsPaddingThreshold = topGridLine - maxValue * MINIMUM_DISTANCE_PERCENTAGE
|
|
37
|
+
const maxValueIsGreaterThanThreshold = maxValue > needsPaddingThreshold
|
|
38
|
+
|
|
39
|
+
if (!maxValueIsGreaterThanThreshold) return 0
|
|
40
|
+
|
|
41
|
+
const tickGap = ticks.length === 1 ? ticks[0] : ticks[1] - ticks[0]
|
|
42
|
+
const nextTick = Math.max(...ticks) + tickGap
|
|
43
|
+
const divideBy = minValue < 0 ? maxValue / 2 : maxValue
|
|
44
|
+
const calculatedPadding = (nextTick - maxValue) / divideBy
|
|
45
|
+
|
|
46
|
+
// if auto padding is too close to next tick, add one more ticks worth of padding
|
|
47
|
+
const newPadding =
|
|
48
|
+
calculatedPadding > MINIMUM_DISTANCE_PERCENTAGE ? calculatedPadding : calculatedPadding + tickGap / divideBy
|
|
49
|
+
|
|
50
|
+
/* sometimes even though the padding is getting to the next tick exactly,
|
|
51
|
+
d3 still doesn't show the tick. we add 0.1 to ensure to tip it over the edge */
|
|
52
|
+
return newPadding * 100 + 0.1
|
|
53
|
+
}
|