@coinbase/cds-mobile-visualization 3.4.0-beta.9 → 3.4.0
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 +132 -0
- package/dts/chart/CartesianChart.d.ts +92 -7
- package/dts/chart/CartesianChart.d.ts.map +1 -1
- package/dts/chart/ChartContextBridge.d.ts.map +1 -1
- package/dts/chart/ChartProvider.d.ts +3 -0
- package/dts/chart/ChartProvider.d.ts.map +1 -1
- package/dts/chart/Path.d.ts +36 -13
- package/dts/chart/Path.d.ts.map +1 -1
- package/dts/chart/PeriodSelector.d.ts +20 -5
- package/dts/chart/PeriodSelector.d.ts.map +1 -1
- package/dts/chart/area/Area.d.ts +14 -11
- package/dts/chart/area/Area.d.ts.map +1 -1
- package/dts/chart/area/AreaChart.d.ts +33 -9
- 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 +22 -42
- 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 +51 -51
- package/dts/chart/bar/Bar.d.ts.map +1 -1
- package/dts/chart/bar/BarChart.d.ts +56 -11
- package/dts/chart/bar/BarChart.d.ts.map +1 -1
- package/dts/chart/bar/BarPlot.d.ts +2 -1
- package/dts/chart/bar/BarPlot.d.ts.map +1 -1
- package/dts/chart/bar/BarStack.d.ts +45 -20
- package/dts/chart/bar/BarStack.d.ts.map +1 -1
- package/dts/chart/bar/BarStackGroup.d.ts +2 -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 +5 -0
- package/dts/chart/gradient/Gradient.d.ts.map +1 -1
- package/dts/chart/index.d.ts +1 -0
- package/dts/chart/index.d.ts.map +1 -1
- package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
- package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
- package/dts/chart/legend/Legend.d.ts +168 -0
- package/dts/chart/legend/Legend.d.ts.map +1 -0
- package/dts/chart/legend/index.d.ts +4 -0
- package/dts/chart/legend/index.d.ts.map +1 -0
- package/dts/chart/line/DottedLine.d.ts.map +1 -1
- package/dts/chart/line/Line.d.ts +23 -19
- package/dts/chart/line/Line.d.ts.map +1 -1
- package/dts/chart/line/LineChart.d.ts +26 -9
- 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 +26 -2
- package/dts/chart/point/Point.d.ts.map +1 -1
- package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +32 -2
- 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 +86 -17
- package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
- package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +10 -0
- package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +16 -1
- package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
- package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
- package/dts/chart/utils/axis.d.ts +45 -10
- package/dts/chart/utils/axis.d.ts.map +1 -1
- package/dts/chart/utils/bar.d.ts +190 -0
- package/dts/chart/utils/bar.d.ts.map +1 -1
- package/dts/chart/utils/chart.d.ts +32 -0
- package/dts/chart/utils/chart.d.ts.map +1 -1
- package/dts/chart/utils/context.d.ts +21 -6
- 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 +26 -0
- package/dts/chart/utils/path.d.ts.map +1 -1
- package/dts/chart/utils/point.d.ts +24 -12
- package/dts/chart/utils/point.d.ts.map +1 -1
- package/dts/chart/utils/scale.d.ts +11 -0
- package/dts/chart/utils/scale.d.ts.map +1 -1
- package/dts/chart/utils/scrubber.d.ts +2 -1
- package/dts/chart/utils/scrubber.d.ts.map +1 -1
- package/dts/chart/utils/transition.d.ts +63 -22
- package/dts/chart/utils/transition.d.ts.map +1 -1
- package/dts/sparkline/Sparkline.d.ts +2 -1
- package/dts/sparkline/Sparkline.d.ts.map +1 -1
- package/dts/sparkline/SparklineArea.d.ts +2 -1
- package/dts/sparkline/SparklineArea.d.ts.map +1 -1
- package/dts/sparkline/SparklineGradient.d.ts +2 -1
- package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
- package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
- package/esm/chart/CartesianChart.js +176 -82
- package/esm/chart/ChartContextBridge.js +14 -3
- package/esm/chart/ChartProvider.js +2 -2
- package/esm/chart/Path.js +34 -29
- package/esm/chart/PeriodSelector.js +5 -1
- package/esm/chart/__stories__/CartesianChart.stories.js +16 -80
- package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
- package/esm/chart/__stories__/ChartTransitions.stories.js +625 -0
- package/esm/chart/__stories__/PeriodSelector.stories.js +99 -1
- package/esm/chart/area/Area.js +21 -9
- package/esm/chart/area/AreaChart.js +18 -13
- package/esm/chart/area/DottedArea.js +28 -18
- package/esm/chart/area/GradientArea.js +14 -7
- package/esm/chart/area/SolidArea.js +6 -2
- package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
- package/esm/chart/axis/Axis.js +5 -41
- package/esm/chart/axis/XAxis.js +116 -47
- package/esm/chart/axis/YAxis.js +105 -26
- package/esm/chart/axis/__stories__/Axis.stories.js +324 -48
- package/esm/chart/bar/Bar.js +17 -15
- package/esm/chart/bar/BarChart.js +38 -33
- package/esm/chart/bar/BarPlot.js +40 -45
- package/esm/chart/bar/BarStack.js +92 -475
- package/esm/chart/bar/BarStackGroup.js +37 -27
- package/esm/chart/bar/DefaultBar.js +27 -18
- package/esm/chart/bar/DefaultBarStack.js +25 -9
- package/esm/chart/bar/__stories__/BarChart.stories.js +728 -54
- package/esm/chart/gradient/Gradient.js +2 -1
- package/esm/chart/index.js +1 -0
- package/esm/chart/legend/DefaultLegendEntry.js +42 -0
- package/esm/chart/legend/DefaultLegendShape.js +64 -0
- package/esm/chart/legend/Legend.js +59 -0
- package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
- package/esm/chart/legend/index.js +3 -0
- package/esm/chart/line/DottedLine.js +6 -2
- package/esm/chart/line/Line.js +42 -38
- package/esm/chart/line/LineChart.js +36 -12
- package/esm/chart/line/SolidLine.js +6 -2
- package/esm/chart/line/__stories__/LineChart.stories.js +236 -590
- package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
- package/esm/chart/point/Point.js +35 -36
- package/esm/chart/scrubber/DefaultScrubberBeacon.js +41 -38
- package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
- package/esm/chart/scrubber/Scrubber.js +67 -35
- package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
- package/esm/chart/scrubber/ScrubberBeaconGroup.js +30 -22
- package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +35 -8
- package/esm/chart/scrubber/ScrubberProvider.js +29 -24
- package/esm/chart/scrubber/__stories__/Scrubber.stories.js +946 -0
- package/esm/chart/utils/axis.js +88 -44
- package/esm/chart/utils/bar.js +820 -0
- package/esm/chart/utils/chart.js +34 -7
- package/esm/chart/utils/context.js +7 -0
- package/esm/chart/utils/gradient.js +8 -4
- package/esm/chart/utils/path.js +91 -61
- package/esm/chart/utils/point.js +92 -39
- package/esm/chart/utils/scale.js +13 -2
- package/esm/chart/utils/scrubber.js +12 -5
- package/esm/chart/utils/transition.js +108 -60
- package/esm/sparkline/Sparkline.js +2 -1
- package/esm/sparkline/SparklineArea.js +2 -1
- package/esm/sparkline/SparklineGradient.js +2 -1
- package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
- package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
- package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +2 -0
- package/package.json +5 -6
- package/esm/chart/__stories__/Chart.stories.js +0 -77
package/esm/chart/utils/chart.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { isSharedValue } from 'react-native-reanimated';
|
|
2
2
|
import { stack as d3Stack, stackOffsetDiverging, stackOrderNone } from 'd3-shape';
|
|
3
3
|
export const defaultStackId = 'DEFAULT_STACK_ID';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shape variants available for legend items.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shape for legend items. Can be a preset variant or a custom ReactNode.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Position of the legend relative to the chart.
|
|
15
|
+
*/
|
|
16
|
+
|
|
4
17
|
/**
|
|
5
18
|
* Type guard to check if bounds are complete with both min and max values.
|
|
6
19
|
* @param bounds - The bounds to validate
|
|
@@ -33,15 +46,16 @@ export const getChartDomain = (series, min, max) => {
|
|
|
33
46
|
};
|
|
34
47
|
|
|
35
48
|
/**
|
|
36
|
-
* Creates a composite stack key that includes
|
|
37
|
-
* This ensures series with different
|
|
49
|
+
* Creates a composite stack key that includes stack ID and axis IDs.
|
|
50
|
+
* This ensures series with different scales don't get stacked together.
|
|
38
51
|
*/
|
|
39
52
|
const createStackKey = series => {
|
|
40
53
|
if (series.stackId === undefined) return undefined;
|
|
41
54
|
|
|
42
|
-
// Include
|
|
55
|
+
// Include axis IDs to prevent cross-scale stacking
|
|
56
|
+
const xAxisId = series.xAxisId || 'default';
|
|
43
57
|
const yAxisId = series.yAxisId || 'default';
|
|
44
|
-
return series.stackId + ":" + yAxisId;
|
|
58
|
+
return series.stackId + ":" + xAxisId + ":" + yAxisId;
|
|
45
59
|
};
|
|
46
60
|
|
|
47
61
|
/**
|
|
@@ -127,9 +141,9 @@ export const getLineData = data => {
|
|
|
127
141
|
const firstNonNull = data.find(d => d !== null);
|
|
128
142
|
if (Array.isArray(firstNonNull)) {
|
|
129
143
|
return data.map(d => {
|
|
130
|
-
var _d
|
|
144
|
+
var _d;
|
|
131
145
|
if (d === null) return null;
|
|
132
|
-
if (Array.isArray(d)) return (_d
|
|
146
|
+
if (Array.isArray(d)) return (_d = d[d.length - 1]) != null ? _d : null;
|
|
133
147
|
return d;
|
|
134
148
|
});
|
|
135
149
|
}
|
|
@@ -212,12 +226,25 @@ export const getChartRange = (series, min, max) => {
|
|
|
212
226
|
}
|
|
213
227
|
return range;
|
|
214
228
|
};
|
|
215
|
-
export const
|
|
229
|
+
export const defaultVerticalLayoutChartInset = {
|
|
216
230
|
top: 32,
|
|
217
231
|
left: 16,
|
|
218
232
|
bottom: 16,
|
|
219
233
|
right: 16
|
|
220
234
|
};
|
|
235
|
+
export const defaultHorizontalLayoutChartInset = {
|
|
236
|
+
top: 16,
|
|
237
|
+
left: 16,
|
|
238
|
+
bottom: 16,
|
|
239
|
+
right: 48
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @deprecated Use `defaultVerticalLayoutChartInset` for vertical layout charts or. This will be removed in a future major release.
|
|
244
|
+
* @deprecationExpectedRemoval v4
|
|
245
|
+
* `defaultHorizontalLayoutChartInset` for horizontal layout charts.
|
|
246
|
+
*/
|
|
247
|
+
export const defaultChartInset = defaultVerticalLayoutChartInset;
|
|
221
248
|
|
|
222
249
|
/**
|
|
223
250
|
* Normalize padding to include all sides with a value.
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Chart layout for Cartesian charts.
|
|
5
|
+
* Describes the direction bars/areas grow.
|
|
6
|
+
* - 'vertical': Bars grow vertically (up/down). X is category axis, Y is value axis.
|
|
7
|
+
* - 'horizontal': Bars grow horizontally (left/right). Y is category axis, X is value axis.
|
|
8
|
+
*/
|
|
9
|
+
|
|
3
10
|
/**
|
|
4
11
|
* Context value for Cartesian (X/Y) coordinate charts.
|
|
5
12
|
* Contains axis-specific methods and properties for rectangular coordinate systems.
|
|
@@ -262,9 +262,13 @@ export const getBaseline = function (axisBounds, baseline) {
|
|
|
262
262
|
* @param fill - The color to use for the gradient
|
|
263
263
|
* @param peakOpacity - Opacity at the peak of the gradient
|
|
264
264
|
* @param baselineOpacity - Opacity at the baseline
|
|
265
|
-
* @
|
|
265
|
+
* @param axis - The axis the gradient maps to ('y' for vertical, 'x' for horizontal layout)
|
|
266
|
+
* @returns A gradient definition with stops in ascending order
|
|
266
267
|
*/
|
|
267
|
-
export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity)
|
|
268
|
+
export const createGradient = function (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity, axis) {
|
|
269
|
+
if (axis === void 0) {
|
|
270
|
+
axis = 'y';
|
|
271
|
+
}
|
|
268
272
|
const {
|
|
269
273
|
min,
|
|
270
274
|
max
|
|
@@ -273,7 +277,7 @@ export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, bas
|
|
|
273
277
|
const upperBound = Math.max(min, max);
|
|
274
278
|
if (lowerBound < baselineValue && baselineValue < upperBound) {
|
|
275
279
|
return {
|
|
276
|
-
axis
|
|
280
|
+
axis,
|
|
277
281
|
stops: [{
|
|
278
282
|
offset: lowerBound,
|
|
279
283
|
color: fill,
|
|
@@ -291,7 +295,7 @@ export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, bas
|
|
|
291
295
|
}
|
|
292
296
|
const peakValue = Math.abs(min - baselineValue) > Math.abs(max - baselineValue) ? min : max;
|
|
293
297
|
return {
|
|
294
|
-
axis
|
|
298
|
+
axis,
|
|
295
299
|
stops: [{
|
|
296
300
|
offset: peakValue,
|
|
297
301
|
color: fill,
|
package/esm/chart/utils/path.js
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
import { area as d3Area, curveBumpX, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
|
|
2
|
-
import {
|
|
1
|
+
import { area as d3Area, curveBumpX, curveBumpY, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
|
|
2
|
+
import { getPointOnScale, projectPoints } from './point';
|
|
3
3
|
import { isCategoricalScale } from './scale';
|
|
4
|
+
/**
|
|
5
|
+
* Default enter transition for path-based components (Line, Area).
|
|
6
|
+
* `{ type: 'timing', duration: 500 }`
|
|
7
|
+
*/
|
|
8
|
+
export const defaultPathEnterTransition = {
|
|
9
|
+
type: 'timing',
|
|
10
|
+
duration: 500
|
|
11
|
+
};
|
|
4
12
|
/**
|
|
5
13
|
* Get the d3 curve function for a path.
|
|
6
14
|
* See https://d3js.org/d3-shape/curve
|
|
7
15
|
* @param curve - The curve type. Defaults to 'linear'.
|
|
16
|
+
* @param layout - The chart layout. Defaults to 'vertical'.
|
|
8
17
|
* @returns The d3 curve function.
|
|
9
18
|
*/
|
|
10
|
-
export const getPathCurveFunction = function (curve) {
|
|
19
|
+
export const getPathCurveFunction = function (curve, layout) {
|
|
11
20
|
if (curve === void 0) {
|
|
12
21
|
curve = 'linear';
|
|
13
22
|
}
|
|
23
|
+
if (layout === void 0) {
|
|
24
|
+
layout = 'vertical';
|
|
25
|
+
}
|
|
14
26
|
switch (curve) {
|
|
15
27
|
case 'catmullRom':
|
|
16
28
|
return curveCatmullRom;
|
|
17
29
|
case 'monotone':
|
|
18
|
-
//
|
|
19
|
-
|
|
30
|
+
// For vertical layout, X is the independent axis (category/index), so use MonotoneX.
|
|
31
|
+
// For horizontal layout, Y is the independent axis (category/index), so use MonotoneY.
|
|
32
|
+
return layout !== 'horizontal' ? curveMonotoneX : curveMonotoneY;
|
|
20
33
|
case 'natural':
|
|
21
34
|
return curveNatural;
|
|
22
35
|
case 'step':
|
|
@@ -26,8 +39,9 @@ export const getPathCurveFunction = function (curve) {
|
|
|
26
39
|
case 'stepAfter':
|
|
27
40
|
return curveStepAfter;
|
|
28
41
|
case 'bump':
|
|
29
|
-
//
|
|
30
|
-
|
|
42
|
+
// For vertical layout, X is the independent axis (category/index), so use BumpX.
|
|
43
|
+
// For horizontal layout, Y is the independent axis (category/index), so use BumpY.
|
|
44
|
+
return layout !== 'horizontal' ? curveBumpX : curveBumpY;
|
|
31
45
|
case 'linearClosed':
|
|
32
46
|
return curveLinearClosed;
|
|
33
47
|
case 'linear':
|
|
@@ -53,17 +67,21 @@ export const getLinePath = _ref => {
|
|
|
53
67
|
xScale,
|
|
54
68
|
yScale,
|
|
55
69
|
xData,
|
|
56
|
-
|
|
70
|
+
yData,
|
|
71
|
+
connectNulls = false,
|
|
72
|
+
layout = 'vertical'
|
|
57
73
|
} = _ref;
|
|
58
74
|
if (data.length === 0) {
|
|
59
75
|
return '';
|
|
60
76
|
}
|
|
61
|
-
const curveFunction = getPathCurveFunction(curve);
|
|
77
|
+
const curveFunction = getPathCurveFunction(curve, layout);
|
|
62
78
|
const dataPoints = projectPoints({
|
|
63
79
|
data,
|
|
64
80
|
xScale,
|
|
65
81
|
yScale,
|
|
66
|
-
xData
|
|
82
|
+
xData,
|
|
83
|
+
yData,
|
|
84
|
+
layout
|
|
67
85
|
});
|
|
68
86
|
|
|
69
87
|
// When connectNulls is true, filter out null values before rendering
|
|
@@ -102,14 +120,20 @@ export const getAreaPath = _ref2 => {
|
|
|
102
120
|
xScale,
|
|
103
121
|
yScale,
|
|
104
122
|
xData,
|
|
105
|
-
|
|
123
|
+
yData,
|
|
124
|
+
connectNulls = false,
|
|
125
|
+
layout = 'vertical'
|
|
106
126
|
} = _ref2;
|
|
107
127
|
if (data.length === 0) {
|
|
108
128
|
return '';
|
|
109
129
|
}
|
|
110
|
-
const curveFunction = getPathCurveFunction(curve);
|
|
111
|
-
const
|
|
112
|
-
|
|
130
|
+
const curveFunction = getPathCurveFunction(curve, layout);
|
|
131
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
132
|
+
|
|
133
|
+
// Determine baseline from the value scale.
|
|
134
|
+
const valueScale = categoryAxisIsX ? yScale : xScale;
|
|
135
|
+
const domain = valueScale.domain();
|
|
136
|
+
const min = Math.min(...domain);
|
|
113
137
|
const normalizedData = data.map((item, index) => {
|
|
114
138
|
if (item === null) {
|
|
115
139
|
return null;
|
|
@@ -121,7 +145,7 @@ export const getAreaPath = _ref2 => {
|
|
|
121
145
|
return null;
|
|
122
146
|
}
|
|
123
147
|
if (typeof item === 'number') {
|
|
124
|
-
return [
|
|
148
|
+
return [min, item];
|
|
125
149
|
}
|
|
126
150
|
return null;
|
|
127
151
|
});
|
|
@@ -129,37 +153,28 @@ export const getAreaPath = _ref2 => {
|
|
|
129
153
|
if (range === null) {
|
|
130
154
|
return {
|
|
131
155
|
x: 0,
|
|
156
|
+
y: 0,
|
|
132
157
|
low: null,
|
|
133
158
|
high: null,
|
|
134
159
|
isValid: false
|
|
135
160
|
};
|
|
136
161
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
162
|
+
|
|
163
|
+
// Determine the position along the independent (index/category) axis.
|
|
164
|
+
let indexValue = index;
|
|
165
|
+
const indexScale = categoryAxisIsX ? xScale : yScale;
|
|
166
|
+
const indexData = categoryAxisIsX ? xData : yData;
|
|
167
|
+
if (!isCategoricalScale(indexScale) && indexData && indexData[index] !== undefined) {
|
|
168
|
+
indexValue = indexData[index];
|
|
140
169
|
}
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
xScale,
|
|
145
|
-
yScale
|
|
146
|
-
});
|
|
147
|
-
const lowPoint = projectPoint({
|
|
148
|
-
x: xValue,
|
|
149
|
-
y: range[0],
|
|
150
|
-
xScale,
|
|
151
|
-
yScale
|
|
152
|
-
});
|
|
153
|
-
const highPoint = projectPoint({
|
|
154
|
-
x: xValue,
|
|
155
|
-
y: range[1],
|
|
156
|
-
xScale,
|
|
157
|
-
yScale
|
|
158
|
-
});
|
|
170
|
+
const position = getPointOnScale(indexValue, indexScale);
|
|
171
|
+
const low = getPointOnScale(range[0], valueScale);
|
|
172
|
+
const high = getPointOnScale(range[1], valueScale);
|
|
159
173
|
return {
|
|
160
|
-
x:
|
|
161
|
-
|
|
162
|
-
|
|
174
|
+
x: categoryAxisIsX ? position : 0,
|
|
175
|
+
y: !categoryAxisIsX ? position : 0,
|
|
176
|
+
low,
|
|
177
|
+
high,
|
|
163
178
|
isValid: true
|
|
164
179
|
};
|
|
165
180
|
});
|
|
@@ -167,16 +182,25 @@ export const getAreaPath = _ref2 => {
|
|
|
167
182
|
// When connectNulls is true, filter out invalid points before rendering
|
|
168
183
|
// When false, use defined() to create gaps in the area
|
|
169
184
|
const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
|
|
170
|
-
const areaGenerator = d3Area()
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
185
|
+
const areaGenerator = d3Area();
|
|
186
|
+
if (categoryAxisIsX) {
|
|
187
|
+
areaGenerator.x(d => d.x).y0(d => {
|
|
188
|
+
var _d$low;
|
|
189
|
+
return (_d$low = d.low) != null ? _d$low : 0;
|
|
190
|
+
}).y1(d => {
|
|
191
|
+
var _d$high;
|
|
192
|
+
return (_d$high = d.high) != null ? _d$high : 0;
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
areaGenerator.y(d => d.y).x0(d => {
|
|
196
|
+
var _d$low2;
|
|
197
|
+
return (_d$low2 = d.low) != null ? _d$low2 : 0;
|
|
198
|
+
}).x1(d => {
|
|
199
|
+
var _d$high2;
|
|
200
|
+
return (_d$high2 = d.high) != null ? _d$high2 : 0;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
areaGenerator.curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null);
|
|
180
204
|
const result = areaGenerator(filteredPoints);
|
|
181
205
|
return result != null ? result : '';
|
|
182
206
|
};
|
|
@@ -208,22 +232,28 @@ export const lineToPath = (x1, y1, x2, y2) => {
|
|
|
208
232
|
* const roundedPath = getBarPath(10, 20, 50, 100, 8, true, false);
|
|
209
233
|
* ```
|
|
210
234
|
*/
|
|
211
|
-
export const getBarPath = (x, y, width, height, radius, roundTop, roundBottom)
|
|
235
|
+
export const getBarPath = function (x, y, width, height, radius, roundTop, roundBottom, layout) {
|
|
236
|
+
if (layout === void 0) {
|
|
237
|
+
layout = 'vertical';
|
|
238
|
+
}
|
|
239
|
+
const isVerticalLayout = layout === 'vertical';
|
|
212
240
|
const roundBothSides = roundTop && roundBottom;
|
|
213
241
|
const r = Math.min(radius, width / 2, roundBothSides ? height / 2 : height);
|
|
214
|
-
const
|
|
215
|
-
const
|
|
242
|
+
const rTL = isVerticalLayout ? roundTop ? r : 0 : roundBottom ? r : 0;
|
|
243
|
+
const rTR = isVerticalLayout ? roundTop ? r : 0 : roundTop ? r : 0;
|
|
244
|
+
const rBR = isVerticalLayout ? roundBottom ? r : 0 : roundTop ? r : 0;
|
|
245
|
+
const rBL = isVerticalLayout ? roundBottom ? r : 0 : roundBottom ? r : 0;
|
|
216
246
|
|
|
217
247
|
// Build path with selective rounding
|
|
218
|
-
let path = "M " + (x +
|
|
219
|
-
path += " L " + (x + width -
|
|
220
|
-
path += " A " +
|
|
221
|
-
path += " L " + (x + width) + " " + (y + height -
|
|
222
|
-
path += " A " +
|
|
223
|
-
path += " L " + (x +
|
|
224
|
-
path += " A " +
|
|
225
|
-
path += " L " + x + " " + (y +
|
|
226
|
-
path += " A " +
|
|
248
|
+
let path = "M " + (x + rTL) + " " + y;
|
|
249
|
+
path += " L " + (x + width - rTR) + " " + y;
|
|
250
|
+
path += " A " + rTR + " " + rTR + " 0 0 1 " + (x + width) + " " + (y + rTR);
|
|
251
|
+
path += " L " + (x + width) + " " + (y + height - rBR);
|
|
252
|
+
path += " A " + rBR + " " + rBR + " 0 0 1 " + (x + width - rBR) + " " + (y + height);
|
|
253
|
+
path += " L " + (x + rBL) + " " + (y + height);
|
|
254
|
+
path += " A " + rBL + " " + rBL + " 0 0 1 " + x + " " + (y + height - rBL);
|
|
255
|
+
path += " L " + x + " " + (y + rTL);
|
|
256
|
+
path += " A " + rTL + " " + rTL + " 0 0 1 " + (x + rTL) + " " + y;
|
|
227
257
|
path += ' Z';
|
|
228
258
|
return path;
|
|
229
259
|
};
|
package/esm/chart/utils/point.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applySerializableScale, isCategoricalScale, isLogScale
|
|
1
|
+
import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale } from './scale';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Position a label should be placed relative to the point
|
|
@@ -10,19 +10,38 @@ import { applySerializableScale, isCategoricalScale, isLogScale, isNumericScale
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Get a point from a data value and a scale.
|
|
13
|
-
*
|
|
14
|
-
* @
|
|
15
|
-
* @param
|
|
16
|
-
* @param
|
|
17
|
-
* @returns
|
|
13
|
+
*
|
|
14
|
+
* @param dataValue - The data value to convert to a pixel position.
|
|
15
|
+
* @param scale - The scale function.
|
|
16
|
+
* @param anchor (@default 'middle') - For band scales, where to anchor the point within the band.
|
|
17
|
+
* @returns The pixel value (@default 0 if data value is not defined in scale).
|
|
18
18
|
*/
|
|
19
|
-
export const getPointOnScale = (dataValue, scale)
|
|
20
|
-
var
|
|
19
|
+
export const getPointOnScale = function (dataValue, scale, anchor) {
|
|
20
|
+
var _scale;
|
|
21
|
+
if (anchor === void 0) {
|
|
22
|
+
anchor = 'middle';
|
|
23
|
+
}
|
|
21
24
|
if (isCategoricalScale(scale)) {
|
|
22
|
-
var
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
var _bandScale$bandwidth, _bandScale$step;
|
|
26
|
+
const bandScale = scale;
|
|
27
|
+
const bandStart = bandScale(dataValue);
|
|
28
|
+
if (bandStart === undefined) return 0;
|
|
29
|
+
const bandwidth = (_bandScale$bandwidth = bandScale.bandwidth == null ? void 0 : bandScale.bandwidth()) != null ? _bandScale$bandwidth : 0;
|
|
30
|
+
const step = (_bandScale$step = bandScale.step == null ? void 0 : bandScale.step()) != null ? _bandScale$step : bandwidth;
|
|
31
|
+
const paddingOffset = (step - bandwidth) / 2;
|
|
32
|
+
const stepStart = bandStart - paddingOffset;
|
|
33
|
+
switch (anchor) {
|
|
34
|
+
case 'stepStart':
|
|
35
|
+
return stepStart;
|
|
36
|
+
case 'bandStart':
|
|
37
|
+
return bandStart;
|
|
38
|
+
case 'middle':
|
|
39
|
+
return bandStart + bandwidth / 2;
|
|
40
|
+
case 'bandEnd':
|
|
41
|
+
return bandStart + bandwidth;
|
|
42
|
+
case 'stepEnd':
|
|
43
|
+
return stepStart + step;
|
|
44
|
+
}
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
// For log scales, ensure the value is positive
|
|
@@ -30,23 +49,47 @@ export const getPointOnScale = (dataValue, scale) => {
|
|
|
30
49
|
if (isLogScale(scale) && dataValue <= 0) {
|
|
31
50
|
adjustedValue = 0.001; // Use a small positive value for log scales
|
|
32
51
|
}
|
|
33
|
-
return (
|
|
52
|
+
return (_scale = scale(adjustedValue)) != null ? _scale : 0;
|
|
34
53
|
};
|
|
35
54
|
|
|
36
55
|
/**
|
|
37
56
|
* Get a point from a data value and a serializable scale (worklet-compatible).
|
|
38
|
-
*
|
|
39
|
-
* @
|
|
40
|
-
* @param
|
|
41
|
-
* @param
|
|
42
|
-
* @returns
|
|
57
|
+
*
|
|
58
|
+
* @param dataValue - The data value to convert to a pixel position.
|
|
59
|
+
* @param scale - The serializable scale function.
|
|
60
|
+
* @param anchor (@default 'middle') - For band scales, where to anchor the point within the band.
|
|
61
|
+
* @returns The pixel value (@default 0 if data value is not defined in scale).
|
|
43
62
|
*/
|
|
44
|
-
export function getPointOnSerializableScale(dataValue, scale) {
|
|
63
|
+
export function getPointOnSerializableScale(dataValue, scale, anchor) {
|
|
45
64
|
'worklet';
|
|
46
65
|
|
|
66
|
+
// Handle band scales with the specified position
|
|
67
|
+
if (anchor === void 0) {
|
|
68
|
+
anchor = 'middle';
|
|
69
|
+
}
|
|
47
70
|
if (scale.type === 'band') {
|
|
48
|
-
const
|
|
49
|
-
|
|
71
|
+
const bandScale = scale;
|
|
72
|
+
const [domainMin, domainMax] = bandScale.domain;
|
|
73
|
+
const index = dataValue - domainMin;
|
|
74
|
+
const n = domainMax - domainMin + 1;
|
|
75
|
+
if (index < 0 || index >= n) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
const bandStart = applyBandScale(dataValue, bandScale);
|
|
79
|
+
const paddingOffset = (bandScale.step - bandScale.bandwidth) / 2;
|
|
80
|
+
const stepStart = bandStart - paddingOffset;
|
|
81
|
+
switch (anchor) {
|
|
82
|
+
case 'stepStart':
|
|
83
|
+
return stepStart;
|
|
84
|
+
case 'bandStart':
|
|
85
|
+
return bandStart;
|
|
86
|
+
case 'middle':
|
|
87
|
+
return bandStart + bandScale.bandwidth / 2;
|
|
88
|
+
case 'bandEnd':
|
|
89
|
+
return bandStart + bandScale.bandwidth;
|
|
90
|
+
case 'stepEnd':
|
|
91
|
+
return stepStart + bandScale.step;
|
|
92
|
+
}
|
|
50
93
|
}
|
|
51
94
|
|
|
52
95
|
// For log scales, ensure the value is positive
|
|
@@ -121,7 +164,8 @@ export const projectPoints = _ref3 => {
|
|
|
121
164
|
xScale,
|
|
122
165
|
yScale,
|
|
123
166
|
xData,
|
|
124
|
-
yData
|
|
167
|
+
yData,
|
|
168
|
+
layout = 'vertical'
|
|
125
169
|
} = _ref3;
|
|
126
170
|
if (data.length === 0) {
|
|
127
171
|
return [];
|
|
@@ -139,28 +183,37 @@ export const projectPoints = _ref3 => {
|
|
|
139
183
|
});
|
|
140
184
|
}
|
|
141
185
|
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
186
|
+
// Determine values/scales based on role (index vs value) and layout.
|
|
187
|
+
const categoryAxisIsX = layout !== 'horizontal';
|
|
188
|
+
const indexScale = categoryAxisIsX ? xScale : yScale;
|
|
189
|
+
const indexData = categoryAxisIsX ? xData : yData;
|
|
190
|
+
|
|
191
|
+
// 1. Calculate position along the index axis (categorical or numeric domain).
|
|
192
|
+
let indexValue = index;
|
|
193
|
+
if (!isCategoricalScale(indexScale)) {
|
|
194
|
+
if (indexData && Array.isArray(indexData) && indexData.length > 0) {
|
|
195
|
+
if (typeof indexData[0] === 'number') {
|
|
196
|
+
var _indexData$index;
|
|
197
|
+
indexValue = (_indexData$index = indexData[index]) != null ? _indexData$index : index;
|
|
154
198
|
}
|
|
155
199
|
}
|
|
156
200
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
201
|
+
|
|
202
|
+
// 2. Calculate position along the value axis (measured magnitude).
|
|
203
|
+
const valueAsNumber = value;
|
|
204
|
+
|
|
205
|
+
// 3. Project final coordinates based on layout.
|
|
206
|
+
if (categoryAxisIsX) {
|
|
207
|
+
return projectPoint({
|
|
208
|
+
x: indexValue,
|
|
209
|
+
y: valueAsNumber,
|
|
210
|
+
xScale,
|
|
211
|
+
yScale
|
|
212
|
+
});
|
|
160
213
|
}
|
|
161
214
|
return projectPoint({
|
|
162
|
-
x:
|
|
163
|
-
y:
|
|
215
|
+
x: valueAsNumber,
|
|
216
|
+
y: indexValue,
|
|
164
217
|
xScale,
|
|
165
218
|
yScale
|
|
166
219
|
});
|
package/esm/chart/utils/scale.js
CHANGED
|
@@ -53,10 +53,21 @@ export const getCategoricalScale = _ref2 => {
|
|
|
53
53
|
const domainArray = Array.from({
|
|
54
54
|
length: domain.max - domain.min + 1
|
|
55
55
|
}, (_, i) => i);
|
|
56
|
-
const scale = scaleBand().domain(domainArray).range([range.min, range.max]).padding(padding);
|
|
56
|
+
const scale = scaleBand().domain(domainArray).range([range.min, range.max]).paddingInner(padding).paddingOuter(padding / 2);
|
|
57
57
|
return scale;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Anchor position for points on a scale. Currently used only for band scales.
|
|
62
|
+
*
|
|
63
|
+
* For band scales, this determines where within the band to position a point:
|
|
64
|
+
* - `'stepStart'` - At the start of the step
|
|
65
|
+
* - `'bandStart'` - At the start of the band
|
|
66
|
+
* - `'middle'` - At the center of the band
|
|
67
|
+
* - `'bandEnd'` - At the end of the band
|
|
68
|
+
* - `'stepEnd'` - At the end of the step
|
|
69
|
+
*/
|
|
70
|
+
|
|
60
71
|
/**
|
|
61
72
|
* Convert a D3 scale to a serializable scale configuration that can be used in worklets
|
|
62
73
|
*/
|
|
@@ -176,7 +187,7 @@ export function applyBandScale(value, scale) {
|
|
|
176
187
|
if (index < 0 || index >= n) {
|
|
177
188
|
return r0;
|
|
178
189
|
}
|
|
179
|
-
const paddingOffset = step - scale.bandwidth;
|
|
190
|
+
const paddingOffset = (step - scale.bandwidth) / 2;
|
|
180
191
|
const bandStart = r0 + step * index + paddingOffset;
|
|
181
192
|
return bandStart;
|
|
182
193
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Determines which side (left/right) to place scrubber labels based on available space.
|
|
3
|
-
*
|
|
3
|
+
* Honors the preferred side when there's enough space, otherwise switches to the opposite side.
|
|
4
4
|
*/
|
|
5
|
-
export const getLabelPosition = function (beaconX, maxLabelWidth, drawingArea, xOffset) {
|
|
5
|
+
export const getLabelPosition = function (beaconX, maxLabelWidth, drawingArea, xOffset, preferredSide) {
|
|
6
6
|
'worklet';
|
|
7
7
|
|
|
8
8
|
// any regular functions in ui thread must be marked with 'worklet'
|
|
9
9
|
if (xOffset === void 0) {
|
|
10
10
|
xOffset = 16;
|
|
11
11
|
}
|
|
12
|
+
if (preferredSide === void 0) {
|
|
13
|
+
preferredSide = 'right';
|
|
14
|
+
}
|
|
12
15
|
if (drawingArea.width <= 0 || drawingArea.height <= 0) {
|
|
13
|
-
return
|
|
16
|
+
return preferredSide;
|
|
14
17
|
}
|
|
15
|
-
const availableRightSpace = drawingArea.x + drawingArea.width - beaconX;
|
|
16
18
|
const requiredSpace = maxLabelWidth + xOffset;
|
|
17
|
-
|
|
19
|
+
if (preferredSide === 'right') {
|
|
20
|
+
const availableSpace = drawingArea.x + drawingArea.width - beaconX;
|
|
21
|
+
return requiredSpace <= availableSpace ? 'right' : 'left';
|
|
22
|
+
}
|
|
23
|
+
const availableSpace = beaconX - drawingArea.x;
|
|
24
|
+
return requiredSpace <= availableSpace ? 'left' : 'right';
|
|
18
25
|
};
|
|
19
26
|
/**
|
|
20
27
|
* Calculates Y positions for all labels avoiding overlaps while maintaining order.
|