@cdc/chart 4.25.3-6 → 4.25.5-1
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-1a1724a1.es.js +4886 -0
- package/dist/cdcchart.js +50347 -75521
- package/index.html +1 -0
- package/package.json +22 -27
- package/src/CdcChart.tsx +1 -22
- package/src/CdcChartComponent.tsx +35 -21
- package/src/_stories/Chart.CI.stories.tsx +43 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +7 -16
- package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
- package/src/_stories/_mock/barchart_labels.mock.json +612 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/DeviationBar.jsx +2 -1
- package/src/components/EditorPanel/EditorPanel.tsx +60 -24
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/Legend/Legend.Component.tsx +69 -58
- package/src/components/Legend/Legend.Suppression.tsx +12 -22
- package/src/components/Legend/Legend.tsx +3 -1
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
- package/src/components/Legend/LegendGroup/index.tsx +3 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +125 -60
- package/src/components/LinearChart.tsx +74 -115
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +71 -91
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/components/ZoomBrush.tsx +4 -4
- package/src/data/initial-state.js +4 -2
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +31 -0
- package/src/helpers/sizeHelpers.ts +23 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +4 -2
- package/src/hooks/useScales.ts +12 -14
- package/src/hooks/useTooltip.tsx +204 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +1 -1
- package/src/types/ChartConfig.ts +7 -1
- package/LICENSE +0 -201
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/not-loading.json +0 -360
- package/examples/private/test.json +0 -493
|
@@ -17,7 +17,8 @@ const Legend = forwardRef((props, ref) => {
|
|
|
17
17
|
transformedData: data,
|
|
18
18
|
currentViewport,
|
|
19
19
|
dimensions,
|
|
20
|
-
getTextWidth
|
|
20
|
+
getTextWidth,
|
|
21
|
+
transformedData
|
|
21
22
|
} = useContext(ConfigContext)
|
|
22
23
|
if (!config.legend) return null
|
|
23
24
|
// create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
|
|
@@ -30,6 +31,7 @@ const Legend = forwardRef((props, ref) => {
|
|
|
30
31
|
getTextWidth={getTextWidth}
|
|
31
32
|
dimensions={dimensions}
|
|
32
33
|
ref={ref}
|
|
34
|
+
transformedData={transformedData}
|
|
33
35
|
skipId={props.skipId || 'legend'}
|
|
34
36
|
config={config}
|
|
35
37
|
colorScale={colorScale}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.legend-container {
|
|
2
|
+
.legend-group {
|
|
3
|
+
font-size: 1rem;
|
|
4
|
+
|
|
5
|
+
line-height: 18px;
|
|
6
|
+
margin-bottom: 0.5rem;
|
|
7
|
+
|
|
8
|
+
.group-item .visx-legend-label {
|
|
9
|
+
font-weight: 400;
|
|
10
|
+
font-size: 0.889rem;
|
|
11
|
+
margin-bottom: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
.group-label {
|
|
14
|
+
font-weight: 500;
|
|
15
|
+
font-family: Nunito, sans-serif;
|
|
16
|
+
font-size: 1rem;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.legend-group.top,
|
|
21
|
+
.legend-group.bottom {
|
|
22
|
+
grid-gap: 10px;
|
|
23
|
+
|
|
24
|
+
&.group-item {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
align-items: flex-start;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
& .inactive {
|
|
31
|
+
opacity: 0.5;
|
|
32
|
+
transition: 0.2s all;
|
|
33
|
+
}
|
|
34
|
+
& .highlighted {
|
|
35
|
+
outline: 1px solid #005ea2;
|
|
36
|
+
outline-offset: 5px;
|
|
37
|
+
border-radius: 1px;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
|
|
3
|
+
import LegendShape from '@cdc/core/components/LegendShape'
|
|
4
|
+
import ConfigContext from '../../../ConfigContext'
|
|
5
|
+
import _ from 'lodash'
|
|
6
|
+
import './LegendGroup.styles.css'
|
|
7
|
+
|
|
8
|
+
interface LegendGroup {
|
|
9
|
+
formatLabels: Function
|
|
10
|
+
}
|
|
11
|
+
const LegendGroup = ({ formatLabels }) => {
|
|
12
|
+
const {
|
|
13
|
+
highlight,
|
|
14
|
+
seriesHighlight,
|
|
15
|
+
colorScale,
|
|
16
|
+
transformedData: data,
|
|
17
|
+
config,
|
|
18
|
+
currentViewport
|
|
19
|
+
} = useContext(ConfigContext)
|
|
20
|
+
|
|
21
|
+
const getSubGroups = (data, key: string | undefined) => {
|
|
22
|
+
const uniqueGroups = new Set()
|
|
23
|
+
data.forEach(d => {
|
|
24
|
+
config.series.forEach(series => {
|
|
25
|
+
if (d[key] && d[series.dataKey]) {
|
|
26
|
+
uniqueGroups.add(d[key])
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
return Array.from(uniqueGroups) as string[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const groups: string[] = getSubGroups(data, config.legend.groupBy)
|
|
34
|
+
|
|
35
|
+
const classNames = ['legend-group', 'container', config.legend.position, currentViewport, 'row']
|
|
36
|
+
|
|
37
|
+
const gridCol =
|
|
38
|
+
currentViewport === 'xs'
|
|
39
|
+
? 'col-12'
|
|
40
|
+
: currentViewport === 'sm'
|
|
41
|
+
? 'col-6'
|
|
42
|
+
: currentViewport === 'md'
|
|
43
|
+
? 'col-4'
|
|
44
|
+
: 'col-3'
|
|
45
|
+
|
|
46
|
+
const isSigleCol = config.legend.position === 'bottom' || config.legend.position === 'top' ? gridCol : 'col-12'
|
|
47
|
+
|
|
48
|
+
let classNameItem = ['legend-group group-item', isSigleCol]
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div className={classNames.join(' ')}>
|
|
52
|
+
{groups.map(group => {
|
|
53
|
+
return (
|
|
54
|
+
<div className={classNameItem.join(' ')} key={group}>
|
|
55
|
+
<div>
|
|
56
|
+
<p className='legend-group group-label'>{group}</p>
|
|
57
|
+
</div>
|
|
58
|
+
<LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
|
|
59
|
+
{labels =>
|
|
60
|
+
formatLabels(labels)
|
|
61
|
+
.filter(label => {
|
|
62
|
+
const groupBy = config.legend.groupBy || ''
|
|
63
|
+
return data.some(d => d[groupBy] === group && d[label.text] !== undefined && d[label.text] !== 'NA')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
.map((label, i) => {
|
|
67
|
+
let className = ['legend-group', 'group-item']
|
|
68
|
+
if (seriesHighlight.length) {
|
|
69
|
+
if (!seriesHighlight.includes(label.datum)) {
|
|
70
|
+
className.push('inactive')
|
|
71
|
+
} else {
|
|
72
|
+
className.push('highlighted')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<LegendItem
|
|
78
|
+
alignItems='start'
|
|
79
|
+
className={className.join(' ')}
|
|
80
|
+
onClick={e => {
|
|
81
|
+
e.preventDefault()
|
|
82
|
+
highlight(label)
|
|
83
|
+
}}
|
|
84
|
+
key={`legend-item-${i}`}
|
|
85
|
+
tabIndex={0}
|
|
86
|
+
>
|
|
87
|
+
<LegendShape shape={config.legend.style === 'boxes' ? 'square' : 'circle'} fill={label.value} />
|
|
88
|
+
<LegendLabel align='left' margin='0'>
|
|
89
|
+
{label.text}
|
|
90
|
+
</LegendLabel>
|
|
91
|
+
</LegendItem>
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
</LegendOrdinal>
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
})}
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default LegendGroup
|
|
@@ -17,8 +17,36 @@ export const createFormatLabels =
|
|
|
17
17
|
})
|
|
18
18
|
: labels
|
|
19
19
|
const reverseLabels = labels => {
|
|
20
|
+
if (config.series.some(series => series.dynamicCategory)) {
|
|
21
|
+
return orderDynamicLabels(labels)
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
|
|
21
25
|
}
|
|
26
|
+
|
|
27
|
+
const orderDynamicLabels = labels => {
|
|
28
|
+
// Handle different ordering configurations
|
|
29
|
+
switch (config.legend.order) {
|
|
30
|
+
case 'dataColumn':
|
|
31
|
+
return labels
|
|
32
|
+
case 'asc':
|
|
33
|
+
case 'desc':
|
|
34
|
+
return labels.sort((a, b) => {
|
|
35
|
+
const valA = a.datum || a.text
|
|
36
|
+
const valB = b.datum || b.text
|
|
37
|
+
const numA = parseFloat(valA)
|
|
38
|
+
const numB = parseFloat(valB)
|
|
39
|
+
if (!isNaN(numA) && !isNaN(numB)) {
|
|
40
|
+
return config.legend.order === 'asc' ? numA - numB : numB - numA
|
|
41
|
+
} else {
|
|
42
|
+
return config.legend.order === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
default:
|
|
47
|
+
return labels // Default case to handle any unexpected config.legend.order values
|
|
48
|
+
}
|
|
49
|
+
}
|
|
22
50
|
const colorCode = config.legend?.colorCode
|
|
23
51
|
if (visualizationType === 'Deviation Bar') {
|
|
24
52
|
const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
|
|
@@ -29,9 +29,11 @@ export interface StyleProps {
|
|
|
29
29
|
handleLineType: Function
|
|
30
30
|
lineType: string
|
|
31
31
|
preliminaryData: PreliminaryDataItem[]
|
|
32
|
-
seriesKey:
|
|
32
|
+
seriesKey: string
|
|
33
33
|
stroke: string
|
|
34
34
|
strokeWidth: number
|
|
35
|
+
dynamicCategory: string
|
|
36
|
+
originalSeriesKey: string
|
|
35
37
|
}
|
|
36
38
|
export interface Style {
|
|
37
39
|
stroke: string
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import chroma from 'chroma-js'
|
|
3
|
-
import { type ChartConfig } from '../../../types/ChartConfig'
|
|
2
|
+
import { LineChartConfig, type ChartConfig } from '../../../types/ChartConfig'
|
|
4
3
|
import { GlyphDiamond, GlyphCircle, GlyphSquare, GlyphTriangle, GlyphCross, Glyph as CustomGlyph } from '@visx/glyph'
|
|
5
4
|
import { Text } from '@visx/text'
|
|
6
5
|
|
|
7
6
|
type LineChartCircleProps = {
|
|
8
|
-
circleData: object[]
|
|
9
7
|
config: ChartConfig
|
|
10
8
|
data: object[]
|
|
11
9
|
tableData: object[]
|
|
12
10
|
d?: Object
|
|
13
11
|
displayArea: boolean
|
|
14
12
|
seriesKey: string
|
|
15
|
-
tooltipData: {
|
|
16
|
-
data: []
|
|
17
|
-
tooltipDataX: number
|
|
18
|
-
tooltipDataY: number
|
|
19
|
-
}
|
|
20
13
|
xScale: any
|
|
21
14
|
yScale: any
|
|
22
15
|
yScaleRight: any
|
|
@@ -25,7 +18,14 @@ type LineChartCircleProps = {
|
|
|
25
18
|
seriesAxis: string
|
|
26
19
|
dataIndex: number
|
|
27
20
|
seriesIndex: number
|
|
28
|
-
mode: 'ISOLATED_POINTS' | 'HOVER_POINTS' | 'ALWAYS_SHOW_POINTS'
|
|
21
|
+
mode: 'ISOLATED_POINTS' | 'HOVER_POINTS' | 'ALWAYS_SHOW_POINTS' | 'TOOLTIP_POINTS'
|
|
22
|
+
tooltipPoint: {
|
|
23
|
+
xValue: string | number
|
|
24
|
+
yValue: string | number
|
|
25
|
+
color: string
|
|
26
|
+
}
|
|
27
|
+
handleTooltipMouseOver?: Function
|
|
28
|
+
handleTooltipMouseOff?: Function
|
|
29
29
|
}
|
|
30
30
|
const Glyphs = [
|
|
31
31
|
GlyphCircle,
|
|
@@ -46,146 +46,80 @@ const Glyphs = [
|
|
|
46
46
|
const LineChartCircle = (props: LineChartCircleProps) => {
|
|
47
47
|
const {
|
|
48
48
|
config,
|
|
49
|
-
d,
|
|
50
|
-
tableData,
|
|
49
|
+
d: pointData,
|
|
51
50
|
displayArea,
|
|
52
51
|
seriesKey,
|
|
53
|
-
tooltipData,
|
|
54
52
|
xScale,
|
|
55
53
|
yScale,
|
|
56
54
|
colorScale,
|
|
57
55
|
parseDate,
|
|
58
56
|
yScaleRight,
|
|
59
57
|
data,
|
|
60
|
-
|
|
58
|
+
tooltipPoint,
|
|
61
59
|
dataIndex,
|
|
62
60
|
mode,
|
|
63
|
-
seriesIndex
|
|
61
|
+
seriesIndex,
|
|
62
|
+
handleTooltipMouseOver,
|
|
63
|
+
handleTooltipMouseOff
|
|
64
64
|
} = props
|
|
65
|
-
const { lineDatapointStyle, visual } = config
|
|
65
|
+
const { isolatedDotsSameSize, lineDatapointStyle, visual } = config as LineChartConfig
|
|
66
66
|
const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
|
|
67
67
|
const Shape =
|
|
68
68
|
Glyphs[
|
|
69
69
|
config.visual.lineDatapointSymbol === 'standard' && seriesIndex < visual.maximumShapeAmount ? seriesIndex : 0
|
|
70
70
|
]
|
|
71
71
|
const isReversedTriangle = seriesIndex === 4
|
|
72
|
-
const
|
|
72
|
+
const LARGE_DOT_SIZE = 124
|
|
73
|
+
const REGULAR_DOT_SIZE = 55
|
|
74
|
+
const dotSize = isolatedDotsSameSize ? REGULAR_DOT_SIZE : LARGE_DOT_SIZE
|
|
73
75
|
|
|
74
|
-
// If we're not showing the circle, simply return
|
|
75
|
-
const getColor = (
|
|
76
|
-
displayArea: boolean,
|
|
77
|
-
colorScale: Function,
|
|
78
|
-
config: ChartConfig,
|
|
79
|
-
hoveredKey: string,
|
|
80
|
-
seriesKey: string
|
|
81
|
-
) => {
|
|
82
|
-
const seriesLabels = config.runtime.seriesLabels || []
|
|
83
|
-
const seriesLabelsAll = config.runtime.seriesLabelsAll || []
|
|
84
|
-
let color = displayArea ? colorScale(seriesLabels[hoveredKey] || seriesLabelsAll[seriesIndex]) : ' transparent'
|
|
85
|
-
if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
|
|
86
|
-
color = chroma(color).brighten(1)
|
|
87
|
-
}
|
|
88
|
-
return color
|
|
89
|
-
}
|
|
90
76
|
const getXPos = hoveredXValue => {
|
|
91
77
|
return (
|
|
92
78
|
(config.xAxis.type === 'categorical' ? xScale(hoveredXValue) : xScale(parseDate(hoveredXValue))) +
|
|
93
79
|
(xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|
|
94
80
|
)
|
|
95
81
|
}
|
|
96
|
-
if (mode === 'ALWAYS_SHOW_POINTS' && lineDatapointStyle !== 'hidden') {
|
|
97
|
-
if (lineDatapointStyle === 'always show') {
|
|
98
|
-
const isMatch = circleData?.some(
|
|
99
|
-
cd => cd[config.xAxis.dataKey] === d[config.xAxis.dataKey] && cd[seriesKey] === d[seriesKey]
|
|
100
|
-
)
|
|
101
82
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
(visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
|
|
106
|
-
)
|
|
107
|
-
return <></>
|
|
108
|
-
const positionLeft = getXPos(d[config.xAxis.dataKey])
|
|
109
|
-
const positionTop = filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])
|
|
83
|
+
const transformShape = (xValue, yValue) => {
|
|
84
|
+
const positionLeft = getXPos(xValue)
|
|
85
|
+
const positionTop = filtered?.axis === 'Right' ? yScaleRight(yValue) : yScale(yValue)
|
|
110
86
|
|
|
111
|
-
|
|
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>
|
|
120
|
-
)
|
|
121
|
-
}
|
|
87
|
+
return `translate(${positionLeft}, ${positionTop})${isReversedTriangle ? ' rotate(180)' : ''}`
|
|
122
88
|
}
|
|
123
89
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
let hoveredSeriesValue
|
|
133
|
-
let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
|
|
134
|
-
let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
|
|
135
|
-
let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
|
|
136
|
-
const dynamicSeriesConfig = config.runtime.series.find(s => s.dynamicCategory)
|
|
137
|
-
const originalDataKey = dynamicSeriesConfig?.originalDataKey ?? seriesKey
|
|
138
|
-
|
|
139
|
-
if (!hoveredSeriesKey) return
|
|
140
|
-
hoveredSeriesValue = tableData?.find(d => {
|
|
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]
|
|
150
|
-
|
|
151
|
-
// hoveredSeriesValue = extractNumber(hoveredSeriesValue)
|
|
152
|
-
return tooltipData?.data.map((tooltipItem, index) => {
|
|
153
|
-
if (isNaN(hoveredSeriesValue)) return <></>
|
|
154
|
-
const isMatch = circleData?.some(cd => cd[config.xAxis.dataKey] === hoveredXValue)
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
isMatch ||
|
|
158
|
-
!hoveredSeriesValue ||
|
|
159
|
-
(visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
|
|
160
|
-
) {
|
|
161
|
-
return <></>
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const positionTop = hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)
|
|
165
|
-
const positionLeft = getXPos(hoveredXValue)
|
|
166
|
-
return (
|
|
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>
|
|
176
|
-
)
|
|
177
|
-
})
|
|
90
|
+
// If we're not showing the circle, simply return
|
|
91
|
+
const getColor = (displayArea: boolean, colorScale: Function, config: ChartConfig, hoveredKey: string) => {
|
|
92
|
+
const seriesLabels = config.runtime.seriesLabels || []
|
|
93
|
+
const seriesLabelsAll = config.runtime.seriesLabelsAll || []
|
|
94
|
+
let color = displayArea ? colorScale(seriesLabels[hoveredKey] || seriesLabelsAll[seriesIndex]) : 'transparent'
|
|
95
|
+
if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
|
|
96
|
+
color = chroma(color).brighten(1)
|
|
178
97
|
}
|
|
98
|
+
return color
|
|
179
99
|
}
|
|
100
|
+
|
|
101
|
+
if (['ALWAYS_SHOW_POINTS', 'HOVER_POINTS'].includes(mode)) {
|
|
102
|
+
if (!filtered || (visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard'))
|
|
103
|
+
return <></>
|
|
104
|
+
return (
|
|
105
|
+
<g
|
|
106
|
+
transform={transformShape(pointData[config.xAxis.dataKey], pointData[filtered?.dataKey])}
|
|
107
|
+
className={`visx-glyph-group${displayArea ? '' : '-hidden'}`}
|
|
108
|
+
data-seriesIndex={seriesIndex}
|
|
109
|
+
>
|
|
110
|
+
<Shape
|
|
111
|
+
fillOpacity={mode === 'ALWAYS_SHOW_POINTS' ? 1 : 0}
|
|
112
|
+
fill={getColor(displayArea, colorScale, config, seriesKey)}
|
|
113
|
+
/>
|
|
114
|
+
</g>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
180
118
|
if (mode === 'ISOLATED_POINTS') {
|
|
181
119
|
const drawIsolatedPoints = (currentIndex, seriesKey) => {
|
|
182
120
|
const currentPoint = data[currentIndex]
|
|
183
121
|
const previousPoint = data[currentIndex - 1] || {}
|
|
184
122
|
const nextPoint = data[currentIndex + 1] || {}
|
|
185
|
-
|
|
186
|
-
const isMatch = circleData.some(item => item?.data[seriesKey] === currentPoint[seriesKey])
|
|
187
|
-
if (isMatch) return false
|
|
188
|
-
|
|
189
123
|
const isFirstPoint = currentIndex === 0 && !nextPoint[seriesKey]
|
|
190
124
|
const isLastPoint = currentIndex === data.length - 1 && !previousPoint[seriesKey]
|
|
191
125
|
const isMiddlePoint =
|
|
@@ -197,20 +131,44 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
197
131
|
|
|
198
132
|
return isFirstPoint || isLastPoint || isMiddlePoint
|
|
199
133
|
}
|
|
134
|
+
const _dataIndex = pointData
|
|
135
|
+
? data.findIndex(item => item[config.xAxis.dataKey] === pointData[config.xAxis.dataKey])
|
|
136
|
+
: dataIndex
|
|
200
137
|
|
|
201
|
-
if (drawIsolatedPoints(
|
|
202
|
-
const positionTop = filtered?.axis === 'Right' ? yScaleRight(d[filtered?.dataKey]) : yScale(d[filtered?.dataKey])
|
|
203
|
-
const positionLeft = getXPos(d[config.xAxis?.dataKey])
|
|
138
|
+
if (drawIsolatedPoints(_dataIndex, seriesKey)) {
|
|
204
139
|
const color = colorScale(config.runtime.seriesLabelsAll[seriesIndex])
|
|
205
140
|
|
|
206
141
|
return (
|
|
207
|
-
<g
|
|
208
|
-
|
|
142
|
+
<g
|
|
143
|
+
transform={transformShape(pointData[config.xAxis?.dataKey], pointData[filtered?.dataKey])}
|
|
144
|
+
className={`visx-glyph-group${displayArea ? '' : '-hidden'}`}
|
|
145
|
+
data-seriesIndex={seriesIndex}
|
|
146
|
+
>
|
|
147
|
+
<Shape size={dotSize} stroke={color} fill={color} />
|
|
209
148
|
</g>
|
|
210
149
|
)
|
|
211
150
|
}
|
|
212
151
|
}
|
|
213
152
|
|
|
153
|
+
if (mode === 'TOOLTIP_POINTS' && displayArea === true) {
|
|
154
|
+
return (
|
|
155
|
+
<g
|
|
156
|
+
transform={transformShape(tooltipPoint.xValue, tooltipPoint.yValue)}
|
|
157
|
+
className='visx-glyph-circle'
|
|
158
|
+
onMouseOver={e => {
|
|
159
|
+
handleTooltipMouseOver(e)
|
|
160
|
+
if (lineDatapointStyle == 'hover') (e.target as HTMLElement).style.fillOpacity = '1'
|
|
161
|
+
}}
|
|
162
|
+
onMouseOut={e => {
|
|
163
|
+
handleTooltipMouseOff()
|
|
164
|
+
if (lineDatapointStyle == 'hover') (e.target as HTMLElement).style.fillOpacity = '0'
|
|
165
|
+
}}
|
|
166
|
+
>
|
|
167
|
+
<Shape size={55} fill={tooltipPoint.color} fillOpacity={'0'} />
|
|
168
|
+
</g>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
214
172
|
return null
|
|
215
173
|
}
|
|
216
174
|
|