@cdc/chart 4.23.6 → 4.23.8
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 +29981 -29995
- package/examples/feature/__data__/area-chart-date-apple.json +5122 -0
- package/examples/feature/__data__/city-temperature.json +2198 -0
- package/examples/feature/__data__/planet-example-data.json +1 -1
- package/examples/feature/area/area-chart-category.json +45 -45
- package/examples/feature/area/area-chart-date-apple.json +10376 -0
- package/examples/feature/area/area-chart-date-city-temperature.json +4528 -0
- package/examples/feature/area/area-chart-date.json +111 -3
- package/examples/feature/combo/right-issues.json +1 -1
- package/examples/feature/forecasting/combo-forecasting.json +72 -46
- package/examples/feature/forecasting/effective_reproduction.json +57 -8
- package/examples/feature/forecasting/forecasting.json +12 -3
- package/examples/feature/forest-plot/broken.json +700 -0
- package/examples/feature/forest-plot/data.csv +24 -0
- package/examples/feature/forest-plot/forest-plot.json +717 -0
- package/examples/feature/line/line-chart.json +11 -11
- package/examples/feature/pie/planet-pie-example-config.json +1 -1
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +167 -20
- package/examples/private/confidence_interval_test.json +248 -0
- package/examples/private/tooltip-issue.json +45275 -0
- package/index.html +13 -11
- package/package.json +4 -3
- package/src/CdcChart.jsx +78 -27
- package/src/components/AreaChart.jsx +65 -151
- package/src/components/BarChart.Horizontal.jsx +251 -0
- package/src/components/BarChart.StackedHorizontal.jsx +118 -0
- package/src/components/BarChart.StackedVertical.jsx +93 -0
- package/src/components/BarChart.Vertical.jsx +204 -0
- package/src/components/BarChart.jsx +17 -667
- package/src/components/BarChartType.jsx +15 -0
- package/src/components/BrushHandle.jsx +17 -0
- package/src/components/DataTable.jsx +67 -22
- package/src/components/EditorPanel.jsx +426 -358
- package/src/components/Forecasting.jsx +23 -86
- package/src/components/ForestPlot.jsx +191 -0
- package/src/components/ForestPlotSettings.jsx +508 -0
- package/src/components/Legend.jsx +10 -8
- package/src/components/LineChart.jsx +31 -6
- package/src/components/LinearChart.jsx +317 -230
- package/src/components/Series.jsx +40 -4
- package/src/data/initial-state.js +50 -3
- package/src/hooks/useBarChart.js +186 -0
- package/src/hooks/useEditorPermissions.js +218 -0
- package/src/hooks/useMinMax.js +18 -5
- package/src/hooks/useRightAxis.js +2 -1
- package/src/hooks/useScales.js +45 -2
- package/src/hooks/useTooltip.jsx +407 -0
- package/src/scss/main.scss +11 -17
|
@@ -1,61 +1,72 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
1
|
+
import React, { useContext, useEffect, useRef, useState, useMemo } from 'react'
|
|
3
2
|
|
|
3
|
+
// Libraries
|
|
4
|
+
import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
|
|
4
5
|
import { Group } from '@visx/group'
|
|
5
|
-
import { Line } from '@visx/shape'
|
|
6
|
+
import { Line, Bar } from '@visx/shape'
|
|
6
7
|
import { Text } from '@visx/text'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { useTooltip } from '@visx/tooltip'
|
|
8
|
+
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
9
|
+
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
10
|
|
|
11
|
+
// CDC Components
|
|
12
|
+
import AreaChart from './AreaChart'
|
|
11
13
|
import BarChart from './BarChart'
|
|
12
14
|
import ConfigContext from '../ConfigContext'
|
|
13
|
-
import CoveAreaChart from './AreaChart'
|
|
14
15
|
import CoveBoxPlot from './BoxPlot'
|
|
15
|
-
import
|
|
16
|
+
import ScatterPlot from './ScatterPlot'
|
|
16
17
|
import DeviationBar from './DeviationBar'
|
|
18
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
19
|
+
import Forecasting from './Forecasting'
|
|
17
20
|
import LineChart from './LineChart'
|
|
21
|
+
import ForestPlot from './ForestPlot'
|
|
18
22
|
import PairedBarChart from './PairedBarChart'
|
|
19
23
|
import useIntersectionObserver from './useIntersectionObserver'
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
import '../scss/LinearChart.scss'
|
|
23
|
-
import useReduceData from '../hooks/useReduceData'
|
|
24
|
-
import useScales from '../hooks/useScales'
|
|
25
|
+
// Hooks
|
|
25
26
|
import useMinMax from '../hooks/useMinMax'
|
|
27
|
+
import useReduceData from '../hooks/useReduceData'
|
|
26
28
|
import useRightAxis from '../hooks/useRightAxis'
|
|
29
|
+
import useScales from '../hooks/useScales'
|
|
27
30
|
import useTopAxis from '../hooks/useTopAxis'
|
|
28
|
-
import
|
|
31
|
+
import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
|
|
32
|
+
|
|
33
|
+
// styles
|
|
34
|
+
import '../scss/LinearChart.scss'
|
|
29
35
|
|
|
30
36
|
export default function LinearChart() {
|
|
31
|
-
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, rawData } = useContext(ConfigContext)
|
|
37
|
+
const { isEditor, isDashboard, transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, rawData, capitalize, setSharedFilter, setSharedFilterValue, getTextWidth, isDebug } = useContext(ConfigContext)
|
|
32
38
|
|
|
33
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
39
|
+
// todo: start destructuring this file for conciseness
|
|
40
|
+
const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
|
|
41
|
+
|
|
42
|
+
const getDate = d => new Date(d[config.xAxis.dataKey])
|
|
37
43
|
|
|
38
44
|
// configure width
|
|
39
45
|
let [width] = dimensions
|
|
40
46
|
if (config && config.legend && !config.legend.hide && config.legend.position !== 'bottom' && ['lg', 'md'].includes(currentViewport)) {
|
|
41
47
|
width = width * 0.73
|
|
42
48
|
}
|
|
43
|
-
// configure height , yMax,
|
|
49
|
+
// configure height , yMax, xMax
|
|
44
50
|
const { horizontal: heightHorizontal } = config.heights
|
|
45
|
-
const isHorizontal =
|
|
51
|
+
const isHorizontal = orientation === 'horizontal'
|
|
46
52
|
const shouldAbbreviate = true
|
|
47
|
-
|
|
48
|
-
const xMax = width -
|
|
49
|
-
|
|
53
|
+
let height = config.aspectRatio ? width * config.aspectRatio : config.visualizationType === 'Forest Plot' ? config.heights['vertical'] : config.heights[orientation]
|
|
54
|
+
const xMax = width - runtime.yAxis.size - (visualizationType === 'Combo' ? config.yAxis.rightAxisSize : 0)
|
|
55
|
+
let yMax = height - (orientation === 'horizontal' ? 0 : runtime.xAxis.size)
|
|
56
|
+
|
|
57
|
+
if (config.visualizationType === 'Forest Plot') {
|
|
58
|
+
height = height + config.data.length * config.forestPlot.rowHeight
|
|
59
|
+
yMax = yMax + config.data.length * config.forestPlot.rowHeight
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let dynamicMarginTop = 0 || config.dynamicMarginTop
|
|
63
|
+
const marginTop = 20
|
|
50
64
|
|
|
51
65
|
// hooks % states
|
|
52
66
|
const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
|
|
53
67
|
const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
|
|
54
68
|
const { hasTopAxis } = useTopAxis(config)
|
|
55
69
|
const [animatedChart, setAnimatedChart] = useState(false)
|
|
56
|
-
const properties = { data, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
|
|
57
|
-
const { min, max } = useMinMax(properties)
|
|
58
|
-
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding } = useScales({ ...properties, min, max })
|
|
59
70
|
|
|
60
71
|
// refs
|
|
61
72
|
const triggerRef = useRef()
|
|
@@ -64,29 +75,47 @@ export default function LinearChart() {
|
|
|
64
75
|
freezeOnceVisible: false
|
|
65
76
|
})
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
// getters & functions
|
|
79
|
+
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
80
|
+
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
81
|
+
const xAxisDataMapped = data.map(d => getXAxisData(d))
|
|
82
|
+
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
83
|
+
const properties = { data, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
|
|
84
|
+
const { min, max } = useMinMax(properties)
|
|
85
|
+
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding } = useScales({ ...properties, min, max })
|
|
86
|
+
|
|
87
|
+
// sets the portal x/y for where tooltips should appear on the page.
|
|
88
|
+
const [chartPosition, setChartPosition] = useState(null)
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
setChartPosition(svgRef?.current?.getBoundingClientRect())
|
|
91
|
+
}, [svgRef, config.legend])
|
|
92
|
+
|
|
93
|
+
const handleLeftTickFormatting = (tick, index) => {
|
|
68
94
|
if (config.useLogScale && tick === 0.1) {
|
|
69
95
|
//when logarithmic scale applied change value of first tick
|
|
70
96
|
tick = 0
|
|
71
97
|
}
|
|
72
|
-
|
|
73
|
-
if (config.
|
|
98
|
+
|
|
99
|
+
if (config.data && !config.data[index] && visualizationType === 'Forest Plot') return
|
|
100
|
+
if (config.visualizationType === 'Forest Plot') return config.data[index][config.xAxis.dataKey]
|
|
101
|
+
if (runtime.yAxis.type === 'date') return formatDate(parseDate(tick))
|
|
102
|
+
if (orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
74
103
|
return tick
|
|
75
104
|
}
|
|
76
105
|
|
|
77
106
|
const handleBottomTickFormatting = tick => {
|
|
78
107
|
if (config.useLogScale && tick === 0.1) {
|
|
79
|
-
// when
|
|
108
|
+
// when logarithmic scale applied change value FIRST of tick
|
|
80
109
|
tick = 0
|
|
81
110
|
}
|
|
82
|
-
if (
|
|
83
|
-
if (
|
|
111
|
+
if (runtime.xAxis.type === 'date') return formatDate(tick)
|
|
112
|
+
if (orientation === 'horizontal') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
84
113
|
if (config.xAxis.type === 'continuous') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
85
114
|
return tick
|
|
86
115
|
}
|
|
87
116
|
|
|
88
117
|
const countNumOfTicks = axis => {
|
|
89
|
-
const { numTicks } =
|
|
118
|
+
const { numTicks } = runtime[axis]
|
|
90
119
|
let tickCount = undefined
|
|
91
120
|
|
|
92
121
|
if (axis === 'yAxis') {
|
|
@@ -121,108 +150,24 @@ export default function LinearChart() {
|
|
|
121
150
|
return tickCount
|
|
122
151
|
}
|
|
123
152
|
|
|
124
|
-
// Tooltip
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// import tooltip helpers
|
|
146
|
-
const { tooltipData, showTooltip, hideTooltip } = useTooltip()
|
|
147
|
-
|
|
148
|
-
const handleTooltipMouseOver = (e, data) => {
|
|
149
|
-
// get the svg coordinates of the mouse
|
|
150
|
-
// and get the closest values
|
|
151
|
-
const eventSvgCoords = localPoint(e)
|
|
152
|
-
const { x, y } = eventSvgCoords
|
|
153
|
-
|
|
154
|
-
const { runtime } = config
|
|
155
|
-
|
|
156
|
-
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
157
|
-
let formattedDate = formatDate(closestXScaleValue)
|
|
158
|
-
|
|
159
|
-
let yScaleValues
|
|
160
|
-
if (xAxis.type === 'categorical') {
|
|
161
|
-
yScaleValues = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
162
|
-
} else {
|
|
163
|
-
yScaleValues = rawData.filter(d => formatDate(parseDate(d[xAxis.dataKey])) === formattedDate)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let seriesToInclude = []
|
|
167
|
-
let stageColumns = []
|
|
168
|
-
let ciItems = []
|
|
169
|
-
|
|
170
|
-
// loop through series for items to add to tooltip.
|
|
171
|
-
// there is probably a better way of doing this.
|
|
172
|
-
config.series?.map(s => {
|
|
173
|
-
if (s.type === 'Forecasting') {
|
|
174
|
-
stageColumns.push(s.stageColumn)
|
|
175
|
-
|
|
176
|
-
// greedy fn 😭
|
|
177
|
-
s?.confidenceIntervals.map(ci => {
|
|
178
|
-
if (ci.showInTooltip === true) {
|
|
179
|
-
ciItems.push(ci.low)
|
|
180
|
-
ciItems.push(ci.high)
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
let standardLoopItems = []
|
|
187
|
-
|
|
188
|
-
if (config.visualizationType === 'Combo') {
|
|
189
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.barSeriesKeys, ...stageColumns, ...ciItems]
|
|
190
|
-
} else {
|
|
191
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...stageColumns, ...ciItems]
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
standardLoopItems.map(seriesKey => {
|
|
195
|
-
if (!seriesKey) return false
|
|
196
|
-
if (!yScaleValues[0]) return false
|
|
197
|
-
for (const item of Object.entries(yScaleValues[0])) {
|
|
198
|
-
if (item[0] === seriesKey) {
|
|
199
|
-
seriesToInclude.push(item)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
// filter out the series that aren't added to the map.
|
|
205
|
-
if (!seriesToInclude) return
|
|
206
|
-
let initialTooltipData = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
|
|
207
|
-
|
|
208
|
-
let tooltipData = {}
|
|
209
|
-
tooltipData.data = initialTooltipData
|
|
210
|
-
tooltipData.dataXPosition = x + 10
|
|
211
|
-
tooltipData.dataYPosition = y
|
|
212
|
-
|
|
213
|
-
let tooltipInformation = {
|
|
214
|
-
tooltipData: tooltipData,
|
|
215
|
-
tooltipTop: 0,
|
|
216
|
-
tooltipValues: yScaleValues,
|
|
217
|
-
tooltipLeft: x
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
showTooltip(tooltipInformation)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const handleTooltipMouseOff = () => {
|
|
224
|
-
hideTooltip()
|
|
225
|
-
}
|
|
153
|
+
// Tooltip Helpers
|
|
154
|
+
const { tooltipData, showTooltip, hideTooltip, tooltipOpen } = useTooltip()
|
|
155
|
+
|
|
156
|
+
// prettier-ignore
|
|
157
|
+
const {
|
|
158
|
+
handleTooltipMouseOver,
|
|
159
|
+
handleTooltipClick,
|
|
160
|
+
handleTooltipMouseOff,
|
|
161
|
+
tooltipStyles,
|
|
162
|
+
TooltipListItem,
|
|
163
|
+
getXValueFromCoordinateDate,
|
|
164
|
+
getXValueFromCoordinate
|
|
165
|
+
} = useCoveTooltip({
|
|
166
|
+
xScale,
|
|
167
|
+
yScale,
|
|
168
|
+
showTooltip,
|
|
169
|
+
hideTooltip
|
|
170
|
+
})
|
|
226
171
|
|
|
227
172
|
// Make sure the chart is visible if in the editor
|
|
228
173
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
@@ -243,14 +188,29 @@ export default function LinearChart() {
|
|
|
243
188
|
}
|
|
244
189
|
}, [dataRef?.isIntersecting, config.animate])
|
|
245
190
|
|
|
246
|
-
const
|
|
191
|
+
const chartHasTooltipGuides = () => {
|
|
192
|
+
const { visualizationType } = config
|
|
193
|
+
if (visualizationType === 'Combo' && runtime.forecastingSeriesKeys > 0) return true
|
|
194
|
+
if (visualizationType === 'Area Chart') return true
|
|
195
|
+
if (visualizationType === 'Line') return true
|
|
196
|
+
if (visualizationType === 'Bar') return true
|
|
197
|
+
return false
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
201
|
+
|
|
202
|
+
const handleNumTicks = () => {
|
|
203
|
+
// On forest plots we need to return every "study" or y axis value.
|
|
204
|
+
if (config.visualizationType === 'Forest Plot') return config.data.length
|
|
205
|
+
return countNumOfTicks('yAxis')
|
|
206
|
+
}
|
|
247
207
|
|
|
248
208
|
return isNaN(width) ? (
|
|
249
|
-
|
|
209
|
+
<React.Fragment></React.Fragment>
|
|
250
210
|
) : (
|
|
251
211
|
<ErrorBoundary component='LinearChart'>
|
|
252
|
-
<svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0} ref={svgRef}>
|
|
253
|
-
{/* Highlighted regions */}
|
|
212
|
+
<svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0} ref={svgRef}>
|
|
213
|
+
<Bar width={width} height={height} fill={'transparent'}></Bar> {/* Highlighted regions */}
|
|
254
214
|
{config.regions
|
|
255
215
|
? config.regions.map(region => {
|
|
256
216
|
if (!Object.keys(region).includes('from') || !Object.keys(region).includes('to')) return null
|
|
@@ -275,7 +235,7 @@ export default function LinearChart() {
|
|
|
275
235
|
if (!to) return null
|
|
276
236
|
|
|
277
237
|
return (
|
|
278
|
-
<Group className='regions' left={Number(
|
|
238
|
+
<Group className='regions' left={Number(runtime.yAxis.size)} key={region.label}>
|
|
279
239
|
<path
|
|
280
240
|
stroke='#333'
|
|
281
241
|
d={`M${from} -5
|
|
@@ -293,12 +253,11 @@ export default function LinearChart() {
|
|
|
293
253
|
)
|
|
294
254
|
})
|
|
295
255
|
: ''}
|
|
296
|
-
|
|
297
256
|
{/* Y axis */}
|
|
298
|
-
{
|
|
299
|
-
<AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(
|
|
257
|
+
{!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
|
|
258
|
+
<AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
|
|
300
259
|
{props => {
|
|
301
|
-
const axisCenter =
|
|
260
|
+
const axisCenter = runtime.horizontal ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
302
261
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
303
262
|
return (
|
|
304
263
|
<Group className='left-axis'>
|
|
@@ -311,13 +270,13 @@ export default function LinearChart() {
|
|
|
311
270
|
|
|
312
271
|
return (
|
|
313
272
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
314
|
-
{!
|
|
273
|
+
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={runtime.horizontal ? 'none' : 'block'} />}
|
|
315
274
|
|
|
316
|
-
{
|
|
275
|
+
{runtime.yAxis.gridLines ? <Line key={`${tick.value}--hide-hideGridLines`} display={(config.useLogScale && showTicks).toString()} from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
|
|
317
276
|
|
|
318
|
-
{
|
|
277
|
+
{orientation === 'horizontal' && visualizationSubType !== 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
|
|
319
278
|
<Text
|
|
320
|
-
transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.to.y - minY : tick.to.y - minY + (Number(config.barHeight * config.series.length) - barMinHeight) / 2}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`}
|
|
279
|
+
transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.to.y - minY : tick.to.y - minY + (Number(config.barHeight * config.series.length) - barMinHeight) / 2}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation || 0 : 0})`}
|
|
321
280
|
verticalAnchor={'start'}
|
|
322
281
|
textAnchor={'end'}
|
|
323
282
|
>
|
|
@@ -325,29 +284,30 @@ export default function LinearChart() {
|
|
|
325
284
|
</Text>
|
|
326
285
|
)}
|
|
327
286
|
|
|
328
|
-
{
|
|
329
|
-
<Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + (Number(config.barHeight) - barMinHeight) / 2}) rotate(-${
|
|
287
|
+
{orientation === 'horizontal' && visualizationSubType === 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
|
|
288
|
+
<Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + (Number(config.barHeight) - barMinHeight) / 2}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`} verticalAnchor={'start'} textAnchor={'end'}>
|
|
330
289
|
{tick.formattedValue}
|
|
331
290
|
</Text>
|
|
332
291
|
)}
|
|
333
292
|
|
|
334
|
-
{
|
|
335
|
-
<Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + Number(config.barHeight) / 2}) rotate(-${
|
|
293
|
+
{orientation === 'horizontal' && visualizationType === 'Paired Bar' && !config.yAxis.hideLabel && (
|
|
294
|
+
<Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + Number(config.barHeight) / 2}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`} textAnchor={'end'} verticalAnchor='middle'>
|
|
336
295
|
{tick.formattedValue}
|
|
337
296
|
</Text>
|
|
338
297
|
)}
|
|
339
|
-
{
|
|
340
|
-
<Text transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.to.y - minY + 2 : tick.to.y - minY + Number(config.barHeight) / 2}) rotate(-${
|
|
298
|
+
{orientation === 'horizontal' && visualizationType === 'Deviation Bar' && !config.yAxis.hideLabel && (
|
|
299
|
+
<Text transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.to.y - minY + 2 : tick.to.y - minY + Number(config.barHeight) / 2}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`} textAnchor={'end'} verticalAnchor='middle'>
|
|
341
300
|
{tick.formattedValue}
|
|
342
301
|
</Text>
|
|
343
302
|
)}
|
|
344
303
|
|
|
345
|
-
{
|
|
304
|
+
{orientation === 'vertical' && visualizationType !== 'Paired Bar' && !config.yAxis.hideLabel && (
|
|
346
305
|
<Text
|
|
347
306
|
display={config.useLogScale ? showTicks : 'block'}
|
|
348
307
|
dx={config.useLogScale ? -6 : 0}
|
|
349
308
|
x={config.runtime.horizontal ? tick.from.x + 2 : tick.to.x}
|
|
350
309
|
y={tick.to.y + (config.runtime.horizontal ? horizontalTickOffset : 0)}
|
|
310
|
+
angle={-Number(config.yAxis.tickRotation) || 0}
|
|
351
311
|
verticalAnchor={config.runtime.horizontal ? 'start' : 'middle'}
|
|
352
312
|
textAnchor={config.runtime.horizontal ? 'start' : 'end'}
|
|
353
313
|
fill={config.yAxis.tickLabelColor}
|
|
@@ -358,10 +318,10 @@ export default function LinearChart() {
|
|
|
358
318
|
</Group>
|
|
359
319
|
)
|
|
360
320
|
})}
|
|
361
|
-
{!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={
|
|
321
|
+
{!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={runtime.horizontal ? { x: 0, y: config.visualizationType === 'Forest Plot' ? height : Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
|
|
362
322
|
{yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
|
|
363
|
-
{
|
|
364
|
-
<Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 *
|
|
323
|
+
{visualizationType === 'Bar' && orientation === 'horizontal' && xScale.domain()[0] < 0 && <Line from={{ x: xScale(0), y: 0 }} to={{ x: xScale(0), y: yMax }} stroke='#333' strokeWidth={2} />}
|
|
324
|
+
<Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
|
|
365
325
|
{props.label}
|
|
366
326
|
</Text>
|
|
367
327
|
</Group>
|
|
@@ -369,24 +329,23 @@ export default function LinearChart() {
|
|
|
369
329
|
}}
|
|
370
330
|
</AxisLeft>
|
|
371
331
|
)}
|
|
372
|
-
|
|
373
332
|
{/* Right Axis */}
|
|
374
333
|
{hasRightAxis && (
|
|
375
|
-
<AxisRight scale={yScaleRight} left={Number(width - config.yAxis.rightAxisSize)} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={
|
|
334
|
+
<AxisRight scale={yScaleRight} left={Number(width - config.yAxis.rightAxisSize)} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={runtime.yAxis.rightNumTicks || undefined} labelOffset={45}>
|
|
376
335
|
{props => {
|
|
377
|
-
const axisCenter =
|
|
336
|
+
const axisCenter = runtime.horizontal ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
378
337
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
379
338
|
return (
|
|
380
339
|
<Group className='right-axis'>
|
|
381
340
|
{props.ticks.map((tick, i) => {
|
|
382
341
|
return (
|
|
383
342
|
<Group key={`vx-tick-${tick.value}-${i}`} className='vx-axis-tick'>
|
|
384
|
-
{!
|
|
343
|
+
{!runtime.yAxis.rightHideTicks && <Line from={tick.from} to={tick.to} display={runtime.horizontal ? 'none' : 'block'} stroke={config.yAxis.rightAxisTickColor} />}
|
|
385
344
|
|
|
386
|
-
{
|
|
345
|
+
{runtime.yAxis.rightGridLines ? <Line from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
|
|
387
346
|
|
|
388
347
|
{!config.yAxis.rightHideLabel && (
|
|
389
|
-
<Text x={tick.to.x} y={tick.to.y + (
|
|
348
|
+
<Text x={tick.to.x} y={tick.to.y + (runtime.horizontal ? horizontalTickOffset : 0)} verticalAnchor={runtime.horizontal ? 'start' : 'middle'} textAnchor={'start'} fill={config.yAxis.rightAxisTickLabelColor}>
|
|
390
349
|
{tick.formattedValue}
|
|
391
350
|
</Text>
|
|
392
351
|
)}
|
|
@@ -402,11 +361,10 @@ export default function LinearChart() {
|
|
|
402
361
|
}}
|
|
403
362
|
</AxisRight>
|
|
404
363
|
)}
|
|
405
|
-
|
|
406
364
|
{hasTopAxis && config.topAxis.hasLine && (
|
|
407
365
|
<AxisTop
|
|
408
366
|
stroke='#333'
|
|
409
|
-
left={Number(
|
|
367
|
+
left={Number(runtime.yAxis.size)}
|
|
410
368
|
scale={xScale}
|
|
411
369
|
hideTicks
|
|
412
370
|
hideZero
|
|
@@ -415,41 +373,84 @@ export default function LinearChart() {
|
|
|
415
373
|
})}
|
|
416
374
|
/>
|
|
417
375
|
)}
|
|
418
|
-
|
|
419
376
|
{/* X axis */}
|
|
420
|
-
{
|
|
377
|
+
{visualizationType !== 'Paired Bar' && visualizationType !== 'Spark Line' && (
|
|
421
378
|
<AxisBottom
|
|
422
|
-
top={
|
|
423
|
-
left={Number(
|
|
424
|
-
label={
|
|
379
|
+
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax + Number(config.xAxis.axisPadding)}
|
|
380
|
+
left={Number(runtime.yAxis.size)}
|
|
381
|
+
label={runtime.xAxis.label}
|
|
425
382
|
tickFormat={handleBottomTickFormatting}
|
|
426
383
|
scale={xScale}
|
|
427
384
|
stroke='#333'
|
|
428
|
-
tickStroke='#333'
|
|
429
385
|
numTicks={countNumOfTicks('xAxis')}
|
|
386
|
+
tickStroke='#333'
|
|
430
387
|
>
|
|
431
388
|
{props => {
|
|
432
|
-
const axisCenter = (props.axisToPoint.x - props.axisFromPoint.x) / 2
|
|
389
|
+
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : width / 2
|
|
390
|
+
const containsMultipleWords = inputString => /\s/.test(inputString)
|
|
391
|
+
const ismultiLabel = props.ticks.some(tick => containsMultipleWords(tick.value))
|
|
392
|
+
|
|
393
|
+
// Calculate sumOfTickWidth here, before map function
|
|
394
|
+
const fontSize = { small: 16, medium: 18, large: 20 }
|
|
395
|
+
const defaultTickLength = 8
|
|
396
|
+
const tickWidthMax = Math.max(...props.ticks.map(tick => getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)))
|
|
397
|
+
// const marginTop = 20 // moved to top bc need for yMax calcs
|
|
398
|
+
const accumulator = ismultiLabel ? 180 : 100
|
|
399
|
+
|
|
400
|
+
const textWidths = props.ticks.map(tick => getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`))
|
|
401
|
+
const sumOfTickWidth = textWidths.reduce((a, b) => a + b, accumulator)
|
|
402
|
+
const spaceBetweenEachTick = (xMax - sumOfTickWidth) / (props.ticks.length - 1)
|
|
403
|
+
|
|
404
|
+
// Check if ticks are overlapping
|
|
405
|
+
// Determine the position of each tick
|
|
406
|
+
let positions = [0] // The first tick is at position 0
|
|
407
|
+
for (let i = 1; i < textWidths.length; i++) {
|
|
408
|
+
// The position of each subsequent tick is the position of the previous tick
|
|
409
|
+
// plus the width of the previous tick and the space
|
|
410
|
+
positions[i] = positions[i - 1] + textWidths[i - 1] + spaceBetweenEachTick
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Check if ticks are overlapping
|
|
414
|
+
let areTicksTouching = false
|
|
415
|
+
textWidths.forEach((_, i) => {
|
|
416
|
+
if (positions[i] + textWidths[i] > positions[i + 1]) {
|
|
417
|
+
areTicksTouching = true
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
dynamicMarginTop = areTicksTouching && config.isResponsiveTicks ? tickWidthMax + defaultTickLength + marginTop : 0
|
|
423
|
+
config.dynamicMarginTop = dynamicMarginTop
|
|
424
|
+
|
|
433
425
|
return (
|
|
434
426
|
<Group className='bottom-axis'>
|
|
435
|
-
{props.ticks.map((tick, i) => {
|
|
427
|
+
{props.ticks.map((tick, i, propsTicks) => {
|
|
436
428
|
// when using LogScale show major ticks values only
|
|
437
429
|
const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
438
|
-
const
|
|
439
|
-
const tickLength = showTick === 'block' ? 16 : 8
|
|
430
|
+
const tickLength = showTick === 'block' ? 16 : defaultTickLength
|
|
440
431
|
const to = { x: tick.to.x, y: tickLength }
|
|
432
|
+
let textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
433
|
+
let limitedWidth = 100 / propsTicks.length
|
|
434
|
+
//reset rotations by updating config
|
|
435
|
+
config.yAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'horizontal' ? 0 : config.yAxis.tickRotation
|
|
436
|
+
config.xAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'vertical' ? 0 : config.xAxis.tickRotation
|
|
437
|
+
//configure rotation
|
|
438
|
+
|
|
439
|
+
const tickRotation = config.isResponsiveTicks && areTicksTouching ? -Number(config.xAxis.maxTickRotation) || -90 : -Number(config.runtime.xAxis.tickRotation)
|
|
441
440
|
|
|
442
441
|
return (
|
|
443
442
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
444
|
-
{!config.xAxis.hideTicks && <Line from={tick.from} to={
|
|
443
|
+
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' ? 1.3 : 1} />}
|
|
445
444
|
{!config.xAxis.hideLabel && (
|
|
446
445
|
<Text
|
|
447
446
|
dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
|
|
448
447
|
display={config.orientation === 'horizontal' && config.useLogScale ? showTick : 'block'}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
448
|
+
x={tick.to.x}
|
|
449
|
+
y={tick.to.y}
|
|
450
|
+
angle={tickRotation}
|
|
451
|
+
verticalAnchor={tickRotation < -50 ? 'middle' : 'start'}
|
|
452
|
+
textAnchor={tickRotation ? 'end' : 'middle'}
|
|
453
|
+
width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : textWidth}
|
|
453
454
|
fill={config.xAxis.tickLabelColor}
|
|
454
455
|
>
|
|
455
456
|
{tick.formattedValue}
|
|
@@ -459,7 +460,7 @@ export default function LinearChart() {
|
|
|
459
460
|
)
|
|
460
461
|
})}
|
|
461
462
|
{!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
462
|
-
<Text x={axisCenter} y={config.orientation === 'horizontal' ? config.xAxis.labelOffset : config.xAxis.size} textAnchor='middle' fontWeight='bold' fill={config.xAxis.labelColor}>
|
|
463
|
+
<Text x={axisCenter} y={config.orientation === 'horizontal' ? dynamicMarginTop || config.xAxis.labelOffset : dynamicMarginTop || config.xAxis.size} textAnchor='middle' fontWeight='bold' fill={config.xAxis.labelColor}>
|
|
463
464
|
{props.label}
|
|
464
465
|
</Text>
|
|
465
466
|
</Group>
|
|
@@ -467,10 +468,9 @@ export default function LinearChart() {
|
|
|
467
468
|
}}
|
|
468
469
|
</AxisBottom>
|
|
469
470
|
)}
|
|
470
|
-
|
|
471
|
-
{config.visualizationType === 'Paired Bar' && (
|
|
471
|
+
{visualizationType === 'Paired Bar' && (
|
|
472
472
|
<>
|
|
473
|
-
<AxisBottom top={yMax} left={Number(
|
|
473
|
+
<AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={runtime.xAxis.type === 'date' ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
|
|
474
474
|
{props => {
|
|
475
475
|
return (
|
|
476
476
|
<Group className='bottom-axis'>
|
|
@@ -479,8 +479,8 @@ export default function LinearChart() {
|
|
|
479
479
|
const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
|
|
480
480
|
return (
|
|
481
481
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
482
|
-
{!
|
|
483
|
-
{!
|
|
482
|
+
{!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
483
|
+
{!runtime.yAxis.hideLabel && (
|
|
484
484
|
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
485
485
|
{formatNumber(tick.value, 'left')}
|
|
486
486
|
</Text>
|
|
@@ -488,20 +488,20 @@ export default function LinearChart() {
|
|
|
488
488
|
</Group>
|
|
489
489
|
)
|
|
490
490
|
})}
|
|
491
|
-
{!
|
|
491
|
+
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
492
492
|
</Group>
|
|
493
493
|
)
|
|
494
494
|
}}
|
|
495
495
|
</AxisBottom>
|
|
496
496
|
<AxisBottom
|
|
497
497
|
top={yMax}
|
|
498
|
-
left={Number(
|
|
499
|
-
label={
|
|
500
|
-
tickFormat={
|
|
498
|
+
left={Number(runtime.yAxis.size)}
|
|
499
|
+
label={runtime.xAxis.label}
|
|
500
|
+
tickFormat={runtime.xAxis.type === 'date' ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
|
|
501
501
|
scale={g2xScale}
|
|
502
502
|
stroke='#333'
|
|
503
503
|
tickStroke='#333'
|
|
504
|
-
numTicks={
|
|
504
|
+
numTicks={runtime.xAxis.numTicks || undefined}
|
|
505
505
|
>
|
|
506
506
|
{props => {
|
|
507
507
|
return (
|
|
@@ -512,8 +512,8 @@ export default function LinearChart() {
|
|
|
512
512
|
const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
|
|
513
513
|
return (
|
|
514
514
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
515
|
-
{!
|
|
516
|
-
{!
|
|
515
|
+
{!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
516
|
+
{!runtime.yAxis.hideLabel && (
|
|
517
517
|
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
518
518
|
{formatNumber(tick.value, 'left')}
|
|
519
519
|
</Text>
|
|
@@ -521,11 +521,11 @@ export default function LinearChart() {
|
|
|
521
521
|
</Group>
|
|
522
522
|
)
|
|
523
523
|
})}
|
|
524
|
-
{!
|
|
524
|
+
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
525
525
|
</Group>
|
|
526
526
|
<Group>
|
|
527
527
|
<Text x={xMax / 2} y={config.xAxis.labelOffset} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
|
|
528
|
-
{
|
|
528
|
+
{runtime.xAxis.label}
|
|
529
529
|
</Text>
|
|
530
530
|
</Group>
|
|
531
531
|
</>
|
|
@@ -534,73 +534,140 @@ export default function LinearChart() {
|
|
|
534
534
|
</AxisBottom>
|
|
535
535
|
</>
|
|
536
536
|
)}
|
|
537
|
-
{
|
|
538
|
-
{
|
|
539
|
-
{
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
537
|
+
{visualizationType === 'Deviation Bar' && <DeviationBar xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
|
|
538
|
+
{visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
|
|
539
|
+
{visualizationType === 'Scatter Plot' && (
|
|
540
|
+
<ScatterPlot
|
|
541
|
+
xScale={xScale}
|
|
542
|
+
yScale={yScale}
|
|
543
|
+
getXAxisData={getXAxisData}
|
|
544
|
+
getYAxisData={getYAxisData}
|
|
545
|
+
xMax={xMax}
|
|
546
|
+
yMax={yMax}
|
|
547
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
548
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
549
|
+
handleTooltipClick={handleTooltipClick}
|
|
550
|
+
tooltipData={tooltipData}
|
|
551
|
+
showTooltip={showTooltip}
|
|
552
|
+
/>
|
|
553
|
+
)}
|
|
554
|
+
{visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
|
|
555
|
+
{(visualizationType === 'Area Chart' || visualizationType === 'Combo') && (
|
|
556
|
+
<AreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
|
|
557
|
+
)}
|
|
558
|
+
{(visualizationType === 'Bar' || visualizationType === 'Combo') && (
|
|
559
|
+
<BarChart
|
|
560
|
+
xScale={xScale}
|
|
561
|
+
yScale={yScale}
|
|
562
|
+
seriesScale={seriesScale}
|
|
563
|
+
xMax={xMax}
|
|
564
|
+
yMax={yMax}
|
|
565
|
+
getXAxisData={getXAxisData}
|
|
566
|
+
getYAxisData={getYAxisData}
|
|
567
|
+
animatedChart={animatedChart}
|
|
568
|
+
visible={animatedChart}
|
|
569
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
570
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
571
|
+
handleTooltipClick={handleTooltipClick}
|
|
572
|
+
tooltipData={tooltipData}
|
|
573
|
+
showTooltip={showTooltip}
|
|
574
|
+
chartRef={svgRef}
|
|
575
|
+
/>
|
|
576
|
+
)}
|
|
577
|
+
{(visualizationType === 'Line' || visualizationType === 'Combo') && (
|
|
578
|
+
<LineChart
|
|
579
|
+
xScale={xScale}
|
|
580
|
+
yScale={yScale}
|
|
581
|
+
getXAxisData={getXAxisData}
|
|
582
|
+
getYAxisData={getYAxisData}
|
|
583
|
+
xMax={xMax}
|
|
584
|
+
yMax={yMax}
|
|
585
|
+
seriesStyle={config.series}
|
|
586
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
587
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
588
|
+
handleTooltipClick={handleTooltipClick}
|
|
589
|
+
tooltipData={tooltipData}
|
|
590
|
+
showTooltip={showTooltip}
|
|
591
|
+
chartRef={svgRef}
|
|
592
|
+
/>
|
|
593
|
+
)}
|
|
594
|
+
{(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
|
|
545
595
|
<Forecasting
|
|
546
|
-
hideTooltip={hideTooltip}
|
|
547
596
|
showTooltip={showTooltip}
|
|
548
597
|
tooltipData={tooltipData}
|
|
549
598
|
xScale={xScale}
|
|
550
599
|
yScale={yScale}
|
|
551
600
|
width={xMax}
|
|
601
|
+
le
|
|
552
602
|
height={yMax}
|
|
553
603
|
xScaleNoPadding={xScaleNoPadding}
|
|
554
604
|
chartRef={svgRef}
|
|
555
605
|
getXValueFromCoordinate={getXValueFromCoordinate}
|
|
556
606
|
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
557
607
|
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
608
|
+
isBrush={false}
|
|
558
609
|
/>
|
|
559
610
|
)}
|
|
560
|
-
|
|
561
611
|
{/* y anchors */}
|
|
562
612
|
{config.yAxis.anchors &&
|
|
563
613
|
config.yAxis.anchors.map(anchor => {
|
|
564
|
-
return <Line strokeDasharray={handleLineType(anchor.lineStyle)} stroke='rgba(0,0,0,1)' className='customAnchor' from={{ x: 0 + config.yAxis.size, y: yScale(anchor.value) }} to={{ x: xMax, y: yScale(anchor.value) }} display={
|
|
614
|
+
return <Line strokeDasharray={handleLineType(anchor.lineStyle)} stroke='rgba(0,0,0,1)' className='customAnchor' from={{ x: 0 + config.yAxis.size, y: yScale(anchor.value) }} to={{ x: xMax, y: yScale(anchor.value) }} display={runtime.horizontal ? 'none' : 'block'} />
|
|
565
615
|
})}
|
|
566
|
-
|
|
616
|
+
{visualizationType === 'Forest Plot' && (
|
|
617
|
+
<ForestPlot
|
|
618
|
+
xScale={xScale}
|
|
619
|
+
yScale={yScale}
|
|
620
|
+
seriesScale={seriesScale}
|
|
621
|
+
width={xMax}
|
|
622
|
+
height={yMax}
|
|
623
|
+
maxWidth={width}
|
|
624
|
+
maxHeight={height}
|
|
625
|
+
getXAxisData={getXAxisData}
|
|
626
|
+
getYAxisData={getYAxisData}
|
|
627
|
+
animatedChart={animatedChart}
|
|
628
|
+
visible={animatedChart}
|
|
629
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
630
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
631
|
+
handleTooltipClick={handleTooltipClick}
|
|
632
|
+
tooltipData={tooltipData}
|
|
633
|
+
showTooltip={showTooltip}
|
|
634
|
+
chartRef={svgRef}
|
|
635
|
+
config={config}
|
|
636
|
+
/>
|
|
637
|
+
)}
|
|
567
638
|
{/* Line chart */}
|
|
568
639
|
{/* TODO: Make this just line or combo? */}
|
|
569
|
-
{
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
config.visualizationType !== 'Deviation Bar' &&
|
|
575
|
-
config.visualizationType !== 'Forecasting' && (
|
|
576
|
-
<>
|
|
577
|
-
<LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
|
|
578
|
-
</>
|
|
579
|
-
)}
|
|
580
|
-
|
|
640
|
+
{visualizationType !== 'Bar' && visualizationType !== 'Paired Bar' && visualizationType !== 'Box Plot' && visualizationType !== 'Area Chart' && visualizationType !== 'Scatter Plot' && visualizationType !== 'Deviation Bar' && visualizationType !== 'Forecasting' && (
|
|
641
|
+
<>
|
|
642
|
+
<LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
|
|
643
|
+
</>
|
|
644
|
+
)}
|
|
581
645
|
{/* y anchors */}
|
|
582
646
|
{config.yAxis.anchors &&
|
|
583
|
-
config.yAxis.anchors.map(anchor => {
|
|
647
|
+
config.yAxis.anchors.map((anchor, index) => {
|
|
584
648
|
let anchorPosition = yScale(anchor.value)
|
|
585
|
-
|
|
586
|
-
const
|
|
649
|
+
// have to move up
|
|
650
|
+
// const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
651
|
+
if (!anchor.value) return
|
|
652
|
+
const middleOffset = orientation === 'horizontal' && visualizationType === 'Bar' ? config.barHeight / 4 : 0
|
|
653
|
+
|
|
654
|
+
if (!anchorPosition) return
|
|
587
655
|
|
|
588
656
|
return (
|
|
589
657
|
// prettier-ignore
|
|
590
658
|
<Line
|
|
591
|
-
key={anchor.value}
|
|
659
|
+
key={`yAxis-${anchor.value}--${index}`}
|
|
592
660
|
strokeDasharray={handleLineType(anchor.lineStyle)}
|
|
593
661
|
stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
594
662
|
className='anchor-y'
|
|
595
663
|
from={{ x: 0 + padding, y: anchorPosition - middleOffset}}
|
|
596
|
-
to={{ x: width, y: anchorPosition - middleOffset }}
|
|
664
|
+
to={{ x: width - config.yAxis.rightAxisSize, y: anchorPosition - middleOffset }}
|
|
597
665
|
/>
|
|
598
666
|
)
|
|
599
667
|
})}
|
|
600
|
-
|
|
601
668
|
{/* x anchors */}
|
|
602
669
|
{config.xAxis.anchors &&
|
|
603
|
-
config.xAxis.anchors.map(anchor => {
|
|
670
|
+
config.xAxis.anchors.map((anchor, index) => {
|
|
604
671
|
let newX = xAxis
|
|
605
672
|
if (orientation === 'horizontal') {
|
|
606
673
|
newX = yAxis
|
|
@@ -608,12 +675,15 @@ export default function LinearChart() {
|
|
|
608
675
|
|
|
609
676
|
let anchorPosition = newX.type === 'date' ? xScale(parseDate(anchor.value, false)) : xScale(anchor.value)
|
|
610
677
|
|
|
611
|
-
|
|
678
|
+
// have to move up
|
|
679
|
+
// const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
680
|
+
|
|
681
|
+
if (!anchorPosition) return
|
|
612
682
|
|
|
613
683
|
return (
|
|
614
684
|
// prettier-ignore
|
|
615
685
|
<Line
|
|
616
|
-
key={anchor.value}
|
|
686
|
+
key={`xAxis-${anchor.value}--${index}`}
|
|
617
687
|
strokeDasharray={handleLineType(anchor.lineStyle)}
|
|
618
688
|
stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
619
689
|
fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
@@ -623,8 +693,25 @@ export default function LinearChart() {
|
|
|
623
693
|
/>
|
|
624
694
|
)
|
|
625
695
|
})}
|
|
696
|
+
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
|
|
697
|
+
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
698
|
+
<Line from={{ x: tooltipData.dataXPosition - 10, y: 0 }} to={{ x: tooltipData.dataXPosition - 10, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
|
|
699
|
+
</Group>
|
|
700
|
+
)}
|
|
701
|
+
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.horizontalHoverLine && (
|
|
702
|
+
<Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
|
|
703
|
+
<Line from={{ x: 0, y: tooltipData.dataYPosition }} to={{ x: xMax, y: tooltipData.dataYPosition }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='horizontal-tooltip-line' />
|
|
704
|
+
</Group>
|
|
705
|
+
)}
|
|
626
706
|
</svg>
|
|
627
|
-
|
|
707
|
+
{tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
|
|
708
|
+
<TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} style={tooltipStyles(tooltipData)} width={width}>
|
|
709
|
+
<ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
|
|
710
|
+
</TooltipWithBounds>
|
|
711
|
+
)}
|
|
712
|
+
{(config.orientation === 'horizontal' || config.visualizationType === 'Scatter Plot' || config.visualizationType === 'Box Plot') && (
|
|
713
|
+
<ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }} />
|
|
714
|
+
)}
|
|
628
715
|
<div className='animation-trigger' ref={triggerRef} />
|
|
629
716
|
</ErrorBoundary>
|
|
630
717
|
)
|