@cdc/chart 4.25.11 → 4.26.2
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/CLAUDE.local.md +79 -0
- package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
- package/dist/cdcchart.js +51401 -50814
- package/examples/default.json +378 -0
- package/examples/feature/__data__/horizon-chart-data.json +373 -0
- package/examples/feature/annotations/index.json +3 -6
- package/examples/feature/horizon/horizon-chart.json +395 -0
- package/examples/feature/pie/planet-pie-example-config.json +48 -2
- package/examples/line-chart-states.json +1085 -0
- package/examples/private/123.json +694 -0
- package/examples/private/DEV-12100.json +1303 -0
- package/examples/private/anchor-issue.json +4094 -0
- package/examples/private/backwards-slider.json +10430 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/data-points.json +228 -0
- package/examples/private/georgia.csv +160 -0
- package/examples/private/height.json +3915 -0
- package/examples/private/links.json +569 -0
- package/examples/private/quadrant.txt +30 -0
- package/examples/private/test-forecast.json +5510 -0
- package/examples/private/timeline-data.json +1 -0
- package/examples/private/timeline.json +389 -0
- package/examples/private/warming-stripe-test.json +2578 -0
- package/examples/private/warming-stripes.json +4763 -0
- package/examples/radar-chart-simple.json +133 -0
- package/examples/radar-chart.json +148 -0
- package/examples/tech-adoption-with-links.json +560 -0
- package/index.html +1 -36
- package/package.json +59 -60
- package/src/CdcChartComponent.tsx +206 -89
- package/src/_stories/Chart.Anchors.stories.tsx +10 -0
- package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
- package/src/_stories/Chart.CI.stories.tsx +13 -0
- package/src/_stories/Chart.Combo.stories.tsx +17 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
- package/src/_stories/Chart.Filters.stories.tsx +4 -0
- package/src/_stories/Chart.Forecast.stories.tsx +4 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
- package/src/_stories/Chart.Patterns.stories.tsx +4 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
- package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
- package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
- package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
- package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
- package/src/_stories/Chart.stories.tsx +45 -0
- package/src/_stories/Chart.tooltip.stories.tsx +7 -0
- package/src/_stories/ChartAnnotation.stories.tsx +10 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
- package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
- package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
- package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
- package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
- package/src/_stories/ChartBrush.stories.tsx +57 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
- package/src/_stories/ChartEditor.stories.tsx +7 -0
- package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
- package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
- package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
- package/src/_stories/_mock/brush_continuous.json +86 -0
- package/src/_stories/_mock/brush_date_large.json +176 -0
- package/src/_stories/_mock/brush_enabled.json +326 -0
- package/src/_stories/_mock/brush_mock.json +2 -69
- package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
- package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
- package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
- package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
- package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
- package/src/components/Annotations/components/AnnotationList.tsx +5 -4
- package/src/components/Annotations/components/findNearestDatum.ts +75 -85
- package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
- package/src/components/Axis/BottomAxis.tsx +270 -0
- package/src/components/Axis/Categorical.Axis.tsx +6 -7
- package/src/components/Axis/LeftAxis.tsx +404 -0
- package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
- package/src/components/Axis/PairedBarAxis.tsx +186 -0
- package/src/components/Axis/README.md +94 -0
- package/src/components/Axis/RightAxis.tsx +108 -0
- package/src/components/Axis/axis.constants.ts +21 -0
- package/src/components/Axis/index.ts +7 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
- package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
- package/src/components/BarChart/components/BarChart.tsx +7 -1
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BarChart/helpers/useBarChart.ts +14 -2
- package/src/components/Brush/BrushSelector.tsx +1390 -0
- package/src/components/Brush/MiniChartPreview.tsx +400 -0
- package/src/components/DeviationBar.jsx +9 -7
- package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
- package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
- package/src/components/HorizonChart/HorizonChart.tsx +131 -0
- package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
- package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
- package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
- package/src/components/HorizonChart/index.tsx +3 -0
- package/src/components/Legend/Legend.Component.tsx +52 -4
- package/src/components/Legend/Legend.tsx +4 -3
- package/src/components/Legend/LegendValueRange.tsx +77 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
- package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
- package/src/components/Legend/helpers/index.ts +10 -6
- package/src/components/LineChart/helpers/README.md +292 -0
- package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
- package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
- package/src/components/LineChart/index.tsx +44 -8
- package/src/components/LinearChart/README.md +109 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
- package/src/components/LinearChart/linearChart.constants.ts +84 -0
- package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
- package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
- package/src/components/LinearChart.tsx +338 -1082
- package/src/components/PairedBarChart.jsx +20 -3
- package/src/components/PieChart/PieChart.tsx +1 -1
- package/src/components/RadarChart/RadarAxis.tsx +78 -0
- package/src/components/RadarChart/RadarChart.tsx +298 -0
- package/src/components/RadarChart/RadarGrid.tsx +64 -0
- package/src/components/RadarChart/RadarPolygon.tsx +91 -0
- package/src/components/RadarChart/helpers.ts +83 -0
- package/src/components/RadarChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +365 -122
- package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
- package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
- package/src/components/WarmingStripes/index.tsx +3 -0
- package/src/data/initial-state.js +17 -2
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
- package/src/helpers/getExcludedData.ts +4 -0
- package/src/helpers/getMinMax.ts +12 -7
- package/src/helpers/handleChartAriaLabels.ts +19 -19
- package/src/helpers/handleLineType.ts +22 -18
- package/src/helpers/sizeHelpers.ts +0 -20
- package/src/helpers/smallMultiplesHelpers.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +10 -9
- package/src/hooks/useProgrammaticTooltip.ts +23 -2
- package/src/hooks/useScales.ts +18 -1
- package/src/hooks/useTooltip.tsx +34 -10
- package/src/scss/DataTable.scss +0 -4
- package/src/scss/main.scss +22 -3
- package/src/selectors/README.md +68 -0
- package/src/store/chart.reducer.ts +2 -0
- package/src/test/CdcChart.test.jsx +1 -1
- package/src/types/ChartConfig.ts +21 -0
- package/src/types/ChartContext.ts +1 -0
- package/src/types/Horizon.ts +64 -0
- package/src/types/Label.ts +1 -0
- package/src/utils/analyticsTracking.ts +19 -0
- package/LICENSE +0 -201
- package/src/components/Annotations/components/helpers/index.tsx +0 -46
- package/src/components/Brush/BrushChart.tsx +0 -128
- package/src/components/Brush/BrushController.tsx +0 -71
- package/src/components/Brush/types.tsx +0 -8
- package/src/components/BrushChart.tsx +0 -223
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { AxisLeft as VisxAxisLeft } from '@visx/axis'
|
|
3
|
+
import { Group } from '@visx/group'
|
|
4
|
+
import { Line } from '@visx/shape'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
6
|
+
import ConfigContext from '../../ConfigContext'
|
|
7
|
+
import { BlurStrokeText } from '@cdc/core/components/BlurStrokeText'
|
|
8
|
+
import { HORIZON_DEFAULTS } from '../../types/Horizon'
|
|
9
|
+
import { calculateHorizonBands } from '../HorizonChart/helpers/calculateHorizonBands'
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_TICK_LENGTH,
|
|
12
|
+
LOGARITHMIC_TICK_LENGTH,
|
|
13
|
+
MAJOR_LOG_TICK_LENGTH,
|
|
14
|
+
TICK_LABEL_MARGIN_RIGHT,
|
|
15
|
+
VALUE_ON_LINE_PADDING_NO_AXIS,
|
|
16
|
+
VALUE_ON_LINE_PADDING_WITH_AXIS,
|
|
17
|
+
LABEL_Y_PADDING_ABOVE_GRIDLINES,
|
|
18
|
+
HORIZONTAL_TICK_OFFSET_ADJUSTMENT,
|
|
19
|
+
ZERO_LINE_STROKE_WIDTH,
|
|
20
|
+
BAR_MIN_HEIGHT,
|
|
21
|
+
LOLLIPOP_SIZES
|
|
22
|
+
} from './axis.constants'
|
|
23
|
+
|
|
24
|
+
interface LeftAxisProps {
|
|
25
|
+
yScale: any
|
|
26
|
+
xScale: any
|
|
27
|
+
yMax: number
|
|
28
|
+
xMax: number
|
|
29
|
+
yAxisWidth: number
|
|
30
|
+
numTicks: number
|
|
31
|
+
tickLabelFontSize: number
|
|
32
|
+
axisLabelFontSize: number
|
|
33
|
+
handleLeftTickFormatting: (tick: any, index: number, ticks: any[]) => string
|
|
34
|
+
topYLabelRef?: React.RefObject<SVGTextElement>
|
|
35
|
+
suffixRef?: React.RefObject<SVGTextElement>
|
|
36
|
+
suffixWidth?: number
|
|
37
|
+
horizontalYAxisLabelSpace?: number
|
|
38
|
+
categoryLabelSpace?: number
|
|
39
|
+
yLabelOffset?: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const LeftAxis: React.FC<LeftAxisProps> = ({
|
|
43
|
+
yScale,
|
|
44
|
+
xScale,
|
|
45
|
+
yMax,
|
|
46
|
+
xMax,
|
|
47
|
+
yAxisWidth,
|
|
48
|
+
numTicks,
|
|
49
|
+
tickLabelFontSize,
|
|
50
|
+
axisLabelFontSize,
|
|
51
|
+
handleLeftTickFormatting,
|
|
52
|
+
topYLabelRef,
|
|
53
|
+
suffixRef,
|
|
54
|
+
suffixWidth = 0,
|
|
55
|
+
horizontalYAxisLabelSpace = 0,
|
|
56
|
+
categoryLabelSpace = 0,
|
|
57
|
+
yLabelOffset = 0
|
|
58
|
+
}) => {
|
|
59
|
+
const { config, colorScale, seriesHighlight } = useContext(ConfigContext)
|
|
60
|
+
const { runtime, orientation, visualizationType, visualizationSubType, heights } = config
|
|
61
|
+
|
|
62
|
+
const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
|
|
63
|
+
const { labelsAboveGridlines, hideAxis, inlineLabel } = config.yAxis
|
|
64
|
+
const inlineLabelHasNoSpace = !inlineLabel?.includes(' ')
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<VisxAxisLeft
|
|
68
|
+
scale={yScale}
|
|
69
|
+
tickLength={isLogarithmicAxis ? LOGARITHMIC_TICK_LENGTH : DEFAULT_TICK_LENGTH}
|
|
70
|
+
left={yAxisWidth - config.yAxis.axisPadding}
|
|
71
|
+
label={runtime.yAxis.label || runtime.yAxis.label}
|
|
72
|
+
stroke='#333'
|
|
73
|
+
tickFormat={handleLeftTickFormatting}
|
|
74
|
+
numTicks={numTicks}
|
|
75
|
+
>
|
|
76
|
+
{props => {
|
|
77
|
+
const axisCenter =
|
|
78
|
+
config.orientation === 'horizontal'
|
|
79
|
+
? Math.abs(props.axisToPoint.y - props.axisFromPoint.y) / 2
|
|
80
|
+
: (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
81
|
+
const horizontalTickOffset =
|
|
82
|
+
yMax / props.ticks.length / 2 -
|
|
83
|
+
(yMax / props.ticks.length) * (1 - config.barThickness) +
|
|
84
|
+
HORIZONTAL_TICK_OFFSET_ADJUSTMENT
|
|
85
|
+
return (
|
|
86
|
+
<Group className='left-axis'>
|
|
87
|
+
{!config.yAxis.hideAxis && (
|
|
88
|
+
<Line
|
|
89
|
+
from={props.axisFromPoint}
|
|
90
|
+
to={
|
|
91
|
+
runtime.horizontal
|
|
92
|
+
? {
|
|
93
|
+
x: 0,
|
|
94
|
+
y:
|
|
95
|
+
config.visualizationType === 'Forest Plot'
|
|
96
|
+
? yMax // Use yMax for forest plot since parentHeight is not available
|
|
97
|
+
: Number(heights.horizontal)
|
|
98
|
+
}
|
|
99
|
+
: props.axisToPoint
|
|
100
|
+
}
|
|
101
|
+
stroke='#000'
|
|
102
|
+
/>
|
|
103
|
+
)}
|
|
104
|
+
{orientation === 'vertical' && yScale.domain()[0] < 0 && (
|
|
105
|
+
<Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />
|
|
106
|
+
)}
|
|
107
|
+
{orientation === 'horizontal' && xScale.domain()[0] < 0 && (
|
|
108
|
+
<Line from={{ x: xScale(0), y: 0 }} to={{ x: xScale(0), y: yMax }} stroke='#333' />
|
|
109
|
+
)}
|
|
110
|
+
{visualizationType === 'Bar' && orientation === 'horizontal' && xScale.domain()[0] < 0 && (
|
|
111
|
+
<Line
|
|
112
|
+
from={{ x: xScale(0), y: 0 }}
|
|
113
|
+
to={{ x: xScale(0), y: yMax }}
|
|
114
|
+
stroke='#333'
|
|
115
|
+
strokeWidth={ZERO_LINE_STROKE_WIDTH}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
118
|
+
{visualizationType !== 'Horizon Chart' &&
|
|
119
|
+
props.ticks.map((tick, i) => {
|
|
120
|
+
const minY = props.ticks[0].to.y
|
|
121
|
+
const showTicks = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
122
|
+
const tickLength = showTicks === 'block' ? MAJOR_LOG_TICK_LENGTH : 0
|
|
123
|
+
const to = { x: tick.to.x - tickLength, y: tick.to.y }
|
|
124
|
+
|
|
125
|
+
// Vertical value/suffix vars
|
|
126
|
+
const lastTick = props.ticks.length - 1 === i
|
|
127
|
+
const useInlineLabel = lastTick && inlineLabel
|
|
128
|
+
const hideTopTick = lastTick && inlineLabel && !inlineLabelHasNoSpace
|
|
129
|
+
const valueOnLinePadding = hideAxis ? VALUE_ON_LINE_PADDING_NO_AXIS : VALUE_ON_LINE_PADDING_WITH_AXIS
|
|
130
|
+
const labelXPadding = labelsAboveGridlines ? valueOnLinePadding : TICK_LABEL_MARGIN_RIGHT
|
|
131
|
+
const labelYPadding = labelsAboveGridlines ? LABEL_Y_PADDING_ABOVE_GRIDLINES : 0
|
|
132
|
+
const labelX = tick.to.x - labelXPadding
|
|
133
|
+
const labelY = tick.to.y - labelYPadding
|
|
134
|
+
const labelVerticalAnchor = labelsAboveGridlines ? 'end' : 'middle'
|
|
135
|
+
const combineDomInlineLabelWithValue = inlineLabel && labelsAboveGridlines && lastTick
|
|
136
|
+
const formattedValue = useInlineLabel
|
|
137
|
+
? String(tick?.formattedValue || '').replace(config.dataFormat.suffix, '')
|
|
138
|
+
: tick?.formattedValue
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
142
|
+
{!runtime.yAxis.hideTicks && !labelsAboveGridlines && !hideTopTick && (
|
|
143
|
+
<Line
|
|
144
|
+
key={`${tick.value}--hide-hideTicks`}
|
|
145
|
+
from={tick.from}
|
|
146
|
+
to={isLogarithmicAxis ? to : tick.to}
|
|
147
|
+
stroke={config.yAxis.tickColor}
|
|
148
|
+
display={orientation === 'horizontal' ? 'none' : 'block'}
|
|
149
|
+
fontSize={tickLabelFontSize}
|
|
150
|
+
/>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{orientation === 'horizontal' &&
|
|
154
|
+
visualizationType === 'Box Plot' &&
|
|
155
|
+
config.yAxis.labelPlacement === 'On Date/Category Axis' &&
|
|
156
|
+
!config.yAxis.hideLabel && (
|
|
157
|
+
<Text
|
|
158
|
+
x={tick.to.x}
|
|
159
|
+
y={yScale(tick.value) + yScale.bandwidth() / 2}
|
|
160
|
+
transform={`rotate(${
|
|
161
|
+
config.orientation === 'horizontal' ? config.runtime.yAxis.tickRotation || 0 : 0
|
|
162
|
+
}, ${tick.to.x}, ${tick.to.y})`}
|
|
163
|
+
verticalAnchor={'middle'}
|
|
164
|
+
textAnchor={'end'}
|
|
165
|
+
fontSize={tickLabelFontSize}
|
|
166
|
+
>
|
|
167
|
+
{tick.formattedValue}
|
|
168
|
+
</Text>
|
|
169
|
+
)}
|
|
170
|
+
|
|
171
|
+
{orientation === 'horizontal' &&
|
|
172
|
+
visualizationType === 'Bar' &&
|
|
173
|
+
config.yAxis.labelPlacement === 'On Date/Category Axis' &&
|
|
174
|
+
!config.yAxis.hideLabel &&
|
|
175
|
+
(() => {
|
|
176
|
+
const barGroupCount =
|
|
177
|
+
config.visualizationSubType === 'stacked' ? 1 : config.runtime.seriesKeys.length
|
|
178
|
+
|
|
179
|
+
// Calculate barHeight based on chart type (regular bar vs lollipop)
|
|
180
|
+
let barHeight
|
|
181
|
+
if (config.isLollipopChart) {
|
|
182
|
+
const lollipopBarWidth = LOLLIPOP_SIZES[config.lollipopSize] || LOLLIPOP_SIZES.small
|
|
183
|
+
barHeight = lollipopBarWidth * barGroupCount
|
|
184
|
+
} else {
|
|
185
|
+
barHeight = Number(config.barHeight) * barGroupCount
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const totalBarHeight = barHeight + Number(config.barSpace)
|
|
189
|
+
const barGroupY = i === 0 ? 0 : totalBarHeight * i
|
|
190
|
+
const labelCenterY = barGroupY + barHeight / 2
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<Text
|
|
194
|
+
x={tick.from.x - yAxisWidth + horizontalYAxisLabelSpace}
|
|
195
|
+
y={labelCenterY}
|
|
196
|
+
verticalAnchor={'middle'}
|
|
197
|
+
textAnchor={'start'}
|
|
198
|
+
fontSize={tickLabelFontSize}
|
|
199
|
+
width={categoryLabelSpace}
|
|
200
|
+
lineHeight={'1.2em'}
|
|
201
|
+
>
|
|
202
|
+
{tick.formattedValue}
|
|
203
|
+
</Text>
|
|
204
|
+
)
|
|
205
|
+
})()}
|
|
206
|
+
|
|
207
|
+
{orientation === 'horizontal' &&
|
|
208
|
+
visualizationType !== 'Bar' &&
|
|
209
|
+
visualizationSubType === 'stacked' &&
|
|
210
|
+
config.yAxis.labelPlacement === 'On Date/Category Axis' &&
|
|
211
|
+
!config.yAxis.hideLabel && (
|
|
212
|
+
<Text
|
|
213
|
+
transform={`translate(${tick.to.x - 5}, ${
|
|
214
|
+
tick.to.y - minY + (Number(config.barHeight) - BAR_MIN_HEIGHT) / 2
|
|
215
|
+
}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`}
|
|
216
|
+
verticalAnchor={'start'}
|
|
217
|
+
textAnchor={'end'}
|
|
218
|
+
fontSize={tickLabelFontSize}
|
|
219
|
+
>
|
|
220
|
+
{tick.formattedValue}
|
|
221
|
+
</Text>
|
|
222
|
+
)}
|
|
223
|
+
|
|
224
|
+
{orientation === 'horizontal' && visualizationType === 'Paired Bar' && !config.yAxis.hideLabel && (
|
|
225
|
+
<Text
|
|
226
|
+
transform={`translate(${tick.to.x - 5}, ${
|
|
227
|
+
tick.to.y - minY + Number(config.barHeight) / 2
|
|
228
|
+
}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`}
|
|
229
|
+
textAnchor={'end'}
|
|
230
|
+
verticalAnchor='middle'
|
|
231
|
+
fontSize={tickLabelFontSize}
|
|
232
|
+
>
|
|
233
|
+
{tick.formattedValue}
|
|
234
|
+
</Text>
|
|
235
|
+
)}
|
|
236
|
+
{orientation === 'horizontal' &&
|
|
237
|
+
visualizationType === 'Deviation Bar' &&
|
|
238
|
+
!config.yAxis.hideLabel && (
|
|
239
|
+
<Text
|
|
240
|
+
transform={`translate(${tick.to.x - 5}, ${
|
|
241
|
+
config.isLollipopChart
|
|
242
|
+
? tick.to.y - minY + 2
|
|
243
|
+
: tick.to.y - minY + Number(config.barHeight) / 2
|
|
244
|
+
}) rotate(-${runtime.horizontal ? runtime.yAxis.tickRotation : 0})`}
|
|
245
|
+
textAnchor={'end'}
|
|
246
|
+
verticalAnchor='middle'
|
|
247
|
+
fontSize={tickLabelFontSize}
|
|
248
|
+
>
|
|
249
|
+
{tick.formattedValue}
|
|
250
|
+
</Text>
|
|
251
|
+
)}
|
|
252
|
+
|
|
253
|
+
{orientation === 'vertical' && visualizationType === 'Bump Chart' && !config.yAxis.hideLabel && (
|
|
254
|
+
<>
|
|
255
|
+
<Text
|
|
256
|
+
display={config.useLogScale ? showTicks : 'block'}
|
|
257
|
+
dx={config.useLogScale ? -6 : 0}
|
|
258
|
+
x={config.runtime.horizontal ? tick.from.x + 2 : tick.to.x - 8.5}
|
|
259
|
+
y={tick.to.y - 13 + (config.runtime.horizontal ? horizontalTickOffset : 0)}
|
|
260
|
+
angle={-Number(config.yAxis.tickRotation) || 0}
|
|
261
|
+
verticalAnchor={config.runtime.horizontal ? 'start' : 'middle'}
|
|
262
|
+
textAnchor={config.runtime.horizontal ? 'start' : 'end'}
|
|
263
|
+
fill={config.yAxis.tickLabelColor}
|
|
264
|
+
fontSize={tickLabelFontSize}
|
|
265
|
+
>
|
|
266
|
+
{config.runtime.seriesLabelsAll[tick.formattedValue - 1]}
|
|
267
|
+
</Text>
|
|
268
|
+
|
|
269
|
+
{(seriesHighlight.length === 0 ||
|
|
270
|
+
seriesHighlight.includes(config.runtime.seriesLabelsAll[tick.formattedValue - 1])) && (
|
|
271
|
+
<rect
|
|
272
|
+
x={0 - yAxisWidth}
|
|
273
|
+
y={tick.to.y - 8 + (config.runtime.horizontal ? horizontalTickOffset : 7)}
|
|
274
|
+
width={yAxisWidth + xScale(xScale.domain()[0])}
|
|
275
|
+
height='2'
|
|
276
|
+
fill={colorScale(config.runtime.seriesLabelsAll[tick.formattedValue - 1])}
|
|
277
|
+
/>
|
|
278
|
+
)}
|
|
279
|
+
</>
|
|
280
|
+
)}
|
|
281
|
+
{orientation === 'vertical' &&
|
|
282
|
+
visualizationType !== 'Paired Bar' &&
|
|
283
|
+
visualizationType !== 'Bump Chart' &&
|
|
284
|
+
!config.yAxis.hideLabel && (
|
|
285
|
+
<>
|
|
286
|
+
{/* INLINE LABEL BEHAVIOR: Dom suffix for 'inlineLabel' behavior */}
|
|
287
|
+
{/* inline label is shown alone and is allowed to 'overflow' to the right */}
|
|
288
|
+
{/* SPECIAL ONE CHAR CASE: a one character inlineLabel does not overflow */}
|
|
289
|
+
{/* IF VALUES ON LINE: suffix is combined with value to avoid having to calculate varying (now left-aligned) value widths */}
|
|
290
|
+
{inlineLabel && lastTick && !labelsAboveGridlines && (
|
|
291
|
+
<BlurStrokeText
|
|
292
|
+
innerRef={suffixRef}
|
|
293
|
+
display={isLogarithmicAxis ? showTicks : 'block'}
|
|
294
|
+
dx={isLogarithmicAxis ? -6 : 0}
|
|
295
|
+
x={labelX}
|
|
296
|
+
y={labelY}
|
|
297
|
+
angle={-Number(config.yAxis.tickRotation) || 0}
|
|
298
|
+
verticalAnchor={labelVerticalAnchor}
|
|
299
|
+
textAnchor={inlineLabelHasNoSpace ? 'end' : 'start'}
|
|
300
|
+
fill={config.yAxis.tickLabelColor}
|
|
301
|
+
stroke={'#fff'}
|
|
302
|
+
paintOrder={'stroke'} // keeps stroke under fill
|
|
303
|
+
strokeLinejoin='round'
|
|
304
|
+
style={{ whiteSpace: 'pre-wrap' }} // prevents leading spaces from being trimmed
|
|
305
|
+
fontSize={tickLabelFontSize}
|
|
306
|
+
>
|
|
307
|
+
{inlineLabel}
|
|
308
|
+
</BlurStrokeText>
|
|
309
|
+
)}
|
|
310
|
+
|
|
311
|
+
{/* VALUE */}
|
|
312
|
+
<BlurStrokeText
|
|
313
|
+
innerRef={el => lastTick && topYLabelRef && (topYLabelRef.current = el)}
|
|
314
|
+
display={isLogarithmicAxis ? showTicks : 'block'}
|
|
315
|
+
dx={isLogarithmicAxis ? -6 : 0}
|
|
316
|
+
x={inlineLabelHasNoSpace ? labelX - suffixWidth : labelX}
|
|
317
|
+
y={labelY + (config.runtime.horizontal ? horizontalTickOffset : 0)}
|
|
318
|
+
angle={-Number(config.yAxis.tickRotation) || 0}
|
|
319
|
+
verticalAnchor={config.runtime.horizontal ? 'start' : labelVerticalAnchor}
|
|
320
|
+
textAnchor={config.runtime.horizontal || labelsAboveGridlines ? 'start' : 'end'}
|
|
321
|
+
fill={config.yAxis.tickLabelColor}
|
|
322
|
+
stroke={'#fff'}
|
|
323
|
+
disableStroke={!labelsAboveGridlines}
|
|
324
|
+
strokeLinejoin='round'
|
|
325
|
+
paintOrder={'stroke'} // keeps stroke under fill
|
|
326
|
+
style={{ whiteSpace: 'pre-wrap' }} // prevents leading spaces from being trimmed
|
|
327
|
+
fontSize={tickLabelFontSize}
|
|
328
|
+
>
|
|
329
|
+
{`${formattedValue}${combineDomInlineLabelWithValue ? inlineLabel : ''}`}
|
|
330
|
+
</BlurStrokeText>
|
|
331
|
+
</>
|
|
332
|
+
)}
|
|
333
|
+
</Group>
|
|
334
|
+
)
|
|
335
|
+
})}
|
|
336
|
+
|
|
337
|
+
{/* Horizon Chart series labels - rendered outside ticks loop since it uses custom band positioning */}
|
|
338
|
+
{visualizationType === 'Horizon Chart' &&
|
|
339
|
+
(() => {
|
|
340
|
+
const horizonConfig = { ...HORIZON_DEFAULTS, ...config.horizon }
|
|
341
|
+
|
|
342
|
+
const seriesKeys =
|
|
343
|
+
runtime?.seriesKeys && runtime.seriesKeys.length > 0
|
|
344
|
+
? runtime.seriesKeys
|
|
345
|
+
: config.series?.map((s: any) => s.dataKey) || []
|
|
346
|
+
if (seriesKeys.length === 0) return null
|
|
347
|
+
|
|
348
|
+
const { bandHeight, getRowY } = calculateHorizonBands(
|
|
349
|
+
seriesKeys.length,
|
|
350
|
+
yMax,
|
|
351
|
+
horizonConfig.bandGap,
|
|
352
|
+
horizonConfig.bottomPadding
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
return seriesKeys.map((seriesKey, index) => {
|
|
356
|
+
const labelY = getRowY(index) + bandHeight / 2
|
|
357
|
+
const labelX = -DEFAULT_TICK_LENGTH - TICK_LABEL_MARGIN_RIGHT
|
|
358
|
+
return (
|
|
359
|
+
<Group key={`horizon-tick-${seriesKey}`} className='horizon-axis-tick'>
|
|
360
|
+
{/* Tick mark */}
|
|
361
|
+
{!runtime.yAxis.hideTicks && (
|
|
362
|
+
<Line
|
|
363
|
+
from={{ x: 0, y: labelY }}
|
|
364
|
+
to={{ x: -DEFAULT_TICK_LENGTH, y: labelY }}
|
|
365
|
+
stroke={(config.yAxis as any).tickColor || '#333'}
|
|
366
|
+
/>
|
|
367
|
+
)}
|
|
368
|
+
{/* Series label */}
|
|
369
|
+
{!config.yAxis.hideLabel && (
|
|
370
|
+
<Text
|
|
371
|
+
x={labelX}
|
|
372
|
+
y={labelY}
|
|
373
|
+
textAnchor='end'
|
|
374
|
+
verticalAnchor='middle'
|
|
375
|
+
fontSize={tickLabelFontSize}
|
|
376
|
+
fill={(config.yAxis as any).tickLabelColor || '#1c1d1f'}
|
|
377
|
+
>
|
|
378
|
+
{runtime?.seriesLabels?.[seriesKey] || seriesKey}
|
|
379
|
+
</Text>
|
|
380
|
+
)}
|
|
381
|
+
</Group>
|
|
382
|
+
)
|
|
383
|
+
})
|
|
384
|
+
})()}
|
|
385
|
+
|
|
386
|
+
<Text
|
|
387
|
+
className='y-label'
|
|
388
|
+
textAnchor='middle'
|
|
389
|
+
verticalAnchor='start'
|
|
390
|
+
transform={`translate(${-1 * yAxisWidth + yLabelOffset}, ${axisCenter}) rotate(-90)`}
|
|
391
|
+
fontWeight='bold'
|
|
392
|
+
fill={config.yAxis.labelColor}
|
|
393
|
+
fontSize={axisLabelFontSize}
|
|
394
|
+
>
|
|
395
|
+
{!config.hideYAxisLabel ? props.label : null}
|
|
396
|
+
</Text>
|
|
397
|
+
</Group>
|
|
398
|
+
)
|
|
399
|
+
}}
|
|
400
|
+
</VisxAxisLeft>
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default LeftAxis
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { AxisLeft } from '@visx/axis'
|
|
3
|
+
import { Group } from '@visx/group'
|
|
4
|
+
import { Line } from '@visx/shape'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
6
|
+
import ConfigContext from '../../ConfigContext'
|
|
7
|
+
|
|
8
|
+
interface LeftAxisGridlinesProps {
|
|
9
|
+
yScale: any
|
|
10
|
+
xMax: number
|
|
11
|
+
yAxisWidth: number
|
|
12
|
+
numTicks: number
|
|
13
|
+
yLabelOffset: number
|
|
14
|
+
axisLabelFontSize: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const LeftAxisGridlines: React.FC<LeftAxisGridlinesProps> = ({
|
|
18
|
+
yScale,
|
|
19
|
+
xMax,
|
|
20
|
+
yAxisWidth,
|
|
21
|
+
numTicks,
|
|
22
|
+
yLabelOffset,
|
|
23
|
+
axisLabelFontSize
|
|
24
|
+
}) => {
|
|
25
|
+
const { config } = useContext(ConfigContext)
|
|
26
|
+
const { runtime } = config
|
|
27
|
+
|
|
28
|
+
const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<AxisLeft scale={yScale} left={yAxisWidth - config.yAxis.axisPadding} numTicks={numTicks}>
|
|
32
|
+
{props => {
|
|
33
|
+
const axisCenter =
|
|
34
|
+
config.orientation === 'horizontal'
|
|
35
|
+
? Math.abs(props.axisToPoint.y - props.axisFromPoint.y) / 2
|
|
36
|
+
: (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
37
|
+
return (
|
|
38
|
+
<Group className='left-axis'>
|
|
39
|
+
{props.ticks.map((tick, i) => {
|
|
40
|
+
const showTicks = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
41
|
+
const hideFirstGridLine = tick.index === 0 && tick.value === 0 && config.xAxis.hideAxis
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
45
|
+
{runtime.yAxis.gridLines && !hideFirstGridLine ? (
|
|
46
|
+
<Line
|
|
47
|
+
key={`${tick.value}--hide-hideGridLines`}
|
|
48
|
+
display={(isLogarithmicAxis && showTicks).toString()}
|
|
49
|
+
from={{ x: tick.from.x + xMax, y: tick.from.y }}
|
|
50
|
+
to={tick.from}
|
|
51
|
+
stroke='#d6d6d6'
|
|
52
|
+
/>
|
|
53
|
+
) : (
|
|
54
|
+
''
|
|
55
|
+
)}
|
|
56
|
+
</Group>
|
|
57
|
+
)
|
|
58
|
+
})}
|
|
59
|
+
<Text
|
|
60
|
+
className='y-label'
|
|
61
|
+
textAnchor='middle'
|
|
62
|
+
verticalAnchor='start'
|
|
63
|
+
transform={`translate(${-1 * yAxisWidth + yLabelOffset}, ${axisCenter}) rotate(-90)`}
|
|
64
|
+
fontWeight='bold'
|
|
65
|
+
fill={config.yAxis.labelColor}
|
|
66
|
+
fontSize={axisLabelFontSize}
|
|
67
|
+
>
|
|
68
|
+
{!config.hideYAxisLabel ? props.label : null}
|
|
69
|
+
</Text>
|
|
70
|
+
</Group>
|
|
71
|
+
)
|
|
72
|
+
}}
|
|
73
|
+
</AxisLeft>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default LeftAxisGridlines
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import React, { useContext, MutableRefObject } from 'react'
|
|
2
|
+
import { AxisBottom } from '@visx/axis'
|
|
3
|
+
import { Group } from '@visx/group'
|
|
4
|
+
import { Line } from '@visx/shape'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
6
|
+
import { ScaleLinear } from 'd3-scale'
|
|
7
|
+
|
|
8
|
+
import ConfigContext from '../../ConfigContext'
|
|
9
|
+
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
10
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
11
|
+
|
|
12
|
+
// Constants
|
|
13
|
+
const BOTTOM_LABEL_PADDING = 9
|
|
14
|
+
const X_TICK_LABEL_PADDING = 4.5
|
|
15
|
+
const DEFAULT_MAX_TICK_ROTATION = 90
|
|
16
|
+
const BASE_TICK_WIDTH_ACCUMULATOR = 100
|
|
17
|
+
|
|
18
|
+
type PairedBarAxisProps = {
|
|
19
|
+
g1xScale: ScaleLinear<number, number>
|
|
20
|
+
g2xScale: ScaleLinear<number, number>
|
|
21
|
+
yMax: number
|
|
22
|
+
xMax: number
|
|
23
|
+
yAxisWidth: number
|
|
24
|
+
bottomLabelStart: number
|
|
25
|
+
tickLabelFontSize: number
|
|
26
|
+
axisLabelFontSize: number
|
|
27
|
+
axisBottomRef: MutableRefObject<SVGGElement | null>
|
|
28
|
+
xAxisLabelRefs: MutableRefObject<(SVGTextElement | null)[]>
|
|
29
|
+
tickLabelFont: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Specialized axis component for Paired Bar charts.
|
|
34
|
+
* Renders two mirrored AxisBottom components with shared tick formatting logic.
|
|
35
|
+
* Extracted from LinearChart.tsx
|
|
36
|
+
*/
|
|
37
|
+
export const PairedBarAxis: React.FC<PairedBarAxisProps> = ({
|
|
38
|
+
g1xScale,
|
|
39
|
+
g2xScale,
|
|
40
|
+
yMax,
|
|
41
|
+
xMax,
|
|
42
|
+
yAxisWidth,
|
|
43
|
+
bottomLabelStart,
|
|
44
|
+
tickLabelFontSize,
|
|
45
|
+
axisLabelFontSize,
|
|
46
|
+
axisBottomRef,
|
|
47
|
+
xAxisLabelRefs,
|
|
48
|
+
tickLabelFont
|
|
49
|
+
}) => {
|
|
50
|
+
const { config, formatDate, formatNumber } = useContext(ConfigContext)
|
|
51
|
+
const { runtime } = config
|
|
52
|
+
|
|
53
|
+
const axisMaxHeight = bottomLabelStart + BOTTOM_LABEL_PADDING
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Determines if ticks are overlapping to trigger responsive rotation
|
|
57
|
+
*/
|
|
58
|
+
const getTickPositions = (ticks: any[], xScale: ScaleLinear<number, number>): boolean => {
|
|
59
|
+
if (!ticks.length) return false
|
|
60
|
+
|
|
61
|
+
// Filter out first index
|
|
62
|
+
const filteredTicks = ticks.filter(tick => tick.index !== 0)
|
|
63
|
+
const numberOfTicks = filteredTicks?.length
|
|
64
|
+
const xMaxHalf = xScale.range()[0] || xMax / 2
|
|
65
|
+
|
|
66
|
+
const tickWidthAll = filteredTicks.map(tick => getTextWidth(formatNumber(tick.value, 'left'), tickLabelFont))
|
|
67
|
+
const sumOfTickWidth = tickWidthAll.reduce((a, b) => a + b, BASE_TICK_WIDTH_ACCUMULATOR)
|
|
68
|
+
const spaceBetweenEachTick = (xMaxHalf - sumOfTickWidth) / numberOfTicks
|
|
69
|
+
|
|
70
|
+
// Determine the position of each tick
|
|
71
|
+
const positions = [0]
|
|
72
|
+
for (let i = 1; i < tickWidthAll.length; i++) {
|
|
73
|
+
positions[i] = positions[i - 1] + tickWidthAll[i - 1] + spaceBetweenEachTick
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if ticks are overlapping
|
|
77
|
+
let isTicksOverlapping = false
|
|
78
|
+
tickWidthAll.forEach((_, i) => {
|
|
79
|
+
if (positions[i] + tickWidthAll[i] > positions[i + 1]) {
|
|
80
|
+
isTicksOverlapping = true
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
return isTicksOverlapping
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Renders tick elements for either the left or right paired bar section
|
|
88
|
+
*/
|
|
89
|
+
const renderTickGroup = (
|
|
90
|
+
tick: any,
|
|
91
|
+
index: number,
|
|
92
|
+
ticks: any[],
|
|
93
|
+
xScale: ScaleLinear<number, number>,
|
|
94
|
+
isRightSection: boolean
|
|
95
|
+
) => {
|
|
96
|
+
const isTicksOverlapping = getTickPositions(ticks, xScale)
|
|
97
|
+
const maxTickRotation = Number(config.xAxis.maxTickRotation) || DEFAULT_MAX_TICK_ROTATION
|
|
98
|
+
const isResponsiveTicks = config.isResponsiveTicks && isTicksOverlapping
|
|
99
|
+
const angle = tick.index !== 0 && (isResponsiveTicks ? maxTickRotation : Number(config.yAxis.tickRotation))
|
|
100
|
+
const textAnchor = angle && tick.index !== 0 ? 'end' : 'middle'
|
|
101
|
+
|
|
102
|
+
// Skip first tick on right section to avoid overlapping 0's
|
|
103
|
+
if (isRightSection && !index) return <React.Fragment key={`vx-tick-empty-${index}`} />
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Group key={`vx-tick-${tick.value}-${index}`} className='vx-axis-tick'>
|
|
107
|
+
{!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
108
|
+
{!runtime.yAxis.hideLabel && (
|
|
109
|
+
<Text
|
|
110
|
+
innerRef={!isRightSection ? el => (xAxisLabelRefs.current[index] = el) : undefined}
|
|
111
|
+
x={tick.to.x}
|
|
112
|
+
y={tick.to.y + (isRightSection ? X_TICK_LABEL_PADDING : 0)}
|
|
113
|
+
angle={-angle}
|
|
114
|
+
verticalAnchor={angle ? 'middle' : 'start'}
|
|
115
|
+
textAnchor={textAnchor}
|
|
116
|
+
fontSize={tickLabelFontSize}
|
|
117
|
+
>
|
|
118
|
+
{formatNumber(tick.value, 'left')}
|
|
119
|
+
</Text>
|
|
120
|
+
)}
|
|
121
|
+
</Group>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
{/* Left section axis */}
|
|
128
|
+
<AxisBottom
|
|
129
|
+
top={yMax}
|
|
130
|
+
left={yAxisWidth}
|
|
131
|
+
label={runtime.xAxis.label}
|
|
132
|
+
tickFormat={isDateScale(runtime.xAxis) ? formatDate : formatNumber}
|
|
133
|
+
scale={g1xScale}
|
|
134
|
+
stroke='#333'
|
|
135
|
+
tickStroke='#333'
|
|
136
|
+
numTicks={runtime.xAxis.numTicks || undefined}
|
|
137
|
+
>
|
|
138
|
+
{props => (
|
|
139
|
+
<Group className='bottom-axis'>
|
|
140
|
+
{props.ticks.map((tick, i) => renderTickGroup(tick, i, props.ticks, g1xScale, false))}
|
|
141
|
+
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
142
|
+
</Group>
|
|
143
|
+
)}
|
|
144
|
+
</AxisBottom>
|
|
145
|
+
|
|
146
|
+
{/* Right section axis */}
|
|
147
|
+
<AxisBottom
|
|
148
|
+
innerRef={axisBottomRef}
|
|
149
|
+
top={yMax}
|
|
150
|
+
left={yAxisWidth}
|
|
151
|
+
label={runtime.xAxis.label}
|
|
152
|
+
tickFormat={
|
|
153
|
+
isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick
|
|
154
|
+
}
|
|
155
|
+
scale={g2xScale}
|
|
156
|
+
stroke='#333'
|
|
157
|
+
tickStroke='#333'
|
|
158
|
+
numTicks={runtime.xAxis.numTicks || undefined}
|
|
159
|
+
>
|
|
160
|
+
{props => (
|
|
161
|
+
<>
|
|
162
|
+
<Group className='bottom-axis'>
|
|
163
|
+
{props.ticks.map((tick, i) => renderTickGroup(tick, i, props.ticks, g2xScale, true))}
|
|
164
|
+
{!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
165
|
+
</Group>
|
|
166
|
+
<Group>
|
|
167
|
+
<Text
|
|
168
|
+
className='x-axis-title-label'
|
|
169
|
+
x={xMax / 2}
|
|
170
|
+
y={axisMaxHeight}
|
|
171
|
+
stroke='#333'
|
|
172
|
+
textAnchor='middle'
|
|
173
|
+
verticalAnchor='start'
|
|
174
|
+
fontSize={axisLabelFontSize}
|
|
175
|
+
>
|
|
176
|
+
{!config.hideXAxisLabel ? runtime.xAxis.label : null}
|
|
177
|
+
</Text>
|
|
178
|
+
</Group>
|
|
179
|
+
</>
|
|
180
|
+
)}
|
|
181
|
+
</AxisBottom>
|
|
182
|
+
</>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default PairedBarAxis
|