@coinbase/cds-web-visualization 3.4.0-beta.21 → 3.4.0-beta.23
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/CHANGELOG.md +13 -0
- package/dts/chart/CartesianChart.d.ts +23 -4
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/PeriodSelector.d.ts +22 -5
- package/dts/chart/PeriodSelector.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +7 -0
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +3 -3
- package/dts/chart/area/AreaChart.d.ts.map +1 -1
- package/dts/chart/area/DottedArea.d.ts.map +1 -1
- package/dts/chart/area/GradientArea.d.ts.map +1 -1
- package/dts/chart/area/SolidArea.d.ts.map +1 -1
- package/dts/chart/axis/Axis.d.ts +10 -10
- package/dts/chart/axis/Axis.d.ts.map +1 -1
- package/dts/chart/axis/XAxis.d.ts +6 -0
- package/dts/chart/axis/XAxis.d.ts.map +1 -1
- package/dts/chart/axis/YAxis.d.ts +1 -0
- package/dts/chart/axis/YAxis.d.ts.map +1 -1
- package/dts/chart/bar/Bar.d.ts +4 -3
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +25 -5
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +47 -12
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
- package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
- package/dts/chart/gradient/Gradient.d.ts +7 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -1
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +7 -0
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +3 -3
- package/dts/chart/line/LineChart.d.ts.map +1 -1
- package/dts/chart/line/ReferenceLine.d.ts +1 -0
- package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
- package/dts/chart/line/SolidLine.d.ts.map +1 -1
- package/dts/chart/point/Point.d.ts +7 -0
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
- package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
- package/dts/chart/scrubber/Scrubber.d.ts +8 -0
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +23 -8
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +6 -5
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +13 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +20 -4
- package/dts/chart/utils/context.d.ts.map +1 -1
- package/dts/chart/utils/gradient.d.ts +3 -1
- package/dts/chart/utils/gradient.d.ts.map +1 -1
- package/dts/chart/utils/path.d.ts +20 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +7 -0
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/transition.d.ts +3 -3
- package/dts/chart/utils/transition.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +89 -57
- package/esm/chart/Path.js +21 -6
- package/esm/chart/area/Area.js +19 -9
- package/esm/chart/area/AreaChart.js +23 -25
- package/esm/chart/area/DottedArea.js +11 -6
- package/esm/chart/area/GradientArea.js +11 -6
- package/esm/chart/area/SolidArea.js +3 -1
- package/esm/chart/axis/XAxis.js +11 -12
- package/esm/chart/axis/YAxis.js +4 -4
- package/esm/chart/bar/Bar.js +11 -5
- package/esm/chart/bar/BarChart.js +34 -31
- package/esm/chart/bar/BarPlot.js +6 -3
- package/esm/chart/bar/BarStack.js +155 -356
- package/esm/chart/bar/BarStackGroup.js +36 -27
- package/esm/chart/bar/DefaultBar.js +26 -10
- package/esm/chart/bar/DefaultBarStack.js +27 -13
- package/esm/chart/gradient/Gradient.js +3 -2
- package/esm/chart/line/DottedLine.js +3 -1
- package/esm/chart/line/Line.js +29 -16
- package/esm/chart/line/LineChart.js +12 -11
- package/esm/chart/line/SolidLine.js +3 -1
- package/esm/chart/point/Point.js +3 -2
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +3 -3
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -8
- package/esm/chart/scrubber/Scrubber.js +36 -28
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +49 -32
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +1 -1
- package/esm/chart/scrubber/ScrubberProvider.js +44 -39
- package/esm/chart/utils/axis.js +44 -13
- package/esm/chart/utils/bar.js +6 -4
- package/esm/chart/utils/chart.js +18 -5
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +6 -4
- package/esm/chart/utils/path.js +87 -61
- package/esm/chart/utils/point.js +30 -21
- package/esm/chart/utils/transition.js +8 -3
- package/package.json +5 -5
|
@@ -50,8 +50,11 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
50
50
|
scrubberPosition
|
|
51
51
|
} = useScrubberContext();
|
|
52
52
|
const {
|
|
53
|
+
layout,
|
|
53
54
|
getXScale,
|
|
55
|
+
getYScale,
|
|
54
56
|
getXAxis,
|
|
57
|
+
getYAxis,
|
|
55
58
|
animate,
|
|
56
59
|
series,
|
|
57
60
|
drawingArea,
|
|
@@ -73,30 +76,31 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
73
76
|
return seriesIds;
|
|
74
77
|
}, [series, seriesIds]);
|
|
75
78
|
const {
|
|
76
|
-
|
|
79
|
+
dataValue,
|
|
77
80
|
dataIndex
|
|
78
81
|
} = useMemo(() => {
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
83
|
+
const indexScale = categoryAxisIsX ? getXScale() : getYScale();
|
|
84
|
+
const indexAxis = categoryAxisIsX ? getXAxis() : getYAxis();
|
|
85
|
+
if (!indexScale) return {
|
|
86
|
+
dataValue: undefined,
|
|
83
87
|
dataIndex: undefined
|
|
84
88
|
};
|
|
85
89
|
const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
|
|
86
90
|
|
|
87
|
-
// Convert index to actual
|
|
88
|
-
let
|
|
89
|
-
if (
|
|
90
|
-
const
|
|
91
|
-
|
|
91
|
+
// Convert index to actual data value if axis has data
|
|
92
|
+
let dataValue;
|
|
93
|
+
if (indexAxis !== null && indexAxis !== void 0 && indexAxis.data && Array.isArray(indexAxis.data) && indexAxis.data[dataIndex] !== undefined) {
|
|
94
|
+
const val = indexAxis.data[dataIndex];
|
|
95
|
+
dataValue = typeof val === 'string' ? dataIndex : val;
|
|
92
96
|
} else {
|
|
93
|
-
|
|
97
|
+
dataValue = dataIndex;
|
|
94
98
|
}
|
|
95
99
|
return {
|
|
96
|
-
|
|
100
|
+
dataValue,
|
|
97
101
|
dataIndex
|
|
98
102
|
};
|
|
99
|
-
}, [getXScale, getXAxis, scrubberPosition, dataLength]);
|
|
103
|
+
}, [getXScale, getYScale, getXAxis, getYAxis, scrubberPosition, dataLength, layout]);
|
|
100
104
|
|
|
101
105
|
// Compute resolved accessibility label
|
|
102
106
|
const resolvedAccessibilityLabel = useMemo(() => {
|
|
@@ -120,11 +124,12 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
120
124
|
}))) !== null && _series$filter$filter !== void 0 ? _series$filter$filter : [];
|
|
121
125
|
}, [series, filteredSeriesIds]);
|
|
122
126
|
const groupEnterTransition = useMemo(() => getTransition(transitions === null || transitions === void 0 ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [transitions === null || transitions === void 0 ? void 0 : transitions.enter, animate]);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
const shouldAnimateGroup = animate && groupEnterTransition !== null;
|
|
128
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
129
|
+
const showBeaconLabels = !hideBeaconLabels && categoryAxisIsX && beaconLabels.length > 0;
|
|
130
|
+
const indexScale = categoryAxisIsX ? getXScale() : getYScale();
|
|
131
|
+
if (!indexScale) return null;
|
|
132
|
+
const pixelPos = dataValue !== undefined && indexScale ? getPointOnScale(dataValue, indexScale) : undefined;
|
|
128
133
|
return /*#__PURE__*/_jsxs(motion.g, _objectSpread(_objectSpread({
|
|
129
134
|
"aria-atomic": "true",
|
|
130
135
|
"aria-label": resolvedAccessibilityLabel,
|
|
@@ -132,7 +137,7 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
132
137
|
"data-component": "scrubber-group",
|
|
133
138
|
"data-testid": testID,
|
|
134
139
|
role: "status"
|
|
135
|
-
},
|
|
140
|
+
}, shouldAnimateGroup ? {
|
|
136
141
|
animate: {
|
|
137
142
|
opacity: 1,
|
|
138
143
|
transition: groupEnterTransition
|
|
@@ -141,23 +146,22 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
141
146
|
opacity: 0
|
|
142
147
|
}
|
|
143
148
|
} : {}), {}, {
|
|
144
|
-
children: [!hideOverlay && scrubberPosition !== undefined &&
|
|
149
|
+
children: [!hideOverlay && scrubberPosition !== undefined && pixelPos !== undefined && /*#__PURE__*/_jsx("rect", {
|
|
145
150
|
className: classNames === null || classNames === void 0 ? void 0 : classNames.overlay,
|
|
146
151
|
fill: "var(--color-bg)",
|
|
147
|
-
height: drawingArea.height + overlayOffset * 2,
|
|
152
|
+
height: categoryAxisIsX ? drawingArea.height + overlayOffset * 2 : drawingArea.y + drawingArea.height - pixelPos + overlayOffset,
|
|
148
153
|
opacity: 0.8,
|
|
149
154
|
style: styles === null || styles === void 0 ? void 0 : styles.overlay,
|
|
150
|
-
width: drawingArea.x + drawingArea.width -
|
|
151
|
-
x:
|
|
152
|
-
y: drawingArea.y - overlayOffset
|
|
153
|
-
}), !hideLine && scrubberPosition !== undefined &&
|
|
155
|
+
width: categoryAxisIsX ? drawingArea.x + drawingArea.width - pixelPos + overlayOffset : drawingArea.width + overlayOffset * 2,
|
|
156
|
+
x: categoryAxisIsX ? pixelPos : drawingArea.x - overlayOffset,
|
|
157
|
+
y: categoryAxisIsX ? drawingArea.y - overlayOffset : pixelPos
|
|
158
|
+
}), !hideLine && scrubberPosition !== undefined && dataValue !== undefined && dataIndex !== undefined && /*#__PURE__*/_jsx(ReferenceLine, _objectSpread({
|
|
154
159
|
LabelComponent: LabelComponent,
|
|
155
160
|
LineComponent: LineComponent,
|
|
156
161
|
classNames: {
|
|
157
162
|
label: classNames === null || classNames === void 0 ? void 0 : classNames.label,
|
|
158
163
|
line: classNames === null || classNames === void 0 ? void 0 : classNames.line
|
|
159
164
|
},
|
|
160
|
-
dataX: dataX,
|
|
161
165
|
label: typeof label === 'function' ? label(dataIndex) : label,
|
|
162
166
|
labelBoundsInset: labelBoundsInset,
|
|
163
167
|
labelElevated: labelElevated,
|
|
@@ -167,7 +171,11 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
167
171
|
label: styles === null || styles === void 0 ? void 0 : styles.label,
|
|
168
172
|
line: styles === null || styles === void 0 ? void 0 : styles.line
|
|
169
173
|
}
|
|
170
|
-
}
|
|
174
|
+
}, categoryAxisIsX ? {
|
|
175
|
+
dataX: dataValue
|
|
176
|
+
} : {
|
|
177
|
+
dataY: dataValue
|
|
178
|
+
})), /*#__PURE__*/_jsx(ScrubberBeaconGroup, {
|
|
171
179
|
ref: beaconGroupRef,
|
|
172
180
|
BeaconComponent: BeaconComponent,
|
|
173
181
|
className: classNames === null || classNames === void 0 ? void 0 : classNames.beacon,
|
|
@@ -177,7 +185,7 @@ export const Scrubber = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
|
|
|
177
185
|
style: styles === null || styles === void 0 ? void 0 : styles.beacon,
|
|
178
186
|
testID: testID,
|
|
179
187
|
transitions: transitions
|
|
180
|
-
}),
|
|
188
|
+
}), showBeaconLabels && /*#__PURE__*/_jsx(ScrubberBeaconLabelGroup, {
|
|
181
189
|
BeaconLabelComponent: BeaconLabelComponent,
|
|
182
190
|
className: classNames === null || classNames === void 0 ? void 0 : classNames.beaconLabel,
|
|
183
191
|
labelFont: beaconLabelFont,
|
|
@@ -9,7 +9,7 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
9
9
|
let {
|
|
10
10
|
seriesId,
|
|
11
11
|
dataIndex,
|
|
12
|
-
|
|
12
|
+
dataIndexValue,
|
|
13
13
|
isIdle,
|
|
14
14
|
BeaconComponent,
|
|
15
15
|
idlePulse,
|
|
@@ -22,23 +22,26 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
22
22
|
stroke
|
|
23
23
|
} = _ref;
|
|
24
24
|
const {
|
|
25
|
+
layout,
|
|
25
26
|
getSeries,
|
|
26
27
|
getSeriesData,
|
|
27
28
|
getXScale,
|
|
28
|
-
getYScale
|
|
29
|
+
getYScale,
|
|
30
|
+
getXAxis,
|
|
31
|
+
getYAxis
|
|
29
32
|
} = useCartesianChartContext();
|
|
30
33
|
const series = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
|
|
31
34
|
const sourceData = useMemo(() => getSeriesData(seriesId), [getSeriesData, seriesId]);
|
|
32
35
|
const gradient = series === null || series === void 0 ? void 0 : series.gradient;
|
|
33
36
|
|
|
34
|
-
// Get
|
|
35
|
-
const
|
|
37
|
+
// Get dataValue from series data
|
|
38
|
+
const dataValue = useMemo(() => {
|
|
36
39
|
if (sourceData && dataIndex >= 0 && dataIndex < sourceData.length) {
|
|
37
|
-
const
|
|
38
|
-
if (typeof
|
|
39
|
-
return
|
|
40
|
-
} else if (Array.isArray(
|
|
41
|
-
const validValues =
|
|
40
|
+
const value = sourceData[dataIndex];
|
|
41
|
+
if (typeof value === 'number') {
|
|
42
|
+
return value;
|
|
43
|
+
} else if (Array.isArray(value)) {
|
|
44
|
+
const validValues = value.filter(val => val !== null);
|
|
42
45
|
if (validValues.length >= 1) {
|
|
43
46
|
return validValues[validValues.length - 1];
|
|
44
47
|
}
|
|
@@ -50,18 +53,27 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
50
53
|
// Evaluate gradient color
|
|
51
54
|
const color = useMemo(() => {
|
|
52
55
|
var _series$color, _series$color2;
|
|
53
|
-
if (
|
|
56
|
+
if (dataValue === undefined) return (_series$color = series === null || series === void 0 ? void 0 : series.color) !== null && _series$color !== void 0 ? _series$color : 'var(--color-fgPrimary)';
|
|
54
57
|
if (gradient) {
|
|
55
|
-
const xScale = getXScale();
|
|
58
|
+
const xScale = getXScale(series === null || series === void 0 ? void 0 : series.xAxisId);
|
|
56
59
|
const yScale = getYScale(series === null || series === void 0 ? void 0 : series.yAxisId);
|
|
57
60
|
if (xScale && yScale) {
|
|
61
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
58
62
|
const gradientScale = gradient.axis === 'x' ? xScale : yScale;
|
|
59
63
|
const stops = getGradientConfig(gradient, xScale, yScale);
|
|
60
64
|
if (stops) {
|
|
61
65
|
var _gradient$axis;
|
|
62
66
|
const gradientAxis = (_gradient$axis = gradient.axis) !== null && _gradient$axis !== void 0 ? _gradient$axis : 'y';
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
// Determine the correct data value to evaluate against based on gradient axis and layout
|
|
68
|
+
let evalValue;
|
|
69
|
+
if (gradientAxis === 'x') {
|
|
70
|
+
// X-axis gradient: In vertical it's the index, in horizontal it's the value.
|
|
71
|
+
evalValue = categoryAxisIsX ? dataIndexValue : dataValue;
|
|
72
|
+
} else {
|
|
73
|
+
// Y-axis gradient: In vertical it's the value, in horizontal it's the index.
|
|
74
|
+
evalValue = categoryAxisIsX ? dataValue : dataIndexValue;
|
|
75
|
+
}
|
|
76
|
+
const evaluatedColor = evaluateGradientAtValue(stops, evalValue, gradientScale);
|
|
65
77
|
if (evaluatedColor) {
|
|
66
78
|
return evaluatedColor;
|
|
67
79
|
}
|
|
@@ -69,15 +81,16 @@ const BeaconWithData = /*#__PURE__*/memo(_ref => {
|
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
return (_series$color2 = series === null || series === void 0 ? void 0 : series.color) !== null && _series$color2 !== void 0 ? _series$color2 : 'var(--color-fgPrimary)';
|
|
72
|
-
}, [gradient,
|
|
73
|
-
if (
|
|
84
|
+
}, [gradient, dataIndexValue, dataValue, series === null || series === void 0 ? void 0 : series.color, series === null || series === void 0 ? void 0 : series.xAxisId, series === null || series === void 0 ? void 0 : series.yAxisId, getXScale, getYScale, layout]);
|
|
85
|
+
if (dataValue === undefined) return null;
|
|
86
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
74
87
|
return /*#__PURE__*/_jsx(BeaconComponent, {
|
|
75
88
|
ref: beaconRef,
|
|
76
89
|
animate: animate,
|
|
77
90
|
className: className,
|
|
78
91
|
color: color,
|
|
79
|
-
dataX:
|
|
80
|
-
dataY:
|
|
92
|
+
dataX: categoryAxisIsX ? dataIndexValue : dataValue,
|
|
93
|
+
dataY: categoryAxisIsX ? dataValue : dataIndexValue,
|
|
81
94
|
idlePulse: idlePulse,
|
|
82
95
|
isIdle: isIdle,
|
|
83
96
|
seriesId: seriesId,
|
|
@@ -103,8 +116,11 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
103
116
|
scrubberPosition
|
|
104
117
|
} = useScrubberContext();
|
|
105
118
|
const {
|
|
119
|
+
layout,
|
|
106
120
|
getXScale,
|
|
121
|
+
getYScale,
|
|
107
122
|
getXAxis,
|
|
123
|
+
getYAxis,
|
|
108
124
|
dataLength,
|
|
109
125
|
series,
|
|
110
126
|
animate
|
|
@@ -123,30 +139,31 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
123
139
|
return (_series$filter = series === null || series === void 0 ? void 0 : series.filter(s => seriesIds.includes(s.id))) !== null && _series$filter !== void 0 ? _series$filter : [];
|
|
124
140
|
}, [series, seriesIds]);
|
|
125
141
|
const {
|
|
126
|
-
|
|
142
|
+
dataIndexValue,
|
|
127
143
|
dataIndex
|
|
128
144
|
} = useMemo(() => {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
146
|
+
const indexScale = categoryAxisIsX ? getXScale() : getYScale();
|
|
147
|
+
const indexAxis = categoryAxisIsX ? getXAxis() : getYAxis();
|
|
148
|
+
if (!indexScale) return {
|
|
149
|
+
dataIndexValue: undefined,
|
|
133
150
|
dataIndex: undefined
|
|
134
151
|
};
|
|
135
152
|
const dataIndex = scrubberPosition !== null && scrubberPosition !== void 0 ? scrubberPosition : Math.max(0, dataLength - 1);
|
|
136
153
|
|
|
137
|
-
// Convert index to actual
|
|
138
|
-
let
|
|
139
|
-
if (
|
|
140
|
-
const
|
|
141
|
-
|
|
154
|
+
// Convert index to actual data value if axis has data
|
|
155
|
+
let dataIndexValue;
|
|
156
|
+
if (indexAxis !== null && indexAxis !== void 0 && indexAxis.data && Array.isArray(indexAxis.data) && indexAxis.data[dataIndex] !== undefined) {
|
|
157
|
+
const val = indexAxis.data[dataIndex];
|
|
158
|
+
dataIndexValue = typeof val === 'string' ? dataIndex : val;
|
|
142
159
|
} else {
|
|
143
|
-
|
|
160
|
+
dataIndexValue = dataIndex;
|
|
144
161
|
}
|
|
145
162
|
return {
|
|
146
|
-
|
|
163
|
+
dataIndexValue,
|
|
147
164
|
dataIndex
|
|
148
165
|
};
|
|
149
|
-
}, [getXScale, getXAxis, scrubberPosition, dataLength]);
|
|
166
|
+
}, [getXScale, getYScale, getXAxis, getYAxis, scrubberPosition, dataLength, layout]);
|
|
150
167
|
const isIdle = scrubberPosition === undefined;
|
|
151
168
|
const createBeaconRef = useCallback(seriesId => {
|
|
152
169
|
return beaconRef => {
|
|
@@ -155,14 +172,14 @@ export const ScrubberBeaconGroup = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_r
|
|
|
155
172
|
}
|
|
156
173
|
};
|
|
157
174
|
}, [ScrubberBeaconRefs]);
|
|
158
|
-
if (
|
|
175
|
+
if (dataIndexValue === undefined || dataIndex === undefined) return null;
|
|
159
176
|
return filteredSeries.map(s => /*#__PURE__*/_jsx(BeaconWithData, {
|
|
160
177
|
BeaconComponent: BeaconComponent,
|
|
161
178
|
animate: animate,
|
|
162
179
|
beaconRef: createBeaconRef(s.id),
|
|
163
180
|
className: className,
|
|
164
181
|
dataIndex: dataIndex,
|
|
165
|
-
|
|
182
|
+
dataIndexValue: dataIndexValue,
|
|
166
183
|
idlePulse: idlePulse,
|
|
167
184
|
isIdle: isIdle,
|
|
168
185
|
seriesId: s.id,
|
|
@@ -46,7 +46,7 @@ const PositionedLabel = /*#__PURE__*/memo(_ref => {
|
|
|
46
46
|
onDimensionsChange: d => onDimensionsChange(seriesId, d),
|
|
47
47
|
seriesId: seriesId,
|
|
48
48
|
style: style,
|
|
49
|
-
transition: updateTransition,
|
|
49
|
+
transition: updateTransition !== null && updateTransition !== void 0 ? updateTransition : instantTransition,
|
|
50
50
|
x: x,
|
|
51
51
|
y: y
|
|
52
52
|
});
|
|
@@ -18,25 +18,29 @@ export const ScrubberProvider = _ref => {
|
|
|
18
18
|
throw new Error('ScrubberProvider must be used within a ChartContext');
|
|
19
19
|
}
|
|
20
20
|
const {
|
|
21
|
+
layout,
|
|
21
22
|
getXScale,
|
|
23
|
+
getYScale,
|
|
22
24
|
getXAxis,
|
|
25
|
+
getYAxis,
|
|
23
26
|
series
|
|
24
27
|
} = chartContext;
|
|
25
28
|
const [scrubberPosition, setScrubberPosition] = useState(undefined);
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
29
|
+
const getDataIndexFromPosition = useCallback(mousePosition => {
|
|
30
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
31
|
+
const categoryScale = categoryAxisIsX ? getXScale() : getYScale();
|
|
32
|
+
const categoryAxis = categoryAxisIsX ? getXAxis() : getYAxis();
|
|
33
|
+
if (!categoryScale || !categoryAxis) return 0;
|
|
34
|
+
if (isCategoricalScale(categoryScale)) {
|
|
35
|
+
var _ref2, _categoryScale$domain, _categoryScale$domain2, _categoryScale$bandwi, _categoryScale$bandwi2;
|
|
36
|
+
const categories = (_ref2 = (_categoryScale$domain = (_categoryScale$domain2 = categoryScale.domain) === null || _categoryScale$domain2 === void 0 ? void 0 : _categoryScale$domain2.call(categoryScale)) !== null && _categoryScale$domain !== void 0 ? _categoryScale$domain : categoryAxis.data) !== null && _ref2 !== void 0 ? _ref2 : [];
|
|
37
|
+
const bandwidth = (_categoryScale$bandwi = (_categoryScale$bandwi2 = categoryScale.bandwidth) === null || _categoryScale$bandwi2 === void 0 ? void 0 : _categoryScale$bandwi2.call(categoryScale)) !== null && _categoryScale$bandwi !== void 0 ? _categoryScale$bandwi : 0;
|
|
34
38
|
let closestIndex = 0;
|
|
35
39
|
let closestDistance = Infinity;
|
|
36
40
|
for (let i = 0; i < categories.length; i++) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
const distance = Math.abs(
|
|
41
|
+
const pos = categoryScale(i);
|
|
42
|
+
if (pos !== undefined) {
|
|
43
|
+
const distance = Math.abs(mousePosition - (pos + bandwidth / 2));
|
|
40
44
|
if (distance < closestDistance) {
|
|
41
45
|
closestDistance = distance;
|
|
42
46
|
closestIndex = i;
|
|
@@ -46,17 +50,17 @@ export const ScrubberProvider = _ref => {
|
|
|
46
50
|
return closestIndex;
|
|
47
51
|
} else {
|
|
48
52
|
// For numeric scales with axis data, find the nearest data point
|
|
49
|
-
const axisData =
|
|
53
|
+
const axisData = categoryAxis.data;
|
|
50
54
|
if (axisData && Array.isArray(axisData) && typeof axisData[0] === 'number') {
|
|
51
55
|
// We have numeric axis data - find the closest data point
|
|
52
56
|
const numericData = axisData;
|
|
53
57
|
let closestIndex = 0;
|
|
54
58
|
let closestDistance = Infinity;
|
|
55
59
|
for (let i = 0; i < numericData.length; i++) {
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
const distance = Math.abs(
|
|
60
|
+
const dataValue = numericData[i];
|
|
61
|
+
const pos = categoryScale(dataValue);
|
|
62
|
+
if (pos !== undefined) {
|
|
63
|
+
const distance = Math.abs(mousePosition - pos);
|
|
60
64
|
if (distance < closestDistance) {
|
|
61
65
|
closestDistance = distance;
|
|
62
66
|
closestIndex = i;
|
|
@@ -66,26 +70,26 @@ export const ScrubberProvider = _ref => {
|
|
|
66
70
|
return closestIndex;
|
|
67
71
|
} else {
|
|
68
72
|
var _domain$min, _domain$max;
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
const domain =
|
|
72
|
-
return Math.max((_domain$min = domain.min) !== null && _domain$min !== void 0 ? _domain$min : 0, Math.min(
|
|
73
|
+
const dataValue = categoryScale.invert(mousePosition);
|
|
74
|
+
const dataIndexVal = Math.round(dataValue);
|
|
75
|
+
const domain = categoryAxis.domain;
|
|
76
|
+
return Math.max((_domain$min = domain.min) !== null && _domain$min !== void 0 ? _domain$min : 0, Math.min(dataIndexVal, (_domain$max = domain.max) !== null && _domain$max !== void 0 ? _domain$max : 0));
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
|
-
}, [getXScale, getXAxis]);
|
|
76
|
-
const handlePointerMove = useCallback((clientX, target) => {
|
|
79
|
+
}, [layout, getXScale, getYScale, getXAxis, getYAxis]);
|
|
80
|
+
const handlePointerMove = useCallback((clientX, clientY, target) => {
|
|
77
81
|
if (!enableScrubbing || !series || series.length === 0) return;
|
|
78
82
|
const rect = target.getBoundingClientRect();
|
|
79
|
-
const
|
|
80
|
-
const dataIndex =
|
|
83
|
+
const position = layout === 'horizontal' ? clientY - rect.top : clientX - rect.left;
|
|
84
|
+
const dataIndex = getDataIndexFromPosition(position);
|
|
81
85
|
if (dataIndex !== scrubberPosition) {
|
|
82
86
|
setScrubberPosition(dataIndex);
|
|
83
87
|
onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(dataIndex);
|
|
84
88
|
}
|
|
85
|
-
}, [enableScrubbing, series,
|
|
89
|
+
}, [enableScrubbing, series, layout, getDataIndexFromPosition, scrubberPosition, onScrubberPositionChange]);
|
|
86
90
|
const handleMouseMove = useCallback(event => {
|
|
87
91
|
const target = event.currentTarget;
|
|
88
|
-
handlePointerMove(event.clientX, target);
|
|
92
|
+
handlePointerMove(event.clientX, event.clientY, target);
|
|
89
93
|
}, [handlePointerMove]);
|
|
90
94
|
const handleTouchMove = useCallback(event => {
|
|
91
95
|
if (!event.touches.length) return;
|
|
@@ -93,14 +97,14 @@ export const ScrubberProvider = _ref => {
|
|
|
93
97
|
event.preventDefault();
|
|
94
98
|
const touch = event.touches[0];
|
|
95
99
|
const target = event.currentTarget;
|
|
96
|
-
handlePointerMove(touch.clientX, target);
|
|
100
|
+
handlePointerMove(touch.clientX, touch.clientY, target);
|
|
97
101
|
}, [handlePointerMove]);
|
|
98
102
|
const handleTouchStart = useCallback(event => {
|
|
99
103
|
if (!enableScrubbing || !event.touches.length) return;
|
|
100
104
|
// Handle initial touch
|
|
101
105
|
const touch = event.touches[0];
|
|
102
106
|
const target = event.currentTarget;
|
|
103
|
-
handlePointerMove(touch.clientX, target);
|
|
107
|
+
handlePointerMove(touch.clientX, touch.clientY, target);
|
|
104
108
|
}, [enableScrubbing, handlePointerMove]);
|
|
105
109
|
const handlePointerLeave = useCallback(() => {
|
|
106
110
|
if (!enableScrubbing) return;
|
|
@@ -111,25 +115,26 @@ export const ScrubberProvider = _ref => {
|
|
|
111
115
|
const handleTouchEnd = handlePointerLeave;
|
|
112
116
|
const handleKeyDown = useCallback(event => {
|
|
113
117
|
if (!enableScrubbing) return;
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
119
|
+
const categoryScale = categoryAxisIsX ? getXScale() : getYScale();
|
|
120
|
+
const categoryAxis = categoryAxisIsX ? getXAxis() : getYAxis();
|
|
121
|
+
if (!categoryScale || !categoryAxis) return;
|
|
122
|
+
const isBand = isCategoricalScale(categoryScale);
|
|
118
123
|
|
|
119
124
|
// Determine the actual data indices we can navigate to
|
|
120
125
|
let minIndex;
|
|
121
126
|
let maxIndex;
|
|
122
127
|
let dataPoints;
|
|
123
128
|
if (isBand) {
|
|
124
|
-
var _ref3,
|
|
129
|
+
var _ref3, _categoryScale$domain3, _categoryScale$domain4;
|
|
125
130
|
// For categorical scales, use the categories
|
|
126
|
-
const categories = (_ref3 = (
|
|
131
|
+
const categories = (_ref3 = (_categoryScale$domain3 = (_categoryScale$domain4 = categoryScale.domain) === null || _categoryScale$domain4 === void 0 ? void 0 : _categoryScale$domain4.call(categoryScale)) !== null && _categoryScale$domain3 !== void 0 ? _categoryScale$domain3 : categoryAxis.data) !== null && _ref3 !== void 0 ? _ref3 : [];
|
|
127
132
|
minIndex = 0;
|
|
128
133
|
maxIndex = Math.max(0, categories.length - 1);
|
|
129
134
|
dataPoints = categories.length;
|
|
130
135
|
} else {
|
|
131
136
|
// For numeric scales, check if we have specific data points
|
|
132
|
-
const axisData =
|
|
137
|
+
const axisData = categoryAxis.data;
|
|
133
138
|
if (axisData && Array.isArray(axisData)) {
|
|
134
139
|
// We have specific data points - use their indices
|
|
135
140
|
minIndex = 0;
|
|
@@ -138,7 +143,7 @@ export const ScrubberProvider = _ref => {
|
|
|
138
143
|
} else {
|
|
139
144
|
var _domain$min2, _domain$max2;
|
|
140
145
|
// Fall back to domain-based navigation for continuous scales without specific data
|
|
141
|
-
const domain =
|
|
146
|
+
const domain = categoryAxis.domain;
|
|
142
147
|
minIndex = (_domain$min2 = domain.min) !== null && _domain$min2 !== void 0 ? _domain$min2 : 0;
|
|
143
148
|
maxIndex = (_domain$max2 = domain.max) !== null && _domain$max2 !== void 0 ? _domain$max2 : 0;
|
|
144
149
|
dataPoints = maxIndex - minIndex + 1;
|
|
@@ -152,11 +157,11 @@ export const ScrubberProvider = _ref => {
|
|
|
152
157
|
const stepSize = multiSkip ? Math.min(10, Math.max(1, Math.floor(dataRange * 0.1))) : 1;
|
|
153
158
|
let newIndex;
|
|
154
159
|
switch (event.key) {
|
|
155
|
-
case 'ArrowLeft':
|
|
160
|
+
case categoryAxisIsX ? 'ArrowLeft' : 'ArrowUp':
|
|
156
161
|
event.preventDefault();
|
|
157
162
|
newIndex = Math.max(minIndex, currentIndex - stepSize);
|
|
158
163
|
break;
|
|
159
|
-
case 'ArrowRight':
|
|
164
|
+
case categoryAxisIsX ? 'ArrowRight' : 'ArrowDown':
|
|
160
165
|
event.preventDefault();
|
|
161
166
|
newIndex = Math.min(maxIndex, currentIndex + stepSize);
|
|
162
167
|
break;
|
|
@@ -180,7 +185,7 @@ export const ScrubberProvider = _ref => {
|
|
|
180
185
|
setScrubberPosition(newIndex);
|
|
181
186
|
onScrubberPositionChange === null || onScrubberPositionChange === void 0 || onScrubberPositionChange(newIndex);
|
|
182
187
|
}
|
|
183
|
-
}, [enableScrubbing, getXScale, getXAxis, scrubberPosition, onScrubberPositionChange]);
|
|
188
|
+
}, [enableScrubbing, layout, getXScale, getYScale, getXAxis, getYAxis, scrubberPosition, onScrubberPositionChange]);
|
|
184
189
|
const handleBlur = useCallback(() => {
|
|
185
190
|
if (!enableScrubbing || scrubberPosition === undefined) return;
|
|
186
191
|
setScrubberPosition(undefined);
|
package/esm/chart/utils/axis.js
CHANGED
|
@@ -58,23 +58,36 @@ export const toPointAnchor = placement => {
|
|
|
58
58
|
* For numeric scales, the domain limit controls whether bounds are "nice" (human-friendly)
|
|
59
59
|
* or "strict" (exact min/max). Range can be customized using function-based configuration.
|
|
60
60
|
*
|
|
61
|
+
* Range inversion is determined by axis role (category vs value) and layout:
|
|
62
|
+
* - Vertical layout: Y axis (value) is inverted for SVG coordinate system
|
|
63
|
+
* - Horizontal layout: Y axis (category) is inverted (first category at top)
|
|
64
|
+
*
|
|
61
65
|
* @param params - Scale parameters
|
|
62
66
|
* @returns The D3 scale function
|
|
63
67
|
* @throws An Error if bounds are invalid
|
|
64
68
|
*/
|
|
65
|
-
export const
|
|
69
|
+
export const getCartesianAxisScale = _ref => {
|
|
66
70
|
var _config$scaleType;
|
|
67
71
|
let {
|
|
68
72
|
config,
|
|
69
73
|
type,
|
|
70
74
|
range,
|
|
71
|
-
dataDomain
|
|
75
|
+
dataDomain,
|
|
76
|
+
layout = 'vertical'
|
|
72
77
|
} = _ref;
|
|
73
78
|
const scaleType = (_config$scaleType = config === null || config === void 0 ? void 0 : config.scaleType) !== null && _config$scaleType !== void 0 ? _config$scaleType : 'linear';
|
|
74
79
|
let adjustedRange = range;
|
|
75
80
|
|
|
76
|
-
//
|
|
77
|
-
|
|
81
|
+
// Determine if this axis needs range inversion for SVG coordinate system.
|
|
82
|
+
// SVG Y coordinates increase downward, so we need to invert for value axes
|
|
83
|
+
// where we want higher values at the top.
|
|
84
|
+
//
|
|
85
|
+
// For vertical layout: Y axis is the value axis → invert (higher values at top)
|
|
86
|
+
// For horizontal layout: Y axis is the category axis → don't invert (first category at top is natural)
|
|
87
|
+
// X axis never needs inversion (left-to-right is natural for both layouts)
|
|
88
|
+
|
|
89
|
+
const shouldInvertRange = type === 'y' && layout !== 'horizontal';
|
|
90
|
+
if (shouldInvertRange) {
|
|
78
91
|
adjustedRange = {
|
|
79
92
|
min: adjustedRange.max,
|
|
80
93
|
max: adjustedRange.min
|
|
@@ -122,6 +135,8 @@ export const getAxisConfig = function (type, axes) {
|
|
|
122
135
|
let defaultId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultAxisId;
|
|
123
136
|
let defaultScaleType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultAxisScaleType;
|
|
124
137
|
const defaultDomainLimit = type === 'x' ? 'strict' : 'nice';
|
|
138
|
+
const axisName = type === 'x' ? 'x-axis' : 'y-axis';
|
|
139
|
+
const axisDocUrl = type === 'x' ? 'https://cds.coinbase.com/components/charts/XAxis' : 'https://cds.coinbase.com/components/charts/YAxis';
|
|
125
140
|
if (!axes) {
|
|
126
141
|
return [{
|
|
127
142
|
id: defaultId,
|
|
@@ -138,16 +153,27 @@ export const getAxisConfig = function (type, axes) {
|
|
|
138
153
|
} = _ref2;
|
|
139
154
|
return id === undefined;
|
|
140
155
|
})) {
|
|
141
|
-
throw new Error(
|
|
156
|
+
throw new Error("When defining multiple ".concat(axisName, ", each must have a unique id. See ").concat(axisDocUrl, "."));
|
|
157
|
+
}
|
|
158
|
+
if (axesLength > 1) {
|
|
159
|
+
const ids = axes.map(_ref3 => {
|
|
160
|
+
let {
|
|
161
|
+
id
|
|
162
|
+
} = _ref3;
|
|
163
|
+
return id;
|
|
164
|
+
}).filter(id => id !== undefined);
|
|
165
|
+
if (new Set(ids).size !== ids.length) {
|
|
166
|
+
throw new Error("When defining multiple ".concat(axisName, ", each must have a unique id. See ").concat(axisDocUrl, "."));
|
|
167
|
+
}
|
|
142
168
|
}
|
|
143
|
-
return axes.map(
|
|
169
|
+
return axes.map(_ref4 => {
|
|
144
170
|
let {
|
|
145
171
|
id
|
|
146
|
-
} =
|
|
147
|
-
axis = _objectWithoutProperties(
|
|
172
|
+
} = _ref4,
|
|
173
|
+
axis = _objectWithoutProperties(_ref4, _excluded);
|
|
148
174
|
return _objectSpread({
|
|
149
175
|
// defaults the axis id if only a single axis is provided
|
|
150
|
-
id: axesLength > 1 ? id !== null && id !== void 0 ? id : defaultAxisId : id,
|
|
176
|
+
id: axesLength > 1 ? id !== null && id !== void 0 ? id : defaultAxisId : id !== null && id !== void 0 ? id : defaultId,
|
|
151
177
|
scaleType: defaultScaleType,
|
|
152
178
|
domainLimit: defaultDomainLimit
|
|
153
179
|
}, axis);
|
|
@@ -169,10 +195,12 @@ export const getAxisConfig = function (type, axes) {
|
|
|
169
195
|
* @param axisParam - The axis configuration
|
|
170
196
|
* @param series - Array of series objects (for stacking support)
|
|
171
197
|
* @param axisType - Whether this is an 'x' or 'y' axis
|
|
198
|
+
* @param layout - Chart layout ('horizontal' or 'vertical')
|
|
172
199
|
* @returns The calculated axis bounds
|
|
173
200
|
*/
|
|
174
|
-
export const
|
|
201
|
+
export const getCartesianAxisDomain = function (axisParam, series, axisType) {
|
|
175
202
|
var _finalDomain$min, _finalDomain$max;
|
|
203
|
+
let layout = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'vertical';
|
|
176
204
|
let dataDomain = null;
|
|
177
205
|
if (axisParam.data && Array.isArray(axisParam.data) && axisParam.data.length > 0) {
|
|
178
206
|
const firstItem = axisParam.data[0];
|
|
@@ -194,7 +222,10 @@ export const getAxisDomain = (axisParam, series, axisType) => {
|
|
|
194
222
|
}
|
|
195
223
|
|
|
196
224
|
// Calculate domain from series data
|
|
197
|
-
|
|
225
|
+
// In vertical layout: X is category (index), Y is value (value)
|
|
226
|
+
// In horizontal layout: Y is category (index), X is value (value)
|
|
227
|
+
const isCategoryAxis = layout !== 'horizontal' && axisType === 'x' || layout === 'horizontal' && axisType === 'y';
|
|
228
|
+
const seriesDomain = isCategoryAxis ? getChartDomain(series) : getChartRange(series);
|
|
198
229
|
|
|
199
230
|
// If data sets the domain, use that instead of the series domain
|
|
200
231
|
const preferredDataDomain = dataDomain !== null && dataDomain !== void 0 ? dataDomain : seriesDomain;
|
|
@@ -488,7 +519,7 @@ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues,
|
|
|
488
519
|
* });
|
|
489
520
|
* // Returns tick positions centered in each selected band
|
|
490
521
|
*/
|
|
491
|
-
export const getAxisTicksData =
|
|
522
|
+
export const getAxisTicksData = _ref5 => {
|
|
492
523
|
var _options$anchor;
|
|
493
524
|
let {
|
|
494
525
|
ticks,
|
|
@@ -498,7 +529,7 @@ export const getAxisTicksData = _ref4 => {
|
|
|
498
529
|
possibleTickValues,
|
|
499
530
|
tickInterval,
|
|
500
531
|
options
|
|
501
|
-
} =
|
|
532
|
+
} = _ref5;
|
|
502
533
|
const anchor = (_options$anchor = options === null || options === void 0 ? void 0 : options.anchor) !== null && _options$anchor !== void 0 ? _options$anchor : 'middle';
|
|
503
534
|
|
|
504
535
|
// Handle band scales
|