@cdc/chart 4.24.7 → 4.24.9
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 +40313 -37543
- package/examples/cases-year.json +13379 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
- package/index.html +17 -8
- package/package.json +2 -2
- package/src/CdcChart.tsx +383 -133
- package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
- package/src/_stories/_mock/legend.gradient_mock.json +236 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +64 -11
- package/src/components/Axis/Categorical.Axis.tsx +145 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -3
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +2 -5
- package/src/components/BarChart/components/BarChart.Vertical.tsx +17 -8
- package/src/components/BarChart/helpers/index.ts +5 -16
- package/src/components/BrushChart.tsx +205 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1766 -509
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +19 -5
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +190 -37
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -7
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -11
- package/src/components/EditorPanel/editor-panel.scss +16 -3
- package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +90 -19
- package/src/components/Legend/Legend.Component.tsx +185 -193
- package/src/components/Legend/Legend.Suppression.tsx +146 -0
- package/src/components/Legend/Legend.tsx +21 -5
- package/src/components/Legend/helpers/index.ts +33 -3
- package/src/components/LegendWrapper.tsx +26 -0
- package/src/components/LineChart/LineChartProps.ts +1 -18
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
- package/src/components/LineChart/helpers.ts +55 -11
- package/src/components/LineChart/index.tsx +113 -38
- package/src/components/LinearChart.tsx +1366 -0
- package/src/components/PieChart/PieChart.tsx +74 -17
- package/src/components/Sankey/index.tsx +22 -16
- package/src/components/Sparkline/components/SparkLine.tsx +2 -2
- package/src/data/initial-state.js +13 -3
- package/src/hooks/useLegendClasses.ts +52 -15
- package/src/hooks/useMinMax.ts +4 -4
- package/src/hooks/useScales.ts +34 -24
- package/src/hooks/useTooltip.tsx +85 -22
- package/src/scss/DataTable.scss +2 -1
- package/src/scss/main.scss +107 -14
- package/src/types/ChartConfig.ts +34 -8
- package/src/types/ChartContext.ts +5 -4
- package/examples/feature/line/line-chart.json +0 -449
- package/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
|
@@ -78,9 +78,14 @@ export const BarChartVertical = () => {
|
|
|
78
78
|
>
|
|
79
79
|
{barGroups => {
|
|
80
80
|
return barGroups.map((barGroup, index) => (
|
|
81
|
-
<Group
|
|
81
|
+
<Group
|
|
82
|
+
className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`}
|
|
83
|
+
key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
|
|
84
|
+
id={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
|
|
85
|
+
left={barGroup.x0}
|
|
86
|
+
>
|
|
82
87
|
{barGroup.bars.map((bar, index) => {
|
|
83
|
-
const scaleVal = config.
|
|
88
|
+
const scaleVal = config.yAxis.type === 'logarithmic' ? 0.1 : 0
|
|
84
89
|
let highlightedBarValues = config.highlightedBarValues.map(item => item.value).filter(item => item !== ('' || undefined))
|
|
85
90
|
highlightedBarValues = config.xAxis.type === 'date' ? HighLightedBarUtils.formatDates(highlightedBarValues) : highlightedBarValues
|
|
86
91
|
const transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
|
|
@@ -94,7 +99,8 @@ export const BarChartVertical = () => {
|
|
|
94
99
|
setBarWidth(barWidth)
|
|
95
100
|
setTotalBarsInGroup(barGroup.bars.length)
|
|
96
101
|
const yAxisValue = formatNumber(/[a-zA-Z]/.test(String(bar.value)) ? '' : bar.value, 'left')
|
|
97
|
-
const xAxisValue =
|
|
102
|
+
const xAxisValue =
|
|
103
|
+
config.runtime[section].type === 'date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
|
|
98
104
|
|
|
99
105
|
// create new Index for bars with negative values
|
|
100
106
|
const newIndex = bar.value < 0 ? -1 : index
|
|
@@ -113,7 +119,6 @@ export const BarChartVertical = () => {
|
|
|
113
119
|
let labelColor = '#000000'
|
|
114
120
|
labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor) // Set if background is transparent'
|
|
115
121
|
let barColor = config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key] ? colorScale(config.runtime.seriesLabels[bar.key]) : colorScale(bar.key)
|
|
116
|
-
barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor) // Color code by category
|
|
117
122
|
const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
|
|
118
123
|
const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
|
|
119
124
|
const isHighlightedBar = highlightedBarValues?.includes(xAxisValue)
|
|
@@ -156,13 +161,16 @@ export const BarChartVertical = () => {
|
|
|
156
161
|
: colorScale(config.runtime.seriesLabels[bar.key])
|
|
157
162
|
|
|
158
163
|
if (isRegularLollipopColor) _barColor = barColor
|
|
159
|
-
|
|
164
|
+
|
|
160
165
|
if (isHighlightedBar) _barColor = 'transparent'
|
|
166
|
+
if (config.legend.colorCode) _barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor)
|
|
167
|
+
if (isTwoToneLollipopColor) _barColor = chroma(barColor).brighten(1)
|
|
161
168
|
return _barColor
|
|
162
169
|
}
|
|
163
170
|
|
|
164
171
|
// if this is a two tone lollipop slightly lighten the bar.
|
|
165
172
|
if (isTwoToneLollipopColor) _barColor = chroma(barColor).brighten(1)
|
|
173
|
+
if (config.legend.colorCode) _barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor)
|
|
166
174
|
|
|
167
175
|
// if we're highlighting a bar make it invisible since it gets a border
|
|
168
176
|
if (isHighlightedBar) _barColor = 'transparent'
|
|
@@ -216,6 +224,7 @@ export const BarChartVertical = () => {
|
|
|
216
224
|
const yPadding = hasAsterisk ? -5 : -8
|
|
217
225
|
const verticalAnchor = hasAsterisk ? 'middle' : 'end'
|
|
218
226
|
const iconSize = pd.symbol === 'Asterisk' ? barWidth * 1.2 : pd.symbol === 'Double Asterisk' ? barWidth : barWidth / 1.5
|
|
227
|
+
const fillColor = pd.displayGray ? '#8b8b8a' : '#000'
|
|
219
228
|
|
|
220
229
|
return (
|
|
221
230
|
<Text // prettier-ignore
|
|
@@ -226,7 +235,7 @@ export const BarChartVertical = () => {
|
|
|
226
235
|
x={barX + barWidth / 2}
|
|
227
236
|
y={barY}
|
|
228
237
|
verticalAnchor={verticalAnchor}
|
|
229
|
-
fill={
|
|
238
|
+
fill={fillColor}
|
|
230
239
|
textAnchor='middle'
|
|
231
240
|
fontSize={`${iconSize}px`}
|
|
232
241
|
>
|
|
@@ -263,7 +272,7 @@ export const BarChartVertical = () => {
|
|
|
263
272
|
cx={barX + lollipopShapeSize / 3.5}
|
|
264
273
|
cy={bar.y}
|
|
265
274
|
r={lollipopShapeSize / 2}
|
|
266
|
-
fill={
|
|
275
|
+
fill={getBarBackgroundColor(colorScale(config.runtime.seriesLabels[bar.key]))}
|
|
267
276
|
key={`circle--${bar.index}`}
|
|
268
277
|
data-tooltip-html={tooltip}
|
|
269
278
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
@@ -277,7 +286,7 @@ export const BarChartVertical = () => {
|
|
|
277
286
|
y={barY}
|
|
278
287
|
width={lollipopShapeSize}
|
|
279
288
|
height={lollipopShapeSize}
|
|
280
|
-
fill={
|
|
289
|
+
fill={getBarBackgroundColor(colorScale(config.runtime.seriesLabels[bar.key]))}
|
|
281
290
|
key={`circle--${bar.index}`}
|
|
282
291
|
data-tooltip-html={tooltip}
|
|
283
292
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
@@ -5,7 +5,7 @@ interface BarConfigProps {
|
|
|
5
5
|
bar?: { [key: string]: any }
|
|
6
6
|
isNumber?: Function
|
|
7
7
|
config: { [key: string]: any }
|
|
8
|
-
getTextWidth:
|
|
8
|
+
getTextWidth: (a: string, b: string) => string
|
|
9
9
|
barWidth: number
|
|
10
10
|
isVertical: boolean
|
|
11
11
|
}
|
|
@@ -20,7 +20,6 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
20
20
|
let barLabel = ''
|
|
21
21
|
let isSuppressed = false
|
|
22
22
|
let showMissingDataLabel = false
|
|
23
|
-
let showZeroValueDataLabel = false
|
|
24
23
|
const showSuppressedSymbol = config.general.showSuppressedSymbol
|
|
25
24
|
|
|
26
25
|
config.preliminaryData.forEach(pd => {
|
|
@@ -41,23 +40,15 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
41
40
|
// Handle undefined, null, or non-calculable bar.value
|
|
42
41
|
if (!isSuppressed && !isNumber(bar.value) && config.general.showMissingDataLabel) {
|
|
43
42
|
const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
|
|
44
|
-
const labelFits = labelWidth < barWidth && barWidth > 10
|
|
43
|
+
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
45
44
|
showMissingDataLabel = true
|
|
46
45
|
barHeight = labelFits ? heightMini : 0
|
|
47
46
|
barWidthHorizontal = heightMini
|
|
48
47
|
}
|
|
49
|
-
// handle zero values
|
|
50
|
-
if (!isSuppressed && String(bar.value) === '0' && config.general.showZeroValueDataLabel) {
|
|
51
|
-
const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
|
|
52
|
-
const labelFits = labelWidth < barWidth && barWidth > 10
|
|
53
|
-
barHeight = config.isLollipopChart ? heightMini * 2 : !config.isLollipopChart && labelFits ? heightMini : 0
|
|
54
|
-
barWidthHorizontal = heightMini
|
|
55
|
-
showZeroValueDataLabel = true
|
|
56
|
-
}
|
|
57
48
|
|
|
58
49
|
const getBarY = (defaultBarY, yScale) => {
|
|
59
50
|
// calculate Y position of small bars (suppressed,N/A,Zero valued) bars
|
|
60
|
-
if (isSuppressed || showMissingDataLabel
|
|
51
|
+
if (isSuppressed || showMissingDataLabel) {
|
|
61
52
|
if (config.isLollipopChart) {
|
|
62
53
|
return yScale - heightMini * 2
|
|
63
54
|
} else {
|
|
@@ -77,12 +68,10 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
77
68
|
if (isSuppressed) label = ''
|
|
78
69
|
// If the config is set to show a label for missing data, display 'N/A'
|
|
79
70
|
if (showMissingDataLabel) label = 'N/A'
|
|
80
|
-
// If the config is set to specifically show zero values, set the label to '0'
|
|
81
|
-
if (showZeroValueDataLabel) label = '0'
|
|
82
71
|
|
|
83
72
|
// determine label width in pixels & check if it fits to the bar width
|
|
84
73
|
const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
|
|
85
|
-
const labelFits = labelWidth < barWidth && barWidth > 10
|
|
74
|
+
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
86
75
|
if (config.isLollipopChart) {
|
|
87
76
|
return label
|
|
88
77
|
} else {
|
|
@@ -90,7 +79,7 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
90
79
|
}
|
|
91
80
|
}
|
|
92
81
|
|
|
93
|
-
return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel,
|
|
82
|
+
return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, getBarY, getAbsentDataLabel }
|
|
94
83
|
}
|
|
95
84
|
|
|
96
85
|
export const testZeroValue = value => {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { Group } from '@visx/group'
|
|
2
|
+
import { useContext, useEffect, useRef, useState } from 'react'
|
|
3
|
+
import ConfigContext from '../ConfigContext'
|
|
4
|
+
import * as d3 from 'd3'
|
|
5
|
+
import { invertValue } from '@cdc/core/helpers/scaling'
|
|
6
|
+
import { Text } from '@visx/text'
|
|
7
|
+
interface BrushChartProps {
|
|
8
|
+
xMax: number
|
|
9
|
+
yMax: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const BrushChart = ({ xMax, yMax }: BrushChartProps) => {
|
|
13
|
+
const { tableData, config, setBrushConfig, getTextWidth, dashboardConfig, formatDate } = useContext(ConfigContext)
|
|
14
|
+
const [brushState, setBrushState] = useState({ isBrushing: false, selection: [] })
|
|
15
|
+
const [brushKey, setBrushKey] = useState(0)
|
|
16
|
+
const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
|
|
17
|
+
const isDashboardFilters = sharedFilters?.length > 0
|
|
18
|
+
const [tooltip, showTooltip] = useState(false)
|
|
19
|
+
const svgRef = useRef()
|
|
20
|
+
const brushheight = 25
|
|
21
|
+
const borderRadius = 15
|
|
22
|
+
const xDomain = d3.extent(tableData, d => new Date(d[config.runtime.originalXAxis.dataKey]))
|
|
23
|
+
|
|
24
|
+
const xScale = d3.scaleTime().domain(xDomain).range([0, xMax])
|
|
25
|
+
|
|
26
|
+
const tooltipText = 'Drag edges to focus on a specific segment '
|
|
27
|
+
const textWidth = getTextWidth(tooltipText, `normal ${16 / 1.1}px sans-serif`)
|
|
28
|
+
|
|
29
|
+
const calculateGroupTop = (): number => {
|
|
30
|
+
return Number(yMax) + config.xAxis.axisBBox + brushheight * 1.5
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const handleMouseOver = () => {
|
|
34
|
+
// show tooltip text only once before brush triggered
|
|
35
|
+
if (brushState.selection[0] === 0 && xMax === brushState.selection[1]) {
|
|
36
|
+
showTooltip(true)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const handleMouseLeave = () => {
|
|
40
|
+
// hide tooltip text if brush was triggered
|
|
41
|
+
if (brushState.selection[0] !== 0 || brushState.selection[1] !== xMax) {
|
|
42
|
+
showTooltip(false)
|
|
43
|
+
}
|
|
44
|
+
showTooltip(false)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const brushHandle = (g, selection, firstDate, lastDate) => {
|
|
48
|
+
const textWidth = getTextWidth(firstDate, `normal ${16 / 1.1}px sans-serif`)
|
|
49
|
+
return g
|
|
50
|
+
.selectAll('.handle--custom')
|
|
51
|
+
.data([{ side: 'left' }, { side: 'right' }])
|
|
52
|
+
.join(enter => {
|
|
53
|
+
const handleGroup = enter.append('g').attr('class', 'handle--custom')
|
|
54
|
+
handleGroup
|
|
55
|
+
.append('text')
|
|
56
|
+
.attr('x', d => (d.side === 'left' ? 0 : -textWidth))
|
|
57
|
+
.attr('y', 30)
|
|
58
|
+
.text(d => (d.side === 'left' ? firstDate : lastDate))
|
|
59
|
+
.attr('font-size', '13px')
|
|
60
|
+
return handleGroup
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
.attr('display', 'block')
|
|
64
|
+
.attr('transform', selection === null ? null : (_, i) => `translate(${selection[i]},${'10'})`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const initializeBrush = () => {
|
|
68
|
+
const svg = d3.select(svgRef.current).attr('overflow', 'visible')
|
|
69
|
+
|
|
70
|
+
svg
|
|
71
|
+
.append('rect') // prettier-ignore
|
|
72
|
+
.attr('fill', '#949494')
|
|
73
|
+
.attr('stroke', '#c5c5c5')
|
|
74
|
+
.attr('stroke-width', 2)
|
|
75
|
+
.attr('ry', borderRadius)
|
|
76
|
+
.attr('rx', borderRadius)
|
|
77
|
+
.attr('height', brushheight)
|
|
78
|
+
.attr('width', xMax)
|
|
79
|
+
|
|
80
|
+
const brushHandler = event => {
|
|
81
|
+
const selection = event?.selection
|
|
82
|
+
//if (!selection) return
|
|
83
|
+
let isUserBrushing = event.type === 'brush' && selection && selection.length > 0
|
|
84
|
+
|
|
85
|
+
const [x0, x1] = selection.map(value => xScale.invert(value))
|
|
86
|
+
|
|
87
|
+
// filter and update brush state directly
|
|
88
|
+
const newFilteredData = tableData.filter(d => {
|
|
89
|
+
const dateValue = d[config.runtime.originalXAxis.dataKey]
|
|
90
|
+
// Check if the date value exists and is valid
|
|
91
|
+
if (!dateValue) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const parsedDate = new Date(dateValue)
|
|
96
|
+
|
|
97
|
+
// Check if parsedDate is a valid date
|
|
98
|
+
if (isNaN(parsedDate.getTime())) {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if the date falls within the selection range
|
|
103
|
+
if (parsedDate >= x0 && parsedDate <= x1) {
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
const firstDate = (newFilteredData.length && newFilteredData[0][config?.runtime?.originalXAxis?.dataKey]) ?? ''
|
|
109
|
+
const lastDate =
|
|
110
|
+
(newFilteredData.length &&
|
|
111
|
+
newFilteredData[newFilteredData.length - 1][config?.runtime?.originalXAxis?.dataKey]) ??
|
|
112
|
+
''
|
|
113
|
+
// add custom blue colored handlers to each corners of brush
|
|
114
|
+
svg.selectAll('.handle--custom').remove()
|
|
115
|
+
// append handler
|
|
116
|
+
svg.call(brushHandle, selection, firstDate, lastDate)
|
|
117
|
+
|
|
118
|
+
setBrushConfig({
|
|
119
|
+
active: config.brush.active,
|
|
120
|
+
isBrushing: isUserBrushing,
|
|
121
|
+
data: newFilteredData
|
|
122
|
+
})
|
|
123
|
+
setBrushState({
|
|
124
|
+
isBrushing: true,
|
|
125
|
+
selection
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const brush = d3
|
|
130
|
+
.brushX()
|
|
131
|
+
.extent([
|
|
132
|
+
[0, 0],
|
|
133
|
+
[xMax, 25]
|
|
134
|
+
]) // brush extent
|
|
135
|
+
.on('start brush end', brushHandler)
|
|
136
|
+
|
|
137
|
+
const defaultSelection = [0, xMax]
|
|
138
|
+
let brushGroup = svg.append('g').call(brush).call(brush.move, defaultSelection)
|
|
139
|
+
brushGroup.select('.overlay').style('pointer-events', 'none')
|
|
140
|
+
|
|
141
|
+
brushGroup
|
|
142
|
+
.selectAll('.selection')
|
|
143
|
+
.attr('fill', '#474747')
|
|
144
|
+
.attr('fill-opacity', 1)
|
|
145
|
+
.attr('rx', borderRadius)
|
|
146
|
+
.attr('ry', borderRadius)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const isFiltersActive = config.filters?.some(filter => filter.active)
|
|
151
|
+
const isExclusionsActive = config.exclusions?.active
|
|
152
|
+
|
|
153
|
+
if ((isFiltersActive || isExclusionsActive || isDashboardFilters) && config.brush?.active) {
|
|
154
|
+
setBrushKey(prevKey => prevKey + 1)
|
|
155
|
+
setBrushConfig(prev => {
|
|
156
|
+
return {
|
|
157
|
+
...prev,
|
|
158
|
+
data: tableData
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
return () =>
|
|
163
|
+
setBrushConfig(prev => {
|
|
164
|
+
return {
|
|
165
|
+
...prev,
|
|
166
|
+
data: []
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
}, [config.filters, config.exclusions, config.brush?.active, isDashboardFilters])
|
|
170
|
+
// Initialize brush when component is first rendered
|
|
171
|
+
|
|
172
|
+
// reset brush on keychange
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (brushKey) {
|
|
175
|
+
initializeBrush()
|
|
176
|
+
}
|
|
177
|
+
}, [brushKey])
|
|
178
|
+
|
|
179
|
+
if (!brushState.isBrushing) {
|
|
180
|
+
initializeBrush()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<Group
|
|
185
|
+
onMouseLeave={handleMouseLeave}
|
|
186
|
+
onMouseOver={handleMouseOver}
|
|
187
|
+
className='brush-container'
|
|
188
|
+
left={Number(config.runtime.yAxis.size)}
|
|
189
|
+
top={calculateGroupTop()}
|
|
190
|
+
>
|
|
191
|
+
<Text
|
|
192
|
+
pointerEvents='visiblePainted'
|
|
193
|
+
display={tooltip ? 'block' : 'none'}
|
|
194
|
+
fontSize={16}
|
|
195
|
+
x={(Number(xMax) - Number(textWidth)) / 2}
|
|
196
|
+
y={-10}
|
|
197
|
+
>
|
|
198
|
+
Drag edges to focus on a specific segment
|
|
199
|
+
</Text>
|
|
200
|
+
<svg width={'100%'} height={brushheight * 3} ref={svgRef}></svg>
|
|
201
|
+
</Group>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default BrushChart
|