@cdc/chart 4.24.12 → 4.25.2-25
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/dist/cdcchart.js +79900 -78999
- package/examples/feature/boxplot/boxplot.json +2 -157
- package/examples/feature/boxplot/testing.csv +23 -38
- package/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json +579 -49
- package/examples/private/ehdi.json +29939 -0
- package/examples/private/line-issue.json +497 -0
- package/examples/private/not-loading.json +360 -0
- package/index.html +11 -15
- package/package.json +2 -2
- package/src/CdcChart.tsx +92 -1512
- package/src/CdcChartComponent.tsx +1113 -0
- package/src/ConfigContext.tsx +6 -1
- package/src/_stories/Chart.Anchors.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +17 -2
- package/src/_stories/Chart.Filters.stories.tsx +19 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.ScatterPlot.stories.tsx +19 -0
- package/src/_stories/Chart.tooltip.stories.tsx +1 -2
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +1 -1
- package/src/_stories/ChartAxisTitles.stories.tsx +1 -1
- package/src/_stories/ChartEditor.stories.tsx +1 -1
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +18 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +1 -1
- package/src/_stories/_mock/line_chart_symbols.json +437 -0
- package/src/_stories/_mock/scatterplot-image-download.json +1244 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +3 -11
- package/src/components/Annotations/components/AnnotationDropdown.tsx +3 -3
- package/src/components/Axis/Categorical.Axis.tsx +3 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +14 -5
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Vertical.tsx +5 -7
- package/src/components/BarChart/components/BarChart.jsx +24 -4
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BoxPlot/BoxPlot.tsx +34 -32
- package/src/components/BoxPlot/helpers/index.ts +108 -18
- package/src/components/BrushChart.tsx +44 -24
- package/src/components/DeviationBar.jsx +2 -6
- package/src/components/EditorPanel/EditorPanel.tsx +64 -8
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +4 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +3 -1
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +44 -7
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +6 -1
- package/src/components/ForestPlot/ForestPlot.tsx +176 -26
- package/src/components/Legend/Legend.Component.tsx +29 -38
- package/src/components/Legend/Legend.Suppression.tsx +3 -5
- package/src/components/Legend/Legend.tsx +2 -2
- package/src/components/Legend/LegendLine.Shape.tsx +51 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +29 -26
- package/src/components/Legend/helpers/getLegendClasses.ts +20 -38
- package/src/components/Legend/helpers/index.ts +22 -9
- package/src/components/Legend/tests/getLegendClasses.test.ts +3 -20
- package/src/components/LineChart/components/LineChart.Circle.tsx +104 -94
- package/src/components/LineChart/index.tsx +6 -2
- package/src/components/LinearChart.tsx +77 -43
- package/src/components/PairedBarChart.jsx +2 -9
- package/src/components/ZoomBrush.tsx +5 -7
- package/src/data/initial-state.js +6 -3
- package/src/helpers/getBoxPlotConfig.ts +68 -0
- package/src/helpers/getColorScale.ts +24 -0
- package/src/helpers/getComboChartConfig.ts +42 -0
- package/src/helpers/getExcludedData.ts +37 -0
- package/src/helpers/getTopAxis.ts +7 -0
- package/src/helpers/isConvertLineToBarGraph.ts +10 -3
- package/src/hooks/useBarChart.ts +40 -13
- package/src/hooks/{useHighlightedBars.js → useHighlightedBars.ts} +2 -1
- package/src/hooks/useIntersectionObserver.ts +37 -0
- package/src/hooks/useMinMax.ts +11 -8
- package/src/hooks/useReduceData.ts +1 -1
- package/src/hooks/useScales.ts +10 -0
- package/src/hooks/useTooltip.tsx +21 -2
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +0 -5
- package/src/scss/main.scss +31 -116
- package/src/store/chart.actions.ts +40 -0
- package/src/store/chart.reducer.ts +83 -0
- package/src/types/ChartConfig.ts +6 -3
- package/src/types/ChartContext.ts +1 -3
- package/src/helpers/getQuartiles.ts +0 -27
- package/src/hooks/useColorScale.ts +0 -50
- package/src/hooks/useIntersectionObserver.jsx +0 -29
- package/src/hooks/useTopAxis.js +0 -6
|
@@ -1,54 +1,36 @@
|
|
|
1
1
|
import { ChartConfig } from './../../../types/ChartConfig'
|
|
2
2
|
|
|
3
3
|
export const getLegendClasses = (config: ChartConfig) => {
|
|
4
|
-
const { position, singleRow,
|
|
5
|
-
const containerClasses = ['legend-container']
|
|
6
|
-
const innerClasses = ['legend-container__inner']
|
|
7
|
-
|
|
8
|
-
// Handle legend positioning
|
|
9
|
-
switch (position) {
|
|
10
|
-
case 'left':
|
|
11
|
-
containerClasses.push('left')
|
|
12
|
-
break
|
|
13
|
-
case 'right':
|
|
14
|
-
containerClasses.push('right')
|
|
15
|
-
break
|
|
16
|
-
case 'bottom':
|
|
17
|
-
containerClasses.push('bottom')
|
|
18
|
-
innerClasses.push('double-column', 'bottom')
|
|
19
|
-
break
|
|
20
|
-
case 'top':
|
|
21
|
-
containerClasses.push('top')
|
|
22
|
-
innerClasses.push('double-column', 'top')
|
|
23
|
-
break
|
|
24
|
-
}
|
|
4
|
+
const { position, singleRow, verticalSorted, hideBorder } = config.legend
|
|
25
5
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
const containerClassMap = {
|
|
7
|
+
left: 'left',
|
|
8
|
+
right: 'right',
|
|
9
|
+
bottom: 'bottom',
|
|
10
|
+
top: 'top'
|
|
29
11
|
}
|
|
30
12
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
const innerClassMap = {
|
|
14
|
+
bottom: singleRow ? ['single-row', 'bottom'] : ['double-column', 'bottom'],
|
|
15
|
+
top: singleRow ? ['single-row', 'top'] : ['double-column', 'top']
|
|
34
16
|
}
|
|
35
17
|
|
|
36
|
-
|
|
18
|
+
const containerClasses = ['legend-container', containerClassMap[position]].filter(Boolean)
|
|
19
|
+
const innerClasses = ['legend-container__inner', ...(innerClassMap[position] || [])]
|
|
20
|
+
|
|
21
|
+
// Add vertical sorting class for 'bottom' and 'top' positions
|
|
37
22
|
if (['bottom', 'top'].includes(position) && verticalSorted) {
|
|
38
23
|
innerClasses.push('vertical-sorted')
|
|
39
24
|
}
|
|
40
25
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (hideBorder.topBottom && ['top', 'bottom'].includes(position)) {
|
|
47
|
-
containerClasses.push('border-0')
|
|
48
|
-
}
|
|
26
|
+
// Add border and padding classes based on position and hideBorder
|
|
27
|
+
const shouldHideBorder = (['right', 'left'].includes(position) || !position) && hideBorder.side
|
|
28
|
+
const shouldHideTopBottomBorder = ['top', 'bottom'].includes(position) && hideBorder.topBottom
|
|
49
29
|
|
|
50
|
-
if (
|
|
51
|
-
containerClasses.push('p-0')
|
|
30
|
+
if (shouldHideBorder || shouldHideTopBottomBorder) {
|
|
31
|
+
containerClasses.push('border-0', 'p-0')
|
|
32
|
+
} else {
|
|
33
|
+
containerClasses.push('p-3')
|
|
52
34
|
}
|
|
53
35
|
|
|
54
36
|
return {
|
|
@@ -10,22 +10,35 @@ export const getGradientConfig = (config, formatLabels, colorScale) => {
|
|
|
10
10
|
return { colors, labels }
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export const getMarginTop = (
|
|
14
|
-
|
|
13
|
+
export const getMarginTop = (isLegendBottom, config) => {
|
|
14
|
+
// margin between charts xAxis legend not to overlap axis labels,ticks.
|
|
15
|
+
const DEFAULT_MARGIN_TOP = 27
|
|
16
|
+
if (isLegendBottom && config.legend.hide) {
|
|
15
17
|
return '0px'
|
|
16
18
|
}
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
if (!isLegendBottom) {
|
|
20
|
+
return '0px'
|
|
21
|
+
}
|
|
22
|
+
if (isLegendBottom && config.brush.active && !config.legend.hide) {
|
|
23
|
+
const additiolMargin = 25
|
|
24
|
+
return `${DEFAULT_MARGIN_TOP + config.brush.height + additiolMargin}px`
|
|
25
|
+
} else {
|
|
26
|
+
return `${DEFAULT_MARGIN_TOP}px`
|
|
20
27
|
}
|
|
21
|
-
return '20px'
|
|
22
28
|
}
|
|
23
|
-
export const getMarginBottom = (
|
|
29
|
+
export const getMarginBottom = (isLegendBottom, config) => {
|
|
24
30
|
const isLegendTop = config.legend?.position === 'top' && !config.legend.hide
|
|
25
|
-
|
|
31
|
+
const hasSuppression =
|
|
32
|
+
!config.legend.hideSuppressionLink &&
|
|
33
|
+
config.visualizationSubType !== 'stacked' &&
|
|
34
|
+
config.preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value && (pd?.style || pd.symbol))
|
|
26
35
|
let marginBottom = 0
|
|
27
36
|
|
|
28
|
-
if (isLegendTop) marginBottom =
|
|
37
|
+
if (isLegendTop) marginBottom = 27
|
|
38
|
+
|
|
39
|
+
if (isLegendTop && config.dataFormat?.onlyShowTopPrefixSuffix) marginBottom += 9
|
|
40
|
+
|
|
41
|
+
if (isLegendBottom) marginBottom += 9
|
|
29
42
|
|
|
30
43
|
if (hasSuppression) marginBottom += 40
|
|
31
44
|
|
|
@@ -47,41 +47,24 @@ describe('getLegendClasses', () => {
|
|
|
47
47
|
}
|
|
48
48
|
const result = getLegendClasses(config)
|
|
49
49
|
expect(result.containerClasses).toContain('bottom')
|
|
50
|
-
expect(result.innerClasses).toContain('double-column')
|
|
51
50
|
expect(result.innerClasses).toContain('bottom')
|
|
52
51
|
expect(result.innerClasses).toContain('single-row')
|
|
53
52
|
})
|
|
54
53
|
|
|
55
|
-
it('should return correct classes for
|
|
54
|
+
it('should return correct classes for TOP position with double column', () => {
|
|
56
55
|
const config: ChartConfig = {
|
|
57
56
|
legend: {
|
|
58
57
|
position: 'top',
|
|
59
58
|
singleRow: false,
|
|
60
59
|
reverseLabelOrder: false,
|
|
61
|
-
verticalSorted:
|
|
60
|
+
verticalSorted: false,
|
|
62
61
|
hideBorder: { side: false, topBottom: false }
|
|
63
62
|
}
|
|
64
63
|
}
|
|
65
64
|
const result = getLegendClasses(config)
|
|
66
65
|
expect(result.containerClasses).toContain('top')
|
|
67
|
-
expect(result.innerClasses).toContain('double-column')
|
|
68
66
|
expect(result.innerClasses).toContain('top')
|
|
69
|
-
expect(result.innerClasses).toContain('
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('should return correct classes for reverse label order', () => {
|
|
73
|
-
const config: ChartConfig = {
|
|
74
|
-
legend: {
|
|
75
|
-
position: 'bottom',
|
|
76
|
-
singleRow: false,
|
|
77
|
-
reverseLabelOrder: true,
|
|
78
|
-
verticalSorted: false,
|
|
79
|
-
hideBorder: { side: false, topBottom: false }
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const result = getLegendClasses(config)
|
|
83
|
-
expect(result.innerClasses).toContain('d-flex')
|
|
84
|
-
expect(result.innerClasses).toContain('flex-column-reverse')
|
|
67
|
+
expect(result.innerClasses).toContain('double-column')
|
|
85
68
|
})
|
|
86
69
|
|
|
87
70
|
it('should return correct classes for hide border side', () => {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import chroma from 'chroma-js'
|
|
3
3
|
import { type ChartConfig } from '../../../types/ChartConfig'
|
|
4
|
+
import { GlyphDiamond, GlyphCircle, GlyphSquare, GlyphTriangle, GlyphCross, Glyph as CustomGlyph } from '@visx/glyph'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
4
6
|
|
|
5
|
-
// todo: change this config obj to ChartConfig once its created
|
|
6
7
|
type LineChartCircleProps = {
|
|
7
8
|
circleData: object[]
|
|
8
9
|
config: ChartConfig
|
|
@@ -23,9 +24,25 @@ type LineChartCircleProps = {
|
|
|
23
24
|
parseDate: any
|
|
24
25
|
seriesAxis: string
|
|
25
26
|
dataIndex: number
|
|
27
|
+
seriesIndex: number
|
|
26
28
|
mode: 'ISOLATED_POINTS' | 'HOVER_POINTS' | 'ALWAYS_SHOW_POINTS'
|
|
27
29
|
}
|
|
28
|
-
|
|
30
|
+
const Glyphs = [
|
|
31
|
+
GlyphCircle,
|
|
32
|
+
GlyphSquare,
|
|
33
|
+
GlyphTriangle,
|
|
34
|
+
GlyphDiamond,
|
|
35
|
+
GlyphTriangle,
|
|
36
|
+
GlyphCross,
|
|
37
|
+
({ fill }: { fill: string }) => (
|
|
38
|
+
<CustomGlyph>
|
|
39
|
+
{/* Render Filled Pentagon */}
|
|
40
|
+
<Text fill={fill} fontSize={14} textAnchor='middle' verticalAnchor='middle'>
|
|
41
|
+
⬟
|
|
42
|
+
</Text>
|
|
43
|
+
</CustomGlyph>
|
|
44
|
+
)
|
|
45
|
+
]
|
|
29
46
|
const LineChartCircle = (props: LineChartCircleProps) => {
|
|
30
47
|
const {
|
|
31
48
|
config,
|
|
@@ -42,10 +59,18 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
42
59
|
data,
|
|
43
60
|
circleData,
|
|
44
61
|
dataIndex,
|
|
45
|
-
mode
|
|
62
|
+
mode,
|
|
63
|
+
seriesIndex
|
|
46
64
|
} = props
|
|
47
|
-
const { lineDatapointStyle } = config
|
|
48
|
-
const filtered = config?.
|
|
65
|
+
const { lineDatapointStyle, visual } = config
|
|
66
|
+
const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
|
|
67
|
+
const Shape =
|
|
68
|
+
Glyphs[
|
|
69
|
+
config.visual.lineDatapointSymbol === 'standard' && seriesIndex < visual.maximumShapeAmount ? seriesIndex : 0
|
|
70
|
+
]
|
|
71
|
+
const isReversedTriangle = seriesIndex === 4
|
|
72
|
+
const transformShape = (top, left) => `translate(${left}, ${top})${isReversedTriangle ? ' rotate(180)' : ''}`
|
|
73
|
+
|
|
49
74
|
// If we're not showing the circle, simply return
|
|
50
75
|
const getColor = (
|
|
51
76
|
displayArea: boolean,
|
|
@@ -55,14 +80,8 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
55
80
|
seriesKey: string
|
|
56
81
|
) => {
|
|
57
82
|
const seriesLabels = config.runtime.seriesLabels || []
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (displayArea) {
|
|
61
|
-
color = colorScale(seriesLabels[hoveredKey] || seriesKey)
|
|
62
|
-
} else {
|
|
63
|
-
color = 'transparent'
|
|
64
|
-
}
|
|
65
|
-
|
|
83
|
+
const seriesLabelsAll = config.runtime.seriesLabelsAll || []
|
|
84
|
+
let color = displayArea ? colorScale(seriesLabels[hoveredKey] || seriesLabelsAll[seriesIndex]) : ' transparent'
|
|
66
85
|
if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
|
|
67
86
|
color = chroma(color).brighten(1)
|
|
68
87
|
}
|
|
@@ -74,27 +93,30 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
74
93
|
(xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|
|
75
94
|
)
|
|
76
95
|
}
|
|
77
|
-
if (mode === 'ALWAYS_SHOW_POINTS') {
|
|
78
|
-
if (lineDatapointStyle === 'hidden') return <></>
|
|
79
|
-
const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
|
|
80
|
-
|
|
96
|
+
if (mode === 'ALWAYS_SHOW_POINTS' && lineDatapointStyle !== 'hidden') {
|
|
81
97
|
if (lineDatapointStyle === 'always show') {
|
|
82
98
|
const isMatch = circleData?.some(
|
|
83
99
|
cd => cd[config.xAxis.dataKey] === d[config.xAxis.dataKey] && cd[seriesKey] === d[seriesKey]
|
|
84
100
|
)
|
|
85
|
-
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
isMatch ||
|
|
104
|
+
!filtered ||
|
|
105
|
+
(visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
|
|
106
|
+
)
|
|
86
107
|
return <></>
|
|
87
|
-
|
|
108
|
+
const positionLeft = getXPos(d[config.xAxis.dataKey])
|
|
109
|
+
const positionTop = filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])
|
|
110
|
+
|
|
88
111
|
return (
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
/>
|
|
112
|
+
<g transform={transformShape(positionTop, positionLeft)}>
|
|
113
|
+
<Shape
|
|
114
|
+
opacity={d[seriesKey] ? 1 : 0}
|
|
115
|
+
fillOpacity={1}
|
|
116
|
+
fill={getColor(displayArea, colorScale, config, seriesKey, seriesKey)}
|
|
117
|
+
style={{ filter: 'unset', opacity: 1 }}
|
|
118
|
+
/>
|
|
119
|
+
</g>
|
|
98
120
|
)
|
|
99
121
|
}
|
|
100
122
|
}
|
|
@@ -108,96 +130,84 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
108
130
|
if (!hoveredXValue) return
|
|
109
131
|
|
|
110
132
|
let hoveredSeriesValue
|
|
111
|
-
let hoveredSeriesIndex
|
|
112
133
|
let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
|
|
113
134
|
let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
|
|
114
135
|
let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
|
|
136
|
+
const dynamicSeriesConfig = config.runtime.series.find(s => s.dynamicCategory)
|
|
137
|
+
const originalDataKey = dynamicSeriesConfig?.originalDataKey ?? seriesKey
|
|
138
|
+
|
|
115
139
|
if (!hoveredSeriesKey) return
|
|
116
|
-
hoveredSeriesIndex = tooltipData?.data.indexOf(hoveredSeriesKey)
|
|
117
140
|
hoveredSeriesValue = tableData?.find(d => {
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
const dynamicCategory = dynamicSeriesConfig?.dynamicCategory
|
|
142
|
+
const matchingXValue = d[config.xAxis.dataKey] === hoveredXValue
|
|
143
|
+
if (!matchingXValue) return false
|
|
144
|
+
if (dynamicCategory) {
|
|
145
|
+
const match = d[dynamicCategory] === hoveredSeriesKey
|
|
146
|
+
return match
|
|
147
|
+
}
|
|
148
|
+
return true
|
|
149
|
+
})?.[originalDataKey]
|
|
120
150
|
|
|
121
151
|
// hoveredSeriesValue = extractNumber(hoveredSeriesValue)
|
|
122
152
|
return tooltipData?.data.map((tooltipItem, index) => {
|
|
123
|
-
let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
|
|
124
|
-
|
|
125
153
|
if (isNaN(hoveredSeriesValue)) return <></>
|
|
126
154
|
const isMatch = circleData?.some(cd => cd[config.xAxis.dataKey] === hoveredXValue)
|
|
127
155
|
|
|
128
|
-
if (
|
|
156
|
+
if (
|
|
157
|
+
isMatch ||
|
|
158
|
+
!hoveredSeriesValue ||
|
|
159
|
+
(visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
|
|
160
|
+
) {
|
|
129
161
|
return <></>
|
|
130
162
|
}
|
|
131
163
|
|
|
164
|
+
const positionTop = hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)
|
|
165
|
+
const positionLeft = getXPos(hoveredXValue)
|
|
132
166
|
return (
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
/>
|
|
167
|
+
<g transform={transformShape(positionTop, positionLeft)}>
|
|
168
|
+
<Shape
|
|
169
|
+
size={55}
|
|
170
|
+
opacity={1}
|
|
171
|
+
fillOpacity={1}
|
|
172
|
+
fill={getColor(displayArea, colorScale, config, hoveredSeriesKey, seriesKey)}
|
|
173
|
+
style={{ filter: 'unset', opacity: 1 }}
|
|
174
|
+
/>
|
|
175
|
+
</g>
|
|
143
176
|
)
|
|
144
177
|
})
|
|
145
178
|
}
|
|
146
179
|
}
|
|
147
|
-
|
|
148
180
|
if (mode === 'ISOLATED_POINTS') {
|
|
149
181
|
const drawIsolatedPoints = (currentIndex, seriesKey) => {
|
|
150
|
-
let isMatch = false
|
|
151
182
|
const currentPoint = data[currentIndex]
|
|
152
|
-
const previousPoint =
|
|
153
|
-
const nextPoint =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
res = true
|
|
169
|
-
}
|
|
170
|
-
// Handle points in the middle
|
|
171
|
-
if (currentIndex > 0 && currentIndex < data.length - 1) {
|
|
172
|
-
if (
|
|
173
|
-
currentPoint &&
|
|
174
|
-
currentPoint[seriesKey] &&
|
|
175
|
-
(!previousPoint || !previousPoint[seriesKey]) &&
|
|
176
|
-
(!nextPoint || !nextPoint[seriesKey])
|
|
177
|
-
) {
|
|
178
|
-
res = true
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (isMatch) {
|
|
182
|
-
res = false
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return res
|
|
183
|
+
const previousPoint = data[currentIndex - 1] || {}
|
|
184
|
+
const nextPoint = data[currentIndex + 1] || {}
|
|
185
|
+
|
|
186
|
+
const isMatch = circleData.some(item => item?.data[seriesKey] === currentPoint[seriesKey])
|
|
187
|
+
if (isMatch) return false
|
|
188
|
+
|
|
189
|
+
const isFirstPoint = currentIndex === 0 && !nextPoint[seriesKey]
|
|
190
|
+
const isLastPoint = currentIndex === data.length - 1 && !previousPoint[seriesKey]
|
|
191
|
+
const isMiddlePoint =
|
|
192
|
+
currentIndex > 0 &&
|
|
193
|
+
currentIndex < data.length - 1 &&
|
|
194
|
+
currentPoint[seriesKey] &&
|
|
195
|
+
!previousPoint[seriesKey] &&
|
|
196
|
+
!nextPoint[seriesKey]
|
|
197
|
+
|
|
198
|
+
return isFirstPoint || isLastPoint || isMiddlePoint
|
|
186
199
|
}
|
|
187
200
|
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/>
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
+
if (drawIsolatedPoints(dataIndex, seriesKey)) {
|
|
202
|
+
const positionTop = filtered?.axis === 'Right' ? yScaleRight(d[filtered?.dataKey]) : yScale(d[filtered?.dataKey])
|
|
203
|
+
const positionLeft = getXPos(d[config.xAxis?.dataKey])
|
|
204
|
+
const color = colorScale(config.runtime.seriesLabelsAll[seriesIndex])
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<g transform={transformShape(positionTop, positionLeft)}>
|
|
208
|
+
<Shape size={124} stroke={color} fill={color} />
|
|
209
|
+
</g>
|
|
210
|
+
)
|
|
201
211
|
}
|
|
202
212
|
}
|
|
203
213
|
|
|
@@ -89,14 +89,14 @@ const LineChart = (props: LineChartProps) => {
|
|
|
89
89
|
opacity={
|
|
90
90
|
legend.behavior === 'highlight' &&
|
|
91
91
|
seriesHighlight.length > 0 &&
|
|
92
|
-
seriesHighlight.indexOf(
|
|
92
|
+
seriesHighlight.indexOf(seriesKey) === -1
|
|
93
93
|
? 0.5
|
|
94
94
|
: 1
|
|
95
95
|
}
|
|
96
96
|
display={
|
|
97
97
|
legend.behavior === 'highlight' ||
|
|
98
98
|
(seriesHighlight.length === 0 && !legend.dynamicLegend) ||
|
|
99
|
-
seriesHighlight.indexOf(
|
|
99
|
+
seriesHighlight.indexOf(seriesKey) !== -1
|
|
100
100
|
? 'block'
|
|
101
101
|
: 'none'
|
|
102
102
|
}
|
|
@@ -150,12 +150,14 @@ const LineChart = (props: LineChartProps) => {
|
|
|
150
150
|
parseDate={parseDate}
|
|
151
151
|
yScaleRight={yScaleRight}
|
|
152
152
|
seriesAxis={seriesAxis}
|
|
153
|
+
seriesIndex={index}
|
|
153
154
|
key={`line-circle--${dataIndex}`}
|
|
154
155
|
/>
|
|
155
156
|
)}
|
|
156
157
|
|
|
157
158
|
<LineChartCircle
|
|
158
159
|
mode='ISOLATED_POINTS'
|
|
160
|
+
seriesIndex={index}
|
|
159
161
|
dataIndex={dataIndex}
|
|
160
162
|
tableData={tableData}
|
|
161
163
|
circleData={circleData}
|
|
@@ -180,6 +182,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
180
182
|
<>
|
|
181
183
|
{lineDatapointStyle === 'hover' && (
|
|
182
184
|
<LineChartCircle
|
|
185
|
+
seriesIndex={index}
|
|
183
186
|
tableData={tableData}
|
|
184
187
|
dataIndex={0}
|
|
185
188
|
mode='HOVER_POINTS'
|
|
@@ -198,6 +201,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
198
201
|
/>
|
|
199
202
|
)}
|
|
200
203
|
</>
|
|
204
|
+
|
|
201
205
|
{/* SPLIT LINE */}
|
|
202
206
|
{isSplitLine ? (
|
|
203
207
|
<>
|