@cdc/chart 4.24.5 → 4.24.9-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.js +50526 -42181
- package/examples/cases-year.json +13379 -0
- package/examples/feature/annotations/index.json +542 -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/examples/xaxis.json +493 -0
- package/index.html +20 -10
- package/package.json +5 -4
- package/src/CdcChart.tsx +461 -172
- package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
- package/src/_stories/Chart.stories.tsx +18 -171
- package/src/_stories/ChartAnnotation.stories.tsx +32 -0
- package/src/_stories/_mock/annotation_category_mock.json +473 -0
- package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
- package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
- package/src/_stories/_mock/legend.gradient_mock.json +236 -0
- package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
- package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
- package/src/_stories/_mock/lollipop.json +171 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
- package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
- package/src/components/Annotations/components/AnnotationList.tsx +42 -0
- package/src/components/Annotations/components/findNearestDatum.ts +138 -0
- package/src/components/Annotations/components/helpers/index.tsx +46 -0
- package/src/components/Annotations/index.tsx +13 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
- package/src/components/AreaChart/components/AreaChart.jsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +145 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
- package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
- package/src/components/BarChart/helpers/index.ts +91 -0
- package/src/components/BrushChart.tsx +205 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/panels.scss +4 -0
- package/src/components/EditorPanel/editor-panel.scss +35 -3
- package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
- package/src/components/Legend/Legend.Component.tsx +185 -194
- package/src/components/Legend/Legend.Suppression.tsx +146 -0
- package/src/components/Legend/Legend.tsx +21 -5
- package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
- package/src/components/Legend/helpers/index.ts +35 -0
- package/src/components/LegendWrapper.tsx +26 -0
- package/src/components/LineChart/LineChartProps.ts +1 -15
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +57 -8
- package/src/components/LineChart/helpers.ts +72 -14
- package/src/components/LineChart/index.tsx +117 -42
- package/src/components/LinearChart.tsx +1366 -0
- package/src/components/PairedBarChart.jsx +9 -9
- package/src/components/PieChart/PieChart.tsx +75 -18
- package/src/components/Sankey/index.tsx +89 -30
- package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
- package/src/components/Sparkline/components/SparkLine.tsx +2 -2
- package/src/components/ZoomBrush.tsx +90 -44
- package/src/data/initial-state.js +25 -7
- package/src/helpers/handleChartTabbing.ts +8 -0
- package/src/helpers/isConvertLineToBarGraph.ts +4 -0
- package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
- package/src/hooks/useColorScale.ts +1 -1
- package/src/hooks/useLegendClasses.ts +68 -0
- package/src/hooks/useMinMax.ts +12 -7
- package/src/hooks/useScales.ts +58 -26
- package/src/hooks/useTooltip.tsx +135 -25
- package/src/scss/DataTable.scss +2 -1
- package/src/scss/main.scss +128 -28
- package/src/types/ChartConfig.ts +83 -10
- package/src/types/ChartContext.ts +14 -4
- package/tests-examples/helpers/testZeroValue.test.ts +30 -0
- package/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
- package/src/components/LinearChart.jsx +0 -774
- package/src/helpers/filterData.ts +0 -18
- package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
- package/src/hooks/useLegendClasses.js +0 -31
- /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
|
@@ -1,774 +0,0 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useRef, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
// Libraries
|
|
4
|
-
import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
|
|
5
|
-
import { Group } from '@visx/group'
|
|
6
|
-
import { Line, Bar } from '@visx/shape'
|
|
7
|
-
import { Text } from '@visx/text'
|
|
8
|
-
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
9
|
-
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
|
-
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
11
|
-
|
|
12
|
-
// CDC Components
|
|
13
|
-
import { AreaChart, AreaChartStacked } from './AreaChart'
|
|
14
|
-
import BarChart from './BarChart'
|
|
15
|
-
import ConfigContext from '../ConfigContext'
|
|
16
|
-
import BoxPlot from './BoxPlot'
|
|
17
|
-
import ScatterPlot from './ScatterPlot'
|
|
18
|
-
import DeviationBar from './DeviationBar'
|
|
19
|
-
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
20
|
-
import Forecasting from './Forecasting'
|
|
21
|
-
import LineChart from './LineChart'
|
|
22
|
-
import ForestPlot from './ForestPlot'
|
|
23
|
-
import PairedBarChart from './PairedBarChart'
|
|
24
|
-
import useIntersectionObserver from './../hooks/useIntersectionObserver'
|
|
25
|
-
import Regions from './Regions'
|
|
26
|
-
|
|
27
|
-
// Hooks
|
|
28
|
-
import useMinMax from '../hooks/useMinMax'
|
|
29
|
-
import useReduceData from '../hooks/useReduceData'
|
|
30
|
-
import useRightAxis from '../hooks/useRightAxis'
|
|
31
|
-
import useScales, { getTickValues } from '../hooks/useScales'
|
|
32
|
-
import useTopAxis from '../hooks/useTopAxis'
|
|
33
|
-
import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
|
|
34
|
-
import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
35
|
-
|
|
36
|
-
// styles
|
|
37
|
-
import ZoomBrush from './ZoomBrush'
|
|
38
|
-
|
|
39
|
-
const LinearChart = props => {
|
|
40
|
-
const { transformedData: data, tableData, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, getTextWidth, brushConfig } = useContext(ConfigContext)
|
|
41
|
-
// todo: start destructuring this file for conciseness
|
|
42
|
-
const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
|
|
43
|
-
|
|
44
|
-
// configure width
|
|
45
|
-
let [width] = dimensions
|
|
46
|
-
if (config && config.legend && !config.legend.hide && config.legend.position !== 'bottom' && ['lg', 'md'].includes(currentViewport)) {
|
|
47
|
-
width = width * 0.73
|
|
48
|
-
}
|
|
49
|
-
// configure height , yMax, xMax
|
|
50
|
-
const { horizontal: heightHorizontal } = config.heights
|
|
51
|
-
const isHorizontal = orientation === 'horizontal' || config.visualizationType === 'Forest Plot'
|
|
52
|
-
const shouldAbbreviate = true
|
|
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.padding || 0))
|
|
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
|
-
width = dimensions[0]
|
|
61
|
-
}
|
|
62
|
-
if (config.brush?.active) {
|
|
63
|
-
height = height + config.brush?.height
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// hooks % states
|
|
67
|
-
const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
|
|
68
|
-
const { visSupportsReactTooltip } = useEditorPermissions()
|
|
69
|
-
const { hasTopAxis } = useTopAxis(config)
|
|
70
|
-
const [animatedChart, setAnimatedChart] = useState(false)
|
|
71
|
-
const [point, setPoint] = useState({ x: 0, y: 0 })
|
|
72
|
-
|
|
73
|
-
// refs
|
|
74
|
-
const triggerRef = useRef()
|
|
75
|
-
const svgRef = useRef()
|
|
76
|
-
const dataRef = useIntersectionObserver(triggerRef, {
|
|
77
|
-
freezeOnceVisible: false
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
// getters & functions
|
|
81
|
-
const getXAxisData = d => (isDateScale(config.runtime.xAxis) ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
82
|
-
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
83
|
-
const xAxisDataMapped = config.brush.active && brushConfig.data?.length ? brushConfig.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
|
|
84
|
-
const section = config.orientation === 'horizontal' || config.visualizationType === 'Forest Plot' ? 'yAxis' : 'xAxis'
|
|
85
|
-
const properties = { data, tableData, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
|
|
86
|
-
const { min, max, leftMax, rightMax } = useMinMax(properties)
|
|
87
|
-
const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
|
|
88
|
-
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush } = useScales({ ...properties, min, max, leftMax, rightMax, dimensions })
|
|
89
|
-
|
|
90
|
-
// sets the portal x/y for where tooltips should appear on the page.
|
|
91
|
-
const [chartPosition, setChartPosition] = useState(null)
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
setChartPosition(svgRef?.current?.getBoundingClientRect())
|
|
94
|
-
}, [svgRef, config.legend])
|
|
95
|
-
|
|
96
|
-
const handleLeftTickFormatting = (tick, index) => {
|
|
97
|
-
if (config.useLogScale && tick === 0.1) {
|
|
98
|
-
//when logarithmic scale applied change value of first tick
|
|
99
|
-
tick = 0
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (config.data && !config.data[index] && visualizationType === 'Forest Plot') return
|
|
103
|
-
if (config.visualizationType === 'Forest Plot') return config.data[index][config.xAxis.dataKey]
|
|
104
|
-
if (isDateScale(runtime.yAxis)) return formatDate(parseDate(tick))
|
|
105
|
-
if (orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
106
|
-
return tick
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const handleBottomTickFormatting = tick => {
|
|
110
|
-
if (config.useLogScale && tick === 0.1) {
|
|
111
|
-
// when logarithmic scale applied change value FIRST of tick
|
|
112
|
-
tick = 0
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (isDateScale(runtime.xAxis) && config.visualizationType !== 'Forest Plot') return formatDate(tick)
|
|
116
|
-
if (orientation === 'horizontal' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
117
|
-
if (config.xAxis.type === 'continuous' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
118
|
-
if (config.visualizationType === 'Forest Plot') return formatNumber(tick, 'left', config.dataFormat.abbreviated, config.runtime.xAxis.prefix, config.runtime.xAxis.suffix, Number(config.dataFormat.roundTo))
|
|
119
|
-
return tick
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const countNumOfTicks = axis => {
|
|
123
|
-
const { numTicks } = runtime[axis]
|
|
124
|
-
let tickCount = undefined
|
|
125
|
-
|
|
126
|
-
if (axis === 'yAxis') {
|
|
127
|
-
tickCount = isHorizontal && !numTicks ? data.length : isHorizontal && numTicks ? numTicks : !isHorizontal && !numTicks ? undefined : !isHorizontal && numTicks && numTicks
|
|
128
|
-
// to fix edge case of small numbers with decimals
|
|
129
|
-
if (tickCount === undefined && !config.dataFormat.roundTo) {
|
|
130
|
-
// then it is set to Auto
|
|
131
|
-
if (Number(max) <= 3) {
|
|
132
|
-
tickCount = 2
|
|
133
|
-
} else {
|
|
134
|
-
tickCount = 4 // same default as standalone components
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (Number(tickCount) > Number(max)) {
|
|
138
|
-
// cap it and round it so its an integer
|
|
139
|
-
tickCount = Number(min) < 0 ? Math.round(max) * 2 : Math.round(max)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (axis === 'xAxis') {
|
|
144
|
-
tickCount = isHorizontal && !numTicks ? undefined : isHorizontal && numTicks ? numTicks : !isHorizontal && !numTicks ? undefined : !isHorizontal && numTicks && numTicks
|
|
145
|
-
if (isHorizontal && tickCount === undefined && !config.dataFormat.roundTo) {
|
|
146
|
-
// then it is set to Auto
|
|
147
|
-
// - check for small numbers situation
|
|
148
|
-
if (max <= 3) {
|
|
149
|
-
tickCount = 2
|
|
150
|
-
} else {
|
|
151
|
-
tickCount = 4 // same default as standalone components
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (config.visualizationType === 'Forest Plot') {
|
|
156
|
-
tickCount = config.yAxis.numTicks !== '' ? config.yAxis.numTicks : 4
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return tickCount
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Tooltip Helpers
|
|
164
|
-
const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip()
|
|
165
|
-
|
|
166
|
-
// prettier-ignore
|
|
167
|
-
const {
|
|
168
|
-
handleTooltipMouseOver,
|
|
169
|
-
handleTooltipClick,
|
|
170
|
-
handleTooltipMouseOff,
|
|
171
|
-
tooltipStyles,
|
|
172
|
-
TooltipListItem,
|
|
173
|
-
getXValueFromCoordinateDate,
|
|
174
|
-
getXValueFromCoordinate
|
|
175
|
-
} = useCoveTooltip({
|
|
176
|
-
xScale,
|
|
177
|
-
yScale,
|
|
178
|
-
showTooltip,
|
|
179
|
-
hideTooltip
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
// Make sure the chart is visible if in the editor
|
|
183
|
-
/* eslint-disable react-hooks/exhaustive-deps */
|
|
184
|
-
useEffect(() => {
|
|
185
|
-
const element = document.querySelector('.isEditor')
|
|
186
|
-
if (element) {
|
|
187
|
-
// parent element is visible
|
|
188
|
-
setAnimatedChart(prevState => true)
|
|
189
|
-
}
|
|
190
|
-
}) /* eslint-disable-line */
|
|
191
|
-
|
|
192
|
-
// If the chart is in view, set to animate if it has not already played
|
|
193
|
-
useEffect(() => {
|
|
194
|
-
if (dataRef?.isIntersecting === true && config.animate) {
|
|
195
|
-
setTimeout(() => {
|
|
196
|
-
setAnimatedChart(prevState => true)
|
|
197
|
-
}, 500)
|
|
198
|
-
}
|
|
199
|
-
}, [dataRef?.isIntersecting, config.animate])
|
|
200
|
-
|
|
201
|
-
const chartHasTooltipGuides = () => {
|
|
202
|
-
const { visualizationType } = config
|
|
203
|
-
if (visualizationType === 'Combo' && runtime.forecastingSeriesKeys > 0) return true
|
|
204
|
-
if (visualizationType === 'Area Chart') return true
|
|
205
|
-
if (visualizationType === 'Line') return true
|
|
206
|
-
if (visualizationType === 'Bar') return true
|
|
207
|
-
return false
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const padding = orientation === 'horizontal' ? Number(config.xAxis.padding) : Number(config.yAxis.size)
|
|
211
|
-
const fontSize = { small: 16, medium: 18, large: 20 }
|
|
212
|
-
|
|
213
|
-
const handleNumTicks = () => {
|
|
214
|
-
// On forest plots we need to return every "study" or y axis value.
|
|
215
|
-
if (config.visualizationType === 'Forest Plot') return config.data.length
|
|
216
|
-
return countNumOfTicks('yAxis')
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const onMouseMove = event => {
|
|
220
|
-
const svgRect = event.currentTarget.getBoundingClientRect()
|
|
221
|
-
const x = event.clientX - svgRect.left
|
|
222
|
-
const y = event.clientY - svgRect.top
|
|
223
|
-
|
|
224
|
-
setPoint({
|
|
225
|
-
x,
|
|
226
|
-
y
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const generatePairedBarAxis = () => {
|
|
231
|
-
let axisMaxHeight = 40;
|
|
232
|
-
|
|
233
|
-
return <>
|
|
234
|
-
<AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={isDateScale(runtime.xAxis) ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
|
|
235
|
-
{props => {
|
|
236
|
-
return (
|
|
237
|
-
<Group className='bottom-axis'>
|
|
238
|
-
{props.ticks.map((tick, i) => {
|
|
239
|
-
const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
|
|
240
|
-
const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
|
|
241
|
-
|
|
242
|
-
const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
243
|
-
const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25;
|
|
244
|
-
|
|
245
|
-
if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
249
|
-
{!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
250
|
-
{!runtime.yAxis.hideLabel && (
|
|
251
|
-
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
252
|
-
{formatNumber(tick.value, 'left')}
|
|
253
|
-
</Text>
|
|
254
|
-
)}
|
|
255
|
-
</Group>
|
|
256
|
-
)
|
|
257
|
-
})}
|
|
258
|
-
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
259
|
-
</Group>
|
|
260
|
-
)
|
|
261
|
-
}}
|
|
262
|
-
</AxisBottom>
|
|
263
|
-
<AxisBottom
|
|
264
|
-
top={yMax}
|
|
265
|
-
left={Number(runtime.yAxis.size)}
|
|
266
|
-
label={runtime.xAxis.label}
|
|
267
|
-
tickFormat={isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
|
|
268
|
-
scale={g2xScale}
|
|
269
|
-
stroke='#333'
|
|
270
|
-
tickStroke='#333'
|
|
271
|
-
numTicks={runtime.xAxis.numTicks || undefined}
|
|
272
|
-
>
|
|
273
|
-
{props => {
|
|
274
|
-
return (
|
|
275
|
-
<>
|
|
276
|
-
<Group className='bottom-axis'>
|
|
277
|
-
{props.ticks.map((tick, i) => {
|
|
278
|
-
const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
|
|
279
|
-
const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
|
|
280
|
-
|
|
281
|
-
const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
282
|
-
const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25;
|
|
283
|
-
|
|
284
|
-
if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
|
|
285
|
-
|
|
286
|
-
return (
|
|
287
|
-
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
288
|
-
{!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
289
|
-
{!runtime.yAxis.hideLabel && (
|
|
290
|
-
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
291
|
-
{formatNumber(tick.value, 'left')}
|
|
292
|
-
</Text>
|
|
293
|
-
)}
|
|
294
|
-
</Group>
|
|
295
|
-
)
|
|
296
|
-
})}
|
|
297
|
-
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
298
|
-
</Group>
|
|
299
|
-
<Group>
|
|
300
|
-
<Text x={xMax / 2} y={axisMaxHeight + 20} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
|
|
301
|
-
{runtime.xAxis.label}
|
|
302
|
-
</Text>
|
|
303
|
-
</Group>
|
|
304
|
-
{svgRef.current ? svgRef.current.setAttribute('height', (Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0)) + 'px') : ''}
|
|
305
|
-
</>
|
|
306
|
-
)
|
|
307
|
-
}}
|
|
308
|
-
</AxisBottom>
|
|
309
|
-
</>
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return isNaN(width) ? (
|
|
313
|
-
<React.Fragment></React.Fragment>
|
|
314
|
-
) : (
|
|
315
|
-
<ErrorBoundary component='LinearChart'>
|
|
316
|
-
{/* ! Notice - div needed for tooltip boundaries (flip/flop) */}
|
|
317
|
-
<div style={{ width: `${width}px`, overflow: 'visible' }} className='tooltip-boundary'>
|
|
318
|
-
<svg
|
|
319
|
-
// onMouseLeave={() => setPoint(null)}
|
|
320
|
-
onMouseMove={onMouseMove}
|
|
321
|
-
width={'100%'}
|
|
322
|
-
height={height}
|
|
323
|
-
className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'}`}
|
|
324
|
-
role='img'
|
|
325
|
-
aria-label={handleChartAriaLabels(config)}
|
|
326
|
-
ref={svgRef}
|
|
327
|
-
style={{ overflow: 'visible' }}
|
|
328
|
-
>
|
|
329
|
-
<Bar width={width} height={height} fill={'transparent'}></Bar> {/* Highlighted regions */}
|
|
330
|
-
{/* Y axis */}
|
|
331
|
-
{!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
|
|
332
|
-
<AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.label || runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
|
|
333
|
-
{props => {
|
|
334
|
-
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
335
|
-
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
336
|
-
return (
|
|
337
|
-
<Group className='left-axis'>
|
|
338
|
-
{props.ticks.map((tick, i) => {
|
|
339
|
-
const minY = props.ticks[0].to.y
|
|
340
|
-
const barMinHeight = 15 // 15 is the min height for bars by default
|
|
341
|
-
const showTicks = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
342
|
-
const tickLength = showTicks === 'block' ? 7 : 0
|
|
343
|
-
const to = { x: tick.to.x - tickLength, y: tick.to.y }
|
|
344
|
-
|
|
345
|
-
return (
|
|
346
|
-
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
347
|
-
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={orientation === 'horizontal' ? 'none' : 'block'} />}
|
|
348
|
-
|
|
349
|
-
{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)' /> : ''}
|
|
350
|
-
|
|
351
|
-
{orientation === 'horizontal' && visualizationSubType !== 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
|
|
352
|
-
<Text
|
|
353
|
-
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})`}
|
|
354
|
-
verticalAnchor={'start'}
|
|
355
|
-
textAnchor={'end'}
|
|
356
|
-
>
|
|
357
|
-
{tick.formattedValue}
|
|
358
|
-
</Text>
|
|
359
|
-
)}
|
|
360
|
-
|
|
361
|
-
{orientation === 'horizontal' && visualizationSubType === 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
|
|
362
|
-
<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'}>
|
|
363
|
-
{tick.formattedValue}
|
|
364
|
-
</Text>
|
|
365
|
-
)}
|
|
366
|
-
|
|
367
|
-
{orientation === 'horizontal' && visualizationType === 'Paired Bar' && !config.yAxis.hideLabel && (
|
|
368
|
-
<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'>
|
|
369
|
-
{tick.formattedValue}
|
|
370
|
-
</Text>
|
|
371
|
-
)}
|
|
372
|
-
{orientation === 'horizontal' && visualizationType === 'Deviation Bar' && !config.yAxis.hideLabel && (
|
|
373
|
-
<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'>
|
|
374
|
-
{tick.formattedValue}
|
|
375
|
-
</Text>
|
|
376
|
-
)}
|
|
377
|
-
|
|
378
|
-
{orientation === 'vertical' && visualizationType !== 'Paired Bar' && !config.yAxis.hideLabel && (
|
|
379
|
-
<Text
|
|
380
|
-
display={config.useLogScale ? showTicks : 'block'}
|
|
381
|
-
dx={config.useLogScale ? -6 : 0}
|
|
382
|
-
x={config.runtime.horizontal ? tick.from.x + 2 : tick.to.x}
|
|
383
|
-
y={tick.to.y + (config.runtime.horizontal ? horizontalTickOffset : 0)}
|
|
384
|
-
angle={-Number(config.yAxis.tickRotation) || 0}
|
|
385
|
-
verticalAnchor={config.runtime.horizontal ? 'start' : 'middle'}
|
|
386
|
-
textAnchor={config.runtime.horizontal ? 'start' : 'end'}
|
|
387
|
-
fill={config.yAxis.tickLabelColor}
|
|
388
|
-
>
|
|
389
|
-
{tick.formattedValue}
|
|
390
|
-
</Text>
|
|
391
|
-
)}
|
|
392
|
-
</Group>
|
|
393
|
-
)
|
|
394
|
-
})}
|
|
395
|
-
{!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={runtime.horizontal ? { x: 0, y: config.visualizationType === 'Forest Plot' ? height : Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
|
|
396
|
-
{yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
|
|
397
|
-
{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} />}
|
|
398
|
-
<Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
|
|
399
|
-
{props.label}
|
|
400
|
-
</Text>
|
|
401
|
-
</Group>
|
|
402
|
-
)
|
|
403
|
-
}}
|
|
404
|
-
</AxisLeft>
|
|
405
|
-
)}
|
|
406
|
-
{/* Right Axis */}
|
|
407
|
-
{hasRightAxis && (
|
|
408
|
-
<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}>
|
|
409
|
-
{props => {
|
|
410
|
-
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
411
|
-
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
412
|
-
return (
|
|
413
|
-
<Group className='right-axis'>
|
|
414
|
-
{props.ticks.map((tick, i) => {
|
|
415
|
-
return (
|
|
416
|
-
<Group key={`vx-tick-${tick.value}-${i}`} className='vx-axis-tick'>
|
|
417
|
-
{!runtime.yAxis.rightHideTicks && <Line from={tick.from} to={tick.to} display={runtime.horizontal ? 'none' : 'block'} stroke={config.yAxis.rightAxisTickColor} />}
|
|
418
|
-
|
|
419
|
-
{runtime.yAxis.rightGridLines ? <Line from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
|
|
420
|
-
|
|
421
|
-
{!config.yAxis.rightHideLabel && (
|
|
422
|
-
<Text x={tick.to.x} y={tick.to.y + (runtime.horizontal ? horizontalTickOffset : 0)} verticalAnchor={runtime.horizontal ? 'start' : 'middle'} textAnchor={'start'} fill={config.yAxis.rightAxisTickLabelColor}>
|
|
423
|
-
{tick.formattedValue}
|
|
424
|
-
</Text>
|
|
425
|
-
)}
|
|
426
|
-
</Group>
|
|
427
|
-
)
|
|
428
|
-
})}
|
|
429
|
-
{!config.yAxis.rightHideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
430
|
-
<Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${config.yAxis.rightLabelOffsetSize ? config.yAxis.rightLabelOffsetSize : 0}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.rightAxisLabelColor}>
|
|
431
|
-
{props.label}
|
|
432
|
-
</Text>
|
|
433
|
-
</Group>
|
|
434
|
-
)
|
|
435
|
-
}}
|
|
436
|
-
</AxisRight>
|
|
437
|
-
)}
|
|
438
|
-
{hasTopAxis && config.topAxis.hasLine && (
|
|
439
|
-
<AxisTop
|
|
440
|
-
stroke='#333'
|
|
441
|
-
left={Number(runtime.yAxis.size)}
|
|
442
|
-
scale={xScale}
|
|
443
|
-
hideTicks
|
|
444
|
-
hideZero
|
|
445
|
-
tickLabelProps={() => ({
|
|
446
|
-
fill: 'transparent'
|
|
447
|
-
})}
|
|
448
|
-
/>
|
|
449
|
-
)}
|
|
450
|
-
{/* X axis */}
|
|
451
|
-
{visualizationType !== 'Paired Bar' && visualizationType !== 'Spark Line' && (
|
|
452
|
-
<AxisBottom
|
|
453
|
-
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax}
|
|
454
|
-
left={config.visualizationType !== 'Forest Plot' ? Number(runtime.yAxis.size) : 0}
|
|
455
|
-
label={config[section].label}
|
|
456
|
-
tickFormat={handleBottomTickFormatting}
|
|
457
|
-
scale={xScale}
|
|
458
|
-
stroke='#333'
|
|
459
|
-
numTicks={countNumOfTicks('xAxis')}
|
|
460
|
-
tickStroke='#333'
|
|
461
|
-
tickValues={config.xAxis.manual ? getTickValues(xAxisDataMapped, xScale, config.xAxis.type === 'date-time' ? countNumOfTicks('xAxis') : config.xAxis.manualStep) : undefined}
|
|
462
|
-
>
|
|
463
|
-
{props => {
|
|
464
|
-
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
|
|
465
|
-
const containsMultipleWords = inputString => /\s/.test(inputString)
|
|
466
|
-
const ismultiLabel = props.ticks.some(tick => containsMultipleWords(tick.value))
|
|
467
|
-
|
|
468
|
-
// Calculate sumOfTickWidth here, before map function
|
|
469
|
-
const defaultTickLength = 8
|
|
470
|
-
const tickWidthMax = Math.max(...props.ticks.map(tick => getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)))
|
|
471
|
-
// const marginTop = 20 // moved to top bc need for yMax calcs
|
|
472
|
-
const accumulator = ismultiLabel ? 180 : 100
|
|
473
|
-
|
|
474
|
-
const textWidths = props.ticks.map(tick => getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`))
|
|
475
|
-
const sumOfTickWidth = textWidths.reduce((a, b) => a + b, accumulator)
|
|
476
|
-
const spaceBetweenEachTick = (xMax - sumOfTickWidth) / (props.ticks.length - 1)
|
|
477
|
-
|
|
478
|
-
// Check if ticks are overlapping
|
|
479
|
-
// Determine the position of each tick
|
|
480
|
-
let positions = [0] // The first tick is at position 0
|
|
481
|
-
for (let i = 1; i < textWidths.length; i++) {
|
|
482
|
-
// The position of each subsequent tick is the position of the previous tick
|
|
483
|
-
// plus the width of the previous tick and the space
|
|
484
|
-
positions[i] = positions[i - 1] + textWidths[i - 1] + spaceBetweenEachTick
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Check if ticks are overlapping
|
|
488
|
-
let areTicksTouching = false
|
|
489
|
-
textWidths.forEach((_, i) => {
|
|
490
|
-
if (positions[i] + textWidths[i] > positions[i + 1]) {
|
|
491
|
-
areTicksTouching = true
|
|
492
|
-
return
|
|
493
|
-
}
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
const dynamicMarginTop = areTicksTouching && config.isResponsiveTicks ? tickWidthMax + defaultTickLength + 20 : 0
|
|
497
|
-
const rotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
|
|
498
|
-
|
|
499
|
-
config.dynamicMarginTop = dynamicMarginTop
|
|
500
|
-
config.xAxis.tickWidthMax = tickWidthMax
|
|
501
|
-
|
|
502
|
-
let axisMaxHeight = 40;
|
|
503
|
-
|
|
504
|
-
const axisContents = <Group className='bottom-axis' width={dimensions[0]}>
|
|
505
|
-
{props.ticks.map((tick, i, propsTicks) => {
|
|
506
|
-
// when using LogScale show major ticks values only
|
|
507
|
-
const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
508
|
-
const tickLength = showTick === 'block' ? 16 : defaultTickLength
|
|
509
|
-
const to = { x: tick.to.x, y: tickLength }
|
|
510
|
-
const textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
511
|
-
const limitedWidth = 100 / propsTicks.length
|
|
512
|
-
//reset rotations by updating config
|
|
513
|
-
config.yAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'horizontal' ? 0 : config.yAxis.tickRotation
|
|
514
|
-
config.xAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'vertical' ? 0 : config.xAxis.tickRotation
|
|
515
|
-
//configure rotation
|
|
516
|
-
|
|
517
|
-
const tickRotation = config.isResponsiveTicks && areTicksTouching ? -Number(config.xAxis.maxTickRotation) || -90 : -Number(config.runtime.xAxis.tickRotation)
|
|
518
|
-
|
|
519
|
-
const axisHeight = textWidth * Math.sin(tickRotation * -1 * (Math.PI / 180)) + 25;
|
|
520
|
-
|
|
521
|
-
if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
|
|
522
|
-
|
|
523
|
-
return (
|
|
524
|
-
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
525
|
-
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' && config.useLogScale ? 1.3 : 1} />}
|
|
526
|
-
{!config.xAxis.hideLabel && (
|
|
527
|
-
<Text
|
|
528
|
-
dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
|
|
529
|
-
display={config.orientation === 'horizontal' && config.useLogScale ? showTick : 'block'}
|
|
530
|
-
x={tick.to.x}
|
|
531
|
-
y={tick.to.y}
|
|
532
|
-
angle={tickRotation}
|
|
533
|
-
verticalAnchor={tickRotation < -50 ? 'middle' : 'start'}
|
|
534
|
-
textAnchor={tickRotation ? 'end' : 'middle'}
|
|
535
|
-
width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : undefined}
|
|
536
|
-
fill={config.xAxis.tickLabelColor}
|
|
537
|
-
>
|
|
538
|
-
{tick.formattedValue}
|
|
539
|
-
</Text>
|
|
540
|
-
)}
|
|
541
|
-
</Group>
|
|
542
|
-
)
|
|
543
|
-
})}
|
|
544
|
-
{!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
545
|
-
<Text
|
|
546
|
-
x={axisCenter}
|
|
547
|
-
y={axisMaxHeight + 20}
|
|
548
|
-
textAnchor='middle'
|
|
549
|
-
verticalAnchor='start'
|
|
550
|
-
fontWeight='bold'
|
|
551
|
-
fill={config.xAxis.labelColor}
|
|
552
|
-
>
|
|
553
|
-
{props.label}
|
|
554
|
-
</Text>
|
|
555
|
-
</Group>
|
|
556
|
-
|
|
557
|
-
if(svgRef.current) svgRef.current.setAttribute('height',(Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0)) + 'px')
|
|
558
|
-
|
|
559
|
-
return axisContents
|
|
560
|
-
}}
|
|
561
|
-
</AxisBottom>
|
|
562
|
-
)}
|
|
563
|
-
{visualizationType === 'Paired Bar' && generatePairedBarAxis()}
|
|
564
|
-
{visualizationType === 'Deviation Bar' && config.series?.length === 1 && <DeviationBar animatedChart={animatedChart} xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
|
|
565
|
-
{visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
|
|
566
|
-
{visualizationType === 'Scatter Plot' && (
|
|
567
|
-
<ScatterPlot
|
|
568
|
-
xScale={xScale}
|
|
569
|
-
yScale={yScale}
|
|
570
|
-
getXAxisData={getXAxisData}
|
|
571
|
-
getYAxisData={getYAxisData}
|
|
572
|
-
xMax={xMax}
|
|
573
|
-
yMax={yMax}
|
|
574
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
575
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
576
|
-
handleTooltipClick={handleTooltipClick}
|
|
577
|
-
tooltipData={tooltipData}
|
|
578
|
-
showTooltip={showTooltip}
|
|
579
|
-
/>
|
|
580
|
-
)}
|
|
581
|
-
{visualizationType === 'Box Plot' && <BoxPlot xScale={xScale} yScale={yScale} />}
|
|
582
|
-
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') || visualizationType === 'Combo') && (
|
|
583
|
-
<AreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
|
|
584
|
-
)}
|
|
585
|
-
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') || visualizationType === 'Combo') && (
|
|
586
|
-
<AreaChartStacked xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
|
|
587
|
-
)}
|
|
588
|
-
{(visualizationType === 'Bar' || visualizationType === 'Combo') && (
|
|
589
|
-
<BarChart
|
|
590
|
-
xScale={xScale}
|
|
591
|
-
yScale={yScale}
|
|
592
|
-
seriesScale={seriesScale}
|
|
593
|
-
xMax={xMax}
|
|
594
|
-
yMax={yMax}
|
|
595
|
-
getXAxisData={getXAxisData}
|
|
596
|
-
getYAxisData={getYAxisData}
|
|
597
|
-
animatedChart={animatedChart}
|
|
598
|
-
visible={animatedChart}
|
|
599
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
600
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
601
|
-
handleTooltipClick={handleTooltipClick}
|
|
602
|
-
tooltipData={tooltipData}
|
|
603
|
-
showTooltip={showTooltip}
|
|
604
|
-
chartRef={svgRef}
|
|
605
|
-
/>
|
|
606
|
-
)}
|
|
607
|
-
{(visualizationType === 'Line' || visualizationType === 'Combo') && (
|
|
608
|
-
<LineChart
|
|
609
|
-
xScale={xScale}
|
|
610
|
-
yScale={yScale}
|
|
611
|
-
getXAxisData={getXAxisData}
|
|
612
|
-
getYAxisData={getYAxisData}
|
|
613
|
-
xMax={xMax}
|
|
614
|
-
yMax={yMax}
|
|
615
|
-
seriesStyle={config.series}
|
|
616
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
617
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
618
|
-
handleTooltipClick={handleTooltipClick}
|
|
619
|
-
tooltipData={tooltipData}
|
|
620
|
-
showTooltip={showTooltip}
|
|
621
|
-
chartRef={svgRef}
|
|
622
|
-
/>
|
|
623
|
-
)}
|
|
624
|
-
{(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
|
|
625
|
-
<Forecasting
|
|
626
|
-
showTooltip={showTooltip}
|
|
627
|
-
tooltipData={tooltipData}
|
|
628
|
-
xScale={xScale}
|
|
629
|
-
yScale={yScale}
|
|
630
|
-
width={xMax}
|
|
631
|
-
le
|
|
632
|
-
height={yMax}
|
|
633
|
-
xScaleNoPadding={xScaleNoPadding}
|
|
634
|
-
chartRef={svgRef}
|
|
635
|
-
getXValueFromCoordinate={getXValueFromCoordinate}
|
|
636
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
637
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
638
|
-
isBrush={false}
|
|
639
|
-
/>
|
|
640
|
-
)}
|
|
641
|
-
{/* y anchors */}
|
|
642
|
-
{config.yAxis.anchors &&
|
|
643
|
-
config.yAxis.anchors.map(anchor => {
|
|
644
|
-
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'} />
|
|
645
|
-
})}
|
|
646
|
-
{visualizationType === 'Forest Plot' && (
|
|
647
|
-
<ForestPlot
|
|
648
|
-
xScale={xScale}
|
|
649
|
-
yScale={yScale}
|
|
650
|
-
seriesScale={seriesScale}
|
|
651
|
-
width={width}
|
|
652
|
-
height={height}
|
|
653
|
-
getXAxisData={getXAxisData}
|
|
654
|
-
getYAxisData={getYAxisData}
|
|
655
|
-
animatedChart={animatedChart}
|
|
656
|
-
visible={animatedChart}
|
|
657
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
658
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
659
|
-
handleTooltipClick={handleTooltipClick}
|
|
660
|
-
tooltipData={tooltipData}
|
|
661
|
-
showTooltip={showTooltip}
|
|
662
|
-
chartRef={svgRef}
|
|
663
|
-
config={config}
|
|
664
|
-
/>
|
|
665
|
-
)}
|
|
666
|
-
{/*Zoom Brush */}
|
|
667
|
-
{['Line', 'Bar', 'Combo', 'Area Chart'].includes(config.visualizationType) && !isHorizontal && <ZoomBrush xScaleBrush={xScaleBrush} yScale={yScale} xMax={xMax} yMax={yMax} />}
|
|
668
|
-
{/* Line chart */}
|
|
669
|
-
{/* TODO: Make this just line or combo? */}
|
|
670
|
-
{visualizationType !== 'Bar' && visualizationType !== 'Paired Bar' && visualizationType !== 'Box Plot' && visualizationType !== 'Area Chart' && visualizationType !== 'Scatter Plot' && visualizationType !== 'Deviation Bar' && visualizationType !== 'Forecasting' && (
|
|
671
|
-
<>
|
|
672
|
-
<LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
|
|
673
|
-
</>
|
|
674
|
-
)}
|
|
675
|
-
{/* y anchors */}
|
|
676
|
-
{config.yAxis.anchors &&
|
|
677
|
-
config.yAxis.anchors.map((anchor, index) => {
|
|
678
|
-
let anchorPosition = yScale(anchor.value)
|
|
679
|
-
// have to move up
|
|
680
|
-
// const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
681
|
-
if (!anchor.value) return
|
|
682
|
-
const middleOffset = orientation === 'horizontal' && visualizationType === 'Bar' ? config.barHeight / 4 : 0
|
|
683
|
-
|
|
684
|
-
if (!anchorPosition) return
|
|
685
|
-
|
|
686
|
-
return (
|
|
687
|
-
// prettier-ignore
|
|
688
|
-
<Line
|
|
689
|
-
key={`yAxis-${anchor.value}--${index}`}
|
|
690
|
-
strokeDasharray={handleLineType(anchor.lineStyle)}
|
|
691
|
-
stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
692
|
-
className='anchor-y'
|
|
693
|
-
from={{ x: 0 + padding, y: anchorPosition - middleOffset}}
|
|
694
|
-
to={{ x: width - config.yAxis.rightAxisSize, y: anchorPosition - middleOffset }}
|
|
695
|
-
/>
|
|
696
|
-
)
|
|
697
|
-
})}
|
|
698
|
-
{/* x anchors */}
|
|
699
|
-
{config.xAxis.anchors &&
|
|
700
|
-
config.xAxis.anchors.map((anchor, index) => {
|
|
701
|
-
let newX = xAxis
|
|
702
|
-
if (orientation === 'horizontal') {
|
|
703
|
-
newX = yAxis
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
let anchorPosition = isDateScale(newX) ? xScale(parseDate(anchor.value, false)) : xScale(anchor.value)
|
|
707
|
-
|
|
708
|
-
// have to move up
|
|
709
|
-
// const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
710
|
-
|
|
711
|
-
if (!anchorPosition) return
|
|
712
|
-
|
|
713
|
-
return (
|
|
714
|
-
// prettier-ignore
|
|
715
|
-
<Line
|
|
716
|
-
key={`xAxis-${anchor.value}--${index}`}
|
|
717
|
-
strokeDasharray={handleLineType(anchor.lineStyle)}
|
|
718
|
-
stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
719
|
-
fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
720
|
-
className='anchor-x'
|
|
721
|
-
from={{ x: Number(anchorPosition) + Number(padding), y: 0 }}
|
|
722
|
-
to={{ x: Number(anchorPosition) + Number(padding), y: yMax }}
|
|
723
|
-
/>
|
|
724
|
-
)
|
|
725
|
-
})}
|
|
726
|
-
{/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
|
|
727
|
-
{/* prettier-ignore */}
|
|
728
|
-
{config.visualizationType !== 'Bar' && config.visualizationType !== 'Combo' && (
|
|
729
|
-
<Regions xScale={xScale} handleTooltipClick={handleTooltipClick} handleTooltipMouseOff={handleTooltipMouseOff} handleTooltipMouseOver={handleTooltipMouseOver} showTooltip={showTooltip} hideTooltip={hideTooltip} tooltipData={tooltipData} yMax={yMax} width={width} />
|
|
730
|
-
)}
|
|
731
|
-
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
|
|
732
|
-
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
733
|
-
<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' />
|
|
734
|
-
</Group>
|
|
735
|
-
)}
|
|
736
|
-
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.horizontalHoverLine && (
|
|
737
|
-
<Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
|
|
738
|
-
<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' />
|
|
739
|
-
</Group>
|
|
740
|
-
)}
|
|
741
|
-
{config.filters && config.filters.values.length === 0 && data.length === 0 && (
|
|
742
|
-
<Text x={Number(config.yAxis.size) + Number(xMax / 2)} y={height / 2 - config.xAxis.padding / 2} textAnchor='middle'>
|
|
743
|
-
{config.chartMessage.noData}
|
|
744
|
-
</Text>
|
|
745
|
-
)}
|
|
746
|
-
{config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
|
|
747
|
-
<Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
|
|
748
|
-
<Line from={{ x: 0, y: point.y }} to={{ x: xMax, y: point.y }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='horizontal-tooltip-line' />
|
|
749
|
-
</Group>
|
|
750
|
-
)}
|
|
751
|
-
{config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
|
|
752
|
-
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
753
|
-
<Line from={{ x: point.x, y: 0 }} to={{ x: point.x, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
|
|
754
|
-
</Group>
|
|
755
|
-
)}
|
|
756
|
-
</svg>
|
|
757
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && !config.tooltips.singleSeries && (
|
|
758
|
-
<>
|
|
759
|
-
<style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important;`}</style>
|
|
760
|
-
<style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
|
|
761
|
-
<TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
|
|
762
|
-
<ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
|
|
763
|
-
</TooltipWithBounds>
|
|
764
|
-
</>
|
|
765
|
-
)}
|
|
766
|
-
|
|
767
|
-
{visSupportsReactTooltip() && <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' }} />}
|
|
768
|
-
<div className='animation-trigger' ref={triggerRef} />
|
|
769
|
-
</div>
|
|
770
|
-
</ErrorBoundary>
|
|
771
|
-
)
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
export default LinearChart
|